pax_global_header00006660000000000000000000000064126725421670014526gustar00rootroot0000000000000052 comment=213339a802827fab23cb61b79ad7d592fad33c2d libsass-3.3.4/000077500000000000000000000000001267254216700131755ustar00rootroot00000000000000libsass-3.3.4/.editorconfig000066400000000000000000000004441267254216700156540ustar00rootroot00000000000000# This file is for unifying the coding style for different editors and IDEs # editorconfig.org root = true [*] charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = spaces indent_size = 2 [{Makefile, GNUmakefile.am}] indent_style = tab indent_size = 4 libsass-3.3.4/.gitattributes000066400000000000000000000001021267254216700160610ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto libsass-3.3.4/.gitignore000066400000000000000000000015151267254216700151670ustar00rootroot00000000000000# Miscellaneous stuff VERSION .DS_Store .sass-cache *.gem *.gcno .svn/* .cproject .project .settings/ # Configuration stuff GNUmakefile.in GNUmakefile /aclocal.m4 /autom4te.cache/ /src/config.h /config.h.in /config.log /config.status /configure /libtool /m4/libtool.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 /script/ar-lib /script/compile /script/config.guess /script/config.sub /script/depcomp /script/install-sh /script/ltmain.sh /script/missing /script/test-driver /src/stamp-h1 /src/Makefile.in /src/Makefile libsass/* # Build stuff *.o *.lo *.so *.dll *.a *.suo *.sdf *.opendb *.opensdf a.out libsass.js tester tester.exe build/ config.h.in* lib/pkgconfig/ bin/* .deps/ .libs/ win/bin *.user # Final results sassc++ libsass.la src/support/libsass.pc # Cloned testing dirs sassc/ sass-spec/ installer/ libsass-3.3.4/.travis.yml000066400000000000000000000021261267254216700153070ustar00rootroot00000000000000language: cpp sudo: false os: - linux - osx compiler: - gcc - clang # don't create redundant code coverage reports # - AUTOTOOLS=yes COVERAGE=yes BUILD=static # - AUTOTOOLS=no COVERAGE=yes BUILD=shared # - AUTOTOOLS=no COVERAGE=no BUILD=static # further speed up day by day travis-ci builds # re-enable this if you change the makefiles # this will still catch all coding errors! # - AUTOTOOLS=yes COVERAGE=no BUILD=static env: - AUTOTOOLS=no COVERAGE=no BUILD=shared - AUTOTOOLS=no COVERAGE=yes BUILD=static - AUTOTOOLS=yes COVERAGE=no BUILD=shared # currenty there are various issues when # built with coverage, clang and autotools # - AUTOTOOLS=yes COVERAGE=yes BUILD=shared matrix: exclude: - compiler: clang env: AUTOTOOLS=yes COVERAGE=yes BUILD=static - os: linux env: AUTOTOOLS=no COVERAGE=no BUILD=shared - os: osx compiler: gcc - os: osx env: AUTOTOOLS=no COVERAGE=yes BUILD=static script: ./script/ci-build-libsass before_install: ./script/ci-install-deps install: ./script/ci-install-compiler after_success: ./script/ci-report-coverage libsass-3.3.4/COPYING000066400000000000000000000023341267254216700142320ustar00rootroot00000000000000 Copyright (C) 2012 by Hampton Catlin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files in the spec were taken from the original Ruby Sass project which is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under the same license. libsass-3.3.4/GNUmakefile.am000066400000000000000000000044261267254216700156510ustar00rootroot00000000000000ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 -I script AM_COPT = -Wall -O2 AM_COVLDFLAGS = if ENABLE_COVERAGE AM_COPT = -O0 --coverage AM_COVLDFLAGS += -lgcov endif AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(AM_COPT) AM_CXXFLAGS = $(AM_COPT) AM_LDFLAGS = $(AM_COPT) $(AM_COVLDFLAGS) # only needed to support old source tree # we have moved the files to src folder AM_CPPFLAGS += -I$(top_srcdir) RESOURCES = if COMPILER_IS_MINGW32 RESOURCES += res/libsass.rc AM_CXXFLAGS += -std=gnu++0x else AM_CXXFLAGS += -std=c++0x endif if ENABLE_TESTS noinst_PROGRAMS = tester tester_LDADD = src/libsass.la tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c tester_VERSION ?= `cd "$(SASS_SASSC_PATH)" && ./version.sh` tester_CFLAGS = $(AM_CFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\"" tester_CXXFLAGS = $(AM_CXXFLAGS) -DSASSC_VERSION="\"$(tester_VERSION)\"" tester_LDFLAGS = $(AM_LDFLAGS) if ENABLE_COVERAGE nodist_EXTRA_tester_SOURCES = non-existent-file-to-force-CXX-linking.cxx endif SASS_SASSC_PATH ?= $(top_srcdir)/sassc SASS_SPEC_PATH ?= $(top_srcdir)/sass-spec TESTS = \ $(SASS_SPEC_PATH)/spec/basic \ $(SASS_SPEC_PATH)/spec/css \ $(SASS_SPEC_PATH)/spec/extend-tests \ $(SASS_SPEC_PATH)/spec/extends \ $(SASS_SPEC_PATH)/spec/libsass \ $(SASS_SPEC_PATH)/spec/libsass-closed-issues \ $(SASS_SPEC_PATH)/spec/maps \ $(SASS_SPEC_PATH)/spec/misc \ $(SASS_SPEC_PATH)/spec/regressions \ $(SASS_SPEC_PATH)/spec/scss \ $(SASS_SPEC_PATH)/spec/scss-tests \ $(SASS_SPEC_PATH)/spec/types SASS_TEST_FLAGS = LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) ./script/tap-driver AM_LOG_FLAGS = -c ./tester --ignore-todo $(LOG_FLAGS) if USE_TAP AM_LOG_FLAGS += -t SASS_TEST_FLAGS += -t | tapout LOG_COMPILER = ./script/tap-runner $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb else LOG_COMPILER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb endif SASS_TESTER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb SASS_TESTER += -c $(SASS_LIBSASS_PATH)/tester$(EXEEXT) test: $(SASS_TESTER) --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) test_build: $(SASS_TESTER) --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) test_full: $(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH) $(SASS_TEST_FLAGS) test_issues: $(SASS_TESTER) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues $(SASS_TEST_FLAGS) endif SUBDIRS = src libsass-3.3.4/INSTALL000066400000000000000000000000611267254216700142230ustar00rootroot00000000000000// Autotools requires us to have this file. Boo. libsass-3.3.4/LICENSE000066400000000000000000000023621267254216700142050ustar00rootroot00000000000000 Copyright (C) 2012-2016 by the Sass Open Source Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. The following files in the spec were taken from the original Ruby Sass project which is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under the same license. libsass-3.3.4/Makefile000066400000000000000000000174761267254216700146540ustar00rootroot00000000000000OS ?= $(shell uname -s) CC ?= gcc CXX ?= g++ RM ?= rm -f CP ?= cp -a MKDIR ?= mkdir RMDIR ?= rmdir WINDRES ?= windres # Solaris/Illumos flavors # ginstall from coreutils ifeq ($(OS),SunOS) INSTALL ?= ginstall endif INSTALL ?= install CFLAGS ?= -Wall CXXFLAGS ?= -Wall LDFLAGS ?= -Wall ifneq "$(COVERAGE)" "yes" CFLAGS += -O2 CXXFLAGS += -O2 LDFLAGS += -O2 endif LDFLAGS += -Wl,-undefined,error 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 endif ifeq ($(SASS_LIBSASS_PATH),) SASS_LIBSASS_PATH = $(abspath $(CURDIR)) endif ifeq ($(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),) CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" endif # enable mandatory flag 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),) CFLAGS += -I $(SASS_LIBSASS_PATH)/include CXXFLAGS += -I $(SASS_LIBSASS_PATH)/include else # this is needed for mingw CFLAGS += -I include 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 LDLIBS = -lm ifneq ($(BUILD),shared) LDLIBS += -lstdc++ endif # link statically into lib # makes it a lot more portable # increases size by about 50KB ifeq ($(STATIC_ALL),1) LDFLAGS += -static endif ifeq ($(STATIC_LIBGCC),1) LDFLAGS += -static-libgcc endif ifeq ($(STATIC_LIBSTDCPP),1) LDFLAGS += -static-libstdc++ endif ifeq ($(UNAME),Darwin) CFLAGS += -stdlib=libc++ CXXFLAGS += -stdlib=libc++ LDFLAGS += -stdlib=libc++ endif ifneq (Windows,$(UNAME)) ifneq (FreeBSD,$(UNAME)) LDFLAGS += -ldl LDLIBS += -ldl endif endif ifneq ($(BUILD),shared) BUILD = static endif ifeq (,$(TRAVIS_BUILD_DIR)) ifeq ($(OS),SunOS) PREFIX ?= /opt/local else PREFIX ?= /usr/local endif else 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 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 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 endif else ifneq (Cygwin,$(UNAME)) CFLAGS += -fPIC CXXFLAGS += -fPIC LDFLAGS += -fPIC endif endif OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o)) COBJECTS = $(addprefix src/,$(CSOURCES:.c=.o)) RCOBJECTS = $(RESOURCES:.rc=.o) DEBUG_LVL ?= NONE CLEANUPS ?= CLEANUPS += $(RCOBJECTS) CLEANUPS += $(COBJECTS) CLEANUPS += $(OBJECTS) CLEANUPS += $(LIBSASS_LIB) all: $(BUILD) debug: $(BUILD) debug-static: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS)) debug-static: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS)) debug-static: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) debug-static: static debug-shared: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS)) debug-shared: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS)) debug-shared: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) debug-shared: shared lib: $(MKDIR) lib lib/libsass.a: lib $(COBJECTS) $(OBJECTS) $(AR) rcvs $@ $(COBJECTS) $(OBJECTS) lib/libsass.so: lib $(COBJECTS) $(OBJECTS) $(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 %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< %.o: %.rc $(WINDRES) -i $< -o $@ %.o: %.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< %: %.o static $(CXX) $(CXXFLAGS) -o $@ $+ $(LDFLAGS) $(LDLIBS) install: install-$(BUILD) static: $(STATICLIB) shared: $(SHAREDLIB) $(DESTDIR)$(PREFIX): $(MKDIR) $(DESTDIR)$(PREFIX) $(DESTDIR)$(PREFIX)/lib: $(DESTDIR)$(PREFIX) $(MKDIR) $(DESTDIR)$(PREFIX)/lib $(DESTDIR)$(PREFIX)/include: $(DESTDIR)$(PREFIX) $(MKDIR) $(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 $(INSTALL) -v -m0644 "$<" "$@" install-headers: $(DESTDIR)$(PREFIX)/include/sass.h \ $(DESTDIR)$(PREFIX)/include/sass2scss.h \ $(DESTDIR)$(PREFIX)/include/sass/base.h \ $(DESTDIR)$(PREFIX)/include/sass/version.h \ $(DESTDIR)$(PREFIX)/include/sass/values.h \ $(DESTDIR)$(PREFIX)/include/sass/context.h \ $(DESTDIR)$(PREFIX)/include/sass/functions.h $(DESTDIR)$(PREFIX)/lib/%.a: lib/%.a \ $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.so: lib/%.so \ $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" $(DESTDIR)$(PREFIX)/lib/%.dll: lib/%.dll \ $(DESTDIR)$(PREFIX)/lib @$(INSTALL) -v -m0755 "$<" "$@" install-static: $(DESTDIR)$(PREFIX)/lib/libsass.a install-shared: $(DESTDIR)$(PREFIX)/lib/libsass.so \ install-headers $(SASSC_BIN): $(BUILD) $(MAKE) -C $(SASS_SASSC_PATH) 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 -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_build: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) test_issues: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues clean-objects: lib -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la -$(RMDIR) lib clean: clean-objects $(RM) $(CLEANUPS) clean-all: $(MAKE) -C $(SASS_SASSC_PATH) clean lib-file: lib-file-$(BUILD) lib-opts: lib-opts-$(BUILD) lib-file-static: @echo $(LIB_STATIC) lib-file-shared: @echo $(LIB_SHARED) lib-opts-static: @echo -L"$(SASS_LIBSASS_PATH)/lib" lib-opts-shared: @echo -L"$(SASS_LIBSASS_PATH)/lib -lsass" .PHONY: all static shared sassc \ version install-headers \ clean clean-all clean-objects \ 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 .DELETE_ON_ERROR: libsass-3.3.4/Makefile.conf000066400000000000000000000013571267254216700155670ustar00rootroot00000000000000# this is merely a common Makefile multiple implementers can use SOURCES = \ ast.cpp \ base64vlq.cpp \ bind.cpp \ color_maps.cpp \ constants.cpp \ context.cpp \ cssize.cpp \ emitter.cpp \ environment.cpp \ error_handling.cpp \ eval.cpp \ expand.cpp \ extend.cpp \ file.cpp \ functions.cpp \ inspect.cpp \ json.cpp \ lexer.cpp \ listize.cpp \ memory_manager.cpp \ node.cpp \ output.cpp \ parser.cpp \ plugins.cpp \ position.cpp \ prelexer.cpp \ remove_placeholders.cpp \ sass.cpp \ sass_util.cpp \ sass_values.cpp \ sass_context.cpp \ sass_functions.cpp \ sass_interface.cpp \ sass2scss.cpp \ source_map.cpp \ to_c.cpp \ to_value.cpp \ units.cpp \ utf8_string.cpp \ values.cpp \ util.cpp CSOURCES = cencode.c libsass-3.3.4/Readme.md000066400000000000000000000100241267254216700147110ustar00rootroot00000000000000LibSass ======= by Aaron Leung ([@akhleung]), Hampton Catlin ([@hcatlin]), Marcel Greter ([@mgreter]) and Michael Mifsud ([@xzyfer]) [![Linux CI](https://travis-ci.org/sass/libsass.svg?branch=master)](https://travis-ci.org/sass/libsass) [![Windows CI](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/sass/libsass/branch/master) [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=283068)](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Coverage Status](https://img.shields.io/coveralls/sass/libsass.svg)](https://coveralls.io/r/sass/libsass?branch=feature%2Ftest-travis-ci-3) [![Join us](https://libsass-slack.herokuapp.com/badge.svg)](https://libsass-slack.herokuapp.com/) https://github.com/sass/libsass LibSass is just a library, but if you want to RUN LibSass, then go to https://github.com/sass/sassc or https://github.com/sass/sassc-ruby or [find your local implementer](docs/implementations.md). LibSass requires GCC 4.6+ or Clang/LLVM. If your OS is older, this version may not compile. On Windows, you need MinGW with GCC 4.6+ or VS 2013 Update 4+. It is also possible to build LibSass with Clang/LLVM on Windows. About ----- LibSass is a C/C++ port of the Sass CSS precompiler. The original version was written in Ruby, but this version is meant for efficiency and portability. This library strives to be light, simple, and easy to build and integrate with a variety of platforms and languages. Developing ---------- As you may have noticed, the LibSass repo itself has no executables and no tests. Oh noes! How can you develop??? Well, luckily, [SassC](http://github.com/sass/sassc) is the official binary wrapper for LibSass and is *always* kept in sync. SassC uses a git submodule to include LibSass. When developing LibSass, its best to actually check out SassC and develop in that directory with the SassC spec and tests there. We even run Travis tests for SassC! Tests ------- Since LibSass is a pure library, tests are run through the [SassSpec](https://github.com/sass/sass-spec) project using the [SassC](http://github.com/sass/sassc) driver. To run tests against LibSass while developing, you can run `./script/spec`. This will clone SassC and Sass-Spec under the project folder and then run the Sass-Spec test suite. You may want to update the clones to ensure you have the latest version. Library Usage ------------- While LibSass is a library primarily written in C++, it provides a simple C interface which should be used by most implementers. LibSass does not do much on its own without an implementer. This can be a command line tool, like [sassc](https://github.com/sass/sassc) or a [binding](docs/implementations.md) to your favorite programing language. If you want to build or interface with LibSass, we recommend to check out the documentation pages about [building LibSass](docs/build.md) and the [C-API documentation](docs/api-doc.md). About Sass ---------- Sass is a CSS pre-processor language to add on exciting, new, awesome features to CSS. Sass was the first language of its kind and by far the most mature and up to date codebase. Sass was originally concieved of by the co-creator of this library, Hampton Catlin ([@hcatlin]). Most of the language has been the result of years of work by Natalie Weizenbaum ([@nex3]) and Chris Eppstein ([@chriseppstein]). For more information about Sass itself, please visit http://sass-lang.com Initial development of libsass by Aaron Leung and Hampton Catlin was supported by [Moovweb](http://www.moovweb.com). Licensing --------- Our MIT license is designed to be as simple, and liberal as possible. [@hcatlin]: https://github.com/hcatlin [@akhleung]: https://github.com/akhleung [@chriseppstein]: https://github.com/chriseppstein [@nex3]: https://github.com/nex3 [@mgreter]: https://github.com/mgreter [@xzyfer]: https://github.com/xzyfer sass2scss was originally written by [Marcel Greter](@mgreter) and he happily agreed to have it merged into the project. libsass-3.3.4/SECURITY.md000066400000000000000000000005621267254216700147710ustar00rootroot00000000000000Serious about security ====================== The LibSass team recognizes the important contributions the security research community can make. We therefore encourage reporting security issues with the code contained in this repository. If you believe you have discovered a security vulnerability, please report it at https://hackerone.com/libsass instead of GitHub. libsass-3.3.4/appveyor.yml000066400000000000000000000045011267254216700155650ustar00rootroot00000000000000os: Visual Studio 2013 environment: CTEST_OUTPUT_ON_FAILURE: 1 ruby_version: 22-x64 TargetPath: sassc/bin/sassc matrix: - Compiler: msvc Config: Release - Compiler: msvc Config: Debug - Compiler: mingw Build: static - Compiler: mingw Build: shared cache: - C:\Ruby%ruby_version%\lib\ruby\gems - C:\mingw64 install: - git clone https://github.com/sass/sassc.git - git clone https://github.com/sass/sass-spec.git - set PATH=C:\Ruby%ruby_version%\bin;%PATH% - ps: | if(!(gem which minitest 2>$nul)) { gem install minitest --no-ri --no-rdoc } if ($env:Compiler -eq "mingw" -AND -Not (Test-Path "C:\mingw64")) { # Install MinGW. $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z" wget https://bintray.com/artifact/download/drewwells/generic/$file -OutFile $file &7z x -oC:\ $file > $null } - set PATH=C:\mingw64\bin;%PATH% - set CC=gcc build_script: - ps: | if ($env:Compiler -eq "mingw") { mingw32-make -j4 sassc } else { msbuild /m:4 /p:Configuration=$env:Config sassc\win\sassc.sln } # print the branding art mv script/branding script/branding.ps1 script/branding.ps1 # print the version info &$env:TargetPath -v ruby -v test_script: - ps: | $PRNR = $env:APPVEYOR_PULL_REQUEST_NUMBER if ($PRNR) { echo "Fetching info for PR $PRNR" wget https://api.github.com/repos/sass/libsass/pulls/$PRNR -OutFile pr.json $json = cat pr.json -Raw $SPEC_PR = [regex]::match($json,'sass\/sass-spec(#|\/pull\/)([0-9]+)').Groups[2].Value if ($SPEC_PR) { echo "Checkout sass spec PR $SPEC_PR" git -C sass-spec fetch -q -u origin pull/$SPEC_PR/head:ci-spec-pr-$SPEC_PR git -C sass-spec checkout -q --force ci-spec-pr-$SPEC_PR } } ruby sass-spec/sass-spec.rb -c $env:TargetPath -s --ignore-todo sass-spec/spec Write-Host "Explicitly testing the case when cwd has Cyrillic characters: " -nonewline # See comments in gh-1774 for details. $env:TargetPath = Join-Path $pwd.Path $env:TargetPath cd sass-spec/spec/libsass/Sáss-UŢF8/ &$env:TargetPath ./input.scss 2>&1>$null if(-not($?)) { echo "Failed!" exit 1 } else { echo "Success!" } libsass-3.3.4/configure.ac000066400000000000000000000104421267254216700154640ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) AC_INIT([libsass], m4_esyscmd_s([./version.sh]), [support@moovweb.com]) AC_CONFIG_SRCDIR([src/ast.hpp]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_FILES([include/sass/version.h]) AC_CONFIG_AUX_DIR([script]) # These are flags passed to automake # Though they look like gcc flags! AM_INIT_AUTOMAKE([foreign parallel-tests -Wall]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([no])]) # would fail with mingw otherwise # m4_pattern_allow([AM_PROG_AR]) # Checks for programs. AC_PROG_CC AC_PROG_CXX AC_LANG_PUSH([C]) AC_LANG_PUSH([C++]) AC_GNU_SOURCE # Check fails on Travis, but it works fine # AX_CXX_COMPILE_STDCXX_11([ext],[optional]) AC_CHECK_TOOL([AR], [ar], [false]) if test "x$is_mingw32" != "xyes"; then AC_CHECK_TOOL([DLLTOOL], [dlltool], [false]) AC_CHECK_TOOL([DLLWRAP], [dllwrap], [false]) AC_CHECK_TOOL([WINDRES], [windres], [false]) fi LT_INIT([dlopen]) # Checks for header files. AC_CHECK_HEADERS([unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions. AC_FUNC_MALLOC AC_CHECK_FUNCS([floor getcwd strtol]) # Checks for testing. AC_ARG_ENABLE(tests, AS_HELP_STRING([--enable-tests], [enable testing the build]), [enable_tests="$enableval"], [enable_tests=no]) AS_CASE([$target], [*-*-mingw32], [is_mingw32=yes], [is_mingw32=no]) AM_CONDITIONAL(COMPILER_IS_MINGW32, test "x$is_mingw32" = "xyes") dnl The dlopen() function is in the C library for *BSD and in dnl libdl on GLIBC-based systems if test "x$is_mingw32" != "xyes"; then AC_SEARCH_LIBS([dlopen], [dl dld], [], [ AC_MSG_ERROR([unable to find the dlopen() function]) ]) fi if test "x$enable_tests" = "xyes"; then AC_PROG_CC AC_PROG_AWK # test need minitest gem AC_PATH_PROG(RUBY, [ruby]) AC_PATH_PROG(TAPOUT, [tapout]) AC_REQUIRE_AUX_FILE([tap-driver]) AC_REQUIRE_AUX_FILE([tap-runner]) AC_ARG_WITH(sassc-dir, AS_HELP_STRING([--with-sassc-dir=], [specify directory of sassc sources for testing (default: sassc)]), [sassc_dir="$withval"], [sassc_dir="sassc"]) AC_CHECK_FILE([$sassc_dir/sassc.c], [], [ AC_MSG_ERROR([Unable to find sassc directory. You must clone the sassc repository in this directory or specify the --with-sassc-dir= argument. ]) ]) SASS_SASSC_PATH=$sassc_dir AC_SUBST(SASS_SASSC_PATH) AC_ARG_WITH(sass-spec-dir, AS_HELP_STRING([--with-sass-spec-dir=], [specify directory of sass-spec for testing (default: sass-spec)]), [sass_spec_dir="$withval"], [sass_spec_dir="sass-spec"]) AC_CHECK_FILE([$sass_spec_dir/sass-spec.rb], [], [ AC_MSG_ERROR([Unable to find sass-spec directory. You must clone the sass-spec repository in this directory or specify the --with-sass-spec-dir= argument. ]) ]) # Automake doesn't like its tests in an absolute path, so we make it relative. case $sass_spec_dir in /*) SASS_SPEC_PATH=`$RUBY -e "require 'pathname'; puts Pathname.new('$sass_spec_dir').relative_path_from(Pathname.new('$PWD')).to_s"` ;; *) SASS_SPEC_PATH="$sass_spec_dir" ;; esac AC_SUBST(SASS_SPEC_PATH) # TODO: Remove this when automake requirements are 1.12+ AC_MSG_CHECKING([whether we can use TAP mode]) tmp=`$AWK '/TEST_LOG_DRIVER/' $srcdir/GNUmakefile.in` if test "x$tmp" != "x"; then use_tap=yes else use_tap=no fi AC_MSG_RESULT([$use_tap]) fi AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "xyes") AM_CONDITIONAL(USE_TAP, test "x$use_tap" = "xyes") AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], [enable coverage report for test suite])], [enable_cov=$enableval], [enable_cov=no]) if test "x$enable_cov" = "xyes"; then AC_CHECK_PROG(GCOV, gcov, gcov) # Remove all optimization flags from C[XX]FLAGS changequote({,}) CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'` CXXFLAGS=`echo "$CXXFLAGS" | $SED -e 's/-O[0-9]*//g'` changequote([,]) AC_SUBST(GCOV) fi AM_CONDITIONAL(ENABLE_COVERAGE, test "x$enable_cov" = "xyes") AC_SUBST(PACKAGE_VERSION) AC_MSG_NOTICE([Building libsass ($VERSION)]) AC_CONFIG_FILES([GNUmakefile src/GNUmakefile src/support/libsass.pc]) AC_OUTPUT libsass-3.3.4/contrib/000077500000000000000000000000001267254216700146355ustar00rootroot00000000000000libsass-3.3.4/contrib/libsass.spec000066400000000000000000000023221267254216700171500ustar00rootroot00000000000000Name: libsass Version: %{version} Release: 1%{?dist} Summary: A C/C++ implementation of a Sass compiler License: MIT URL: http://libsass.org Source0: %{name}-%{version}.tar.gz BuildRequires: gcc-c++ >= 4.7 BuildRequires: autoconf BuildRequires: automake BuildRequires: libtool %description LibSass is a C/C++ port of the Sass engine. The point is to be simple, fast, and easy to integrate. %package devel Summary: Development files for %{name} Requires: %{name}%{?_isa} = %{version}-%{release} %description devel The %{name}-devel package contains libraries and header files for developing applications that use %{name}. %prep %setup -q autoreconf --force --install %build %configure --disable-static \ --disable-tests \ --enable-shared make %{?_smp_mflags} %install %make_install find $RPM_BUILD_ROOT -name '*.la' -exec rm -f {} ';' %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %doc Readme.md LICENSE %{_libdir}/*.so.* %files devel %doc %{_includedir}/* %{_libdir}/*.so %{_libdir}/pkgconfig/*.pc %changelog * Tue Feb 10 2015 Gawain Lynch - 3.1.0-1 - Initial SPEC file libsass-3.3.4/contrib/plugin.cpp000066400000000000000000000041051267254216700166370ustar00rootroot00000000000000#include #include #include #include // gcc: g++ -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass // mingw: g++ -shared plugin.cpp -o plugin.dll -Llib -lsass extern "C" const char* ADDCALL libsass_get_version() { return libsass_version(); } union Sass_Value* custom_function(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp) { // get context/option struct associated with this compiler struct Sass_Context* ctx = sass_compiler_get_context(comp); struct Sass_Options* opts = sass_compiler_get_options(comp); // get the cookie from function descriptor void* cookie = sass_function_get_cookie(cb); // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } extern "C" Sass_Function_List ADDCALL libsass_load_functions() { // allocate a custom function caller Sass_Function_Entry c_func = sass_make_function("foo()", custom_function, (void*)42); // create list of all custom functions Sass_Function_List fn_list = sass_make_function_list(1); // put the only function in this plugin to the list sass_function_set_list_entry(fn_list, 0, c_func); // return the list return fn_list; } Sass_Import_List custom_importer(const char* cur_path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // get the cookie from importer descriptor void* cookie = sass_importer_get_cookie(cb); // create a list to hold our import entries Sass_Import_List incs = sass_make_import_list(1); // create our only import entry (route path back) incs[0] = sass_make_import_entry(cur_path, 0, 0); // return imports return incs; } extern "C" Sass_Importer_List ADDCALL libsass_load_importers() { // allocate a custom function caller Sass_Importer_Entry c_imp = sass_make_importer(custom_importer, - 99, (void*)42); // create list of all custom functions Sass_Importer_List imp_list = sass_make_importer_list(1); // put the only function in this plugin to the list sass_importer_set_list_entry(imp_list, 0, c_imp); // return the list return imp_list; } libsass-3.3.4/docs/000077500000000000000000000000001267254216700141255ustar00rootroot00000000000000libsass-3.3.4/docs/README.md000066400000000000000000000030741267254216700154100ustar00rootroot00000000000000Welcome to the LibSass documentation! ## First Off LibSass is just a library. To run the code locally (i.e. to compile your stylesheets), you need an implementer. SassC (get it?) is an implementer written in C. There are a number of other implementations of LibSass - for example Node. We encourage you to write your own port - the whole point of LibSass is that we want to bring Sass to many other languages, not just Ruby! We're working hard on moving to full parity with Ruby Sass... learn more at the [The-LibSass-Compatibility-Plan](compatibility-plan.md)! ### Implementing LibSass If you're interested in implementing LibSass in your own project see the [API Documentation](api-doc.md) which now includes implementing your own [Sass functions](api-function.md). You may wish to [look at other implementations](implementations.md) for your language of choice. Or make your own! ### Contributing to LibSass | Issue Tracker | Issue Triage | Community Guidelines | |-------------------|----------------------------------|-----------------------------| | We're always needing help, so check out our issue tracker, help some people out, and read our article on [Contributing](contributing.md)! It's got all the details on what to do! | To help understand the process of triaging bugs, have a look at our [Issue-Triage](triage.md) document. | Oh, and don't forget we always follow [[Sass Community Guidelines|http://sass-lang.com/community-guidelines]]. Be nice and everyone else will be nice too! | Please refer to the steps on [Building LibSass](build.md) libsass-3.3.4/docs/api-context-example.md000066400000000000000000000021361267254216700203350ustar00rootroot00000000000000## Example main.c ```C #include #include "sass/context.h" int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // configure some options ... sass_option_set_precision(ctx_opt, 10); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ### Compile main.c ```bash gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: 21px * 2; }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` libsass-3.3.4/docs/api-context-internal.md000066400000000000000000000062631267254216700205230ustar00rootroot00000000000000```C // Input behaviours enum Sass_Input_Style { SASS_CONTEXT_NULL, SASS_CONTEXT_FILE, SASS_CONTEXT_DATA, SASS_CONTEXT_FOLDER }; // simple linked list struct string_list { string_list* next; char* string; }; // sass config options structure struct Sass_Options { // Precision for fractional numbers int precision; // Output style for the generated css code // A value from above SASS_STYLE_* constants enum Sass_Output_Style output_style; // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; // embed sourceMappingUrl as data uri bool source_map_embed; // embed include contents in maps bool source_map_contents; // Disable sourceMappingUrl in css output bool omit_source_map_url; // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; // The input path is used for source map // generation. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; // The output path is used for source map // generation. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; // String to be used for indentation const char* indent; // String to be used to for line feeds const char* linefeed; // Colon-separated list of paths // Semicolon-separated on Windows // Note: It may be better to use // array interface instead char* include_path; char* plugin_path; // Include paths (linked string list) struct string_list* include_paths; // Plugin paths (linked string list) struct string_list* plugin_paths; // Path to source map file // Enables source map generation // Used to create sourceMappingUrl char* source_map_file; // Directly inserted in source maps char* source_map_root; // Custom functions that can be called from sccs code Sass_C_Function_List c_functions; // Callback to overload imports Sass_C_Import_Callback importer; }; // base for all contexts struct Sass_Context : Sass_Options { // store context type info enum Sass_Input_Style type; // generated output data char* output_string; // generated source map json char* source_map_string; // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; // report imported files char** included_files; }; // struct for file compilation struct Sass_File_Context : Sass_Context { // no additional fields required // input_path is already on options }; // struct for data compilation struct Sass_Data_Context : Sass_Context { // provided source string char* source_string; }; // Compiler states enum Sass_Compiler_State { SASS_COMPILER_CREATED, SASS_COMPILER_PARSED, SASS_COMPILER_EXECUTED }; // link c and cpp context struct Sass_Compiler { // progress status Sass_Compiler_State state; // original c context Sass_Context* c_ctx; // Sass::Context void* cpp_ctx; // Sass::Block void* root; }; ``` libsass-3.3.4/docs/api-context.md000066400000000000000000000224041267254216700167040ustar00rootroot00000000000000Sass Contexts come in two flavors: - `Sass_File_Context` - `Sass_Data_Context` ### Basic Usage ```C #include "sass/context.h" ``` ***Sass_Options*** ```C // Precision for fractional numbers int precision; ``` ```C // Output style for the generated css code // A value from above SASS_STYLE_* constants int output_style; ``` ```C // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; ``` ```C // embed sourceMappingUrl as data uri bool source_map_embed; ``` ```C // embed include contents in maps bool source_map_contents; ``` ```C // Disable sourceMappingUrl in css output bool omit_source_map_url; ``` ```C // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; ``` ```C // The input path is used for source map // generating. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; ``` ```C // The output path is used for source map // generating. LibSass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; ``` ```C // String to be used for indentation const char* indent; ``` ```C // String to be used to for line feeds const char* linefeed; ``` ```C // Colon-separated list of paths // Semicolon-separated on Windows char* include_path; char* plugin_path; ``` ```C // Additional include paths // Must be null delimited char** include_paths; char** plugin_paths; ``` ```C // Path to source map file // Enables the source map generating // Used to create sourceMappingUrl char* source_map_file; ``` ```C // Directly inserted in source maps char* source_map_root; ``` ```C // Custom functions that can be called from sccs code Sass_C_Function_List c_functions; ``` ```C // Callback to overload imports Sass_C_Import_Callback importer; ``` ***Sass_Context*** ```C // store context type info enum Sass_Input_Style type; ```` ```C // generated output data char* output_string; ``` ```C // generated source map json char* source_map_string; ``` ```C // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; ``` ```C // report imported files char** included_files; ``` ***Sass_File_Context*** ```C // no additional fields required // input_path is already on options ``` ***Sass_Data_Context*** ```C // provided source string char* source_string; ``` ### Sass Context API ```C // Forward declaration struct Sass_Compiler; // Forward declaration struct Sass_Options; struct Sass_Context; // : Sass_Options struct Sass_File_Context; // : Sass_Context struct Sass_Data_Context; // : Sass_Context // Create and initialize an option struct struct Sass_Options* sass_make_options (void); // Create and initialize a specific context struct Sass_File_Context* sass_make_file_context (const char* input_path); struct Sass_Data_Context* sass_make_data_context (char* source_string); // Call the compilation step for the specific context int sass_compile_file_context (struct Sass_File_Context* ctx); int sass_compile_data_context (struct Sass_Data_Context* ctx); // Create a sass compiler instance for more control struct Sass_Compiler* sass_make_file_compiler (struct Sass_File_Context* file_ctx); struct Sass_Compiler* 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 int sass_compiler_parse(struct Sass_Compiler* compiler); int sass_compiler_execute(struct Sass_Compiler* compiler); // Release all memory allocated with the compiler // This does _not_ include any contexts or options void sass_delete_compiler(struct Sass_Compiler* compiler); // Release all memory allocated and also ourself void sass_delete_file_context (struct Sass_File_Context* ctx); void sass_delete_data_context (struct Sass_Data_Context* ctx); // Getters for Context from specific implementation struct Sass_Context* sass_file_context_get_context (struct Sass_File_Context* file_ctx); struct Sass_Context* sass_data_context_get_context (struct Sass_Data_Context* data_ctx); // Getters for Context_Options from Sass_Context struct Sass_Options* sass_context_get_options (struct Sass_Context* ctx); struct Sass_Options* sass_file_context_get_options (struct Sass_File_Context* file_ctx); struct Sass_Options* sass_data_context_get_options (struct Sass_Data_Context* data_ctx); void sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt); void sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt); // Getters for Sass_Context values const char* sass_context_get_output_string (struct Sass_Context* ctx); int sass_context_get_error_status (struct Sass_Context* ctx); const char* sass_context_get_error_json (struct Sass_Context* ctx); const char* sass_context_get_error_text (struct Sass_Context* ctx); const char* sass_context_get_error_message (struct Sass_Context* ctx); const char* sass_context_get_error_file (struct Sass_Context* ctx); size_t sass_context_get_error_line (struct Sass_Context* ctx); size_t sass_context_get_error_column (struct Sass_Context* ctx); const char* sass_context_get_source_map_string (struct Sass_Context* ctx); char** sass_context_get_included_files (struct Sass_Context* ctx); // Take ownership of memory (value on context is set to 0) char* sass_context_take_error_json (struct Sass_Context* ctx); char* sass_context_take_error_text (struct Sass_Context* ctx); char* sass_context_take_error_message (struct Sass_Context* ctx); char* sass_context_take_error_file (struct Sass_Context* ctx); char* sass_context_take_output_string (struct Sass_Context* ctx); char* sass_context_take_source_map_string (struct Sass_Context* ctx); // Push function for plugin/include paths (no manipulation support for now) void sass_option_push_plugin_path (struct Sass_Options* options, const char* path); void sass_option_push_include_path (struct Sass_Options* options, const char* path); ``` ### Sass Options API ```C // Getters for Context_Option values int sass_option_get_precision (struct Sass_Options* options); enum Sass_Output_Style sass_option_get_output_style (struct Sass_Options* options); bool sass_option_get_source_comments (struct Sass_Options* options); bool sass_option_get_source_map_embed (struct Sass_Options* options); bool sass_option_get_source_map_contents (struct Sass_Options* options); bool sass_option_get_omit_source_map_url (struct Sass_Options* options); bool sass_option_get_is_indented_syntax_src (struct Sass_Options* options); const char* sass_option_get_indent (struct Sass_Options* options); const char* sass_option_get_linefeed (struct Sass_Options* options); const char* sass_option_get_input_path (struct Sass_Options* options); const char* sass_option_get_output_path (struct Sass_Options* options); const char* sass_option_get_plugin_path (struct Sass_Options* options); const char* sass_option_get_include_path (struct Sass_Options* options); const char* sass_option_get_source_map_file (struct Sass_Options* options); const char* sass_option_get_source_map_root (struct Sass_Options* options); Sass_C_Function_List sass_option_get_c_functions (struct Sass_Options* options); Sass_C_Import_Callback sass_option_get_importer (struct Sass_Options* options); // Setters for Context_Option values void sass_option_set_precision (struct Sass_Options* options, int precision); void sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style); void sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); void sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); void sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents); void sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); void sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); void sass_option_set_indent (struct Sass_Options* options, const char* indent); void sass_option_set_linefeed (struct Sass_Options* options, const char* linefeed); void sass_option_set_input_path (struct Sass_Options* options, const char* input_path); void sass_option_set_output_path (struct Sass_Options* options, const char* output_path); void sass_option_set_plugin_path (struct Sass_Options* options, const char* plugin_path); void sass_option_set_include_path (struct Sass_Options* options, const char* include_path); void sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file); void sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root); void sass_option_set_c_functions (struct Sass_Options* options, Sass_C_Function_List c_functions); void sass_option_set_importer (struct Sass_Options* options, Sass_C_Import_Callback importer); // Push function for paths (no manipulation support for now) void sass_option_push_plugin_path (struct Sass_Options* options, const char* path); void sass_option_push_include_path (struct Sass_Options* options, const char* path); ``` ### More links - [Sass Context Example](api-context-example.md) - [Sass Context Internal](api-context-internal.md) libsass-3.3.4/docs/api-doc.md000066400000000000000000000153021267254216700157640ustar00rootroot00000000000000## Introduction LibSass wouldn't be much good without a way to interface with it. These interface documentations describe the various functions and data structures available to implementers. They are split up over three major components, which have all their own source files (plus some common functionality). - [Sass Context](api-context.md) - Trigger and handle the main Sass compilation - [Sass Value](api-value.md) - Exchange values and its format with LibSass - [Sass Function](api-function.md) - Get invoked by LibSass for function statments - [Sass Importer](api-importer.md) - Get invoked by LibSass for @import statments ### Basic usage First you will need to include the header file! This will automatically load all other headers too! ```C #include "sass/context.h" ``` ### Deprecated usage The old API is kept in the source for backward compatibility. It's deprecated and incompatible with this documentation, use `sass/context.h`! ```C // deprecated interface #include "sass/interface.h" ``` ## Basic C Example ```C #include #include "sass/context.h" int main() { puts(libsass_VERSION()); return 0; } ``` ```bash gcc -Wall version.c -lsass -o version && ./version ``` ## More C Examples - [Sample code for Sass Context](api-context-example.md) - [Sample code for Sass Value](api-value-example.md) - [Sample code for Sass Function](api-function-example.md) - [Sample code for Sass Importer](api-importer-example.md) ## Compiling your code The most important is your sass file (or string of sass code). With this, you will want to start a LibSass compiler. Here is some pseudocode describing the process. The compiler has two different modes: direct input as a string with `Sass_Data_Context` or LibSass will do file reading for you by using `Sass_File_Context`. See the code for a list of options available [Sass_Options](https://github.com/sass/libsass/blob/36feef0/include/sass/interface.h#L18) **Building a file compiler** context = sass_make_file_context("file.scss") options = sass_file_context_get_options(context) sass_option_set_precision(options, 1) sass_option_set_source_comments(options, true) sass_file_context_set_options(context, options) compiler = sass_make_file_compiler(sass_context) sass_compiler_parse(compiler) sass_compiler_execute(compiler) output = sass_context_get_output_string(context) // Retrieve errors during compilation error_status = sass_context_get_error_status(context) json_error = sass_context_get_error_json(context) // Release memory dedicated to the C compiler sass_delete_compiler(compiler) **Building a data compiler** context = sass_make_data_context("div { a { color: blue; } }") options = sass_data_context_get_options(context) sass_option_set_precision(options, 1) sass_option_set_source_comments(options, true) sass_data_context_set_options(context, options) compiler = sass_make_data_compiler(context) sass_compiler_parse(compiler) sass_compiler_execute(compiler) output = sass_context_get_output_string(context) // div a { color: blue; } // Retrieve errors during compilation error_status = sass_context_get_error_status(context) json_error = sass_context_get_error_json(context) // Release memory dedicated to the C compiler sass_delete_compiler(compiler) ## Sass Context Internals Everything is stored in structs: ```C struct Sass_Options; struct Sass_Context : Sass_Options; struct Sass_File_context : Sass_Context; struct Sass_Data_context : Sass_Context; ``` This mirrors very well how `libsass` uses these structures. - `Sass_Options` holds everything you feed in before the compilation. It also hosts `input_path` and `output_path` options, because they are used to generate/calculate relative links in source-maps. The `input_path` is shared with `Sass_File_Context`. - `Sass_Context` holds all the data returned by the compilation step. - `Sass_File_Context` is a specific implementation that requires no additional fields - `Sass_Data_Context` is a specific implementation that adds the `input_source` field Structs can be down-casted to access `context` or `options`! ## Common Pitfalls **input_path** The `input_path` is part of `Sass_Options`, but it also is the main option for `Sass_File_Context`. It is also used to generate relative file links in source-maps. Therefore it is pretty usefull to pass this information if you have a `Sass_Data_Context` and know the original path. **output_path** Be aware that `libsass` does not write the output file itself. This option merely exists to give `libsass` the proper information to generate links in source-maps. The file has to be written to the disk by the binding/implementation. If the `output_path` is omitted, `libsass` tries to extrapolate one from the `input_path` by replacing (or adding) the file ending with `.css`. ## Error Codes The `error_code` is integer value which indicates the type of error that occurred inside the LibSass process. Following is the list of error codes along with the short description: * 1: normal errors like parsing or `eval` errors * 2: bad allocation error (memory error) * 3: "untranslated" C++ exception (`throw std::exception`) * 4: legacy string exceptions ( `throw const char*` or `std::string` ) * 5: Some other unknown exception Although for the API consumer, error codes do not offer much value except indicating whether *any* error occurred during the compilation, it helps debugging the LibSass internal code paths. ## Real-World Implementations The proof is in the pudding, so we have highlighted a few implementations that should be on par with the latest LibSass interface version. Some of them may not have all features implemented! 1. [Perl Example](https://github.com/sass/perl-libsass/blob/master/lib/CSS/Sass.xs) 2. [Go Example](http://godoc.org/github.com/wellington/go-libsass#example-Context-Compile) 3. [Node Example](https://github.com/sass/node-sass/blob/master/src/binding.cpp) ## ABI forward compatibility We use a functional API to make dynamic linking more robust and future compatible. The API is not yet 100% stable, so we do not yet guarantee [ABI](https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) forward compatibility. We will do so, once we increase the shared library version above 1.0. ## Plugins (experimental) LibSass can load plugins from directories. Just define `plugin_path` on context options to load all plugins from the given directories. To implement plugins, please consult the [[Wiki-Page for plugins|API-Plugins]]. ## Internal Structs - [Sass Context Internals](api-context-internal.md) - [Sass Value Internals](api-value-internal.md) - [Sass Function Internals](api-function-internal.md) - [Sass Importer Internals](api-importer-internal.md) libsass-3.3.4/docs/api-function-example.md000066400000000000000000000041471267254216700205020ustar00rootroot00000000000000## Example main.c ```C #include #include #include "sass/context.h" union Sass_Value* call_fn_foo(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp) { // get context/option struct associated with this compiler struct Sass_Context* ctx = sass_compiler_get_context(comp); struct Sass_Options* opts = sass_compiler_get_options(comp); // get information about previous importer entry from the stack struct Sass_Import* import = sass_compiler_get_last_import(comp); const char* prev_abs_path = sass_import_get_abs_path(import); const char* prev_imp_path = sass_import_get_imp_path(import); // get the cookie from function descriptor void* cookie = sass_function_get_cookie(cb); // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller Sass_Function_Entry fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ### Compile main.c ```bash gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: foo(); }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` libsass-3.3.4/docs/api-function-internal.md000066400000000000000000000002441267254216700206550ustar00rootroot00000000000000```C // Struct to hold custom function callback struct Sass_Function { const char* signature; Sass_Function_Fn function; void* cookie; }; ``` libsass-3.3.4/docs/api-function.md000066400000000000000000000042461267254216700170510ustar00rootroot00000000000000Sass functions are used to define new custom functions callable by Sass code. They are also used to overload debug or error statements. You can also define a fallback function, which is called for every unknown function found in the Sass code. Functions get passed zero or more `Sass_Values` (a `Sass_List` value) and they must also return a `Sass_Value`. Return a `Sass_Error` if you want to signal an error. ## Special signatures - `*` - Fallback implementation - `@warn` - Overload warn statements - `@error` - Overload error statements - `@debug` - Overload debug statements Note: The fallback implementation will be given the name of the called function as the first argument, before all the original function arguments. These features are pretty new and should be considered experimental. ### Basic Usage ```C #include "sass/functions.h" ``` ## Sass Function API ```C // Forward declaration struct Sass_Compiler; struct Sass_Function; // Typedef helpers for custom functions lists typedef struct Sass_Function (*Sass_Function_Entry); typedef struct Sass_Function* (*Sass_Function_List); // Typedef defining function signature and return type typedef union Sass_Value* (*Sass_Function_Fn) (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); // Creators for sass function list and function descriptors ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length); ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie); // Setters and getters for callbacks on function lists ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos); ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb); // Getters for custom function descriptors ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb); ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb); ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb); ``` ### More links - [Sass Function Example](api-function-example.md) - [Sass Function Internal](api-function-internal.md) libsass-3.3.4/docs/api-importer-example.md000066400000000000000000000070551267254216700205170ustar00rootroot00000000000000## Example importer.c ```C #include #include #include "sass/context.h" Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // get the cookie from importer descriptor void* cookie = sass_importer_get_cookie(cb); Sass_Import_List list = sass_make_import_list(2); const char* local = "local { color: green; }"; const char* remote = "remote { color: red; }"; list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); return list; } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate custom importer Sass_Importer_Entry c_imp = sass_make_importer(sass_importer, 0, 0); // create list for all custom importers Sass_Importer_List imp_list = sass_make_importer_list(1); // put only the importer on to the list sass_importer_set_list_entry(imp_list, 0, c_imp); // register list on to the context options sass_option_set_c_importers(ctx_opt, imp_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` Compile importer.c ```bash gcc -c importer.c -o importer.o gcc -o importer importer.o -lsass echo "@import 'foobar';" > importer.scss ./importer importer.scss ``` ## Importer Behavior Examples ```C Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass handle the import request return NULL; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass handle the request // swallows »@import "http://…"« pass-through // (arguably a bug) Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry(path, 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // return an error to halt execution Sass_Import_List list = sass_make_import_list(1); const char* message = "some error message"; list[0] = sass_make_import_entry(path, 0, 0); sass_import_set_error(list[0], strdup(message), 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // let LibSass load the file identifed by the importer Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // completely hide the import // (arguably a bug) Sass_Import_List list = sass_make_import_list(0); return list; } Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { // completely hide the import // (arguably a bug) Sass_Import_List list = sass_make_import_list(1); list[0] = sass_make_import_entry(0, 0, 0); return list; } ``` libsass-3.3.4/docs/api-importer-internal.md000066400000000000000000000006451267254216700206760ustar00rootroot00000000000000```C // External import entry struct Sass_Import { char* imp_path; // path as found in the import statement char *abs_path; // path after importer has resolved it char* source; char* srcmap; // error handling char* error; size_t line; size_t column; }; // Struct to hold importer callback struct Sass_Importer { Sass_Importer_Fn importer; double priority; void* cookie; }; ``` libsass-3.3.4/docs/api-importer.md000066400000000000000000000101341267254216700170560ustar00rootroot00000000000000By using custom importers, Sass stylesheets can be implemented in any possible way, such as by being loaded via a remote server. Please note: this feature is experimental and is implemented differently than importers in Ruby Sass. Imports must be relative to the parent import context and therefore we need to pass this information to the importer callback. This is currently done by passing the complete import string/path of the previous import context. ## Return Imports You actually have to return a list of imports, since some importers may want to import multiple files from one import statement (ie. a glob/star importer). The memory you pass with source and srcmap is taken over by LibSass and freed automatically when the import is done. You are also allowed to return `0` instead of a list, which will tell LibSass to handle the import by itself (as if no custom importer was in use). ```C struct Sass_Import** rv = sass_make_import_list(1); rv[0] = sass_make_import(rel, abs, source, srcmap); ``` Every import will then be included in LibSass. You are allowed to only return a file path without any loaded source. This way you can ie. implement rewrite rules for import paths and leave the loading part for LibSass. ### Basic Usage ```C #include "sass/functions.h" ``` ## Sass Importer API ```C // Forward declaration struct Sass_Import; // Forward declaration struct Sass_C_Import_Descriptor; // Typedef defining the custom importer callback typedef struct Sass_C_Import_Descriptor (*Sass_C_Import_Callback); // Typedef defining the importer c function prototype typedef struct Sass_Import** (*Sass_C_Import_Fn) (const char* url, const char* prev, void* cookie); // Creators for custom importer callback (with some additional pointer) // The pointer is mostly used to store the callback into the actual function Sass_C_Import_Callback sass_make_importer (Sass_C_Import_Fn, void* cookie); // Getters for import function descriptors Sass_C_Import_Fn sass_import_get_function (Sass_C_Import_Callback fn); void* sass_import_get_cookie (Sass_C_Import_Callback fn); // Deallocator for associated memory void sass_delete_importer (Sass_C_Import_Callback fn); // Creator for sass custom importer return argument list struct Sass_Import** sass_make_import_list (size_t length); // Creator for a single import entry returned by the custom importer inside the list struct Sass_Import* sass_make_import_entry (const char* path, char* source, char* srcmap); struct Sass_Import* sass_make_import (const char* rel, const char* abs, char* source, char* srcmap); // set error message to abort import and to print out a message (path from existing object is used in output) struct Sass_Import* sass_import_set_error(struct Sass_Import* import, const char* message, size_t line, size_t col); // Setters to insert an entry into the import list (you may also use [] access directly) // Since we are dealing with pointers they should have a guaranteed and fixed size void sass_import_set_list_entry (struct Sass_Import** list, size_t idx, struct Sass_Import* entry); struct Sass_Import* sass_import_get_list_entry (struct Sass_Import** list, size_t idx); // Getters for import entry const char* sass_import_get_rel_path (struct Sass_Import*); const char* sass_import_get_abs_path (struct Sass_Import*); const char* sass_import_get_source (struct Sass_Import*); const char* sass_import_get_srcmap (struct Sass_Import*); // Explicit functions to take ownership of these items // The property on our struct will be reset to NULL char* sass_import_take_source (struct Sass_Import*); char* sass_import_take_srcmap (struct Sass_Import*); // Getters for import error entries size_t sass_import_get_error_line (struct Sass_Import*); size_t sass_import_get_error_column (struct Sass_Import*); const char* sass_import_get_error_message (struct Sass_Import*); // Deallocator for associated memory (incl. entries) void sass_delete_import_list (struct Sass_Import**); // Just in case we have some stray import structs void sass_delete_import (struct Sass_Import*); ``` ### More links - [Sass Importer Example](api-importer-example.md) - [Sass Importer Internal](api-importer-internal.md) libsass-3.3.4/docs/api-value-example.md000066400000000000000000000000001267254216700177510ustar00rootroot00000000000000libsass-3.3.4/docs/api-value-internal.md000066400000000000000000000023711267254216700201470ustar00rootroot00000000000000```C struct Sass_Unknown { enum Sass_Tag tag; }; struct Sass_Boolean { enum Sass_Tag tag; bool value; }; struct Sass_Number { enum Sass_Tag tag; double value; char* unit; }; struct Sass_Color { enum Sass_Tag tag; double r; double g; double b; double a; }; struct Sass_String { enum Sass_Tag tag; char* value; }; struct Sass_List { enum Sass_Tag tag; enum Sass_Separator separator; size_t length; // null terminated "array" union Sass_Value** values; }; struct Sass_Map { enum Sass_Tag tag; size_t length; struct Sass_MapPair* pairs; }; struct Sass_Null { enum Sass_Tag tag; }; struct Sass_Error { enum Sass_Tag tag; char* message; }; struct Sass_Warning { enum Sass_Tag tag; char* message; }; union Sass_Value { struct Sass_Unknown unknown; struct Sass_Boolean boolean; struct Sass_Number number; struct Sass_Color color; struct Sass_String string; struct Sass_List list; struct Sass_Map map; struct Sass_Null null; struct Sass_Error error; struct Sass_Warning warning; }; struct Sass_MapPair { union Sass_Value* key; union Sass_Value* value; }; ``` libsass-3.3.4/docs/api-value.md000066400000000000000000000114041267254216700163320ustar00rootroot00000000000000`Sass_Values` are used to pass values and their types between the implementer and LibSass. Sass knows various different value types (including nested arrays and hash-maps). If you implement a binding to another programming language, you have to find a way to convert `Sass_Values` between the targeted language and C. `Sass_Values` are currently only used by custom functions. ### Basic Usage ```C #include "sass/values.h" ``` ```C // Type for Sass values enum Sass_Tag { SASS_BOOLEAN, SASS_NUMBER, SASS_COLOR, SASS_STRING, SASS_LIST, SASS_MAP, SASS_NULL, SASS_ERROR, SASS_WARNING }; // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA, SASS_SPACE }; ``` ### Sass Value API ```C // Forward declaration union Sass_Value; // Return the sass tag for a generic sass value // Check is needed before accessing specific values! enum Sass_Tag sass_value_get_tag (const union Sass_Value* v); // Check value to be of a specific type // Can also be used before accessing properties! bool sass_value_is_null (const union Sass_Value* v); bool sass_value_is_number (const union Sass_Value* v); bool sass_value_is_string (const union Sass_Value* v); bool sass_value_is_boolean (const union Sass_Value* v); bool sass_value_is_color (const union Sass_Value* v); bool sass_value_is_list (const union Sass_Value* v); bool sass_value_is_map (const union Sass_Value* v); bool sass_value_is_error (const union Sass_Value* v); bool sass_value_is_warning (const union Sass_Value* v); // Getters and setters for Sass_Number double sass_number_get_value (const union Sass_Value* v); void sass_number_set_value (union Sass_Value* v, double value); const char* sass_number_get_unit (const union Sass_Value* v); void sass_number_set_unit (union Sass_Value* v, char* unit); // Getters and setters for Sass_String const char* sass_string_get_value (const union Sass_Value* v); void sass_string_set_value (union Sass_Value* v, char* value); // Getters and setters for Sass_Boolean bool sass_boolean_get_value (const union Sass_Value* v); void sass_boolean_set_value (union Sass_Value* v, bool value); // Getters and setters for Sass_Color double sass_color_get_r (const union Sass_Value* v); void sass_color_set_r (union Sass_Value* v, double r); double sass_color_get_g (const union Sass_Value* v); void sass_color_set_g (union Sass_Value* v, double g); double sass_color_get_b (const union Sass_Value* v); void sass_color_set_b (union Sass_Value* v, double b); double sass_color_get_a (const union Sass_Value* v); void sass_color_set_a (union Sass_Value* v, double a); // Getter for the number of items in list size_t sass_list_get_length (const union Sass_Value* v); // Getters and setters for Sass_List enum Sass_Separator sass_list_get_separator (const union Sass_Value* v); void sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value); // Getters and setters for Sass_List values union Sass_Value* sass_list_get_value (const union Sass_Value* v, size_t i); void sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value); // Getter for the number of items in map size_t sass_map_get_length (const union Sass_Value* v); // Getters and setters for Sass_List keys and values union Sass_Value* sass_map_get_key (const union Sass_Value* v, size_t i); void sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*); union Sass_Value* sass_map_get_value (const union Sass_Value* v, size_t i); void sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); // Getters and setters for Sass_Error char* sass_error_get_message (const union Sass_Value* v); void sass_error_set_message (union Sass_Value* v, char* msg); // Getters and setters for Sass_Warning char* sass_warning_get_message (const union Sass_Value* v); void sass_warning_set_message (union Sass_Value* v, char* msg); // Creator functions for all value types union Sass_Value* sass_make_null (void); union Sass_Value* sass_make_boolean (bool val); union Sass_Value* sass_make_string (const char* val); union Sass_Value* sass_make_number (double val, const char* unit); union Sass_Value* sass_make_color (double r, double g, double b, double a); union Sass_Value* sass_make_list (size_t len, enum Sass_Separator sep); union Sass_Value* sass_make_map (size_t len); union Sass_Value* sass_make_error (const char* msg); union Sass_Value* sass_make_warning (const char* msg); // Generic destructor function for all types // Will release memory of all associated Sass_Values // Means we will delete recursively for lists and maps void sass_delete_value (union Sass_Value* val); // Make a deep cloned copy of the given sass value union Sass_Value* sass_clone_value (const union Sass_Value* val); ``` ### More links - [Sass Value Example](api-value-example.md) - [Sass Value Internal](api-value-internal.md) libsass-3.3.4/docs/build-on-darwin.md000066400000000000000000000014301267254216700174400ustar00rootroot00000000000000To install LibSass, make sure the OS X build tools are installed: xcode-select --install ## Homebrew To install homebrew, see [http://brew.sh](http://brew.sh) ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" You can install the latest version of LibSass quite easily with brew. brew install --HEAD libsass To update this, do: brew reinstall --HEAD libsass Brew will build static and shared libraries, and a `libsass.pc` file in `/usr/local/lib/pkgconfig`. To use `libsass.pc`, make sure this path is in your `PKG_CONFIG_PATH` export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ## Manually See the linux instructions [Building-with-autotools](build-with-autotools.md) or [Building-with-makefiles](build-with-makefiles.md) libsass-3.3.4/docs/build-on-gentoo.md000066400000000000000000000024011267254216700174460ustar00rootroot00000000000000Here are two ebuilds to compile LibSass and sassc on gentoo linux. If you do not know how to use these ebuilds, you should probably read the gentoo wiki page about [portage overlays](http://wiki.gentoo.org/wiki/Overlay). ## www-misc/libsass/libsass-9999.ebuild ```ebuild EAPI=4 inherit eutils git-2 autotools DESCRIPTION="A C/C++ implementation of a Sass compiler." HOMEPAGE="http://libsass.org/" EGIT_PROJECT='libsass' EGIT_REPO_URI="https://github.com/sass/libsass.git" LICENSE="MIT" SLOT="0" KEYWORDS="" IUSE="" DEPEND="" RDEPEND="${DEPEND}" DEPEND="${DEPEND}" pkg_pretend() { # older gcc is not supported local major=$(gcc-major-version) local minor=$(gcc-minor-version) [[ "${MERGE_TYPE}" != "binary" && ( $major > 4 || ( $major == 4 && $minor < 5 ) ) ]] && \ die "Sorry, but gcc earlier than 4.5 will not work for LibSass." } src_prepare() { eautoreconf } ``` ## www-misc/sassc/sassc-9999.ebuild ```ebuild EAPI=4 inherit eutils git-2 autotools DESCRIPTION="Command Line Tool for LibSass." HOMEPAGE="http://libsass.org/" EGIT_PROJECT='sassc' EGIT_REPO_URI="https://github.com/sass/sassc.git" LICENSE="MIT" SLOT="0" KEYWORDS="" IUSE="" DEPEND="www-misc/libsass" RDEPEND="${DEPEND}" DEPEND="${DEPEND}" src_prepare() { eautoreconf } ``` libsass-3.3.4/docs/build-on-windows.md000066400000000000000000000113051267254216700176500ustar00rootroot00000000000000We support builds via MingGW and via Visual Studio Community 2013. Both should be considered experimental (MinGW was better tested)! ## Building via MingGW (makefiles) First grab the latest [MinGW for windows] [1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`. You need to have the following components installed: ![Visualization of components installed in the interface](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png) Next we need to install [git for windows] [2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools. If you want to run the spec test-suite you also need [ruby] [3] and a few gems available. Grab the [latest installer] [3] and make sure to add it the global path. Then install the missing gems: ```bash gem install minitest ``` ### Mount the mingw root directory As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line: ``` C:\MinGW /mingw ``` ### Starting a "MingGW" console Create a batch file with this content: ```bat @echo off set PATH=C:\MinGW\bin;%PATH% REM only needed if not already available set PATH=%PROGRAMFILES%\git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`! ### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bat set BUILD="shared" ``` ### Compile the library ```bash mingw32-make -C libsass ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.dll libsass.so ``` ### Run the spec test-suite ```bash mingw32-make -C libsass test_build ``` ## Building via MingGW 64bit (makefiles) Building libass to dll on window 64bit. + downloads [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) , and unzip to "C:\mingw64". + Create a batch file with this content: ```bat @echo off set PATH=C:\mingw64\bin;%PATH% set CC=gcc REM only needed if not already available set PATH=%PROGRAMFILES%\Git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` + By default , mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​" , we can modify Makefile to fix this:(add "-static") ``` bash lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(MKDIR) lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a ``` + Compile the library ```bash mingw32-make -C libsass ``` By the way , if you are using java jna , [JNAerator](http://jnaerator.googlecode.com/) is a good tool. ## Building via Visual Studio Community 2013 Open a Visual Studio 2013 command prompt: - `VS2013 x86 Native Tools Command Prompt` Note: When I installed the community edition, I only got the 2012 command prompts. I copied them from the Startmenu to the Desktop and adjusted the paths from `Visual Studio 11.0` to `Visual Studio 12.0`. Since `libsass` uses some `C++11` features, you need at least a MSVC 2013 compiler (v120). ### Get the source ```bash # using git is preferred git clone https://github.com/sass/libsass.git git clone https://github.com/sass/sassc.git libsass/sassc # only needed if you want to run the testsuite git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Compile sassc Sometimes `msbuild` seems not available from the command prompt. Just search for it and add it to the global path. It seems to be included in the .net folders too. ```bat cd libsass REM set PATH=%PATH%;%PROGRAMFILES%\MSBuild\12.0\Bin msbuild /m:4 /p:Configuration=Release win\libsass.sln REM running the spec test-suite manually (needs ruby and minitest gem) ruby sass-spec\sass-spec.rb -c win\bin\sassc.exe -s --ignore-todo sass-spec/spec cd .. ``` [1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files [2]: https://msysgit.github.io/ [3]: http://rubyinstaller.org/ libsass-3.3.4/docs/build-shared-library.md000066400000000000000000000026121267254216700204550ustar00rootroot00000000000000This page is mostly intended for people that want to build a system library that gets distributed via RPMs or other means. This is currently in a experimental phase, as we currently do not really guarantee any ABI forward compatibility. The C API was rewritten to make this possible in the future, but we want to wait some more time till we can call this final and stable. Building via autotools -- You want to build a system library only via autotools, since it will create the proper `libtool` files to make it loadable on multiple systems. We hope this works correctly, but nobody of the `libsass` core team has much knowledge in this area. Therefore we are open for comments or improvements by people that have more experience in that matter (like package maintainers from various linux distributions). ```bash apt-get install autoconf libtool git clone https://github.com/sass/libsass.git cd libsass autoreconf --force --install ./configure \ --disable-tests \ --disable-static \ --enable-shared \ --prefix=/usr make -j5 install cd .. ``` This should install these files ```bash # $ ls -la /usr/lib/libsass.* /usr/lib/libsass.la /usr/lib/libsass.so -> libsass.so.0.0.9 /usr/lib/libsass.so.0 -> libsass.so.0.0.9 /usr/lib/libsass.so.0.0.9 # $ ls -la /usr/include/sass* /usr/include/sass.h /usr/include/sass2scss.h /usr/include/sass/context.h /usr/include/sass/functions.h /usr/include/sass/values.h ``` libsass-3.3.4/docs/build-with-autotools.md000066400000000000000000000037571267254216700205620ustar00rootroot00000000000000### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Prerequisites In order to run autotools you need a few tools installed on your system. ```bash yum install automake libtool # RedHat Linux emerge -a automake libtool # Gentoo Linux pkgin install automake libtool # SmartOS ``` ### Create configure script ```bash cd libsass autoreconf --force --install cd .. ``` ### Create custom makefiles ```bash cd libsass ./configure \ --disable-tests \ --disable-shared \ --prefix=/usr cd .. ``` ### Build the library ```bash make -C libsass -j5 ``` ### Install the library The library will be installed to the location given as `prefix` to `configure`. This is standard behavior for autotools and not `libsass` specific. ```bash make -C libsass -j5 install ``` ### Configure options The `configure` script is created by autotools. To get an overview of available options you can call `./configure --help`. When you execute this script, it will create specific makefiles, which you then use via the regular make command. There are some `libsass` specific options: ``` Optional Features: --enable-tests enable testing the build --enable-coverage enable coverage report for test suite --enable-shared build shared libraries [default=yes] --enable-static build static libraries [default=yes] Optional Packages: --with-sassc-dir= specify directory of sassc sources for testing (default: sassc) --with-sass-spec-dir= specify directory of sass-spec for testing (default: sass-spec) ``` ### Build sassc and run spec test-suite ```bash cd libsass autoreconf --force --install ./configure \ --enable-tests \ --enable-shared \ --prefix=/usr make -j5 test_build cd .. ``` libsass-3.3.4/docs/build-with-makefiles.md000066400000000000000000000031001267254216700204470ustar00rootroot00000000000000### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bash export BUILD="shared" ``` Alternatively you can also define it directly when calling make: ```bash BUILD="shared" make ... ``` ### Compile the library ```bash make -C libsass -j5 ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.so ``` ### Install onto the system We recommend to use [autotools to install](build-with-autotools.md) libsass onto the system, since that brings all the benefits of using libtools as the main install method. If you still want to install libsass via the makefile, you need to make sure that gnu `install` utility (or compatible) is installed on your system. ```bash yum install coreutils # RedHat Linux emerge -a coreutils # Gentoo Linux pkgin install coreutils # SmartOS ``` You can set the install location by setting `PREFIX` ```bash PREFIX="/opt/local" make install ``` ### Compling sassc ```bash # Let build know library location export SASS_LIBSASS_PATH="`pwd`/libsass" # Invokes the sassc makefile make -C libsass -j5 sassc ``` ### Run the spec test-suite ```bash # needs ruby available # also gem install minitest make -C libsass -j5 test_build ``` libsass-3.3.4/docs/build-with-mingw.md000066400000000000000000000065541267254216700176500ustar00rootroot00000000000000## Building LibSass with MingGW (makefiles) First grab the latest [MinGW for windows] [1] installer. Once it is installed, you can click on continue or open the Installation Manager via `bin\mingw-get.exe`. You need to have the following components installed: ![](https://cloud.githubusercontent.com/assets/282293/5525466/947bf396-89e6-11e4-841d-4aa916f14de1.png) Next we need to install [git for windows] [2]. You probably want to check the option to add it to the global path, but you do not need to install the unix tools. If you want to run the spec test-suite you also need [ruby] [3] and a few gems available. Grab the [latest installer] [3] and make sure to add it the global path. Then install the missing gems: ```bash gem install minitest ``` ### Mount the mingw root directory As mentioned in the [MinGW Getting Started](http://www.mingw.org/wiki/Getting_Started#toc5) guide, you should edit `C:\MinGW\msys\1.0\etc\fstab` to contain the following line: ``` C:\MinGW /mingw ``` ### Starting a "MingGW" console Create a batch file with this content: ```bat @echo off set PATH=C:\MinGW\bin;%PATH% REM only needed if not already available set PATH=%PROGRAMFILES%\git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` Execute it and make sure these commands can be called: `git`, `mingw32-make`, `rm` and `gcc`! Once this is all set, you should be ready to compile `libsass`! ### Get the sources ```bash # using git is preferred git clone https://github.com/sass/libsass.git # only needed for sassc and/or testsuite git clone https://github.com/sass/sassc.git libsass/sassc git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` ### Decide for static or shared library `libsass` can be built and linked as a `static` or as a `shared` library. The default is `static`. To change it you can set the `BUILD` environment variable: ```bat set BUILD="shared" ``` ### Compile the library ```bash mingw32-make -C libsass ``` ### Results can be found in ```bash $ ls libsass/lib libsass.a libsass.dll libsass.so ``` ### Run the spec test-suite ```bash mingw32-make -C libsass test_build ``` ## Building via MingGW 64bit (makefiles) Building libass to dll on window 64bit. Download [MinGW64 for windows7 64bit](http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v3-rev0.7z/download) and unzip to "C:\mingw64". Create a batch file with this content: ```bat @echo off set PATH=C:\mingw64\bin;%PATH% set CC=gcc REM only needed if not already available set PATH=%PROGRAMFILES%\Git\bin;%PATH% REM C:\MinGW\msys\1.0\msys.bat cmd ``` By default, mingw64 dll will depends on "​m​i​n​g​w​m​1​0​.​d​l​l​、​ ​l​i​b​g​c​c​_​s​_​d​w​2​-​1​.​d​l​l​", we can modify Makefile to fix this:(add "-static") ``` bash lib/libsass.dll: $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(MKDIR) lib $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -static -Wl,--subsystem,windows,--out-implib,lib/libsass.a ``` Compile the library ```bash mingw32-make -C libsass ``` By the way, if you are using java jna, [JNAerator](http://jnaerator.googlecode.com/) is a good tool. [1]: http://sourceforge.net/projects/mingw/files/latest/download?source=files [2]: https://msysgit.github.io/ [3]: http://rubyinstaller.org/ libsass-3.3.4/docs/build-with-visual-studio.md000066400000000000000000000056031267254216700213310ustar00rootroot00000000000000## Building LibSass with Visual Studio ### Requirements: The minimum requirement to build LibSass with Visual Studio is "Visual Studio 2013 Express for Desktop". Additionally, it is recommended to have `git` installed and available in `PATH`, so to deduce the `libsass` version information. For instance, if GitHub for Windows (https://windows.github.com/) is installed, the `PATH` will have an entry resembling: `X:\Users\\AppData\Local\GitHub\PortableGit_\cmd\` (where `X` is the drive letter of system drive). If `git` is not available, inquiring the LibSass version will result in `[NA]`. ### Build Steps: #### From Visual Studio: On opening the `win\libsass.sln` solution and build (Ctrl+Shift+B) to build `libsass.dll`. To Build LibSass as a static Library, it is recommended to set an environment variable `LIBSASS_STATIC_LIB` before launching the project: ```cmd cd path\to\libsass SET LIBSASS_STATIC_LIB=1 :: :: or in PowerShell: :: $env:LIBSASS_STATIC_LIB=1 :: win\libsass.sln ``` Visual Studio will form the filtered source tree as shown below: ![image](https://cloud.githubusercontent.com/assets/3840695/9298985/aae9e072-44bf-11e5-89eb-e7995c098085.png) `Header Files` contains the .h and .hpp files, while `Source Files` covers `.c` and `.cpp`. The other used headers/sources will appear under `External Dependencies`. If there is a LibSass code file appearing under External Dependencies, it can be changed by altering the `win\libsass.vcxproj.filters` file or dragging in Solution Explorer. #### From Command Prompt: Notice that in the following commands: * If the platform is 32-bit Windows, replace `ProgramFiles(x86)` with `ProgramFiles`. * To build with Visual Studio 2015, replace `12.0` with `14.0` in the aforementioned command. Open a command prompt: To build dynamic/shared library (`libsass.dll`): ```cmd :: debug build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln :: release build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:Configuration=Release ``` To build static library (`libsass.lib`): ```cmd :: debug build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:LIBSASS_STATIC_LIB=1 :: release build: "%ProgramFiles(x86)%\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ^ /p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release ``` #### From PowerShell: To build dynamic/shared library (`libsass.dll`): ```powershell # debug build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln # release build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:Configuration=Release ``` To build static library (`libsass.lib`): ```powershell # build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:LIBSASS_STATIC_LIB=1 # release build: &"${env:ProgramFiles(x86)}\MSBuild\12.0\Bin\MSBuild" win\libsass.sln ` /p:LIBSASS_STATIC_LIB=1 /p:Configuration=Release ``` libsass-3.3.4/docs/build.md000066400000000000000000000113671267254216700155560ustar00rootroot00000000000000`libsass` is only a library and does not do much on its own. You need an implementation that you can use from the [command line] [6]. Or some [[bindings|Implementations]] to use it within your favorite programming language. You should be able to get [`sassc`] [6] running by following the instructions in this guide. Before starting, see [setup dev environment](setup-environment.md). Building on different Operating Systems -- We try to keep the code as OS independent and standard compliant as possible. Reading files from the file-system has some OS depending code, but will ultimately fall back to a posix compatible implementation. We do use some `C++11` features, but are so far only committed to use `unordered_map`. This means you will need a pretty recent compiler on most systems (gcc 4.5 seems to be the minimum). ### Building on Linux (and other *nix flavors) Linux is the main target for `libsass` and we support two ways to build `libsass` here. The old plain makefiles should still work on most systems (including MinGW), while the autotools build is preferred if you want to create a [system library] (experimental). - [Building with makefiles] [1] - [Building with autotools] [2] ### Building on Windows (experimental) Windows build support was added very recently and should be considered experimental. Credits go to @darrenkopp and @am11 for their work on getting `libsass` and `sassc` to compile with visual studio! - [Building with MinGW] [3] - [Building with Visual Studio] [11] ### Building on Max OS X (untested) Works the same as on linux, but you can also install LibSass via `homebrew`. - [Building on Mac OS X] [10] ### Building a system library (experimental) Since `libsass` is a library, it makes sense to install it as a shared library on your system. On linux this means creating a `.so` library via autotools. This should work pretty well already, but we are not yet committed to keep the ABI 100% stable. This should be the case once we increase the version number for the library to 1.0.0 or higher. On Windows you should be able get a `dll` by creating a shared build with MinGW. There is currently no target in the MSVC project files to do this. - [Building shared system library] [4] Compiling with clang instead of gcc -- To use clang you just need to set the appropriate environment variables: ```bash export CC=/usr/bin/clang export CXX=/usr/bin/clang++ ``` Running the spec test-suite -- We constantly and automatically test `libsass` against the official [spec test-suite] [5]. To do this we need to have a test-runner (which is written in ruby) and a command-line tool ([`sassc`] [6]) to run the tests. Therefore we need to additionally compile `sassc`. To do this, the build files of all three projects need to work together. This may not have the same quality for all build flavors. You definitely need to have ruby (2.1?) installed (version 1.9 seems to cause problems at least on windows). You also need some gems installed: ```bash ruby -v gem install minitest # should be optional gem install minitap ``` Including the LibSass version -- There is a function in `libsass` to query the current version. This has to be defined at compile time. We use a C macro for this, which can be defined by calling `g++ -DLIBSASS_VERSION="\"x.y.z.\""`. The two quotes are necessary, since it needs to end up as a valid C string. Normally you do not need to do anything if you use the makefiles or autotools. They will try to fetch the version via git directly. If you only have the sources without the git repo, you can pass the version as an environment variable to `make` or `configure`: ``` export LIBSASS_VERSION="x.y.z." ``` Continuous Integration -- We use two CI services to automatically test all commits against the latest [spec test-suite] [5]. - [LibSass on Travis-CI (linux)][7] [![Build Status](https://travis-ci.org/sass/libsass.png?branch=master)](https://travis-ci.org/sass/libsass) - [LibSass on AppVeyor (windows)][8] [![Build status](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/mgreter/libsass-513/branch/master) Why not using CMake? -- There were some efforts to get `libsass` to compile with CMake, which should make it easier to create build files for linux and windows. Unfortunately this was not completed. But we are certainly open for PRs! Miscellaneous -- - [Ebuilds for Gentoo Linux](build-on-gentoo.md) [1]: build-with-makefiles.md [2]: build-with-autotools.md [3]: build-with-mingw.md [4]: build-shared-library.md [5]: https://github.com/sass/sass-spec [6]: https://github.com/sass/sassc [7]: https://github.com/sass/libsass/blob/master/.travis.yml [8]: https://github.com/sass/libsass/blob/master/appveyor.yml [9]: implementations.md [10]: build-on-darwin.md [11]: build-with-visual-studio.md libsass-3.3.4/docs/compatibility-plan.md000066400000000000000000000055231267254216700202550ustar00rootroot00000000000000This document is to serve as a living, changing plan for getting LibSass caught up with Ruby Sass. _Note: an "s" preceeding a version number is specifying a Ruby Sass version. Without an s, it's a version of LibSass._ # Goal **Our goal is to reach full s3.4 compatibility as soon as possible. LibSass version 3.4 will behave just like Ruby Sass 3.4** I highlight the goal, because there are some things that are *not* currently priorities. To be clear, they WILL be priorities, but they are not at the moment: * Performance Improvements * Extensibility The overriding goal is correctness. ## Verifying Correctness LibSass uses the spec for its testing. The spec was originally based off s3.2 tests. Many things have changed in Ruby Sass since then and some of the tests need to be updated and changed in order to get them to match both LibSass and Ruby Sass. Until this project is complete, the spec will be primarily a place to test LibSass. By the time LibSass reaches 3.4, it is our goal that sass-spec will be fully usable as an official testing source for ALL implementations of Sass. ## Version Naming Until LibSass reaches parity with Ruby Sass, we will be aggressively bumping versions, and LibSass 3.4 will be the peer to Ruby Sass 3.4 in every way. # Release Plan ## 3.0 The goal of 3.0 is to introduce some of the most demanded features for LibSass. That is, we are focusing on issues and features that have kept adoption down. This is a mongrel release wrt which version of Sass it's targeting. It's often a mixture of 3.2 / 3.3 / 3.4 behaviours. This is not ideal, but it's favourable to not existing. Targeting 3.4 strictly during this release would mean we never actually release. # 3.1 The goal of 3.1 is to update all the passing specs to agree with 3.4. This will not be a complete representation of s3.4 (aka, there will me missing features), but the goal is to change existing features and implemented features to match 3.4 behaviour. By the end of this, the sass-spec must pass against 3.4. Major issues: * Variable Scoping * Color Handling * Precision # 3.2 This version will focus on edge case fixes. There are a LOT of edge cases in the _todo_ tests and this is the release where we hunt those down like dogs (not that we want to hurt dogs, it's just a figure of speech in English). # 3.3 Dress rehearsal. When we are 99% sure that we've fixed the main issues keeping us from saying we are compliant in s3.4 behaviour. # 3.4 Compass Compatibility. We need to be able to work with Compass and all the other libraries out there. At this point, we are calling LibSass "mature" # Beyond 3.4 Obviously, there is matching Sass 3.5 behaviour. But, beyond that, we'll want to focus on performance, stability, and error handling. These can always be improved upon and are the life's work of an open source project. We'll have to work closely with Sass in the future. libsass-3.3.4/docs/contributing.md000066400000000000000000000025751267254216700171670ustar00rootroot00000000000000First of all, welcome! Thanks for even reading this page. If you're here, you're probably wondering what you can do to help make the LibSass project even more awesome. And, even having that feeling means you are awesome! ## I'm a programmer Awesome! We need your help. The best thing to do is go find issues that are tagged with both "bug" and "test written". We do spec driven development here and these issues have a test that's written already in the sass-spec project. Go find the test by going to sass-spec/spec/LibSass-todo-issues/issue_XXX/ where XXX is the issue number. Write the code, and compile, and then issue a pull request referencing the issue. We'll quickly verify it and get it merged in! To get your dev environment setup, check out our article on [Setup-Dev-Environment](setup-environment.md). ## I'm not a backend programmer COOL! We also need your help. Doing [Issue-Triage](triage.md) is a big deal and something we need constant help with. That means helping to verify issues, write tests for them, and make sure they are getting fixed. It's being part of the smiling face of the project. Also, we need help with the Sass-Spec project itself. Just people to organize, refactor, and understand the tests in there. ## I don't know what a computer is? Hmm.... well, it's the thing you are looking at right now. Ummm... check out training courses! Then, come back and join us! libsass-3.3.4/docs/custom-functions-internal.md000066400000000000000000000100271267254216700216010ustar00rootroot00000000000000# Developer Documentation Custom functions are internally represented by `struct Sass_C_Function_Descriptor`. ## Sass_C_Function_Descriptor ```C struct Sass_C_Function_Descriptor { const char* signature; Sass_C_Function function; void* cookie; }; ``` - `signature`: The function declaration, like `foo($bar, $baz:1)` - `function`: Reference to the C function callback - `cookie`: any pointer you want to attach ### signature The signature defines how the function can be invoked. It also declares which arguments are required and which are optional. Required arguments will be enforced by LibSass and a Sass error is thrown in the event a call as missing an argument. Optional arguments only need to be present when you want to overwrite the default value. foo($bar, $baz: 2) In this example, `$bar` is required and will error if not passed. `$baz` is optional and the default value of it is 2. A call like `foo(10)` is therefore equal to `foo(10, 2)`, while `foo()` will produce an error. ### function The callback function needs to be of the following form: ```C union Sass_Value* call_sass_function( const union Sass_Value* s_args, void* cookie ) { return sass_clone_value(s_args); } ``` ### cookie The cookie can hold any pointer you want. In the `perl-libsass` implementation it holds the structure with the reference of the actual registered callback into the perl interpreter. Before that call `perl-libsass` will convert all `Sass_Values` to corresponding perl data types (so they can be used natively inside the perl interpretor). The callback can also return a `Sass_Value`. In `perl-libsass` the actual function returns a perl value, which has to be converted before `libsass` can work with it again! ## Sass_Values ```C // allocate memory (copies passed strings) union Sass_Value* make_sass_boolean (int val); union Sass_Value* make_sass_number (double val, const char* unit); union Sass_Value* make_sass_color (double r, double g, double b, double a); union Sass_Value* make_sass_string (const char* val); union Sass_Value* make_sass_list (size_t len, enum Sass_Separator sep); union Sass_Value* make_sass_map (size_t len); union Sass_Value* make_sass_null (); union Sass_Value* make_sass_error (const char* msg); // Make a deep cloned copy of the given sass value union Sass_Value* sass_clone_value (const union Sass_Value* val); // deallocate memory (incl. all copied memory) void sass_delete_value (const union Sass_Value* val); ``` ## Example main.c ```C #include #include #include "sass/context.h" union Sass_Value* call_fn_foo(const union Sass_Value* s_args, void* cookie) { // we actually abuse the void* to store an "int" return sass_make_number((size_t)cookie, "px"); } int main( int argc, const char* argv[] ) { // get the input file from first argument or use default const char* input = argc > 1 ? argv[1] : "styles.scss"; // create the file context and get all related structs struct Sass_File_Context* file_ctx = sass_make_file_context(input); struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); struct Sass_Options* ctx_opt = sass_context_get_options(ctx); // allocate a custom function caller Sass_C_Function_Callback fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_C_Function_List fn_list = sass_make_function_list(1); sass_function_set_list_entry(fn_list, 0, fn_foo); sass_option_set_c_functions(ctx_opt, fn_list); // context is set up, call the compile step now int status = sass_compile_file_context(file_ctx); // print the result or the error to the stdout if (status == 0) puts(sass_context_get_output_string(ctx)); else puts(sass_context_get_error_message(ctx)); // release allocated memory sass_delete_file_context(file_ctx); // exit status return status; } ``` ## Compile main.c ```bash gcc -c main.c -o main.o gcc -o sample main.o -lsass echo "foo { margin: foo(); }" > foo.scss ./sample foo.scss => "foo { margin: 42px }" ``` libsass-3.3.4/docs/implementations.md000066400000000000000000000027451267254216700176670ustar00rootroot00000000000000There are several implementations of `libsass` for a variety of languages. Here are just a few of them. Note, some implementations may or may not be up to date. We have not verified whether they work. ### C * [sassc](https://github.com/hcatlin/sassc) ### Go * [go-libsass](https://github.com/wellington/go-libsass) * [go_sass](https://github.com/suapapa/go_sass) * [go-sass](https://github.com/SamWhited/go-sass) ### Lua * [lua-sass](https://github.com/craigbarnes/lua-sass) ### .NET * [libsass-net](https://github.com/darrenkopp/libsass-net) * [NSass](https://github.com/TBAPI-0KA/NSass) * [Sass.Net](https://github.com/andyalm/Sass.Net) ### node.js * [node-sass](https://github.com/andrew/node-sass) ### Java * [libsass-maven-plugin](https://github.com/warmuuh/libsass-maven-plugin) * [jsass](https://github.com/bit3/jsass) ### JavaScript * [sass.js](https://github.com/medialize/sass.js) ### Perl * [CSS::Sass](https://github.com/caldwell/CSS-Sass) * [Text::Sass::XS](https://github.com/ysasaki/Text-Sass-XS) ### PHP * [sassphp](https://github.com/sensational/sassphp) ### Python * [libsass-python](https://github.com/dahlia/libsass-python) * [SassPython](https://github.com/marianoguerra/SassPython) * [pylibsass](https://github.com/rsenk330/pylibsass) * [python-scss](https://github.com/pistolero/python-scss) ### Ruby * [sassruby](https://github.com/hcatlin/sassruby) ### Scala * [Sass-Scala](https://github.com/kkung/Sass-Scala) ### Tcl * [tclsass](https://github.com/flightaware/tclsass) libsass-3.3.4/docs/plugins.md000066400000000000000000000035731267254216700161400ustar00rootroot00000000000000Plugins are shared object files (.so on *nix and .dll on win) that can be loaded by LibSass on runtime. Currently we only provide a way to load internal/custom functions from plugins. In the future we probably will also add a way to provide custom importers via plugins (needs more refactoring to [support multiple importers with some kind of priority system](https://github.com/sass/libsass/issues/962)). ## plugin.cpp ```C++ #include #include #include #include "sass_values.h" union Sass_Value* ADDCALL call_fn_foo(const union Sass_Value* s_args, void* cookie) { // we actually abuse the void* to store an "int" return sass_make_number((intptr_t)cookie, "px"); } extern "C" const char* ADDCALL libsass_get_version() { return libsass_version(); } extern "C" Sass_C_Function_List ADDCALL libsass_load_functions() { // allocate a custom function caller Sass_C_Function_Callback fn_foo = sass_make_function("foo()", call_fn_foo, (void*)42); // create list of all custom functions Sass_C_Function_List fn_list = sass_make_function_list(1); // put the only function in this plugin to the list sass_function_set_list_entry(fn_list, 0, fn_foo); // return the list return fn_list; } ``` To compile the plugin you need to have LibSass already built as a shared library (to link against it). The commands below expect the shared library in the `lib` sub-directory (`-Llib`). The plugin and the main LibSass process should "consume" the same shared LibSass library on runtime. It will propably also work if they use different LibSass versions. In this case we check if the major versions are compatible (i.e. 3.1.3 and 3.1.1 would be considered compatible). ## Compile with gcc on linux ```bash g++ -O2 -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass ``` ## Compile with mingw on windows ```bash g++ -O2 -shared plugin.cpp -o plugin.dll -Llib -lsass ``` libsass-3.3.4/docs/setup-environment.md000066400000000000000000000046311267254216700201550ustar00rootroot00000000000000## Requirements In order to install and setup your local development environment, there are some prerequisites: * git * gcc/clang/llvm (Linux: build tools, Mac OS X: XCode w/ Command Line Tools) * ruby w/ bundler OS X: First you'll need to install XCode which you can now get from the AppStore installed on your mac. After you download that and run it, then run this on the command line: ```` xcode-select --install ```` ## Cloning the Projects First, clone the project and then add a line to your `~/.bash_profile` that will let other programs know where the LibSass dev files are. ```` git clone git@github.com:sass/libsass.git cd libsass echo "export SASS_LIBSASS_PATH=$(pwd)" >> ~/.bash_profile ```` Then, if you run the "bootstrap" script, it should clone all the other required projects. ```` ./script/bootstrap ```` You should now have a `sass-spec` and `sassc` folder within the libsass folder. Both of these are clones of their respective git projects. If you want to do a pull request, remember to work in those folders. For instance, if you want to add a test (see other documentation for how to do that), make sure to commit it to your *fork* of the sass-spec github project. Also, whenever you are running tests, make sure to `pull` from the origin! We want to make sure we are testing against the newest libsass, sassc, and sass-spec! Now, try and see if you can build the project. We do that with the `make` command. ```` make ```` At this point, if you get an error, something is most likely wrong with your compiler installation. Yikes. It's hard to cover how to fix this in an article. Feel free to open an issue and we'll try and help! But, remember, before you do that, googling the error message is your friend! Many problems are solved quickly that way. ## Running The Spec Against LibSass Then, to run the spec against LibSass, just run: ```` ./script/spec ```` If you get an error about `SASS_LIBSASS_PATH`, you may still need to set a variable pointing to the libsass folder, like this: ```` export SASS_LIBSASS_PATH=/Users/you/path/libsass ```` ...where the latter part is to the `libsass` directory you've cloned. You can get this path by typing `pwd` in the Terminal ## Running the Spec Against Ruby Sass Go into the sass-spec folder that should have been cloned earlier with the "bootstrap" command. Run the following. ```` bundle install ./sass-spec.rb ```` Voila! Now you are testing against Sass too! libsass-3.3.4/docs/source-map-internals.md000066400000000000000000000044251267254216700205240ustar00rootroot00000000000000This document is mainly intended for developers! # Documenting some of the source map internals Since source maps are somewhat a black box to all LibSass maintainers, [I](@mgreter) will try to document my findings with source maps in LibSass, as I come across them. This document will also brievely explain how LibSass parses the source and how it outputs the result. The main storage for SourceMap mappings is the `mappings` vector: ``` # in source_map.hpp vector mappings # in mappings.hpp struct Mapping ... Position original_position; Position generated_position; ``` ## Every parsed token has its source associated LibSass uses a lexical parser. Whenever LibSass finds a token of interest, it creates a specific `AST_Node`, which will hold a reference to the input source with line/column information. `AST_Node` is the base class for all parsed items. They are declared in `ast.hpp` and are used in `parser.hpp`. Here a simple example: ``` if (lex< custom_property_name >()) { Sass::String* prop = new (ctx.mem) String_Constant(path, source_position, lexed); return new (ctx.mem) Declaration(path, prop->position(), prop, ...); } ``` ## How is the `source_position` calculated This is automatically done with `lex` in `parser.hpp`. Whenever something is lexed, the `source_position` is updated. But be aware that `source_position` points to the begining of the parsed text. If you need a mapping for the position where the parsing ended, you need to add another call to `lex` (to match nothing)! ``` lex< exactly < empty_str > >(); end = new (ctx.mem) String_Constant(path, source_position, lexed); ``` ## How are mappings for the output created So far we have collected all needed data for all tokens in the input stream. We can now use this information to create mappings when we put things into the output stream. Mappings are created via the `add_mappings` method: ``` # in source_map.hpp void add_mapping(AST_Node* node); ``` This method is called in two places: - `Inspect::append_to_buffer` - `Output_[Nested|Compressed]::append_to_buffer` Mappings can only be created for things that have been parsed into a `AST_Node`. Otherwise we do not have the information to create the mappings, which is the reason why LibSass currently only maps the most important tokens in source maps. libsass-3.3.4/docs/trace.md000066400000000000000000000015741267254216700155540ustar00rootroot00000000000000## This is proposed interface in https://github.com/sass/libsass/pull/1288 Additional debugging macros with low overhead are available, `TRACE()` and `TRACEINST()`. Both macros simulate a string stream, so they can be used like this: TRACE() << "Reached."; produces: [LibSass] parse_value parser.cpp:1384 Reached. `TRACE()` logs function name, source filename, source file name to the standard error and the attached stream to the standard error. `TRACEINST(obj)` logs object instance address, function name, source filename, source file name to the standard error and the attached stream to the standard error, for example: TRACEINST(this) << "String_Constant created " << this; produces: [LibSass] 0x8031ba980:String_Constant ./ast.hpp:1371 String_Constant created (0,"auto") The macros generate output only of `LibSass_TRACE` is set in the environment. libsass-3.3.4/docs/triage.md000066400000000000000000000033551267254216700157300ustar00rootroot00000000000000This is an article about how to help with LibSass issues. Issue triage is a fancy word for explaining how we deal with incoming issues and make sure that the right problems get worked on. The lifecycle of an issue goes like this: 1. Issue is reported by a user. 2. If the issue seems like a bug, then the "bug" tag is added. 3. If the reporting user didn't also create a spec test over at sass/sass-spec, the "needs test" tag is added. 4. Verify that Ruby Sass *does not* have the same bug. LibSass strives to be an exact replica of how Ruby Sass works. If it's an issue that neither project has solved, please close the ticket with the "not in sass" label. 5. The smallest possible breaking test is created in sass-spec. Cut away any extra information or non-breaking code until the core issue is made clear. 6. Again, verify that the expected output matches the latest Ruby Sass release. Do this by using your own tool OR by running ./sass-spec.rb in the spec folder and making sure that your test passes! 7. Create the test cases in sass-spec with the name spec/LibSass-todo-issues/issue_XXX/input.scss and expected_output.css where the XXX is the issue number here. 8. Commit that test to sass-spec, making sure to reference the issue in the comment message like "Test to demonstrate sass/LibSass#XXX". 9. Once the spec test exists, remove the "needs test" tag and replace it with "test written". 10. A C++ developer will then work on the issue and issue a pull request to fix the issue. 11. A core member verifies that the fix does actually fix the spec tests. 12. The fix is merged into the project. 13. The spec is moved from the LibSass-todo-issues folder into LibSass-closed-issues 14. The issue is closed 15. Have a soda pop or enjoyable beverage of your choice libsass-3.3.4/docs/unicode.md000066400000000000000000000075751267254216700161130ustar00rootroot00000000000000LibSass currently expects all input to be utf8 encoded (and outputs only utf8), if you actually have any unicode characters at all. We do not support conversion between encodings, even if you declare it with a `@charset` rule. The text below was originally posted as an [issue](https://github.com/sass/libsass/issues/381) on the LibSass tracker. ### [Declaring character encodings in CSS](http://www.w3.org/International/questions/qa-css-charset.en) This [explains](http://www.w3.org/International/questions/qa-css-charset.en) how the character encoding of a css file is determined. Since we are only dealing with local files, we never have a HTTP header. So the precedence should be 'charset' rule, byte-order mark (BOM) or auto-detection (finally falling back to system default/UTF-8). This may not sound too hard to implement, but what about import rules? The CSS specs do not forbid the mixing of different encodings! I solved that by converting all files to UTF-8 internally. On writing there is an option to tell the tool what encoding it should be (UTF-8 by default). One can also define if it should write a BOM or not and if it should add the charset declaration. Since my tool is written in perl, I have a lot of utilities at hand to deal with different unicode charsets. I'm pretty sure that most OSS uses [libiconv](https://www.gnu.org/software/libiconv/) to convert between different encodings. But I have now idea how easy/hard this would be to integrate platform independent (it seems doable). ### Current status on LibSass unicode support Currently LibSass seems to handle the common UTF-8 case pretty well. I believe it should correctly support all ASCII compatible encodings (like UTF-8 or Latin-1). If all includes use the same encoding, the output should be correct (in the same encoding). It should also handle unicode chars in [selectors, variable names and other identifiers](https://github.com/hcatlin/libsass/issues/244#issuecomment-34681227). This is true for all ASCII compatible encodings. So the main incompatible encodings (I'm aware of) are UTF-16/UTF-32 (which could be converted to UTF-8 with libiconv). ### Current encoding auto detection LibSass currently reads all kind of BOMs and will error out if it finds something it doesn't know how to handle! It seems that it throws away the optional UTF-8 BOM (if any is found). IMO it would be nice if users could configure that (also if a charset rule should be added to the output). ### What is currently not supported - Using non ASCII compatible encodings (like UTF-16) - Using non ASCII characters in different encodings in different includes ### What is missing to support the above cases - A way to convert between encodings (like libiconv) - Sniffing the charset inside the file (source is available) - Handling the conversion on import (and export) - Optional: Make output encoding configurable - Optional: Add optional/mandatory BOM (configurable) ### Low priority feature I guess the current implementation should handle more than 99% of all real world use cases. A) Unicode characters are still seldomly seen (as they can be written escaped) B) It will still work if it's UTF-8 or in any of the most common known western ISO codepages. Although I'm not sure how this applies to asian and other "exotic" codepages! I guess the biggest Problem is to have libiconv (or some other) library as a dependency. Since it contains a lot of rules for the conversions, I see it as the only way to handle this correctly. Once that is sorted out it should be pretty much straight forward to implement the missing pieces (in parser.cpp - Parser::parse should return encoding and add Parser::sniff_charset, then convert the source byte stream to UTF-8). I hope the statements above all hold true. Unicode is really not the easiest topic to wrap your head around. But since I did all the above recently in Perl, I wanted to document it here. Feel free to extend or criticize. libsass-3.3.4/extconf.rb000066400000000000000000000002351267254216700151700ustar00rootroot00000000000000require 'mkmf' # .. more stuff #$LIBPATH.push(Config::CONFIG['libdir']) $CFLAGS << " #{ENV["CFLAGS"]}" $LIBS << " #{ENV["LIBS"]}" create_makefile("libsass") libsass-3.3.4/include/000077500000000000000000000000001267254216700146205ustar00rootroot00000000000000libsass-3.3.4/include/sass.h000066400000000000000000000003521267254216700157420ustar00rootroot00000000000000#ifndef SASS_H #define SASS_H // #define DEBUG 1 // include API headers #include #include #include #include #include #include #endif libsass-3.3.4/include/sass/000077500000000000000000000000001267254216700155715ustar00rootroot00000000000000libsass-3.3.4/include/sass/base.h000066400000000000000000000034511267254216700166570ustar00rootroot00000000000000#ifndef SASS_BASE_H #define SASS_BASE_H #ifdef _MSC_VER #pragma warning(disable : 4503) #ifndef _SCL_SECURE_NO_WARNINGS #define _SCL_SECURE_NO_WARNINGS #endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #include #include #ifdef __GNUC__ #define DEPRECATED(func) func __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define DEPRECATED(func) __declspec(deprecated) func #else #pragma message("WARNING: You need to implement DEPRECATED for this compiler") #define DEPRECATED(func) func #endif #ifdef _WIN32 /* You should define ADD_EXPORTS *only* when building the DLL. */ #ifdef ADD_EXPORTS #define ADDAPI __declspec(dllexport) #define ADDCALL __cdecl #else #define ADDAPI #define ADDCALL #endif #else /* _WIN32 not defined. */ /* Define with no value on non-Windows OSes. */ #define ADDAPI #define ADDCALL #endif /* Make sure functions are exported with C linkage under C++ compilers. */ #ifdef __cplusplus extern "C" { #endif // Different render styles enum Sass_Output_Style { SASS_STYLE_NESTED, SASS_STYLE_EXPANDED, SASS_STYLE_COMPACT, SASS_STYLE_COMPRESSED, // only used internaly SASS_STYLE_INSPECT, SASS_STYLE_TO_SASS }; // Some convenient string helper function ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark); ADDAPI char* ADDCALL sass_string_unquote (const char* str); // Resolve a file via the given include paths in the include char* array ADDAPI char* ADDCALL sass_resolve_file (const char* path, const char* incs[]); // Get compiled libsass version ADDAPI const char* ADDCALL libsass_version(void); #ifdef __cplusplus } // __cplusplus defined. #endif #endif libsass-3.3.4/include/sass/context.h000066400000000000000000000216151267254216700174330ustar00rootroot00000000000000#ifndef SASS_C_CONTEXT_H #define SASS_C_CONTEXT_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration struct Sass_Compiler; // Forward declaration struct Sass_Options; // base struct struct Sass_Context; // : Sass_Options struct Sass_File_Context; // : Sass_Context struct Sass_Data_Context; // : Sass_Context // Compiler states enum Sass_Compiler_State { SASS_COMPILER_CREATED, SASS_COMPILER_PARSED, SASS_COMPILER_EXECUTED }; // Create and initialize an option struct ADDAPI struct Sass_Options* ADDCALL sass_make_options (void); // Create and initialize a specific context ADDAPI struct Sass_File_Context* ADDCALL sass_make_file_context (const char* input_path); ADDAPI struct Sass_Data_Context* ADDCALL sass_make_data_context (char* source_string); // Call the compilation step for the specific context ADDAPI int ADDCALL sass_compile_file_context (struct Sass_File_Context* ctx); ADDAPI int ADDCALL sass_compile_data_context (struct Sass_Data_Context* ctx); // Create a sass compiler instance for more control ADDAPI struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx); 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 ADDAPI int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler); ADDAPI int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler); // Release all memory allocated with the compiler // This does _not_ include any contexts or options ADDAPI void ADDCALL sass_delete_compiler(struct Sass_Compiler* compiler); // Release all memory allocated and also ourself ADDAPI void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx); ADDAPI void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx); // Getters for context from specific implementation ADDAPI struct Sass_Context* ADDCALL sass_file_context_get_context (struct Sass_File_Context* file_ctx); ADDAPI struct Sass_Context* ADDCALL sass_data_context_get_context (struct Sass_Data_Context* data_ctx); // Getters for Context_Options from Sass_Context ADDAPI struct Sass_Options* ADDCALL sass_context_get_options (struct Sass_Context* ctx); ADDAPI struct Sass_Options* ADDCALL sass_file_context_get_options (struct Sass_File_Context* file_ctx); ADDAPI struct Sass_Options* ADDCALL sass_data_context_get_options (struct Sass_Data_Context* data_ctx); ADDAPI void ADDCALL sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt); ADDAPI void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt); // Getters for Context_Option values ADDAPI int ADDCALL sass_option_get_precision (struct Sass_Options* options); ADDAPI enum Sass_Output_Style ADDCALL sass_option_get_output_style (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_comments (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_embed (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_source_map_contents (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_omit_source_map_url (struct Sass_Options* options); ADDAPI bool ADDCALL sass_option_get_is_indented_syntax_src (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_indent (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_linefeed (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_input_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_output_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_plugin_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_include_path (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_file (struct Sass_Options* options); ADDAPI const char* ADDCALL sass_option_get_source_map_root (struct Sass_Options* options); ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_headers (struct Sass_Options* options); ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_importers (struct Sass_Options* options); ADDAPI Sass_Function_List ADDCALL sass_option_get_c_functions (struct Sass_Options* options); // Setters for Context_Option values ADDAPI void ADDCALL sass_option_set_precision (struct Sass_Options* options, int precision); ADDAPI void ADDCALL sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style); ADDAPI void ADDCALL sass_option_set_source_comments (struct Sass_Options* options, bool source_comments); ADDAPI void ADDCALL sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed); ADDAPI void ADDCALL sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents); ADDAPI void ADDCALL sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url); ADDAPI void ADDCALL sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src); ADDAPI void ADDCALL sass_option_set_indent (struct Sass_Options* options, const char* indent); ADDAPI void ADDCALL sass_option_set_linefeed (struct Sass_Options* options, const char* linefeed); ADDAPI void ADDCALL sass_option_set_input_path (struct Sass_Options* options, const char* input_path); ADDAPI void ADDCALL sass_option_set_output_path (struct Sass_Options* options, const char* output_path); ADDAPI void ADDCALL sass_option_set_plugin_path (struct Sass_Options* options, const char* plugin_path); ADDAPI void ADDCALL sass_option_set_include_path (struct Sass_Options* options, const char* include_path); ADDAPI void ADDCALL sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file); ADDAPI void ADDCALL sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root); ADDAPI void ADDCALL sass_option_set_c_headers (struct Sass_Options* options, Sass_Importer_List c_headers); ADDAPI void ADDCALL sass_option_set_c_importers (struct Sass_Options* options, Sass_Importer_List c_importers); ADDAPI void ADDCALL sass_option_set_c_functions (struct Sass_Options* options, Sass_Function_List c_functions); // Getters for Sass_Context values ADDAPI const char* ADDCALL sass_context_get_output_string (struct Sass_Context* ctx); ADDAPI int ADDCALL sass_context_get_error_status (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_json (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_text (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_message (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_file (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_error_src (struct Sass_Context* ctx); ADDAPI size_t ADDCALL sass_context_get_error_line (struct Sass_Context* ctx); ADDAPI size_t ADDCALL sass_context_get_error_column (struct Sass_Context* ctx); ADDAPI const char* ADDCALL sass_context_get_source_map_string (struct Sass_Context* ctx); ADDAPI char** ADDCALL sass_context_get_included_files (struct Sass_Context* ctx); // Calculate the size of the stored null terminated array ADDAPI size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx); // Take ownership of memory (value on context is set to 0) ADDAPI char* ADDCALL sass_context_take_error_json (struct Sass_Context* ctx); 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_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); // Getters for Sass_Compiler options ADDAPI enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler); ADDAPI struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler); ADDAPI struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler); ADDAPI size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler); ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx); // Push function for paths (no manipulation support for now) ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path); ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path); #ifdef __cplusplus } // __cplusplus defined. #endif #endif libsass-3.3.4/include/sass/functions.h000066400000000000000000000116751267254216700177640ustar00rootroot00000000000000#ifndef SASS_C_FUNCTIONS_H #define SASS_C_FUNCTIONS_H #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration struct Sass_Import; struct Sass_Options; struct Sass_Compiler; struct Sass_Importer; struct Sass_Function; // Typedef helpers for import lists typedef struct Sass_Import (*Sass_Import_Entry); typedef struct Sass_Import* (*Sass_Import_List); // Typedef helpers for custom importer lists typedef struct Sass_Importer (*Sass_Importer_Entry); typedef struct Sass_Importer* (*Sass_Importer_List); // Typedef defining importer signature and return type typedef Sass_Import_List (*Sass_Importer_Fn) (const char* url, Sass_Importer_Entry cb, struct Sass_Compiler* compiler); // Typedef helpers for custom functions lists typedef struct Sass_Function (*Sass_Function_Entry); typedef struct Sass_Function* (*Sass_Function_List); // Typedef defining function signature and return type typedef union Sass_Value* (*Sass_Function_Fn) (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Compiler* compiler); // Creator for sass custom importer return argument list ADDAPI Sass_Importer_List ADDCALL sass_make_importer_list (size_t length); ADDAPI Sass_Importer_Entry ADDCALL sass_importer_get_list_entry (Sass_Importer_List list, size_t idx); ADDAPI void ADDCALL sass_importer_set_list_entry (Sass_Importer_List list, size_t idx, Sass_Importer_Entry entry); // Creators for custom importer callback (with some additional pointer) // The pointer is mostly used to store the callback into the actual binding ADDAPI Sass_Importer_Entry ADDCALL sass_make_importer (Sass_Importer_Fn importer, double priority, void* cookie); // Getters for import function descriptors ADDAPI Sass_Importer_Fn ADDCALL sass_importer_get_function (Sass_Importer_Entry cb); ADDAPI double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb); ADDAPI void* ADDCALL sass_importer_get_cookie (Sass_Importer_Entry cb); // Deallocator for associated memory ADDAPI void ADDCALL sass_delete_importer (Sass_Importer_Entry cb); // Creator for sass custom importer return argument list ADDAPI Sass_Import_List ADDCALL sass_make_import_list (size_t length); // Creator for a single import entry returned by the custom importer inside the list ADDAPI Sass_Import_Entry ADDCALL sass_make_import_entry (const char* path, char* source, char* srcmap); ADDAPI Sass_Import_Entry ADDCALL sass_make_import (const char* imp_path, const char* abs_base, char* source, char* srcmap); // set error message to abort import and to print out a message (path from existing object is used in output) ADDAPI Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col); // Setters to insert an entry into the import list (you may also use [] access directly) // Since we are dealing with pointers they should have a guaranteed and fixed size ADDAPI void ADDCALL sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry); ADDAPI Sass_Import_Entry ADDCALL sass_import_get_list_entry (Sass_Import_List list, size_t idx); // Getters for import entry ADDAPI const char* ADDCALL sass_import_get_imp_path (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_abs_path (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_source (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_srcmap (Sass_Import_Entry); // Explicit functions to take ownership of these items // The property on our struct will be reset to NULL ADDAPI char* ADDCALL sass_import_take_source (Sass_Import_Entry); ADDAPI char* ADDCALL sass_import_take_srcmap (Sass_Import_Entry); // Getters from import error entry ADDAPI size_t ADDCALL sass_import_get_error_line (Sass_Import_Entry); ADDAPI size_t ADDCALL sass_import_get_error_column (Sass_Import_Entry); ADDAPI const char* ADDCALL sass_import_get_error_message (Sass_Import_Entry); // Deallocator for associated memory (incl. entries) ADDAPI void ADDCALL sass_delete_import_list (Sass_Import_List); // Just in case we have some stray import structs ADDAPI void ADDCALL sass_delete_import (Sass_Import_Entry); // Creators for sass function list and function descriptors ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length); ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie); // Setters and getters for callbacks on function lists ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos); ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb); // Getters for custom function descriptors ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb); ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb); ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb); #ifdef __cplusplus } // __cplusplus defined. #endif #endif libsass-3.3.4/include/sass/interface.h000066400000000000000000000052401267254216700177030ustar00rootroot00000000000000#ifndef SASS_C_INTERFACE_H #define SASS_C_INTERFACE_H // the API in this header has been deprecated // please use the new API from sass/context.h #include #include #include #ifdef __cplusplus extern "C" { #endif // Please ensure there are no null values. // Thar be dragons. struct sass_options { // Output style for the generated css code // A value from above SASS_STYLE_* constants int output_style; // If you want inline source comments bool source_comments; // Path to source map file // Enables the source map generating // Used to create sourceMappingUrl const char* source_map_file; // Disable sourceMappingUrl in css output bool omit_source_map_url; // embed sourceMappingUrl as data uri bool source_map_embed; // embed include contents in maps bool source_map_contents; // Pass-through as sourceRoot property const char* source_map_root; // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; // Colon-separated list of paths // Semicolon-separated on Windows const char* include_paths; const char* plugin_paths; // String to be used for indentation const char* indent; // String to be used to for line feeds const char* linefeed; // Precision for outputting fractional numbers int precision; }; struct sass_context { const char* input_path; const char* output_path; char* source_string; char* output_string; char* source_map_string; struct sass_options options; int error_status; char* error_message; Sass_Function_List c_functions; char** included_files; int num_included_files; }; struct sass_file_context { const char* input_path; const char* output_path; char* output_string; char* source_map_string; struct sass_options options; int error_status; char* error_message; Sass_Function_List c_functions; char** included_files; int num_included_files; }; struct sass_folder_context { const char* search_path; const char* output_path; struct sass_options options; int error_status; char* error_message; Sass_Function_List c_functions; char** included_files; int num_included_files; }; struct sass_context* sass_new_context (void); struct sass_file_context* sass_new_file_context (void); struct sass_folder_context* sass_new_folder_context (void); void sass_free_context (struct sass_context* ctx); void sass_free_file_context (struct sass_file_context* ctx); void sass_free_folder_context(struct sass_folder_context* ctx); int sass_compile (struct sass_context* ctx); int sass_compile_file (struct sass_file_context* ctx); int sass_compile_folder (struct sass_folder_context* ctx); #ifdef __cplusplus } #endif #endif libsass-3.3.4/include/sass/values.h000066400000000000000000000140441267254216700172440ustar00rootroot00000000000000#ifndef SASS_C_VALUES_H #define SASS_C_VALUES_H #include #include #include #ifdef __cplusplus extern "C" { #endif // Forward declaration union Sass_Value; // Type for Sass values enum Sass_Tag { SASS_BOOLEAN, SASS_NUMBER, SASS_COLOR, SASS_STRING, SASS_LIST, SASS_MAP, SASS_NULL, SASS_ERROR, SASS_WARNING }; // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA, SASS_SPACE, SASS_HASH }; // Value Operators enum Sass_OP { AND, OR, // logical connectives EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations ADD, SUB, MUL, DIV, MOD, // arithmetic functions NUM_OPS // so we know how big to make the op table }; // Return the sass tag for a generic sass value // Check is needed before accessing specific values! ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v); // Check value to be of a specific type // Can also be used before accessing properties! ADDAPI bool ADDCALL sass_value_is_null (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_number (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_string (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_boolean (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_color (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_list (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_map (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_error (const union Sass_Value* v); ADDAPI bool ADDCALL sass_value_is_warning (const union Sass_Value* v); // Getters and setters for Sass_Number ADDAPI double ADDCALL sass_number_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_number_set_value (union Sass_Value* v, double value); ADDAPI const char* ADDCALL sass_number_get_unit (const union Sass_Value* v); ADDAPI void ADDCALL sass_number_set_unit (union Sass_Value* v, char* unit); // Getters and setters for Sass_String ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_string_set_value (union Sass_Value* v, char* value); ADDAPI bool ADDCALL sass_string_is_quoted(const union Sass_Value* v); ADDAPI void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted); // Getters and setters for Sass_Boolean ADDAPI bool ADDCALL sass_boolean_get_value (const union Sass_Value* v); ADDAPI void ADDCALL sass_boolean_set_value (union Sass_Value* v, bool value); // Getters and setters for Sass_Color ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r); ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_g (union Sass_Value* v, double g); ADDAPI double ADDCALL sass_color_get_b (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_b (union Sass_Value* v, double b); ADDAPI double ADDCALL sass_color_get_a (const union Sass_Value* v); ADDAPI void ADDCALL sass_color_set_a (union Sass_Value* v, double a); // Getter for the number of items in list ADDAPI size_t ADDCALL sass_list_get_length (const union Sass_Value* v); // Getters and setters for Sass_List ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v); ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value); // Getters and setters for Sass_List values ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value); // Getter for the number of items in map ADDAPI size_t ADDCALL sass_map_get_length (const union Sass_Value* v); // Getters and setters for Sass_Map keys and values ADDAPI union Sass_Value* ADDCALL sass_map_get_key (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*); ADDAPI union Sass_Value* ADDCALL sass_map_get_value (const union Sass_Value* v, size_t i); ADDAPI void ADDCALL sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*); // Getters and setters for Sass_Error ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg); // Getters and setters for Sass_Warning ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v); ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg); // Creator functions for all value types ADDAPI union Sass_Value* ADDCALL sass_make_null (void); ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val); ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val); ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val); ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit); ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a); ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep); ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len); ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg); ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg); // Generic destructor function for all types // Will release memory of all associated Sass_Values // Means we will delete recursively for lists and maps ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val); // Make a deep cloned copy of the given sass value ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val); // Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING) ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision); // Execute an operation for two Sass_Values and return the result as a Sass_Value too ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b); #ifdef __cplusplus } // __cplusplus defined. #endif #endif libsass-3.3.4/include/sass/version.h000066400000000000000000000001651267254216700174310ustar00rootroot00000000000000#ifndef SASS_VERSION_H #define SASS_VERSION_H #ifndef LIBSASS_VERSION #define LIBSASS_VERSION "[NA]" #endif #endif libsass-3.3.4/include/sass/version.h.in000066400000000000000000000002021267254216700200260ustar00rootroot00000000000000#ifndef SASS_VERSION_H #define SASS_VERSION_H #ifndef LIBSASS_VERSION #define LIBSASS_VERSION "@PACKAGE_VERSION@" #endif #endif libsass-3.3.4/include/sass2scss.h000066400000000000000000000047261267254216700167310ustar00rootroot00000000000000/** * sass2scss * Licensed under the MIT License * Copyright (c) Marcel Greter */ #ifndef SASS2SCSS_H #define SASS2SCSS_H #ifdef _WIN32 /* You should define ADD_EXPORTS *only* when building the DLL. */ #ifdef ADD_EXPORTS #define ADDAPI __declspec(dllexport) #define ADDCALL __cdecl #else #define ADDAPI #define ADDCALL #endif #else /* _WIN32 not defined. */ /* Define with no value on non-Windows OSes. */ #define ADDAPI #define ADDCALL #endif #ifdef __cplusplus #include #include #include #include #include #ifndef SASS2SCSS_VERSION // Hardcode once the file is copied from // https://github.com/mgreter/sass2scss #define SASS2SCSS_VERSION "1.0.5" #endif // add namespace for c++ namespace Sass { // pretty print options const int SASS2SCSS_PRETTIFY_0 = 0; const int SASS2SCSS_PRETTIFY_1 = 1; const int SASS2SCSS_PRETTIFY_2 = 2; const int SASS2SCSS_PRETTIFY_3 = 3; // remove one-line comment const int SASS2SCSS_KEEP_COMMENT = 32; // remove multi-line comments const int SASS2SCSS_STRIP_COMMENT = 64; // convert one-line to multi-line const int SASS2SCSS_CONVERT_COMMENT = 128; // String for finding something interesting const std::string SASS2SCSS_FIND_WHITESPACE = " \t\n\v\f\r"; // converter struct // holding all states struct converter { // bit options int options; // is selector bool selector; // concat lists bool comma; // has property bool property; // has semicolon bool semicolon; // comment context std::string comment; // flag end of file bool end_of_file; // whitespace buffer std::string whitespace; // context/block stack std::stack indents; }; // function only available in c++ code char* sass2scss (const std::string& sass, const int options); } // EO namespace // declare for c extern "C" { #endif // prettyfy print options #define SASS2SCSS_PRETTIFY_0 0 #define SASS2SCSS_PRETTIFY_1 1 #define SASS2SCSS_PRETTIFY_2 2 #define SASS2SCSS_PRETTIFY_3 3 // keep one-line comments #define SASS2SCSS_KEEP_COMMENT 32 // remove multi-line comments #define SASS2SCSS_STRIP_COMMENT 64 // convert one-line to multi-line #define SASS2SCSS_CONVERT_COMMENT 128 // available to c and c++ code ADDAPI char* ADDCALL sass2scss (const char* sass, const int options); // Get compiled sass2scss version ADDAPI const char* ADDCALL sass2scss_version(void); #ifdef __cplusplus } // __cplusplus defined. #endif #endiflibsass-3.3.4/m4/000077500000000000000000000000001267254216700135155ustar00rootroot00000000000000libsass-3.3.4/m4/.gitkeep000066400000000000000000000000001267254216700151340ustar00rootroot00000000000000libsass-3.3.4/m4/m4-ax_cxx_compile_stdcxx_11.m4000066400000000000000000000130001267254216700211670ustar00rootroot00000000000000# ============================================================================ # http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html # ============================================================================ # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the C++11 # standard; if necessary, add switches to CXXFLAGS to enable support. # # The first argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for an extended mode. # # The second argument, if specified 'mandatory' or if left unspecified, # indicates that baseline C++11 support is required and that the macro # should error out if no mode with that support is found. If specified # 'optional', then configuration proceeds regardless, after defining # HAVE_CXX11 if and only if a supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 11 m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[ template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; struct Base { virtual void f() {} }; struct Child : public Base { virtual void f() override {} }; typedef check> right_angle_brackets; int a; decltype(a) b; typedef check check_type; check_type c; check_type&& cr = static_cast(c); auto d = a; auto l = [](){}; // Prevent Clang error: unused variable 'l' [-Werror,-Wunused-variable] struct use_l { use_l() { l(); } }; // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } ]]) AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl m4_if([$1], [], [], [$1], [ext], [], [$1], [noext], [], [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl m4_if([$2], [], [ax_cxx_compile_cxx11_required=true], [$2], [mandatory], [ax_cxx_compile_cxx11_required=true], [$2], [optional], [ax_cxx_compile_cxx11_required=false], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])]) AC_LANG_PUSH([C++])dnl ac_success=no AC_CACHE_CHECK(whether $CXX supports C++11 features by default, ax_cv_cxx_compile_cxx11, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [ax_cv_cxx_compile_cxx11=yes], [ax_cv_cxx_compile_cxx11=no])]) if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi m4_if([$1], [noext], [], [dnl if test x$ac_success = xno; then for switch in -std=gnu++11 -std=gnu++0x; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) m4_if([$1], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf for switch in -std=c++11 -std=c++0x +std=c++11; do cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch]) AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch, $cachevar, [ac_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$CXXFLAGS $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])], [eval $cachevar=yes], [eval $cachevar=no]) CXXFLAGS="$ac_save_CXXFLAGS"]) if eval test x\$$cachevar = xyes; then CXXFLAGS="$CXXFLAGS $switch" ac_success=yes break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.]) fi else if test x$ac_success = xno; then HAVE_CXX11=0 AC_MSG_NOTICE([No compiler with C++11 support was found]) else HAVE_CXX11=1 AC_DEFINE(HAVE_CXX11,1, [define if the compiler supports basic C++11 syntax]) fi AC_SUBST(HAVE_CXX11) fi ]) libsass-3.3.4/res/000077500000000000000000000000001267254216700137665ustar00rootroot00000000000000libsass-3.3.4/res/resource.rc000066400000000000000000000016161267254216700161470ustar00rootroot00000000000000#include // DLL version information. VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE #else FILEFLAGS 0 #endif FILEOS VOS_NT_WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_UNKNOWN BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904b0" BEGIN VALUE "CompanyName", "Libsass Organization" VALUE "FileDescription", "A C/C++ implementation of a Sass compiler" VALUE "FileVersion", "0.9.0.0" VALUE "InternalName", "libsass" VALUE "LegalCopyright", "©2014 libsass.org" VALUE "OriginalFilename", "libsass.dll" VALUE "ProductName", "Libsass Library" VALUE "ProductVersion", "0.9.0.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x809, 1200 END ENDlibsass-3.3.4/script/000077500000000000000000000000001267254216700145015ustar00rootroot00000000000000libsass-3.3.4/script/bootstrap000077500000000000000000000002741267254216700164470ustar00rootroot00000000000000#!/bin/bash script/branding if [ ! -d "sass-spec" ]; then git clone https://github.com/sass/sass-spec.git fi if [ ! -d "sassc" ]; then git clone https://github.com/sass/sassc.git fi libsass-3.3.4/script/branding000077500000000000000000000005371267254216700162200ustar00rootroot00000000000000#! /bin/bash echo " " echo " _ ___ ____ ____ _ ____ ____ " echo "| | |_ _| __ ) ___| / \ / ___/ ___| " echo "| | | || _ \___ \ / _ \ \___ \___ \ " echo "| |___ | || |_) |__) / ___ \ ___) |__) |" echo "|_____|___|____/____/_/ \_\____/____/ " echo " " libsass-3.3.4/script/ci-build-libsass000077500000000000000000000067021267254216700175620ustar00rootroot00000000000000#!/bin/bash set -e script/bootstrap # export this path right here (was in script/spec before) export SASS_LIBSASS_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../ && pwd )" # use some defaults if not running under travis ci if [ "x$CONTINUOUS_INTEGRATION" == "x" ]; then export CONTINUOUS_INTEGRATION=true; fi if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then export TRAVIS_BUILD_DIR=$(pwd); fi if [ "x$SASS_SASSC_PATH" == "x" ]; then export SASS_SASSC_PATH=$(pwd)/sassc; fi if [ "x$SASS_SPEC_PATH" == "x" ]; then export SASS_SPEC_PATH=$(pwd)/sass-spec; fi # try to get the os name from uname (and filter via perl - probably not the most portable way?) if [ "x$TRAVIS_OS_NAME" == "x" ]; then export TRAVIS_OS_NAME=`uname -s | perl -ne 'print lc \$1 if\(/^([a-zA-Z]+)/'\)`; fi if [ "x$COVERAGE" == "xyes" ]; then COVERAGE_OPT="--enable-coverage" export EXTRA_CFLAGS="--coverage" export EXTRA_CXXFLAGS="--coverage" export EXTRA_LDFLAGS="--coverage" else COVERAGE_OPT="--disable-coverage" fi if [ "x$BUILD" == "xstatic" ]; then SHARED_OPT="--disable-shared --enable-static" MAKE_TARGET="static" else # Makefile of sassc wants to link to static SHARED_OPT="--enable-shared --enable-static" MAKE_TARGET="shared" fi if [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then MAKE_OPTS="$MAKE_OPTS -j1 V=1" else MAKE_OPTS="$MAKE_OPTS -j3 V=1" fi if [ "x$PREFIX" == "x" ]; then if [ "x$TRAVIS_BUILD_DIR" == "x" ]; then PREFIX=$SASS_LIBSASS_PATH/build else PREFIX=$TRAVIS_BUILD_DIR/build fi fi echo SASS_LIBSASS_PATH: $SASS_LIBSASS_PATH echo TRAVIS_BUILD_DIR: $TRAVIS_BUILD_DIR echo SASS_SASSC_PATH: $SASS_SASSC_PATH echo SASS_SPEC_PATH: $SASS_SPEC_PATH echo INSTALL_LOCATION: $PREFIX if [ "x$AUTOTOOLS" == "xyes" ]; then echo -en 'travis_fold:start:configure\r' autoreconf --force --install ./configure --enable-tests $COVERAGE_OPT \ --disable-silent-rules \ --with-sassc-dir=$SASS_SASSC_PATH \ --with-sass-spec-dir=$SASS_SPEC_PATH \ --prefix=$PREFIX \ ${SHARED_OPT} echo -en 'travis_fold:end:configure\r' make $MAKE_OPTS clean # install to prefix directory PREFIX="$PREFIX" make $MAKE_OPTS install else make $MAKE_OPTS clean fi # install to prefix directory PREFIX="$PREFIX" make $MAKE_OPTS install ls -la $PREFIX/* echo successfully compiled libsass echo AUTOTOOLS=$AUTOTOOLS COVERAGE=$COVERAGE BUILD=$BUILD if [ "$CONTINUOUS_INTEGRATION" == "true" ] && [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "x$TRAVIS_PULL_REQUEST" != "x" ] && ([ "$TRAVIS_OS_NAME" == "linux" ] || [ "$TRAVIS_OS_NAME" == "osx" ] || [ "$TRAVIS_OS_NAME" == "cygwin" ]); then echo "Fetching PR $TRAVIS_PULL_REQUEST" JSON=$(curl -L -sS https://api.github.com/repos/sass/libsass/pulls/$TRAVIS_PULL_REQUEST) if [[ $JSON =~ "API rate limit exceeded" ]]; then echo "Travis rate limit on github exceeded" echo "Retrying via 'special purpose proxy'" JSON=$(curl -L -sS http://libsass.ocbnet.ch/libsass-spec-pr.psgi/$TRAVIS_PULL_REQUEST) fi RE_SPEC_PR="sass\/sass-spec(#|\/pull\/)([0-9]+)" if [[ $JSON =~ $RE_SPEC_PR ]]; then SPEC_PR="${BASH_REMATCH[2]}" echo "Fetching Sass Spec PR $SPEC_PR" git -C sass-spec fetch -u origin pull/$SPEC_PR/head:ci-spec-pr-$SPEC_PR git -C sass-spec checkout --force ci-spec-pr-$SPEC_PR LD_LIBRARY_PATH="$PREFIX/lib/" make $MAKE_OPTS test_build else LD_LIBRARY_PATH="$PREFIX/lib/" make $MAKE_OPTS test_build fi else LD_LIBRARY_PATH="$PREFIX/lib/" make $MAKE_OPTS test_build fi libsass-3.3.4/script/ci-install-compiler000077500000000000000000000000661267254216700203000ustar00rootroot00000000000000#!/bin/bash gem install minitest gem install minitap libsass-3.3.4/script/ci-install-deps000077500000000000000000000005311267254216700174160ustar00rootroot00000000000000#!/bin/bash if [ "x$COVERAGE" == "xyes" ]; then pip install --user gcovr pip install --user cpp-coveralls else echo "no dependencies to install" fi if [ "x$AUTOTOOLS" == "xyes" ]; then sudo add-apt-repository -y ppa:rbose-debianizer/automake &> /dev/null sudo apt-get -qq update sudo apt-get -qq install automake AUTOTOOLS=yes fi libsass-3.3.4/script/ci-report-coverage000077500000000000000000000022071267254216700201250ustar00rootroot00000000000000#!/bin/bash if [ "x$COVERAGE" = "xyes" ]; then # exclude some directories from profiling (.libs is from autotools) export EXCLUDE_COVERAGE="--exclude src/sassc --exclude src/sass-spec --exclude src/.libs --exclude src/debug.hpp --exclude src/json.cpp --exclude src/json.hpp --exclude src/cencode.c --exclude src/b64 --exclude src/utf8 --exclude src/utf8_string.hpp --exclude src/utf8.h --exclude src/utf8_string.cpp --exclude src/sass2scss.h --exclude src/sass2scss.cpp --exclude src/test --exclude src/posix --exclude src/debugger.hpp" # debug via gcovr gcov -v gcovr -r . # generate and submit report to coveralls.io coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' else echo "skip coverage reporting" fi libsass-3.3.4/script/spec000077500000000000000000000000721267254216700153600ustar00rootroot00000000000000#!/bin/bash script/bootstrap make $MAKE_OPTS test_build libsass-3.3.4/script/tap-driver000077500000000000000000000460721267254216700165150ustar00rootroot00000000000000#! /bin/sh # Copyright (C) 2011-2013 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . scriptversion=2011-12-27.17; # UTC # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u me=tap-driver.sh fatal () { echo "$me: fatal: $*" >&2 exit 1 } usage_error () { echo "$me: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat < # trap : 1 3 2 13 15 if test $merge -gt 0; then exec 2>&1 else exec 2>&3 fi "$@" echo $? ) | LC_ALL=C ${AM_TAP_AWK-awk} \ -v me="$me" \ -v test_script_name="$test_name" \ -v log_file="$log_file" \ -v trs_file="$trs_file" \ -v expect_failure="$expect_failure" \ -v merge="$merge" \ -v ignore_exit="$ignore_exit" \ -v comments="$comments" \ -v diag_string="$diag_string" \ ' # FIXME: the usages of "cat >&3" below could be optimized when using # FIXME: GNU awk, and/on on systems that supports /dev/fd/. # Implementation note: in what follows, `result_obj` will be an # associative array that (partly) simulates a TAP result object # from the `TAP::Parser` perl module. ## ----------- ## ## FUNCTIONS ## ## ----------- ## function fatal(msg) { print me ": " msg | "cat >&2" exit 1 } function abort(where) { fatal("internal error " where) } # Convert a boolean to a "yes"/"no" string. function yn(bool) { return bool ? "yes" : "no"; } function add_test_result(result) { if (!test_results_index) test_results_index = 0 test_results_list[test_results_index] = result test_results_index += 1 test_results_seen[result] = 1; } # Whether the test script should be re-run by "make recheck". function must_recheck() { for (k in test_results_seen) if (k != "XFAIL" && k != "PASS" && k != "SKIP") return 1 return 0 } # Whether the content of the log file associated to this test should # be copied into the "global" test-suite.log. function copy_in_global_log() { for (k in test_results_seen) if (k != "PASS") return 1 return 0 } # FIXME: this can certainly be improved ... function get_global_test_result() { if ("ERROR" in test_results_seen) return "ERROR" if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) return "FAIL" all_skipped = 1 for (k in test_results_seen) if (k != "SKIP") all_skipped = 0 if (all_skipped) return "SKIP" return "PASS"; } function stringify_result_obj(result_obj) { if (result_obj["is_unplanned"] || result_obj["number"] != testno) return "ERROR" if (plan_seen == LATE_PLAN) return "ERROR" if (result_obj["directive"] == "TODO") return result_obj["is_ok"] ? "XPASS" : "XFAIL" if (result_obj["directive"] == "SKIP") return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; if (length(result_obj["directive"])) abort("in function stringify_result_obj()") return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL } function decorate_result(result) { color_name = color_for_result[result] if (color_name) return color_map[color_name] "" result "" color_map["std"] # If we are not using colorized output, or if we do not know how # to colorize the given result, we should return it unchanged. return result } function report(result, details) { if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) { msg = ": " test_script_name add_test_result(result) } else if (result == "#") { msg = " " test_script_name ":" } else { abort("in function report()") } if (length(details)) msg = msg " " details # Output on console might be colorized. print decorate_result(result) msg # Log the result in the log file too, to help debugging (this is # especially true when said result is a TAP error or "Bail out!"). print result msg | "cat >&3"; } function testsuite_error(error_message) { report("ERROR", "- " error_message) } function handle_tap_result() { details = result_obj["number"]; if (length(result_obj["description"])) details = details " " result_obj["description"] if (plan_seen == LATE_PLAN) { details = details " # AFTER LATE PLAN"; } else if (result_obj["is_unplanned"]) { details = details " # UNPLANNED"; } else if (result_obj["number"] != testno) { details = sprintf("%s # OUT-OF-ORDER (expecting %d)", details, testno); } else if (result_obj["directive"]) { details = details " # " result_obj["directive"]; if (length(result_obj["explanation"])) details = details " " result_obj["explanation"] } report(stringify_result_obj(result_obj), details) } # `skip_reason` should be empty whenever planned > 0. function handle_tap_plan(planned, skip_reason) { planned += 0 # Avoid getting confused if, say, `planned` is "00" if (length(skip_reason) && planned > 0) abort("in function handle_tap_plan()") if (plan_seen) { # Error, only one plan per stream is acceptable. testsuite_error("multiple test plans") return; } planned_tests = planned # The TAP plan can come before or after *all* the TAP results; we speak # respectively of an "early" or a "late" plan. If we see the plan line # after at least one TAP result has been seen, assume we have a late # plan; in this case, any further test result seen after the plan will # be flagged as an error. plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) # If testno > 0, we have an error ("too many tests run") that will be # automatically dealt with later, so do not worry about it here. If # $plan_seen is true, we have an error due to a repeated plan, and that # has already been dealt with above. Otherwise, we have a valid "plan # with SKIP" specification, and should report it as a particular kind # of SKIP result. if (planned == 0 && testno == 0) { if (length(skip_reason)) skip_reason = "- " skip_reason; report("SKIP", skip_reason); } } function extract_tap_comment(line) { if (index(line, diag_string) == 1) { # Strip leading `diag_string` from `line`. line = substr(line, length(diag_string) + 1) # And strip any leading and trailing whitespace left. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # Return what is left (if any). return line; } return ""; } # When this function is called, we know that line is a TAP result line, # so that it matches the (perl) RE "^(not )?ok\b". function setup_result_obj(line) { # Get the result, and remove it from the line. result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) sub("^(not )?ok[ \t]*", "", line) # If the result has an explicit number, get it and strip it; otherwise, # automatically assing the next progresive number to it. if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) { match(line, "^[0-9]+") # The final `+ 0` is to normalize numbers with leading zeros. result_obj["number"] = substr(line, 1, RLENGTH) + 0 line = substr(line, RLENGTH + 1) } else { result_obj["number"] = testno } if (plan_seen == LATE_PLAN) # No further test results are acceptable after a "late" TAP plan # has been seen. result_obj["is_unplanned"] = 1 else if (plan_seen && testno > planned_tests) result_obj["is_unplanned"] = 1 else result_obj["is_unplanned"] = 0 # Strip trailing and leading whitespace. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) # This will have to be corrected if we have a "TODO"/"SKIP" directive. result_obj["description"] = line result_obj["directive"] = "" result_obj["explanation"] = "" if (index(line, "#") == 0) return # No possible directive, nothing more to do. # Directives are case-insensitive. rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" # See whether we have the directive, and if yes, where. pos = match(line, rx "$") if (!pos) pos = match(line, rx "[^a-zA-Z0-9_]") # If there was no TAP directive, we have nothing more to do. if (!pos) return # Let`s now see if the TAP directive has been escaped. For example: # escaped: ok \# SKIP # not escaped: ok \\# SKIP # escaped: ok \\\\\# SKIP # not escaped: ok \ # SKIP if (substr(line, pos, 1) == "#") { bslash_count = 0 for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) bslash_count += 1 if (bslash_count % 2) return # Directive was escaped. } # Strip the directive and its explanation (if any) from the test # description. result_obj["description"] = substr(line, 1, pos - 1) # Now remove the test description from the line, that has been dealt # with already. line = substr(line, pos) # Strip the directive, and save its value (normalized to upper case). sub("^[ \t]*#[ \t]*", "", line) result_obj["directive"] = toupper(substr(line, 1, 4)) line = substr(line, 5) # Now get the explanation for the directive (if any), with leading # and trailing whitespace removed. sub("^[ \t]*", "", line) sub("[ \t]*$", "", line) result_obj["explanation"] = line } function get_test_exit_message(status) { if (status == 0) return "" if (status !~ /^[1-9][0-9]*$/) abort("getting exit status") if (status < 127) exit_details = "" else if (status == 127) exit_details = " (command not found?)" else if (status >= 128 && status <= 255) exit_details = sprintf(" (terminated by signal %d?)", status - 128) else if (status > 256 && status <= 384) # We used to report an "abnormal termination" here, but some Korn # shells, when a child process die due to signal number n, can leave # in $? an exit status of 256+n instead of the more standard 128+n. # Apparently, both behaviours are allowed by POSIX (2008), so be # prepared to handle them both. See also Austing Group report ID # 0000051 exit_details = sprintf(" (terminated by signal %d?)", status - 256) else # Never seen in practice. exit_details = " (abnormal termination)" return sprintf("exited with status %d%s", status, exit_details) } function write_test_results() { print ":global-test-result: " get_global_test_result() > trs_file print ":recheck: " yn(must_recheck()) > trs_file print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file for (i = 0; i < test_results_index; i += 1) print ":test-result: " test_results_list[i] > trs_file close(trs_file); } BEGIN { ## ------- ## ## SETUP ## ## ------- ## '"$init_colors"' # Properly initialized once the TAP plan is seen. planned_tests = 0 COOKED_PASS = expect_failure ? "XPASS": "PASS"; COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; # Enumeration-like constants to remember which kind of plan (if any) # has been seen. It is important that NO_PLAN evaluates "false" as # a boolean. NO_PLAN = 0 EARLY_PLAN = 1 LATE_PLAN = 2 testno = 0 # Number of test results seen so far. bailed_out = 0 # Whether a "Bail out!" directive has been seen. # Whether the TAP plan has been seen or not, and if yes, which kind # it is ("early" is seen before any test result, "late" otherwise). plan_seen = NO_PLAN ## --------- ## ## PARSING ## ## --------- ## is_first_read = 1 while (1) { # Involutions required so that we are able to read the exit status # from the last input line. st = getline if (st < 0) # I/O error. fatal("I/O error while reading from input stream") else if (st == 0) # End-of-input { if (is_first_read) abort("in input loop: only one input line") break } if (is_first_read) { is_first_read = 0 nextline = $0 continue } else { curline = nextline nextline = $0 $0 = curline } # Copy any input line verbatim into the log file. print | "cat >&3" # Parsing of TAP input should stop after a "Bail out!" directive. if (bailed_out) continue # TAP test result. if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) { testno += 1 setup_result_obj($0) handle_tap_result() } # TAP plan (normal or "SKIP" without explanation). else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) { # The next two lines will put the number of planned tests in $0. sub("^1\\.\\.", "") sub("[^0-9]*$", "") handle_tap_plan($0, "") continue } # TAP "SKIP" plan, with an explanation. else if ($0 ~ /^1\.\.0+[ \t]*#/) { # The next lines will put the skip explanation in $0, stripping # any leading and trailing whitespace. This is a little more # tricky in truth, since we want to also strip a potential leading # "SKIP" string from the message. sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") sub("[ \t]*$", ""); handle_tap_plan(0, $0) } # "Bail out!" magic. # Older versions of prove and TAP::Harness (e.g., 3.17) did not # recognize a "Bail out!" directive when preceded by leading # whitespace, but more modern versions (e.g., 3.23) do. So we # emulate the latter, "more modern" behaviour. else if ($0 ~ /^[ \t]*Bail out!/) { bailed_out = 1 # Get the bailout message (if any), with leading and trailing # whitespace stripped. The message remains stored in `$0`. sub("^[ \t]*Bail out![ \t]*", ""); sub("[ \t]*$", ""); # Format the error message for the bailout_message = "Bail out!" if (length($0)) bailout_message = bailout_message " " $0 testsuite_error(bailout_message) } # Maybe we have too look for dianogtic comments too. else if (comments != 0) { comment = extract_tap_comment($0); if (length(comment)) report("#", comment); } } ## -------- ## ## FINISH ## ## -------- ## # A "Bail out!" directive should cause us to ignore any following TAP # error, as well as a non-zero exit status from the TAP producer. if (!bailed_out) { if (!plan_seen) { testsuite_error("missing test plan") } else if (planned_tests != testno) { bad_amount = testno > planned_tests ? "many" : "few" testsuite_error(sprintf("too %s tests run (expected %d, got %d)", bad_amount, planned_tests, testno)) } if (!ignore_exit) { # Fetch exit status from the last line. exit_message = get_test_exit_message(nextline) if (exit_message) testsuite_error(exit_message) } } write_test_results() exit 0 } # End of "BEGIN" block. ' # TODO: document that we consume the file descriptor 3 :-( } 3>"$log_file" test $? -eq 0 || fatal "I/O or internal error" # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC" # time-stamp-end: "; # UTC" # End: libsass-3.3.4/script/tap-runner000077500000000000000000000000171267254216700165200ustar00rootroot00000000000000$@ | tapout taplibsass-3.3.4/src/000077500000000000000000000000001267254216700137645ustar00rootroot00000000000000libsass-3.3.4/src/GNUmakefile.am000066400000000000000000000023731267254216700164370ustar00rootroot00000000000000ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 -I script AM_COPT = -Wall -O2 AM_COVLDFLAGS = if ENABLE_COVERAGE AM_COPT = -O0 --coverage AM_COVLDFLAGS += -lgcov endif AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = $(AM_COPT) 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 EXTRA_DIST = \ COPYING \ INSTALL \ LICENSE \ Readme.md pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = support/libsass.pc lib_LTLIBRARIES = libsass.la include $(top_srcdir)/Makefile.conf libsass_la_SOURCES = ${CSOURCES} ${SOURCES} libsass_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 0:9:0 if ENABLE_TESTS if ENABLE_COVERAGE nodist_EXTRA_libsass_la_SOURCES = non-existent-file-to-force-CXX-linking.cxx endif endif include_HEADERS = $(top_srcdir)/include/sass.h \ $(top_srcdir)/include/sass2scss.h sass_includedir = $(includedir)/sass sass_include_HEADERS = $(top_srcdir)/include/sass/base.h \ $(top_srcdir)/include/sass/values.h \ $(top_srcdir)/include/sass/version.h \ $(top_srcdir)/include/sass/context.h \ $(top_srcdir)/include/sass/functions.h libsass-3.3.4/src/ast.cpp000066400000000000000000002036441267254216700152700ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "context.hpp" #include "node.hpp" #include "extend.hpp" #include "emitter.hpp" #include "color_maps.hpp" #include #include #include #include namespace Sass { static Null sass_null(Sass::Null(ParserState("null"))); bool Supports_Operator::needs_parens(Supports_Condition* cond) const { return dynamic_cast(cond) || (dynamic_cast(cond) && dynamic_cast(cond)->operand() != operand()); } bool Supports_Negation::needs_parens(Supports_Condition* cond) const { return dynamic_cast(cond) || dynamic_cast(cond); } void AST_Node::update_pstate(const ParserState& pstate) { pstate_.offset += pstate - pstate_ + pstate.offset; } inline bool is_ns_eq(const std::string& l, const std::string& r) { if (l.empty() && r.empty()) return true; else if (l.empty() && r == "*") return true; else if (r.empty() && l == "*") return true; else return l == r; } bool Compound_Selector::operator< (const Compound_Selector& rhs) const { size_t L = std::min(length(), rhs.length()); for (size_t i = 0; i < L; ++i) { Simple_Selector* l = (*this)[i]; Simple_Selector* r = rhs[i]; if (!l && !r) return false; else if (!r) return false; else if (!l) return true; else if (*l != *r) { return *l < *r; } } // just compare the length now return length() < rhs.length(); } bool Compound_Selector::has_parent_ref() { for (Simple_Selector* s : *this) { if (s->has_parent_ref()) return true; } return false; } bool Complex_Selector::has_parent_ref() { return (head() && head()->has_parent_ref()) || (tail() && tail()->has_parent_ref()); } bool Complex_Selector::operator< (const Complex_Selector& rhs) const { // const iterators for tails const Complex_Selector* l = this; const Complex_Selector* r = &rhs; Compound_Selector* l_h = l ? l->head() : 0; Compound_Selector* r_h = r ? r->head() : 0; // process all tails while (true) { // skip empty ancestor first if (l && l->is_empty_ancestor()) { l = l->tail(); l_h = l ? l->head() : 0; continue; } // skip empty ancestor first if (r && r->is_empty_ancestor()) { r = r->tail(); r_h = r ? r->head() : 0; continue; } // check for valid selectors if (!l) return !!r; if (!r) return false; // both are null else if (!l_h && !r_h) { // check combinator after heads if (l->combinator() != r->combinator()) { return l->combinator() < r->combinator(); } // advance to next tails l = l->tail(); r = r->tail(); // fetch the next headers l_h = l ? l->head() : 0; r_h = r ? r->head() : 0; } // one side is null else if (!r_h) return true; else if (!l_h) return false; // heads ok and equal else if (*l_h == *r_h) { // check combinator after heads if (l->combinator() != r->combinator()) { return l->combinator() < r->combinator(); } // advance to next tails l = l->tail(); r = r->tail(); // fetch the next headers l_h = l ? l->head() : 0; r_h = r ? r->head() : 0; } // heads are not equal else return *l_h < *r_h; } return true; } bool Complex_Selector::operator== (const Complex_Selector& rhs) const { // const iterators for tails const Complex_Selector* l = this; const Complex_Selector* r = &rhs; Compound_Selector* l_h = l ? l->head() : 0; Compound_Selector* r_h = r ? r->head() : 0; // process all tails while (true) { // skip empty ancestor first if (l && l->is_empty_ancestor()) { l = l->tail(); l_h = l ? l->head() : 0; continue; } // skip empty ancestor first if (r && r->is_empty_ancestor()) { r = r->tail(); r_h = r ? r->head() : 0; continue; } // check the pointers if (!r) return !l; if (!l) return !r; // both are null if (!l_h && !r_h) { // check combinator after heads if (l->combinator() != r->combinator()) { return l->combinator() < r->combinator(); } // advance to next tails l = l->tail(); r = r->tail(); // fetch the next heads l_h = l ? l->head() : 0; r_h = r ? r->head() : 0; } // fail if only one is null else if (!r_h) return !l_h; else if (!l_h) return !r_h; // heads ok and equal else if (*l_h == *r_h) { // check combinator after heads if (l->combinator() != r->combinator()) { return l->combinator() == r->combinator(); } // advance to next tails l = l->tail(); r = r->tail(); // fetch the next heads l_h = l ? l->head() : 0; r_h = r ? r->head() : 0; } // abort else break; } // unreachable return false; } Compound_Selector* Compound_Selector::unify_with(Compound_Selector* rhs, Context& ctx) { Compound_Selector* unified = rhs; for (size_t i = 0, L = length(); i < L; ++i) { if (!unified) break; unified = (*this)[i]->unify_with(unified, ctx); } return unified; } bool Simple_Selector::operator== (const Simple_Selector& rhs) const { if (const Wrapped_Selector* lw = dynamic_cast(this)) return *lw == rhs; if (const Attribute_Selector* la = dynamic_cast(this)) return *la == rhs; if (is_ns_eq(ns(), rhs.ns())) { return name() == rhs.name(); } return ns() == rhs.ns(); } bool Simple_Selector::operator< (const Simple_Selector& rhs) const { if (const Wrapped_Selector* lw = dynamic_cast(this)) return *lw < rhs; if (const Attribute_Selector* la = dynamic_cast(this)) return *la < rhs; if (is_ns_eq(ns(), rhs.ns())) { return name() < rhs.name(); } return ns() < rhs.ns(); } bool Selector_List::operator== (const Selector& rhs) const { // solve the double dispatch problem by using RTTI information via dynamic cast if (const Selector_List* ls = dynamic_cast(&rhs)) { return *this == *ls; } else if (const Complex_Selector* ls = dynamic_cast(&rhs)) { return *this == *ls; } else if (const Compound_Selector* ls = dynamic_cast(&rhs)) { return *this == *ls; } // no compare method return this == &rhs; } // Selector lists can be compared to comma lists bool Selector_List::operator==(const Expression& rhs) const { // solve the double dispatch problem by using RTTI information via dynamic cast if (const List* ls = dynamic_cast(&rhs)) { return *this == *ls; } if (const Selector* ls = dynamic_cast(&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 l_lst = this->elements(); std::vector r_lst = rhs.elements(); std::sort(l_lst.begin(), l_lst.end(), cmp_complex_selector()); std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector()); // process loop while (true) { // first check for valid index if (i == iL) return iL == nL; else if (n == nL) return iL == nL; // the access the vector items Complex_Selector* l = l_lst[i]; Complex_Selector* r = r_lst[n]; // skip nulls if (!l) ++i; else if (!r) ++n; // do the check else if (*l != *r) { return false; } // advance ++i; ++n; } // no mismatch return true; } Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx) { for (size_t i = 0, L = rhs->length(); i < L; ++i) { if (to_string(ctx.c_options) == (*rhs)[i]->to_string(ctx.c_options)) return rhs; } // check for pseudo elements because they are always last size_t i, L; bool found = false; if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector)) { for (i = 0, L = rhs->length(); i < L; ++i) { if ((dynamic_cast((*rhs)[i]) || dynamic_cast((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element()) { found = true; break; } } } else { for (i = 0, L = rhs->length(); i < L; ++i) { if (dynamic_cast((*rhs)[i]) || dynamic_cast((*rhs)[i])) { found = true; break; } } } if (!found) { Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs); (*cpy) << this; return cpy; } Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate()); for (size_t j = 0; j < i; ++j) { (*cpy) << (*rhs)[j]; } (*cpy) << this; for (size_t j = i; j < L; ++j) { (*cpy) << (*rhs)[j]; } return cpy; } Simple_Selector* Type_Selector::unify_with(Simple_Selector* rhs, Context& ctx) { // check if ns can be extended // true for no ns or universal if (has_universal_ns()) { // but dont extend with universal // true for valid ns and universal if (!rhs->is_universal_ns()) { // creaty the copy inside (avoid unnecessary copies) Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this); // overwrite the name if star is given as name if (ts->name() == "*") { ts->name(rhs->name()); } // now overwrite the namespace name and flag ts->ns(rhs->ns()); ts->has_ns(rhs->has_ns()); // return copy return ts; } } // namespace may changed, check the name now // overwrite star (but not with another star) if (name() == "*" && rhs->name() != "*") { // creaty the copy inside (avoid unnecessary copies) Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this); // simply set the new name ts->name(rhs->name()); // return copy return ts; } // return original return this; } Compound_Selector* Type_Selector::unify_with(Compound_Selector* rhs, Context& ctx) { // TODO: handle namespaces // if the rhs is empty, just return a copy of this if (rhs->length() == 0) { Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate()); (*cpy) << this; return cpy; } Simple_Selector* rhs_0 = (*rhs)[0]; // otherwise, this is a tag name if (name() == "*") { if (typeid(*rhs_0) == typeid(Type_Selector)) { // if rhs is universal, just return this tagname + rhs's qualifiers Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs); Type_Selector* ts = static_cast(rhs_0); (*cpy)[0] = this->unify_with(ts, ctx); return cpy; } else if (dynamic_cast(rhs_0)) { // qualifier is `.class`, so we can prefix with `ns|*.class` Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate()); if (has_ns() && !rhs_0->has_ns()) { if (ns() != "*") (*cpy) << this; } for (size_t i = 0, L = rhs->length(); i < L; ++i) { (*cpy) << (*rhs)[i]; } return cpy; } return rhs; } if (typeid(*rhs_0) == typeid(Type_Selector)) { // if rhs is universal, just return this tagname + rhs's qualifiers if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0; // otherwise create new compound and unify first simple selector Compound_Selector* copy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs); (*copy)[0] = this->unify_with(rhs_0, ctx); return copy; } // else it's a tag name and a bunch of qualifiers -- just append them Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate()); if (name() != "*") (*cpy) << this; (*cpy) += rhs; return cpy; } Compound_Selector* Selector_Qualifier::unify_with(Compound_Selector* rhs, Context& ctx) { if (name()[0] == '#') { for (size_t i = 0, L = rhs->length(); i < L; ++i) { Simple_Selector* rhs_i = (*rhs)[i]; if (typeid(*rhs_i) == typeid(Selector_Qualifier) && static_cast(rhs_i)->name()[0] == '#' && static_cast(rhs_i)->name() != name()) return 0; } } rhs->has_line_break(has_line_break()); return Simple_Selector::unify_with(rhs, ctx); } Compound_Selector* Pseudo_Selector::unify_with(Compound_Selector* rhs, Context& ctx) { if (is_pseudo_element()) { for (size_t i = 0, L = rhs->length(); i < L; ++i) { Simple_Selector* rhs_i = (*rhs)[i]; if (typeid(*rhs_i) == typeid(Pseudo_Selector) && static_cast(rhs_i)->is_pseudo_element() && static_cast(rhs_i)->name() != name()) { return 0; } } } return Simple_Selector::unify_with(rhs, ctx); } bool Attribute_Selector::operator< (const Attribute_Selector& rhs) const { if (is_ns_eq(ns(), rhs.ns())) { if (name() == rhs.name()) { if (matcher() == rhs.matcher()) { return value() < rhs.value(); } else { return matcher() < rhs.matcher(); } } else { return name() < rhs.name(); } } else return false; } bool Attribute_Selector::operator< (const Simple_Selector& rhs) const { if (const Attribute_Selector* w = dynamic_cast(&rhs)) { return *this < *w; } if (is_ns_eq(ns(), rhs.ns())) { return name() < rhs.name(); } return ns() < rhs.ns(); } bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const { if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name()) { return matcher() == rhs.matcher() && value() == rhs.value(); } else return false; } bool Attribute_Selector::operator== (const Simple_Selector& rhs) const { if (const Attribute_Selector* w = dynamic_cast(&rhs)) { return *this == *w; } if (is_ns_eq(ns(), rhs.ns())) { return name() == rhs.name(); } return ns() == rhs.ns(); } bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const { if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name()) { return *(selector()) == *(rhs.selector()); } else return false; } bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const { if (const Wrapped_Selector* w = dynamic_cast(&rhs)) { return *this == *w; } if (is_ns_eq(ns(), rhs.ns())) { return name() == rhs.name(); } return ns() == rhs.ns(); } bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const { if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name()) { return *(selector()) < *(rhs.selector()); } if (is_ns_eq(ns(), rhs.ns())) { return name() < rhs.name(); } return ns() < rhs.ns(); } bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const { if (const Wrapped_Selector* w = dynamic_cast(&rhs)) { return *this < *w; } if (is_ns_eq(ns(), rhs.ns())) { return name() < rhs.name(); } return ns() < rhs.ns(); } bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub) { if (this->name() != sub->name()) return false; if (this->name() == ":current") return false; if (Selector_List* rhs_list = dynamic_cast(sub->selector())) { if (Selector_List* lhs_list = dynamic_cast(selector())) { return lhs_list->is_superselector_of(rhs_list); } error("is_superselector expected a Selector_List", sub->pstate()); } else { error("is_superselector expected a Selector_List", sub->pstate()); } return false; } bool Compound_Selector::is_superselector_of(Selector_List* rhs, std::string wrapped) { for (Complex_Selector* item : rhs->elements()) { if (is_superselector_of(item, wrapped)) return true; } return false; } bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapped) { if (rhs->head()) return is_superselector_of(rhs->head(), wrapped); return false; } bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping) { Compound_Selector* lhs = this; Simple_Selector* lbase = lhs->base(); Simple_Selector* rbase = rhs->base(); // Check if pseudo-elements are the same between the selectors std::set lpsuedoset, rpsuedoset; for (size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->is_pseudo_element()) { std::string pseudo((*this)[i]->to_string()); pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving lpsuedoset.insert(pseudo); } } for (size_t i = 0, L = rhs->length(); i < L; ++i) { if ((*rhs)[i]->is_pseudo_element()) { std::string pseudo((*rhs)[i]->to_string()); pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving rpsuedoset.insert(pseudo); } } if (lpsuedoset != rpsuedoset) { return false; } std::set 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* lhs = (*this)[i]; // very special case for wrapped matches selector if (Wrapped_Selector* wrapped = dynamic_cast(lhs)) { if (wrapped->name() == ":not") { if (Selector_List* not_list = dynamic_cast(wrapped->selector())) { if (not_list->is_superselector_of(rhs, wrapped->name())) return false; } else { throw std::runtime_error("wrapped not selector is not a list"); } } if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { lhs = wrapped->selector(); if (Selector_List* list = dynamic_cast(wrapped->selector())) { if (Compound_Selector* comp = dynamic_cast(rhs)) { if (!wrapping.empty() && wrapping != wrapped->name()) return false; if (wrapping.empty() || wrapping != wrapped->name()) {; if (list->is_superselector_of(comp, wrapped->name())) return true; } } } } Simple_Selector* rhs_sel = rhs->elements().size() > i ? (*rhs)[i] : 0; if (Wrapped_Selector* wrapped_r = dynamic_cast(rhs_sel)) { if (wrapped->name() == wrapped_r->name()) { if (wrapped->is_superselector_of(wrapped_r)) { continue; rset.insert(lhs->to_string()); }} } } // match from here on as strings lset.insert(lhs->to_string()); } for (size_t n = 0, nL = rhs->length(); n < nL; ++n) { auto r = (*rhs)[n]; if (Wrapped_Selector* wrapped = dynamic_cast(r)) { if (wrapped->name() == ":not") { if (Selector_List* ls = dynamic_cast(wrapped->selector())) { ls->remove_parent_selectors(); if (is_superselector_of(ls, wrapped->name())) return false; } } if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") { if (!wrapping.empty()) { if (wrapping != wrapped->name()) return false; } if (Selector_List* ls = dynamic_cast(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* Compound_Selector::to_complex(Memory_Manager& mem) { // create an intermediate complex selector return SASS_MEMORY_NEW(mem, Complex_Selector, pstate(), Complex_Selector::ANCESTOR_OF, this, 0); } Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx) { // get last tails (on the right side) Complex_Selector* l_last = this->last(); Complex_Selector* r_last = other->last(); // check valid pointers (assertion) SASS_ASSERT(l_last, "lhs is null"); SASS_ASSERT(r_last, "rhs is null"); // Not sure about this check, but closest way I could check // was to see if this is a ruby 'SimpleSequence' equivalent. // It seems to do the job correctly as some specs react to this if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0; if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0; // get the headers for the last tails Compound_Selector* l_last_head = l_last->head(); Compound_Selector* r_last_head = r_last->head(); // check valid head pointers (assertion) SASS_ASSERT(l_last_head, "lhs head is null"); SASS_ASSERT(r_last_head, "rhs head is null"); // get the unification of the last compound selectors Compound_Selector* unified = r_last_head->unify_with(l_last_head, ctx); // abort if we could not unify heads if (unified == 0) return 0; // check for universal (star: `*`) selector bool is_universal = l_last_head->is_universal() || r_last_head->is_universal(); if (is_universal) { // move the head l_last->head(0); r_last->head(unified); } // create nodes from both selectors Node lhsNode = complexSelectorToNode(this, ctx); Node rhsNode = complexSelectorToNode(other, ctx); // overwrite universal base if (!is_universal) { // create some temporaries to convert to node Complex_Selector* fake = unified->to_complex(ctx.mem); Node unified_node = complexSelectorToNode(fake, ctx); // add to permutate the list? rhsNode.plus(unified_node); } // do some magic we inherit from node and extend Node node = Extend::subweave(lhsNode, rhsNode, ctx); Selector_List* result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); NodeDequePtr col = node.collection(); // move from collection to list for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++) { (*result) << nodeToComplexSelector(Node::naiveTrim(*it, ctx), ctx); } // only return if list has some entries return result->length() ? result : 0; } bool Compound_Selector::operator== (const Compound_Selector& rhs) const { // for array access size_t i = 0, n = 0; size_t iL = length(); size_t nL = rhs.length(); // create temporary vectors and sort them std::vector l_lst = this->elements(); std::vector r_lst = rhs.elements(); std::sort(l_lst.begin(), l_lst.end(), cmp_simple_selector()); std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector()); // process loop while (true) { // first check for valid index if (i == iL) return iL == nL; else if (n == nL) return iL == nL; // the access the vector items Simple_Selector* l = l_lst[i]; Simple_Selector* r = r_lst[n]; // skip nulls if (!l) ++i; if (!r) ++n; // do the check now else if (*l != *r) { return false; } // advance now ++i; ++n; } // no mismatch return true; } bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const { return *pLeft < *pRight; } bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping) { return last()->head() && last()->head()->is_superselector_of(rhs, wrapping); } bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping) { Complex_Selector* lhs = this; // check for selectors with leading or trailing combinators if (!lhs->head() || !rhs->head()) { return false; } const Complex_Selector* l_innermost = lhs->innermost(); if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF) { return false; } const Complex_Selector* r_innermost = rhs->innermost(); if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF) { return false; } // more complex (i.e., longer) selectors are always more specific size_t l_len = lhs->length(), r_len = rhs->length(); if (l_len > r_len) { return false; } if (l_len == 1) { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); } // we have to look one tail deeper, since we cary the // combinator around for it (which is important here) if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) { Complex_Selector* lhs_tail = lhs->tail(); Complex_Selector* rhs_tail = rhs->tail(); if (lhs_tail->combinator() != rhs_tail->combinator()) return false; if (lhs_tail->head() && !rhs_tail->head()) return false; if (!lhs_tail->head() && rhs_tail->head()) return false; if (lhs_tail->head() && rhs_tail->head()) { if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; } } bool found = false; Complex_Selector* marker = rhs; for (size_t i = 0, L = rhs->length(); i < L; ++i) { if (i == L-1) { return false; } if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping)) { found = true; break; } marker = marker->tail(); } if (!found) { return false; } /* Hmm, I hope I have the logic right: if lhs has a combinator: if !(marker has a combinator) return false if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost) else if marker has a combinator: if !(marker.combinator == ">") return false return lhs.tail.is_superselector_of(marker.tail) else return lhs.tail.is_superselector_of(marker.tail) */ if (lhs->combinator() != Complex_Selector::ANCESTOR_OF) { if (marker->combinator() == Complex_Selector::ANCESTOR_OF) { return false; } if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator())) { return false; } return lhs->tail()->is_superselector_of(marker->tail()); } else if (marker->combinator() != Complex_Selector::ANCESTOR_OF) { if (marker->combinator() != Complex_Selector::PARENT_OF) { return false; } return lhs->tail()->is_superselector_of(marker->tail()); } else { return lhs->tail()->is_superselector_of(marker->tail()); } // catch-all return false; } size_t Complex_Selector::length() const { // TODO: make this iterative if (!tail()) return 1; return 1 + tail()->length(); } Complex_Selector* Complex_Selector::context(Context& ctx) { if (!tail()) return 0; if (!head()) return tail()->context(ctx); Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx)); cpy->media_block(media_block()); return cpy; } // append another complex selector at the end // check if we need to append some headers // then we need to check for the combinator // only then we can safely set the new tail void Complex_Selector::append(Context& ctx, Complex_Selector* ss) { Complex_Selector* t = ss->tail(); Combinator c = ss->combinator(); String* r = ss->reference(); Compound_Selector* h = ss->head(); if (ss->has_line_feed()) has_line_feed(true); if (ss->has_line_break()) has_line_break(true); // append old headers if (h && h->length()) { if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { error("Invalid parent selector", pstate_); } else if (last()->head_ && last()->head_->length()) { Compound_Selector* rh = last()->head(); size_t i = 0, L = h->length(); if (dynamic_cast(h->first())) { if (Selector_Qualifier* sq = dynamic_cast(rh->last())) { Selector_Qualifier* sqs = new Selector_Qualifier(*sq); sqs->name(sqs->name() + (*h)[0]->name()); (*rh)[rh->length()-1] = sqs; for (i = 1; i < L; ++i) *rh << (*h)[i]; } else if (Type_Selector* ts = dynamic_cast(rh->last())) { Type_Selector* tss = new Type_Selector(*ts); tss->name(tss->name() + (*h)[0]->name()); (*rh)[rh->length()-1] = tss; for (i = 1; i < L; ++i) *rh << (*h)[i]; } else if (Selector_Placeholder* ps = dynamic_cast(rh->last())) { Selector_Placeholder* pss = new Selector_Placeholder(*ps); pss->name(pss->name() + (*h)[0]->name()); (*rh)[rh->length()-1] = pss; for (i = 1; i < L; ++i) *rh << (*h)[i]; } else { *last()->head_ += h; } } else { *last()->head_ += h; } } else { *last()->head_ += h; } } else { // std::cerr << "has no or empty head\n"; } if (last()) { if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { Complex_Selector* inter = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate()); inter->reference(r); inter->combinator(c); inter->tail(t); last()->tail(inter); } else { if (last()->combinator() == ANCESTOR_OF) { last()->combinator(c); last()->reference(r); } last()->tail(t); } } } Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx) { if (!this->has_parent_ref()) return this; Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); *list << (*ps)[pi]; for (size_t si = 0, sL = this->length(); si < sL; ++si) { *ss += (*this)[si]->parentize(list, ctx); } } return ss; } Selector_List* Complex_Selector::parentize(Selector_List* parents, Context& ctx) { Complex_Selector* tail = this->tail(); Compound_Selector* head = this->head(); // first parentize the tail (which may return an expanded list) Selector_List* tails = tail ? tail->parentize(parents, ctx) : 0; if (head && head->length() > 0) { Selector_List* retval = 0; // we have a parent selector in a simple compound list // mix parent complex selector into the compound list if (dynamic_cast((*head)[0])) { retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); 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* t = (*tails)[n]; Complex_Selector* parent = (*parents)[i]; Complex_Selector* s = parent->cloneFully(ctx); Complex_Selector* ss = this->clone(ctx); ss->tail(t ? t->clone(ctx) : 0); Compound_Selector* h = head_->clone(ctx); if (h->length()) h->erase(h->begin()); ss->head(h->length() ? h : 0); s->append(ctx, ss); *retval << s; } } } // have no tails but parents // loop above is inside out else { for (size_t i = 0, iL = parents->length(); i < iL; ++i) { Complex_Selector* parent = (*parents)[i]; Complex_Selector* s = parent->cloneFully(ctx); Complex_Selector* ss = this->clone(ctx); // this is only if valid if the parent has no trailing op // otherwise we cannot append more simple selectors to head if (parent->last()->combinator() != ANCESTOR_OF) { throw Exception::InvalidParent(parent, ss); } ss->tail(tail ? tail->clone(ctx) : 0); Compound_Selector* h = head_->clone(ctx); if (h->length()) h->erase(h->begin()); ss->head(h->length() ? h : 0); // \/ IMO ruby sass bug \/ ss->has_line_feed(false); s->append(ctx, ss); *retval << s; } } } // 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* cpy = this->clone(ctx); cpy->tail((*tails)[n]->cloneFully(ctx)); cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate())); for (size_t i = 1, L = this->head()->length(); i < L; ++i) *cpy->head() << (*this->head())[i]; if (!cpy->head()->length()) cpy->head(0); *retval << cpy->skip_empty_reference(); } } // have no parent nor tails else { Complex_Selector* cpy = this->clone(ctx); cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate())); for (size_t i = 1, L = this->head()->length(); i < L; ++i) *cpy->head() << (*this->head())[i]; if (!cpy->head()->length()) cpy->head(0); *retval << cpy->skip_empty_reference(); } } } // no parent selector in head else { retval = this->tails(ctx, tails); } for (Simple_Selector* ss : *head) { if (Wrapped_Selector* ws = dynamic_cast(ss)) { if (Selector_List* sl = dynamic_cast(ws->selector())) { if (parents) ws->selector(sl->parentize(parents, ctx)); } } } return retval; } // has no head else { return this->tails(ctx, tails); } // unreachable return 0; } Selector_List* Complex_Selector::tails(Context& ctx, Selector_List* tails) { Selector_List* rv = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate_); if (tails && tails->length()) { for (size_t i = 0, iL = tails->length(); i < iL; ++i) { Complex_Selector* pr = this->clone(ctx); pr->tail((*tails)[i]); *rv << pr; } } else { *rv << this; } return rv; } // return the last tail that is defined Complex_Selector* Complex_Selector::first() { // declare variables used in loop Complex_Selector* cur = this; const Compound_Selector* 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 || !dynamic_cast((*head)[0])) { break; } // advance to next cur = cur->tail_; } // result return cur; } // return the last tail that is defined const Complex_Selector* Complex_Selector::first() const { // declare variables used in loop const Complex_Selector* cur = this->tail_; const Compound_Selector* head = head_; // processing loop while (cur) { // get the head head = cur->head_; // check for single parent ref if (head && head->length() == 1) { // abort (and return) if it is not a parent selector if (!dynamic_cast((*head)[0])) break; } // advance to next cur = cur->tail_; } // result return cur; } // return the last tail that is defined Complex_Selector* Complex_Selector::last() { // ToDo: implement with a while loop return tail_? tail_->last() : this; } // return the last tail that is defined const Complex_Selector* Complex_Selector::last() const { // ToDo: implement with a while loop return tail_? tail_->last() : this; } Complex_Selector::Combinator Complex_Selector::clear_innermost() { Combinator c; if (!tail() || tail()->tail() == 0) { c = combinator(); combinator(ANCESTOR_OF); tail(0); } else { c = tail()->clear_innermost(); } return c; } void Complex_Selector::set_innermost(Complex_Selector* val, Combinator c) { if (!tail()) { tail(val); combinator(c); } else { tail()->set_innermost(val, c); } } Complex_Selector* Complex_Selector::clone(Context& ctx) const { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); if (tail()) cpy->tail(tail()->clone(ctx)); return cpy; } Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); if (head()) { cpy->head(head()->clone(ctx)); } if (tail()) { cpy->tail(tail()->cloneFully(ctx)); } return cpy; } Compound_Selector* Compound_Selector::clone(Context& ctx) const { Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); cpy->extended(this->extended()); return cpy; } Selector_List* Selector_List::clone(Context& ctx) const { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); return cpy; } Selector_List* Selector_List::cloneFully(Context& ctx) const { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); for (size_t i = 0, L = length(); i < L; ++i) { *cpy << (*this)[i]->cloneFully(ctx); } return cpy; } /* not used anymore - remove? Selector_Placeholder* Selector::find_placeholder() { return 0; }*/ // remove parent selector references // basically unwraps parsed selectors void Selector_List::remove_parent_selectors() { // Check every rhs selector against left hand list for(size_t i = 0, L = length(); i < L; ++i) { if (!(*this)[i]->head()) continue; if ((*this)[i]->head()->is_empty_reference()) { // simply move to the next tail if we have "no" combinator if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) { if ((*this)[i]->tail() && (*this)[i]->has_line_feed()) { (*this)[i]->tail()->has_line_feed(true); } (*this)[i] = (*this)[i]->tail(); } // otherwise remove the first item from head else { (*this)[i]->head()->erase((*this)[i]->head()->begin()); } } } } bool Selector_List::has_parent_ref() { for (Complex_Selector* s : *this) { if (s->has_parent_ref()) return true; } return false; } void Selector_List::adjust_after_pushing(Complex_Selector* c) { // if (c->has_reference()) has_reference(true); } // it's a superselector if every selector of the right side // list is a superselector of the given left side selector bool Complex_Selector::is_superselector_of(Selector_List *sub, std::string wrapping) { // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector of the right side // list is a superselector of the given left side selector bool Selector_List::is_superselector_of(Selector_List *sub, std::string wrapping) { // Check every rhs selector against left hand list for(size_t i = 0, L = sub->length(); i < L; ++i) { if (!is_superselector_of((*sub)[i], wrapping)) return false; } return true; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors bool Selector_List::is_superselector_of(Compound_Selector *sub, std::string wrapping) { // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->is_superselector_of(sub, wrapping)) return true; } return false; } // it's a superselector if every selector on the right side // is a superselector of any one of the left side selectors bool Selector_List::is_superselector_of(Complex_Selector *sub, std::string wrapping) { // Check every lhs selector against right hand for(size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->is_superselector_of(sub)) return true; } return false; } Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) { std::vector unified_complex_selectors; // Unify all of children with RHS's children, storing the results in `unified_complex_selectors` for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) { Complex_Selector* seq1 = (*this)[lhs_i]; for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) { Complex_Selector* seq2 = (*rhs)[rhs_i]; Selector_List* result = seq1->unify_with(seq2, ctx); if( result ) { for(size_t i = 0, L = result->length(); i < L; ++i) { unified_complex_selectors.push_back( (*result)[i] ); } } } } // Creates the final Selector_List by combining all the complex selectors Selector_List* final_result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) { *final_result << *itr; } return final_result; } void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { Selector_List* extender = this; for (auto complex_sel : extendee->elements()) { Complex_Selector* c = complex_sel; // Ignore any parent selectors, until we find the first non Selector_Reference head Compound_Selector* compound_sel = c->head(); Complex_Selector* pIter = complex_sel; while (pIter) { Compound_Selector* pHead = pIter->head(); if (pHead && dynamic_cast(pHead->elements()[0]) == NULL) { compound_sel = pHead; break; } pIter = pIter->tail(); } if (!pIter->head() || pIter->tail()) { error("nested selectors may not be extended", c->pstate()); } compound_sel->is_optional(extendee->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { extends.put(compound_sel->to_str_vec(), std::make_pair((*extender)[i], compound_sel)); } } }; std::vector Compound_Selector::to_str_vec() { std::vector result; result.reserve(length()); for (size_t i = 0, L = length(); i < L; ++i) { result.push_back((*this)[i]->to_string()); } return result; } Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx) { Compound_Selector* result = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate()); // result->has_parent_reference(has_parent_reference()); // not very efficient because it needs to preserve order for (size_t i = 0, L = length(); i < L; ++i) { bool found = false; std::string thisSelector((*this)[i]->to_string(ctx.c_options)); for (size_t j = 0, M = rhs->length(); j < M; ++j) { if (thisSelector == (*rhs)[j]->to_string(ctx.c_options)) { found = true; break; } } if (!found) (*result) << (*this)[i]; } return result; } void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx) { for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { this->sources_.insert((*iterator)->clone(ctx)); } } Argument* Arguments::get_rest_argument() { Argument* arg = 0; if (this->has_rest_argument()) { for (auto a : this->elements()) { if (a->is_rest_argument()) { arg = a; break; } } } return arg; } Argument* Arguments::get_keyword_argument() { Argument* arg = 0; if (this->has_keyword_argument()) { for (auto a : this->elements()) { if (a->is_keyword_argument()) { arg = a; break; } } } return arg; } void Arguments::adjust_after_pushing(Argument* a) { if (!a->name().empty()) { if (/* has_rest_argument_ || */ has_keyword_argument_) { error("named arguments must precede variable-length argument", a->pstate()); } has_named_arguments_ = true; } else if (a->is_rest_argument()) { if (has_rest_argument_) { error("functions and mixins may only be called with one variable-length argument", a->pstate()); } if (has_keyword_argument_) { error("only keyword arguments may follow variable arguments", a->pstate()); } has_rest_argument_ = true; } else if (a->is_keyword_argument()) { if (has_keyword_argument_) { error("functions and mixins may only be called with one keyword argument", a->pstate()); } has_keyword_argument_ = true; } else { if (has_rest_argument_) { error("ordinal arguments must precede variable-length arguments", a->pstate()); } if (has_named_arguments_) { error("ordinal arguments must precede named arguments", a->pstate()); } } } bool Ruleset::is_invisible() const { Selector_List* sl = static_cast(selector()); for (size_t i = 0, L = sl->length(); i < L; ++i) if (!(*sl)[i]->has_placeholder()) return false; return true; } bool Media_Block::is_invisible() const { for (size_t i = 0, L = block()->length(); i < L; ++i) { if (!(*block())[i]->is_invisible()) return false; } return true; } Number::Number(ParserState pstate, double val, std::string u, bool zero) : Value(pstate), value_(val), zero_(zero), numerator_units_(std::vector()), denominator_units_(std::vector()), hash_(0) { size_t l = 0, r = 0; if (!u.empty()) { bool nominator = true; while (true) { r = u.find_first_of("*/", l); std::string unit(u.substr(l, r == std::string::npos ? r : r - l)); if (!unit.empty()) { if (nominator) numerator_units_.push_back(unit); else denominator_units_.push_back(unit); } if (r == std::string::npos) break; // ToDo: should error for multiple slashes // if (!nominator && u[r] == '/') error(...) if (u[r] == '/') nominator = false; l = r + 1; } } concrete_type(NUMBER); } std::string Number::unit() const { std::string u; for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { if (i) u += '*'; u += numerator_units_[i]; } if (!denominator_units_.empty()) u += '/'; for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { if (i) u += '*'; u += denominator_units_[i]; } return u; } bool Number::is_valid_css_unit() const { return numerator_units().size() <= 1 && denominator_units().size() == 0; } bool Number::is_unitless() const { return numerator_units_.empty() && denominator_units_.empty(); } void Number::normalize(const std::string& prefered, bool strict) { // first make sure same units cancel each other out // it seems that a map table will fit nicely to do this // we basically construct exponents for each unit // has the advantage that they will be pre-sorted std::map exponents; // initialize by summing up occurences in unit vectors for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]]; for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]]; // the final conversion factor double factor = 1; // get the first entry of numerators // forward it when entry is converted std::vector::iterator nom_it = numerator_units_.begin(); std::vector::iterator nom_end = numerator_units_.end(); std::vector::iterator denom_it = denominator_units_.begin(); std::vector::iterator denom_end = denominator_units_.end(); // main normalization loop // should be close to optimal while (denom_it != denom_end) { // get and increment afterwards const std::string denom = *(denom_it ++); // skip already canceled out unit if (exponents[denom] >= 0) continue; // skip all units we don't know how to convert if (string_to_unit(denom) == UNKNOWN) continue; // now search for nominator while (nom_it != nom_end) { // get and increment afterwards const std::string nom = *(nom_it ++); // skip already canceled out unit if (exponents[nom] <= 0) continue; // skip all units we don't know how to convert if (string_to_unit(nom) == UNKNOWN) continue; // we now have two convertable units // add factor for current conversion factor *= conversion_factor(nom, denom, strict); // update nominator/denominator exponent -- exponents[nom]; ++ exponents[denom]; // inner loop done break; } } // now we can build up the new unit arrays numerator_units_.clear(); denominator_units_.clear(); // build them by iterating over the exponents for (auto exp : exponents) { // maybe there is more effecient way to push // the same item multiple times to a vector? for(size_t i = 0, S = abs(exp.second); i < S; ++i) { // opted to have these switches in the inner loop // makes it more readable and should not cost much if (!exp.first.empty()) { if (exp.second < 0) denominator_units_.push_back(exp.first); else if (exp.second > 0) numerator_units_.push_back(exp.first); } } } // apply factor to value_ // best precision this way value_ *= factor; // maybe convert to other unit // easier implemented on its own try { convert(prefered, strict); } catch (incompatibleUnits& err) { error(err.what(), pstate()); } catch (...) { throw; } } // this does not cover all cases (multiple prefered units) double Number::convert_factor(const Number& n) const { // first make sure same units cancel each other out // it seems that a map table will fit nicely to do this // we basically construct exponents for each unit class // std::map exponents; // initialize by summing up occurences in unit vectors // for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[unit_to_class(numerator_units_[i])]; // for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[unit_to_class(denominator_units_[i])]; std::vector l_miss_nums(0); std::vector l_miss_dens(0); // create copy since we need these for state keeping std::vector r_nums(n.numerator_units_); std::vector r_dens(n.denominator_units_); std::vector::const_iterator l_num_it = numerator_units_.begin(); std::vector::const_iterator l_num_end = numerator_units_.end(); bool l_unitless = is_unitless(); bool r_unitless = n.is_unitless(); // overall conversion double factor = 1; // process all left numerators while (l_num_it != l_num_end) { // get and increment afterwards const std::string l_num = *(l_num_it ++); std::vector::iterator r_num_it = r_nums.begin(); std::vector::iterator r_num_end = r_nums.end(); bool found = false; // search for compatible numerator while (r_num_it != r_num_end) { // get and increment afterwards const std::string r_num = *(r_num_it); // get possible converstion factor for units double conversion = conversion_factor(l_num, r_num, false); // skip incompatible numerator if (conversion == 0) { ++ r_num_it; continue; } // apply to global factor factor *= conversion; // remove item from vector r_nums.erase(r_num_it); // found numerator found = true; break; } // maybe we did not find any // left numerator is leftover if (!found) l_miss_nums.push_back(l_num); } std::vector::const_iterator l_den_it = denominator_units_.begin(); std::vector::const_iterator l_den_end = denominator_units_.end(); // process all left denominators while (l_den_it != l_den_end) { // get and increment afterwards const std::string l_den = *(l_den_it ++); std::vector::iterator r_den_it = r_dens.begin(); std::vector::iterator r_den_end = r_dens.end(); bool found = false; // search for compatible denominator while (r_den_it != r_den_end) { // get and increment afterwards const std::string r_den = *(r_den_it); // get possible converstion factor for units double conversion = conversion_factor(l_den, r_den, false); // skip incompatible denominator if (conversion == 0) { ++ r_den_it; continue; } // apply to global factor factor *= conversion; // remove item from vector r_dens.erase(r_den_it); // found denominator found = true; break; } // maybe we did not find any // left denominator is leftover if (!found) l_miss_dens.push_back(l_den); } // check left-overs (ToDo: might cancel out) if (l_miss_nums.size() > 0 && !r_unitless) { throw Exception::IncompatibleUnits(n, *this); } if (l_miss_dens.size() > 0 && !r_unitless) { throw Exception::IncompatibleUnits(n, *this); } if (r_nums.size() > 0 && !l_unitless) { throw Exception::IncompatibleUnits(n, *this); } if (r_dens.size() > 0 && !l_unitless) { throw Exception::IncompatibleUnits(n, *this); } return factor; } // this does not cover all cases (multiple prefered units) bool Number::convert(const std::string& prefered, bool strict) { // no conversion if unit is empty if (prefered.empty()) return true; // first make sure same units cancel each other out // it seems that a map table will fit nicely to do this // we basically construct exponents for each unit // has the advantage that they will be pre-sorted std::map exponents; // initialize by summing up occurences in unit vectors for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]]; for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]]; // the final conversion factor double factor = 1; std::vector::iterator denom_it = denominator_units_.begin(); std::vector::iterator denom_end = denominator_units_.end(); // main normalization loop // should be close to optimal while (denom_it != denom_end) { // get and increment afterwards const std::string denom = *(denom_it ++); // check if conversion is needed if (denom == prefered) continue; // skip already canceled out unit if (exponents[denom] >= 0) continue; // skip all units we don't know how to convert if (string_to_unit(denom) == UNKNOWN) continue; // we now have two convertable units // add factor for current conversion factor *= conversion_factor(denom, prefered, strict); // update nominator/denominator exponent ++ exponents[denom]; -- exponents[prefered]; } std::vector::iterator nom_it = numerator_units_.begin(); std::vector::iterator nom_end = numerator_units_.end(); // now search for nominator while (nom_it != nom_end) { // get and increment afterwards const std::string nom = *(nom_it ++); // check if conversion is needed if (nom == prefered) continue; // skip already canceled out unit if (exponents[nom] <= 0) continue; // skip all units we don't know how to convert if (string_to_unit(nom) == UNKNOWN) continue; // we now have two convertable units // add factor for current conversion factor *= conversion_factor(nom, prefered, strict); // update nominator/denominator exponent -- exponents[nom]; ++ exponents[prefered]; } // now we can build up the new unit arrays numerator_units_.clear(); denominator_units_.clear(); // build them by iterating over the exponents for (auto exp : exponents) { // maybe there is more effecient way to push // the same item multiple times to a vector? for(size_t i = 0, S = abs(exp.second); i < S; ++i) { // opted to have these switches in the inner loop // makes it more readable and should not cost much if (!exp.first.empty()) { if (exp.second < 0) denominator_units_.push_back(exp.first); else if (exp.second > 0) numerator_units_.push_back(exp.first); } } } // apply factor to value_ // best precision this way value_ *= factor; // success? return true; } // useful for making one number compatible with another std::string Number::find_convertible_unit() const { for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { std::string u(numerator_units_[i]); if (string_to_unit(u) != UNKNOWN) return u; } for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { std::string u(denominator_units_[i]); if (string_to_unit(u) != UNKNOWN) return u; } return std::string(); } bool Custom_Warning::operator== (const Expression& rhs) const { if (const Custom_Warning* r = dynamic_cast(&rhs)) { return message() == r->message(); } return false; } bool Custom_Error::operator== (const Expression& rhs) const { if (const Custom_Error* r = dynamic_cast(&rhs)) { return message() == r->message(); } return false; } bool Number::operator== (const Expression& rhs) const { if (const Number* r = dynamic_cast(&rhs)) { size_t lhs_units = numerator_units_.size() + denominator_units_.size(); size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size(); // unitless and only having one unit seems equivalent (will change in future) if (!lhs_units || !rhs_units) { return std::fabs(value() - r->value()) < NUMBER_EPSILON; } return (numerator_units_ == r->numerator_units_) && (denominator_units_ == r->denominator_units_) && std::fabs(value() - r->value()) < NUMBER_EPSILON; } return false; } bool Number::operator< (const Number& rhs) const { size_t lhs_units = numerator_units_.size() + denominator_units_.size(); size_t rhs_units = rhs.numerator_units_.size() + rhs.denominator_units_.size(); // unitless and only having one unit seems equivalent (will change in future) if (!lhs_units || !rhs_units) { return value() < rhs.value(); } Number tmp_r(rhs); tmp_r.normalize(find_convertible_unit()); std::string l_unit(unit()); std::string r_unit(tmp_r.unit()); if (unit() != tmp_r.unit()) { error("cannot compare numbers with incompatible units", pstate()); } return value() < tmp_r.value(); } bool String_Quoted::operator== (const Expression& rhs) const { if (const String_Quoted* qstr = dynamic_cast(&rhs)) { return (value() == qstr->value()); } else if (const String_Constant* cstr = dynamic_cast(&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 (const String_Quoted* qstr = dynamic_cast(&rhs)) { return (value() == qstr->value()); } else if (const String_Constant* cstr = dynamic_cast(&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 (const String_Schema* r = dynamic_cast(&rhs)) { if (length() != r->length()) return false; for (size_t i = 0, L = length(); i < L; ++i) { Expression* rv = (*r)[i]; Expression* lv = (*this)[i]; if (!lv || !rv) return false; if (!(*lv == *rv)) return false; } return true; } return false; } bool Boolean::operator== (const Expression& rhs) const { if (const Boolean* r = dynamic_cast(&rhs)) { return (value() == r->value()); } return false; } bool Color::operator== (const Expression& rhs) const { if (const Color* r = dynamic_cast(&rhs)) { return r_ == r->r() && g_ == r->g() && b_ == r->b() && a_ == r->a(); } return false; } bool List::operator== (const Expression& rhs) const { if (const List* r = dynamic_cast(&rhs)) { if (length() != r->length()) return false; if (separator() != r->separator()) return false; for (size_t i = 0, L = length(); i < L; ++i) { Expression* rv = (*r)[i]; Expression* lv = (*this)[i]; if (!lv || !rv) return false; if (!(*lv == *rv)) return false; } return true; } return false; } bool Map::operator== (const Expression& rhs) const { if (const Map* r = dynamic_cast(&rhs)) { if (length() != r->length()) return false; for (auto key : keys()) { Expression* lv = at(key); Expression* rv = r->at(key); if (!rv || !lv) return false; if (!(*lv == *rv)) return false; } return true; } return false; } bool Null::operator== (const Expression& rhs) const { return rhs.concrete_type() == NULL_VAL; } size_t List::size() const { if (!is_arglist_) return length(); // arglist expects a list of arguments // so we need to break before keywords for (size_t i = 0, L = length(); i < L; ++i) { if (Argument* arg = dynamic_cast((*this)[i])) { if (!arg->name().empty()) return i; } } return length(); } Expression* Hashed::at(Expression* k) const { if (elements_.count(k)) { return elements_.at(k); } else { return &sass_null; } } bool Binary_Expression::is_left_interpolant(void) const { return is_interpolant() || (left() && left()->is_left_interpolant()); } bool Binary_Expression::is_right_interpolant(void) const { return is_interpolant() || (right() && right()->is_right_interpolant()); } std::string AST_Node::to_string(Sass_Inspect_Options opt) const { Sass_Output_Options out(opt); Emitter emitter(out); Inspect i(emitter); i.in_declaration = true; // ToDo: inspect should be const const_cast(this)->perform(&i); return i.get_buffer(); } std::string AST_Node::to_string() const { return to_string({ NESTED, 5 }); } std::string String_Quoted::inspect() const { return quote(value_, '*'); } std::string String_Constant::inspect() const { return quote(value_, '*'); } ////////////////////////////////////////////////////////////////////////////////////////// // Additional method on Lists to retrieve values directly or from an encompassed Argument. ////////////////////////////////////////////////////////////////////////////////////////// Expression* List::value_at_index(size_t i) { if (is_arglist_) { if (Argument* arg = dynamic_cast((*this)[i])) { return arg->value(); } else { return (*this)[i]; } } else { return (*this)[i]; } } } libsass-3.3.4/src/ast.hpp000066400000000000000000002462461267254216700153020ustar00rootroot00000000000000#ifndef SASS_AST_H #define SASS_AST_H #include #include #include #include #include #include #include #include #include #include "sass/base.h" #ifdef __clang__ /* * There are some overloads used here that trigger the clang overload * hiding warning. Specifically: * * Type type() which hides string type() from Expression * * and * * Block* block() which hides virtual Block* block() from Statement * */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" #endif #include "util.hpp" #include "units.hpp" #include "context.hpp" #include "position.hpp" #include "constants.hpp" #include "operation.hpp" #include "position.hpp" #include "inspect.hpp" #include "source_map.hpp" #include "environment.hpp" #include "error_handling.hpp" #include "ast_def_macros.hpp" #include "ast_fwd_decl.hpp" #include "source_map.hpp" #include "sass.h" namespace Sass { // ToDo: should this really be hardcoded // Note: most methods follow precision option const double NUMBER_EPSILON = 0.00000000000001; // ToDo: where does this fit best? // We don't share this with C-API? class Operand { public: Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false) : operand(operand), ws_before(ws_before), ws_after(ws_after) { } public: enum Sass_OP operand; bool ws_before; bool ws_after; }; // 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 void hash_combine (std::size_t& seed, const T& val) { seed ^= std::hash()(val) + 0x9e3779b9 + (seed<<6) + (seed>>2); } ////////////////////////////////////////////////////////// // Abstract base class for all abstract syntax tree nodes. ////////////////////////////////////////////////////////// class AST_Node : public Memory_Object { ADD_PROPERTY(ParserState, pstate) public: AST_Node(ParserState pstate) : pstate_(pstate) { } virtual ~AST_Node() = 0; virtual size_t hash() { 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_string() const; // virtual Block* block() { return 0; } public: void update_pstate(const ParserState& pstate); public: Offset off() { return pstate(); } Position pos() { return pstate(); } ATTACH_OPERATIONS() }; inline AST_Node::~AST_Node() { } ////////////////////////////////////////////////////////////////////// // Abstract base class for expressions. This side of the AST hierarchy // represents elements in value contexts, which exist primarily to be // evaluated and returned. ////////////////////////////////////////////////////////////////////// class Expression : public AST_Node { public: enum Concrete_Type { NONE, BOOLEAN, NUMBER, COLOR, STRING, LIST, MAP, SELECTOR, NULL_VAL, C_WARNING, C_ERROR, NUM_TYPES }; private: // expressions in some contexts shouldn't be evaluated ADD_PROPERTY(bool, is_delayed) ADD_PROPERTY(bool, is_expanded) ADD_PROPERTY(bool, is_interpolant) ADD_PROPERTY(Concrete_Type, concrete_type) public: Expression(ParserState pstate, bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) : AST_Node(pstate), is_delayed_(d), is_expanded_(d), is_interpolant_(i), concrete_type_(ct) { } virtual operator bool() { return true; } virtual ~Expression() { } virtual std::string type() { return ""; /* TODO: raise an error? */ } virtual bool is_invisible() const { return false; } static std::string type_name() { return ""; } virtual bool is_false() { return false; } virtual bool operator== (const Expression& rhs) const { return false; } virtual void set_delayed(bool delayed) { is_delayed(delayed); } virtual 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 }); } 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) { } 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) { } 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 { size_t operator()(Sass::Expression* s) const { return s->hash(); } }; template<> struct equal_to { bool operator()( Sass::Expression* lhs, Sass::Expression* rhs) const { return lhs->hash() == rhs->hash(); } }; } namespace Sass { ///////////////////////////////////////////////////////////////////////////// // Mixin class for AST nodes that should behave like vectors. Uses the // "Template Method" design pattern to allow subclasses to adjust their flags // when certain objects are pushed. ///////////////////////////////////////////////////////////////////////////// template class Vectorized { std::vector 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()) { elements_.reserve(s); } virtual ~Vectorized() = 0; size_t length() const { return elements_.size(); } bool empty() const { return elements_.empty(); } 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); } const T& operator[](size_t i) const { return elements_[i]; } Vectorized& operator<<(T element) { if (!element) return *this; reset_hash(); elements_.push_back(element); adjust_after_pushing(element); return *this; } Vectorized& operator+=(Vectorized* v) { for (size_t i = 0, L = v->length(); i < L; ++i) *this << (*v)[i]; return *this; } Vectorized& unshift(T element) { elements_.insert(elements_.begin(), element); return *this; } std::vector& elements() { return elements_; } const std::vector& elements() const { return elements_; } std::vector& elements(std::vector& 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::iterator end() { return elements_.end(); } typename std::vector::iterator begin() { return elements_.begin(); } typename std::vector::const_iterator end() const { return elements_.end(); } typename std::vector::const_iterator begin() const { return elements_.begin(); } typename std::vector::iterator erase(typename std::vector::iterator el) { return elements_.erase(el); } typename std::vector::const_iterator erase(typename std::vector::const_iterator el) { return elements_.erase(el); } }; template inline Vectorized::~Vectorized() { } ///////////////////////////////////////////////////////////////////////////// // Mixin class for AST nodes that should behave like a hash table. Uses an // extra internally to maintain insertion order for interation. ///////////////////////////////////////////////////////////////////////////// class Hashed { private: std::unordered_map elements_; std::vector list_; protected: size_t hash_; Expression* duplicate_key_; void reset_hash() { hash_ = 0; } void reset_duplicate_key() { duplicate_key_ = 0; } virtual void adjust_after_pushing(std::pair p) { } public: Hashed(size_t s = 0) : elements_(std::unordered_map(s)), list_(std::vector()) { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); } virtual ~Hashed(); size_t length() const { return list_.size(); } bool empty() const { return list_.empty(); } bool has(Expression* k) const { return elements_.count(k) == 1; } Expression* at(Expression* k) const; bool has_duplicate_key() const { return duplicate_key_ != 0; } Expression* get_duplicate_key() const { return duplicate_key_; } const std::unordered_map elements() { return elements_; } Hashed& operator<<(std::pair p) { reset_hash(); if (!has(p.first)) list_.push_back(p.first); else if (!duplicate_key_) duplicate_key_ = p.first; elements_[p.first] = p.second; adjust_after_pushing(p); return *this; } Hashed& operator+=(Hashed* h) { if (length() == 0) { this->elements_ = h->elements_; this->list_ = h->list_; return *this; } for (auto key : h->keys()) { *this << std::make_pair(key, h->at(key)); } reset_duplicate_key(); return *this; } const std::unordered_map& pairs() const { return elements_; } const std::vector& keys() const { return list_; } std::unordered_map::iterator end() { return elements_.end(); } std::unordered_map::iterator begin() { return elements_.begin(); } std::unordered_map::const_iterator end() const { return elements_.end(); } std::unordered_map::const_iterator begin() const { return elements_.begin(); } }; inline Hashed::~Hashed() { } ///////////////////////////////////////////////////////////////////////// // Abstract base class for statements. This side of the AST hierarchy // represents elements in expansion contexts, which exist primarily to be // rewritten and macro-expanded. ///////////////////////////////////////////////////////////////////////// class Statement : public AST_Node { public: enum Statement_Type { NONE, RULESET, MEDIA, DIRECTIVE, SUPPORTS, ATROOT, BUBBLE, CONTENT, KEYFRAMERULE, DECLARATION, ASSIGNMENT, IMPORT_STUB, IMPORT, COMMENT, WARNING, RETURN, EXTEND, ERROR, DEBUGSTMT, WHILE, EACH, FOR, IF }; private: ADD_PROPERTY(Block*, block) ADD_PROPERTY(Statement_Type, statement_type) ADD_PROPERTY(size_t, tabs) ADD_PROPERTY(bool, group_end) public: Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0) : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false) { } virtual ~Statement() = 0; // needed for rearranging nested rulesets during CSS emission virtual bool is_hoistable() { return false; } virtual bool is_invisible() const { return false; } virtual bool bubbles() { return false; } virtual Block* block() { return 0; } virtual bool has_content() { return statement_type_ == CONTENT; } }; inline Statement::~Statement() { } //////////////////////// // Blocks of statements. //////////////////////// class Block : public Statement, public Vectorized { ADD_PROPERTY(bool, is_root) ADD_PROPERTY(bool, is_at_root); // needed for properly formatted CSS emission ADD_PROPERTY(bool, has_hoistable) ADD_PROPERTY(bool, has_non_hoistable) protected: void adjust_after_pushing(Statement* s) { if (s->is_hoistable()) has_hoistable_ = true; else has_non_hoistable_ = true; } public: Block(ParserState pstate, size_t s = 0, bool r = false) : Statement(pstate), Vectorized(s), is_root_(r), is_at_root_(false), has_hoistable_(false), has_non_hoistable_(false) { } virtual bool has_content() { for (size_t i = 0, L = elements().size(); i < L; ++i) { if (elements()[i]->has_content()) return true; } return Statement::has_content(); } Block* block() { return this; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////// // Abstract base class for statements that contain blocks of statements. //////////////////////////////////////////////////////////////////////// class Has_Block : public Statement { ADD_PROPERTY(Block*, block) public: Has_Block(ParserState pstate, Block* b) : Statement(pstate), block_(b) { } virtual bool has_content() { return (block_ && block_->has_content()) || Statement::has_content(); } virtual ~Has_Block() = 0; }; inline Has_Block::~Has_Block() { } ///////////////////////////////////////////////////////////////////////////// // Rulesets (i.e., sets of styles headed by a selector and containing a block // of style declarations. ///////////////////////////////////////////////////////////////////////////// class Ruleset : public Has_Block { ADD_PROPERTY(Selector*, selector) ADD_PROPERTY(bool, at_root); ADD_PROPERTY(bool, is_root); public: Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0) : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false) { statement_type(RULESET); } bool is_invisible() const; // nested rulesets need to be hoisted out of their enclosing blocks bool is_hoistable() { return true; } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////// // Nested declaration sets (i.e., namespaced properties). ///////////////////////////////////////////////////////// class Propset : public Has_Block { ADD_PROPERTY(String*, property_fragment) public: Propset(ParserState pstate, String* pf, Block* b = 0) : Has_Block(pstate, b), property_fragment_(pf) { } ATTACH_OPERATIONS() }; ///////////////// // Bubble. ///////////////// class Bubble : public Statement { ADD_PROPERTY(Statement*, node) ADD_PROPERTY(bool, group_end) public: Bubble(ParserState pstate, Statement* n, Statement* g = 0, size_t t = 0) : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0) { } bool bubbles() { return true; } ATTACH_OPERATIONS() }; ///////////////// // Media queries. ///////////////// class Media_Block : public Has_Block { ADD_PROPERTY(List*, media_queries) public: Media_Block(ParserState pstate, List* mqs, Block* b) : Has_Block(pstate, b), media_queries_(mqs) { statement_type(MEDIA); } Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s) : Has_Block(pstate, b), media_queries_(mqs) { statement_type(MEDIA); } bool bubbles() { return true; } bool is_hoistable() { return true; } bool is_invisible() const; ATTACH_OPERATIONS() }; /////////////////////////////////////////////////////////////////////// // At-rules -- arbitrary directives beginning with "@" that may have an // optional statement block. /////////////////////////////////////////////////////////////////////// class At_Rule : public Has_Block { ADD_PROPERTY(std::string, keyword) ADD_PROPERTY(Selector*, selector) ADD_PROPERTY(Expression*, value) public: At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0) : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed { statement_type(DIRECTIVE); } bool bubbles() { return is_keyframes() || is_media(); } bool is_media() { return keyword_.compare("@-webkit-media") == 0 || keyword_.compare("@-moz-media") == 0 || keyword_.compare("@-o-media") == 0 || keyword_.compare("@media") == 0; } bool is_keyframes() { return keyword_.compare("@-webkit-keyframes") == 0 || keyword_.compare("@-moz-keyframes") == 0 || keyword_.compare("@-o-keyframes") == 0 || keyword_.compare("@keyframes") == 0; } ATTACH_OPERATIONS() }; /////////////////////////////////////////////////////////////////////// // Keyframe-rules -- the child blocks of "@keyframes" nodes. /////////////////////////////////////////////////////////////////////// class Keyframe_Rule : public Has_Block { ADD_PROPERTY(Selector*, selector) public: Keyframe_Rule(ParserState pstate, Block* b) : Has_Block(pstate, b), selector_(0) { statement_type(KEYFRAMERULE); } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////// // Declarations -- style rules consisting of a property name and values. //////////////////////////////////////////////////////////////////////// class Declaration : public Statement { ADD_PROPERTY(String*, property) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_important) ADD_PROPERTY(bool, is_indented) public: Declaration(ParserState pstate, String* prop, Expression* val, bool i = false) : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false) { statement_type(DECLARATION); } ATTACH_OPERATIONS() }; ///////////////////////////////////// // Assignments -- variable and value. ///////////////////////////////////// class Assignment : public Statement { ADD_PROPERTY(std::string, variable) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_default) ADD_PROPERTY(bool, is_global) public: Assignment(ParserState pstate, std::string var, Expression* val, bool is_default = false, bool is_global = false) : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global) { statement_type(ASSIGNMENT); } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////////// // Import directives. CSS and Sass import lists can be intermingled, so it's // necessary to store a list of each in an Import node. //////////////////////////////////////////////////////////////////////////// class Import : public Statement { std::vector urls_; std::vector incs_; ADD_PROPERTY(List*, media_queries); public: Import(ParserState pstate) : Statement(pstate), urls_(std::vector()), incs_(std::vector()), media_queries_(0) { statement_type(IMPORT); } std::vector& urls() { return urls_; } std::vector& incs() { return incs_; } ATTACH_OPERATIONS() }; // not yet resolved single import // so far we only know requested name class Import_Stub : public Statement { Include resource_; public: std::string abs_path() { return resource_.abs_path; }; std::string imp_path() { return resource_.imp_path; }; Include resource() { return resource_; }; Import_Stub(ParserState pstate, Include res) : Statement(pstate), resource_(res) { statement_type(IMPORT_STUB); } ATTACH_OPERATIONS() }; ////////////////////////////// // The Sass `@warn` directive. ////////////////////////////// class Warning : public Statement { ADD_PROPERTY(Expression*, message) public: Warning(ParserState pstate, Expression* msg) : Statement(pstate), message_(msg) { statement_type(WARNING); } ATTACH_OPERATIONS() }; /////////////////////////////// // The Sass `@error` directive. /////////////////////////////// class Error : public Statement { ADD_PROPERTY(Expression*, message) public: Error(ParserState pstate, Expression* msg) : Statement(pstate), message_(msg) { statement_type(ERROR); } ATTACH_OPERATIONS() }; /////////////////////////////// // The Sass `@debug` directive. /////////////////////////////// class Debug : public Statement { ADD_PROPERTY(Expression*, value) public: Debug(ParserState pstate, Expression* val) : Statement(pstate), value_(val) { statement_type(DEBUGSTMT); } ATTACH_OPERATIONS() }; /////////////////////////////////////////// // CSS comments. These may be interpolated. /////////////////////////////////////////// class Comment : public Statement { ADD_PROPERTY(String*, text) ADD_PROPERTY(bool, is_important) public: Comment(ParserState pstate, String* txt, bool is_important) : Statement(pstate), text_(txt), is_important_(is_important) { statement_type(COMMENT); } virtual bool is_invisible() const { return is_important() == false; } ATTACH_OPERATIONS() }; //////////////////////////////////// // The Sass `@if` control directive. //////////////////////////////////// class If : public Has_Block { ADD_PROPERTY(Expression*, predicate) ADD_PROPERTY(Block*, alternative) public: If(ParserState pstate, Expression* pred, Block* con, Block* alt = 0) : Has_Block(pstate, con), predicate_(pred), alternative_(alt) { statement_type(IF); } virtual bool has_content() { return Has_Block::has_content() || (alternative_ && alternative_->has_content()); } ATTACH_OPERATIONS() }; ///////////////////////////////////// // The Sass `@for` control directive. ///////////////////////////////////// class For : public Has_Block { ADD_PROPERTY(std::string, variable) ADD_PROPERTY(Expression*, lower_bound) ADD_PROPERTY(Expression*, upper_bound) ADD_PROPERTY(bool, is_inclusive) public: For(ParserState pstate, std::string var, Expression* lo, Expression* hi, Block* b, bool inc) : Has_Block(pstate, b), variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) { statement_type(FOR); } ATTACH_OPERATIONS() }; ////////////////////////////////////// // The Sass `@each` control directive. ////////////////////////////////////// class Each : public Has_Block { ADD_PROPERTY(std::vector, variables) ADD_PROPERTY(Expression*, list) public: Each(ParserState pstate, std::vector vars, Expression* lst, Block* b) : Has_Block(pstate, b), variables_(vars), list_(lst) { statement_type(EACH); } ATTACH_OPERATIONS() }; /////////////////////////////////////// // The Sass `@while` control directive. /////////////////////////////////////// class While : public Has_Block { ADD_PROPERTY(Expression*, predicate) public: While(ParserState pstate, Expression* pred, Block* b) : Has_Block(pstate, b), predicate_(pred) { statement_type(WHILE); } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////////// // The @return directive for use inside SassScript functions. ///////////////////////////////////////////////////////////// class Return : public Statement { ADD_PROPERTY(Expression*, value) public: Return(ParserState pstate, Expression* val) : Statement(pstate), value_(val) { statement_type(RETURN); } ATTACH_OPERATIONS() }; //////////////////////////////// // The Sass `@extend` directive. //////////////////////////////// class Extension : public Statement { ADD_PROPERTY(Selector*, selector) public: Extension(ParserState pstate, Selector* s) : Statement(pstate), selector_(s) { statement_type(EXTEND); } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////////////////////////// // Definitions for both mixins and functions. The two cases are distinguished // by a type tag. ///////////////////////////////////////////////////////////////////////////// struct Backtrace; typedef Environment Env; typedef const char* Signature; typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*); typedef const char* Signature; class Definition : public Has_Block { public: enum Type { MIXIN, FUNCTION }; ADD_PROPERTY(std::string, name) ADD_PROPERTY(Parameters*, parameters) ADD_PROPERTY(Env*, environment) ADD_PROPERTY(Type, type) ADD_PROPERTY(Native_Function, native_function) ADD_PROPERTY(Sass_Function_Entry, c_function) ADD_PROPERTY(void*, cookie) ADD_PROPERTY(bool, is_overload_stub) ADD_PROPERTY(Signature, signature) public: Definition(ParserState pstate, std::string n, Parameters* params, Block* b, Type t) : Has_Block(pstate, b), name_(n), parameters_(params), environment_(0), type_(t), native_function_(0), c_function_(0), cookie_(0), is_overload_stub_(false), signature_(0) { } Definition(ParserState pstate, Signature sig, std::string n, Parameters* params, Native_Function func_ptr, bool overload_stub = false) : Has_Block(pstate, 0), name_(n), parameters_(params), environment_(0), type_(FUNCTION), native_function_(func_ptr), c_function_(0), cookie_(0), is_overload_stub_(overload_stub), signature_(sig) { } Definition(ParserState pstate, Signature sig, std::string n, Parameters* params, Sass_Function_Entry c_func, bool whatever, bool whatever2) : Has_Block(pstate, 0), name_(n), parameters_(params), environment_(0), type_(FUNCTION), native_function_(0), c_function_(c_func), cookie_(sass_function_get_cookie(c_func)), is_overload_stub_(false), signature_(sig) { } ATTACH_OPERATIONS() }; ////////////////////////////////////// // Mixin calls (i.e., `@include ...`). ////////////////////////////////////// class Mixin_Call : public Has_Block { ADD_PROPERTY(std::string, name) ADD_PROPERTY(Arguments*, arguments) public: Mixin_Call(ParserState pstate, std::string n, Arguments* args, Block* b = 0) : Has_Block(pstate, b), name_(n), arguments_(args) { } ATTACH_OPERATIONS() }; /////////////////////////////////////////////////// // The @content directive for mixin content blocks. /////////////////////////////////////////////////// class Content : public Statement { public: Content(ParserState pstate) : Statement(pstate) { statement_type(CONTENT); } ATTACH_OPERATIONS() }; /////////////////////////////////////////////////////////////////////// // Lists of values, both comma- and space-separated (distinguished by a // type-tag.) Also used to represent variable-length argument lists. /////////////////////////////////////////////////////////////////////// class List : public Value, public Vectorized { void adjust_after_pushing(Expression* e) { is_expanded(false); } private: ADD_PROPERTY(enum Sass_Separator, separator) ADD_PROPERTY(bool, is_arglist) public: List(ParserState pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false) : Value(pstate), Vectorized(size), separator_(sep), is_arglist_(argl) { concrete_type(LIST); } std::string type() { return is_arglist_ ? "arglist" : "list"; } static std::string type_name() { return "list"; } const char* sep_string(bool compressed = false) const { return separator() == SASS_SPACE ? " " : (compressed ? "," : ", "); } bool is_invisible() const { return empty(); } Expression* value_at_index(size_t i); virtual size_t size() const; virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(sep_string()); for (size_t i = 0, L = length(); i < L; ++i) hash_combine(hash_, (elements()[i])->hash()); } return hash_; } virtual void set_delayed(bool delayed) { for (size_t i = 0, L = length(); i < L; ++i) (elements()[i])->set_delayed(delayed); is_delayed(delayed); } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; /////////////////////////////////////////////////////////////////////// // Key value paris. /////////////////////////////////////////////////////////////////////// class Map : public Value, public Hashed { void adjust_after_pushing(std::pair p) { is_expanded(false); } public: Map(ParserState pstate, size_t size = 0) : Value(pstate), Hashed(size) { concrete_type(MAP); } std::string type() { return "map"; } static std::string type_name() { return "map"; } bool is_invisible() const { return empty(); } virtual size_t hash() { if (hash_ == 0) { for (auto key : keys()) { hash_combine(hash_, key->hash()); hash_combine(hash_, at(key)->hash()); } } return hash_; } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; inline static const std::string sass_op_to_name(enum Sass_OP op) { switch (op) { case AND: return "and"; break; case OR: return "or"; break; case EQ: return "eq"; break; case NEQ: return "neq"; break; case GT: return "gt"; break; case GTE: return "gte"; break; case LT: return "lt"; break; case LTE: return "lte"; break; case ADD: return "plus"; break; case SUB: return "sub"; break; case MUL: return "times"; break; case DIV: return "div"; break; case MOD: return "mod"; break; // this is only used internally! case NUM_OPS: return "[OPS]"; break; default: return "invalid"; break; } } ////////////////////////////////////////////////////////////////////////// // Binary expressions. Represents logical, relational, and arithmetic // operations. Templatized to avoid large switch statements and repetitive // subclassing. ////////////////////////////////////////////////////////////////////////// class Binary_Expression : public PreValue { private: ADD_HASHED(Operand, op) ADD_HASHED(Expression*, left) ADD_HASHED(Expression*, right) size_t hash_; public: Binary_Expression(ParserState pstate, Operand op, Expression* lhs, Expression* rhs) : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) { } const std::string type_name() { switch (type()) { case AND: return "and"; break; case OR: return "or"; break; case EQ: return "eq"; break; case NEQ: return "neq"; break; case GT: return "gt"; break; case GTE: return "gte"; break; case LT: return "lt"; break; case LTE: return "lte"; break; case ADD: return "add"; break; case SUB: return "sub"; break; case MUL: return "mul"; break; case DIV: return "div"; break; case MOD: return "mod"; break; // this is only used internally! case NUM_OPS: return "[OPS]"; break; default: return "invalid"; break; } } const std::string separator() { switch (type()) { case AND: return "&&"; break; case OR: return "||"; break; case EQ: return "=="; break; case NEQ: return "!="; break; case GT: return ">"; break; case GTE: return ">="; break; case LT: return "<"; break; case LTE: return "<="; break; case ADD: return "+"; break; case SUB: return "-"; break; case MUL: return "*"; break; case DIV: return "/"; break; case MOD: return "%"; break; // this is only used internally! case NUM_OPS: return "[OPS]"; break; default: return "invalid"; break; } } bool is_left_interpolant(void) const; bool is_right_interpolant(void) const; bool has_interpolant() const { return is_left_interpolant() || is_right_interpolant(); } virtual void set_delayed(bool delayed) { right()->set_delayed(delayed); left()->set_delayed(delayed); is_delayed(delayed); } virtual bool operator==(const Expression& rhs) const { try { const Binary_Expression* m = dynamic_cast(&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()(type()); hash_combine(hash_, left()->hash()); hash_combine(hash_, right()->hash()); } return hash_; } enum Sass_OP type() const { return op_.operand; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////////// // Arithmetic negation (logical negation is just an ordinary function call). //////////////////////////////////////////////////////////////////////////// class Unary_Expression : public Expression { public: enum Type { PLUS, MINUS, NOT }; private: ADD_HASHED(Type, type) ADD_HASHED(Expression*, operand) size_t hash_; public: Unary_Expression(ParserState pstate, Type t, Expression* o) : Expression(pstate), type_(t), operand_(o), hash_(0) { } const std::string type_name() { switch (type_) { case PLUS: return "plus"; break; case MINUS: return "minus"; break; case NOT: return "not"; break; default: return "invalid"; break; } } virtual bool operator==(const Expression& rhs) const { try { const Unary_Expression* m = dynamic_cast(&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()(type_); hash_combine(hash_, operand()->hash()); }; return hash_; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////// // Individual argument objects for mixin and function calls. //////////////////////////////////////////////////////////// class Argument : public Expression { ADD_HASHED(Expression*, value) ADD_HASHED(std::string, name) ADD_PROPERTY(bool, is_rest_argument) ADD_PROPERTY(bool, is_keyword_argument) size_t hash_; public: Argument(ParserState pstate, Expression* val, std::string n = "", bool rest = false, bool keyword = false) : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0) { if (!name_.empty() && is_rest_argument_) { error("variable-length argument may not be passed by name", pstate); } } virtual bool operator==(const Expression& rhs) const { try { const Argument* m = dynamic_cast(&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()(name()); hash_combine(hash_, value()->hash()); } return hash_; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////// // Argument lists -- in their own class to facilitate context-sensitive // error checking (e.g., ensuring that all ordinal arguments precede all // named arguments). //////////////////////////////////////////////////////////////////////// class Arguments : public Expression, public Vectorized { ADD_PROPERTY(bool, has_named_arguments) ADD_PROPERTY(bool, has_rest_argument) ADD_PROPERTY(bool, has_keyword_argument) protected: void adjust_after_pushing(Argument* a); public: Arguments(ParserState pstate) : Expression(pstate), Vectorized(), has_named_arguments_(false), has_rest_argument_(false), has_keyword_argument_(false) { } Argument* get_rest_argument(); Argument* get_keyword_argument(); ATTACH_OPERATIONS() }; ////////////////// // Function calls. ////////////////// class Function_Call : public PreValue { ADD_HASHED(std::string, name) ADD_HASHED(Arguments*, arguments) ADD_PROPERTY(void*, cookie) size_t hash_; public: Function_Call(ParserState pstate, std::string n, Arguments* args, void* cookie) : PreValue(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0) { concrete_type(STRING); } Function_Call(ParserState pstate, std::string n, Arguments* args) : PreValue(pstate), name_(n), arguments_(args), cookie_(0), hash_(0) { concrete_type(STRING); } virtual bool operator==(const Expression& rhs) const { try { const Function_Call* m = dynamic_cast(&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()(name()); for (auto argument : arguments()->elements()) hash_combine(hash_, argument->hash()); } return hash_; } ATTACH_OPERATIONS() }; ///////////////////////// // Function call schemas. ///////////////////////// class Function_Call_Schema : public Expression { ADD_PROPERTY(String*, name) ADD_PROPERTY(Arguments*, arguments) public: Function_Call_Schema(ParserState pstate, String* n, Arguments* args) : Expression(pstate), name_(n), arguments_(args) { concrete_type(STRING); } ATTACH_OPERATIONS() }; /////////////////////// // Variable references. /////////////////////// class Variable : public PreValue { ADD_PROPERTY(std::string, name) public: Variable(ParserState pstate, std::string n) : PreValue(pstate), name_(n) { } virtual bool operator==(const Expression& rhs) const { try { const Variable* e = dynamic_cast(&rhs); return e && name() == e->name(); } catch (std::bad_cast&) { return false; } catch (...) { throw; } } virtual size_t hash() { return std::hash()(name()); } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////////// // Textual (i.e., unevaluated) numeric data. Variants are distinguished with // a type tag. //////////////////////////////////////////////////////////////////////////// class Textual : public Expression { public: enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX }; private: ADD_HASHED(Type, type) ADD_HASHED(std::string, value) size_t hash_; public: Textual(ParserState pstate, Type t, std::string val) : Expression(pstate, true), type_(t), value_(val), hash_(0) { } virtual bool operator==(const Expression& rhs) const { try { const Textual* e = dynamic_cast(&rhs); return e && value() == e->value() && type() == e->type(); } catch (std::bad_cast&) { return false; } catch (...) { throw; } } virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(value_); hash_combine(hash_, std::hash()(type_)); } return hash_; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////// // Numbers, percentages, dimensions, and colors. //////////////////////////////////////////////// class Number : public Value { ADD_HASHED(double, value) ADD_PROPERTY(bool, zero) std::vector numerator_units_; std::vector denominator_units_; size_t hash_; public: Number(ParserState pstate, double val, std::string u = "", bool zero = true); bool zero() { return zero_; } bool is_valid_css_unit() const; std::vector& numerator_units() { return numerator_units_; } std::vector& denominator_units() { return denominator_units_; } const std::vector& numerator_units() const { return numerator_units_; } const std::vector& denominator_units() const { return denominator_units_; } std::string type() { return "number"; } static std::string type_name() { return "number"; } std::string unit() const; bool is_unitless() const; double convert_factor(const Number&) const; bool convert(const std::string& unit = "", bool strict = false); void normalize(const std::string& unit = "", bool strict = false); // useful for making one number compatible with another std::string find_convertible_unit() const; virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(value_); for (const auto numerator : numerator_units()) hash_combine(hash_, std::hash()(numerator)); for (const auto denominator : denominator_units()) hash_combine(hash_, std::hash()(denominator)); } return hash_; } virtual bool operator< (const Number& rhs) const; virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; ////////// // Colors. ////////// class Color : public Value { ADD_HASHED(double, r) ADD_HASHED(double, g) ADD_HASHED(double, b) ADD_HASHED(double, a) ADD_PROPERTY(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); } std::string type() { return "color"; } static std::string type_name() { return "color"; } virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(a_); hash_combine(hash_, std::hash()(r_)); hash_combine(hash_, std::hash()(g_)); hash_combine(hash_, std::hash()(b_)); } return hash_; } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; ////////////////////////////// // Errors from Sass_Values. ////////////////////////////// class Custom_Error : public Value { ADD_PROPERTY(std::string, message) public: Custom_Error(ParserState pstate, std::string msg) : Value(pstate), message_(msg) { concrete_type(C_ERROR); } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; ////////////////////////////// // Warnings from Sass_Values. ////////////////////////////// class Custom_Warning : public Value { ADD_PROPERTY(std::string, message) public: Custom_Warning(ParserState pstate, std::string msg) : Value(pstate), message_(msg) { concrete_type(C_WARNING); } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; //////////// // Booleans. //////////// class Boolean : public Value { ADD_HASHED(bool, value) size_t hash_; public: Boolean(ParserState pstate, bool val) : Value(pstate), value_(val), hash_(0) { concrete_type(BOOLEAN); } virtual operator bool() { return value_; } std::string type() { return "bool"; } static std::string type_name() { return "bool"; } virtual bool is_false() { return !value_; } virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(value_); } return hash_; } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////// // Abstract base class for Sass string values. Includes interpolated and // "flat" strings. //////////////////////////////////////////////////////////////////////// class String : public Value { ADD_PROPERTY(bool, sass_fix_1291) public: String(ParserState pstate, bool delayed = false, bool sass_fix_1291 = false) : Value(pstate, delayed), sass_fix_1291_(sass_fix_1291) { concrete_type(STRING); } static std::string type_name() { return "string"; } virtual ~String() = 0; virtual bool operator==(const Expression& rhs) const = 0; 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 { // ADD_PROPERTY(bool, has_interpolants) size_t hash_; public: String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false) : String(pstate), Vectorized(size), hash_(0) { concrete_type(STRING); } std::string type() { return "string"; } static std::string type_name() { return "string"; } bool is_left_interpolant(void) const; bool is_right_interpolant(void) const; // void has_interpolants(bool tc) { } bool has_interpolants() { for (auto el : elements()) { if (el->is_interpolant()) return true; } return false; } virtual size_t hash() { if (hash_ == 0) { for (auto string : elements()) hash_combine(hash_, string->hash()); } return hash_; } virtual bool operator==(const Expression& rhs) const; ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////// // Flat strings -- the lowest level of raw textual data. //////////////////////////////////////////////////////// class String_Constant : public String { ADD_PROPERTY(char, quote_mark) ADD_PROPERTY(bool, can_compress_whitespace) ADD_HASHED(std::string, value) protected: size_t hash_; public: String_Constant(ParserState pstate, std::string val) : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0) { } String_Constant(ParserState pstate, const char* beg) : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0) { } String_Constant(ParserState pstate, const char* beg, const char* end) : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0) { } String_Constant(ParserState pstate, const Token& tok) : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0) { } std::string type() { return "string"; } static std::string type_name() { return "string"; } virtual bool is_invisible() const; virtual size_t hash() { if (hash_ == 0) { hash_ = std::hash()(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_OPERATIONS() }; //////////////////////////////////////////////////////// // Possibly quoted string (unquote on instantiation) //////////////////////////////////////////////////////// class String_Quoted : public String_Constant { public: String_Quoted(ParserState pstate, std::string val, char q = 0, bool keep_utf8_escapes = false) : String_Constant(pstate, val) { value_ = unquote(value_, "e_mark_, keep_utf8_escapes); if (q && quote_mark_) quote_mark_ = q; } virtual bool operator==(const Expression& rhs) const; virtual std::string inspect() const; // quotes are forced on inspection ATTACH_OPERATIONS() }; ///////////////// // Media queries. ///////////////// class Media_Query : public Expression, public Vectorized { ADD_PROPERTY(String*, media_type) ADD_PROPERTY(bool, is_negated) ADD_PROPERTY(bool, is_restricted) public: Media_Query(ParserState pstate, String* t = 0, size_t s = 0, bool n = false, bool r = false) : Expression(pstate), Vectorized(s), media_type_(t), is_negated_(n), is_restricted_(r) { } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////// // Media expressions (for use inside media queries). //////////////////////////////////////////////////// class Media_Query_Expression : public Expression { ADD_PROPERTY(Expression*, feature) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_interpolated) public: Media_Query_Expression(ParserState pstate, Expression* f, Expression* v, bool i = false) : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) { } ATTACH_OPERATIONS() }; //////////////////// // `@supports` rule. //////////////////// class Supports_Block : public Has_Block { ADD_PROPERTY(Supports_Condition*, condition) public: Supports_Block(ParserState pstate, Supports_Condition* condition, Block* block = 0) : Has_Block(pstate, block), condition_(condition) { statement_type(SUPPORTS); } bool is_hoistable() { return true; } bool bubbles() { return true; } ATTACH_OPERATIONS() }; ////////////////////////////////////////////////////// // The abstract superclass of all Supports conditions. ////////////////////////////////////////////////////// class Supports_Condition : public Expression { public: Supports_Condition(ParserState pstate) : Expression(pstate) { } virtual bool needs_parens(Supports_Condition* cond) const { return false; } ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////// // An operator condition (e.g. `CONDITION1 and CONDITION2`). //////////////////////////////////////////////////////////// class Supports_Operator : public Supports_Condition { public: enum Operand { AND, OR }; private: ADD_PROPERTY(Supports_Condition*, left); ADD_PROPERTY(Supports_Condition*, right); ADD_PROPERTY(Operand, operand); public: Supports_Operator(ParserState pstate, Supports_Condition* l, Supports_Condition* r, Operand o) : Supports_Condition(pstate), left_(l), right_(r), operand_(o) { } virtual bool needs_parens(Supports_Condition* cond) const; ATTACH_OPERATIONS() }; ////////////////////////////////////////// // A negation condition (`not CONDITION`). ////////////////////////////////////////// class Supports_Negation : public Supports_Condition { private: ADD_PROPERTY(Supports_Condition*, condition); public: Supports_Negation(ParserState pstate, Supports_Condition* c) : Supports_Condition(pstate), condition_(c) { } virtual bool needs_parens(Supports_Condition* cond) const; ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////// // A declaration condition (e.g. `(feature: value)`). ///////////////////////////////////////////////////// class Supports_Declaration : public Supports_Condition { private: ADD_PROPERTY(Expression*, feature); ADD_PROPERTY(Expression*, value); public: Supports_Declaration(ParserState pstate, Expression* f, Expression* v) : Supports_Condition(pstate), feature_(f), value_(v) { } virtual bool needs_parens(Supports_Condition* cond) const { return false; } ATTACH_OPERATIONS() }; /////////////////////////////////////////////// // An interpolation condition (e.g. `#{$var}`). /////////////////////////////////////////////// class Supports_Interpolation : public Supports_Condition { private: ADD_PROPERTY(Expression*, value); public: Supports_Interpolation(ParserState pstate, Expression* v) : Supports_Condition(pstate), value_(v) { } virtual bool needs_parens(Supports_Condition* cond) const { return false; } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////// // At root expressions (for use inside @at-root). ///////////////////////////////////////////////// class At_Root_Expression : public Expression { private: ADD_PROPERTY(String*, feature) ADD_PROPERTY(Expression*, value) ADD_PROPERTY(bool, is_interpolated) public: At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false) : Expression(pstate), feature_(f), value_(v), is_interpolated_(i) { } bool exclude(std::string str) { bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; List* l = static_cast(value()); std::string v; if (with) { if (!l || l->length() == 0) return str.compare("rule") != 0; for (size_t i = 0, L = l->length(); i < L; ++i) { v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return false; } return true; } else { if (!l || !l->length()) return str.compare("rule") == 0; for (size_t i = 0, L = l->length(); i < L; ++i) { v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return true; } return false; } } ATTACH_OPERATIONS() }; /////////// // At-root. /////////// class At_Root_Block : public Has_Block { ADD_PROPERTY(At_Root_Expression*, expression) public: At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0) : Has_Block(pstate, b), expression_(e) { statement_type(ATROOT); } bool is_hoistable() { return true; } bool bubbles() { return true; } bool exclude_node(Statement* s) { if (s->statement_type() == Statement::DIRECTIVE) { return expression()->exclude(static_cast(s)->keyword().erase(0, 1)); } if (s->statement_type() == Statement::MEDIA) { return expression()->exclude("media"); } if (s->statement_type() == Statement::RULESET) { return expression()->exclude("rule"); } if (s->statement_type() == Statement::SUPPORTS) { return expression()->exclude("supports"); } if (static_cast(s)->is_keyframes()) { return expression()->exclude("keyframes"); } return false; } ATTACH_OPERATIONS() }; ////////////////// // The null value. ////////////////// class Null : public Value { public: Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); } std::string type() { return "null"; } static std::string type_name() { return "null"; } bool is_invisible() const { return true; } operator bool() { return false; } bool is_false() { return true; } virtual size_t hash() { return -1; } virtual bool operator== (const Expression& rhs) const; ATTACH_OPERATIONS() }; ///////////////////////////////// // Thunks for delayed evaluation. ///////////////////////////////// class Thunk : public Expression { ADD_PROPERTY(Expression*, expression) ADD_PROPERTY(Env*, environment) public: Thunk(ParserState pstate, Expression* exp, Env* env = 0) : Expression(pstate), expression_(exp), environment_(env) { } }; ///////////////////////////////////////////////////////// // Individual parameter objects for mixins and functions. ///////////////////////////////////////////////////////// class Parameter : public AST_Node { ADD_PROPERTY(std::string, name) ADD_PROPERTY(Expression*, default_value) ADD_PROPERTY(bool, is_rest_parameter) public: Parameter(ParserState pstate, std::string n, Expression* def = 0, bool rest = false) : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest) { if (default_value_ && is_rest_parameter_) { error("variable-length parameter may not have a default value", pstate); } } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////////////////////// // Parameter lists -- in their own class to facilitate context-sensitive // error checking (e.g., ensuring that all optional parameters follow all // required parameters). ///////////////////////////////////////////////////////////////////////// class Parameters : public AST_Node, public Vectorized { ADD_PROPERTY(bool, has_optional_parameters) ADD_PROPERTY(bool, has_rest_parameter) protected: void adjust_after_pushing(Parameter* p) { if (p->default_value()) { if (has_rest_parameter_) { error("optional parameters may not be combined with variable-length parameters", p->pstate()); } has_optional_parameters_ = true; } else if (p->is_rest_parameter()) { if (has_rest_parameter_) { error("functions and mixins cannot have more than one variable-length parameter", p->pstate()); } has_rest_parameter_ = true; } else { if (has_rest_parameter_) { error("required parameters must precede variable-length parameters", p->pstate()); } if (has_optional_parameters_) { error("required parameters must precede optional parameters", p->pstate()); } } } public: Parameters(ParserState pstate) : AST_Node(pstate), Vectorized(), has_optional_parameters_(false), has_rest_parameter_(false) { } ATTACH_OPERATIONS() }; ///////////////////////////////////////// // Abstract base class for CSS selectors. ///////////////////////////////////////// class Selector : public Expression { // ADD_PROPERTY(bool, has_reference) ADD_PROPERTY(bool, has_placeholder) // line break before list separator ADD_PROPERTY(bool, has_line_feed) // line break after list separator ADD_PROPERTY(bool, has_line_break) // maybe we have optional flag ADD_PROPERTY(bool, is_optional) // parent block pointers ADD_PROPERTY(Media_Block*, media_block) protected: size_t hash_; public: Selector(ParserState pstate, bool r = false, bool h = false) : Expression(pstate), // has_reference_(r), has_placeholder_(h), has_line_feed_(false), has_line_break_(false), is_optional_(false), media_block_(0), hash_(0) { concrete_type(SELECTOR); } virtual ~Selector() = 0; virtual size_t hash() = 0; virtual bool has_parent_ref() { return false; } virtual unsigned long specificity() { return Constants::Specificity_Universal; } }; inline Selector::~Selector() { } ///////////////////////////////////////////////////////////////////////// // Interpolated selectors -- the interpolated String will be expanded and // re-parsed into a normal selector class. ///////////////////////////////////////////////////////////////////////// class Selector_Schema : public Selector { ADD_PROPERTY(String*, contents) ADD_PROPERTY(bool, at_root); public: Selector_Schema(ParserState pstate, String* c) : Selector(pstate), contents_(c), at_root_(false) { } virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, contents_->hash()); } return hash_; } ATTACH_OPERATIONS() }; //////////////////////////////////////////// // Abstract base class for simple selectors. //////////////////////////////////////////// class Simple_Selector : public Selector { ADD_PROPERTY(std::string, ns); ADD_PROPERTY(std::string, name) ADD_PROPERTY(bool, has_ns) public: Simple_Selector(ParserState pstate, std::string n = "") : Selector(pstate), ns_(""), name_(n), has_ns_(false) { size_t pos = n.find('|'); // found some namespace if (pos != std::string::npos) { has_ns_ = true; ns_ = n.substr(0, pos); name_ = n.substr(pos + 1); } } virtual std::string ns_name() const { std::string name(""); if (has_ns_) name += ns_ + "|"; return name + name_; } virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, std::hash()(SELECTOR)); hash_combine(hash_, std::hash()(ns())); hash_combine(hash_, std::hash()(name())); } return hash_; } // namespace query functions bool is_universal_ns() const { return has_ns_ && ns_ == "*"; } bool has_universal_ns() const { return !has_ns_ || ns_ == "*"; } bool is_empty_ns() const { return !has_ns_ || ns_ == ""; } bool has_empty_ns() const { return has_ns_ && ns_ == ""; } bool has_qualified_ns() const { return has_ns_ && ns_ != "" && ns_ != "*"; } // name query functions bool is_universal() const { return name_ == "*"; } virtual ~Simple_Selector() = 0; virtual Compound_Selector* unify_with(Compound_Selector*, Context&); virtual bool has_parent_ref() { return false; }; virtual bool is_pseudo_element() { return false; } virtual bool is_pseudo_class() { return false; } virtual bool is_superselector_of(Compound_Selector* sub) { return false; } bool operator==(const Simple_Selector& rhs) const; inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } bool operator<(const Simple_Selector& rhs) const; // default implementation should work for most of the simple selectors (otherwise overload) ATTACH_OPERATIONS(); }; inline Simple_Selector::~Simple_Selector() { } ////////////////////////////////// // The Parent Selector Expression. ////////////////////////////////// // parent selectors can occur in selectors but also // inside strings in declarations (Compound_Selector). // only one simple parent selector means the first case. class Parent_Selector : public Simple_Selector { public: Parent_Selector(ParserState pstate) : Simple_Selector(pstate, "&") { /* has_reference(true); */ } virtual bool has_parent_ref() { return true; }; virtual unsigned long specificity() { return 0; } std::string type() { return "selector"; } static std::string type_name() { return "selector"; } ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////////////////////// // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. ///////////////////////////////////////////////////////////////////////// class Selector_Placeholder : public Simple_Selector { public: Selector_Placeholder(ParserState pstate, std::string n) : Simple_Selector(pstate, n) { has_placeholder(true); } // virtual Selector_Placeholder* find_placeholder(); virtual ~Selector_Placeholder() {}; ATTACH_OPERATIONS() }; ///////////////////////////////////////////////////////////////////// // Type selectors (and the universal selector) -- e.g., div, span, *. ///////////////////////////////////////////////////////////////////// class Type_Selector : public Simple_Selector { public: Type_Selector(ParserState pstate, std::string n) : Simple_Selector(pstate, n) { } virtual unsigned long specificity() { // ToDo: What is the specificity of the star selector? if (name() == "*") return Constants::Specificity_Universal; else return Constants::Specificity_Type; } virtual Simple_Selector* unify_with(Simple_Selector*, Context&); virtual Compound_Selector* unify_with(Compound_Selector*, Context&); ATTACH_OPERATIONS() }; //////////////////////////////////////////////// // Selector qualifiers -- i.e., classes and ids. //////////////////////////////////////////////// class Selector_Qualifier : public Simple_Selector { public: Selector_Qualifier(ParserState pstate, std::string n) : Simple_Selector(pstate, n) { } virtual unsigned long specificity() { if (name()[0] == '#') return Constants::Specificity_ID; if (name()[0] == '.') return Constants::Specificity_Class; else return Constants::Specificity_Type; } virtual Compound_Selector* unify_with(Compound_Selector*, Context&); ATTACH_OPERATIONS() }; /////////////////////////////////////////////////// // Attribute selectors -- e.g., [src*=".jpg"], etc. /////////////////////////////////////////////////// class Attribute_Selector : public Simple_Selector { ADD_PROPERTY(std::string, matcher) ADD_PROPERTY(String*, value) // might be interpolated public: Attribute_Selector(ParserState pstate, std::string n, std::string m, String* v) : Simple_Selector(pstate, n), matcher_(m), value_(v) { } virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, Simple_Selector::hash()); hash_combine(hash_, std::hash()(matcher())); if (value_) hash_combine(hash_, value_->hash()); } return hash_; } virtual unsigned long specificity() { return Constants::Specificity_Attr; } bool operator==(const Simple_Selector& rhs) const; bool operator==(const Attribute_Selector& rhs) const; bool operator<(const Simple_Selector& rhs) const; bool operator<(const Attribute_Selector& rhs) const; 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*, expression) public: Pseudo_Selector(ParserState pstate, std::string n, String* expr = 0) : Simple_Selector(pstate, n), expression_(expr) { } // A pseudo-class always consists of a "colon" (:) followed by the name // of the pseudo-class and optionally by a value between parentheses. virtual bool is_pseudo_class() { return (name_[0] == ':' && name_[1] != ':') && ! is_pseudo_class_element(name_); } // A pseudo-element is made of two colons (::) followed by the name. // The `::` notation is introduced by the current document in order to // establish a discrimination between pseudo-classes and pseudo-elements. // For compatibility with existing style sheets, user agents must also // accept the previous one-colon notation for pseudo-elements introduced // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and // :after). This compatibility is not allowed for the new pseudo-elements // introduced in this specification. virtual bool is_pseudo_element() { return (name_[0] == ':' && name_[1] == ':') || is_pseudo_class_element(name_); } virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, Simple_Selector::hash()); if (expression_) hash_combine(hash_, expression_->hash()); } return hash_; } virtual unsigned long specificity() { if (is_pseudo_element()) return Constants::Specificity_Type; return Constants::Specificity_Pseudo; } virtual Compound_Selector* unify_with(Compound_Selector*, Context&); ATTACH_OPERATIONS() }; ///////////////////////////////////////////////// // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir) ///////////////////////////////////////////////// class Wrapped_Selector : public Simple_Selector { ADD_PROPERTY(Selector*, selector) public: Wrapped_Selector(ParserState pstate, std::string n, Selector* sel) : Simple_Selector(pstate, n), selector_(sel) { } virtual bool has_parent_ref() { // if (has_reference()) return true; if (!selector()) return false; return selector()->has_parent_ref(); } virtual bool is_superselector_of(Wrapped_Selector* sub); // Selectors inside the negation pseudo-class are counted like any // other, but the negation itself does not count as a pseudo-class. virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, Simple_Selector::hash()); if (selector_) hash_combine(hash_, selector_->hash()); } return hash_; } virtual unsigned long specificity() { return selector_ ? selector_->specificity() : 0; } bool operator==(const Simple_Selector& rhs) const; bool operator==(const Wrapped_Selector& rhs) const; bool operator<(const Simple_Selector& rhs) const; bool operator<(const Wrapped_Selector& rhs) const; ATTACH_OPERATIONS() }; struct Complex_Selector_Pointer_Compare { bool operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const; }; //////////////////////////////////////////////////////////////////////////// // Simple selector sequences. Maintains flags indicating whether it contains // any parent references or placeholders, to simplify expansion. //////////////////////////////////////////////////////////////////////////// typedef std::set SourcesSet; class Compound_Selector : public Selector, public Vectorized { private: SourcesSet sources_; ADD_PROPERTY(bool, extended); ADD_PROPERTY(bool, has_parent_reference); protected: void adjust_after_pushing(Simple_Selector* s) { // if (s->has_reference()) has_reference(true); if (s->has_placeholder()) has_placeholder(true); } public: Compound_Selector(ParserState pstate, size_t s = 0) : Selector(pstate), Vectorized(s), extended_(false), has_parent_reference_(false) { } bool contains_placeholder() { for (size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->has_placeholder()) return true; } return false; }; bool is_universal() const { return length() == 1 && (*this)[0]->is_universal(); } Complex_Selector* to_complex(Memory_Manager& mem); Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); // virtual Selector_Placeholder* find_placeholder(); virtual bool has_parent_ref(); Simple_Selector* base() { // Implement non-const in terms of const. Safe to const_cast since this method is non-const return const_cast(static_cast(this)->base()); } const Simple_Selector* base() const { if (length() == 0) return 0; // ToDo: why is this needed? if (dynamic_cast((*this)[0])) return (*this)[0]; return 0; } virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapped = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapped = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapped = ""); virtual size_t hash() { if (Selector::hash_ == 0) { hash_combine(Selector::hash_, std::hash()(SELECTOR)); if (length()) hash_combine(Selector::hash_, Vectorized::hash()); } return Selector::hash_; } virtual unsigned long specificity() { int sum = 0; for (size_t i = 0, L = length(); i < L; ++i) { sum += (*this)[i]->specificity(); } return sum; } bool is_empty_reference() { return length() == 1 && dynamic_cast((*this)[0]); } std::vector to_str_vec(); // sometimes need to convert to a flat "by-value" data structure bool operator<(const Compound_Selector& rhs) const; bool operator==(const Compound_Selector& rhs) const; inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } SourcesSet& sources() { return sources_; } void clearSources() { sources_.clear(); } void mergeSources(SourcesSet& sources, Context& ctx); Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s Compound_Selector* minus(Compound_Selector* rhs, Context& ctx); ATTACH_OPERATIONS() }; //////////////////////////////////////////////////////////////////////////// // General selectors -- i.e., simple sequences combined with one of the four // CSS selector combinators (">", "+", "~", and whitespace). Essentially a // linked list. //////////////////////////////////////////////////////////////////////////// class Complex_Selector : public Selector { public: enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO, REFERENCE }; private: ADD_PROPERTY(Combinator, combinator) ADD_PROPERTY(Compound_Selector*, head) ADD_PROPERTY(Complex_Selector*, tail) ADD_PROPERTY(String*, reference); public: bool contains_placeholder() { if (head() && head()->contains_placeholder()) return true; if (tail() && tail()->contains_placeholder()) return true; return false; }; Complex_Selector(ParserState pstate, Combinator c = ANCESTOR_OF, Compound_Selector* h = 0, Complex_Selector* t = 0, String* r = 0) : Selector(pstate), combinator_(c), head_(h), tail_(t), reference_(r) { // if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); } virtual bool has_parent_ref(); Complex_Selector* skip_empty_reference() { if ((!head_ || !head_->length() || head_->is_empty_reference()) && combinator() == Combinator::ANCESTOR_OF) { if (!tail_) return 0; tail_->has_line_feed_ = this->has_line_feed_; // tail_->has_line_break_ = this->has_line_break_; return tail_->skip_empty_reference(); } return this; } // can still have a tail bool is_empty_ancestor() const { return (!head() || head()->length() == 0) && combinator() == Combinator::ANCESTOR_OF; } Complex_Selector* context(Context&); // front returns the first real tail // skips over parent and empty ones const Complex_Selector* first() const; // last returns the last real tail const Complex_Selector* last() const; Selector_List* tails(Context& ctx, Selector_List* tails); // unconstant accessors Complex_Selector* first(); Complex_Selector* last(); // some shortcuts that should be removed const Complex_Selector* innermost() const { return last(); }; Complex_Selector* innermost() { return last(); }; size_t length() const; Selector_List* parentize(Selector_List* parents, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = ""); // virtual Selector_Placeholder* find_placeholder(); Selector_List* unify_with(Complex_Selector* rhs, Context& ctx); Combinator clear_innermost(); void append(Context&, Complex_Selector*); void set_innermost(Complex_Selector*, Combinator); virtual size_t hash() { if (hash_ == 0) { hash_combine(hash_, std::hash()(SELECTOR)); hash_combine(hash_, std::hash()(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; } bool operator<(const Complex_Selector& rhs) const; bool operator==(const Complex_Selector& rhs) const; inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); } SourcesSet sources() { //s = Set.new //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} //s SourcesSet srcs; Compound_Selector* pHead = head(); Complex_Selector* pTail = tail(); if (pHead) { SourcesSet& headSources = pHead->sources(); srcs.insert(headSources.begin(), headSources.end()); } if (pTail) { SourcesSet tailSources = pTail->sources(); srcs.insert(tailSources.begin(), tailSources.end()); } return srcs; } void addSources(SourcesSet& sources, Context& ctx) { // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} Complex_Selector* pIter = this; while (pIter) { Compound_Selector* pHead = pIter->head(); if (pHead) { pHead->mergeSources(sources, ctx); } pIter = pIter->tail(); } } void clearSources() { Complex_Selector* pIter = this; while (pIter) { Compound_Selector* pHead = pIter->head(); if (pHead) { pHead->clearSources(); } pIter = pIter->tail(); } } Complex_Selector* clone(Context&) const; // does not clone Compound_Selector*s Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s // std::vector to_vector(); ATTACH_OPERATIONS() }; typedef std::deque ComplexSelectorDeque; typedef Subset_Map > ExtensionSubsetMap; /////////////////////////////////// // Comma-separated selector groups. /////////////////////////////////// class Selector_List : public Selector, public Vectorized { ADD_PROPERTY(std::vector, wspace) protected: void adjust_after_pushing(Complex_Selector* c); public: Selector_List(ParserState pstate, size_t s = 0) : Selector(pstate), Vectorized(s), wspace_(0) { } std::string type() { return "list"; } // remove parent selector references // basically unwraps parsed selectors virtual bool has_parent_ref(); void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); Selector_List* parentize(Selector_List* parents, Context& ctx); virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = ""); Selector_List* unify_with(Selector_List*, Context&); void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); virtual size_t hash() { if (Selector::hash_ == 0) { hash_combine(Selector::hash_, std::hash()(SELECTOR)); hash_combine(Selector::hash_, Vectorized::hash()); } return Selector::hash_; } virtual unsigned long specificity() { unsigned long sum = 0; unsigned long specificity = 0; for (size_t i = 0, L = length(); i < L; ++i) { specificity = (*this)[i]->specificity(); if (sum < specificity) sum = specificity; } return sum; } Selector_List* clone(Context&) const; // does not clone Compound_Selector*s Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s virtual bool operator==(const Selector& rhs) const; virtual bool operator==(const Selector_List& rhs) const; // Selector Lists can be compared to comma lists virtual bool operator==(const Expression& rhs) const; ATTACH_OPERATIONS() }; template bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) { // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the // members of a SimpleSequence (Compound_Selector)). // // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for // use with Compound_Selector and Complex_Selector objects. if (simpleSelectorOrderDependent) { return !(one < two) && !(two < one); } else { return one == two; } } // compare function for sorting and probably other other uses struct cmp_complex_selector { inline bool operator() (const Complex_Selector* l, const Complex_Selector* r) { return (*l < *r); } }; struct cmp_compound_selector { inline bool operator() (const Compound_Selector* l, const Compound_Selector* r) { return (*l < *r); } }; struct cmp_simple_selector { inline bool operator() (const Simple_Selector* l, const Simple_Selector* r) { return (*l < *r); } }; } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif libsass-3.3.4/src/ast_def_macros.hpp000066400000000000000000000031701267254216700174470ustar00rootroot00000000000000#ifndef SASS_AST_DEF_MACROS_H #define SASS_AST_DEF_MACROS_H // Helper class to switch a flag and revert once we go out of scope template class LocalOption { private: T* var; // pointer to original variable T orig; // copy of the original option public: LocalOption(T& var) { this->var = &var; this->orig = var; } LocalOption(T& var, T orig) { this->var = &var; this->orig = var; *(this->var) = orig; } ~LocalOption() { *(this->var) = this->orig; } }; #define LOCAL_FLAG(name,opt) LocalOption flag_##name(name, opt) #define ATTACH_OPERATIONS()\ virtual void perform(Operation* op) { (*op)(this); }\ virtual AST_Node* perform(Operation* op) { return (*op)(this); }\ virtual Statement* perform(Operation* op) { return (*op)(this); }\ virtual Expression* perform(Operation* op) { return (*op)(this); }\ virtual Selector* perform(Operation* op) { return (*op)(this); }\ virtual std::string perform(Operation* op) { return (*op)(this); }\ virtual union Sass_Value* perform(Operation* op) { return (*op)(this); }\ virtual Value* perform(Operation* op) { return (*op)(this); } #define ADD_PROPERTY(type, name)\ protected:\ type name##_;\ public:\ type name() const { return name##_; }\ type name(type name##__) { return name##_ = name##__; }\ private: #define ADD_HASHED(type, name)\ protected:\ type name##_;\ public:\ type name() const { return name##_; }\ type name(type name##__) { hash_ = 0; return name##_ = name##__; }\ private: #endif libsass-3.3.4/src/ast_factory.hpp000066400000000000000000000150401267254216700170130ustar00rootroot00000000000000#ifndef SASS_AST_FACTORY_H #define SASS_AST_FACTORY_H #include #include "ast.hpp" namespace Sass { class AST_Factory { std::vector nodes; public: // statements Block* new_Block(std::string p, size_t l, size_t s = 0, bool r = false); Ruleset* new_Ruleset(std::string p, size_t l, Selector* s, Block* b); Propset* new_Propset(std::string p, size_t l, String* pf, Block* b); Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b); Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b); At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b); At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b); Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b); Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals); Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false); Import* new_CSS_Import(std::string p, size_t l, Function_Call* loc); Import* new_SASS_Import(std::string p, size_t l, String* loc); Custom_Warning* new_Custom_Warning(std::string msg, size_t l, std::string msg); Custom_Error* new_Custom_Error(std::string p, size_t l, std::string msg); Warning* new_Warning(std::string p, size_t l, Expression* msg); Error* new_Error(std::string p, size_t l, Expression* msg); Debug* new_Debug(std::string p, size_t l, Expression* val); Comment* new_Comment(std::string p, size_t l, String* txt); If* new_If(std::string p, size_t l, Expression* pred, Block* con, Block* alt = 0); For* new_For(std::string p, size_t l, std::string var, Expression* lo, Expression* hi, Block* b, bool inc); Each* new_Each(std::string p, size_t l, std::vector vars, Expression* lst, Block* b); While* new_While(std::string p, size_t l, Expression* pred, Block* b); Extension* new_Extension(std::string p, size_t l, Selector* s); Definition* new_Mixin_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b); Definition* new_Function_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b); Mixin_Call* new_Mixin_Call(std::string p, size_t l, std::string n, Arguments* args, Block* b = 0); // expressions List* new_List(std::string p, size_t l, size_t size = 0, enum Sass_Separator sep = List::space, bool argl = false); Map* new_Map(std::string p, size_t l, size_t size = 0); Binary_Expression* new_And(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Or(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Eq(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Neq(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Gt(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Gte(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Lt(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Lte(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Add(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Sub(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression* new_Mul(std::string p, size_t l, Expression* lhs, Expression* rhs); Binary_Expression
* new_Div(std::string p, size_t l, Expression* lhs, Expression* rhs); Negation* new_Negation(std::string p, size_t l, Expression* o); Function_Call* new_Function_Call(std::string p, size_t l, String* n, Arguments* args); Variable* new_Variable(std::string p, size_t l, std::string n); Textual* new_Textual_Number(std::string p, size_t l, std::string val); Textual* new_Textual_Percentage(std::string p, size_t l, std::string val); Textual* new_Textual_Dimension(std::string p, size_t l, std::string val); Textual* new_Textual_Hex(std::string p, size_t l, std::string val); Number* new_Number(std::string p, size_t l, double val); Percentage* new_Percentage(std::string p, size_t l, double val); Dimension* new_Dimension(std::string p, size_t l, double val, std::string unit); Color* new_Color(std::string p, size_t l, double r, double g, double b, double a = 1, std::string disp = ""); Boolean* new_Boolean(std::string p, size_t l, bool val); String_Schema* new_String_Schema(std::string p, size_t l, size_t size = 0); String_Constant* new_String_Constant(std::string p, size_t l, std::string val); String_Constant* new_String_Constant(std::string p, size_t l, const char* beg); String_Constant* new_String_Constant(std::string p, size_t l, const char* beg, const char* end); Supports_Condition* new_Supports_Condition(std::string p, size_t l, String* f, Expression* v); Media_Expression* new_Media_Expression(std::string p, size_t l, String* f, Expression* v); Parent_Selector* new_Parent_Selector(std::string p, size_t l, Selector* s); // parameters and arguments Parameter* new_Parameter(std::string p, size_t l, std::string n, Expression* def = 0, bool rest = false); Parameters* new_Parameters(std::string p, size_t l); Argument* new_Argument(std::string p, size_t l, Expression* val, std::string n = "", bool rest = false); Arguments* new_Arguments(std::string p, size_t l); // selectors Selector_Schema* new_Selector_Schema(std::string p, size_t l, String* c); Attribute_Selector* new_Attribute_Selector(std::string p, size_t l, std::string n, std::string m, String* v); Simple_Selector* new_Simple_Selector(std::string p, size_t l, std::string c); Reference_Selector* new_Reference_Selector(std::string p, size_t l); Placeholder_Selector* new_Placeholder_Selector(std::string p, size_t l, std::string n); Pseudo_Selector* new_Pseudo_Selector(std::string p, size_t l, std::string n, Expression* expr = 0); Wrapped_Selector* new_Wrapped_Selector(std::string p, size_t l, std::string n, Simple_Base* sel); Compound_Selector* new_Compound_Selector(std::string p, size_t l, size_t s = 0); Complex_Selector* new_Complex_Selector(std::string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel); Selector_List* new_Selector_List(std::string p, size_t l, size_t s = 0); }; } #endif libsass-3.3.4/src/ast_fwd_decl.hpp000066400000000000000000000034611267254216700171170ustar00rootroot00000000000000#ifndef SASS_AST_FWD_DECL_H #define SASS_AST_FWD_DECL_H ///////////////////////////////////////////// // Forward declarations for the AST visitors. ///////////////////////////////////////////// namespace Sass { class AST_Node; // statements class Statement; class Block; class Ruleset; class Propset; class Bubble; class Media_Block; class Supports_Block; class At_Rule; class Keyframe_Rule; class At_Root_Block; class Declaration; class Assignment; class Import; class Import_Stub; class Warning; class Error; class Debug; class Comment; class If; class For; class Each; class While; class Return; class Content; class Extension; class Definition; class Mixin_Call; // expressions class Value; class Expression; class List; class Map; class Binary_Expression; class Unary_Expression; class Function_Call; class Function_Call_Schema; class Custom_Warning; class Custom_Error; class Variable; class Textual; class Number; class Color; class Boolean; class String_Schema; class String; class String_Constant; class String_Quoted; class Media_Query; class Media_Query_Expression; class Supports_Condition; class Supports_Operator; class Supports_Negation; class Supports_Declaration; class Supports_Interpolation; class At_Root_Expression; class Null; class Parent_Selector; // parameters and arguments class Parameter; class Parameters; class Argument; class Arguments; // selectors class Selector; class Selector_Schema; class Selector_Placeholder; class Type_Selector; class Selector_Qualifier; class Attribute_Selector; class Pseudo_Selector; class Wrapped_Selector; class Compound_Selector; class Complex_Selector; class Selector_List; // common classes class Context; } #endif libsass-3.3.4/src/b64/000077500000000000000000000000001267254216700143575ustar00rootroot00000000000000libsass-3.3.4/src/b64/cencode.h000066400000000000000000000013251267254216700161310ustar00rootroot00000000000000/* cencode.h - c header for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_CENCODE_H #define BASE64_CENCODE_H typedef enum { step_A, step_B, step_C } base64_encodestep; typedef struct { base64_encodestep step; char result; int stepcount; } base64_encodestate; void base64_init_encodestate(base64_encodestate* state_in); char base64_encode_value(char value_in); int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); int base64_encode_blockend(char* code_out, base64_encodestate* state_in); #endif /* BASE64_CENCODE_H */ libsass-3.3.4/src/b64/encode.h000066400000000000000000000030261267254216700157660ustar00rootroot00000000000000// :mode=c++: /* encode.h - c++ wrapper for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #ifndef BASE64_ENCODE_H #define BASE64_ENCODE_H #include namespace base64 { extern "C" { #include "cencode.h" } struct encoder { base64_encodestate _state; int _buffersize; encoder(int buffersize_in = BUFFERSIZE) : _buffersize(buffersize_in) {} int encode(char value_in) { return base64_encode_value(value_in); } int encode(const char* code_in, const int length_in, char* plaintext_out) { return base64_encode_block(code_in, length_in, plaintext_out, &_state); } int encode_end(char* plaintext_out) { return base64_encode_blockend(plaintext_out, &_state); } void encode(std::istream& istream_in, std::ostream& ostream_in) { base64_init_encodestate(&_state); // const int N = _buffersize; char* plaintext = new char[N]; char* code = new char[2*N]; int plainlength; int codelength; do { istream_in.read(plaintext, N); plainlength = static_cast(istream_in.gcount()); // codelength = encode(plaintext, plainlength, code); ostream_in.write(code, codelength); } while (istream_in.good() && plainlength > 0); codelength = encode_end(code); ostream_in.write(code, codelength); // base64_init_encodestate(&_state); delete [] code; delete [] plaintext; } }; } // namespace base64 #endif // BASE64_ENCODE_H libsass-3.3.4/src/backtrace.hpp000066400000000000000000000030241267254216700164130ustar00rootroot00000000000000#ifndef SASS_BACKTRACE_H #define SASS_BACKTRACE_H #include #include "file.hpp" #include "position.hpp" namespace Sass { struct Backtrace { Backtrace* parent; ParserState pstate; std::string caller; Backtrace(Backtrace* prn, ParserState pstate, std::string c) : parent(prn), pstate(pstate), caller(c) { } std::string to_string(bool warning = false) { size_t i = -1; std::stringstream ss; std::string cwd(Sass::File::get_cwd()); Backtrace* this_point = this; if (!warning) ss << std::endl << "Backtrace:"; // the first tracepoint (which is parent-less) is an empty placeholder while (this_point->parent) { // make path relative to the current directory std::string rel_path(Sass::File::abs2rel(this_point->pstate.path, cwd, cwd)); if (warning) { ss << std::endl << "\t" << (++i == 0 ? "on" : "from") << " line " << this_point->pstate.line + 1 << " of " << rel_path; } else { ss << std::endl << "\t" << rel_path << ":" << this_point->pstate.line + 1 << this_point->parent->caller; } this_point = this_point->parent; } return ss.str(); } size_t depth() { size_t d = 0; Backtrace* p = parent; while (p) { ++d; p = p->parent; } return d-1; } }; } #endif libsass-3.3.4/src/base64vlq.cpp000066400000000000000000000020411267254216700162740ustar00rootroot00000000000000#include "sass.hpp" #include "base64vlq.hpp" namespace Sass { std::string Base64VLQ::encode(const int number) const { std::string encoded = ""; int vlq = to_vlq_signed(number); do { int digit = vlq & VLQ_BASE_MASK; vlq >>= VLQ_BASE_SHIFT; if (vlq > 0) { digit |= VLQ_CONTINUATION_BIT; } encoded += base64_encode(digit); } while (vlq > 0); return encoded; } char Base64VLQ::base64_encode(const int number) const { int index = number; if (index < 0) index = 0; if (index > 63) index = 63; return CHARACTERS[index]; } int Base64VLQ::to_vlq_signed(const int number) const { return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0; } const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const int Base64VLQ::VLQ_BASE_SHIFT = 5; const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT; const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1; const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE; } libsass-3.3.4/src/base64vlq.hpp000066400000000000000000000007351267254216700163110ustar00rootroot00000000000000#ifndef SASS_BASE64VLQ_H #define SASS_BASE64VLQ_H #include namespace Sass { class Base64VLQ { public: std::string encode(const int number) const; private: char base64_encode(const int number) const; int to_vlq_signed(const int number) const; static const char* CHARACTERS; static const int VLQ_BASE_SHIFT; static const int VLQ_BASE; static const int VLQ_BASE_MASK; static const int VLQ_CONTINUATION_BIT; }; } #endif libsass-3.3.4/src/bind.cpp000066400000000000000000000260101267254216700154030ustar00rootroot00000000000000#include "sass.hpp" #include "bind.hpp" #include "ast.hpp" #include "context.hpp" #include "eval.hpp" #include #include #include namespace Sass { void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval) { std::string callee(type + " " + name); Listize listize(ctx->mem); std::map param_map; for (size_t i = 0, L = as->length(); i < L; ++i) { if (auto str = dynamic_cast((*as)[i]->value())) { // force optional quotes (only if needed) if (str->quote_mark()) { str->quote_mark('*'); str->is_delayed(true); } } } // Set up a map to ensure named arguments refer to actual parameters. Also // eval each default value left-to-right, wrt env, populating env as we go. for (size_t i = 0, L = ps->length(); i < L; ++i) { Parameter* p = (*ps)[i]; param_map[p->name()] = p; // if (p->default_value()) { // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env)); // } } // plug in all args; if we have leftover params, deal with it later size_t ip = 0, LP = ps->length(); size_t ia = 0, LA = as->length(); while (ia < LA) { Argument* a = (*as)[ia]; if (ip >= LP) { // skip empty rest arguments if (a->is_rest_argument()) { if (List* l = dynamic_cast(a->value())) { if (l->length() == 0) { ++ ia; continue; } } } std::stringstream msg; msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << " for `" << name << "'"; return error(msg.str(), as->pstate()); } Parameter* p = (*ps)[ip]; // If the current parameter is the rest parameter, process and break the loop if (p->is_rest_parameter()) { // The next argument by coincidence provides a rest argument if (a->is_rest_argument()) { // We should always get a list for rest arguments if (List* rest = dynamic_cast(a->value())) { // create a new list object for wrapped items List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, rest->separator(), true); // wrap each item from list as an argument for (Expression* item : rest->elements()) { if (Argument* arg = dynamic_cast(item)) { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); } else { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, item->pstate(), item, "", false, false); } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } // invalid state else { throw std::runtime_error("invalid state"); } } else if (a->is_keyword_argument()) { // expand keyword arguments into their parameters List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, SASS_COMMA, true); env->local_frame()[p->name()] = arglist; Map* argmap = static_cast(a->value()); for (auto key : argmap->keys()) { std::string name = unquote(static_cast(key)->value()); (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, key->pstate(), argmap->at(key), "$" + name, false, false); } } else { // create a new list object for wrapped items List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, SASS_COMMA, true); // consume the next args while (ia < LA) { // get and post inc a = (*as)[ia++]; // maybe we have another list as argument List* ls = dynamic_cast(a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue; if (Argument* arg = dynamic_cast(a->value())) { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); } // check if we have rest argument else if (a->is_rest_argument()) { // preserve the list separator from rest args if (List* rest = dynamic_cast(a->value())) { arglist->separator(rest->separator()); for (size_t i = 0, L = rest->size(); i < L; ++i) { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, (*rest)[i]->pstate(), (*rest)[i], "", false, false); } } // no more arguments break; } // wrap all other value types into Argument else { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, a->pstate(), a->value(), a->name(), false, false); } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } // consumed parameter ++ip; // no more paramaters break; } // If the current argument is the rest argument, extract a value for processing else if (a->is_rest_argument()) { // normal param and rest arg List* arglist = static_cast(a->value()); // empty rest arg - treat all args as default values if (!arglist->length()) { break; } else { if (arglist->length() > LP - ip && !ps->has_rest_parameter()) { int arg_count = (arglist->length() + LA - 1); std::stringstream msg; msg << callee << " takes " << LP; msg << (LP == 1 ? " argument" : " arguments"); msg << " but " << arg_count; msg << (arg_count == 1 ? " was passed" : " were passed."); deprecated_bind(msg.str(), as->pstate()); while (arglist->length() > LP - ip) { arglist->elements().erase(arglist->elements().end() - 1); } } } // otherwise move one of the rest args into the param, converting to argument if necessary if (!(a = dynamic_cast((*arglist)[0]))) { Expression* a_to_convert = (*arglist)[0]; a = SASS_MEMORY_NEW(ctx->mem, Argument, a_to_convert->pstate(), a_to_convert, "", false, false); } arglist->elements().erase(arglist->elements().begin()); if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { ++ia; } } else if (a->is_keyword_argument()) { Map* argmap = static_cast(a->value()); for (auto key : argmap->keys()) { std::string name = "$" + unquote(static_cast(key)->value()); if (!param_map.count(name)) { std::stringstream msg; msg << callee << " has no parameter named " << name; error(msg.str(), a->pstate()); } env->local_frame()[name] = argmap->at(key); } ++ia; continue; } else { ++ia; } if (a->name().empty()) { if (env->has_local(p->name())) { std::stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee; error(msg.str(), a->pstate()); } // ordinal arg -- bind it to the next param env->local_frame()[p->name()] = a->value(); ++ip; } else { // named arg -- bind it to the appropriately named param if (!param_map.count(a->name())) { std::stringstream msg; msg << callee << " has no parameter named " << a->name(); error(msg.str(), a->pstate()); } if (param_map[a->name()]->is_rest_parameter()) { std::stringstream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument"; error(msg.str(), a->pstate()); } if (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()); } env->local_frame()[a->name()] = a->value(); } } // EO while ia // If we make it here, we're out of args but may have leftover params. // That's only okay if they have default values, or were already bound by // named arguments, or if it's a single rest-param. for (size_t i = ip; i < LP; ++i) { Parameter* leftover = (*ps)[i]; // cerr << "env for default params:" << endl; // env->print(); // cerr << "********" << endl; if (!env->has_local(leftover->name())) { if (leftover->is_rest_parameter()) { env->local_frame()[leftover->name()] = SASS_MEMORY_NEW(ctx->mem, List, leftover->pstate(), 0, SASS_COMMA, true); } else if (leftover->default_value()) { Expression* dv = leftover->default_value()->perform(eval); env->local_frame()[leftover->name()] = dv; } else { // param is unbound and has no default value -- error std::stringstream msg; msg << "required parameter " << leftover->name() << " is missing in call to " << callee; error(msg.str(), as->pstate()); } } } return; } } libsass-3.3.4/src/bind.hpp000066400000000000000000000005631267254216700154150ustar00rootroot00000000000000#ifndef SASS_BIND_H #define SASS_BIND_H #include #include "listize.hpp" #include "environment.hpp" namespace Sass { class AST_Node; class Parameters; class Arguments; class Context; class Eval; typedef Environment Env; void bind(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*); } #endif libsass-3.3.4/src/c99func.c000066400000000000000000000033131267254216700154100ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if defined(_MSC_VER) && _MSC_VER < 1900 #include #include #include static int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { int count = -1; if (size != 0) count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); if (count == -1) count = _vscprintf(format, ap); return count; } int snprintf(char* str, size_t size, const char* format, ...) { int count; va_list ap; va_start(ap, format); count = c99_vsnprintf(str, size, format, ap); va_end(ap); return count; } #endif libsass-3.3.4/src/cencode.c000066400000000000000000000046121267254216700155330ustar00rootroot00000000000000/* cencoder.c - c source to a base64 encoding algorithm implementation This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 */ #include "b64/cencode.h" void base64_init_encodestate(base64_encodestate* state_in) { state_in->step = step_A; state_in->result = 0; state_in->stepcount = 0; } char base64_encode_value(char value_in) { static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (value_in > 63) return '='; return encoding[(int)value_in]; } int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) { const char* plainchar = plaintext_in; const char* const plaintextend = plaintext_in + length_in; char* codechar = code_out; char result; char fragment; result = state_in->result; switch (state_in->step) { while (1) { case step_A: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_A; return (int)(codechar - code_out); } fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; case step_B: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_B; return (int)(codechar - code_out); } fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; case step_C: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_C; return (int)(codechar - code_out); } fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); ++(state_in->stepcount); } } /* control should not reach here */ return (int)(codechar - code_out); } int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { char* codechar = code_out; switch (state_in->step) { case step_B: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; *codechar++ = '='; break; case step_C: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; break; case step_A: break; } *codechar++ = '\n'; return (int)(codechar - code_out); } libsass-3.3.4/src/color_maps.cpp000066400000000000000000000776511267254216700166460ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "color_maps.hpp" namespace Sass { namespace ColorNames { const char aliceblue [] = "aliceblue"; const char antiquewhite [] = "antiquewhite"; const char cyan [] = "cyan"; const char aqua [] = "aqua"; const char aquamarine [] = "aquamarine"; const char azure [] = "azure"; const char beige [] = "beige"; const char bisque [] = "bisque"; const char black [] = "black"; const char blanchedalmond [] = "blanchedalmond"; const char blue [] = "blue"; const char blueviolet [] = "blueviolet"; const char brown [] = "brown"; const char burlywood [] = "burlywood"; const char cadetblue [] = "cadetblue"; const char chartreuse [] = "chartreuse"; const char chocolate [] = "chocolate"; const char coral [] = "coral"; const char cornflowerblue [] = "cornflowerblue"; const char cornsilk [] = "cornsilk"; const char crimson [] = "crimson"; const char darkblue [] = "darkblue"; const char darkcyan [] = "darkcyan"; const char darkgoldenrod [] = "darkgoldenrod"; const char darkgray [] = "darkgray"; const char darkgrey [] = "darkgrey"; const char darkgreen [] = "darkgreen"; const char darkkhaki [] = "darkkhaki"; const char darkmagenta [] = "darkmagenta"; const char darkolivegreen [] = "darkolivegreen"; const char darkorange [] = "darkorange"; const char darkorchid [] = "darkorchid"; const char darkred [] = "darkred"; const char darksalmon [] = "darksalmon"; const char darkseagreen [] = "darkseagreen"; const char darkslateblue [] = "darkslateblue"; const char darkslategray [] = "darkslategray"; const char darkslategrey [] = "darkslategrey"; const char darkturquoise [] = "darkturquoise"; const char darkviolet [] = "darkviolet"; const char deeppink [] = "deeppink"; const char deepskyblue [] = "deepskyblue"; const char dimgray [] = "dimgray"; const char dimgrey [] = "dimgrey"; const char dodgerblue [] = "dodgerblue"; const char firebrick [] = "firebrick"; const char floralwhite [] = "floralwhite"; const char forestgreen [] = "forestgreen"; const char magenta [] = "magenta"; const char fuchsia [] = "fuchsia"; const char gainsboro [] = "gainsboro"; const char ghostwhite [] = "ghostwhite"; const char gold [] = "gold"; const char goldenrod [] = "goldenrod"; const char gray [] = "gray"; const char grey [] = "grey"; const char green [] = "green"; const char greenyellow [] = "greenyellow"; const char honeydew [] = "honeydew"; const char hotpink [] = "hotpink"; const char indianred [] = "indianred"; const char indigo [] = "indigo"; const char ivory [] = "ivory"; const char khaki [] = "khaki"; const char lavender [] = "lavender"; const char lavenderblush [] = "lavenderblush"; const char lawngreen [] = "lawngreen"; const char lemonchiffon [] = "lemonchiffon"; const char lightblue [] = "lightblue"; const char lightcoral [] = "lightcoral"; const char lightcyan [] = "lightcyan"; const char lightgoldenrodyellow [] = "lightgoldenrodyellow"; const char lightgray [] = "lightgray"; const char lightgrey [] = "lightgrey"; const char lightgreen [] = "lightgreen"; const char lightpink [] = "lightpink"; const char lightsalmon [] = "lightsalmon"; const char lightseagreen [] = "lightseagreen"; const char lightskyblue [] = "lightskyblue"; const char lightslategray [] = "lightslategray"; const char lightslategrey [] = "lightslategrey"; const char lightsteelblue [] = "lightsteelblue"; const char lightyellow [] = "lightyellow"; const char lime [] = "lime"; const char limegreen [] = "limegreen"; const char linen [] = "linen"; const char maroon [] = "maroon"; const char mediumaquamarine [] = "mediumaquamarine"; const char mediumblue [] = "mediumblue"; const char mediumorchid [] = "mediumorchid"; const char mediumpurple [] = "mediumpurple"; const char mediumseagreen [] = "mediumseagreen"; const char mediumslateblue [] = "mediumslateblue"; const char mediumspringgreen [] = "mediumspringgreen"; const char mediumturquoise [] = "mediumturquoise"; const char mediumvioletred [] = "mediumvioletred"; const char midnightblue [] = "midnightblue"; const char mintcream [] = "mintcream"; const char mistyrose [] = "mistyrose"; const char moccasin [] = "moccasin"; const char navajowhite [] = "navajowhite"; const char navy [] = "navy"; const char oldlace [] = "oldlace"; const char olive [] = "olive"; const char olivedrab [] = "olivedrab"; const char orange [] = "orange"; const char orangered [] = "orangered"; const char orchid [] = "orchid"; const char palegoldenrod [] = "palegoldenrod"; const char palegreen [] = "palegreen"; const char paleturquoise [] = "paleturquoise"; const char palevioletred [] = "palevioletred"; const char papayawhip [] = "papayawhip"; const char peachpuff [] = "peachpuff"; const char peru [] = "peru"; const char pink [] = "pink"; const char plum [] = "plum"; const char powderblue [] = "powderblue"; const char purple [] = "purple"; const char red [] = "red"; const char rosybrown [] = "rosybrown"; const char royalblue [] = "royalblue"; const char saddlebrown [] = "saddlebrown"; const char salmon [] = "salmon"; const char sandybrown [] = "sandybrown"; const char seagreen [] = "seagreen"; const char seashell [] = "seashell"; const char sienna [] = "sienna"; const char silver [] = "silver"; const char skyblue [] = "skyblue"; const char slateblue [] = "slateblue"; const char slategray [] = "slategray"; const char slategrey [] = "slategrey"; const char snow [] = "snow"; const char springgreen [] = "springgreen"; const char steelblue [] = "steelblue"; const char tan [] = "tan"; const char teal [] = "teal"; const char thistle [] = "thistle"; const char tomato [] = "tomato"; const char turquoise [] = "turquoise"; const char violet [] = "violet"; const char wheat [] = "wheat"; const char white [] = "white"; const char whitesmoke [] = "whitesmoke"; const char yellow [] = "yellow"; const char yellowgreen [] = "yellowgreen"; const char rebeccapurple [] = "rebeccapurple"; const char transparent [] = "transparent"; } 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 std::map colors_to_names { { 240 * 0x10000 + 248 * 0x100 + 255, ColorNames::aliceblue }, { 250 * 0x10000 + 235 * 0x100 + 215, ColorNames::antiquewhite }, { 0 * 0x10000 + 255 * 0x100 + 255, ColorNames::cyan }, { 127 * 0x10000 + 255 * 0x100 + 212, ColorNames::aquamarine }, { 240 * 0x10000 + 255 * 0x100 + 255, ColorNames::azure }, { 245 * 0x10000 + 245 * 0x100 + 220, ColorNames::beige }, { 255 * 0x10000 + 228 * 0x100 + 196, ColorNames::bisque }, { 0 * 0x10000 + 0 * 0x100 + 0, ColorNames::black }, { 255 * 0x10000 + 235 * 0x100 + 205, ColorNames::blanchedalmond }, { 0 * 0x10000 + 0 * 0x100 + 255, ColorNames::blue }, { 138 * 0x10000 + 43 * 0x100 + 226, ColorNames::blueviolet }, { 165 * 0x10000 + 42 * 0x100 + 42, ColorNames::brown }, { 222 * 0x10000 + 184 * 0x100 + 135, ColorNames::burlywood }, { 95 * 0x10000 + 158 * 0x100 + 160, ColorNames::cadetblue }, { 127 * 0x10000 + 255 * 0x100 + 0, ColorNames::chartreuse }, { 210 * 0x10000 + 105 * 0x100 + 30, ColorNames::chocolate }, { 255 * 0x10000 + 127 * 0x100 + 80, ColorNames::coral }, { 100 * 0x10000 + 149 * 0x100 + 237, ColorNames::cornflowerblue }, { 255 * 0x10000 + 248 * 0x100 + 220, ColorNames::cornsilk }, { 220 * 0x10000 + 20 * 0x100 + 60, ColorNames::crimson }, { 0 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkblue }, { 0 * 0x10000 + 139 * 0x100 + 139, ColorNames::darkcyan }, { 184 * 0x10000 + 134 * 0x100 + 11, ColorNames::darkgoldenrod }, { 169 * 0x10000 + 169 * 0x100 + 169, ColorNames::darkgray }, { 0 * 0x10000 + 100 * 0x100 + 0, ColorNames::darkgreen }, { 189 * 0x10000 + 183 * 0x100 + 107, ColorNames::darkkhaki }, { 139 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkmagenta }, { 85 * 0x10000 + 107 * 0x100 + 47, ColorNames::darkolivegreen }, { 255 * 0x10000 + 140 * 0x100 + 0, ColorNames::darkorange }, { 153 * 0x10000 + 50 * 0x100 + 204, ColorNames::darkorchid }, { 139 * 0x10000 + 0 * 0x100 + 0, ColorNames::darkred }, { 233 * 0x10000 + 150 * 0x100 + 122, ColorNames::darksalmon }, { 143 * 0x10000 + 188 * 0x100 + 143, ColorNames::darkseagreen }, { 72 * 0x10000 + 61 * 0x100 + 139, ColorNames::darkslateblue }, { 47 * 0x10000 + 79 * 0x100 + 79, ColorNames::darkslategray }, { 0 * 0x10000 + 206 * 0x100 + 209, ColorNames::darkturquoise }, { 148 * 0x10000 + 0 * 0x100 + 211, ColorNames::darkviolet }, { 255 * 0x10000 + 20 * 0x100 + 147, ColorNames::deeppink }, { 0 * 0x10000 + 191 * 0x100 + 255, ColorNames::deepskyblue }, { 105 * 0x10000 + 105 * 0x100 + 105, ColorNames::dimgray }, { 30 * 0x10000 + 144 * 0x100 + 255, ColorNames::dodgerblue }, { 178 * 0x10000 + 34 * 0x100 + 34, ColorNames::firebrick }, { 255 * 0x10000 + 250 * 0x100 + 240, ColorNames::floralwhite }, { 34 * 0x10000 + 139 * 0x100 + 34, ColorNames::forestgreen }, { 255 * 0x10000 + 0 * 0x100 + 255, ColorNames::magenta }, { 220 * 0x10000 + 220 * 0x100 + 220, ColorNames::gainsboro }, { 248 * 0x10000 + 248 * 0x100 + 255, ColorNames::ghostwhite }, { 255 * 0x10000 + 215 * 0x100 + 0, ColorNames::gold }, { 218 * 0x10000 + 165 * 0x100 + 32, ColorNames::goldenrod }, { 128 * 0x10000 + 128 * 0x100 + 128, ColorNames::gray }, { 0 * 0x10000 + 128 * 0x100 + 0, ColorNames::green }, { 173 * 0x10000 + 255 * 0x100 + 47, ColorNames::greenyellow }, { 240 * 0x10000 + 255 * 0x100 + 240, ColorNames::honeydew }, { 255 * 0x10000 + 105 * 0x100 + 180, ColorNames::hotpink }, { 205 * 0x10000 + 92 * 0x100 + 92, ColorNames::indianred }, { 75 * 0x10000 + 0 * 0x100 + 130, ColorNames::indigo }, { 255 * 0x10000 + 255 * 0x100 + 240, ColorNames::ivory }, { 240 * 0x10000 + 230 * 0x100 + 140, ColorNames::khaki }, { 230 * 0x10000 + 230 * 0x100 + 250, ColorNames::lavender }, { 255 * 0x10000 + 240 * 0x100 + 245, ColorNames::lavenderblush }, { 124 * 0x10000 + 252 * 0x100 + 0, ColorNames::lawngreen }, { 255 * 0x10000 + 250 * 0x100 + 205, ColorNames::lemonchiffon }, { 173 * 0x10000 + 216 * 0x100 + 230, ColorNames::lightblue }, { 240 * 0x10000 + 128 * 0x100 + 128, ColorNames::lightcoral }, { 224 * 0x10000 + 255 * 0x100 + 255, ColorNames::lightcyan }, { 250 * 0x10000 + 250 * 0x100 + 210, ColorNames::lightgoldenrodyellow }, { 211 * 0x10000 + 211 * 0x100 + 211, ColorNames::lightgray }, { 144 * 0x10000 + 238 * 0x100 + 144, ColorNames::lightgreen }, { 255 * 0x10000 + 182 * 0x100 + 193, ColorNames::lightpink }, { 255 * 0x10000 + 160 * 0x100 + 122, ColorNames::lightsalmon }, { 32 * 0x10000 + 178 * 0x100 + 170, ColorNames::lightseagreen }, { 135 * 0x10000 + 206 * 0x100 + 250, ColorNames::lightskyblue }, { 119 * 0x10000 + 136 * 0x100 + 153, ColorNames::lightslategray }, { 176 * 0x10000 + 196 * 0x100 + 222, ColorNames::lightsteelblue }, { 255 * 0x10000 + 255 * 0x100 + 224, ColorNames::lightyellow }, { 0 * 0x10000 + 255 * 0x100 + 0, ColorNames::lime }, { 50 * 0x10000 + 205 * 0x100 + 50, ColorNames::limegreen }, { 250 * 0x10000 + 240 * 0x100 + 230, ColorNames::linen }, { 128 * 0x10000 + 0 * 0x100 + 0, ColorNames::maroon }, { 102 * 0x10000 + 205 * 0x100 + 170, ColorNames::mediumaquamarine }, { 0 * 0x10000 + 0 * 0x100 + 205, ColorNames::mediumblue }, { 186 * 0x10000 + 85 * 0x100 + 211, ColorNames::mediumorchid }, { 147 * 0x10000 + 112 * 0x100 + 219, ColorNames::mediumpurple }, { 60 * 0x10000 + 179 * 0x100 + 113, ColorNames::mediumseagreen }, { 123 * 0x10000 + 104 * 0x100 + 238, ColorNames::mediumslateblue }, { 0 * 0x10000 + 250 * 0x100 + 154, ColorNames::mediumspringgreen }, { 72 * 0x10000 + 209 * 0x100 + 204, ColorNames::mediumturquoise }, { 199 * 0x10000 + 21 * 0x100 + 133, ColorNames::mediumvioletred }, { 25 * 0x10000 + 25 * 0x100 + 112, ColorNames::midnightblue }, { 245 * 0x10000 + 255 * 0x100 + 250, ColorNames::mintcream }, { 255 * 0x10000 + 228 * 0x100 + 225, ColorNames::mistyrose }, { 255 * 0x10000 + 228 * 0x100 + 181, ColorNames::moccasin }, { 255 * 0x10000 + 222 * 0x100 + 173, ColorNames::navajowhite }, { 0 * 0x10000 + 0 * 0x100 + 128, ColorNames::navy }, { 253 * 0x10000 + 245 * 0x100 + 230, ColorNames::oldlace }, { 128 * 0x10000 + 128 * 0x100 + 0, ColorNames::olive }, { 107 * 0x10000 + 142 * 0x100 + 35, ColorNames::olivedrab }, { 255 * 0x10000 + 165 * 0x100 + 0, ColorNames::orange }, { 255 * 0x10000 + 69 * 0x100 + 0, ColorNames::orangered }, { 218 * 0x10000 + 112 * 0x100 + 214, ColorNames::orchid }, { 238 * 0x10000 + 232 * 0x100 + 170, ColorNames::palegoldenrod }, { 152 * 0x10000 + 251 * 0x100 + 152, ColorNames::palegreen }, { 175 * 0x10000 + 238 * 0x100 + 238, ColorNames::paleturquoise }, { 219 * 0x10000 + 112 * 0x100 + 147, ColorNames::palevioletred }, { 255 * 0x10000 + 239 * 0x100 + 213, ColorNames::papayawhip }, { 255 * 0x10000 + 218 * 0x100 + 185, ColorNames::peachpuff }, { 205 * 0x10000 + 133 * 0x100 + 63, ColorNames::peru }, { 255 * 0x10000 + 192 * 0x100 + 203, ColorNames::pink }, { 221 * 0x10000 + 160 * 0x100 + 221, ColorNames::plum }, { 176 * 0x10000 + 224 * 0x100 + 230, ColorNames::powderblue }, { 128 * 0x10000 + 0 * 0x100 + 128, ColorNames::purple }, { 255 * 0x10000 + 0 * 0x100 + 0, ColorNames::red }, { 188 * 0x10000 + 143 * 0x100 + 143, ColorNames::rosybrown }, { 65 * 0x10000 + 105 * 0x100 + 225, ColorNames::royalblue }, { 139 * 0x10000 + 69 * 0x100 + 19, ColorNames::saddlebrown }, { 250 * 0x10000 + 128 * 0x100 + 114, ColorNames::salmon }, { 244 * 0x10000 + 164 * 0x100 + 96, ColorNames::sandybrown }, { 46 * 0x10000 + 139 * 0x100 + 87, ColorNames::seagreen }, { 255 * 0x10000 + 245 * 0x100 + 238, ColorNames::seashell }, { 160 * 0x10000 + 82 * 0x100 + 45, ColorNames::sienna }, { 192 * 0x10000 + 192 * 0x100 + 192, ColorNames::silver }, { 135 * 0x10000 + 206 * 0x100 + 235, ColorNames::skyblue }, { 106 * 0x10000 + 90 * 0x100 + 205, ColorNames::slateblue }, { 112 * 0x10000 + 128 * 0x100 + 144, ColorNames::slategray }, { 255 * 0x10000 + 250 * 0x100 + 250, ColorNames::snow }, { 0 * 0x10000 + 255 * 0x100 + 127, ColorNames::springgreen }, { 70 * 0x10000 + 130 * 0x100 + 180, ColorNames::steelblue }, { 210 * 0x10000 + 180 * 0x100 + 140, ColorNames::tan }, { 0 * 0x10000 + 128 * 0x100 + 128, ColorNames::teal }, { 216 * 0x10000 + 191 * 0x100 + 216, ColorNames::thistle }, { 255 * 0x10000 + 99 * 0x100 + 71, ColorNames::tomato }, { 64 * 0x10000 + 224 * 0x100 + 208, ColorNames::turquoise }, { 238 * 0x10000 + 130 * 0x100 + 238, ColorNames::violet }, { 245 * 0x10000 + 222 * 0x100 + 179, ColorNames::wheat }, { 255 * 0x10000 + 255 * 0x100 + 255, ColorNames::white }, { 245 * 0x10000 + 245 * 0x100 + 245, ColorNames::whitesmoke }, { 255 * 0x10000 + 255 * 0x100 + 0, ColorNames::yellow }, { 154 * 0x10000 + 205 * 0x100 + 50, ColorNames::yellowgreen }, { 102 * 0x10000 + 51 * 0x100 + 153, ColorNames::rebeccapurple } }; const std::map names_to_colors { { ColorNames::aliceblue, &Colors::aliceblue }, { ColorNames::antiquewhite, &Colors::antiquewhite }, { ColorNames::cyan, &Colors::cyan }, { ColorNames::aqua, &Colors::aqua }, { ColorNames::aquamarine, &Colors::aquamarine }, { ColorNames::azure, &Colors::azure }, { ColorNames::beige, &Colors::beige }, { ColorNames::bisque, &Colors::bisque }, { ColorNames::black, &Colors::black }, { ColorNames::blanchedalmond, &Colors::blanchedalmond }, { ColorNames::blue, &Colors::blue }, { ColorNames::blueviolet, &Colors::blueviolet }, { ColorNames::brown, &Colors::brown }, { ColorNames::burlywood, &Colors::burlywood }, { ColorNames::cadetblue, &Colors::cadetblue }, { ColorNames::chartreuse, &Colors::chartreuse }, { ColorNames::chocolate, &Colors::chocolate }, { ColorNames::coral, &Colors::coral }, { ColorNames::cornflowerblue, &Colors::cornflowerblue }, { ColorNames::cornsilk, &Colors::cornsilk }, { ColorNames::crimson, &Colors::crimson }, { ColorNames::darkblue, &Colors::darkblue }, { ColorNames::darkcyan, &Colors::darkcyan }, { ColorNames::darkgoldenrod, &Colors::darkgoldenrod }, { ColorNames::darkgray, &Colors::darkgray }, { ColorNames::darkgrey, &Colors::darkgrey }, { ColorNames::darkgreen, &Colors::darkgreen }, { ColorNames::darkkhaki, &Colors::darkkhaki }, { ColorNames::darkmagenta, &Colors::darkmagenta }, { ColorNames::darkolivegreen, &Colors::darkolivegreen }, { ColorNames::darkorange, &Colors::darkorange }, { ColorNames::darkorchid, &Colors::darkorchid }, { ColorNames::darkred, &Colors::darkred }, { ColorNames::darksalmon, &Colors::darksalmon }, { ColorNames::darkseagreen, &Colors::darkseagreen }, { ColorNames::darkslateblue, &Colors::darkslateblue }, { ColorNames::darkslategray, &Colors::darkslategray }, { ColorNames::darkslategrey, &Colors::darkslategrey }, { ColorNames::darkturquoise, &Colors::darkturquoise }, { ColorNames::darkviolet, &Colors::darkviolet }, { ColorNames::deeppink, &Colors::deeppink }, { ColorNames::deepskyblue, &Colors::deepskyblue }, { ColorNames::dimgray, &Colors::dimgray }, { ColorNames::dimgrey, &Colors::dimgrey }, { ColorNames::dodgerblue, &Colors::dodgerblue }, { ColorNames::firebrick, &Colors::firebrick }, { ColorNames::floralwhite, &Colors::floralwhite }, { ColorNames::forestgreen, &Colors::forestgreen }, { ColorNames::magenta, &Colors::magenta }, { ColorNames::fuchsia, &Colors::fuchsia }, { ColorNames::gainsboro, &Colors::gainsboro }, { ColorNames::ghostwhite, &Colors::ghostwhite }, { ColorNames::gold, &Colors::gold }, { ColorNames::goldenrod, &Colors::goldenrod }, { ColorNames::gray, &Colors::gray }, { ColorNames::grey, &Colors::grey }, { ColorNames::green, &Colors::green }, { ColorNames::greenyellow, &Colors::greenyellow }, { ColorNames::honeydew, &Colors::honeydew }, { ColorNames::hotpink, &Colors::hotpink }, { ColorNames::indianred, &Colors::indianred }, { ColorNames::indigo, &Colors::indigo }, { ColorNames::ivory, &Colors::ivory }, { ColorNames::khaki, &Colors::khaki }, { ColorNames::lavender, &Colors::lavender }, { ColorNames::lavenderblush, &Colors::lavenderblush }, { ColorNames::lawngreen, &Colors::lawngreen }, { ColorNames::lemonchiffon, &Colors::lemonchiffon }, { ColorNames::lightblue, &Colors::lightblue }, { ColorNames::lightcoral, &Colors::lightcoral }, { ColorNames::lightcyan, &Colors::lightcyan }, { ColorNames::lightgoldenrodyellow, &Colors::lightgoldenrodyellow }, { ColorNames::lightgray, &Colors::lightgray }, { ColorNames::lightgrey, &Colors::lightgrey }, { ColorNames::lightgreen, &Colors::lightgreen }, { ColorNames::lightpink, &Colors::lightpink }, { ColorNames::lightsalmon, &Colors::lightsalmon }, { ColorNames::lightseagreen, &Colors::lightseagreen }, { ColorNames::lightskyblue, &Colors::lightskyblue }, { ColorNames::lightslategray, &Colors::lightslategray }, { ColorNames::lightslategrey, &Colors::lightslategrey }, { ColorNames::lightsteelblue, &Colors::lightsteelblue }, { ColorNames::lightyellow, &Colors::lightyellow }, { ColorNames::lime, &Colors::lime }, { ColorNames::limegreen, &Colors::limegreen }, { ColorNames::linen, &Colors::linen }, { ColorNames::maroon, &Colors::maroon }, { ColorNames::mediumaquamarine, &Colors::mediumaquamarine }, { ColorNames::mediumblue, &Colors::mediumblue }, { ColorNames::mediumorchid, &Colors::mediumorchid }, { ColorNames::mediumpurple, &Colors::mediumpurple }, { ColorNames::mediumseagreen, &Colors::mediumseagreen }, { ColorNames::mediumslateblue, &Colors::mediumslateblue }, { ColorNames::mediumspringgreen, &Colors::mediumspringgreen }, { ColorNames::mediumturquoise, &Colors::mediumturquoise }, { ColorNames::mediumvioletred, &Colors::mediumvioletred }, { ColorNames::midnightblue, &Colors::midnightblue }, { ColorNames::mintcream, &Colors::mintcream }, { ColorNames::mistyrose, &Colors::mistyrose }, { ColorNames::moccasin, &Colors::moccasin }, { ColorNames::navajowhite, &Colors::navajowhite }, { ColorNames::navy, &Colors::navy }, { ColorNames::oldlace, &Colors::oldlace }, { ColorNames::olive, &Colors::olive }, { ColorNames::olivedrab, &Colors::olivedrab }, { ColorNames::orange, &Colors::orange }, { ColorNames::orangered, &Colors::orangered }, { ColorNames::orchid, &Colors::orchid }, { ColorNames::palegoldenrod, &Colors::palegoldenrod }, { ColorNames::palegreen, &Colors::palegreen }, { ColorNames::paleturquoise, &Colors::paleturquoise }, { ColorNames::palevioletred, &Colors::palevioletred }, { ColorNames::papayawhip, &Colors::papayawhip }, { ColorNames::peachpuff, &Colors::peachpuff }, { ColorNames::peru, &Colors::peru }, { ColorNames::pink, &Colors::pink }, { ColorNames::plum, &Colors::plum }, { ColorNames::powderblue, &Colors::powderblue }, { ColorNames::purple, &Colors::purple }, { ColorNames::red, &Colors::red }, { ColorNames::rosybrown, &Colors::rosybrown }, { ColorNames::royalblue, &Colors::royalblue }, { ColorNames::saddlebrown, &Colors::saddlebrown }, { ColorNames::salmon, &Colors::salmon }, { ColorNames::sandybrown, &Colors::sandybrown }, { ColorNames::seagreen, &Colors::seagreen }, { ColorNames::seashell, &Colors::seashell }, { ColorNames::sienna, &Colors::sienna }, { ColorNames::silver, &Colors::silver }, { ColorNames::skyblue, &Colors::skyblue }, { ColorNames::slateblue, &Colors::slateblue }, { ColorNames::slategray, &Colors::slategray }, { ColorNames::slategrey, &Colors::slategrey }, { ColorNames::snow, &Colors::snow }, { ColorNames::springgreen, &Colors::springgreen }, { ColorNames::steelblue, &Colors::steelblue }, { ColorNames::tan, &Colors::tan }, { ColorNames::teal, &Colors::teal }, { ColorNames::thistle, &Colors::thistle }, { ColorNames::tomato, &Colors::tomato }, { ColorNames::turquoise, &Colors::turquoise }, { ColorNames::violet, &Colors::violet }, { ColorNames::wheat, &Colors::wheat }, { ColorNames::white, &Colors::white }, { ColorNames::whitesmoke, &Colors::whitesmoke }, { ColorNames::yellow, &Colors::yellow }, { ColorNames::yellowgreen, &Colors::yellowgreen }, { ColorNames::rebeccapurple, &Colors::rebeccapurple }, { ColorNames::transparent, &Colors::transparent } }; const Color* name_to_color(const char* key) { auto p = names_to_colors.find(key); if (p != names_to_colors.end()) { return p->second; } return 0; } const Color* name_to_color(const std::string& key) { return name_to_color(key.c_str()); } const char* color_to_name(const int key) { auto p = colors_to_names.find(key); if (p != colors_to_names.end()) { return p->second; } return 0; } const char* color_to_name(const double key) { return color_to_name((int)key); } const char* color_to_name(const Color& c) { double key = c.r() * 0x10000 + c.g() * 0x100 + c.b(); return color_to_name(key); } } libsass-3.3.4/src/color_maps.hpp000066400000000000000000000253401267254216700166370ustar00rootroot00000000000000#ifndef SASS_COLOR_MAPS_H #define SASS_COLOR_MAPS_H #include #include "ast.hpp" 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[]; extern const char antiquewhite[]; extern const char cyan[]; extern const char aqua[]; extern const char aquamarine[]; extern const char azure[]; extern const char beige[]; extern const char bisque[]; extern const char black[]; extern const char blanchedalmond[]; extern const char blue[]; extern const char blueviolet[]; extern const char brown[]; extern const char burlywood[]; extern const char cadetblue[]; extern const char chartreuse[]; extern const char chocolate[]; extern const char coral[]; extern const char cornflowerblue[]; extern const char cornsilk[]; extern const char crimson[]; extern const char darkblue[]; extern const char darkcyan[]; extern const char darkgoldenrod[]; extern const char darkgray[]; extern const char darkgrey[]; extern const char darkgreen[]; extern const char darkkhaki[]; extern const char darkmagenta[]; extern const char darkolivegreen[]; extern const char darkorange[]; extern const char darkorchid[]; extern const char darkred[]; extern const char darksalmon[]; extern const char darkseagreen[]; extern const char darkslateblue[]; extern const char darkslategray[]; extern const char darkslategrey[]; extern const char darkturquoise[]; extern const char darkviolet[]; extern const char deeppink[]; extern const char deepskyblue[]; extern const char dimgray[]; extern const char dimgrey[]; extern const char dodgerblue[]; extern const char firebrick[]; extern const char floralwhite[]; extern const char forestgreen[]; extern const char magenta[]; extern const char fuchsia[]; extern const char gainsboro[]; extern const char ghostwhite[]; extern const char gold[]; extern const char goldenrod[]; extern const char gray[]; extern const char grey[]; extern const char green[]; extern const char greenyellow[]; extern const char honeydew[]; extern const char hotpink[]; extern const char indianred[]; extern const char indigo[]; extern const char ivory[]; extern const char khaki[]; extern const char lavender[]; extern const char lavenderblush[]; extern const char lawngreen[]; extern const char lemonchiffon[]; extern const char lightblue[]; extern const char lightcoral[]; extern const char lightcyan[]; extern const char lightgoldenrodyellow[]; extern const char lightgray[]; extern const char lightgrey[]; extern const char lightgreen[]; extern const char lightpink[]; extern const char lightsalmon[]; extern const char lightseagreen[]; extern const char lightskyblue[]; extern const char lightslategray[]; extern const char lightslategrey[]; extern const char lightsteelblue[]; extern const char lightyellow[]; extern const char lime[]; extern const char limegreen[]; extern const char linen[]; extern const char maroon[]; extern const char mediumaquamarine[]; extern const char mediumblue[]; extern const char mediumorchid[]; extern const char mediumpurple[]; extern const char mediumseagreen[]; extern const char mediumslateblue[]; extern const char mediumspringgreen[]; extern const char mediumturquoise[]; extern const char mediumvioletred[]; extern const char midnightblue[]; extern const char mintcream[]; extern const char mistyrose[]; extern const char moccasin[]; extern const char navajowhite[]; extern const char navy[]; extern const char oldlace[]; extern const char olive[]; extern const char olivedrab[]; extern const char orange[]; extern const char orangered[]; extern const char orchid[]; extern const char palegoldenrod[]; extern const char palegreen[]; extern const char paleturquoise[]; extern const char palevioletred[]; extern const char papayawhip[]; extern const char peachpuff[]; extern const char peru[]; extern const char pink[]; extern const char plum[]; extern const char powderblue[]; extern const char purple[]; extern const char red[]; extern const char rosybrown[]; extern const char royalblue[]; extern const char saddlebrown[]; extern const char salmon[]; extern const char sandybrown[]; extern const char seagreen[]; extern const char seashell[]; extern const char sienna[]; extern const char silver[]; extern const char skyblue[]; extern const char slateblue[]; extern const char slategray[]; extern const char slategrey[]; extern const char snow[]; extern const char springgreen[]; extern const char steelblue[]; extern const char tan[]; extern const char teal[]; extern const char thistle[]; extern const char tomato[]; extern const char turquoise[]; extern const char violet[]; extern const char wheat[]; extern const char white[]; extern const char whitesmoke[]; extern const char yellow[]; extern const char yellowgreen[]; extern const char rebeccapurple[]; extern const char transparent[]; } 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 std::map colors_to_names; extern const std::map names_to_colors; extern const Color* name_to_color(const char*); extern const Color* name_to_color(const std::string&); extern const char* color_to_name(const int); extern const char* color_to_name(const Color&); extern const char* color_to_name(const double); } #endif libsass-3.3.4/src/constants.cpp000066400000000000000000000202611267254216700165050ustar00rootroot00000000000000#include "sass.hpp" #include "constants.hpp" namespace Sass { namespace Constants { extern const unsigned long MaxCallStack = 1024; // https://github.com/sass/libsass/issues/592 // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity // https://github.com/sass/sass/issues/1495#issuecomment-61189114 extern const unsigned long Specificity_Star = 0; extern const unsigned long Specificity_Universal = 1 << 0; extern const unsigned long Specificity_Type = 1 << 8; extern const unsigned long Specificity_Class = 1 << 16; extern const unsigned long Specificity_Attr = 1 << 16; extern const unsigned long Specificity_Pseudo = 1 << 16; extern const unsigned long Specificity_ID = 1 << 24; // sass keywords extern const char at_root_kwd[] = "@at-root"; extern const char import_kwd[] = "@import"; extern const char mixin_kwd[] = "@mixin"; extern const char function_kwd[] = "@function"; extern const char return_kwd[] = "@return"; extern const char include_kwd[] = "@include"; extern const char content_kwd[] = "@content"; extern const char extend_kwd[] = "@extend"; extern const char if_kwd[] = "@if"; extern const char else_kwd[] = "@else"; extern const char if_after_else_kwd[] = "if"; extern const char for_kwd[] = "@for"; extern const char from_kwd[] = "from"; extern const char to_kwd[] = "to"; extern const char through_kwd[] = "through"; extern const char each_kwd[] = "@each"; extern const char in_kwd[] = "in"; extern const char while_kwd[] = "@while"; extern const char warn_kwd[] = "@warn"; extern const char error_kwd[] = "@error"; extern const char debug_kwd[] = "@debug"; extern const char default_kwd[] = "default"; extern const char global_kwd[] = "global"; extern const char null_kwd[] = "null"; extern const char optional_kwd[] = "optional"; extern const char with_kwd[] = "with"; extern const char without_kwd[] = "without"; extern const char all_kwd[] = "all"; extern const char rule_kwd[] = "rule"; // css standard units extern const char em_kwd[] = "em"; extern const char ex_kwd[] = "ex"; extern const char px_kwd[] = "px"; extern const char cm_kwd[] = "cm"; extern const char mm_kwd[] = "mm"; extern const char pt_kwd[] = "pt"; extern const char pc_kwd[] = "pc"; extern const char deg_kwd[] = "deg"; extern const char rad_kwd[] = "rad"; extern const char grad_kwd[] = "grad"; extern const char turn_kwd[] = "turn"; extern const char ms_kwd[] = "ms"; extern const char s_kwd[] = "s"; extern const char Hz_kwd[] = "Hz"; extern const char kHz_kwd[] = "kHz"; // vendor prefixes extern const char vendor_opera_kwd[] = "-o-"; extern const char vendor_webkit_kwd[] = "-webkit-"; extern const char vendor_mozilla_kwd[] = "-moz-"; extern const char vendor_ms_kwd[] = "-ms-"; extern const char vendor_khtml_kwd[] = "-khtml-"; // css functions and keywords extern const char charset_kwd[] = "@charset"; extern const char media_kwd[] = "@media"; extern const char supports_kwd[] = "@supports"; extern const char keyframes_kwd[] = "keyframes"; extern const char only_kwd[] = "only"; extern const char rgb_kwd[] = "rgb("; extern const char url_kwd[] = "url"; // extern const char url_prefix_kwd[] = "url-prefix("; extern const char important_kwd[] = "important"; extern const char pseudo_not_kwd[] = ":not("; extern const char even_kwd[] = "even"; extern const char odd_kwd[] = "odd"; extern const char progid_kwd[] = "progid"; extern const char expression_kwd[] = "expression"; extern const char calc_fn_kwd[] = "calc"; extern const char calc_kwd[] = "calc("; extern const char moz_calc_kwd[] = "-moz-calc("; extern const char webkit_calc_kwd[] = "-webkit-calc("; extern const char ms_calc_kwd[] = "-ms-calc("; // css selector keywords extern const char sel_deep_kwd[] = "/deep/"; // css attribute-matching operators extern const char tilde_equal[] = "~="; extern const char pipe_equal[] = "|="; extern const char caret_equal[] = "^="; extern const char dollar_equal[] = "$="; extern const char star_equal[] = "*="; // relational & logical operators and constants extern const char and_kwd[] = "and"; extern const char or_kwd[] = "or"; extern const char not_kwd[] = "not"; extern const char gt[] = ">"; extern const char gte[] = ">="; extern const char lt[] = "<"; extern const char lte[] = "<="; extern const char eq[] = "=="; extern const char neq[] = "!="; extern const char true_kwd[] = "true"; extern const char false_kwd[] = "false"; // miscellaneous punctuation and delimiters extern const char percent_str[] = "%"; extern const char empty_str[] = ""; extern const char slash_slash[] = "//"; extern const char slash_star[] = "/*"; extern const char star_slash[] = "*/"; extern const char hash_lbrace[] = "#{"; extern const char rbrace[] = "}"; extern const char rparen[] = ")"; extern const char sign_chars[] = "-+"; extern const char op_chars[] = "-+"; extern const char hyphen[] = "-"; extern const char ellipsis[] = "..."; // extern const char url_space_chars[] = " \t\r\n\f"; // type names extern const char numeric_name[] = "numeric value"; extern const char number_name[] = "number"; extern const char percentage_name[] = "percentage"; extern const char dimension_name[] = "numeric dimension"; extern const char string_name[] = "string"; extern const char bool_name[] = "bool"; extern const char color_name[] = "color"; extern const char list_name[] = "list"; extern const char map_name[] = "map"; extern const char arglist_name[] = "arglist"; // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[] = ":;/?!%&#@|[]{}'`^\"*+-.,_=~"; extern const char real_uri_chars[] = "#%&"; // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[] = "*/%"; // some character classes for the parser extern const char selector_list_delims[] = "){};!"; extern const char complex_selector_delims[] = ",){};!"; extern const char selector_combinator_ops[] = "+~>"; // optional modifiers for alternative compare context extern const char attribute_compare_modifiers[] = "~|^$*"; extern const char selector_lookahead_ops[] = "*&%,()[]"; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) extern const unsigned char utf_8_bom[] = { 0xEF, 0xBB, 0xBF }; extern const unsigned char utf_16_bom_be[] = { 0xFE, 0xFF }; extern const unsigned char utf_16_bom_le[] = { 0xFF, 0xFE }; extern const unsigned char utf_32_bom_be[] = { 0x00, 0x00, 0xFE, 0xFF }; extern const unsigned char utf_32_bom_le[] = { 0xFF, 0xFE, 0x00, 0x00 }; extern const unsigned char utf_7_bom_1[] = { 0x2B, 0x2F, 0x76, 0x38 }; extern const unsigned char utf_7_bom_2[] = { 0x2B, 0x2F, 0x76, 0x39 }; extern const unsigned char utf_7_bom_3[] = { 0x2B, 0x2F, 0x76, 0x2B }; extern const unsigned char utf_7_bom_4[] = { 0x2B, 0x2F, 0x76, 0x2F }; extern const unsigned char utf_7_bom_5[] = { 0x2B, 0x2F, 0x76, 0x38, 0x2D }; extern const unsigned char utf_1_bom[] = { 0xF7, 0x64, 0x4C }; extern const unsigned char utf_ebcdic_bom[] = { 0xDD, 0x73, 0x66, 0x73 }; extern const unsigned char scsu_bom[] = { 0x0E, 0xFE, 0xFF }; extern const unsigned char bocu_1_bom[] = { 0xFB, 0xEE, 0x28 }; extern const unsigned char gb_18030_bom[] = { 0x84, 0x31, 0x95, 0x33 }; } } libsass-3.3.4/src/constants.hpp000066400000000000000000000137251267254216700165210ustar00rootroot00000000000000#ifndef SASS_CONSTANTS_H #define SASS_CONSTANTS_H namespace Sass { namespace Constants { // The maximum call stack that can be created extern const unsigned long MaxCallStack; // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity // The following list of selectors is by increasing specificity: extern const unsigned long Specificity_Star; extern const unsigned long Specificity_Universal; extern const unsigned long Specificity_Type; extern const unsigned long Specificity_Class; extern const unsigned long Specificity_Attr; extern const unsigned long Specificity_Pseudo; extern const unsigned long Specificity_ID; // sass keywords extern const char at_root_kwd[]; extern const char import_kwd[]; extern const char mixin_kwd[]; extern const char function_kwd[]; extern const char return_kwd[]; extern const char include_kwd[]; extern const char content_kwd[]; extern const char extend_kwd[]; extern const char if_kwd[]; extern const char else_kwd[]; extern const char if_after_else_kwd[]; extern const char for_kwd[]; extern const char from_kwd[]; extern const char to_kwd[]; extern const char through_kwd[]; extern const char each_kwd[]; extern const char in_kwd[]; extern const char while_kwd[]; extern const char warn_kwd[]; extern const char error_kwd[]; extern const char debug_kwd[]; extern const char default_kwd[]; extern const char global_kwd[]; extern const char null_kwd[]; extern const char optional_kwd[]; extern const char with_kwd[]; extern const char without_kwd[]; extern const char all_kwd[]; extern const char rule_kwd[]; // css standard units extern const char em_kwd[]; extern const char ex_kwd[]; extern const char px_kwd[]; extern const char cm_kwd[]; extern const char mm_kwd[]; extern const char pt_kwd[]; extern const char pc_kwd[]; extern const char deg_kwd[]; extern const char rad_kwd[]; extern const char grad_kwd[]; extern const char turn_kwd[]; extern const char ms_kwd[]; extern const char s_kwd[]; extern const char Hz_kwd[]; extern const char kHz_kwd[]; // vendor prefixes extern const char vendor_opera_kwd[]; extern const char vendor_webkit_kwd[]; extern const char vendor_mozilla_kwd[]; extern const char vendor_ms_kwd[]; extern const char vendor_khtml_kwd[]; // css functions and keywords extern const char charset_kwd[]; extern const char media_kwd[]; extern const char supports_kwd[]; extern const char keyframes_kwd[]; extern const char only_kwd[]; extern const char rgb_kwd[]; extern const char url_kwd[]; // extern const char url_prefix_kwd[]; extern const char important_kwd[]; extern const char pseudo_not_kwd[]; extern const char even_kwd[]; extern const char odd_kwd[]; extern const char progid_kwd[]; extern const char expression_kwd[]; extern const char calc_kwd[]; extern const char calc_fn_kwd[]; extern const char moz_calc_kwd[]; extern const char webkit_calc_kwd[]; extern const char ms_calc_kwd[]; // css selector keywords extern const char sel_deep_kwd[]; // css attribute-matching operators extern const char tilde_equal[]; extern const char pipe_equal[]; extern const char caret_equal[]; extern const char dollar_equal[]; extern const char star_equal[]; // relational & logical operators and constants extern const char and_kwd[]; extern const char or_kwd[]; extern const char not_kwd[]; extern const char gt[]; extern const char gte[]; extern const char lt[]; extern const char lte[]; extern const char eq[]; extern const char neq[]; extern const char true_kwd[]; extern const char false_kwd[]; // miscellaneous punctuation and delimiters extern const char percent_str[]; extern const char empty_str[]; extern const char slash_slash[]; extern const char slash_star[]; extern const char star_slash[]; extern const char hash_lbrace[]; extern const char rbrace[]; extern const char rparen[]; extern const char sign_chars[]; extern const char op_chars[]; extern const char hyphen[]; extern const char ellipsis[]; // extern const char url_space_chars[]; // type names extern const char numeric_name[]; extern const char number_name[]; extern const char percentage_name[]; extern const char dimension_name[]; extern const char string_name[]; extern const char bool_name[]; extern const char color_name[]; extern const char list_name[]; extern const char map_name[]; extern const char arglist_name[]; // constants for uri parsing (RFC 3986 Appendix A.) extern const char uri_chars[]; extern const char real_uri_chars[]; // some specific constant character classes // they must be static to be useable by lexer extern const char static_ops[]; extern const char selector_list_delims[]; extern const char complex_selector_delims[]; extern const char selector_combinator_ops[]; extern const char attribute_compare_modifiers[]; extern const char selector_lookahead_ops[]; // byte order marks // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) extern const unsigned char utf_8_bom[]; extern const unsigned char utf_16_bom_be[]; extern const unsigned char utf_16_bom_le[]; extern const unsigned char utf_32_bom_be[]; extern const unsigned char utf_32_bom_le[]; extern const unsigned char utf_7_bom_1[]; extern const unsigned char utf_7_bom_2[]; extern const unsigned char utf_7_bom_3[]; extern const unsigned char utf_7_bom_4[]; extern const unsigned char utf_7_bom_5[]; extern const unsigned char utf_1_bom[]; extern const unsigned char utf_ebcdic_bom[]; extern const unsigned char scsu_bom[]; extern const unsigned char bocu_1_bom[]; extern const unsigned char gb_18030_bom[]; } } #endif libsass-3.3.4/src/context.cpp000066400000000000000000000764251267254216700161720ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include #include "ast.hpp" #include "util.hpp" #include "sass.h" #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 "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; using namespace File; using namespace Sass; inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j) { return sass_importer_get_priority(i) > sass_importer_get_priority(j); } 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; } static std::string safe_output(const char* out_path, const 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(input_path.find_last_of(".")); return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; } // enforce some safe defaults // used to create relative file links return safe_path == "" ? "stdout" : safe_path; } Context::Context(struct Sass_Context& c_ctx) : CWD(File::get_cwd()), c_options(c_ctx), entry_path(""), head_imports(0), mem(Memory_Manager()), plugins(), emitter(c_options), strings(), resources(), sheets(), subset_map(), import_stack(), c_headers (std::vector()), c_importers (std::vector()), c_functions (std::vector()), indent (safe_str(c_options.indent, " ")), linefeed (safe_str(c_options.linefeed, "\n")), input_path (make_canonical_path(safe_input(c_options.input_path))), output_path (make_canonical_path(safe_output(c_options.output_path, input_path))), source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))), source_map_root (make_canonical_path(safe_str(c_options.source_map_root, ""))) { // add cwd to include paths include_paths.push_back(CWD); // collect more paths from different options collect_include_paths(c_options.include_path); // collect_include_paths(c_options.include_paths); collect_plugin_paths(c_options.plugin_path); // collect_plugin_paths(c_options.plugin_paths); // load plugins and register custom behaviors for(auto plug : plugin_paths) plugins.load_plugins(plug); for(auto fn : plugins.get_headers()) c_headers.push_back(fn); for(auto fn : plugins.get_importers()) c_importers.push_back(fn); for(auto fn : plugins.get_functions()) c_functions.push_back(fn); // sort the items by priority (lowest first) sort (c_headers.begin(), c_headers.end(), sort_importers); sort (c_importers.begin(), c_importers.end(), sort_importers); emitter.set_filename(abs2rel(output_path, source_map_file, CWD)); } void Context::add_c_function(Sass_Function_Entry function) { c_functions.push_back(function); } void Context::add_c_header(Sass_Importer_Entry header) { c_headers.push_back(header); // need to sort the array afterwards (no big deal) sort (c_headers.begin(), c_headers.end(), sort_importers); } void Context::add_c_importer(Sass_Importer_Entry importer) { c_importers.push_back(importer); // need to sort the array afterwards (no big deal) sort (c_importers.begin(), c_importers.end(), sort_importers); } Context::~Context() { // resources were allocated by strdup or malloc for (size_t i = 0; i < resources.size(); ++i) { free(resources[i].contents); free(resources[i].srcmap); } // free all strings we kept alive during compiler execution for (size_t n = 0; n < strings.size(); ++n) free(strings[n]); // everything that gets put into sources will be freed by us // this shouldn't have anything in it anyway!? for (size_t m = 0; m < import_stack.size(); ++m) { sass_import_take_source(import_stack[m]); sass_import_take_srcmap(import_stack[m]); sass_delete_import(import_stack[m]); } // clear inner structures (vectors) and input source resources.clear(); import_stack.clear(); } Data_Context::~Data_Context() { // --> this will be freed by resources // make sure we free the source even if not processed! // if (resources.size() == 0 && source_c_str) free(source_c_str); // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str); // source_c_str = 0; srcmap_c_str = 0; } File_Context::~File_Context() { } void Context::collect_include_paths(const char* paths_str) { if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first(beg); while (end) { std::string path(beg, end - beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; include_paths.push_back(path); } beg = end + 1; end = Prelexer::find_first(beg); } std::string path(beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; include_paths.push_back(path); } } } void Context::collect_include_paths(const char** paths_array) { if (!paths_array) return; for (size_t i = 0; paths_array[i]; i++) { collect_include_paths(paths_array[i]); } } void Context::collect_plugin_paths(const char* paths_str) { if (paths_str) { const char* beg = paths_str; const char* end = Prelexer::find_first(beg); while (end) { std::string path(beg, end - beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; plugin_paths.push_back(path); } beg = end + 1; end = Prelexer::find_first(beg); } std::string path(beg); if (!path.empty()) { if (*path.rbegin() != '/') path += '/'; plugin_paths.push_back(path); } } } void Context::collect_plugin_paths(const char** paths_array) { if (!paths_array) return; for (size_t i = 0; paths_array[i]; i++) { collect_plugin_paths(paths_array[i]); } } // resolve the imp_path in base_path or include_paths // looks for alternatives and returns a list from one directory std::vector Context::find_includes(const Importer& import) { // make sure we resolve against an absolute path std::string base_path(rel2abs(import.base_path)); // first try to resolve the load path relative to the base path std::vector vec(resolve_includes(base_path, import.imp_path)); // then search in every include path (but only if nothing found yet) for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i) { // call resolve_includes and individual base path and append all results std::vector resolved(resolve_includes(include_paths[i], import.imp_path)); if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end()); } // return vector return vec; } // register include with resolved path and its content // memory of the resources will be freed by us on exit void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate) { // do not parse same resource twice // maybe raise an error in this case // if (sheets.count(inc.abs_path)) { // free(res.contents); free(res.srcmap); // throw std::runtime_error("duplicate resource registered"); // return; // } // get index for this resource size_t idx = resources.size(); // tell emitter about new resource emitter.add_source_index(idx); // put resources under our control // the memory will be freed later resources.push_back(res); // add a relative link to the working directory included_files.push_back(inc.abs_path); // add a relative link to the source map output file srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD)); // get pointer to the loaded content Sass_Import_Entry import = sass_make_import( inc.imp_path.c_str(), inc.abs_path.c_str(), res.contents, res.srcmap ); // add the entry to the stack import_stack.push_back(import); // get pointer to the loaded content const char* contents = resources[idx].contents; // keep a copy of the path around (for parserstates) // ToDo: we clean it, but still not very elegant!? strings.push_back(sass_strdup(inc.abs_path.c_str())); // create the initial parser state from resource ParserState pstate(strings.back(), contents, idx); // check existing import stack for possible recursion for (size_t i = 0; i < import_stack.size() - 2; ++i) { auto parent = import_stack[i]; if (std::strcmp(parent->abs_path, import->abs_path) == 0) { std::string stack("An @import loop has been found:"); for (size_t n = 1; n < i + 2; ++n) { stack += "\n " + std::string(import_stack[n]->imp_path) + " imports " + std::string(import_stack[n+1]->imp_path); } // implement error throw directly until we // decided how to handle full stack traces ParserState state = prstate ? *prstate : pstate; throw Exception::InvalidSyntax(state, stack, &import_stack); // error(stack, prstate ? *prstate : pstate, import_stack); } } // create a parser instance from the given c_str buffer Parser p(Parser::from_c_str(contents, *this, pstate)); // do not yet dispose these buffers sass_import_take_source(import); sass_import_take_srcmap(import); // then parse the root block Block* root = p.parse(); // delete memory of current stack frame sass_delete_import(import_stack.back()); // remove current stack frame import_stack.pop_back(); // create key/value pair for ast node std::pair ast_pair(inc.abs_path, { res, root }); // register resulting resource sheets.insert(ast_pair); } // Add a new import to the context (called from `import_url`) Include Context::load_import(const Importer& imp, ParserState pstate) { // search for valid imports (ie. partials) on the filesystem // this may return more than one valid result (ambiguous imp_path) const std::vector resolved(find_includes(imp)); // error nicely on ambiguous imp_path if (resolved.size() > 1) { std::stringstream msg_stream; msg_stream << "It's not clear which file to import for "; msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n"; msg_stream << "Candidates:" << "\n"; for (size_t i = 0, L = resolved.size(); i < L; ++i) { msg_stream << " " << resolved[i].imp_path << "\n"; } msg_stream << "Please delete or rename all but one of these files." << "\n"; error(msg_stream.str(), pstate); } // process the resolved entry else if (resolved.size() == 1) { bool use_cache = c_importers.size() == 0; // use cache for the resource loading if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; // try to read the content of the resolved file entry // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { // register the newly resolved file resource register_resource(resolved[0], { contents, 0 }, &pstate); // return resolved entry return resolved[0]; } } // nothing found return { imp, "" }; } 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)); std::string protocol("file"); using namespace Prelexer; if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { protocol = std::string(imp_path.c_str(), proto - 3); // if (protocol.compare("file") && true) { } } // add urls (protocol other than file) and urls without procotol to `urls` member // ToDo: if ctx_path is already a file resource, we should not add it here? if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") { imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path)); } else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") { String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path)); Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc); Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate); (*loc_args) << loc_arg; Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args); imp->urls().push_back(new_url); } else { const Importer importer(imp_path, ctx_path); Include include(load_import(importer, pstate)); if (include.abs_path.empty()) { error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate); } imp->incs().push_back(include); } } // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one) { // unique counter size_t count = 0; // need one correct import bool has_import = false; // process all custom importers (or custom headers) for (Sass_Importer_Entry& importer : importers) { // int priority = sass_importer_get_priority(importer); Sass_Importer_Fn fn = sass_importer_get_function(importer); // skip importer if it returns NULL if (Sass_Import_List includes = fn(load_path.c_str(), importer, c_compiler) ) { // get c pointer copy to iterate over Sass_Import_List it_includes = includes; while (*it_includes) { ++count; // create unique path to use as key std::string uniq_path = load_path; if (!only_one && count) { std::stringstream path_strm; path_strm << uniq_path << ":" << count; uniq_path = path_strm.str(); } // create the importer struct Importer importer(uniq_path, ctx_path); // query data from the current include Sass_Import_Entry include = *it_includes; char* source = sass_import_take_source(include); char* srcmap = sass_import_take_srcmap(include); size_t line = sass_import_get_error_line(include); size_t column = sass_import_get_error_column(include); const char *abs_path = sass_import_get_abs_path(include); // handle error message passed back from custom importer // it may (or may not) override the line and column info if (const char* err_message = sass_import_get_error_message(include)) { if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate); if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); else error(err_message, ParserState(ctx_path, source, Position(line, column))); } // content for import was set else if (source) { // resolved abs_path should be set by custom importer // use the created uniq_path as fallback (maybe enforce) std::string path_key(abs_path ? abs_path : uniq_path); // create the importer struct Include include(importer, path_key); // attach information to AST node imp->incs().push_back(include); // register the resource buffers register_resource(include, { source, srcmap }, &pstate); } // only a path was retuned // try to load it like normal else if(abs_path) { // checks some urls to preserve // `http://`, `https://` and `//` // or dispatchs to `import_file` // which will check for a `.css` extension // or resolves the file on the filesystem // added and resolved via `add_file` // finally stores everything on `imp` import_url(imp, abs_path, ctx_path); } // move to next ++it_includes; } // deallocate the returned memory sass_delete_import_list(includes); // set success flag has_import = true; // break out of loop if (only_one) break; } } // return result return has_import; } void register_function(Context&, Signature sig, Native_Function f, Env* env); void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); void register_overload_stub(Context&, std::string name, Env* env); void register_built_in_functions(Context&, Env* env); void register_c_functions(Context&, Env* env, Sass_Function_List); void register_c_function(Context&, Env* env, Sass_Function_Entry); char* Context::render(Block* root) { // check for valid block if (!root) return 0; // start the render process root->perform(&emitter); // finish emitter stream emitter.finalize(); // get the resulting buffer from stream OutputBuffer emitted = emitter.get_buffer(); // should we append a source map url? if (!c_options.omit_source_map_url) { // generate an embeded source map if (c_options.source_map_embed) { emitted.buffer += linefeed; emitted.buffer += format_embedded_source_map(); } // or just link the generated one else if (source_map_file != "") { emitted.buffer += linefeed; emitted.buffer += format_source_mapping_url(source_map_file); } } // create a copy of the resulting buffer string // this must be freed or taken over by implementor return sass_strdup(emitted.buffer.c_str()); } void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate) { // create a custom import to resolve headers Import* imp = SASS_MEMORY_NEW(mem, Import, pstate); // dispatch headers which will add custom functions // custom headers are added to the import instance call_headers(entry_path, ctx_path, pstate, imp); // increase head count to skip later head_imports += resources.size() - 1; // add the statement if we have urls if (!imp->urls().empty()) (*root) << imp; // process all other resources (add Import_Stub nodes) for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]); } } Block* File_Context::parse() { // check if entry file is given if (input_path.empty()) return 0; // create absolute path from input filename // ToDo: this should be resolved via custom importers std::string abs_path(rel2abs(input_path, CWD)); // try to load the entry file char* contents = read_file(abs_path); // alternatively also look inside each include path folder // I think this differs from ruby sass (IMO too late to remove) for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) { // build absolute path for this include path entry abs_path = rel2abs(input_path, include_paths[i]); // try to load the resulting path contents = read_file(abs_path); } // abort early if no content could be loaded (various reasons) if (!contents) throw "File to read not found or unreadable: " + input_path; // store entry path entry_path = abs_path; // create entry only for import stack Sass_Import_Entry import = sass_make_import( input_path.c_str(), entry_path.c_str(), contents, 0 ); // add the entry to the stack import_stack.push_back(import); // create the source entry for file entry register_resource({{ input_path, "." }, abs_path }, { contents, 0 }); // create root ast tree node return compile(); } Block* Data_Context::parse() { // check if source string is given if (!source_c_str) return 0; // convert indented sass syntax if(c_options.is_indented_syntax_src) { // call sass2scss to convert the string char * converted = sass2scss(source_c_str, // preserve the structure as much as possible SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT); // replace old source_c_str with converted free(source_c_str); source_c_str = converted; } // remember entry path (defaults to stdin for string) entry_path = input_path.empty() ? "stdin" : input_path; // ToDo: this may be resolved via custom importers std::string abs_path(rel2abs(entry_path)); char* abs_path_c_str = sass_strdup(abs_path.c_str()); strings.push_back(abs_path_c_str); // create entry only for the import stack Sass_Import_Entry import = sass_make_import( entry_path.c_str(), abs_path_c_str, source_c_str, srcmap_c_str ); // add the entry to the stack import_stack.push_back(import); // register a synthetic resource (path does not really exist, skip in includes) register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str }); // create root ast tree node return compile(); } // parse root block from includes Block* Context::compile() { // abort if there is no data if (resources.size() == 0) return 0; // get root block from the first style sheet Block* root = sheets.at(entry_path).root; // abort on invalid root if (root == 0) return 0; Env global; // create root environment // register built-in functions on env register_built_in_functions(*this, &global); // register custom functions (defined via C-API) for (size_t i = 0, S = c_functions.size(); i < S; ++i) { register_c_function(*this, &global, c_functions[i]); } // create initial backtrace entry Backtrace backtrace(0, ParserState("", 0), ""); // create crtp visitor objects Expand expand(*this, &global, &backtrace); Cssize cssize(*this, &backtrace); // expand and eval the tree root = root->perform(&expand)->block(); // merge and bubble certain rules root = root->perform(&cssize)->block(); // should we extend something? if (!subset_map.empty()) { // create crtp visitor object Extend extend(*this, subset_map); // extend tree nodes root->perform(&extend); } // clean up by removing empty placeholders // ToDo: maybe we can do this somewhere else? Remove_Placeholders remove_placeholders(*this); root->perform(&remove_placeholders); // return processed tree return root; } // EO compile std::string Context::format_embedded_source_map() { std::string map = emitter.render_srcmap(*this); std::istringstream is( map ); std::ostringstream buffer; base64::encoder E; E.encode(is, buffer); std::string url = "data:application/json;base64," + buffer.str(); url.erase(url.size() - 1); return "/*# sourceMappingURL=" + url + " */"; } std::string Context::format_source_mapping_url(const std::string& file) { std::string url = abs2rel(file, output_path, CWD); return "/*# sourceMappingURL=" + url + " */"; } char* Context::render_srcmap() { if (source_map_file == "") return 0; char* result = 0; std::string map = emitter.render_srcmap(*this); result = sass_strdup(map.c_str()); return result; } // for data context we want to start after "stdin" // we probably always want to skip the header includes? std::vector Context::get_included_files(bool skip, size_t headers) { // create a copy of the vector for manipulations std::vector includes = included_files; if (includes.size() == 0) return includes; if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); } else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); } includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() ); std::sort( includes.begin() + (skip ? 0 : 1), includes.end() ); return includes; } void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) { 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* def = make_native_function(sig, f, ctx); std::stringstream ss; ss << def->name() << "[f]" << arity; def->environment(env); (*env)[ss.str()] = def; } void register_overload_stub(Context& ctx, std::string name, Env* env) { Definition* stub = SASS_MEMORY_NEW(ctx.mem, Definition, ParserState("[built-in function]"), 0, name, 0, 0, true); (*env)[name + "[f]"] = stub; } void register_built_in_functions(Context& ctx, Env* env) { using namespace Functions; // RGB Functions register_function(ctx, rgb_sig, rgb, env); register_overload_stub(ctx, "rgba", env); register_function(ctx, rgba_4_sig, rgba_4, 4, env); register_function(ctx, rgba_2_sig, rgba_2, 2, env); register_function(ctx, red_sig, red, env); register_function(ctx, green_sig, green, env); register_function(ctx, blue_sig, blue, env); register_function(ctx, mix_sig, mix, env); // HSL Functions register_function(ctx, hsl_sig, hsl, env); register_function(ctx, hsla_sig, hsla, env); register_function(ctx, hue_sig, hue, env); register_function(ctx, saturation_sig, saturation, env); register_function(ctx, lightness_sig, lightness, env); register_function(ctx, adjust_hue_sig, adjust_hue, env); register_function(ctx, lighten_sig, lighten, env); register_function(ctx, darken_sig, darken, env); register_function(ctx, saturate_sig, saturate, env); register_function(ctx, desaturate_sig, desaturate, env); register_function(ctx, grayscale_sig, grayscale, env); register_function(ctx, complement_sig, complement, env); register_function(ctx, invert_sig, invert, env); // Opacity Functions register_function(ctx, alpha_sig, alpha, env); register_function(ctx, opacity_sig, alpha, env); register_function(ctx, opacify_sig, opacify, env); register_function(ctx, fade_in_sig, opacify, env); register_function(ctx, transparentize_sig, transparentize, env); register_function(ctx, fade_out_sig, transparentize, env); // Other Color Functions register_function(ctx, adjust_color_sig, adjust_color, env); register_function(ctx, scale_color_sig, scale_color, env); register_function(ctx, change_color_sig, change_color, env); register_function(ctx, ie_hex_str_sig, ie_hex_str, env); // String Functions register_function(ctx, unquote_sig, sass_unquote, env); register_function(ctx, quote_sig, sass_quote, env); register_function(ctx, str_length_sig, str_length, env); register_function(ctx, str_insert_sig, str_insert, env); register_function(ctx, str_index_sig, str_index, env); register_function(ctx, str_slice_sig, str_slice, env); register_function(ctx, to_upper_case_sig, to_upper_case, env); register_function(ctx, to_lower_case_sig, to_lower_case, env); // Number Functions register_function(ctx, percentage_sig, percentage, env); register_function(ctx, round_sig, round, env); register_function(ctx, ceil_sig, ceil, env); register_function(ctx, floor_sig, floor, env); register_function(ctx, abs_sig, abs, env); register_function(ctx, min_sig, min, env); register_function(ctx, max_sig, max, env); register_function(ctx, random_sig, random, env); // List Functions register_function(ctx, length_sig, length, env); register_function(ctx, nth_sig, nth, env); register_function(ctx, set_nth_sig, set_nth, env); register_function(ctx, index_sig, index, env); register_function(ctx, join_sig, join, env); register_function(ctx, append_sig, append, env); register_function(ctx, zip_sig, zip, env); register_function(ctx, list_separator_sig, list_separator, env); // Map Functions register_function(ctx, map_get_sig, map_get, env); register_function(ctx, map_merge_sig, map_merge, env); register_function(ctx, map_remove_sig, map_remove, env); register_function(ctx, map_keys_sig, map_keys, env); register_function(ctx, map_values_sig, map_values, env); register_function(ctx, map_has_key_sig, map_has_key, env); register_function(ctx, keywords_sig, keywords, env); // Introspection Functions register_function(ctx, type_of_sig, type_of, env); register_function(ctx, unit_sig, unit, env); register_function(ctx, unitless_sig, unitless, env); register_function(ctx, comparable_sig, comparable, env); register_function(ctx, variable_exists_sig, variable_exists, env); register_function(ctx, global_variable_exists_sig, global_variable_exists, env); register_function(ctx, function_exists_sig, function_exists, env); register_function(ctx, mixin_exists_sig, mixin_exists, env); register_function(ctx, feature_exists_sig, feature_exists, env); register_function(ctx, call_sig, call, env); // Boolean Functions register_function(ctx, not_sig, sass_not, env); register_function(ctx, if_sig, sass_if, env); // Misc Functions register_function(ctx, inspect_sig, inspect, env); register_function(ctx, unique_id_sig, unique_id, env); // Selector functions register_function(ctx, selector_nest_sig, selector_nest, env); register_function(ctx, selector_append_sig, selector_append, env); register_function(ctx, selector_extend_sig, selector_extend, env); register_function(ctx, selector_replace_sig, selector_replace, env); register_function(ctx, selector_unify_sig, selector_unify, env); register_function(ctx, is_superselector_sig, is_superselector, env); register_function(ctx, simple_selectors_sig, simple_selectors, env); register_function(ctx, selector_parse_sig, selector_parse, env); } void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs) { while (descrs && *descrs) { register_c_function(ctx, env, *descrs); ++descrs; } } void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr) { Definition* def = make_c_function(descr, ctx); def->environment(env); (*env)[def->name() + "[f]"] = def; } } libsass-3.3.4/src/context.hpp000066400000000000000000000113111267254216700161560ustar00rootroot00000000000000#ifndef SASS_CONTEXT_H #define SASS_CONTEXT_H #include #include #include #define BUFFERSIZE 255 #include "b64/encode.h" #include "ast_fwd_decl.hpp" #include "kwd_arg_macros.hpp" #include "memory_manager.hpp" #include "ast_fwd_decl.hpp" #include "sass_context.hpp" #include "environment.hpp" #include "source_map.hpp" #include "subset_map.hpp" #include "output.hpp" #include "plugins.hpp" #include "file.hpp" struct Sass_Function; namespace Sass { class Context { public: void import_url (Import* imp, std::string load_path, const std::string& ctx_path); bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); }; bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp) { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); }; private: bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector importers, bool only_one = true); public: const std::string CWD; struct Sass_Options& c_options; std::string entry_path; size_t head_imports; Memory_Manager mem; Plugins plugins; Output emitter; // resources add under our control // these are guaranteed to be freed std::vector strings; std::vector resources; std::map sheets; Subset_Map > subset_map; std::vector import_stack; struct Sass_Compiler* c_compiler; // absolute paths to includes std::vector included_files; // relative includes for sourcemap std::vector srcmap_links; // vectors above have same size std::vector plugin_paths; // relative paths to load plugins std::vector include_paths; // lookup paths for includes void apply_custom_headers(Block* root, const char* path, ParserState pstate); std::vector c_headers; std::vector c_importers; std::vector c_functions; void add_c_header(Sass_Importer_Entry header); void add_c_importer(Sass_Importer_Entry importer); void add_c_function(Sass_Function_Entry function); const std::string indent; // String to be used for indentation const std::string linefeed; // String to be used for line feeds const std::string input_path; // for relative paths in src-map const std::string output_path; // for relative paths to the output const std::string source_map_file; // path to source map file (enables feature) const std::string source_map_root; // path for sourceRoot property (pass-through) virtual ~Context(); Context(struct Sass_Context&); virtual Block* parse() = 0; virtual Block* compile(); virtual char* render(Block* root); virtual char* render_srcmap(); void register_resource(const Include&, const Resource&, ParserState* = 0); std::vector find_includes(const Importer& import); Include load_import(const Importer&, ParserState pstate); Sass_Output_Style output_style() { return c_options.output_style; }; std::vector get_included_files(bool skip = false, size_t headers = 0); private: void collect_plugin_paths(const char* paths_str); void collect_plugin_paths(const char** paths_array); void collect_include_paths(const char* paths_str); void collect_include_paths(const char** paths_array); std::string format_embedded_source_map(); std::string format_source_mapping_url(const std::string& out_path); // void register_built_in_functions(Env* env); // void register_function(Signature sig, Native_Function f, Env* env); // void register_function(Signature sig, Native_Function f, size_t arity, Env* env); // void register_overload_stub(std::string name, Env* env); public: const std::string& cwd() { return CWD; }; }; class File_Context : public Context { public: File_Context(struct Sass_File_Context& ctx) : Context(ctx) { } virtual ~File_Context(); virtual Block* parse(); }; class Data_Context : public Context { public: char* source_c_str; char* srcmap_c_str; Data_Context(struct Sass_Data_Context& ctx) : Context(ctx) { source_c_str = ctx.source_string; srcmap_c_str = ctx.srcmap_string; ctx.source_string = 0; // passed away ctx.srcmap_string = 0; // passed away } virtual ~Data_Context(); virtual Block* parse(); }; } #endif libsass-3.3.4/src/cssize.cpp000066400000000000000000000427521267254216700160020ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include "cssize.hpp" #include "context.hpp" #include "backtrace.hpp" namespace Sass { Cssize::Cssize(Context& ctx, Backtrace* bt) : ctx(ctx), block_stack(std::vector()), p_stack(std::vector()), backtrace(bt) { } Statement* Cssize::parent() { return p_stack.size() ? p_stack.back() : block_stack.front(); } Statement* Cssize::operator()(Block* b) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root()); // bb->tabs(b->tabs()); block_stack.push_back(bb); append_block(b); block_stack.pop_back(); return bb; } Statement* Cssize::operator()(At_Rule* r) { if (!r->block() || !r->block()->length()) return r; if (parent()->statement_type() == Statement::RULESET) { return (r->is_keyframes()) ? SASS_MEMORY_NEW(ctx.mem, Bubble, r->pstate(), r) : bubble(r); } p_stack.push_back(r); At_Rule* rr = SASS_MEMORY_NEW(ctx.mem, At_Rule, r->pstate(), r->keyword(), r->selector(), r->block() ? r->block()->perform(this)->block() : 0); if (r->value()) rr->value(r->value()); p_stack.pop_back(); bool directive_exists = false; size_t L = rr->block() ? rr->block()->length() : 0; for (size_t i = 0; i < L && !directive_exists; ++i) { Statement* s = (*r->block())[i]; if (s->statement_type() != Statement::BUBBLE) directive_exists = true; else { s = static_cast(s)->node(); if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false; else directive_exists = (static_cast(s)->keyword() == rr->keyword()); } } Block* result = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()); if (!(directive_exists || rr->is_keyframes())) { At_Rule* empty_node = static_cast(rr); empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate())); *result << empty_node; } Statement* ss = debubble(rr->block() ? rr->block() : SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate()), rr); for (size_t i = 0, L = ss->block()->length(); i < L; ++i) { *result << (*ss->block())[i]; } return result; } Statement* Cssize::operator()(Keyframe_Rule* r) { if (!r->block() || !r->block()->length()) return r; Keyframe_Rule* rr = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, r->pstate(), r->block()->perform(this)->block()); if (r->selector()) rr->selector(r->selector()); return debubble(rr->block(), rr)->block(); } Statement* Cssize::operator()(Ruleset* r) { p_stack.push_back(r); Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), r->selector(), r->block()->perform(this)->block()); // rr->tabs(r->block()->tabs()); p_stack.pop_back(); if (!rr->block()) { error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate()); } Block* props = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate()); Block* rules = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate()); for (size_t i = 0, L = rr->block()->length(); i < L; i++) { Statement* s = (*rr->block())[i]; if (bubblable(s)) *rules << s; if (!bubblable(s)) *props << s; } if (props->length()) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate()); *bb += props; rr->block(bb); for (size_t i = 0, L = rules->length(); i < L; i++) { (*rules)[i]->tabs((*rules)[i]->tabs() + 1); } rules->unshift(rr); } rules = debubble(rules)->block(); if (!(!rules->length() || !bubblable(rules->last()) || parent()->statement_type() == Statement::RULESET)) { rules->last()->group_end(true); } return rules; } Statement* Cssize::operator()(Null* m) { return 0; } Statement* Cssize::operator()(Media_Block* m) { if (parent()->statement_type() == Statement::RULESET) { return bubble(m); } if (parent()->statement_type() == Statement::MEDIA) { return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m); } p_stack.push_back(m); Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block, m->pstate(), m->media_queries(), m->block()->perform(this)->block()); mm->tabs(m->tabs()); p_stack.pop_back(); return debubble(mm->block(), mm)->block(); } Statement* Cssize::operator()(Supports_Block* m) { if (!m->block()->length()) { return m; } if (parent()->statement_type() == Statement::RULESET) { return bubble(m); } p_stack.push_back(m); Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block, m->pstate(), m->condition(), m->block()->perform(this)->block()); mm->tabs(m->tabs()); p_stack.pop_back(); return debubble(mm->block(), mm)->block(); } Statement* Cssize::operator()(At_Root_Block* m) { bool tmp = false; for (size_t i = 0, L = p_stack.size(); i < L; ++i) { Statement* s = p_stack[i]; tmp |= m->exclude_node(s); } if (!tmp) { Block* bb = m->block()->perform(this)->block(); for (size_t i = 0, L = bb->length(); i < L; ++i) { // (bb->elements())[i]->tabs(m->tabs()); if (bubblable((*bb)[i])) (*bb)[i]->tabs((*bb)[i]->tabs() + m->tabs()); } if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end()); return bb; } if (m->exclude_node(parent())) { return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m); } return bubble(m); } Statement* Cssize::bubble(At_Rule* m) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate()); Has_Block* new_rule = static_cast(shallow_copy(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); size_t L = m->block() ? m->block()->length() : 0; for (size_t i = 0; i < L; ++i) { *new_rule->block() << (*m->block())[i]; } Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate()); *wrapper_block << new_rule; At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule, m->pstate(), m->keyword(), m->selector(), wrapper_block); if (m->value()) mm->value(m->value()); Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(At_Root_Block* m) { Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, this->parent()->pstate()); Has_Block* new_rule = static_cast(shallow_copy(this->parent())); new_rule->block(bb); new_rule->tabs(this->parent()->tabs()); for (size_t i = 0, L = m->block()->length(); i < L; ++i) { *new_rule->block() << (*m->block())[i]; } Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate()); *wrapper_block << new_rule; At_Root_Block* mm = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, m->pstate(), wrapper_block, m->expression()); Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(Supports_Block* m) { Ruleset* parent = static_cast(shallow_copy(this->parent())); Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate()); Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); for (size_t i = 0, L = m->block()->length(); i < L; ++i) { *new_rule->block() << (*m->block())[i]; } Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate()); *wrapper_block << new_rule; Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block, m->pstate(), m->condition(), wrapper_block); mm->tabs(m->tabs()); Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm); return bubble; } Statement* Cssize::bubble(Media_Block* m) { Ruleset* parent = static_cast(shallow_copy(this->parent())); Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate()); Ruleset* new_rule = SASS_MEMORY_NEW(ctx.mem, Ruleset, parent->pstate(), parent->selector(), bb); new_rule->tabs(parent->tabs()); for (size_t i = 0, L = m->block()->length(); i < L; ++i) { *new_rule->block() << (*m->block())[i]; } Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate()); *wrapper_block << new_rule; Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block, m->pstate(), m->media_queries(), wrapper_block, 0); mm->tabs(m->tabs()); Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm); return bubble; } bool Cssize::bubblable(Statement* s) { return s->statement_type() == Statement::RULESET || s->bubbles(); } Statement* Cssize::flatten(Statement* s) { Block* bb = s->block(); Block* result = SASS_MEMORY_NEW(ctx.mem, Block, bb->pstate(), 0, bb->is_root()); for (size_t i = 0, L = bb->length(); i < L; ++i) { Statement* ss = (*bb)[i]; if (ss->block()) { ss = flatten(ss); for (size_t j = 0, K = ss->block()->length(); j < K; ++j) { *result << (*ss->block())[j]; } } else { *result << ss; } } return result; } std::vector> Cssize::slice_by_bubble(Statement* b) { std::vector> results; for (size_t i = 0, L = b->block()->length(); i < L; ++i) { Statement* value = (*b->block())[i]; bool key = value->statement_type() == Statement::BUBBLE; if (!results.empty() && results.back().first == key) { Block* wrapper_block = results.back().second; *wrapper_block << value; } else { Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, value->pstate()); *wrapper_block << value; results.push_back(std::make_pair(key, wrapper_block)); } } return results; } Statement* Cssize::shallow_copy(Statement* s) { switch (s->statement_type()) { case Statement::RULESET: return SASS_MEMORY_NEW(ctx.mem, Ruleset, *static_cast(s)); case Statement::MEDIA: return SASS_MEMORY_NEW(ctx.mem, Media_Block, *static_cast(s)); case Statement::BUBBLE: return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast(s)); case Statement::DIRECTIVE: return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast(s)); case Statement::SUPPORTS: return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast(s)); case Statement::ATROOT: return SASS_MEMORY_NEW(ctx.mem, At_Root_Block, *static_cast(s)); case Statement::KEYFRAMERULE: return SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, *static_cast(s)); case Statement::NONE: default: error("unknown internal error; please contact the LibSass maintainers", s->pstate(), backtrace); String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), std::string("`CSSize` can't clone ") + typeid(*s).name()); return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg); } } Statement* Cssize::debubble(Block* children, Statement* parent) { Has_Block* previous_parent = 0; std::vector> baz = slice_by_bubble(children); Block* result = SASS_MEMORY_NEW(ctx.mem, Block, children->pstate()); for (size_t i = 0, L = baz.size(); i < L; ++i) { bool is_bubble = baz[i].first; Block* slice = baz[i].second; if (!is_bubble) { if (!parent) { *result << slice; } else if (previous_parent) { *previous_parent->block() += slice; } else { previous_parent = static_cast(shallow_copy(parent)); previous_parent->tabs(parent->tabs()); Has_Block* new_parent = static_cast(shallow_copy(parent)); new_parent->block(slice); new_parent->tabs(parent->tabs()); *result << new_parent; } continue; } Block* wrapper_block = SASS_MEMORY_NEW(ctx.mem, Block, children->block()->pstate(), children->block()->length(), children->block()->is_root()); for (size_t j = 0, K = slice->length(); j < K; ++j) { Statement* ss = 0; Bubble* b = static_cast((*slice)[j]); if (!parent || parent->statement_type() != Statement::MEDIA || b->node()->statement_type() != Statement::MEDIA || static_cast(b->node())->media_queries() == static_cast(parent)->media_queries()) { ss = b->node(); } else { List* mq = merge_media_queries(static_cast(b->node()), static_cast(parent)); if (!mq->length()) continue; static_cast(b->node())->media_queries(mq); ss = b->node(); } if (!ss) continue; ss->tabs(ss->tabs() + b->tabs()); ss->group_end(b->group_end()); if (!ss) continue; Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, children->block()->pstate(), children->block()->length(), children->block()->is_root()); *bb << ss->perform(this); Statement* wrapper = flatten(bb); *wrapper_block << wrapper; if (wrapper->block()->length()) { previous_parent = 0; } } if (wrapper_block) { *result << flatten(wrapper_block); } } return flatten(result); } Statement* Cssize::fallback_impl(AST_Node* n) { return static_cast(n); } void Cssize::append_block(Block* b) { Block* current_block = block_stack.back(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* ith = (*b)[i]->perform(this); if (ith && ith->block()) { for (size_t j = 0, K = ith->block()->length(); j < K; ++j) { *current_block << (*ith->block())[j]; } } else if (ith) { *current_block << ith; } } } List* Cssize::merge_media_queries(Media_Block* m1, Media_Block* m2) { List* qq = SASS_MEMORY_NEW(ctx.mem, List, m1->media_queries()->pstate(), m1->media_queries()->length(), SASS_COMMA); for (size_t i = 0, L = m1->media_queries()->length(); i < L; i++) { for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) { Media_Query* mq1 = static_cast((*m1->media_queries())[i]); Media_Query* mq2 = static_cast((*m2->media_queries())[j]); Media_Query* mq = merge_media_query(mq1, mq2); if (mq) *qq << mq; } } return qq; } Media_Query* Cssize::merge_media_query(Media_Query* mq1, Media_Query* 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" : mq1->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* mm = SASS_MEMORY_NEW(ctx.mem, Media_Query, mq1->pstate(), 0, mq1->length() + mq2->length(), mod == "not", mod == "only" ); if (!type.empty()) { mm->media_type(SASS_MEMORY_NEW(ctx.mem, String_Quoted, mq1->pstate(), type)); } *mm += mq2; *mm += mq1; return mm; } } libsass-3.3.4/src/cssize.hpp000066400000000000000000000043131267254216700157760ustar00rootroot00000000000000#ifndef SASS_CSSIZE_H #define SASS_CSSIZE_H #include "ast.hpp" #include "context.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { typedef Environment Env; struct Backtrace; class Cssize : public Operation_CRTP { Context& ctx; std::vector block_stack; std::vector p_stack; Backtrace* backtrace; Statement* fallback_impl(AST_Node* n); public: Cssize(Context&, Backtrace*); ~Cssize() { } Statement* operator()(Block*); Statement* operator()(Ruleset*); // Statement* operator()(Propset*); // Statement* operator()(Bubble*); Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); Statement* operator()(At_Rule*); Statement* operator()(Keyframe_Rule*); // Statement* operator()(Declaration*); // Statement* operator()(Assignment*); // Statement* operator()(Import*); // Statement* operator()(Import_Stub*); // Statement* operator()(Warning*); // Statement* operator()(Error*); // Statement* operator()(Comment*); // Statement* operator()(If*); // Statement* operator()(For*); // Statement* operator()(Each*); // Statement* operator()(While*); // Statement* operator()(Return*); // Statement* operator()(Extension*); // Statement* operator()(Definition*); // Statement* operator()(Mixin_Call*); // Statement* operator()(Content*); Statement* operator()(Null*); Statement* parent(); std::vector> slice_by_bubble(Statement*); Statement* bubble(At_Rule*); Statement* bubble(At_Root_Block*); Statement* bubble(Media_Block*); Statement* bubble(Supports_Block*); Statement* shallow_copy(Statement*); Statement* debubble(Block* children, Statement* parent = 0); Statement* flatten(Statement*); bool bubblable(Statement*); List* merge_media_queries(Media_Block*, Media_Block*); Media_Query* merge_media_query(Media_Query*, Media_Query*); template Statement* fallback(U x) { return fallback_impl(x); } void append_block(Block*); }; } #endif libsass-3.3.4/src/debug.hpp000066400000000000000000000014561267254216700155710ustar00rootroot00000000000000#ifndef SASS_DEBUG_H #define SASS_DEBUG_H #include #ifndef UINT32_MAX #define UINT32_MAX 0xffffffffU #endif enum dbg_lvl_t : uint32_t { NONE = 0, TRIM = 1, CHUNKS = 2, SUBWEAVE = 4, WEAVE = 8, EXTEND_COMPOUND = 16, EXTEND_COMPLEX = 32, LCS = 64, EXTEND_OBJECT = 128, ALL = UINT32_MAX }; #ifdef DEBUG #ifndef DEBUG_LVL const uint32_t debug_lvl = UINT32_MAX; #else const uint32_t debug_lvl = (DEBUG_LVL); #endif // DEBUG_LVL #define DEBUG_PRINT(lvl, x) if((lvl) & debug_lvl) { std::cerr << x; } #define DEBUG_PRINTLN(lvl, x) if((lvl) & debug_lvl) { std::cerr << x << std::endl; } #define DEBUG_EXEC(lvl, x) if((lvl) & debug_lvl) { x; } #else // DEBUG #define DEBUG_PRINT(lvl, x) #define DEBUG_PRINTLN(lvl, x) #define DEBUG_EXEC(lvl, x) #endif // DEBUG #endif // SASS_DEBUG libsass-3.3.4/src/debugger.hpp000066400000000000000000001147161267254216700162730ustar00rootroot00000000000000#ifndef SASS_DEBUGGER_H #define SASS_DEBUGGER_H #include #include #include "node.hpp" #include "ast_fwd_decl.hpp" using namespace Sass; inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0); inline void debug_sources_set(SourcesSet& set, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : set) { debug_ast(pair, ind + ""); // debug_ast(set[pair], ind + "first: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; } inline std::string str_replace(std::string str, const std::string& oldStr, const std::string& newStr) { size_t pos = 0; while((pos = str.find(oldStr, pos)) != std::string::npos) { str.replace(pos, oldStr.length(), newStr); pos += newStr.length(); } return str; } inline std::string prettyprint(const std::string& str) { std::string clean = str_replace(str, "\n", "\\n"); clean = str_replace(clean, " ", "\\t"); clean = str_replace(clean, "\r", "\\r"); return clean; } inline std::string longToHex(long long t) { std::stringstream is; is << std::hex << t; return is.str(); } 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) << "@[" << start.line << ":" << start.column << "]" << "-[" << end.line << ":" << end.column << "]"; return str.str(); } inline void debug_ast(AST_Node* node, std::string ind, Env* env) { if (node == 0) return; if (ind == "") std::cerr << "####################################################################\n"; if (dynamic_cast(node)) { Bubble* bubble = dynamic_cast(node); std::cerr << ind << "Bubble " << bubble; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << bubble->tabs(); std::cerr << std::endl; } else if (dynamic_cast(node)) { At_Root_Block* root_block = dynamic_cast(node); std::cerr << ind << "At_Root_Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << root_block->tabs(); std::cerr << std::endl; debug_ast(root_block->expression(), ind + ":", env); debug_ast(root_block->block(), ind + " ", env); } else if (dynamic_cast(node)) { Selector_List* selector = dynamic_cast(node); std::cerr << ind << "Selector_List " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " [@media:" << selector->media_block() << "]"; std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } // } else if (dynamic_cast(node)) { // Expression* expression = dynamic_cast(node); // std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl; } else if (dynamic_cast(node)) { Parent_Selector* selector = dynamic_cast(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 << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; // debug_ast(selector->selector(), ind + "->", env); } else if (dynamic_cast(node)) { Complex_Selector* selector = dynamic_cast(node); std::cerr << ind << "Complex_Selector " << selector << " (" << pstate_source_position(node) << ")" << " <" << selector->hash() << ">" << " [weight:" << longToHex(selector->specificity()) << "]" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_parent_ref() ? " [has parent]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << (selector->has_line_break() ? " [line-break]": " -") << " -- "; 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; } // 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; } SourcesSet set = selector->sources(); // debug_sources_set(set, ind + " @--> "); } else if (dynamic_cast(node)) { Compound_Selector* selector = dynamic_cast(node); std::cerr << ind << "Compound_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]"; std::cerr << " [@media:" << selector->media_block() << "]"; std::cerr << (selector->extended() ? " [extended]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Propset* selector = dynamic_cast(node); std::cerr << ind << "Propset " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << selector->tabs() << std::endl; if (selector->block()) for(auto i : selector->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Wrapped_Selector* selector = dynamic_cast(node); std::cerr << ind << "Wrapped_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->selector(), ind + " () ", env); } else if (dynamic_cast(node)) { Pseudo_Selector* selector = dynamic_cast(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 << std::endl; debug_ast(selector->expression(), ind + " <= ", env); } else if (dynamic_cast(node)) { Attribute_Selector* selector = dynamic_cast(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 (dynamic_cast(node)) { Selector_Qualifier* selector = dynamic_cast(node); std::cerr << ind << "Selector_Qualifier " << 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 (dynamic_cast(node)) { Type_Selector* selector = dynamic_cast(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 (dynamic_cast(node)) { Selector_Placeholder* selector = dynamic_cast(node); std::cerr << ind << "Selector_Placeholder [" << selector->ns_name() << "] " << selector << " <" << selector->hash() << ">" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; } else if (dynamic_cast(node)) { Simple_Selector* selector = dynamic_cast(node); std::cerr << ind << "Simple_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; } else if (dynamic_cast(node)) { Selector_Schema* selector = dynamic_cast(node); std::cerr << ind << "Selector_Schema " << selector; std::cerr << " (" << pstate_source_position(node) << ")" << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "") << " [@media:" << selector->media_block() << "]" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; debug_ast(selector->contents(), ind + " "); // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Selector* selector = dynamic_cast(node); std::cerr << ind << "Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; } else if (dynamic_cast(node)) { Media_Query_Expression* block = dynamic_cast(node); std::cerr << ind << "Media_Query_Expression " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -") << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (dynamic_cast(node)) { Media_Query* block = dynamic_cast(node); std::cerr << ind << "Media_Query " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << (block->is_negated() ? " [is_negated]": " -") << (block->is_restricted() ? " [is_restricted]": " -") << std::endl; debug_ast(block->media_type(), ind + " "); for(auto i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Media_Block* block = dynamic_cast(node); std::cerr << ind << "Media_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->media_queries(), ind + " =@ "); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Supports_Block* block = dynamic_cast(node); std::cerr << ind << "Supports_Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->condition(), ind + " =@ "); } else if (dynamic_cast(node)) { Supports_Operator* block = dynamic_cast(node); std::cerr << ind << "Supports_Operator " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->left(), ind + " left) "); debug_ast(block->right(), ind + " right) "); } else if (dynamic_cast(node)) { Supports_Negation* block = dynamic_cast(node); std::cerr << ind << "Supports_Negation " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->condition(), ind + " condition) "); } else if (dynamic_cast(node)) { Supports_Declaration* block = dynamic_cast(node); std::cerr << ind << "Supports_Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")" << std::endl; debug_ast(block->feature(), ind + " feature) "); debug_ast(block->value(), ind + " value) "); } else if (dynamic_cast(node)) { Block* root_block = dynamic_cast(node); std::cerr << ind << "Block " << root_block; std::cerr << " (" << pstate_source_position(node) << ")"; if (root_block->is_root()) std::cerr << " [root]"; std::cerr << " " << root_block->tabs() << std::endl; if (root_block->block()) for(auto i : root_block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Warning* block = dynamic_cast(node); std::cerr << ind << "Warning " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Error* block = dynamic_cast(node); std::cerr << ind << "Error " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Debug* block = dynamic_cast(node); std::cerr << ind << "Debug " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->value(), ind + " "); } else if (dynamic_cast(node)) { Comment* block = dynamic_cast(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl; debug_ast(block->text(), ind + "// ", env); } else if (dynamic_cast(node)) { If* block = dynamic_cast(node); std::cerr << ind << "If " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->predicate(), ind + " = "); debug_ast(block->block(), ind + " <>"); debug_ast(block->alternative(), ind + " ><"); } else if (dynamic_cast(node)) { Return* block = dynamic_cast(node); std::cerr << ind << "Return " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Extension* block = dynamic_cast(node); std::cerr << ind << "Extension " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "-> ", env); } else if (dynamic_cast(node)) { Content* block = dynamic_cast(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Import_Stub* block = dynamic_cast(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Import* block = dynamic_cast(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; // std::vector files_; for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env); debug_ast(block->media_queries(), ind + "@@ "); } else if (dynamic_cast(node)) { Assignment* block = dynamic_cast(node); std::cerr << ind << "Assignment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl; debug_ast(block->value(), ind + "=", env); } else if (dynamic_cast(node)) { Declaration* block = dynamic_cast(node); std::cerr << ind << "Declaration " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->property(), ind + " prop: ", env); debug_ast(block->value(), ind + " value: ", env); } else if (dynamic_cast(node)) { Keyframe_Rule* has_block = dynamic_cast(node); std::cerr << ind << "Keyframe_Rule " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl; if (has_block->selector()) debug_ast(has_block->selector(), ind + "@"); if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { At_Rule* block = dynamic_cast(node); std::cerr << ind << "At_Rule " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl; debug_ast(block->selector(), ind + "~", env); debug_ast(block->value(), ind + "+", env); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Each* block = dynamic_cast(node); std::cerr << ind << "Each " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { For* block = dynamic_cast(node); std::cerr << ind << "For " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { While* block = dynamic_cast(node); std::cerr << ind << "While " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Definition* block = dynamic_cast(node); std::cerr << ind << "Definition " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << block->name() << "] "; std::cerr << " [type: " << (block->type() == Sass::Definition::Type::MIXIN ? "Mixin " : "Function ") << "] "; // this seems to lead to segfaults some times? // std::cerr << " [signature: " << block->signature() << "] "; std::cerr << " [native: " << block->native_function() << "] "; std::cerr << " " << block->tabs() << std::endl; debug_ast(block->parameters(), ind + " params: ", env); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Mixin_Call* block = dynamic_cast(node); std::cerr << ind << "Mixin_Call " << block << " " << block->tabs(); std::cerr << " [" << block->name() << "]"; std::cerr << " [has_content: " << block->has_content() << "] " << std::endl; debug_ast(block->arguments(), ind + " args: "); if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (Ruleset* ruleset = dynamic_cast(node)) { std::cerr << ind << "Ruleset " << ruleset; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [indent: " << ruleset->tabs() << "]"; std::cerr << (ruleset->at_root() ? " [@ROOT]" : ""); std::cerr << (ruleset->is_root() ? " [root]" : ""); std::cerr << std::endl; debug_ast(ruleset->selector(), ind + ">"); debug_ast(ruleset->block(), ind + " "); } else if (dynamic_cast(node)) { Block* block = dynamic_cast(node); std::cerr << ind << "Block " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [indent: " << block->tabs() << "]" << std::endl; for(auto i : block->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Textual* expression = dynamic_cast(node); std::cerr << ind << "Textual "; if (expression->type() == Textual::NUMBER) std::cerr << " [NUMBER]"; else if (expression->type() == Textual::PERCENTAGE) std::cerr << " [PERCENTAGE]"; else if (expression->type() == Textual::DIMENSION) std::cerr << " [DIMENSION]"; else if (expression->type() == Textual::HEX) std::cerr << " [HEX]"; std::cerr << expression << " [" << expression->value() << "]"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << std::endl; } else if (dynamic_cast(node)) { Variable* expression = dynamic_cast(node); std::cerr << ind << "Variable " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name()); if (env && env->has(name)) debug_ast(static_cast((*env)[name]), ind + " -> ", env); } else if (dynamic_cast(node)) { Function_Call_Schema* expression = dynamic_cast(node); std::cerr << ind << "Function_Call_Schema " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << "" << std::endl; debug_ast(expression->name(), ind + "name: ", env); debug_ast(expression->arguments(), ind + " args: ", env); } else if (dynamic_cast(node)) { Function_Call* expression = dynamic_cast(node); std::cerr << ind << "Function_Call " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << std::endl; debug_ast(expression->arguments(), ind + " args: ", env); } else if (dynamic_cast(node)) { Arguments* expression = dynamic_cast(node); std::cerr << ind << "Arguments " << expression; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->has_named_arguments()) std::cerr << " [has_named_arguments]"; if (expression->has_rest_argument()) std::cerr << " [has_rest_argument]"; if (expression->has_keyword_argument()) std::cerr << " [has_keyword_argument]"; std::cerr << std::endl; for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Argument* expression = dynamic_cast(node); std::cerr << ind << "Argument " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->value() << "]"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [rest: " << expression->is_rest_argument() << "] "; std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl; debug_ast(expression->value(), ind + " value: ", env); } else if (dynamic_cast(node)) { Parameters* expression = dynamic_cast(node); std::cerr << ind << "Parameters " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] "; std::cerr << " [has_rest: " << expression->has_rest_parameter() << "] "; std::cerr << std::endl; for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Parameter* expression = dynamic_cast(node); std::cerr << ind << "Parameter " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [name: " << expression->name() << "] "; std::cerr << " [default: " << expression->default_value() << "] "; std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl; } else if (dynamic_cast(node)) { Unary_Expression* expression = dynamic_cast(node); std::cerr << ind << "Unary_Expression " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type() << "]" << std::endl; debug_ast(expression->operand(), ind + " operand: ", env); } else if (dynamic_cast(node)) { Binary_Expression* expression = dynamic_cast(node); std::cerr << ind << "Binary_Expression " << expression; if (expression->is_interpolant()) std::cerr << " [is interpolant] "; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; std::cerr << " [ws_before: " << expression->op().ws_before << "] "; std::cerr << " [ws_after: " << expression->op().ws_after << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type_name() << "]" << std::endl; debug_ast(expression->left(), ind + " left: ", env); debug_ast(expression->right(), ind + " right: ", env); } else if (dynamic_cast(node)) { Map* expression = dynamic_cast(node); std::cerr << ind << "Map " << expression; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Hashed]" << std::endl; for (auto i : expression->elements()) { debug_ast(i.first, ind + " key: "); debug_ast(i.second, ind + " val: "); } } else if (dynamic_cast(node)) { List* expression = dynamic_cast(node); std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " << (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map" : "Space ") << " [delayed: " << expression->is_delayed() << "] " << " [interpolant: " << expression->is_interpolant() << "] " << " [arglist: " << expression->is_arglist() << "] " << " [hash: " << expression->hash() << "] " << std::endl; for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Content* expression = dynamic_cast(node); std::cerr << ind << "Content " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Statement]" << std::endl; } else if (dynamic_cast(node)) { Boolean* expression = dynamic_cast(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl; } else if (dynamic_cast(node)) { Color* expression = dynamic_cast(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl; } else if (dynamic_cast(node)) { Number* expression = dynamic_cast(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << expression->unit() << "]" << " [hash: " << expression->hash() << "] " << std::endl; } else if (dynamic_cast(node)) { String_Quoted* expression = dynamic_cast(node); std::cerr << ind << "String_Quoted " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (dynamic_cast(node)) { String_Constant* expression = dynamic_cast(node); std::cerr << ind << "String_Constant " << expression; if (expression->concrete_type()) { std::cerr << " " << expression->concrete_type(); } std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (dynamic_cast(node)) { String_Schema* expression = dynamic_cast(node); std::cerr << ind << "String_Schema " << expression; std::cerr << " " << expression->concrete_type(); if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [is interpolant]"; if (expression->has_interpolant()) std::cerr << " [has interpolant]"; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { String* expression = dynamic_cast(node); std::cerr << ind << "String " << expression; std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; } else if (dynamic_cast(node)) { Expression* expression = dynamic_cast(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::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break; } std::cerr << std::endl; } else if (dynamic_cast(node)) { Has_Block* has_block = dynamic_cast(node); std::cerr << ind << "Has_Block " << has_block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << has_block->tabs() << std::endl; if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { Statement* statement = dynamic_cast(node); std::cerr << ind << "Statement " << statement; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << statement->tabs() << std::endl; } 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* node, std::string ind = "", Env* env = 0) { debug_ast(const_cast(node), ind, env); } inline void debug_node(const Node* node, std::string ind = "") { debug_node(const_cast(node), ind); } inline void debug_subset_map(Sass::ExtensionSubsetMap& 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"; } typedef std::pair ExtensionPair; typedef std::vector SubsetMapEntries; inline void debug_subset_entries(SubsetMapEntries* entries, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; for(auto const &pair : *entries) { debug_ast(pair.first, ind + "first: "); debug_ast(pair.second, ind + "second: "); } if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; } #endif // SASS_DEBUGGER libsass-3.3.4/src/emitter.cpp000066400000000000000000000150611267254216700161440ustar00rootroot00000000000000#include "sass.hpp" #include "util.hpp" #include "context.hpp" #include "output.hpp" #include "emitter.hpp" #include "utf8_string.hpp" namespace Sass { Emitter::Emitter(struct Sass_Output_Options& opt) : wbuf(), opt(opt), indentation(0), scheduled_space(0), scheduled_linefeed(0), scheduled_delimiter(false), scheduled_mapping(0), in_comment(false), in_wrapped(false), in_media_block(false), in_declaration(false), in_space_array(false), in_comma_array(false) { } // return buffer as string std::string Emitter::get_buffer(void) { return wbuf.buffer; } Sass_Output_Style Emitter::output_style(void) const { return opt.output_style; } // PROXY METHODS FOR SOURCE MAPS void Emitter::add_source_index(size_t idx) { wbuf.smap.source_index.push_back(idx); } std::string Emitter::render_srcmap(Context &ctx) { return wbuf.smap.render_srcmap(ctx); } void Emitter::set_filename(const std::string& str) { wbuf.smap.file = str; } void Emitter::schedule_mapping(const AST_Node* node) { scheduled_mapping = node; } void Emitter::add_open_mapping(const AST_Node* node) { wbuf.smap.add_open_mapping(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); } // MAIN BUFFER MANIPULATION // add outstanding delimiter void Emitter::finalize(bool final) { scheduled_space = 0; if (output_style() == SASS_STYLE_COMPRESSED) if (final) scheduled_delimiter = false; if (scheduled_linefeed) scheduled_linefeed = 1; flush_schedules(); } // flush scheduled space/linefeed void Emitter::flush_schedules(void) { // check the schedule if (scheduled_linefeed) { std::string linefeeds = ""; for (size_t i = 0; i < scheduled_linefeed; i++) linefeeds += opt.linefeed; scheduled_space = 0; scheduled_linefeed = 0; append_string(linefeeds); } else if (scheduled_space) { std::string spaces(scheduled_space, ' '); scheduled_space = 0; append_string(spaces); } if (scheduled_delimiter) { scheduled_delimiter = false; append_string(";"); } } // prepend some text or token to the buffer void Emitter::prepend_output(const OutputBuffer& output) { wbuf.smap.prepend(output); wbuf.buffer = output.buffer + wbuf.buffer; } // prepend some text or token to the buffer void Emitter::prepend_string(const std::string& text) { wbuf.smap.prepend(Offset(text)); wbuf.buffer = text + wbuf.buffer; } // append some text or token to the buffer void Emitter::append_string(const std::string& text) { // write space/lf flush_schedules(); if (in_comment && output_style() == COMPACT) { // unescape comment nodes std::string out = comment_to_string(text); // add to buffer wbuf.buffer += out; // account for data in source-maps wbuf.smap.append(Offset(out)); } else { // add to buffer wbuf.buffer += text; // account for data in source-maps wbuf.smap.append(Offset(text)); } } // append some white-space only text void Emitter::append_wspace(const std::string& text) { if (text.empty()) return; if (peek_linefeed(text.c_str())) { scheduled_space = 0; append_mandatory_linefeed(); } } // 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* node) { flush_schedules(); add_open_mapping(node); // hotfix for browser issues // this is pretty ugly indeed if (scheduled_mapping) { add_open_mapping(scheduled_mapping); scheduled_mapping = 0; } append_string(text); add_close_mapping(node); } // HELPER METHODS void Emitter::append_indentation() { if (output_style() == COMPRESSED) return; if (output_style() == COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; std::string indent = ""; for (size_t i = 0; i < indentation; i++) indent += opt.indent; append_string(indent); } void Emitter::append_delimiter() { scheduled_delimiter = true; if (output_style() == COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); } } else if (output_style() != COMPRESSED) { append_optional_linefeed(); } } void Emitter::append_comma_separator() { // scheduled_space = 0; append_string(","); append_optional_space(); } void Emitter::append_colon_separator() { scheduled_space = 0; append_string(":"); append_optional_space(); } void Emitter::append_mandatory_space() { scheduled_space = 1; } void Emitter::append_optional_space() { if ((output_style() != COMPRESSED) && buffer().size()) { char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { append_mandatory_space(); } } } void Emitter::append_special_linefeed() { if (output_style() == COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++) append_string(opt.indent); } } void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return; if (output_style() == COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed(); } } void Emitter::append_mandatory_linefeed() { if (output_style() != COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); } } void Emitter::append_scope_opener(AST_Node* node) { scheduled_linefeed = 0; append_optional_space(); flush_schedules(); if (node) add_open_mapping(node); append_string("{"); append_optional_linefeed(); // append_optional_space(); ++ indentation; } void Emitter::append_scope_closer(AST_Node* node) { -- indentation; scheduled_linefeed = 0; if (output_style() == COMPRESSED) scheduled_delimiter = false; if (output_style() == EXPANDED) { append_optional_linefeed(); append_indentation(); } else { append_optional_space(); } append_string("}"); if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return; if (output_style() != COMPRESSED) scheduled_linefeed = 2; } } libsass-3.3.4/src/emitter.hpp000066400000000000000000000055041267254216700161520ustar00rootroot00000000000000#ifndef SASS_EMITTER_H #define SASS_EMITTER_H #include #include "sass.hpp" #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp" namespace Sass { class Context; class Emitter { public: Emitter(struct Sass_Output_Options& opt); virtual ~Emitter() { } protected: OutputBuffer wbuf; public: const std::string& buffer(void) { return wbuf.buffer; } const SourceMap smap(void) { return wbuf.smap; } const OutputBuffer output(void) { return wbuf; } // 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* 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); public: struct Sass_Output_Options& opt; size_t indentation; size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter; const AST_Node* scheduled_mapping; public: // output strings different in comments bool in_comment; // selector list does not get linefeeds bool in_wrapped; // lists always get a space after delimiter bool in_media_block; // nested list must not have parentheses bool in_declaration; // nested lists need parentheses bool in_space_array; bool in_comma_array; public: // return buffer as std::string std::string get_buffer(void); // flush scheduled space/linefeed Sass_Output_Style output_style(void) const; // add outstanding linefeed void finalize(bool final = true); // flush scheduled space/linefeed void flush_schedules(void); // prepend some text or token to the buffer void prepend_string(const std::string& text); void prepend_output(const OutputBuffer& out); // append some text or token to the buffer void append_string(const std::string& text); // append some white-space only text void append_wspace(const std::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end void append_token(const std::string& text, const AST_Node* node); public: // syntax sugar void append_indentation(); void append_optional_space(void); void append_mandatory_space(void); void append_special_linefeed(void); void append_optional_linefeed(void); void append_mandatory_linefeed(void); void append_scope_opener(AST_Node* node = 0); void append_scope_closer(AST_Node* node = 0); void append_comma_separator(void); void append_colon_separator(void); void append_delimiter(void); }; } #endif libsass-3.3.4/src/environment.cpp000066400000000000000000000121571267254216700170420ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "environment.hpp" namespace Sass { template Environment::Environment(bool is_shadow) : local_frame_(std::map()), parent_(0), is_shadow_(false) { } template Environment::Environment(Environment* env, bool is_shadow) : local_frame_(std::map()), parent_(env), is_shadow_(is_shadow) { } template Environment::Environment(Environment& env, bool is_shadow) : local_frame_(std::map()), parent_(&env), is_shadow_(is_shadow) { } // link parent to create a stack template void Environment::link(Environment& env) { parent_ = &env; } template void Environment::link(Environment* env) { parent_ = env; } // this is used to find the global frame // which is the second last on the stack template bool Environment::is_lexical() const { return !! parent_ && parent_->parent_; } // only match the real root scope // there is still a parent around // not sure what it is actually use for // I guess we store functions etc. there template bool Environment::is_global() const { return parent_ && ! parent_->parent_; } template std::map& Environment::local_frame() { return local_frame_; } template bool Environment::has_local(const std::string& key) const { return local_frame_.find(key) != local_frame_.end(); } template T& Environment::get_local(const std::string& key) { return local_frame_[key]; } template void Environment::set_local(const std::string& key, T val) { local_frame_[key] = val; } template void Environment::del_local(const std::string& key) { local_frame_.erase(key); } template Environment* Environment::global_env() { Environment* cur = this; while (cur->is_lexical()) { cur = cur->parent_; } return cur; } template bool Environment::has_global(const std::string& key) { return global_env()->has(key); } template T& Environment::get_global(const std::string& key) { return (*global_env())[key]; } template void Environment::set_global(const std::string& key, T val) { global_env()->local_frame_[key] = val; } template void Environment::del_global(const std::string& key) { global_env()->local_frame_.erase(key); } template Environment* Environment::lexical_env(const std::string& key) { Environment* cur = this; while (cur) { if (cur->has_local(key)) { return cur; } cur = cur->parent_; } return this; } // see if we have a lexical variable // move down the stack but stop before we // reach the global frame (is not included) template bool Environment::has_lexical(const std::string& key) const { auto cur = this; while (cur->is_lexical()) { if (cur->has_local(key)) return true; cur = cur->parent_; } return false; } // see if we have a lexical we could update // either update already existing lexical value // or if flag is set, we create one if no lexical found template void Environment::set_lexical(const std::string& key, T val) { auto cur = this; bool shadow = false; while (cur->is_lexical() || shadow) { if (cur->has_local(key)) { cur->set_local(key, val); return; } shadow = cur->is_shadow(); cur = cur->parent_; } set_local(key, val); } // look on the full stack for key // include all scopes available template bool Environment::has(const std::string& key) const { auto cur = this; while (cur) { if (cur->has_local(key)) { return true; } cur = cur->parent_; } return false; } // use array access for getter and setter functions template T& Environment::operator[](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); } #ifdef DEBUG template size_t Environment::print(std::string prefix) { size_t indent = 0; if (parent_) indent = parent_->print(prefix) + 1; std::cerr << prefix << std::string(indent, ' ') << "== " << this << std::endl; for (typename std::map::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) { if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) { std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second; if (Value* val = dynamic_cast(i->second)) { std::cerr << " : " << val->to_string(true, 5); } std::cerr << std::endl; } } return indent ; } #endif // compile implementation for AST_Node template class Environment; } libsass-3.3.4/src/environment.hpp000066400000000000000000000047451267254216700170530ustar00rootroot00000000000000#ifndef SASS_ENVIRONMENT_H #define SASS_ENVIRONMENT_H #include #include #include "ast_fwd_decl.hpp" #include "ast_def_macros.hpp" #include "memory_manager.hpp" namespace Sass { template class Environment { // TODO: test with map std::map local_frame_; ADD_PROPERTY(Environment*, parent) ADD_PROPERTY(bool, is_shadow) public: Memory_Manager mem; Environment(bool is_shadow = false); Environment(Environment* env, bool is_shadow = false); Environment(Environment& env, bool is_shadow = false); // link parent to create a stack void link(Environment& env); void link(Environment* env); // this is used to find the global frame // which is the second last on the stack bool is_lexical() const; // only match the real root scope // there is still a parent around // not sure what it is actually use for // I guess we store functions etc. there bool is_global() const; // scope operates on the current frame std::map& local_frame(); bool has_local(const std::string& key) const; T& get_local(const std::string& key); // set variable on the current frame void set_local(const std::string& key, T val); void del_local(const std::string& key); // global operates on the global frame // which is the second last on the stack Environment* global_env(); // get the env where the variable already exists // if it does not yet exist, we return current env Environment* lexical_env(const std::string& key); bool has_global(const std::string& key); T& get_global(const std::string& key); // set a variable on the global frame void set_global(const std::string& key, T val); void del_global(const std::string& key); // see if we have a lexical variable // move down the stack but stop before we // reach the global frame (is not included) bool has_lexical(const std::string& key) const; // see if we have a lexical we could update // either update already existing lexical value // or we create a new one on the current frame void set_lexical(const std::string& key, T val); // look on the full stack for key // include all scopes available bool has(const std::string& key) const; // use array access for getter and setter functions T& operator[](const std::string& key); #ifdef DEBUG size_t print(std::string prefix = ""); #endif }; } #endif libsass-3.3.4/src/error_handling.cpp000066400000000000000000000137311267254216700174720ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp" #include "error_handling.hpp" #include namespace Sass { namespace Exception { Base::Base(ParserState pstate, std::string msg, std::vector* import_stack) : std::runtime_error(msg), msg(msg), prefix("Error"), pstate(pstate), import_stack(import_stack) { } InvalidSass::InvalidSass(ParserState pstate, std::string msg) : Base(pstate, msg) { } InvalidParent::InvalidParent(Selector* parent, Selector* selector) : Base(selector->pstate()), 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 += "\""; } InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value) : Base(pstate), fn(fn), arg(arg), type(type), value(value) { msg = arg + ": \""; msg += value->to_string(Sass_Inspect_Options()); msg += "\" is not a " + type; msg += " for `" + fn + "'"; } InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector* import_stack) : Base(pstate, msg, import_stack) { } UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op) : lhs(lhs), rhs(rhs), op(op) { msg = def_op_msg + ": \""; msg += lhs->to_string({ NESTED, 5 }); msg += " " + op + " "; msg += rhs->to_string({ TO_SASS, 5 }); msg += "\"."; } InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op) : UndefinedOperation(lhs, rhs, op) { msg = def_op_null_msg + ": \""; msg += lhs->inspect(); msg += " " + op + " "; msg += rhs->inspect(); msg += "\"."; } ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs) : lhs(lhs), rhs(rhs) { msg = "divided by 0"; } DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org) : Base(org.pstate()), dup(dup), org(org) { msg = "Duplicate key "; dup.get_duplicate_key()->is_delayed(false); msg += dup.get_duplicate_key()->inspect(); msg += " in map ("; msg += org.inspect(); msg += ")."; } TypeMismatch::TypeMismatch(const Expression& var, const std::string type) : Base(var.pstate()), var(var), type(type) { msg = var.to_string(); msg += " is not an "; msg += type; msg += "."; } InvalidValue::InvalidValue(const Expression& val) : Base(val.pstate()), val(val) { msg = val.to_string(); msg += " isn't a valid CSS value."; } IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs) : lhs(lhs), rhs(rhs) { msg = "Incompatible units: '"; msg += rhs.unit(); msg += "' and '"; msg += lhs.unit(); msg += "'."; } AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op) : lhs(lhs), rhs(rhs), op(op) { msg = "Alpha channels must be equal: "; msg += lhs->to_string({ NESTED, 5 }); msg += " " + op + " "; msg += rhs->to_string({ NESTED, 5 }); msg += "."; } SassValueError::SassValueError(ParserState pstate, OperationError& err) : Base(pstate, err.what()) { msg = err.what(); prefix = err.errtype(); } } void warn(std::string msg, ParserState pstate) { std::cerr << "Warning: " << msg<< std::endl; } void warn(std::string msg, ParserState pstate, Backtrace* bt) { Backtrace top(bt, pstate, ""); msg += top.to_string(); warn(msg, pstate); } void deprecated_function(std::string msg, ParserState pstate) { std::string cwd(Sass::File::get_cwd()); std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.path)); std::cerr << "DEPRECATION WARNING: " << msg << std::endl; std::cerr << "will be an error in future versions of Sass." << std::endl; std::cerr << " on line " << pstate.line+1 << " of " << output_path << std::endl; } void deprecated(std::string msg, std::string msg2, ParserState pstate) { std::string cwd(Sass::File::get_cwd()); std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); std::string output_path(Sass::File::path_for_console(rel_path, pstate.path, pstate.path)); std::cerr << "DEPRECATION WARNING on line " << pstate.line + 1; if (output_path.length()) std::cerr << " of " << output_path; std::cerr << ":" << std::endl; std::cerr << msg << " and will be an error in future versions of Sass." << std::endl; if (msg2.length()) std::cerr << msg2 << std::endl; std::cerr << std::endl; } void deprecated_bind(std::string msg, ParserState pstate) { std::string cwd(Sass::File::get_cwd()); std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.path)); std::cerr << "WARNING: " << msg << std::endl; std::cerr << " on line " << pstate.line+1 << " of " << output_path << std::endl; std::cerr << "This will be an error in future versions of Sass." << std::endl; } void error(std::string msg, ParserState pstate) { throw Exception::InvalidSyntax(pstate, msg); } void error(std::string msg, ParserState pstate, Backtrace* bt) { Backtrace top(bt, pstate, ""); msg += "\n" + top.to_string(); error(msg, pstate); } } libsass-3.3.4/src/error_handling.hpp000066400000000000000000000127671267254216700175070ustar00rootroot00000000000000#ifndef SASS_ERROR_HANDLING_H #define SASS_ERROR_HANDLING_H #include #include #include #include "position.hpp" namespace Sass { struct Backtrace; namespace Exception { const std::string def_msg = "Invalid sass detected"; const std::string def_op_msg = "Undefined operation"; const std::string def_op_null_msg = "Invalid null operation"; class Base : public std::runtime_error { protected: std::string msg; std::string prefix; public: ParserState pstate; std::vector* import_stack; public: Base(ParserState pstate, std::string msg = def_msg, std::vector* import_stack = 0); virtual const char* errtype() const { return prefix.c_str(); } virtual const char* what() const throw() { return msg.c_str(); } virtual ~Base() throw() {}; }; class InvalidSass : public Base { public: InvalidSass(ParserState pstate, std::string msg); virtual ~InvalidSass() throw() {}; }; class InvalidParent : public Base { protected: Selector* parent; Selector* selector; public: InvalidParent(Selector* parent, Selector* selector); virtual ~InvalidParent() throw() {}; }; class InvalidArgumentType : public Base { protected: std::string fn; std::string arg; std::string type; const Value* value; public: InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value = 0); virtual ~InvalidArgumentType() throw() {}; }; class InvalidSyntax : public Base { public: InvalidSyntax(ParserState pstate, std::string msg, std::vector* import_stack = 0); virtual ~InvalidSyntax() throw() {}; }; /* common virtual base class (has no pstate) */ class OperationError : public std::runtime_error { protected: std::string msg; public: OperationError(std::string msg = def_op_msg) : std::runtime_error(msg), msg(msg) {}; public: virtual const char* errtype() const { return "Error"; } virtual const char* what() const throw() { return msg.c_str(); } virtual ~OperationError() throw() {}; }; class ZeroDivisionError : public OperationError { protected: const Expression& lhs; const Expression& rhs; public: ZeroDivisionError(const Expression& lhs, const Expression& rhs); virtual const char* errtype() const { return "ZeroDivisionError"; } virtual ~ZeroDivisionError() throw() {}; }; class DuplicateKeyError : public Base { protected: const Map& dup; const Expression& org; public: DuplicateKeyError(const Map& dup, const Expression& org); virtual const char* errtype() const { return "Error"; } virtual ~DuplicateKeyError() throw() {}; }; class TypeMismatch : public Base { protected: const Expression& var; const std::string type; public: TypeMismatch(const Expression& var, const std::string type); virtual const char* errtype() const { return "Error"; } virtual ~TypeMismatch() throw() {}; }; class InvalidValue : public Base { protected: const Expression& val; public: InvalidValue(const Expression& val); virtual const char* errtype() const { return "Error"; } virtual ~InvalidValue() throw() {}; }; class IncompatibleUnits : public OperationError { protected: const Number& lhs; const Number& rhs; public: IncompatibleUnits(const Number& lhs, const Number& rhs); virtual ~IncompatibleUnits() throw() {}; }; class UndefinedOperation : public OperationError { protected: const Expression* lhs; const Expression* rhs; const std::string op; public: UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op); // virtual const char* errtype() const { return "Error"; } virtual ~UndefinedOperation() throw() {}; }; class InvalidNullOperation : public UndefinedOperation { public: InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op); virtual ~InvalidNullOperation() throw() {}; }; class AlphaChannelsNotEqual : public OperationError { protected: const Expression* lhs; const Expression* rhs; const std::string op; public: AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op); // virtual const char* errtype() const { return "Error"; } virtual ~AlphaChannelsNotEqual() throw() {}; }; class SassValueError : public Base { public: SassValueError(ParserState pstate, OperationError& err); virtual ~SassValueError() throw() {}; }; } void warn(std::string msg, ParserState pstate); void warn(std::string msg, ParserState pstate, Backtrace* bt); void deprecated_function(std::string msg, ParserState pstate); void deprecated(std::string msg, std::string msg2, ParserState pstate); void deprecated_bind(std::string msg, ParserState pstate); // void deprecated(std::string msg, ParserState pstate, Backtrace* bt); void error(std::string msg, ParserState pstate); void error(std::string msg, ParserState pstate, Backtrace* bt); } #endif libsass-3.3.4/src/eval.cpp000066400000000000000000001711151267254216700154250ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include #include "file.hpp" #include "eval.hpp" #include "ast.hpp" #include "bind.hpp" #include "util.hpp" #include "inspect.hpp" #include "environment.hpp" #include "position.hpp" #include "sass/values.h" #include "to_value.hpp" #include "to_c.hpp" #include "context.hpp" #include "backtrace.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "parser.hpp" #include "expand.hpp" #include "color_maps.hpp" namespace Sass { inline double add(double x, double y) { return x + y; } inline double sub(double x, double y) { return x - y; } inline double mul(double x, double y) { return x * y; } inline double div(double x, double y) { return x / y; } // x/0 checked by caller inline double mod(double x, double y) { return std::abs(std::fmod(x, y)); } // x/0 checked by caller typedef double (*bop)(double, double); bop ops[Sass_OP::NUM_OPS] = { 0, 0, // and, or 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte add, sub, mul, div, mod }; Eval::Eval(Expand& exp) : exp(exp), ctx(exp.ctx) { } Eval::~Eval() { } Context& Eval::context() { return ctx; } Env* Eval::environment() { return exp.environment(); } Selector_List* Eval::selector() { return exp.selector(); } Backtrace* Eval::backtrace() { return exp.backtrace(); } Expression* Eval::operator()(Block* b) { Expression* val = 0; for (size_t i = 0, L = b->length(); i < L; ++i) { val = (*b)[i]->perform(this); if (val) return val; } return val; } Expression* Eval::operator()(Assignment* a) { Env* env = exp.environment(); std::string var(a->variable()); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) { Expression* e = dynamic_cast(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); } } else { env->set_global(var, a->value()->perform(this)); } } else { env->set_global(var, a->value()->perform(this)); } } else if (a->is_default()) { if (env->has_lexical(var)) { auto cur = env; while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node* node = cur->get_local(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(this)); } } else { throw std::runtime_error("Env not in sync"); } return 0; } cur = cur->parent(); } throw std::runtime_error("Env not in sync"); } else if (env->has_global(var)) { if (AST_Node* node = env->get_global(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(this)); } } } else if (env->is_lexical()) { env->set_local(var, a->value()->perform(this)); } else { env->set_local(var, a->value()->perform(this)); } } else { env->set_lexical(var, a->value()->perform(this)); } return 0; } Expression* Eval::operator()(If* i) { Expression* rv = 0; Env env(exp.environment()); exp.env_stack.push_back(&env); if (*i->predicate()->perform(this)) { rv = i->block()->perform(this); } else { Block* alt = i->alternative(); if (alt) rv = alt->perform(this); } exp.env_stack.pop_back(); return rv; } // For does not create a new env scope // But iteration vars are reset afterwards Expression* Eval::operator()(For* f) { std::string variable(f->variable()); Expression* low = f->lower_bound()->perform(this); if (low->concrete_type() != Expression::NUMBER) { throw Exception::TypeMismatch(*low, "integer"); } Expression* high = f->upper_bound()->perform(this); if (high->concrete_type() != Expression::NUMBER) { throw Exception::TypeMismatch(*high, "integer"); } Number* sass_start = static_cast(low); Number* sass_end = static_cast(high); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { std::stringstream msg; msg << "Incompatible units: '" << sass_start->unit() << "' and '" << sass_end->unit() << "'."; error(msg.str(), low->pstate(), backtrace()); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true); exp.env_stack.push_back(&env); Number* it = SASS_MEMORY_NEW(env.mem, Number, low->pstate(), start, sass_end->unit()); env.set_local(variable, it); Block* body = f->block(); Expression* val = 0; if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) { it->value(i); env.set_local(variable, it); val = body->perform(this); if (val) break; } } else { if (f->is_inclusive()) --end; for (double i = start; i > end; --i) { it->value(i); env.set_local(variable, it); val = body->perform(this); if (val) break; } } exp.env_stack.pop_back(); return val; } // Eval does not create a new env scope // But iteration vars are reset afterwards Expression* Eval::operator()(Each* e) { std::vector variables(e->variables()); Expression* expr = e->list()->perform(this); Env env(environment(), true); exp.env_stack.push_back(&env); Vectorized* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { map = static_cast(expr); } else if (Selector_List* ls = dynamic_cast(expr)) { Listize listize(ctx.mem); list = dynamic_cast(ls->perform(&listize)); } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA); *list << expr; } else { list = static_cast(expr); } Block* body = e->block(); Expression* val = 0; if (map) { for (auto key : map->keys()) { Expression* value = map->at(key); if (variables.size() == 1) { List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE); *variable << key; *variable << value; env.set_local(variables[0], variable); } else { env.set_local(variables[0], key); env.set_local(variables[1], value); } val = body->perform(this); if (val) break; } } else { if (list->length() == 1 && dynamic_cast(list)) { list = dynamic_cast*>(list); } for (size_t i = 0, L = list->length(); i < L; ++i) { Expression* e = (*list)[i]; // unwrap value if the expression is an argument if (Argument* arg = dynamic_cast(e)) e = arg->value(); // check if we got passed a list of args (investigate) if (List* scalars = dynamic_cast(e)) { if (variables.size() == 1) { 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* res = j >= scalars->length() ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()) : (*scalars)[j]; env.set_local(variables[j], res); } } } else { if (variables.size() > 0) { env.set_local(variables[0], e); for (size_t j = 1, K = variables.size(); j < K; ++j) { // XXX: this is never hit via spec tests Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()); env.set_local(variables[j], res); } } } val = body->perform(this); if (val) break; } } exp.env_stack.pop_back(); return val; } Expression* Eval::operator()(While* w) { Expression* pred = w->predicate(); Block* body = w->block(); Env env(environment(), true); exp.env_stack.push_back(&env); while (*pred->perform(this)) { Expression* val = body->perform(this); if (val) { exp.env_stack.pop_back(); return val; } } exp.env_stack.pop_back(); return 0; } Expression* Eval::operator()(Return* r) { return r->value()->perform(this); } Expression* Eval::operator()(Warning* w) { Sass_Output_Style outstyle = ctx.c_options.output_style; ctx.c_options.output_style = NESTED; Expression* message = w->message()->perform(this); Env* env = exp.environment(); // try to use generic function if (env->has("@warn[f]")) { Definition* def = static_cast((*env)["@warn[f]"]); // Block* body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); To_C to_c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; } std::string result(unquote(message->to_sass())); Backtrace top(backtrace(), w->pstate(), ""); std::cerr << "WARNING: " << result; std::cerr << top.to_string(); std::cerr << std::endl << std::endl; ctx.c_options.output_style = outstyle; return 0; } Expression* Eval::operator()(Error* e) { Sass_Output_Style outstyle = ctx.c_options.output_style; ctx.c_options.output_style = NESTED; Expression* message = e->message()->perform(this); Env* env = exp.environment(); // try to use generic function if (env->has("@error[f]")) { Definition* def = static_cast((*env)["@error[f]"]); // Block* body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); To_C to_c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; } std::string result(unquote(message->to_sass())); ctx.c_options.output_style = outstyle; error(result, e->pstate()); return 0; } Expression* Eval::operator()(Debug* d) { Sass_Output_Style outstyle = ctx.c_options.output_style; ctx.c_options.output_style = NESTED; Expression* message = d->value()->perform(this); Env* env = exp.environment(); // try to use generic function if (env->has("@debug[f]")) { Definition* def = static_cast((*env)["@debug[f]"]); // Block* body = def->block(); // Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Sass_Function_Fn c_func = sass_function_get_function(c_function); To_C to_c; union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; } std::string cwd(ctx.cwd()); std::string result(unquote(message->to_sass())); std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd)); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path)); ctx.c_options.output_style = outstyle; std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result; std::cerr << std::endl; return 0; } Expression* Eval::operator()(List* l) { // special case for unevaluated map if (l->separator() == SASS_HASH) { Map* lm = SASS_MEMORY_NEW(ctx.mem, Map, l->pstate(), l->length() / 2); for (size_t i = 0, L = l->length(); i < L; i += 2) { Expression* key = (*l)[i+0]->perform(this); Expression* val = (*l)[i+1]->perform(this); // make sure the color key never displays its real name key->is_delayed(true); *lm << std::make_pair(key, val); } if (lm->has_duplicate_key()) { throw Exception::DuplicateKeyError(*lm, *l); } lm->is_interpolant(l->is_interpolant()); return lm->perform(this); } // check if we should expand it if (l->is_expanded()) return l; // regular case for unevaluated lists List* ll = SASS_MEMORY_NEW(ctx.mem, List, l->pstate(), l->length(), l->separator(), l->is_arglist()); for (size_t i = 0, L = l->length(); i < L; ++i) { *ll << (*l)[i]->perform(this); } ll->is_interpolant(l->is_interpolant()); ll->is_expanded(true); return ll; } Expression* Eval::operator()(Map* m) { if (m->is_expanded()) return m; // make sure we're not starting with duplicate keys. // the duplicate key state will have been set in the parser phase. if (m->has_duplicate_key()) { throw Exception::DuplicateKeyError(*m, *m); } Map* mm = SASS_MEMORY_NEW(ctx.mem, Map, m->pstate(), m->length()); for (auto key : m->keys()) { Expression* ex_key = key->perform(this); Expression* ex_val = m->at(key)->perform(this); *mm << std::make_pair(ex_key, ex_val); } // check the evaluated keys aren't duplicates. if (mm->has_duplicate_key()) { throw Exception::DuplicateKeyError(*mm, *m); } mm->is_expanded(true); return mm; } Expression* Eval::operator()(Binary_Expression* b) { String_Schema* ret_schema = 0; enum Sass_OP op_type = b->type(); // don't eval delayed expressions (the '/' when used as a separator) if (op_type == Sass_OP::DIV && b->is_delayed()) { b->right(b->right()->perform(this)); b->left(b->left()->perform(this)); return b; } // only the last item will be used to eval the binary expression if (String_Schema* s_l = dynamic_cast(b->left())) { if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_l->pstate()); Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), b->op(), s_l->last(), b->right()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // bin_ex->is_interpolant(b->left()->is_interpolant()); for (size_t i = 0; i < s_l->length() - 1; ++i) { *ret_schema << s_l->at(i)->perform(this); } *ret_schema << bin_ex->perform(this); return ret_schema->perform(this); } } if (String_Schema* s_r = dynamic_cast(b->right())) { if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_r->pstate()); Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), b->op(), b->left(), s_r->first()); bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); // if (op_type == Sass_OP::SUB && b->is_right_interpolant()) bin_ex->is_interpolant(true); *ret_schema << bin_ex->perform(this); for (size_t i = 1; i < s_r->length(); ++i) { *ret_schema << s_r->at(i)->perform(this); } return ret_schema->perform(this); } } // don't eval delayed expressions (the '/' when used as a separator) if (op_type == Sass_OP::DIV && b->is_delayed()) { b->right(b->right()->perform(this)); b->left(b->left()->perform(this)); return b; } // b->is_delayed(false); Expression* lhs = b->left(); Expression* rhs = b->right(); // bool delay_lhs = false; // bool delay_rhs = false; if (String_Schema* schema = dynamic_cast(lhs)) { if (schema->is_right_interpolant()) { b->is_delayed(true); // delay_lhs = true; } } if (String_Schema* schema = dynamic_cast(rhs)) { if (schema->is_left_interpolant()) { b->is_delayed(true); // delay_rhs = true; } } // maybe fully evaluate structure if (op_type == Sass_OP::EQ || op_type == Sass_OP::NEQ || op_type == Sass_OP::GT || op_type == Sass_OP::GTE || op_type == Sass_OP::LT || op_type == Sass_OP::LTE) { if (String_Schema* schema = dynamic_cast(lhs)) { if (schema->has_interpolants()) { b->is_delayed(true); } } if (String_Schema* schema = dynamic_cast(rhs)) { if (schema->has_interpolants()) { b->is_delayed(true); } } lhs->is_expanded(false); lhs->set_delayed(false); lhs = lhs->perform(this); lhs->is_expanded(false); lhs->set_delayed(false); lhs = lhs->perform(this); rhs->is_expanded(false); rhs->set_delayed(false); rhs = rhs->perform(this); rhs->is_expanded(false); rhs->set_delayed(false); rhs = rhs->perform(this); } else { // rhs->set_delayed(false); // rhs = rhs->perform(this); } // if one of the operands is a '/' then make sure it's evaluated lhs = lhs->perform(this); lhs->is_delayed(false); while (typeid(*lhs) == typeid(Binary_Expression)) { Binary_Expression* lhs_ex = static_cast(lhs); if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break; lhs = Eval::operator()(lhs_ex); } switch (op_type) { case Sass_OP::AND: return *lhs ? b->right()->perform(this) : lhs; break; case Sass_OP::OR: return *lhs ? lhs : b->right()->perform(this); break; default: break; } // not a logical connective, so go ahead and eval the rhs rhs = rhs->perform(this); // upgrade string to number if possible (issue #948) if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) { if (String_Constant* str = dynamic_cast(rhs)) { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::number >(start) != 0) { rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value()); rhs->is_delayed(false); rhs = rhs->perform(this); } } } Expression::Concrete_Type l_type = lhs->concrete_type(); Expression::Concrete_Type r_type = rhs->concrete_type(); // Is one of the operands an interpolant? String_Schema* s1 = dynamic_cast(b->left()); String_Schema* s2 = dynamic_cast(b->right()); Binary_Expression* b1 = dynamic_cast(b->left()); Binary_Expression* b2 = dynamic_cast(b->right()); bool schema_op = false; bool force_delay = (s2 && s2->is_left_interpolant()) || (s1 && s1->is_right_interpolant()) || (b1 && b1->is_right_interpolant()) || (b2 && b2->is_left_interpolant()); if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay) { // If possible upgrade LHS to a number 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 (String_Constant* str = dynamic_cast(lhs)) { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) { lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value()); lhs->is_delayed(false); lhs = lhs->perform(this); } } if (String_Constant* str = dynamic_cast(rhs)) { std::string value(str->value()); const char* start = value.c_str(); if (Prelexer::sequence < Prelexer::number >(start) != 0) { rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value()); rhs->is_delayed(false); rhs = rhs->perform(this); } } } To_Value to_value(ctx, ctx.mem); Value* v_l = dynamic_cast(lhs->perform(&to_value)); Value* v_r = dynamic_cast(rhs->perform(&to_value)); l_type = lhs->concrete_type(); r_type = rhs->concrete_type(); if (s2 && s2->has_interpolants() && s2->length()) { Textual* front = dynamic_cast(s2->elements().front()); if (front && !front->is_interpolant()) { // XXX: this is never hit via spec tests schema_op = true; rhs = front->perform(this); } } if (force_delay) { std::string str(""); str += v_l->to_string(ctx.c_options); if (b->op().ws_before) str += " "; str += b->separator(); if (b->op().ws_after) str += " "; str += v_r->to_string(ctx.c_options); String_Constant* val = SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(), str); val->is_interpolant(b->left()->has_interpolant()); return val; } } // see if it's a relational expression try { switch(op_type) { case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs)); case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs)); case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs)); case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gte")); case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lt")); case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs)); default: break; } } catch (Exception::OperationError& err) { // throw Exception::Base(b->pstate(), err.what()); throw Exception::SassValueError(b->pstate(), err); } l_type = lhs->concrete_type(); r_type = rhs->concrete_type(); // ToDo: throw error in op functions // ToDo: then catch and re-throw them Expression* rv = 0; try { ParserState pstate(b->pstate()); if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { const Number* l_n = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); rv = op_numbers(ctx.mem, op_type, *l_n, *r_n, ctx.c_options, &pstate); } else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { const Number* l_n = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); rv = op_number_color(ctx.mem, op_type, *l_n, *r_c, ctx.c_options, &pstate); } else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { const Color* l_c = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); rv = op_color_number(ctx.mem, op_type, *l_c, *r_n, ctx.c_options, &pstate); } else if (l_type == Expression::COLOR && r_type == Expression::COLOR) { const Color* l_c = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); rv = op_colors(ctx.mem, op_type, *l_c, *r_c, ctx.c_options, &pstate); } else { To_Value to_value(ctx, ctx.mem); Value* v_l = dynamic_cast(lhs->perform(&to_value)); Value* v_r = dynamic_cast(rhs->perform(&to_value)); bool interpolant = b->is_right_interpolant() || b->is_left_interpolant() || b->is_interpolant(); if (op_type == Sass_OP::SUB) interpolant = false; // if (op_type == Sass_OP::DIV) interpolant = true; Value* ex = op_strings(ctx.mem, b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress if (String_Constant* str = dynamic_cast(ex)) { if (str->concrete_type() == Expression::STRING) { String_Constant* lstr = dynamic_cast(lhs); String_Constant* rstr = dynamic_cast(rhs); if (op_type != Sass_OP::SUB) { if (String_Constant* org = lstr ? lstr : rstr) { str->quote_mark(org->quote_mark()); } } } } ex->is_interpolant(b->is_interpolant()); rv = ex; } } catch (Exception::OperationError& err) { // throw Exception::Base(b->pstate(), err.what()); throw Exception::SassValueError(b->pstate(), err); } if (rv) { if (schema_op) { // XXX: this is never hit via spec tests (*s2)[0] = rv; rv = s2->perform(this); } } return rv; } Expression* Eval::operator()(Unary_Expression* u) { Expression* operand = u->operand()->perform(this); if (u->type() == Unary_Expression::NOT) { Boolean* result = SASS_MEMORY_NEW(ctx.mem, Boolean, u->pstate(), (bool)*operand); result->value(!result->value()); return result; } else if (operand->concrete_type() == Expression::NUMBER) { Number* result = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast(operand)); result->value(u->type() == Unary_Expression::MINUS ? -result->value() : result->value()); return result; } else { // Special cases: +/- variables which evaluate to null ouput just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && dynamic_cast(u->operand())) { u->operand(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), "")); } else u->operand(operand); String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), u->inspect()); return result; } // unreachable return u; } Expression* Eval::operator()(Function_Call* c) { if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) { // XXX: this is never hit via spec tests std::ostringstream stm; stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str(), c->pstate(), backtrace()); } std::string name(Util::normalize_underscores(c->name())); std::string full_name(name + "[f]"); Arguments* args = c->arguments(); if (full_name != "if[f]") { args = static_cast(args->perform(this)); } Env* env = environment(); if (!env->has(full_name)) { if (!env->has("*[f]")) { // just pass it through as a literal Function_Call* lit = SASS_MEMORY_NEW(ctx.mem, Function_Call, c->pstate(), c->name(), args); if (args->has_named_arguments()) { error("Function " + c->name() + " doesn't support keyword arguments", c->pstate()); } String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, c->pstate(), lit->to_string(ctx.c_options)); str->is_interpolant(c->is_interpolant()); return str; } else { // call generic function full_name = "*[f]"; } } Definition* def = static_cast((*env)[full_name]); if (def->is_overload_stub()) { std::stringstream ss; ss << full_name << args->length(); full_name = ss.str(); std::string resolved_name(full_name); if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate()); def = static_cast((*env)[resolved_name]); } Expression* result = c; Block* body = def->block(); Native_Function func = def->native_function(); Sass_Function_Entry c_function = def->c_function(); Parameters* params = def->parameters(); Env fn_env(def->environment()); exp.env_stack.push_back(&fn_env); if (func || body) { bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this); Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`"); exp.backtrace_stack.push_back(&here); // eval the body if user-defined or special, invoke underlying CPP function if native if (body && !Prelexer::re_special_fun(c->name().c_str())) { result = body->perform(this); } else if (func) { result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace()); } if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate()); exp.backtrace_stack.pop_back(); } // else if it's a user-defined c function // convert call into C-API compatible form else if (c_function) { Sass_Function_Fn c_func = sass_function_get_function(c_function); if (full_name == "*[f]") { String_Quoted *str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, c->pstate(), c->name()); Arguments* new_args = SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate()); *new_args << SASS_MEMORY_NEW(ctx.mem, Argument, c->pstate(), str); *new_args += args; args = new_args; } // populates env with default values for params std::string ff(c->name()); bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this); Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`"); exp.backtrace_stack.push_back(&here); To_C to_c; union Sass_Value* c_args = sass_make_list(params[0].length(), SASS_COMMA); for(size_t i = 0; i < params[0].length(); i++) { std::string key = params[0][i]->name(); AST_Node* node = fn_env.get_local(key); Expression* arg = static_cast(node); sass_list_set_value(c_args, i, arg->perform(&to_c)); } union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); if (sass_value_get_tag(c_val) == SASS_ERROR) { error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace()); } else if (sass_value_get_tag(c_val) == SASS_WARNING) { error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace()); } result = cval_to_astnode(ctx.mem, c_val, ctx, backtrace(), c->pstate()); exp.backtrace_stack.pop_back(); sass_delete_value(c_args); if (c_val != c_args) sass_delete_value(c_val); } // link back to function definition // only do this for custom functions if (result->pstate().file == std::string::npos) result->pstate(c->pstate()); result->is_delayed(result->concrete_type() == Expression::STRING); if (!result->is_delayed()) result = result->perform(this); result->is_interpolant(c->is_interpolant()); exp.env_stack.pop_back(); return result; } Expression* Eval::operator()(Function_Call_Schema* s) { Expression* evaluated_name = s->name()->perform(this); Expression* evaluated_args = s->arguments()->perform(this); String_Schema* ss = SASS_MEMORY_NEW(ctx.mem, String_Schema, s->pstate(), 2); (*ss) << evaluated_name << evaluated_args; return ss->perform(this); } Expression* Eval::operator()(Variable* v) { std::string name(v->name()); Expression* value = 0; Env* env = environment(); if (env->has(name)) value = static_cast((*env)[name]); else error("Undefined variable: \"" + v->name() + "\".", v->pstate()); if (typeid(*value) == typeid(Argument)) value = static_cast(value)->value(); // behave according to as ruby sass (add leading zero) if (value->concrete_type() == Expression::NUMBER) { value = SASS_MEMORY_NEW(ctx.mem, Number, *static_cast(value)); static_cast(value)->zero(true); } else if (value->concrete_type() == Expression::STRING) { if (auto str = dynamic_cast(value)) { value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str); } else if (auto str = dynamic_cast(value)) { value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->value()); } } else if (value->concrete_type() == Expression::LIST) { value = SASS_MEMORY_NEW(ctx.mem, List, *static_cast(value)); } else if (value->concrete_type() == Expression::MAP) { value = SASS_MEMORY_NEW(ctx.mem, Map, *static_cast(value)); } else if (value->concrete_type() == Expression::BOOLEAN) { value = SASS_MEMORY_NEW(ctx.mem, Boolean, *static_cast(value)); } else if (value->concrete_type() == Expression::COLOR) { value = SASS_MEMORY_NEW(ctx.mem, Color, *static_cast(value)); } else if (value->concrete_type() == Expression::NULL_VAL) { value = SASS_MEMORY_NEW(ctx.mem, Null, value->pstate()); } else if (value->concrete_type() == Expression::SELECTOR) { value = value->perform(this); // ->perform(&listize); } value->is_interpolant(v->is_interpolant()); value->is_expanded(false); return value->perform(this); } Expression* Eval::operator()(Textual* t) { using Prelexer::number; Expression* result = 0; size_t L = t->value().length(); bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") || (L > 1 && t->value().substr(0, 2) == "0.") || (L > 1 && t->value().substr(0, 2) == "-.") || (L > 2 && t->value().substr(0, 3) == "-0.") ); const std::string& text = t->value(); size_t num_pos = text.find_first_not_of(" \n\r\t"); if (num_pos == std::string::npos) num_pos = text.length(); size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos); if (unit_pos == std::string::npos) unit_pos = text.length(); const std::string& num = text.substr(num_pos, unit_pos - num_pos); switch (t->type()) { case Textual::NUMBER: result = SASS_MEMORY_NEW(ctx.mem, Number, t->pstate(), sass_atof(num.c_str()), "", zero); break; case Textual::PERCENTAGE: result = SASS_MEMORY_NEW(ctx.mem, Number, t->pstate(), sass_atof(num.c_str()), "%", true); break; case Textual::DIMENSION: result = SASS_MEMORY_NEW(ctx.mem, Number, t->pstate(), sass_atof(num.c_str()), Token(number(text.c_str())), zero); break; case Textual::HEX: { if (t->value().substr(0, 1) != "#") { result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, t->pstate(), t->value()); break; } std::string hext(t->value().substr(1)); // chop off the '#' if (hext.length() == 6) { std::string r(hext.substr(0,2)); std::string g(hext.substr(2,2)); std::string b(hext.substr(4,2)); result = SASS_MEMORY_NEW(ctx.mem, Color, t->pstate(), static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), 1, // alpha channel t->value()); } else { result = SASS_MEMORY_NEW(ctx.mem, Color, t->pstate(), static_cast(strtol(std::string(2,hext[0]).c_str(), NULL, 16)), static_cast(strtol(std::string(2,hext[1]).c_str(), NULL, 16)), static_cast(strtol(std::string(2,hext[2]).c_str(), NULL, 16)), 1, // alpha channel t->value()); } } break; } result->is_interpolant(t->is_interpolant()); return result; } Expression* Eval::operator()(Number* n) { return n; } Expression* Eval::operator()(Boolean* b) { return b; } void Eval::interpolation(Context& ctx, std::string& res, Expression* ex, bool into_quotes, bool was_itpl) { bool needs_closing_brace = false; //debug_ast(ex); if (Arguments* args = dynamic_cast(ex)) { List* ll = SASS_MEMORY_NEW(ctx.mem, List, args->pstate(), 0, SASS_COMMA); for(auto arg : *args) { *ll << arg->value(); } ll->is_interpolant(args->is_interpolant()); needs_closing_brace = true; res += "("; ex = ll; } if (Number* nr = dynamic_cast(ex)) { if (!nr->is_valid_css_unit()) { throw Exception::InvalidValue(*nr); } } if (Argument* arg = dynamic_cast(ex)) { ex = arg->value(); } if (String_Quoted* sq = dynamic_cast(ex)) { if (was_itpl) { bool was_interpolant = ex->is_interpolant(); ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, sq->pstate(), sq->value()); ex->is_interpolant(was_interpolant); } } if (dynamic_cast(ex)) { return; } // parent selector needs another go if (dynamic_cast(ex)) { // XXX: this is never hit via spec tests ex = ex->perform(this); } if (List* l = dynamic_cast(ex)) { List* ll = SASS_MEMORY_NEW(ctx.mem, List, l->pstate(), 0, l->separator()); // this fixes an issue with bourbon sample, not really sure why // if (l->size() && dynamic_cast((*l)[0])) { res += ""; } for(auto item : *l) { item->is_interpolant(l->is_interpolant()); std::string rl(""); interpolation(ctx, rl, item, into_quotes, l->is_interpolant()); bool is_null = dynamic_cast(item) != 0; // rl != "" if (!is_null) *ll << SASS_MEMORY_NEW(ctx.mem, String_Quoted, item->pstate(), rl); } res += (ll->to_string(ctx.c_options)); ll->is_interpolant(l->is_interpolant()); } // Value // Textual // Function_Call // Selector_List // String_Quoted // String_Constant // Parent_Selector // Binary_Expression else { // ex = ex->perform(this); if (into_quotes && ex->is_interpolant()) { res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : ""); } else { res += ex ? ex->to_string(ctx.c_options) : ""; } } if (needs_closing_brace) res += ")"; } Expression* Eval::operator()(String_Schema* s) { size_t L = s->length(); bool into_quotes = false; if (L > 1) { if (!dynamic_cast((*s)[0]) && !dynamic_cast((*s)[L - 1])) { if (String_Constant* l = dynamic_cast((*s)[0])) { if (String_Constant* r = dynamic_cast((*s)[L - 1])) { if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; } } } } bool was_quoted = false; bool was_interpolant = false; std::string res(""); for (size_t i = 0; i < L; ++i) { bool is_quoted = dynamic_cast((*s)[i]) != NULL; (*s)[i]->perform(this); if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } Expression* ex = (*s)[i]->is_delayed() ? (*s)[i] : (*s)[i]->perform(this); interpolation(ctx, res, ex, into_quotes, ex->is_interpolant()); was_quoted = dynamic_cast((*s)[i]) != NULL; was_interpolant = (*s)[i]->is_interpolant(); } if (!s->is_interpolant()) { if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(ctx.mem, Null, s->pstate()); return SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res); } String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), res); // if (s->is_interpolant()) str->quote_mark(0); // String_Constant* str = SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res); if (str->quote_mark()) str->quote_mark('*'); else if (!is_in_comment) str->value(string_to_output(str->value())); str->is_interpolant(s->is_interpolant()); return str; } Expression* Eval::operator()(String_Constant* s) { if (!s->is_delayed() && name_to_color(s->value())) { Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *name_to_color(s->value())); c->pstate(s->pstate()); c->disp(s->value()); return c; } return s; } Expression* Eval::operator()(String_Quoted* s) { String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), ""); str->value(s->value()); str->quote_mark(s->quote_mark()); str->is_interpolant(s->is_interpolant()); return str; } Expression* Eval::operator()(Supports_Operator* c) { Expression* left = c->left()->perform(this); Expression* right = c->right()->perform(this); Supports_Operator* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, c->pstate(), static_cast(left), static_cast(right), c->operand()); return cc; } Expression* Eval::operator()(Supports_Negation* c) { Expression* condition = c->condition()->perform(this); Supports_Negation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Negation, c->pstate(), static_cast(condition)); return cc; } Expression* Eval::operator()(Supports_Declaration* c) { Expression* feature = c->feature()->perform(this); Expression* value = c->value()->perform(this); Supports_Declaration* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration, c->pstate(), feature, value); return cc; } Expression* Eval::operator()(Supports_Interpolation* c) { Expression* value = c->value()->perform(this); Supports_Interpolation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, c->pstate(), value); return cc; } Expression* Eval::operator()(At_Root_Expression* e) { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); Expression* value = e->value(); value = (value ? value->perform(this) : 0); Expression* ee = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, e->pstate(), static_cast(feature), value, e->is_interpolated()); return ee; } Expression* Eval::operator()(Media_Query* q) { String* t = q->media_type(); t = static_cast(t ? t->perform(this) : 0); Media_Query* qq = SASS_MEMORY_NEW(ctx.mem, Media_Query, q->pstate(), t, q->length(), q->is_negated(), q->is_restricted()); for (size_t i = 0, L = q->length(); i < L; ++i) { *qq << static_cast((*q)[i]->perform(this)); } return qq; } Expression* Eval::operator()(Media_Query_Expression* e) { Expression* feature = e->feature(); feature = (feature ? feature->perform(this) : 0); if (feature && dynamic_cast(feature)) { feature = SASS_MEMORY_NEW(ctx.mem, String_Quoted, feature->pstate(), dynamic_cast(feature)->value()); } Expression* value = e->value(); value = (value ? value->perform(this) : 0); if (value && dynamic_cast(value)) { // XXX: this is never hit via spec tests value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, value->pstate(), dynamic_cast(value)->value()); } return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, e->pstate(), feature, value, e->is_interpolated()); } Expression* Eval::operator()(Null* n) { return n; } Expression* Eval::operator()(Argument* a) { Expression* val = a->value(); val->is_delayed(false); val = val->perform(this); val->is_delayed(false); bool is_rest_argument = a->is_rest_argument(); bool is_keyword_argument = a->is_keyword_argument(); if (a->is_rest_argument()) { if (val->concrete_type() == Expression::MAP) { is_rest_argument = false; is_keyword_argument = true; } else if(val->concrete_type() != Expression::LIST) { List* wrapper = SASS_MEMORY_NEW(ctx.mem, List, val->pstate(), 0, SASS_COMMA, true); *wrapper << val; val = wrapper; } } return SASS_MEMORY_NEW(ctx.mem, Argument, a->pstate(), val, a->name(), is_rest_argument, is_keyword_argument); } Expression* Eval::operator()(Arguments* a) { Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate()); for (size_t i = 0, L = a->length(); i < L; ++i) { Argument* arg = static_cast((*a)[i]->perform(this)); if (!(arg->is_rest_argument() || arg->is_keyword_argument())) { *aa << arg; } } if (a->has_rest_argument()) { Expression* splat = static_cast( a->get_rest_argument()->perform(this) )->value()->perform(this); Sass_Separator separator = SASS_COMMA; List* ls = dynamic_cast(splat); Map* ms = dynamic_cast(splat); List* arglist = SASS_MEMORY_NEW(ctx.mem, List, splat->pstate(), 0, ls ? ls->separator() : separator, true); if (ls && ls->is_arglist()) { for (auto as : *ls) *arglist << as; } else if (ms) { *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), ms, "", false, true); } else if (ls) { for (auto as : *ls) *arglist << as; } else { *arglist << splat; } if (arglist->length()) { *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), arglist, "", true); } } if (a->has_keyword_argument()) { Expression* kwarg = static_cast( a->get_keyword_argument()->perform(this) )->value()->perform(this); *aa << SASS_MEMORY_NEW(ctx.mem, Argument, kwarg->pstate(), kwarg, "", false, true); } return aa; } Expression* Eval::operator()(Comment* c) { return 0; } inline Expression* Eval::fallback_impl(AST_Node* n) { return static_cast(n); } // All the binary helpers. bool Eval::eq(Expression* lhs, Expression* rhs) { // use compare operator from ast node return lhs && rhs && *lhs == *rhs; } bool Eval::lt(Expression* lhs, Expression* rhs, std::string op) { Number* l = dynamic_cast(lhs); Number* r = dynamic_cast(rhs); // use compare operator from ast node if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op); // use compare operator from ast node return *l < *r; } Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate) { double lv = l.value(); double rv = r.value(); if (op == Sass_OP::DIV && rv == 0) { // XXX: this is never hit via spec tests return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN"); } if (op == Sass_OP::MOD && !rv) { // XXX: this is never hit via spec tests throw Exception::ZeroDivisionError(l, r); } Number tmp(r); bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV; tmp.normalize(l.find_convertible_unit(), strict); std::string l_unit(l.unit()); std::string r_unit(tmp.unit()); Number* v = SASS_MEMORY_NEW(mem, Number, l); v->pstate(pstate ? *pstate : l.pstate()); if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) { v->numerator_units() = r.numerator_units(); v->denominator_units() = r.denominator_units(); } if (op == Sass_OP::MUL) { v->value(ops[op](lv, rv)); for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) { v->numerator_units().push_back(r.numerator_units()[i]); } for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) { v->denominator_units().push_back(r.denominator_units()[i]); } } else if (op == Sass_OP::DIV) { v->value(ops[op](lv, rv)); for (size_t i = 0, S = r.numerator_units().size(); i < S; ++i) { v->denominator_units().push_back(r.numerator_units()[i]); } for (size_t i = 0, S = r.denominator_units().size(); i < S; ++i) { v->numerator_units().push_back(r.denominator_units()[i]); } } else { Number rh(r); v->value(ops[op](lv, rh.value() * r.convert_factor(l))); // v->normalize(); return v; v->value(ops[op](lv, tmp.value())); } v->normalize(); return v; } Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, struct Sass_Inspect_Options opt, ParserState* pstate) { Color r(rh); double lv = l.value(); switch (op) { case Sass_OP::ADD: case Sass_OP::MUL: { return SASS_MEMORY_NEW(mem, Color, pstate ? *pstate : l.pstate(), ops[op](lv, r.r()), ops[op](lv, r.g()), ops[op](lv, r.b()), r.a()); } break; case Sass_OP::SUB: case Sass_OP::DIV: { std::string sep(op == Sass_OP::SUB ? "-" : "/"); std::string color(r.to_string(opt)); return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), l.to_string(opt) + sep + color); } break; case Sass_OP::MOD: { throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op)); } break; default: break; // caller should ensure that we don't get here } // unreachable return SASS_MEMORY_NEW(mem, Color, rh); } Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate) { double rv = r.value(); if (op == Sass_OP::DIV && !rv) { // comparison of Fixnum with Float failed? throw Exception::ZeroDivisionError(l, r); } return SASS_MEMORY_NEW(mem, Color, pstate ? *pstate : l.pstate(), ops[op](l.r(), rv), ops[op](l.g(), rv), ops[op](l.b(), rv), l.a()); } Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate) { if (l.a() != r.a()) { throw Exception::AlphaChannelsNotEqual(&l, &r, "+"); } if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) { // comparison of Fixnum with Float failed? throw Exception::ZeroDivisionError(l, r); } return SASS_MEMORY_NEW(mem, Color, pstate ? *pstate : l.pstate(), ops[op](l.r(), r.r()), ops[op](l.g(), r.g()), ops[op](l.b(), r.b()), l.a()); } Value* Eval::op_strings(Memory_Manager& mem, Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed) { Expression::Concrete_Type ltype = lhs.concrete_type(); Expression::Concrete_Type rtype = rhs.concrete_type(); enum Sass_OP op = operand.operand; String_Quoted* lqstr = dynamic_cast(&lhs); String_Quoted* rqstr = dynamic_cast(&rhs); std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt)); std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt)); if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op)); if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op)); if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op)); if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op)); std::string sep; switch (op) { case Sass_OP::SUB: sep = "-"; break; case Sass_OP::DIV: sep = "/"; break; case Sass_OP::MUL: sep = "*"; break; case Sass_OP::MOD: sep = "%"; break; case Sass_OP::EQ: sep = "=="; break; case Sass_OP::NEQ: sep = "!="; break; case Sass_OP::LT: sep = "<"; break; case Sass_OP::GT: sep = ">"; break; case Sass_OP::LTE: sep = "<="; break; case Sass_OP::GTE: sep = ">="; break; default: break; } if ( (sep == "") /* && (sep != "/" || !rqstr || !rqstr->quote_mark()) */ ) { char quote_mark = 0; std::string unq(unquote(lstr + sep + rstr, "e_mark, true)); if (quote_mark && quote_mark != '*') { return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), quote_mark + unq + quote_mark); } return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr); } if (sep != "" && !delayed) { if (operand.ws_before) sep = " " + sep; if (operand.ws_after) sep = sep + " "; } if (op == Sass_OP::SUB || op == Sass_OP::DIV) { if (lqstr && lqstr->quote_mark()) lstr = quote(lstr); if (rqstr && rqstr->quote_mark()) rstr = quote(rstr); return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), lstr + sep + rstr); } return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + (rstr)); } Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate) { using std::strlen; using std::strcpy; Expression* e = 0; switch (sass_value_get_tag(v)) { case SASS_BOOLEAN: { e = SASS_MEMORY_NEW(mem, Boolean, pstate, !!sass_boolean_get_value(v)); } break; case SASS_NUMBER: { e = SASS_MEMORY_NEW(mem, Number, pstate, sass_number_get_value(v), sass_number_get_unit(v)); } break; case SASS_COLOR: { e = SASS_MEMORY_NEW(mem, Color, pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v)); } break; case SASS_STRING: { if (sass_string_is_quoted(v)) e = SASS_MEMORY_NEW(mem, String_Quoted, pstate, sass_string_get_value(v)); else { e = SASS_MEMORY_NEW(mem, String_Constant, pstate, sass_string_get_value(v)); } } break; case SASS_LIST: { List* l = SASS_MEMORY_NEW(mem, List, pstate, sass_list_get_length(v), sass_list_get_separator(v)); for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) { *l << cval_to_astnode(mem, sass_list_get_value(v, i), ctx, backtrace, pstate); } e = l; } break; case SASS_MAP: { Map* m = SASS_MEMORY_NEW(mem, Map, pstate); for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) { *m << std::make_pair( cval_to_astnode(mem, sass_map_get_key(v, i), ctx, backtrace, pstate), cval_to_astnode(mem, sass_map_get_value(v, i), ctx, backtrace, pstate)); } e = m; } break; case SASS_NULL: { e = SASS_MEMORY_NEW(mem, Null, pstate); } break; case SASS_ERROR: { error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace); } break; case SASS_WARNING: { error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace); } break; } return e; } Selector_List* Eval::operator()(Selector_List* s) { std::vector rv; Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate()); sl->media_block(s->media_block()); for (size_t i = 0, iL = s->length(); i < iL; ++i) { rv.push_back(operator()((*s)[i])); } // we should actually permutate parent first // but here we have permutated the selector first size_t round = 0; while (round != std::string::npos) { bool abort = true; for (size_t i = 0, iL = rv.size(); i < iL; ++i) { if (rv[i]->length() > round) { *sl << (*rv[i])[round]; abort = false; } } if (abort) { round = std::string::npos; } else { ++ round; } } return sl; } Selector_List* Eval::operator()(Complex_Selector* s) { return s->parentize(selector(), ctx); } // XXX: this is never hit via spec tests Attribute_Selector* Eval::operator()(Attribute_Selector* s) { String* attr = s->value(); if (attr) { attr = static_cast(attr->perform(this)); } Attribute_Selector* ss = SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, *s); ss->value(attr); return ss; } Selector_List* Eval::operator()(Selector_Schema* s) { // the parser will look for a brace to end the selector std::string result_str(s->contents()->perform(this)->to_string(ctx.c_options)); result_str = unquote(Util::rtrim(result_str)) + "{"; Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()); return operator()(p.parse_selector_list(exp.block_stack.back()->is_root())); } Expression* Eval::operator()(Parent_Selector* p) { Selector_List* pr = selector(); if (pr) { exp.selector_stack.pop_back(); pr = operator()(pr); exp.selector_stack.push_back(pr); return pr; } else { return SASS_MEMORY_NEW(ctx.mem, Null, p->pstate()); } } } libsass-3.3.4/src/eval.hpp000066400000000000000000000100501267254216700154200ustar00rootroot00000000000000#ifndef SASS_EVAL_H #define SASS_EVAL_H #include "ast.hpp" #include "context.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { class Expand; class Context; class Eval : public Operation_CRTP { private: Expression* fallback_impl(AST_Node* n); public: Expand& exp; Context& ctx; Eval(Expand& exp); ~Eval(); bool is_in_comment; Env* environment(); Context& context(); Selector_List* selector(); Backtrace* backtrace(); // for evaluating function bodies Expression* operator()(Block*); Expression* operator()(Assignment*); Expression* operator()(If*); Expression* operator()(For*); Expression* operator()(Each*); Expression* operator()(While*); Expression* operator()(Return*); Expression* operator()(Warning*); Expression* operator()(Error*); Expression* operator()(Debug*); Expression* operator()(List*); Expression* operator()(Map*); Expression* operator()(Binary_Expression*); Expression* operator()(Unary_Expression*); Expression* operator()(Function_Call*); Expression* operator()(Function_Call_Schema*); Expression* operator()(Variable*); Expression* operator()(Textual*); Expression* operator()(Number*); Expression* operator()(Boolean*); Expression* operator()(String_Schema*); Expression* operator()(String_Quoted*); Expression* operator()(String_Constant*); // Expression* operator()(Selector_List*); Expression* operator()(Media_Query*); Expression* operator()(Media_Query_Expression*); Expression* operator()(At_Root_Expression*); Expression* operator()(Supports_Operator*); Expression* operator()(Supports_Negation*); Expression* operator()(Supports_Declaration*); Expression* operator()(Supports_Interpolation*); Expression* operator()(Null*); Expression* operator()(Argument*); Expression* operator()(Arguments*); Expression* operator()(Comment*); // these will return selectors Selector_List* operator()(Selector_List*); Selector_List* operator()(Complex_Selector*); Attribute_Selector* operator()(Attribute_Selector*); // they don't have any specific implementatio (yet) Type_Selector* operator()(Type_Selector* s) { return s; }; Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; }; Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; }; Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; }; Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; }; // actual evaluated selectors Selector_List* operator()(Selector_Schema*); Expression* operator()(Parent_Selector*); template Expression* fallback(U x) { return fallback_impl(x); } // -- only need to define two comparisons, and the rest can be implemented in terms of them static bool eq(Expression*, Expression*); static bool lt(Expression*, Expression*, std::string op); // -- arithmetic on the combinations that matter static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); static Value* op_strings(Memory_Manager&, Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, ParserState* pstate = 0, bool interpolant = false); private: void interpolation(Context& ctx, std::string& res, Expression* ex, bool into_quotes, bool was_itpl = false); }; Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate = ParserState("[AST]")); } #endif libsass-3.3.4/src/expand.cpp000066400000000000000000000603071267254216700157550ustar00rootroot00000000000000#include "sass.hpp" #include #include #include "expand.hpp" #include "bind.hpp" #include "eval.hpp" #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp" namespace Sass { Expand::Expand(Context& ctx, Env* env, Backtrace* bt) : ctx(ctx), eval(Eval(*this)), env_stack(std::vector()), block_stack(std::vector()), call_stack(std::vector()), property_stack(std::vector()), selector_stack(std::vector()), backtrace_stack(std::vector()), in_keyframes(false) { env_stack.push_back(0); env_stack.push_back(env); block_stack.push_back(0); call_stack.push_back(0); // import_stack.push_back(0); property_stack.push_back(0); selector_stack.push_back(0); backtrace_stack.push_back(0); backtrace_stack.push_back(bt); } Context& Expand::context() { return ctx; } Env* Expand::environment() { if (env_stack.size() > 0) return env_stack.back(); return 0; } Selector_List* Expand::selector() { if (selector_stack.size() > 0) return selector_stack.back(); return 0; } Backtrace* Expand::backtrace() { if (backtrace_stack.size() > 0) return backtrace_stack.back(); return 0; } // blocks create new variable scopes Statement* Expand::operator()(Block* b) { // create new local environment // set the current env as parent Env env(environment()); // copy the block object (add items later) Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, b->pstate(), b->length(), b->is_root()); // setup block and env stack this->block_stack.push_back(bb); this->env_stack.push_back(&env); // operate on block this->append_block(b); // revert block and env stack this->block_stack.pop_back(); this->env_stack.pop_back(); // return copy return bb; } Statement* Expand::operator()(Ruleset* r) { // reset when leaving scope if (in_keyframes) { Keyframe_Rule* k = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, r->pstate(), r->block()->perform(this)->block()); if (r->selector()) { selector_stack.push_back(0); k->selector(static_cast(r->selector()->perform(&eval))); selector_stack.pop_back(); } return k; } // do some special checks for the base level rules if (r->is_root()) { if (Selector_List* selector_list = dynamic_cast(r->selector())) { for (Complex_Selector* complex_selector : selector_list->elements()) { Complex_Selector* tail = complex_selector; while (tail) { if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) { if (dynamic_cast(header) == NULL) continue; // skip all others std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace()); } tail = tail->tail(); } } } } Expression* ex = r->selector()->perform(&eval); Selector_List* sel = dynamic_cast(ex); if (sel == 0) throw std::runtime_error("Expanded null selector"); if (sel->length() == 0 || sel->has_parent_ref()) { bool has_parent_selector = false; for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) { Selector_List* ll = selector_stack.at(i); has_parent_selector = ll != 0 && ll->length() > 0; } if (!has_parent_selector) { error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace()); } } selector_stack.push_back(sel); Env* env = 0; if (block_stack.back()->is_root()) { env = new Env(environment()); env_stack.push_back(env); } Block* blk = r->block()->perform(this)->block(); Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), sel, blk); selector_stack.pop_back(); if (block_stack.back()->is_root()) { env_stack.pop_back(); delete env; } rr->tabs(r->tabs()); return rr; } Statement* Expand::operator()(Propset* p) { property_stack.push_back(p->property_fragment()); Block* expanded_block = p->block()->perform(this)->block(); for (size_t i = 0, L = expanded_block->length(); i < L; ++i) { Statement* stm = (*expanded_block)[i]; if (Declaration* dec = static_cast(stm)) { String_Schema* combined_prop = SASS_MEMORY_NEW(ctx.mem, String_Schema, p->pstate()); if (!property_stack.empty()) { *combined_prop << property_stack.back()->perform(&eval) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, p->pstate(), "-") << dec->property(); // TODO: eval the prop into a string constant } else { *combined_prop << dec->property(); } dec->property(combined_prop); *block_stack.back() << dec; } else if (typeid(*stm) == typeid(Comment)) { // drop comments in propsets } else { error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace()); } } property_stack.pop_back(); return 0; } Statement* Expand::operator()(Supports_Block* f) { Expression* condition = f->condition()->perform(&eval); Supports_Block* ff = SASS_MEMORY_NEW(ctx.mem, Supports_Block, f->pstate(), static_cast(condition), f->block()->perform(this)->block()); return ff; } Statement* Expand::operator()(Media_Block* m) { Expression* mq = m->media_queries()->perform(&eval); std::string str_mq(mq->to_string(ctx.c_options)); char* str = sass_strdup(str_mq.c_str()); ctx.strings.push_back(str); Parser p(Parser::from_c_str(str, ctx, mq->pstate())); mq = p.parse_media_queries(); Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block, m->pstate(), static_cast(mq->perform(&eval)), m->block()->perform(this)->block(), 0); mm->tabs(m->tabs()); return mm; } Statement* Expand::operator()(At_Root_Block* a) { Block* ab = a->block(); // if (ab) ab->is_root(true); Expression* ae = a->expression(); if (ae) ae = ae->perform(&eval); else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate()); Block* bb = ab ? ab->perform(this)->block() : 0; At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, a->pstate(), bb, static_cast(ae)); // aa->block()->is_root(true); return aa; } Statement* Expand::operator()(At_Rule* a) { LOCAL_FLAG(in_keyframes, a->is_keyframes()); Block* ab = a->block(); Selector* as = a->selector(); Expression* av = a->value(); selector_stack.push_back(0); if (av) av = av->perform(&eval); if (as) as = dynamic_cast(as->perform(&eval)); selector_stack.pop_back(); Block* bb = ab ? ab->perform(this)->block() : 0; At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule, a->pstate(), a->keyword(), as, bb, av); return aa; } Statement* Expand::operator()(Declaration* d) { String* old_p = d->property(); String* new_p = static_cast(old_p->perform(&eval)); Expression* value = d->value()->perform(&eval); if (!value || (value->is_invisible() && !d->is_important())) return 0; Declaration* decl = SASS_MEMORY_NEW(ctx.mem, Declaration, d->pstate(), new_p, value, d->is_important()); decl->tabs(d->tabs()); return decl; } Statement* Expand::operator()(Assignment* a) { Env* env = environment(); std::string var(a->variable()); if (a->is_global()) { if (a->is_default()) { if (env->has_global(var)) { Expression* e = dynamic_cast(env->get_global(var)); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); } } else { env->set_global(var, a->value()->perform(&eval)); } } else { env->set_global(var, a->value()->perform(&eval)); } } else if (a->is_default()) { if (env->has_lexical(var)) { auto cur = env; while (cur && cur->is_lexical()) { if (cur->has_local(var)) { if (AST_Node* node = cur->get_local(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { cur->set_local(var, a->value()->perform(&eval)); } } else { throw std::runtime_error("Env not in sync"); } return 0; } cur = cur->parent(); } throw std::runtime_error("Env not in sync"); } else if (env->has_global(var)) { if (AST_Node* node = env->get_global(var)) { Expression* e = dynamic_cast(node); if (!e || e->concrete_type() == Expression::NULL_VAL) { env->set_global(var, a->value()->perform(&eval)); } } } else if (env->is_lexical()) { env->set_local(var, a->value()->perform(&eval)); } else { env->set_local(var, a->value()->perform(&eval)); } } else { env->set_lexical(var, a->value()->perform(&eval)); } return 0; } Statement* Expand::operator()(Import* imp) { Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate()); if (imp->media_queries() && imp->media_queries()->size()) { Expression* ex = imp->media_queries()->perform(&eval); result->media_queries(dynamic_cast(ex)); } for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { result->urls().push_back(imp->urls()[i]->perform(&eval)); } // all resources have been dropped for Input_Stubs // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {} return result; } Statement* Expand::operator()(Import_Stub* i) { // get parent node from call stack AST_Node* parent = call_stack.back(); if (parent && dynamic_cast(parent) == NULL) { error("Import directives may not be used within control directives or mixins.", i->pstate()); } // we don't seem to need that actually afterall Sass_Import_Entry import = sass_make_import( i->imp_path().c_str(), i->abs_path().c_str(), 0, 0 ); ctx.import_stack.push_back(import); const std::string& abs_path(i->resource().abs_path); append_block(ctx.sheets.at(abs_path).root); sass_delete_import(ctx.import_stack.back()); ctx.import_stack.pop_back(); return 0; } Statement* Expand::operator()(Warning* w) { // eval handles this too, because warnings may occur in functions w->perform(&eval); return 0; } Statement* Expand::operator()(Error* e) { // eval handles this too, because errors may occur in functions e->perform(&eval); return 0; } Statement* Expand::operator()(Debug* d) { // eval handles this too, because warnings may occur in functions d->perform(&eval); return 0; } Statement* Expand::operator()(Comment* c) { eval.is_in_comment = true; auto rv = SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast(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* Expand::operator()(If* i) { Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(i); if (*i->predicate()->perform(&eval)) { append_block(i->block()); } else { Block* alt = i->alternative(); if (alt) append_block(alt); } call_stack.pop_back(); env_stack.pop_back(); return 0; } // For does not create a new env scope // But iteration vars are reset afterwards Statement* Expand::operator()(For* f) { std::string variable(f->variable()); Expression* low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) { throw Exception::TypeMismatch(*low, "integer"); } Expression* high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) { throw Exception::TypeMismatch(*high, "integer"); } Number* sass_start = static_cast(low); Number* sass_end = static_cast(high); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { std::stringstream msg; msg << "Incompatible units: '" << sass_start->unit() << "' and '" << sass_end->unit() << "'."; error(msg.str(), low->pstate(), backtrace()); } double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(f); Number* it = SASS_MEMORY_NEW(env.mem, Number, low->pstate(), start, sass_end->unit()); env.set_local(variable, it); Block* body = f->block(); if (start < end) { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) { it->value(i); env.set_local(variable, it); append_block(body); } } else { if (f->is_inclusive()) --end; for (double i = start; i > end; --i) { it->value(i); env.set_local(variable, it); append_block(body); } } call_stack.pop_back(); env_stack.pop_back(); return 0; } // Eval does not create a new env scope // But iteration vars are reset afterwards Statement* Expand::operator()(Each* e) { std::vector variables(e->variables()); Expression* expr = e->list()->perform(&eval); Vectorized* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { map = static_cast(expr); } else if (Selector_List* ls = dynamic_cast(expr)) { Listize listize(ctx.mem); list = dynamic_cast(ls->perform(&listize)); } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA); *list << expr; } else { list = static_cast(expr); } // remember variables and then reset them Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(e); Block* body = e->block(); if (map) { for (auto key : map->keys()) { Expression* k = key->perform(&eval); Expression* v = map->at(key)->perform(&eval); if (variables.size() == 1) { List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE); *variable << k; *variable << v; env.set_local(variables[0], variable); } else { env.set_local(variables[0], k); env.set_local(variables[1], v); } append_block(body); } } else { // bool arglist = list->is_arglist(); if (list->length() == 1 && dynamic_cast(list)) { list = dynamic_cast*>(list); } for (size_t i = 0, L = list->length(); i < L; ++i) { Expression* e = (*list)[i]; // unwrap value if the expression is an argument if (Argument* arg = dynamic_cast(e)) e = arg->value(); // check if we got passed a list of args (investigate) if (List* scalars = dynamic_cast(e)) { if (variables.size() == 1) { Expression* var = scalars; // if (arglist) var = (*scalars)[0]; env.set_local(variables[0], var); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) { Expression* res = j >= scalars->length() ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()) : (*scalars)[j]->perform(&eval); env.set_local(variables[j], res); } } } else { if (variables.size() > 0) { env.set_local(variables[0], e); for (size_t j = 1, K = variables.size(); j < K; ++j) { Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()); env.set_local(variables[j], res); } } } append_block(body); } } call_stack.pop_back(); env_stack.pop_back(); return 0; } Statement* Expand::operator()(While* w) { Expression* pred = w->predicate(); Block* body = w->block(); Env env(environment(), true); env_stack.push_back(&env); call_stack.push_back(w); while (*pred->perform(&eval)) { append_block(body); } call_stack.pop_back(); env_stack.pop_back(); return 0; } Statement* Expand::operator()(Return* r) { error("@return may only be used within a function", r->pstate(), backtrace()); return 0; } void Expand::expand_selector_list(Selector* s, Selector_List* extender) { if (Selector_List* sl = dynamic_cast(s)) { for (Complex_Selector* complex_selector : sl->elements()) { Complex_Selector* tail = complex_selector; while (tail) { if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) { if (dynamic_cast(header) == NULL) continue; // skip all others std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace()); } tail = tail->tail(); } } } Selector_List* contextualized = dynamic_cast(s->perform(&eval)); if (contextualized == NULL) return; for (auto complex_sel : contextualized->elements()) { Complex_Selector* c = complex_sel; if (!c->head() || c->tail()) { std::string sel_str(contextualized->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace()); } Compound_Selector* placeholder = c->head(); placeholder->is_optional(s->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { Complex_Selector* sel = (*extender)[i]; if (!(sel->head() && sel->head()->length() > 0 && dynamic_cast((*sel->head())[0]))) { Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate()); hh->media_block((*extender)[i]->media_block()); Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate()); ssel->media_block((*extender)[i]->media_block()); if (sel->has_line_feed()) ssel->has_line_feed(true); Parent_Selector* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate()); ps->media_block((*extender)[i]->media_block()); *hh << ps; ssel->tail(sel); ssel->head(hh); sel = ssel; } // if (c->has_line_feed()) sel->has_line_feed(true); ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder)); } } } Statement* Expand::operator()(Extension* e) { if (Selector_List* extender = dynamic_cast(selector())) { selector_stack.push_back(0); Selector* s = e->selector(); expand_selector_list(s, extender); selector_stack.pop_back(); } return 0; } Statement* Expand::operator()(Definition* d) { Env* env = environment(); Definition* dd = SASS_MEMORY_NEW(ctx.mem, Definition, *d); env->local_frame()[d->name() + (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd; if (d->type() == Definition::FUNCTION && ( d->name() == "calc" || d->name() == "element" || d->name() == "expression" || d->name() == "url" )) { deprecated( "Naming a function \"" + d->name() + "\" is disallowed", "This name conflicts with an existing CSS function with special parse rules.", d->pstate() ); } // set the static link so we can have lexical scoping dd->environment(env); return 0; } Statement* Expand::operator()(Mixin_Call* c) { Env* env = environment(); std::string full_name(c->name() + "[m]"); if (!env->has(full_name)) { error("no mixin named " + c->name(), c->pstate(), backtrace()); } Definition* def = static_cast((*env)[full_name]); Block* body = def->block(); Parameters* params = def->parameters(); if (c->block() && c->name() != "@content" && !body->has_content()) { error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace()); } Arguments* args = static_cast(c->arguments() ->perform(&eval)); Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`"); backtrace_stack.push_back(&new_bt); Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) { // represent mixin content blocks as thunks/closures Definition* thunk = SASS_MEMORY_NEW(ctx.mem, Definition, c->pstate(), "@content", SASS_MEMORY_NEW(ctx.mem, Parameters, c->pstate()), 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); append_block(body); backtrace_stack.pop_back(); env_stack.pop_back(); return 0; } 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); } Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, c->pstate(), "@content", SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate())); Statement* stm = call->perform(this); if (block_stack.back()->is_root()) { selector_stack.pop_back(); } return stm; } // produce an error if something is not implemented inline Statement* Expand::fallback_impl(AST_Node* n) { std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name(); String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), err); error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace()); return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg); } // process and add to last block on stack 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* ith = (*b)[i]->perform(this); if (ith) *block_stack.back() << ith; } if (b->is_root()) call_stack.pop_back(); } } libsass-3.3.4/src/expand.hpp000066400000000000000000000040311267254216700157520ustar00rootroot00000000000000#ifndef SASS_EXPAND_H #define SASS_EXPAND_H #include #include "ast.hpp" #include "eval.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { class Listize; class Context; class Eval; typedef Environment Env; struct Backtrace; class Expand : public Operation_CRTP { public: Env* environment(); Context& context(); Selector_List* selector(); Backtrace* backtrace(); Context& ctx; Eval eval; // it's easier to work with vectors std::vector env_stack; std::vector block_stack; std::vector call_stack; std::vector property_stack; std::vector selector_stack; std::vectorbacktrace_stack; bool in_keyframes; Statement* fallback_impl(AST_Node* n); private: void expand_selector_list(Selector*, Selector_List* extender); public: Expand(Context&, Env*, Backtrace*); ~Expand() { } Statement* operator()(Block*); Statement* operator()(Ruleset*); Statement* operator()(Propset*); Statement* operator()(Media_Block*); Statement* operator()(Supports_Block*); Statement* operator()(At_Root_Block*); Statement* operator()(At_Rule*); Statement* operator()(Declaration*); Statement* operator()(Assignment*); Statement* operator()(Import*); Statement* operator()(Import_Stub*); Statement* operator()(Warning*); Statement* operator()(Error*); Statement* operator()(Debug*); Statement* operator()(Comment*); Statement* operator()(If*); Statement* operator()(For*); Statement* operator()(Each*); Statement* operator()(While*); Statement* operator()(Return*); Statement* operator()(Extension*); Statement* operator()(Definition*); Statement* operator()(Mixin_Call*); Statement* operator()(Content*); template Statement* fallback(U x) { return fallback_impl(x); } void append_block(Block*); }; } #endif libsass-3.3.4/src/extend.cpp000066400000000000000000002231561267254216700157700ustar00rootroot00000000000000#include "sass.hpp" #include "extend.hpp" #include "context.hpp" #include "backtrace.hpp" #include "paths.hpp" #include "parser.hpp" #include "node.hpp" #include "sass_util.hpp" #include "debug.hpp" #include #include #include /* 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 { typedef std::pair ExtensionPair; typedef std::vector SubsetMapEntries; #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 typedef std::pair SelsNewSeqPair; typedef std::vector SelsNewSeqPairCollection; // Print a string representation of a Compound_Selector static void printCompoundSelector(Compound_Selector* 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* 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* 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(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) { if (message) { std::cerr << message; } bool first = true; std::cerr << "["; for(SelsNewSeqPair& pair : collection) { if (first) { first = false; } else { std::cerr << ", "; } std::cerr << "["; Compound_Selector* pSels = pair.first; Complex_Selector* pNewSelector = pair.second; std::cerr << "[" << *pSels << "], "; printComplexSelector(pNewSelector, NULL, false); } std::cerr << "]"; if (newline) { std::cerr << std::endl; } } // Print a string representation of a SourcesSet static void printSourcesSet(SourcesSet& sources, Context& ctx, 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 SourceStrings; SourceStrings sourceStrings; for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) { Complex_Selector* pSource = *iterator; std::stringstream sstream; sstream << complexSelectorToNode(pSource, ctx); sourceStrings.push_back(sstream.str()); } // Sort to get consistent output std::sort(sourceStrings.begin(), sourceStrings.end()); std::cerr << "SourcesSet["; 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, SubsetMapEntries& entries) { os << "SUBSET_MAP_ENTRIES["; for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) { Complex_Selector* pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge) Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend 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* pOne, Complex_Selector* pTwo, Context& ctx) { // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick... Position noPosition(-1, -1, -1); Type_Selector fakeParent(ParserState("[FAKE]"), "temp"); Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/); fakeHead.elements().push_back(&fakeParent); Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); 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, Context& ctx) { for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { Node& child = *iter; out.push_back(nodeToComplexSelector(child, ctx)); } } Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque, Context& ctx) { Node result = Node::createCollection(); for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) { Complex_Selector* pChild = *iter; result.collection()->push_back(complexSelectorToNode(pChild, ctx)); } return result; } class LcsCollectionComparator { public: LcsCollectionComparator(Context& ctx) : mCtx(ctx) {} Context& mCtx; bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& 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 (selectors_equal(*pOne, *pTwo, true /*simpleSelectorOrderDependent*/)) { pOut = pOne; return true; } if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) { return false; } if (parentSuperselector(pOne, pTwo, mCtx)) { pOut = pTwo; return true; } if (parentSuperselector(pTwo, pOne, mCtx)) { 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* pCompareOut = NULL; 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(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* pCompareOut = NULL; 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, Context& ctx, 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(x.size()) - 1, static_cast(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. */ static Node trim(Node& seqses, Context& ctx, 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* pSeq1 = nodeToComplexSelector(seq1, ctx); // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code // for a good description of sources. // // 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; SourcesSet sources = pSeq1->sources(); DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1) DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIM SOURCES: ")) for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) { const Complex_Selector* const pCurrentSelector = *sourcesSetIterator; 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* pSeq2 = nodeToComplexSelector(seq2, ctx); 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, Context& ctx) { // TODO: figure out a better way to create a Complex_Selector from scratch // TODO: There's got to be a better way. This got ugly quick... Position noPosition(-1, -1, -1); Type_Selector fakeParent(ParserState("[FAKE]"), "temp"); Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/); fakeHead.elements().push_back(&fakeParent); Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); Complex_Selector* pOneWithFakeParent = nodeToComplexSelector(one, ctx); pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx); pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent); } class ParentSuperselectorChunker { public: ParentSuperselectorChunker(Node& lcs, Context& ctx) : mLcs(lcs), mCtx(ctx) {} Node& mLcs; Context& mCtx; bool operator()(const Node& seq) const { // {|s| parent_superselector?(s.first, lcs.first)} if (seq.collection()->size() == 0) return false; return parentSuperselector(seq.collection()->front(), mLcs.collection()->front(), mCtx); } }; 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] 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 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 (!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, Context& ctx) { 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, Context& ctx) { 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, ctx); 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, Context& ctx, 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, ctx); // 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* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?) Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx); 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, ctx)); 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 tildeOp = op1; Node plusSel = sel2; Node plusOp = op2; if (op1.combinator() != Complex_Selector::PRECEDES) { tildeSel = sel2; tildeOp = op2; 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* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?) Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx); 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, ctx)); 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* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?) Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx); 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, ctx)); DEBUG_PRINTLN(ALL, "RESULT: " << res) } else { return Node::createNil(); } return mergeFinalOps(seq1, seq2, ctx, 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, ctx, 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, ctx, 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 Extend::subweave(Node& one, Node& two, Context& ctx) { // 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, ctx); if (init.isNil()) { return Node::createNil(); } DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init) Node res = Node::createCollection(); Node fin = mergeFinalOps(seq1, seq2, ctx, 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, ctx); DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1) Node groupSeq2 = groupSelectors(seq2, ctx); DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2) ComplexSelectorDeque groupSeq1Converted; nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx); ComplexSelectorDeque groupSeq2Converted; nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx); ComplexSelectorDeque out; LcsCollectionComparator collectionComparator(ctx); lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out); Node seqLcs = complexSelectorDequeToNode(out, ctx); 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, ctx); 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, ctx); 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, ctx); } 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, Context& ctx) { Node out = Node::createCollection(); // Check for the simple cases if (one.isNil()) { out.collection()->push_back(two.clone(ctx)); } else if (two.isNil()) { out.collection()->push_back(one.clone(ctx)); } else { // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B // See https://gist.github.com/nex3/7609394 for details. Node firstPerm = one.clone(ctx); Node twoCloned = two.clone(ctx); firstPerm.plus(twoCloned); out.collection()->push_back(firstPerm); Node secondPerm = two.clone(ctx); Node oneCloned = one.clone(ctx); 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 */ static Node weave(Node& path, Context& ctx) { 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().clone(ctx); afters.collection()->pop_front(); DEBUG_PRINTLN(WEAVE, "CURRENT: " << current) 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 = Extend::subweave(before, current, ctx); 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); tempResult.collection()->push_back(toPush); } } befores = tempResult; } return befores; } // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively // call extendComplexSelector again. static Node extendComplexSelector( Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map, std::set seen, bool isReplace, bool isOriginal); /* 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 class GroupByToAFunctor { public: KeyType operator()(ExtensionPair& extPair) const { Complex_Selector* pSelector = extPair.first; return *pSelector; } }; static Node extendCompoundSelector( Compound_Selector* pSelector, Context& ctx, ExtensionSubsetMap& subset_map, std::set seen, bool isReplace) { DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) // TODO: Ruby has another loop here to skip certain members? Node extendedSelectors = Node::createCollection(); // extendedSelectors.got_line_feed = true; SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec()); typedef std::vector > > GroupedByToAResult; GroupByToAFunctor extPairKeyFunctor; GroupedByToAResult arr; group_by_to_a(entries, extPairKeyFunctor, arr); typedef std::pair SelsNewSeqPair; typedef std::vector SelsNewSeqPairCollection; SelsNewSeqPairCollection holder; for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) { std::pair >& groupedPair = *groupedIter; Complex_Selector& seq = groupedPair.first; std::vector& group = groupedPair.second; DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) Compound_Selector* pSels = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate()); for (std::vector::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) { ExtensionPair& pair = *groupIter; Compound_Selector* pCompound = pair.second; for (size_t index = 0; index < pCompound->length(); index++) { Simple_Selector* pSimpleSelector = (*pCompound)[index]; (*pSels) << pSimpleSelector; pCompound->extended(true); } } DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge) Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL? // RUBY: self_without_sel = Sass::Util.array_minus(members, sels) Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx); DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->last()->head(); Compound_Selector* pUnifiedSelector = NULL; if (!pInnermostCompoundSelector) { pInnermostCompoundSelector = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate()); } pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx); 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* pNewSelector = pExtComplexSelector->cloneFully(ctx); // ->first(); Complex_Selector* pNewInnerMost = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); Complex_Selector::Combinator combinator = pNewSelector->clear_innermost(); pNewSelector->set_innermost(pNewInnerMost, combinator); #ifdef DEBUG SourcesSet debugSet; debugSet = pNewSelector->sources(); if (debugSet.size() > 0) { throw "The new selector should start with no sources. Something needs to be cloned to fix this."; } debugSet = pExtComplexSelector->sources(); if (debugSet.size() > 0) { throw "The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this."; } #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, ctx)) DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, ctx, "SOURCES NEW SEQ BEGIN: ")) SourcesSet newSourcesSet = pSelector->sources(); DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES THIS EXTEND: ")) newSourcesSet.insert(pExtComplexSelector); DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES WITH NEW SOURCE: ")) // RUBY: new_seq.add_sources!(sources + [seq]) pNewSelector->addSources(newSourcesSet, ctx); DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet newSet = pNewSelector->sources(); printSourcesSet(newSet, ctx, "SOURCES ON NEW SELECTOR AFTER ADD: ")) DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: ")) if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);; holder.push_back(std::make_pair(pSels, pNewSelector)); } for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) { SelsNewSeqPair& pair = *holderIter; Compound_Selector* pSels = pair.first; Complex_Selector* pNewSelector = pair.second; // RUBY??: next [] if seen.include?(sels) if (seen.find(*pSels) != seen.end()) { continue; } std::set recurseSeen(seen); recurseSeen.insert(*pSels); DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND 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: " << extendedSelectors) // DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)); if (!extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)) { // DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR") extendedSelectors.collection()->push_back(newSelector); } } } DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: ")) return extendedSelectors; } static bool complexSelectorHasExtension( Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map) { bool hasExtension = false; Complex_Selector* pIter = pComplexSelector; while (!hasExtension && pIter) { Compound_Selector* pHead = pIter->head(); if (pHead) { for (Simple_Selector* pSimple : *pHead) { if (Wrapped_Selector* ws = dynamic_cast(pSimple)) { if (Selector_List* sl = dynamic_cast(ws->selector())) { for (Complex_Selector* cs : sl->elements()) { while (cs) { if (complexSelectorHasExtension(cs, ctx, subset_map)) { hasExtension = true; break; } cs = cs->tail(); } } } } } SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec()); for (ExtensionPair 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 (pComplexSelector->media_block() && ext.second->media_block()->media_queries() && pComplexSelector->media_block()->media_queries() ) { std::string query_left(ext.second->media_block()->media_queries()->to_string(ctx.c_options)); std::string query_right(pComplexSelector->media_block()->media_queries()->to_string(ctx.c_options)); 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(ctx.c_options) << "\""; err << " on line " << pstate.line+1 << " of " << rel_path << "\n"; error(err.str(), pComplexSelector->pstate()); } 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) */ static Node extendComplexSelector( Complex_Selector* pComplexSelector, Context& ctx, ExtensionSubsetMap& subset_map, std::set seen, bool isReplace, bool isOriginal) { Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) Node extendedNotExpanded = Node::createCollection(); for (NodeDeque::iterator complexSelIter = complexSelector.collection()->begin(), complexSelIterEnd = complexSelector.collection()->end(); complexSelIter != complexSelIterEnd; ++complexSelIter) { Node& sseqOrOp = *complexSelIter; 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); extendedNotExpanded.collection()->push_back(outer); continue; } Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head(); // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen) Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen, isReplace); if (sseqOrOp.got_line_feed) extended.got_line_feed = true; DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence // due to the member mapping: choices = extended.map {|seq| seq.members} Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector(); // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder? if (isOriginal && !pComplexSelector->has_placeholder()) { SourcesSet srcset; srcset.insert(pComplexSelector); pJustCurrentCompoundSelector->addSources(srcset, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector) } bool isSuperselector = false; for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end(); iterator != endIterator; ++iterator) { Node& childNode = *iterator; Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx); if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) { isSuperselector = true; break; } } if (!isSuperselector) { if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed); extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx)); } DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended) // Aggregate our current extensions extendedNotExpanded.collection()->push_back(extended); } DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << extendedNotExpanded) // Ruby Equivalent: paths Node paths = Sass::paths(extendedNotExpanded, ctx); DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths) // Ruby Equivalent: weave Node weaves = Node::createCollection(); for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) { Node& path = *pathsIter; Node weaved = weave(path, ctx); weaved.got_line_feed = path.got_line_feed; weaves.collection()->push_back(weaved); } DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves) // Ruby Equivalent: trim Node trimmed = trim(weaves, ctx, isReplace); DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed) // Ruby Equivalent: flatten Node extendedSelectors = flatten(trimmed, ctx, 1); DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors) DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector) return extendedSelectors; } /* This is the equivalent of ruby's CommaSequence.do_extend. */ Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) { Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length()); extendedSomething = false; for (size_t index = 0, length = pSelectorList->length(); index < length; index++) { Complex_Selector* pSelector = (*pSelectorList)[index]; // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that. // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to // run through the extend code (which does a data model transformation), check if there is anything to extend before doing // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps // when debugging). if (!complexSelectorHasExtension(pSelector, ctx, subset_map)) { *pNewSelectors << pSelector; continue; } extendedSomething = true; std::set seen; Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true); if (!pSelector->has_placeholder()) { if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { *pNewSelectors << pSelector; } } for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { // When it is a replace, skip the first one, unless there is only one if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue; Node& childNode = *iterator; *pNewSelectors << nodeToComplexSelector(childNode, ctx); } } for (Complex_Selector* cs : *pNewSelectors) { while (cs) { if (cs->head()) { for (Simple_Selector* ss : *cs->head()) { if (Wrapped_Selector* ws = dynamic_cast(ss)) { if (Selector_List* sl = dynamic_cast(ws->selector())) { bool extended = false; ws->selector(extendSelectorList(sl, ctx, subset_map, false, extended)); } } } } cs = cs->tail(); } } return pNewSelectors; } bool shouldExtendBlock(Block* 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* stm = (*b)[i]; if (typeid(*stm) == typeid(Ruleset)) { // 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. template static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subset_map) { DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast(pObject->selector())->to_string(ctx.c_options)) // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions // of doing so, so for now, let's just not extend things that won't be output later. if (!shouldExtendBlock(pObject->block())) { DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT") return; } bool extendedSomething = false; Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subset_map, false, extendedSomething); if (extendedSomething && pNewSelectorList) { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->to_string(ctx.c_options)) DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string(ctx.c_options)) pNewSelectorList->remove_parent_selectors(); pObject->selector(pNewSelectorList); } else { DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") } } Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm) : ctx(ctx), subset_map(ssm) { } void Extend::operator()(Block* b) { for (size_t i = 0, L = b->length(); i < L; ++i) { (*b)[i]->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* sel = it.first ? it.first->first() : NULL; Compound_Selector* ext = it.second ? it.second : NULL; if (ext && (ext->extended() || ext->is_optional())) continue; std::string str_sel(sel->to_string({ NESTED, 5 })); std::string str_ext(ext->to_string({ NESTED, 5 })); // debug_ast(sel, "sel: "); // debug_ast(ext, "ext: "); error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n" "The selector \"" + str_ext + "\" was not found.\n" "Use \"@extend " + str_ext + " !optional\" if the" " extend should be able to fail.", ext->pstate()); } } } void Extend::operator()(Ruleset* pRuleset) { extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); pRuleset->block()->perform(this); } void Extend::operator()(Supports_Block* pFeatureBlock) { pFeatureBlock->block()->perform(this); } void Extend::operator()(Media_Block* pMediaBlock) { pMediaBlock->block()->perform(this); } void Extend::operator()(At_Rule* a) { // Selector_List* ls = dynamic_cast(a->selector()); // selector_stack.push_back(ls); if (a->block()) a->block()->perform(this); // exp.selector_stack.pop_back(); } } libsass-3.3.4/src/extend.hpp000066400000000000000000000017621267254216700157720ustar00rootroot00000000000000#ifndef SASS_EXTEND_H #define SASS_EXTEND_H #include #include "ast.hpp" #include "operation.hpp" #include "subset_map.hpp" namespace Sass { class Context; class Node; typedef Subset_Map > ExtensionSubsetMap; class Extend : public Operation_CRTP { Context& ctx; ExtensionSubsetMap& subset_map; void fallback_impl(AST_Node* n) { } public: static Node subweave(Node& one, Node& two, Context& ctx); static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething); Extend(Context&, ExtensionSubsetMap&); ~Extend() { } void operator()(Block*); void operator()(Ruleset*); void operator()(Supports_Block*); void operator()(Media_Block*); void operator()(At_Rule*); template void fallback(U x) { return fallback_impl(x); } }; } #endif libsass-3.3.4/src/file.cpp000066400000000000000000000333321267254216700154130ustar00rootroot00000000000000#ifdef _WIN32 # ifdef __MINGW32__ # ifndef off64_t # define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */ # endif # endif # include # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #else # include #endif #include "sass.hpp" #include #include #include #include #include #include #include "file.hpp" #include "context.hpp" #include "prelexer.hpp" #include "utf8_string.hpp" #include "sass2scss.h" #ifdef _WIN32 # include # ifdef _MSC_VER # include inline static std::string wstring_to_string(const std::wstring& wstr) { std::wstring_convert, wchar_t> wchar_converter; return wchar_converter.to_bytes(wstr); } # else // mingw(/gcc) does not support C++11's codecvt yet. inline static std::string wstring_to_string(const std::wstring &wstr) { int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string strTo(size_needed, 0); WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); return strTo; } # endif #endif namespace Sass { namespace File { // return the current directory // always with forward slashes std::string get_cwd() { const size_t wd_len = 1024; #ifndef _WIN32 char wd[wd_len]; std::string cwd = getcwd(wd, wd_len); #else wchar_t wd[wd_len]; std::string cwd = wstring_to_string(_wgetcwd(wd, wd_len)); //convert backslashes to forward slashes replace(cwd.begin(), cwd.end(), '\\', '/'); #endif if (cwd[cwd.length() - 1] != '/') cwd += '/'; return cwd; } // test if path exists and is a file bool file_exists(const std::string& path) { #ifdef _WIN32 std::wstring wpath = UTF_8::convert_to_utf16(path); DWORD dwAttrib = GetFileAttributesW(wpath.c_str()); return (dwAttrib != INVALID_FILE_ATTRIBUTES && (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))); #else struct stat st_buf; return (stat (path.c_str(), &st_buf) == 0) && (!S_ISDIR (st_buf.st_mode)); #endif } // return if given path is absolute // works with *nix and windows paths bool is_absolute_path(const std::string& path) { #ifdef _WIN32 if (path.length() >= 2 && 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])) { // skip over all alphanumeric characters while (path[i] && Prelexer::is_alnum(path[i])) ++i; i = i && path[i] == ':' ? i + 1 : 0; } return path[i] == '/'; } // helper function to find the last directory seperator inline size_t find_last_folder_separator(const std::string& path, size_t limit = std::string::npos) { size_t pos = std::string::npos; size_t pos_p = path.find_last_of('/', limit); #ifdef _WIN32 size_t pos_w = path.find_last_of('\\', limit); #else size_t pos_w = std::string::npos; #endif if (pos_p != std::string::npos && pos_w != std::string::npos) { pos = std::max(pos_p, pos_w); } else if (pos_p != std::string::npos) { pos = pos_p; } else { pos = pos_w; } return pos; } // return only the directory part of path std::string dir_name(const std::string& path) { size_t pos = find_last_folder_separator(path); if (pos == std::string::npos) return ""; else return path.substr(0, pos+1); } // return only the filename part of path std::string base_name(const std::string& path) { size_t pos = find_last_folder_separator(path); if (pos == std::string::npos) return path; else return path.substr(pos+1); } // do a logical clean up of the path // no physical check on the filesystem std::string make_canonical_path (std::string path) { // declarations size_t pos; #ifdef _WIN32 //convert backslashes to forward slashes replace(path.begin(), path.end(), '\\', '/'); #endif pos = 0; // remove all self references inside the path string while((pos = path.find("/./", pos)) != std::string::npos) path.erase(pos, 2); pos = 0; // remove all leading and trailing self references 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); size_t proto = 0; // check if we have a protocol if (path[proto] && Prelexer::is_alpha(path[proto])) { // skip over all alphanumeric characters while (path[proto] && Prelexer::is_alnum(path[proto++])) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } // then skip over start slashes while (path[proto++] == '/') {} pos = proto; // collapse multiple delimiters into a single one while((pos = path.find("//", pos)) != std::string::npos) path.erase(pos, 1); return path; } // join two path segments cleanly together // but only if right side is not absolute yet std::string join_paths(std::string l, std::string r) { #ifdef _WIN32 // convert Windows backslashes to URL forward slashes replace(l.begin(), l.end(), '\\', '/'); replace(r.begin(), r.end(), '\\', '/'); #endif if (l.empty()) return r; if (r.empty()) return l; if (is_absolute_path(r)) return r; if (l[l.length()-1] != '/') l += '/'; while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) { size_t L = l.length(), pos = find_last_folder_separator(l, L - 2); bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\'); bool is_self = pos + 3 == L && (l[pos+1] == '.'); if (!is_self && !is_slash) r = r.substr(3); l = l.substr(0, pos == std::string::npos ? pos : pos + 1); } return l + r; } std::string path_for_console(const std::string& rel_path, const std::string& abs_path, const std::string& orig_path) { // magic algorith goes here!! // if the file is outside this directory show the absolute path if (rel_path.substr(0, 3) == "../") { return orig_path; } // this seems to work most of the time return abs_path == orig_path ? abs_path : rel_path; } // create an absolute path by resolving relative paths with cwd std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd) { return make_canonical_path(join_paths(join_paths(cwd, base), path)); } // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute std::string abs2rel(const std::string& path, const std::string& base, const std::string& cwd) { std::string abs_path = rel2abs(path, cwd); std::string abs_base = rel2abs(base, cwd); size_t proto = 0; // check if we have a protocol if (path[proto] && Prelexer::is_alpha(path[proto])) { // skip over all alphanumeric characters while (path[proto] && Prelexer::is_alnum(path[proto++])) {} // then skip over the mandatory colon if (proto && path[proto] == ':') ++ proto; } // distinguish between windows absolute paths and valid protocols // we assume that protocols must at least have two chars to be valid if (proto && path[proto++] == '/' && proto > 3) return path; #ifdef _WIN32 // absolute link must have a drive letter, and we know that we // can only create relative links if both are on the same drive if (abs_base[0] != abs_path[0]) return abs_path; #endif std::string stripped_uri = ""; std::string stripped_base = ""; size_t index = 0; size_t minSize = std::min(abs_path.size(), abs_base.size()); for (size_t i = 0; i < minSize; ++i) { #ifdef FS_CASE_SENSITIVE if (abs_path[i] != abs_base[i]) break; #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; #endif if (abs_path[i] == '/') index = i + 1; } for (size_t i = index; i < abs_path.size(); ++i) { stripped_uri += abs_path[i]; } for (size_t i = index; i < abs_base.size(); ++i) { stripped_base += abs_base[i]; } size_t left = 0; size_t directories = 0; for (size_t right = 0; right < stripped_base.size(); ++right) { if (stripped_base[right] == '/') { if (stripped_base.substr(left, 2) != "..") { ++directories; } else if (directories > 1) { --directories; } else { directories = 0; } left = right + 1; } } std::string result = ""; for (size_t i = 0; i < directories; ++i) { result += "../"; } result += stripped_uri; return result; } // Resolution order for ambiguous imports: // (1) filename as given // (2) underscore + given // (3) underscore + given + extension // (4) given + extension std::vector resolve_includes(const std::string& root, const std::string& file) { std::string filename = join_paths(root, file); // supported extensions const std::vector exts = { ".scss", ".sass", ".css" }; // split the filename std::string base(dir_name(file)); std::string name(base_name(file)); std::vector includes; // create full path (maybe relative) std::string rel_path(join_paths(base, name)); std::string abs_path(join_paths(root, rel_path)); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test variation with underscore rel_path = join_paths(base, "_" + name); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); // next test exts plus underscore for(auto ext : exts) { rel_path = join_paths(base, "_" + name + ext); abs_path = join_paths(root, rel_path); if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path }); } // next test plain name with exts for(auto ext : exts) { rel_path = join_paths(base, name + 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; } // helper function to resolve a filename std::string find_file(const std::string& file, const std::vector paths) { // search in every include path for a match for (size_t i = 0, S = paths.size(); i < S; ++i) { std::vector resolved(resolve_includes(paths[i], file)); if (resolved.size()) return resolved[0].abs_path; } // nothing found return std::string(""); } // inc paths can be directly passed from C code std::string find_file(const std::string& file, const char* paths[]) { if (paths == 0) return std::string(""); std::vector includes(0); // includes.push_back("."); const char** it = paths; while (it && *it) { includes.push_back(*it); ++it; } return find_file(file, includes); } // try to load the given filename // returned memory must be freed // will auto convert .sass files char* read_file(const std::string& path) { #ifdef _WIN32 BYTE* pBuffer; DWORD dwBytes; // windows unicode filepaths are encoded in utf16 std::wstring wpath = UTF_8::convert_to_utf16(path); HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; DWORD dwFileLength = GetFileSize(hFile, NULL); if (dwFileLength == INVALID_FILE_SIZE) return 0; // allocate an extra byte for the null char pBuffer = (BYTE*)malloc((dwFileLength+1)*sizeof(BYTE)); ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL); pBuffer[dwFileLength] = '\0'; CloseHandle(hFile); // just convert from unsigned char* char* contents = (char*) pBuffer; #else 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 contents = (char*) malloc((size+1)*sizeof(char)); file.seekg(0, std::ios::beg); file.read(contents, size); contents[size] = '\0'; file.close(); } #endif std::string extension; if (path.length() > 5) { extension = path.substr(path.length() - 5, 5); } for(size_t i=0; i #include namespace Sass { class Block; class Context; namespace File { // return the current directory // always with forward slashes std::string get_cwd(); // test if path exists and is a file bool file_exists(const std::string& file); // return if given path is absolute // works with *nix and windows paths bool is_absolute_path(const std::string& path); // return only the directory part of path std::string dir_name(const std::string& path); // return only the filename part of path std::string base_name(const std::string&); // do a locigal clean up of the path // no physical check on the filesystem std::string make_canonical_path (std::string path); // join two path segments cleanly together // but only if right side is not absolute yet std::string join_paths(std::string root, std::string name); // if the relative path is outside of the cwd we want want to // show the absolute path in console messages std::string path_for_console(const std::string& rel_path, const std::string& abs_path, const std::string& orig_path); // create an absolute path by resolving relative paths with cwd std::string rel2abs(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); // create a path that is relative to the given base directory // path and base will first be resolved against cwd to make them absolute std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd()); // helper function to resolve a filename std::string find_file(const std::string& file, const std::vector paths); // inc paths can be directly passed from C code std::string find_file(const std::string& file, const char** paths); // try to load the given filename // returned memory must be freed // will auto convert .sass files char* read_file(const std::string& file); } // requested import class Importer { public: // requested import path std::string imp_path; // parent context path std::string ctx_path; // base derived from context path // this really just acts as a cache std::string base_path; public: Importer(std::string imp_path, std::string ctx_path) : imp_path(File::make_canonical_path(imp_path)), ctx_path(File::make_canonical_path(ctx_path)), base_path(File::dir_name(ctx_path)) { } }; // a resolved include (final import) class Include : public Importer { public: // resolved absolute path std::string abs_path; public: Include(const Importer& imp, std::string abs_path) : Importer(imp), abs_path(abs_path) { } }; // a loaded resource class Resource { public: // the file contents char* contents; // conected 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* root; public: StyleSheet(const Resource& res, Block* root) : Resource(res), root(root) { } }; namespace File { std::vector resolve_includes(const std::string& root, const std::string& file); } } #endif libsass-3.3.4/src/functions.cpp000066400000000000000000002127261267254216700165120ustar00rootroot00000000000000#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 "utf8_string.hpp" #include "sass/base.h" #include "utf8.h" #include #include #include #include #include #include #include #include #include #include #ifdef __MINGW32__ #include "windows.h" #include "wincrypt.h" #endif #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, backtrace) #define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace) #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx) namespace Sass { using std::stringstream; using std::endl; Definition* make_native_function(Signature sig, Native_Function func, Context& ctx) { Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]")); sig_parser.lex(); std::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters* params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(ctx.mem, 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, 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* params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(ctx.mem, 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, Backtrace* backtrace) { try { throw; } catch (utf8::invalid_code_point) { std::string msg("utf8::invalid_code_point"); error(msg, pstate, backtrace); } catch (utf8::not_enough_room) { std::string msg("utf8::not_enough_room"); error(msg, pstate, backtrace); } catch (utf8::invalid_utf8) { std::string msg("utf8::invalid_utf8"); error(msg, pstate, backtrace); } catch (...) { throw; } } template T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly! T* val = dynamic_cast(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, backtrace); } return val; } Map* get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { // Minimal error handling -- the expectation is that built-ins will be written correctly! Map* val = dynamic_cast(env[argname]); if (val) return val; List* lval = dynamic_cast(env[argname]); if (lval && lval->length() == 0) return SASS_MEMORY_NEW(ctx.mem, Map, pstate, 0); // fallback on get_arg for error handling val = get_arg(argname, env, sig, pstate, backtrace); return val; } Number* get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly! Number* val = get_arg(argname, env, sig, pstate, backtrace); double v = val->value(); if (!(lo <= v && v <= hi)) { std::stringstream msg; msg << "argument `" << argname << "` of `" << sig << "` must be between "; msg << lo << " and " << hi; error(msg.str(), pstate, backtrace); } return val; } #define ARGSEL(argname, seltype, contextualize) get_arg_sel(argname, env, sig, pstate, backtrace, ctx) template T* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx); template <> Selector_List* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { Expression* exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), pstate); } if (String_Constant* str =dynamic_cast(exp)) { str->quote_mark(0); } std::string exp_src = exp->to_string(ctx.c_options) + "{"; return Parser::parse_selector(exp_src.c_str(), ctx); } template <> Complex_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { Expression* exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a valid selector: it must be a string,\n"; msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), pstate); } if (String_Constant* str =dynamic_cast(exp)) { str->quote_mark(0); } std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); return (sel_list->length() > 0) ? sel_list->first() : 0; } template <> Compound_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { Expression* 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); } if (String_Constant* str =dynamic_cast(exp)) { str->quote_mark(0); } std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0; } #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(GetSeed())); // features static std::set features { "global-variable-shadowing", "extend-selector-pseudoclass", "at-error", "units-level-3" }; //////////////// // RGB FUNCTIONS //////////////// inline double color_num(Number* n) { if (n->unit() == "%") { return std::min(std::max(n->value() * 255 / 100.0, 0.0), 255.0); } else { return std::min(std::max(n->value(), 0.0), 255.0); } } inline double alpha_num(Number* n) { if (n->unit() == "%") { return std::min(std::max(n->value(), 0.0), 100.0); } else { return std::min(std::max(n->value(), 0.0), 1.0); } } Signature rgb_sig = "rgb($red, $green, $blue)"; BUILT_IN(rgb) { return SASS_MEMORY_NEW(ctx.mem, Color, pstate, color_num(ARG("$red", Number)), color_num(ARG("$green", Number)), color_num(ARG("$blue", Number))); } Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; BUILT_IN(rgba_4) { return SASS_MEMORY_NEW(ctx.mem, Color, pstate, color_num(ARG("$red", Number)), color_num(ARG("$green", Number)), color_num(ARG("$blue", Number)), alpha_num(ARG("$alpha", Number))); } Signature rgba_2_sig = "rgba($color, $alpha)"; BUILT_IN(rgba_2) { Color* c_arg = ARG("$color", Color); Color* new_c = SASS_MEMORY_NEW(ctx.mem, Color, *c_arg); new_c->a(alpha_num(ARG("$alpha", Number))); new_c->disp(""); return new_c; } Signature red_sig = "red($color)"; BUILT_IN(red) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->r()); } Signature green_sig = "green($color)"; BUILT_IN(green) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->g()); } Signature blue_sig = "blue($color)"; BUILT_IN(blue) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->b()); } Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; BUILT_IN(mix) { Color* color1 = ARG("$color-1", Color); Color* color2 = ARG("$color-2", Color); Number* weight = ARGR("$weight", Number, 0, 100); double p = weight->value()/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(ctx.mem, 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)); } //////////////// // 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, s = 0, l = (max + min) / 2.0; if (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* 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; // 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(ctx.mem, Color, pstate, r, g, b, a); } Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; BUILT_IN(hsl) { return hsla_impl(ARG("$hue", Number)->value(), ARG("$saturation", Number)->value(), ARG("$lightness", Number)->value(), 1.0, ctx, pstate); } Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; BUILT_IN(hsla) { return hsla_impl(ARG("$hue", Number)->value(), ARG("$saturation", Number)->value(), ARG("$lightness", Number)->value(), ARG("$alpha", Number)->value(), ctx, pstate); } Signature hue_sig = "hue($color)"; BUILT_IN(hue) { Color* rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.h, "deg"); } Signature saturation_sig = "saturation($color)"; BUILT_IN(saturation) { Color* rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.s, "%"); } Signature lightness_sig = "lightness($color)"; BUILT_IN(lightness) { Color* rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, hsl_color.l, "%"); } Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; BUILT_IN(adjust_hue) { Color* rgb_color = ARG("$color", Color); Number* degrees = ARG("$degrees", Number); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); return hsla_impl(hsl_color.h + degrees->value(), hsl_color.s, hsl_color.l, rgb_color->a(), ctx, pstate); } Signature lighten_sig = "lighten($color, $amount)"; BUILT_IN(lighten) { Color* rgb_color = ARG("$color", Color); Number* amount = ARGR("$amount", Number, 0, 100); 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->value(), rgb_color->a(), ctx, pstate); } Signature darken_sig = "darken($color, $amount)"; BUILT_IN(darken) { Color* rgb_color = ARG("$color", Color); Number* amount = ARGR("$amount", Number, 0, 100); 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->value(), rgb_color->a(), ctx, pstate); } Signature saturate_sig = "saturate($color, $amount: false)"; BUILT_IN(saturate) { // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$amount"]); if (!amount) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); } ARGR("$amount", Number, 0, 100); Color* rgb_color = ARG("$color", Color); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); double hslcolorS = hsl_color.s + amount->value(); // 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* rgb_color = ARG("$color", Color); Number* amount = ARGR("$amount", Number, 0, 100); HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); double hslcolorS = hsl_color.s - amount->value(); // 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* amount = dynamic_cast(env["$color"]); if (amount) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); } Color* 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* 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)"; BUILT_IN(invert) { // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); } Color* rgb_color = ARG("$color", Color); return SASS_MEMORY_NEW(ctx.mem, Color, pstate, 255 - rgb_color->r(), 255 - rgb_color->g(), 255 - rgb_color->b(), rgb_color->a()); } //////////////////// // OPACITY FUNCTIONS //////////////////// Signature alpha_sig = "alpha($color)"; Signature opacity_sig = "opacity($color)"; BUILT_IN(alpha) { String_Constant* ie_kwd = dynamic_cast(env["$color"]); if (ie_kwd) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")"); } // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); } return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->a()); } Signature opacify_sig = "opacify($color, $amount)"; Signature fade_in_sig = "fade-in($color, $amount)"; BUILT_IN(opacify) { Color* color = ARG("$color", Color); double amount = ARGR("$amount", Number, 0, 1)->value(); double alpha = std::min(color->a() + amount, 1.0); return SASS_MEMORY_NEW(ctx.mem, 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* color = ARG("$color", Color); double amount = ARGR("$amount", Number, 0, 1)->value(); double alpha = std::max(color->a() - amount, 0.0); return SASS_MEMORY_NEW(ctx.mem, Color, 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* color = ARG("$color", Color); Number* r = dynamic_cast(env["$red"]); Number* g = dynamic_cast(env["$green"]); Number* b = dynamic_cast(env["$blue"]); Number* h = dynamic_cast(env["$hue"]); Number* s = dynamic_cast(env["$saturation"]); Number* l = dynamic_cast(env["$lightness"]); Number* a = dynamic_cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("cannot specify both RGB and HSL values for `adjust-color`", pstate); } if (rgb) { double rr = r ? ARGR("$red", Number, -255, 255)->value() : 0; double gg = g ? ARGR("$green", Number, -255, 255)->value() : 0; double bb = b ? ARGR("$blue", Number, -255, 255)->value() : 0; double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0; return SASS_MEMORY_NEW(ctx.mem, Color, 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 ? ARGR("$saturation", Number, -100, 100)->value() : 0; double ll = l ? ARGR("$lightness", Number, -100, 100)->value() : 0; double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0; 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(ctx.mem, Color, pstate, color->r(), color->g(), color->b(), color->a() + (a ? a->value() : 0)); } error("not enough arguments for `adjust-color`", pstate); // 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* color = ARG("$color", Color); Number* r = dynamic_cast(env["$red"]); Number* g = dynamic_cast(env["$green"]); Number* b = dynamic_cast(env["$blue"]); Number* h = dynamic_cast(env["$hue"]); Number* s = dynamic_cast(env["$saturation"]); Number* l = dynamic_cast(env["$lightness"]); Number* a = dynamic_cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("cannot specify both RGB and HSL values for `scale-color`", pstate); } if (rgb) { double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; return SASS_MEMORY_NEW(ctx.mem, Color, 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 ? ARGR("$hue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double sscale = (s ? ARGR("$saturation", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double lscale = (l ? ARGR("$lightness", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; 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 = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; return SASS_MEMORY_NEW(ctx.mem, 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); // 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* color = ARG("$color", Color); Number* r = dynamic_cast(env["$red"]); Number* g = dynamic_cast(env["$green"]); Number* b = dynamic_cast(env["$blue"]); Number* h = dynamic_cast(env["$hue"]); Number* s = dynamic_cast(env["$saturation"]); Number* l = dynamic_cast(env["$lightness"]); Number* a = dynamic_cast(env["$alpha"]); bool rgb = r || g || b; bool hsl = h || s || l; if (rgb && hsl) { error("cannot specify both RGB and HSL values for `change-color`", pstate); } if (rgb) { return SASS_MEMORY_NEW(ctx.mem, Color, pstate, r ? ARGR("$red", Number, 0, 255)->value() : color->r(), g ? ARGR("$green", Number, 0, 255)->value() : color->g(), b ? ARGR("$blue", Number, 0, 255)->value() : color->b(), a ? ARGR("$alpha", Number, 0, 255)->value() : color->a()); } if (hsl) { HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); if (h) hsl_struct.h = static_cast(((static_cast(h->value()) % 360) + 360) % 360) / 360.0; if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value(); if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value(); double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate); } if (a) { double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); return SASS_MEMORY_NEW(ctx.mem, Color, pstate, color->r(), color->g(), color->b(), alpha); } error("not enough arguments for `change-color`", pstate); // unreachable return color; } template 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* 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(Sass::round(a, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(r, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); std::string result(ss.str()); for (size_t i = 0, L = result.length(); i < L; ++i) { result[i] = std::toupper(result[i]); } return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, result); } /////////////////// // STRING FUNCTIONS /////////////////// Signature unquote_sig = "unquote($string)"; BUILT_IN(sass_unquote) { AST_Node* arg = env["$string"]; if (String_Quoted* string_quoted = dynamic_cast(arg)) { String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, string_quoted->value()); // remember if the string was quoted (color tokens) result->sass_fix_1291(string_quoted->quote_mark() != 0); return result; } else if (dynamic_cast(arg)) { return (Expression*) arg; } else { 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 = dynamic_cast(arg) ? "null" : val; ctx.c_options.output_style = oldstyle; deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); return (Expression*) arg; } } Signature quote_sig = "quote($string)"; BUILT_IN(sass_quote) { AST_Node* arg = env["$string"]; std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); String_Quoted* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str); result->is_delayed(true); 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, backtrace); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(ctx.mem, 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(); str = unquote(str); String_Constant* i = ARG("$insert", String_Constant); std::string ins = i->value(); ins = unquote(ins); Number* ind = ARG("$index", Number); double index = ind->value(); 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(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(index)), ins); } else { // negative and past string length str = ins + str; } if (String_Quoted* ss = dynamic_cast(s)) { if (ss->quote_mark()) str = quote(str); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); } return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str); } 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(); 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(ctx.mem, 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, backtrace); } // return something even if we had an error (-1) return SASS_MEMORY_NEW(ctx.mem, 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 = ARG("$start-at", Number)->value(); double end_at = ARG("$end-at", Number)->value(); std::string str = unquote(s->value()); size_t size = utf8::distance(str.begin(), str.end()); if (end_at <= size * -1.0) { end_at += size; } if (end_at < 0) { end_at += size + 1; } if (end_at > size) { end_at = (double)size; } if (start_at < 0) { start_at += size + 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 (String_Quoted* ss = dynamic_cast(s)) { if(ss->quote_mark()) newstr = quote(newstr); } } // handle any invalid utf8 errors // other errors will be re-thrown catch (...) { handle_utf8_error(pstate, backtrace); } return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, newstr); } 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(); 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* ss = dynamic_cast(s)) { String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss); cpy->value(str); return cpy; } else { return SASS_MEMORY_NEW(ctx.mem, 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(); 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* ss = dynamic_cast(s)) { String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss); cpy->value(str); return cpy; } else { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str); } } /////////////////// // NUMBER FUNCTIONS /////////////////// Signature percentage_sig = "percentage($number)"; BUILT_IN(percentage) { Number* n = ARG("$number", Number); if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, n->value() * 100, "%"); } Signature round_sig = "round($number)"; BUILT_IN(round) { Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); r->value(Sass::round(r->value(), ctx.c_options.precision)); return r; } Signature ceil_sig = "ceil($number)"; BUILT_IN(ceil) { Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); r->value(std::ceil(r->value())); return r; } Signature floor_sig = "floor($number)"; BUILT_IN(floor) { Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); r->value(std::floor(r->value())); return r; } Signature abs_sig = "abs($number)"; BUILT_IN(abs) { Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); r->value(std::abs(r->value())); return r; } Signature min_sig = "min($numbers...)"; BUILT_IN(min) { List* arglist = ARG("$numbers", List); Number* least = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression* val = arglist->value_at_index(i); Number* xi = dynamic_cast(val); if (!xi) { error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate); } if (least) { if (*xi < *least) least = xi; } else least = xi; } return least; } Signature max_sig = "max($numbers...)"; BUILT_IN(max) { List* arglist = ARG("$numbers", List); Number* greatest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression* val = arglist->value_at_index(i); Number* xi = dynamic_cast(val); if (!xi) { error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate); } if (greatest) { if (*greatest < *xi) greatest = xi; } else greatest = xi; } return greatest; } Signature random_sig = "random($limit:false)"; BUILT_IN(random) { AST_Node* arg = env["$limit"]; Value* v = dynamic_cast(arg); Number* l = dynamic_cast(arg); Boolean* b = dynamic_cast(arg); if (l) { double v = l->value(); if (v < 1) { stringstream err; err << "$limit " << v << " must be greater than or equal to 1 for `random`"; error(err.str(), pstate); } bool eq_int = std::fabs(trunc(v) - v) < NUMBER_EPSILON; if (!eq_int) { stringstream err; err << "Expected $limit to be an integer but got `" << v << "` for `random`"; error(err.str(), pstate); } std::uniform_real_distribution<> distributor(1, v + 1); uint_fast32_t distributed = static_cast(distributor(rand)); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)distributed); } else if (b) { std::uniform_real_distribution<> distributor(0, 1); double distributed = static_cast(distributor(rand)); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, distributed); } else if (v) { throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v); } else { throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number"); } return 0; } ///////////////// // LIST FUNCTIONS ///////////////// Signature length_sig = "length($list)"; BUILT_IN(length) { if (Selector_List* sl = dynamic_cast(env["$list"])) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)sl->length()); } Expression* v = ARG("$list", Expression); if (v->concrete_type() == Expression::MAP) { Map* map = dynamic_cast(env["$list"]); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(map ? map->length() : 1)); } if (v->concrete_type() == Expression::SELECTOR) { if (Compound_Selector* h = dynamic_cast(v)) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)h->length()); } else if (Selector_List* ls = dynamic_cast(v)) { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)ls->length()); } else { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, 1); } } List* list = dynamic_cast(env["$list"]); return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(list ? list->size() : 1)); } Signature nth_sig = "nth($list, $n)"; BUILT_IN(nth) { Number* n = ARG("$n", Number); Map* m = dynamic_cast(env["$list"]); if (Selector_List* sl = dynamic_cast(env["$list"])) { size_t len = m ? m->length() : sl->length(); bool empty = m ? m->empty() : sl->empty(); if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); // return (*sl)[static_cast(index)]; Listize listize(ctx.mem); return (*sl)[static_cast(index)]->perform(&listize); } List* l = dynamic_cast(env["$list"]); if (n->value() == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate); // if the argument isn't a list, then wrap it in a singleton list if (!m && !l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << ARG("$list", Expression); } size_t len = m ? m->length() : l->length(); bool empty = m ? m->empty() : l->empty(); if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); if (m) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << m->keys()[static_cast(index)]; *l << m->at(m->keys()[static_cast(index)]); return l; } else { return l->value_at_index(static_cast(index)); } } Signature set_nth_sig = "set-nth($list, $n, $value)"; BUILT_IN(set_nth) { List* l = dynamic_cast(env["$list"]); Number* n = ARG("$n", Number); Expression* v = ARG("$value", Expression); if (!l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << ARG("$list", Expression); } if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length(), l->separator()); for (size_t i = 0, L = l->length(); i < L; ++i) { *result << ((i == index) ? v : (*l)[i]); } return result; } Signature index_sig = "index($list, $value)"; BUILT_IN(index) { List* l = dynamic_cast(env["$list"]); Expression* v = ARG("$value", Expression); if (!l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << ARG("$list", Expression); } for (size_t i = 0, L = l->length(); i < L; ++i) { if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(i+1)); } return SASS_MEMORY_NEW(ctx.mem, Null, pstate); } Signature join_sig = "join($list1, $list2, $separator: auto)"; BUILT_IN(join) { List* l1 = dynamic_cast(env["$list1"]); List* l2 = dynamic_cast(env["$list2"]); String_Constant* sep = ARG("$separator", String_Constant); enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); if (!l1) { l1 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l1 << ARG("$list1", Expression); sep_val = (l2 ? l2->separator() : SASS_SPACE); } if (!l2) { l2 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l2 << ARG("$list2", Expression); } size_t len = l1->length() + l2->length(); std::string sep_str = unquote(sep->value()); if (sep_str == "space") sep_val = SASS_SPACE; else if (sep_str == "comma") sep_val = SASS_COMMA; else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate); List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, len, sep_val); *result += l1; *result += l2; return result; } Signature append_sig = "append($list, $val, $separator: auto)"; BUILT_IN(append) { List* l = dynamic_cast(env["$list"]); Expression* v = ARG("$val", Expression); if (Selector_List* sl = dynamic_cast(env["$list"])) { Listize listize(ctx.mem); l = dynamic_cast(sl->perform(&listize)); } String_Constant* sep = ARG("$separator", String_Constant); if (!l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << ARG("$list", Expression); } List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length() + 1, l->separator()); std::string sep_str(unquote(sep->value())); if (sep_str == "space") result->separator(SASS_SPACE); else if (sep_str == "comma") result->separator(SASS_COMMA); else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate); *result += l; bool is_arglist = l->is_arglist(); result->is_arglist(is_arglist); if (is_arglist) { *result << SASS_MEMORY_NEW(ctx.mem, Argument, v->pstate(), v, "", false, false); } else { *result << v; } return result; } Signature zip_sig = "zip($lists...)"; BUILT_IN(zip) { List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$lists", List)); size_t shortest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { List* ith = dynamic_cast(arglist->value_at_index(i)); if (!ith) { ith = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *ith << arglist->value_at_index(i); if (arglist->is_arglist()) { ((Argument*)(*arglist)[i])->value(ith); } else { (*arglist)[i] = ith; } } shortest = (i ? std::min(shortest, ith->length()) : ith->length()); } List* zippers = SASS_MEMORY_NEW(ctx.mem, List, pstate, shortest, SASS_COMMA); size_t L = arglist->length(); for (size_t i = 0; i < shortest; ++i) { List* zipper = SASS_MEMORY_NEW(ctx.mem, List, pstate, L); for (size_t j = 0; j < L; ++j) { *zipper << (*static_cast(arglist->value_at_index(j)))[i]; } *zippers << zipper; } return zippers; } Signature list_separator_sig = "list_separator($list)"; BUILT_IN(list_separator) { List* l = dynamic_cast(env["$list"]); if (!l) { l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1); *l << ARG("$list", Expression); } return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, l->separator() == SASS_COMMA ? "comma" : "space"); } ///////////////// // MAP FUNCTIONS ///////////////// Signature map_get_sig = "map-get($map, $key)"; BUILT_IN(map_get) { Map* m = ARGM("$map", Map, ctx); Expression* v = ARG("$key", Expression); try { return m->at(v); } catch (const std::out_of_range&) { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); } catch (...) { throw; } } Signature map_has_key_sig = "map-has-key($map, $key)"; BUILT_IN(map_has_key) { Map* m = ARGM("$map", Map, ctx); Expression* v = ARG("$key", Expression); return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, m->has(v)); } Signature map_keys_sig = "map-keys($map)"; BUILT_IN(map_keys) { Map* m = ARGM("$map", Map, ctx); List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) { *result << key; } return result; } Signature map_values_sig = "map-values($map)"; BUILT_IN(map_values) { Map* m = ARGM("$map", Map, ctx); List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_COMMA); for ( auto key : m->keys()) { *result << m->at(key); } return result; } Signature map_merge_sig = "map-merge($map1, $map2)"; BUILT_IN(map_merge) { Map* m1 = ARGM("$map1", Map, ctx); Map* m2 = ARGM("$map2", Map, ctx); size_t len = m1->length() + m2->length(); Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, len); *result += m1; *result += m2; return result; } Signature map_remove_sig = "map-remove($map, $keys...)"; BUILT_IN(map_remove) { bool remove; Map* m = ARGM("$map", Map, ctx); List* arglist = ARG("$keys", List); Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1); for (auto key : m->keys()) { remove = false; for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) { remove = Eval::eq(key, arglist->value_at_index(j)); } if (!remove) *result << std::make_pair(key, m->at(key)); } return result; } Signature keywords_sig = "keywords($args)"; BUILT_IN(keywords) { List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List)); Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1); for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) { std::string name = std::string(((Argument*)(*arglist)[i])->name()); name = name.erase(0, 1); // sanitize name (remove dollar sign) *result << std::make_pair(SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, name), ((Argument*)(*arglist)[i])->value()); } return result; } ////////////////////////// // INTROSPECTION FUNCTIONS ////////////////////////// Signature type_of_sig = "type-of($value)"; BUILT_IN(type_of) { Expression* v = ARG("$value", Expression); return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, v->type()); } Signature unit_sig = "unit($number)"; BUILT_IN(unit) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); } Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$number", Number)->is_unitless()); } Signature comparable_sig = "comparable($number-1, $number-2)"; BUILT_IN(comparable) { Number* n1 = ARG("$number-1", Number); Number* n2 = ARG("$number-2", Number); if (n1->is_unitless() || n2->is_unitless()) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); } Number tmp_n2(*n2); tmp_n2.normalize(n1->find_convertible_unit()); return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, n1->unit() == tmp_n2.unit()); } 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(ctx.mem, Boolean, pstate, true); } else { return SASS_MEMORY_NEW(ctx.mem, 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(ctx.mem, Boolean, pstate, true); } else { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); } } Signature function_exists_sig = "function-exists($name)"; BUILT_IN(function_exists) { std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); if(d_env.has_global(s+"[f]")) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); } else { return SASS_MEMORY_NEW(ctx.mem, 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(ctx.mem, Boolean, pstate, true); } else { return SASS_MEMORY_NEW(ctx.mem, 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(ctx.mem, Boolean, pstate, false); } else { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); } } Signature call_sig = "call($name, $args...)"; BUILT_IN(call) { std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value())); List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List)); Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); // std::string full_name(name + "[f]"); // Definition* def = d_env.has(full_name) ? static_cast((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* expr = arglist->value_at_index(i); // if (params && params->has_rest_parameter()) { // Parameter* p = param_size > i ? (*params)[i] : 0; // List* list = dynamic_cast(expr); // if (list && p && !p->is_rest_parameter()) expr = (*list)[0]; // } if (arglist->is_arglist()) { Argument* arg = dynamic_cast((*arglist)[i]); *args << SASS_MEMORY_NEW(ctx.mem, Argument, pstate, expr, arg ? arg->name() : "", arg ? arg->is_rest_argument() : false, arg ? arg->is_keyword_argument() : false); } else { *args << SASS_MEMORY_NEW(ctx.mem, Argument, pstate, expr); } } Function_Call* func = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, name, args); Expand expand(ctx, &d_env, backtrace); return func->perform(&expand.eval); } //////////////////// // BOOLEAN FUNCTIONS //////////////////// Signature not_sig = "not($value)"; BUILT_IN(sass_not) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, ARG("$value", Expression)->is_false()); } Signature if_sig = "if($condition, $if-true, $if-false)"; // BUILT_IN(sass_if) // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } BUILT_IN(sass_if) { Expand expand(ctx, &d_env, backtrace); bool is_true = !ARG("$condition", Expression)->perform(&expand.eval)->is_false(); if (is_true) { return ARG("$if-true", Expression)->perform(&expand.eval); } else { return ARG("$if-false", Expression)->perform(&expand.eval); } } //////////////// // URL FUNCTIONS //////////////// Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)"; BUILT_IN(image_url) { error("`image_url` has been removed from libsass because it's not part of the Sass spec", pstate); return 0; // suppress warning, error will exit anyway } ////////////////////////// // MISCELLANEOUS FUNCTIONS ////////////////////////// // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String) // unquoted_string(value.to_sass) Signature inspect_sig = "inspect($value)"; BUILT_IN(inspect) { Expression* v = ARG("$value", Expression); if (v->concrete_type() == Expression::NULL_VAL) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "null"); } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "false"); } 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(ctx.mem, String_Quoted, pstate, i.get_buffer()); } // return v; } 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", pstate); // Parse args into vector of selectors std::vector parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression* exp = dynamic_cast(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); } if (String_Constant* str =dynamic_cast(exp)) { str->quote_mark(0); } std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } // Nothing to do if( parsedSelectors.empty() ) { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); } // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. std::vector::iterator itr = parsedSelectors.begin(); Selector_List* result = *itr; ++itr; for(;itr != parsedSelectors.end(); ++itr) { Selector_List* child = *itr; std::vector exploded; Selector_List* rv = child->parentize(result, ctx); for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) { exploded.push_back((*rv)[m]); } result->elements(exploded); } Listize listize(ctx.mem); return result->perform(&listize); } Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { List* arglist = ARG("$selectors", List); // Not enough parameters if( arglist->length() == 0 ) error("$selectors: At least one selector must be passed", pstate); // Parse args into vector of selectors std::vector parsedSelectors; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Expression* exp = dynamic_cast(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); } if (String_Constant* str =dynamic_cast(exp)) { str->quote_mark(0); } std::string exp_src = exp->to_string() + "{"; Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } // Nothing to do if( parsedSelectors.empty() ) { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); } // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector. std::vector::iterator itr = parsedSelectors.begin(); Selector_List* result = *itr; ++itr; for(;itr != parsedSelectors.end(); ++itr) { Selector_List* child = *itr; std::vector 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* parentSeqClone = (*result)[i]->cloneFully(ctx); Complex_Selector* childSeq = (*child)[j]; Complex_Selector* 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 += "`"; error(msg, pstate, backtrace); } // Cannot be a Universal selector Type_Selector* pType = dynamic_cast(base->head()->first()); if(pType && pType->name() == "*") { std::string msg("Can't append `"); msg += childSeq->to_string(); msg += "` to `"; msg += parentSeqClone->to_string(); msg += "`"; error(msg, pstate, backtrace); } // TODO: Add check for namespace stuff // append any selectors in childSeq's head *(parentSeqClone->innermost()->head()) += (base->head()); // Set parentSeqClone new tail parentSeqClone->innermost()->tail( base->tail() ); newElements.push_back(parentSeqClone); } } result->elements(newElements); } Listize listize(ctx.mem); return result->perform(&listize); } Signature selector_unify_sig = "selector-unify($selector1, $selector2)"; BUILT_IN(selector_unify) { Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize); Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); Selector_List* result = selector1->unify_with(selector2, ctx); Listize listize(ctx.mem); return result->perform(&listize); } Signature simple_selectors_sig = "simple-selectors($selector)"; BUILT_IN(simple_selectors) { Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { Simple_Selector* ss = (*sel)[i]; std::string ss_string = ss->to_string() ; *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, ss->pstate(), ss_string); } return l; } Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)"; BUILT_IN(selector_extend) { Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize); Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize); ExtensionSubsetMap subset_map; extender->populate_extends(extendee, ctx, subset_map); bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); Listize listize(ctx.mem); return result->perform(&listize); } Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)"; BUILT_IN(selector_replace) { Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize); Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize); Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize); ExtensionSubsetMap subset_map; replacement->populate_extends(original, ctx, subset_map); bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); Listize listize(ctx.mem); return result->perform(&listize); } Signature selector_parse_sig = "selector-parse($selector)"; BUILT_IN(selector_parse) { Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize); Listize listize(ctx.mem); return sel->perform(&listize); } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { Selector_List* sel_sup = ARGSEL("$super", Selector_List, p_contextualize); Selector_List* sel_sub = ARGSEL("$sub", Selector_List, p_contextualize); bool result = sel_sup->is_superselector_of(sel_sub); return SASS_MEMORY_NEW(ctx.mem, 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(distributor(rand)); ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed; return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, ss.str()); } } } libsass-3.3.4/src/functions.hpp000066400000000000000000000132201267254216700165030ustar00rootroot00000000000000#ifndef SASS_FUNCTIONS_H #define SASS_FUNCTIONS_H #include "listize.hpp" #include "position.hpp" #include "environment.hpp" #include "sass/functions.h" #define BUILT_IN(name) Expression*\ name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace) namespace Sass { class Context; struct Backtrace; class AST_Node; class Expression; class Definition; typedef Environment Env; typedef const char* Signature; typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*); Definition* make_native_function(Signature, Native_Function, Context& ctx); Definition* 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 image_url_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; 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(image_url); 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); } } #endif libsass-3.3.4/src/inspect.cpp000066400000000000000000000713151267254216700161440ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include #include "ast.hpp" #include "inspect.hpp" #include "context.hpp" #include "listize.hpp" #include "color_maps.hpp" #include "utf8/checked.h" namespace Sass { Inspect::Inspect(Emitter emi) : Emitter(emi) { } Inspect::~Inspect() { } // statements void Inspect::operator()(Block* block) { if (!block->is_root()) { add_open_mapping(block); append_scope_opener(); } if (output_style() == NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); } if (output_style() == NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block); } } void Inspect::operator()(Ruleset* ruleset) { ruleset->selector()->perform(this); ruleset->block()->perform(this); } void Inspect::operator()(Keyframe_Rule* rule) { if (rule->selector()) rule->selector()->perform(this); if (rule->block()) rule->block()->perform(this); } void Inspect::operator()(Propset* propset) { propset->property_fragment()->perform(this); append_colon_separator(); propset->block()->perform(this); } void Inspect::operator()(Bubble* bubble) { append_indentation(); append_token("::BUBBLE", bubble); append_scope_opener(); bubble->node()->perform(this); append_scope_closer(); } void Inspect::operator()(Media_Block* media_block) { append_indentation(); append_token("@media", media_block); append_mandatory_space(); in_media_block = true; media_block->media_queries()->perform(this); in_media_block = false; media_block->block()->perform(this); } void Inspect::operator()(Supports_Block* feature_block) { append_indentation(); append_token("@supports", feature_block); append_mandatory_space(); feature_block->condition()->perform(this); feature_block->block()->perform(this); } void Inspect::operator()(At_Root_Block* at_root_block) { append_indentation(); append_token("@at-root ", at_root_block); append_mandatory_space(); if(at_root_block->expression()) at_root_block->expression()->perform(this); at_root_block->block()->perform(this); } void Inspect::operator()(At_Rule* at_rule) { append_indentation(); append_token(at_rule->keyword(), at_rule); if (at_rule->selector()) { append_mandatory_space(); bool was_wrapped = in_wrapped; in_wrapped = true; at_rule->selector()->perform(this); in_wrapped = was_wrapped; } if (at_rule->value()) { append_mandatory_space(); at_rule->value()->perform(this); } if (at_rule->block()) { at_rule->block()->perform(this); } else { append_delimiter(); } } void Inspect::operator()(Declaration* dec) { if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true; if (output_style() == NESTED) indentation += dec->tabs(); append_indentation(); dec->property()->perform(this); append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) { Memory_Manager mem; Listize listize(mem); dec->value()->perform(&listize)->perform(this); } else { dec->value()->perform(this); } if (dec->is_important()) { append_optional_space(); append_string("!important"); } append_delimiter(); if (output_style() == NESTED) indentation -= dec->tabs(); in_declaration = was_decl; } void Inspect::operator()(Assignment* assn) { append_token(assn->variable(), assn); append_colon_separator(); assn->value()->perform(this); if (assn->is_default()) { append_optional_space(); append_string("!default"); } append_delimiter(); } void Inspect::operator()(Import* import) { if (!import->urls().empty()) { append_token("@import", import); append_mandatory_space(); if (String_Quoted* strq = dynamic_cast(import->urls().front())) { strq->is_delayed(false); } import->urls().front()->perform(this); if (import->urls().size() == 1) { if (import->media_queries()) { append_mandatory_space(); import->media_queries()->perform(this); } } append_delimiter(); for (size_t i = 1, S = import->urls().size(); i < S; ++i) { append_mandatory_linefeed(); append_token("@import", import); append_mandatory_space(); if (String_Quoted* strq = dynamic_cast(import->urls()[i])) { strq->is_delayed(false); } import->urls()[i]->perform(this); if (import->urls().size() - 1 == i) { if (import->media_queries()) { append_mandatory_space(); import->media_queries()->perform(this); } } append_delimiter(); } } } void Inspect::operator()(Import_Stub* import) { append_indentation(); append_token("@import", import); append_mandatory_space(); append_string(import->imp_path()); append_delimiter(); } void Inspect::operator()(Warning* warning) { append_indentation(); append_token("@warn", warning); append_mandatory_space(); warning->message()->perform(this); append_delimiter(); } void Inspect::operator()(Error* error) { append_indentation(); append_token("@error", error); append_mandatory_space(); error->message()->perform(this); append_delimiter(); } void Inspect::operator()(Debug* debug) { append_indentation(); append_token("@debug", debug); append_mandatory_space(); debug->value()->perform(this); append_delimiter(); } void Inspect::operator()(Comment* comment) { in_comment = true; comment->text()->perform(this); in_comment = false; } void Inspect::operator()(If* cond) { append_indentation(); append_token("@if", cond); append_mandatory_space(); cond->predicate()->perform(this); cond->block()->perform(this); if (cond->alternative()) { append_optional_linefeed(); append_indentation(); append_string("else"); cond->alternative()->perform(this); } } void Inspect::operator()(For* loop) { append_indentation(); append_token("@for", loop); append_mandatory_space(); append_string(loop->variable()); append_string(" from "); loop->lower_bound()->perform(this); append_string(loop->is_inclusive() ? " through " : " to "); loop->upper_bound()->perform(this); loop->block()->perform(this); } void Inspect::operator()(Each* loop) { append_indentation(); append_token("@each", loop); append_mandatory_space(); append_string(loop->variables()[0]); for (size_t i = 1, L = loop->variables().size(); i < L; ++i) { append_comma_separator(); append_string(loop->variables()[i]); } append_string(" in "); loop->list()->perform(this); loop->block()->perform(this); } void Inspect::operator()(While* loop) { append_indentation(); append_token("@while", loop); append_mandatory_space(); loop->predicate()->perform(this); loop->block()->perform(this); } void Inspect::operator()(Return* ret) { append_indentation(); append_token("@return", ret); append_mandatory_space(); ret->value()->perform(this); append_delimiter(); } void Inspect::operator()(Extension* extend) { append_indentation(); append_token("@extend", extend); append_mandatory_space(); extend->selector()->perform(this); append_delimiter(); } void Inspect::operator()(Definition* def) { append_indentation(); if (def->type() == Definition::MIXIN) { append_token("@mixin", def); append_mandatory_space(); } else { append_token("@function", def); append_mandatory_space(); } append_string(def->name()); def->parameters()->perform(this); def->block()->perform(this); } void Inspect::operator()(Mixin_Call* call) { append_indentation(); append_token("@include", call); append_mandatory_space(); append_string(call->name()); if (call->arguments()) { call->arguments()->perform(this); } if (call->block()) { append_optional_space(); call->block()->perform(this); } if (!call->block()) append_delimiter(); } void Inspect::operator()(Content* content) { append_indentation(); append_token("@content", content); append_delimiter(); } void Inspect::operator()(Map* map) { if (output_style() == TO_SASS && map->empty()) { append_string("()"); return; } if (map->empty()) return; if (map->is_invisible()) return; bool items_output = false; append_string("("); for (auto key : map->keys()) { if (items_output) append_comma_separator(); key->perform(this); append_colon_separator(); map->at(key)->perform(this); items_output = true; } append_string(")"); } void Inspect::operator()(List* list) { if (output_style() == TO_SASS && list->empty()) { append_string("()"); return; } std::string sep(list->separator() == SASS_SPACE ? " " : ","); if ((output_style() != COMPRESSED) && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; bool was_space_array = in_space_array; bool was_comma_array = in_comma_array; // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && list->length() == 1 && (!dynamic_cast((*list)[0]) && !dynamic_cast((*list)[0]))) { append_string("("); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string("("); } if (list->separator() == SASS_SPACE) in_space_array = true; else if (list->separator() == SASS_COMMA) in_comma_array = true; for (size_t i = 0, L = list->size(); i < L; ++i) { if (list->separator() == SASS_HASH) { sep[0] = i % 2 ? ':' : ','; } Expression* list_item = (*list)[i]; if (output_style() != TO_SASS) { if (list_item->is_invisible()) { // this fixes an issue with "" in a list if (!dynamic_cast(list_item)) { continue; } } } if (items_output) { append_string(sep); } if (items_output && sep != " ") append_optional_space(); list_item->perform(this); items_output = true; } in_comma_array = was_comma_array; in_space_array = was_space_array; // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && list->length() == 1 && (!dynamic_cast((*list)[0]) && !dynamic_cast((*list)[0]))) { append_string(",)"); } else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { append_string(")"); } } void Inspect::operator()(Binary_Expression* expr) { expr->left()->perform(this); if ( in_media_block || (output_style() == INSPECT) || ( expr->op().ws_before && (!expr->is_interpolant()) && (!expr->is_delayed() || expr->is_left_interpolant() || expr->is_right_interpolant() ) )) append_string(" "); switch (expr->type()) { case Sass_OP::AND: append_string("&&"); break; case Sass_OP::OR: append_string("||"); break; case Sass_OP::EQ: append_string("=="); break; case Sass_OP::NEQ: append_string("!="); break; case Sass_OP::GT: append_string(">"); break; case Sass_OP::GTE: append_string(">="); break; case Sass_OP::LT: append_string("<"); break; case Sass_OP::LTE: append_string("<="); break; case Sass_OP::ADD: append_string("+"); break; case Sass_OP::SUB: append_string("-"); break; case Sass_OP::MUL: append_string("*"); break; case Sass_OP::DIV: append_string("/"); break; case Sass_OP::MOD: append_string("%"); break; default: break; // shouldn't get here } if ( in_media_block || (output_style() == INSPECT) || ( expr->op().ws_after && (!expr->is_interpolant()) && (!expr->is_delayed() || expr->is_left_interpolant() || expr->is_right_interpolant() ) )) append_string(" "); expr->right()->perform(this); } void Inspect::operator()(Unary_Expression* expr) { if (expr->type() == Unary_Expression::PLUS) append_string("+"); else append_string("-"); expr->operand()->perform(this); } void Inspect::operator()(Function_Call* call) { append_token(call->name(), call); call->arguments()->perform(this); } void Inspect::operator()(Function_Call_Schema* call) { call->name()->perform(this); call->arguments()->perform(this); } void Inspect::operator()(Variable* var) { append_token(var->name(), var); } void Inspect::operator()(Textual* txt) { append_token(txt->value(), txt); } void Inspect::operator()(Number* n) { std::string res; // check if the fractional part of the value equals to zero // neat trick from http://stackoverflow.com/a/1521682/1550314 // double int_part; bool is_int = modf(value, &int_part) == 0.0; // this all cannot be done with one run only, since fixed // output differs from normal output and regular output // can contain scientific notation which we do not want! // first sample std::stringstream ss; ss.precision(12); ss << n->value(); // check if we got scientific notation in result if (ss.str().find_first_of("e") != std::string::npos) { ss.clear(); ss.str(std::string()); ss.precision(std::max(12, opt.precision)); ss << std::fixed << n->value(); } std::string tmp = ss.str(); size_t pos_point = tmp.find_first_of(".,"); size_t pos_fract = tmp.find_last_not_of("0"); bool is_int = pos_point == pos_fract || pos_point == std::string::npos; // reset stream for another run ss.clear(); ss.str(std::string()); // take a shortcut for integers if (is_int) { ss.precision(0); ss << std::fixed << n->value(); res = std::string(ss.str()); } // process floats else { // do we have have too much precision? if (pos_fract < opt.precision + pos_point) { ss.precision((int)(pos_fract - pos_point)); } else { ss.precision(opt.precision); } // round value again ss << std::fixed << n->value(); res = std::string(ss.str()); // maybe we truncated up to decimal point size_t pos = res.find_last_not_of("0"); // handle case where we have a "0" if (pos == std::string::npos) { res = "0.0"; } else { bool at_dec_point = res[pos] == '.' || res[pos] == ','; // don't leave a blank point if (at_dec_point) ++ pos; res.resize (pos + 1); } } // some final cosmetics if (res == "0.0") res = "0"; else if (res == "") res = "0"; else if (res == "-0") res = "0"; else if (res == "-0.0") res = "0"; else if (opt.output_style == COMPRESSED) { // check if handling negative nr size_t off = res[0] == '-' ? 1 : 0; // remove leading zero from floating point in compressed mode if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1); } // add unit now res += n->unit(); // output the final token append_token(res, n); } // helper function for serializing colors template static double cap_channel(double c) { if (c > range) return range; else if (c < 0) return 0; else return c; } void Inspect::operator()(Color* c) { // output the final token std::stringstream ss; // original color name // maybe an unknown token std::string name = c->disp(); // resolved color std::string res_name = name; double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision); double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision); double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision); double a = cap_channel<1> (c->a()); // get color from given name (if one was given at all) if (name != "" && name_to_color(name)) { const Color* n = name_to_color(name); r = Sass::round(cap_channel<0xff>(n->r()), opt.precision); g = Sass::round(cap_channel<0xff>(n->g()), opt.precision); b = Sass::round(cap_channel<0xff>(n->b()), opt.precision); a = cap_channel<1> (n->a()); } // otherwise get the possible resolved color name else { double numval = r * 0x10000 + g * 0x100 + b; if (color_to_name(numval)) res_name = color_to_name(numval); } std::stringstream hexlet; bool compressed = opt.output_style == COMPRESSED; hexlet << '#' << std::setw(1) << std::setfill('0'); // create a short color hexlet if there is any need for it if (compressed && is_color_doublet(r, g, b) && a == 1) { hexlet << std::hex << std::setw(1) << (static_cast(r) >> 4); hexlet << std::hex << std::setw(1) << (static_cast(g) >> 4); hexlet << std::hex << std::setw(1) << (static_cast(b) >> 4); } else { hexlet << std::hex << std::setw(2) << static_cast(r); hexlet << std::hex << std::setw(2) << static_cast(g); hexlet << std::hex << std::setw(2) << static_cast(b); } if (compressed && !c->is_delayed()) name = ""; if (opt.output_style == INSPECT && a >= 1) { append_token(hexlet.str(), c); return; } // retain the originally specified color definition if unchanged if (name != "") { ss << name; } else if (r == 0 && g == 0 && b == 0 && a == 0) { ss << "transparent"; } else if (a >= 1) { if (res_name != "") { if (compressed && hexlet.str().size() < res_name.size()) { ss << hexlet.str(); } else { ss << res_name; } } else { ss << hexlet.str(); } } else { ss << "rgba("; ss << static_cast(r) << ","; if (!compressed) ss << " "; ss << static_cast(g) << ","; if (!compressed) ss << " "; ss << static_cast(b) << ","; if (!compressed) ss << " "; ss << a << ')'; } append_token(ss.str(), c); } void Inspect::operator()(Boolean* b) { // output the final token append_token(b->value() ? "true" : "false", b); } void Inspect::operator()(String_Schema* ss) { // Evaluation should turn these into String_Constants, // so this method is only for inspection purposes. for (size_t i = 0, L = ss->length(); i < L; ++i) { if ((*ss)[i]->is_interpolant()) append_string("#{"); (*ss)[i]->perform(this); if ((*ss)[i]->is_interpolant()) append_string("}"); } } void Inspect::operator()(String_Constant* s) { append_token(s->value(), s); } void Inspect::operator()(String_Quoted* s) { if (const char q = s->quote_mark()) { append_token(quote(s->value(), q), s); } else { append_token(s->value(), s); } } void Inspect::operator()(Custom_Error* e) { append_token(e->message(), e); } void Inspect::operator()(Custom_Warning* w) { append_token(w->message(), w); } void Inspect::operator()(Supports_Operator* so) { if (so->needs_parens(so->left())) append_string("("); so->left()->perform(this); if (so->needs_parens(so->left())) append_string(")"); if (so->operand() == Supports_Operator::AND) { append_mandatory_space(); append_token("and", so); append_mandatory_space(); } else if (so->operand() == Supports_Operator::OR) { append_mandatory_space(); append_token("or", so); append_mandatory_space(); } if (so->needs_parens(so->right())) append_string("("); so->right()->perform(this); if (so->needs_parens(so->right())) append_string(")"); } void Inspect::operator()(Supports_Negation* sn) { append_token("not", sn); append_mandatory_space(); if (sn->needs_parens(sn->condition())) append_string("("); sn->condition()->perform(this); if (sn->needs_parens(sn->condition())) append_string(")"); } void Inspect::operator()(Supports_Declaration* sd) { append_string("("); sd->feature()->perform(this); append_string(": "); sd->value()->perform(this); append_string(")"); } void Inspect::operator()(Supports_Interpolation* sd) { sd->value()->perform(this); } void Inspect::operator()(Media_Query* mq) { size_t i = 0; if (mq->media_type()) { if (mq->is_negated()) append_string("not "); else if (mq->is_restricted()) append_string("only "); mq->media_type()->perform(this); } else { (*mq)[i++]->perform(this); } for (size_t L = mq->length(); i < L; ++i) { append_string(" and "); (*mq)[i]->perform(this); } } void Inspect::operator()(Media_Query_Expression* mqe) { if (mqe->is_interpolated()) { mqe->feature()->perform(this); } else { append_string("("); mqe->feature()->perform(this); if (mqe->value()) { append_string(": "); // verified mqe->value()->perform(this); } append_string(")"); } } void Inspect::operator()(At_Root_Expression* ae) { if (ae->is_interpolated()) { ae->feature()->perform(this); } else { append_string("("); ae->feature()->perform(this); if (ae->value()) { append_colon_separator(); ae->value()->perform(this); } append_string(")"); } } void Inspect::operator()(Null* n) { // output the final token append_token("null", n); } // parameters and arguments void Inspect::operator()(Parameter* p) { append_token(p->name(), p); if (p->default_value()) { append_colon_separator(); p->default_value()->perform(this); } else if (p->is_rest_parameter()) { append_string("..."); } } void Inspect::operator()(Parameters* p) { append_string("("); if (!p->empty()) { (*p)[0]->perform(this); for (size_t i = 1, L = p->length(); i < L; ++i) { append_comma_separator(); (*p)[i]->perform(this); } } append_string(")"); } void Inspect::operator()(Argument* a) { if (!a->name().empty()) { append_token(a->name(), a); append_colon_separator(); } // Special case: argument nulls can be ignored if (a->value()->concrete_type() == Expression::NULL_VAL) { return; } if (a->value()->concrete_type() == Expression::STRING) { String_Constant* s = static_cast(a->value()); s->perform(this); } else a->value()->perform(this); if (a->is_rest_argument()) { append_string("..."); } } void Inspect::operator()(Arguments* a) { append_string("("); if (!a->empty()) { (*a)[0]->perform(this); for (size_t i = 1, L = a->length(); i < L; ++i) { append_string(", "); // verified // Sass Bug? append_comma_separator(); (*a)[i]->perform(this); } } append_string(")"); } void Inspect::operator()(Selector_Schema* s) { s->contents()->perform(this); } void Inspect::operator()(Parent_Selector* p) { append_string("&"); } void Inspect::operator()(Selector_Placeholder* s) { append_token(s->name(), s); if (s->has_line_break()) append_optional_linefeed(); if (s->has_line_break()) append_indentation(); } void Inspect::operator()(Type_Selector* s) { append_token(s->ns_name(), s); } void Inspect::operator()(Selector_Qualifier* s) { append_token(s->ns_name(), s); if (s->has_line_break()) append_optional_linefeed(); if (s->has_line_break()) append_indentation(); } void Inspect::operator()(Attribute_Selector* s) { append_string("["); add_open_mapping(s); append_token(s->ns_name(), s); if (!s->matcher().empty()) { append_string(s->matcher()); if (s->value()) { s->value()->perform(this); } } add_close_mapping(s); append_string("]"); } 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* s) { bool was = in_wrapped; in_wrapped = true; append_token(s->name(), s); append_string("("); bool was_comma_array = in_comma_array; in_comma_array = false; s->selector()->perform(this); in_comma_array = was_comma_array; append_string(")"); in_wrapped = was; } void Inspect::operator()(Compound_Selector* 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* c) { Compound_Selector* head = c->head(); Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); if (c->has_line_feed()) { if (!(c->has_parent_ref())) { append_optional_linefeed(); append_indentation(); } } 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("/"); 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; } 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(); } } } void Inspect::operator()(Selector_List* g) { if (g->empty()) { if (output_style() == TO_SASS) { append_token("()", g); } return; } bool was_comma_array = in_comma_array; // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!dynamic_cast((*g)[0]) && !dynamic_cast((*g)[0]))) { append_string("("); } else if (!in_declaration && in_comma_array) { append_string("("); } if (in_declaration) in_comma_array = true; for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); if ((*g)[i] == 0) continue; schedule_mapping((*g)[i]->last()); // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this); // add_close_mapping((*g)[i]->last()); if (i < L - 1) { scheduled_space = 0; append_comma_separator(); } } in_comma_array = was_comma_array; // probably ruby sass eqivalent of element_needs_parens if (output_style() == TO_SASS && g->length() == 1 && (!dynamic_cast((*g)[0]) && !dynamic_cast((*g)[0]))) { append_string(",)"); } else if (!in_declaration && in_comma_array) { append_string(")"); } } void Inspect::fallback_impl(AST_Node* n) { } } libsass-3.3.4/src/inspect.hpp000066400000000000000000000067571267254216700161610ustar00rootroot00000000000000#ifndef SASS_INSPECT_H #define SASS_INSPECT_H #include "position.hpp" #include "operation.hpp" #include "emitter.hpp" namespace Sass { class Context; class Inspect : public Operation_CRTP, public Emitter { protected: // import all the class-specific methods and override as desired using Operation_CRTP::operator(); void fallback_impl(AST_Node* n); public: Inspect(Emitter emi); virtual ~Inspect(); // statements virtual void operator()(Block*); virtual void operator()(Ruleset*); virtual void operator()(Propset*); virtual void operator()(Bubble*); virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Root_Block*); virtual void operator()(At_Rule*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Declaration*); virtual void operator()(Assignment*); virtual void operator()(Import*); virtual void operator()(Import_Stub*); virtual void operator()(Warning*); virtual void operator()(Error*); virtual void operator()(Debug*); virtual void operator()(Comment*); virtual void operator()(If*); virtual void operator()(For*); virtual void operator()(Each*); virtual void operator()(While*); virtual void operator()(Return*); virtual void operator()(Extension*); virtual void operator()(Definition*); virtual void operator()(Mixin_Call*); virtual void operator()(Content*); // expressions virtual void operator()(Map*); virtual void operator()(List*); virtual void operator()(Binary_Expression*); virtual void operator()(Unary_Expression*); virtual void operator()(Function_Call*); virtual void operator()(Function_Call_Schema*); // virtual void operator()(Custom_Warning*); // virtual void operator()(Custom_Error*); virtual void operator()(Variable*); virtual void operator()(Textual*); virtual void operator()(Number*); virtual void operator()(Color*); virtual void operator()(Boolean*); virtual void operator()(String_Schema*); virtual void operator()(String_Constant*); virtual void operator()(String_Quoted*); virtual void operator()(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()(Media_Query*); virtual void operator()(Media_Query_Expression*); virtual void operator()(At_Root_Expression*); virtual void operator()(Null*); virtual void operator()(Parent_Selector* p); // parameters and arguments virtual void operator()(Parameter*); virtual void operator()(Parameters*); virtual void operator()(Argument*); virtual void operator()(Arguments*); // selectors virtual void operator()(Selector_Schema*); virtual void operator()(Selector_Placeholder*); virtual void operator()(Type_Selector*); virtual void operator()(Selector_Qualifier*); virtual void operator()(Attribute_Selector*); virtual void operator()(Pseudo_Selector*); virtual void operator()(Wrapped_Selector*); virtual void operator()(Compound_Selector*); virtual void operator()(Complex_Selector*); virtual void operator()(Selector_List*); // template // void fallback(U x) { fallback_impl(reinterpret_cast(x)); } }; } #endif libsass-3.3.4/src/json.cpp000066400000000000000000000766341267254216700154610ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE #endif #include "json.hpp" #include #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1900 #include #ifdef snprintf #undef snprintf #endif extern "C" int snprintf(char *, size_t, const char *, ...); #endif #define out_of_memory() do { \ fprintf(stderr, "Out of memory.\n"); \ exit(EXIT_FAILURE); \ } while (0) /* Sadly, strdup is not portable. */ static char *json_strdup(const char *str) { char *ret = (char*) malloc(strlen(str) + 1); if (ret == NULL) out_of_memory(); strcpy(ret, str); return ret; } /* String buffer */ typedef struct { char *cur; char *end; char *start; } SB; static void sb_init(SB *sb) { sb->start = (char*) malloc(17); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start; sb->end = sb->start + 16; } /* sb and need may be evaluated multiple times. */ #define sb_need(sb, need) do { \ if ((sb)->end - (sb)->cur < (need)) \ sb_grow(sb, need); \ } while (0) static void sb_grow(SB *sb, int need) { size_t length = sb->cur - sb->start; size_t alloc = sb->end - sb->start; do { alloc *= 2; } while (alloc < length + need); sb->start = (char*) realloc(sb->start, alloc + 1); if (sb->start == NULL) out_of_memory(); sb->cur = sb->start + length; sb->end = sb->start + alloc; } static void sb_put(SB *sb, const char *bytes, int count) { sb_need(sb, count); memcpy(sb->cur, bytes, count); sb->cur += count; } #define sb_putc(sb, c) do { \ if ((sb)->cur >= (sb)->end) \ sb_grow(sb, 1); \ *(sb)->cur++ = (c); \ } while (0) static void sb_puts(SB *sb, const char *str) { sb_put(sb, str, (int)strlen(str)); } static char *sb_finish(SB *sb) { *sb->cur = 0; assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start)); return sb->start; } static void sb_free(SB *sb) { free(sb->start); } /* * Unicode helper functions * * These are taken from the ccan/charset module and customized a bit. * Putting them here means the compiler can (choose to) inline them, * and it keeps ccan/json from having a dependency. * * We use uint32_t Type for Unicode codepoints. * We need our own because wchar_t might be 16 bits. */ /* * Validate a single UTF-8 character starting at @s. * The string must be null-terminated. * * If it's valid, return its length (1 thru 4). * If it's invalid or clipped, return 0. * * This function implements the syntax given in RFC3629, which is * the same as that given in The Unicode Standard, Version 6.0. * * It has the following properties: * * * All codepoints U+0000..U+10FFFF may be encoded, * except for U+D800..U+DFFF, which are reserved * for UTF-16 surrogate pair encoding. * * UTF-8 byte sequences longer than 4 bytes are not permitted, * as they exceed the range of Unicode. * * The sixty-six Unicode "non-characters" are permitted * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF). */ static int utf8_validate_cz(const char *s) { unsigned char c = *s++; if (c <= 0x7F) { /* 00..7F */ return 1; } else if (c <= 0xC1) { /* 80..C1 */ /* Disallow overlong 2-byte sequence. */ return 0; } else if (c <= 0xDF) { /* C2..DF */ /* Make sure subsequent byte is in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 2; } else if (c <= 0xEF) { /* E0..EF */ /* Disallow overlong 3-byte sequence. */ if (c == 0xE0 && (unsigned char)*s < 0xA0) return 0; /* Disallow U+D800..U+DFFF. */ if (c == 0xED && (unsigned char)*s > 0x9F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 3; } else if (c <= 0xF4) { /* F0..F4 */ /* Disallow overlong 4-byte sequence. */ if (c == 0xF0 && (unsigned char)*s < 0x90) return 0; /* Disallow codepoints beyond U+10FFFF. */ if (c == 0xF4 && (unsigned char)*s > 0x8F) return 0; /* Make sure subsequent bytes are in the range 0x80..0xBF. */ if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; if (((unsigned char)*s++ & 0xC0) != 0x80) return 0; return 4; } else { /* F5..FF */ return 0; } } /* Validate a null-terminated UTF-8 string. */ static bool utf8_validate(const char *s) { int len; for (; *s != 0; s += len) { len = utf8_validate_cz(s); if (len == 0) return false; } return true; } /* * Read a single UTF-8 character starting at @s, * returning the length, in bytes, of the character read. * * This function assumes input is valid UTF-8, * and that there are enough characters in front of @s. */ static int utf8_read_char(const char *s, uint32_t *out) { const unsigned char *c = (const unsigned char*) s; assert(utf8_validate_cz(s)); if (c[0] <= 0x7F) { /* 00..7F */ *out = c[0]; return 1; } else if (c[0] <= 0xDF) { /* C2..DF (unless input is invalid) */ *out = ((uint32_t)c[0] & 0x1F) << 6 | ((uint32_t)c[1] & 0x3F); return 2; } else if (c[0] <= 0xEF) { /* E0..EF */ *out = ((uint32_t)c[0] & 0xF) << 12 | ((uint32_t)c[1] & 0x3F) << 6 | ((uint32_t)c[2] & 0x3F); return 3; } else { /* F0..F4 (unless input is invalid) */ *out = ((uint32_t)c[0] & 0x7) << 18 | ((uint32_t)c[1] & 0x3F) << 12 | ((uint32_t)c[2] & 0x3F) << 6 | ((uint32_t)c[3] & 0x3F); return 4; } } /* * Write a single UTF-8 character to @s, * returning the length, in bytes, of the character written. * * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF. * * This function will write up to 4 bytes to @out. */ static int utf8_write_char(uint32_t unicode, char *out) { unsigned char *o = (unsigned char*) out; assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF)); if (unicode <= 0x7F) { /* U+0000..U+007F */ *o++ = unicode; return 1; } else if (unicode <= 0x7FF) { /* U+0080..U+07FF */ *o++ = 0xC0 | unicode >> 6; *o++ = 0x80 | (unicode & 0x3F); return 2; } else if (unicode <= 0xFFFF) { /* U+0800..U+FFFF */ *o++ = 0xE0 | unicode >> 12; *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 3; } else { /* U+10000..U+10FFFF */ *o++ = 0xF0 | unicode >> 18; *o++ = 0x80 | (unicode >> 12 & 0x3F); *o++ = 0x80 | (unicode >> 6 & 0x3F); *o++ = 0x80 | (unicode & 0x3F); return 4; } } /* * Compute the Unicode codepoint of a UTF-16 surrogate pair. * * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF. * If they aren't, this function returns false. */ static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode) { if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) { *unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF)); return true; } else { return false; } } /* * Construct a UTF-16 surrogate pair given a Unicode codepoint. * * @unicode must be U+10000..U+10FFFF. */ static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc) { uint32_t n; assert(unicode >= 0x10000 && unicode <= 0x10FFFF); n = unicode - 0x10000; *uc = ((n >> 10) & 0x3FF) | 0xD800; *lc = (n & 0x3FF) | 0xDC00; } static bool is_space (const char *c); static bool is_digit (const char *c); static bool parse_value (const char **sp, JsonNode **out); static bool parse_string (const char **sp, char **out); static bool parse_number (const char **sp, double *out); static bool parse_array (const char **sp, JsonNode **out); static bool parse_object (const char **sp, JsonNode **out); static bool parse_hex16 (const char **sp, uint16_t *out); static bool expect_literal (const char **sp, const char *str); static void skip_space (const char **sp); static void emit_value (SB *out, const JsonNode *node); static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level); static void emit_string (SB *out, const char *str); static void emit_number (SB *out, double num); static void emit_array (SB *out, const JsonNode *array); static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level); static void emit_object (SB *out, const JsonNode *object); static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level); static int write_hex16(char *out, uint16_t val); static JsonNode *mknode(JsonTag tag); static void append_node(JsonNode *parent, JsonNode *child); static void prepend_node(JsonNode *parent, JsonNode *child); static void append_member(JsonNode *object, char *key, JsonNode *value); /* Assertion-friendly validity checks */ static bool tag_is_valid(unsigned int tag); static bool number_is_valid(const char *num); JsonNode *json_decode(const char *json) { const char *s = json; JsonNode *ret; skip_space(&s); if (!parse_value(&s, &ret)) return NULL; skip_space(&s); if (*s != 0) { json_delete(ret); return NULL; } return ret; } char *json_encode(const JsonNode *node) { return json_stringify(node, NULL); } char *json_encode_string(const char *str) { SB sb; sb_init(&sb); emit_string(&sb, str); return sb_finish(&sb); } char *json_stringify(const JsonNode *node, const char *space) { SB sb; sb_init(&sb); if (space != NULL) emit_value_indented(&sb, node, space, 0); else emit_value(&sb, node); return sb_finish(&sb); } void json_delete(JsonNode *node) { if (node != NULL) { json_remove_from_parent(node); switch (node->tag) { case JSON_STRING: free(node->string_); break; case JSON_ARRAY: case JSON_OBJECT: { JsonNode *child, *next; for (child = node->children.head; child != NULL; child = next) { next = child->next; json_delete(child); } break; } default:; } free(node); } } bool json_validate(const char *json) { const char *s = json; skip_space(&s); if (!parse_value(&s, NULL)) return false; skip_space(&s); if (*s != 0) return false; return true; } JsonNode *json_find_element(JsonNode *array, int index) { JsonNode *element; int i = 0; if (array == NULL || array->tag != JSON_ARRAY) return NULL; json_foreach(element, array) { if (i == index) return element; i++; } return NULL; } JsonNode *json_find_member(JsonNode *object, const char *name) { JsonNode *member; if (object == NULL || object->tag != JSON_OBJECT) return NULL; json_foreach(member, object) if (strcmp(member->key, name) == 0) return member; return NULL; } JsonNode *json_first_child(const JsonNode *node) { if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT)) return node->children.head; return NULL; } static JsonNode *mknode(JsonTag tag) { JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode)); if (ret == NULL) out_of_memory(); ret->tag = tag; return ret; } JsonNode *json_mknull(void) { return mknode(JSON_NULL); } JsonNode *json_mkbool(bool b) { JsonNode *ret = mknode(JSON_BOOL); ret->bool_ = b; return ret; } static JsonNode *mkstring(char *s) { JsonNode *ret = mknode(JSON_STRING); ret->string_ = s; return ret; } JsonNode *json_mkstring(const char *s) { return mkstring(json_strdup(s)); } JsonNode *json_mknumber(double n) { JsonNode *node = mknode(JSON_NUMBER); node->number_ = n; return node; } JsonNode *json_mkarray(void) { return mknode(JSON_ARRAY); } JsonNode *json_mkobject(void) { return mknode(JSON_OBJECT); } static void append_node(JsonNode *parent, JsonNode *child) { if (child != NULL && parent != NULL) { child->parent = parent; child->prev = parent->children.tail; child->next = NULL; if (parent->children.tail != NULL) parent->children.tail->next = child; else parent->children.head = child; parent->children.tail = child; } } static void prepend_node(JsonNode *parent, JsonNode *child) { if (child != NULL && parent != NULL) { child->parent = parent; child->prev = NULL; child->next = parent->children.head; if (parent->children.head != NULL) parent->children.head->prev = child; else parent->children.tail = child; parent->children.head = child; } } static void append_member(JsonNode *object, char *key, JsonNode *value) { if (value != NULL && object != NULL) { value->key = key; append_node(object, value); } } void json_append_element(JsonNode *array, JsonNode *element) { if (array != NULL && element !=NULL) { assert(array->tag == JSON_ARRAY); assert(element->parent == NULL); append_node(array, element); } } void json_prepend_element(JsonNode *array, JsonNode *element) { assert(array->tag == JSON_ARRAY); assert(element->parent == NULL); prepend_node(array, element); } void json_append_member(JsonNode *object, const char *key, JsonNode *value) { if (object != NULL && key != NULL && value != NULL) { assert(object->tag == JSON_OBJECT); assert(value->parent == NULL); append_member(object, json_strdup(key), value); } } void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) { if (object != NULL && key != NULL && value != NULL) { assert(object->tag == JSON_OBJECT); assert(value->parent == NULL); value->key = json_strdup(key); prepend_node(object, value); } } void json_remove_from_parent(JsonNode *node) { if (node != NULL) { JsonNode *parent = node->parent; if (parent != NULL) { if (node->prev != NULL) node->prev->next = node->next; else parent->children.head = node->next; if (node->next != NULL) node->next->prev = node->prev; else parent->children.tail = node->prev; free(node->key); node->parent = NULL; node->prev = node->next = NULL; node->key = NULL; } } } static bool parse_value(const char **sp, JsonNode **out) { const char *s = *sp; switch (*s) { case 'n': if (expect_literal(&s, "null")) { if (out) *out = json_mknull(); *sp = s; return true; } return false; case 'f': if (expect_literal(&s, "false")) { if (out) *out = json_mkbool(false); *sp = s; return true; } return false; case 't': if (expect_literal(&s, "true")) { if (out) *out = json_mkbool(true); *sp = s; return true; } return false; case '"': { char *str = NULL; if (parse_string(&s, out ? &str : NULL)) { if (out) *out = mkstring(str); *sp = s; return true; } return false; } case '[': if (parse_array(&s, out)) { *sp = s; return true; } return false; case '{': if (parse_object(&s, out)) { *sp = s; return true; } return false; default: { double num; if (parse_number(&s, out ? &num : NULL)) { if (out) *out = json_mknumber(num); *sp = s; return true; } return false; } } } static bool parse_array(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkarray() : NULL; JsonNode *element = NULL; if (*s++ != '[') goto failure; skip_space(&s); if (*s == ']') { s++; goto success; } for (;;) { if (!parse_value(&s, out ? &element : NULL)) goto failure; skip_space(&s); if (out) json_append_element(ret, element); if (*s == ']') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure: json_delete(ret); return false; } static bool parse_object(const char **sp, JsonNode **out) { const char *s = *sp; JsonNode *ret = out ? json_mkobject() : NULL; char *key = NULL; JsonNode *value = NULL; if (*s++ != '{') goto failure; skip_space(&s); if (*s == '}') { s++; goto success; } for (;;) { if (!parse_string(&s, out ? &key : NULL)) goto failure; skip_space(&s); if (*s++ != ':') goto failure_free_key; skip_space(&s); if (!parse_value(&s, out ? &value : NULL)) goto failure_free_key; skip_space(&s); if (out) append_member(ret, key, value); if (*s == '}') { s++; goto success; } if (*s++ != ',') goto failure; skip_space(&s); } success: *sp = s; if (out) *out = ret; return true; failure_free_key: if (out) free(key); failure: json_delete(ret); return false; } bool parse_string(const char **sp, char **out) { const char *s = *sp; SB sb = { 0, 0, 0 }; char throwaway_buffer[4]; /* enough space for a UTF-8 character */ char *b; if (*s++ != '"') return false; if (out) { sb_init(&sb); sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } while (*s != '"') { unsigned char c = *s++; /* Parse next character, and write it to b. */ if (c == '\\') { c = *s++; switch (c) { case '"': case '\\': case '/': *b++ = c; break; case 'b': *b++ = '\b'; break; case 'f': *b++ = '\f'; break; case 'n': *b++ = '\n'; break; case 'r': *b++ = '\r'; break; case 't': *b++ = '\t'; break; case 'u': { uint16_t uc, lc; uint32_t unicode; if (!parse_hex16(&s, &uc)) goto failed; if (uc >= 0xD800 && uc <= 0xDFFF) { /* Handle UTF-16 surrogate pair. */ if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc)) goto failed; /* Incomplete surrogate pair. */ if (!from_surrogate_pair(uc, lc, &unicode)) goto failed; /* Invalid surrogate pair. */ } else if (uc == 0) { /* Disallow "\u0000". */ goto failed; } else { unicode = uc; } b += utf8_write_char(unicode, b); break; } default: /* Invalid escape */ goto failed; } } else if (c <= 0x1F) { /* Control characters are not allowed in string literals. */ goto failed; } else { /* Validate and echo a UTF-8 character. */ int len; s--; len = utf8_validate_cz(s); if (len == 0) goto failed; /* Invalid UTF-8 character. */ while (len--) *b++ = *s++; } /* * Update sb to know about the new bytes, * and set up b to write another character. */ if (out) { sb.cur = b; sb_need(&sb, 4); b = sb.cur; } else { b = throwaway_buffer; } } s++; if (out) *out = sb_finish(&sb); *sp = s; return true; failed: if (out) sb_free(&sb); return false; } bool is_space(const char *c) { return ((*c) == '\t' || (*c) == '\n' || (*c) == '\r' || (*c) == ' '); } bool is_digit(const char *c){ return ((*c) >= '0' && (*c) <= '9'); } /* * The JSON spec says that a number shall follow this precise pattern * (spaces and quotes added for readability): * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)? * * However, some JSON parsers are more liberal. For instance, PHP accepts * '.5' and '1.'. JSON.parse accepts '+3'. * * This function takes the strict approach. */ bool parse_number(const char **sp, double *out) { const char *s = *sp; /* '-'? */ if (*s == '-') s++; /* (0 | [1-9][0-9]*) */ if (*s == '0') { s++; } else { if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } /* ('.' [0-9]+)? */ if (*s == '.') { s++; if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } /* ([Ee] [+-]? [0-9]+)? */ if (*s == 'E' || *s == 'e') { s++; if (*s == '+' || *s == '-') s++; if (!is_digit(s)) return false; do { s++; } while (is_digit(s)); } if (out) *out = strtod(*sp, NULL); *sp = s; return true; } static void skip_space(const char **sp) { const char *s = *sp; while (is_space(s)) s++; *sp = s; } static void emit_value(SB *out, const JsonNode *node) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array(out, node); break; case JSON_OBJECT: emit_object(out, node); break; default: assert(false); } } void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level) { assert(tag_is_valid(node->tag)); switch (node->tag) { case JSON_NULL: sb_puts(out, "null"); break; case JSON_BOOL: sb_puts(out, node->bool_ ? "true" : "false"); break; case JSON_STRING: emit_string(out, node->string_); break; case JSON_NUMBER: emit_number(out, node->number_); break; case JSON_ARRAY: emit_array_indented(out, node, space, indent_level); break; case JSON_OBJECT: emit_object_indented(out, node, space, indent_level); break; default: assert(false); } } static void emit_array(SB *out, const JsonNode *array) { const JsonNode *element; sb_putc(out, '['); json_foreach(element, array) { emit_value(out, element); if (element->next != NULL) sb_putc(out, ','); } sb_putc(out, ']'); } static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level) { const JsonNode *element = array->children.head; int i; if (element == NULL) { sb_puts(out, "[]"); return; } sb_puts(out, "[\n"); while (element != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_value_indented(out, element, space, indent_level + 1); element = element->next; sb_puts(out, element != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, ']'); } static void emit_object(SB *out, const JsonNode *object) { const JsonNode *member; sb_putc(out, '{'); json_foreach(member, object) { emit_string(out, member->key); sb_putc(out, ':'); emit_value(out, member); if (member->next != NULL) sb_putc(out, ','); } sb_putc(out, '}'); } static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level) { const JsonNode *member = object->children.head; int i; if (member == NULL) { sb_puts(out, "{}"); return; } sb_puts(out, "{\n"); while (member != NULL) { for (i = 0; i < indent_level + 1; i++) sb_puts(out, space); emit_string(out, member->key); sb_puts(out, ": "); emit_value_indented(out, member, space, indent_level + 1); member = member->next; sb_puts(out, member != NULL ? ",\n" : "\n"); } for (i = 0; i < indent_level; i++) sb_puts(out, space); sb_putc(out, '}'); } void emit_string(SB *out, const char *str) { bool escape_unicode = false; const char *s = str; char *b; assert(utf8_validate(str)); /* * 14 bytes is enough space to write up to two * \uXXXX escapes and two quotation marks. */ sb_need(out, 14); b = out->cur; *b++ = '"'; while (*s != 0) { unsigned char c = *s++; /* Encode the next character, and write it to b. */ switch (c) { case '"': *b++ = '\\'; *b++ = '"'; break; case '\\': *b++ = '\\'; *b++ = '\\'; break; case '\b': *b++ = '\\'; *b++ = 'b'; break; case '\f': *b++ = '\\'; *b++ = 'f'; break; case '\n': *b++ = '\\'; *b++ = 'n'; break; case '\r': *b++ = '\\'; *b++ = 'r'; break; case '\t': *b++ = '\\'; *b++ = 't'; break; default: { int len; s--; len = utf8_validate_cz(s); if (len == 0) { /* * Handle invalid UTF-8 character gracefully in production * by writing a replacement character (U+FFFD) * and skipping a single byte. * * This should never happen when assertions are enabled * due to the assertion at the beginning of this function. */ assert(false); if (escape_unicode) { strcpy(b, "\\uFFFD"); b += 6; } else { *b++ = 0xEFu; *b++ = 0xBFu; *b++ = 0xBDu; } s++; } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) { /* Encode using \u.... */ uint32_t unicode; s += utf8_read_char(s, &unicode); if (unicode <= 0xFFFF) { *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, unicode); } else { /* Produce a surrogate pair. */ uint16_t uc, lc; assert(unicode <= 0x10FFFF); to_surrogate_pair(unicode, &uc, &lc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, uc); *b++ = '\\'; *b++ = 'u'; b += write_hex16(b, lc); } } else { /* Write the character directly. */ while (len--) *b++ = *s++; } break; } } /* * Update *out to know about the new bytes, * and set up b to write another encoded character. */ out->cur = b; sb_need(out, 14); b = out->cur; } *b++ = '"'; out->cur = b; } static void emit_number(SB *out, double num) { /* * This isn't exactly how JavaScript renders numbers, * but it should produce valid JSON for reasonable numbers * preserve precision well enough, and avoid some oddities * like 0.3 -> 0.299999999999999988898 . */ char buf[64]; sprintf(buf, "%.16g", num); if (number_is_valid(buf)) sb_puts(out, buf); else sb_puts(out, "null"); } static bool tag_is_valid(unsigned int tag) { return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT); } static bool number_is_valid(const char *num) { return (parse_number(&num, NULL) && *num == '\0'); } static bool expect_literal(const char **sp, const char *str) { const char *s = *sp; while (*str != '\0') if (*s++ != *str++) return false; *sp = s; return true; } /* * Parses exactly 4 hex characters (capital or lowercase). * Fails if any input chars are not [0-9A-Fa-f]. */ static bool parse_hex16(const char **sp, uint16_t *out) { const char *s = *sp; uint16_t ret = 0; uint16_t i; uint16_t tmp; char c; for (i = 0; i < 4; i++) { c = *s++; if (c >= '0' && c <= '9') tmp = c - '0'; else if (c >= 'A' && c <= 'F') tmp = c - 'A' + 10; else if (c >= 'a' && c <= 'f') tmp = c - 'a' + 10; else return false; ret <<= 4; ret += tmp; } if (out) *out = ret; *sp = s; return true; } /* * Encodes a 16-bit number into hexadecimal, * writing exactly 4 hex chars. */ static int write_hex16(char *out, uint16_t val) { const char *hex = "0123456789ABCDEF"; *out++ = hex[(val >> 12) & 0xF]; *out++ = hex[(val >> 8) & 0xF]; *out++ = hex[(val >> 4) & 0xF]; *out++ = hex[ val & 0xF]; return 4; } bool json_check(const JsonNode *node, char errmsg[256]) { #define problem(...) do { \ if (errmsg != NULL) \ snprintf(errmsg, 256, __VA_ARGS__); \ return false; \ } while (0) if (node->key != NULL && !utf8_validate(node->key)) problem("key contains invalid UTF-8"); if (!tag_is_valid(node->tag)) problem("tag is invalid (%u)", node->tag); if (node->tag == JSON_BOOL) { if (node->bool_ != false && node->bool_ != true) problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true); } else if (node->tag == JSON_STRING) { if (node->string_ == NULL) problem("string_ is NULL"); if (!utf8_validate(node->string_)) problem("string_ contains invalid UTF-8"); } else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) { JsonNode *head = node->children.head; JsonNode *tail = node->children.tail; if (head == NULL || tail == NULL) { if (head != NULL) problem("tail is NULL, but head is not"); if (tail != NULL) problem("head is NULL, but tail is not"); } else { JsonNode *child; JsonNode *last = NULL; if (head->prev != NULL) problem("First child's prev pointer is not NULL"); for (child = head; child != NULL; last = child, child = child->next) { if (child == node) problem("node is its own child"); if (child->next == child) problem("child->next == child (cycle)"); if (child->next == head) problem("child->next == head (cycle)"); if (child->parent != node) problem("child does not point back to parent"); if (child->next != NULL && child->next->prev != child) problem("child->next does not point back to child"); if (node->tag == JSON_ARRAY && child->key != NULL) problem("Array element's key is not NULL"); if (node->tag == JSON_OBJECT && child->key == NULL) problem("Object member's key is NULL"); if (!json_check(child, errmsg)) return false; } if (last != tail) problem("tail does not match pointer found by starting at head and following next links"); } } return true; #undef problem } libsass-3.3.4/src/json.hpp000066400000000000000000000067261267254216700154610ustar00rootroot00000000000000/* Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com) All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef CCAN_JSON_H #define CCAN_JSON_H #include #include typedef enum { JSON_NULL, JSON_BOOL, JSON_STRING, JSON_NUMBER, JSON_ARRAY, JSON_OBJECT, } JsonTag; typedef struct JsonNode JsonNode; struct JsonNode { /* only if parent is an object or array (NULL otherwise) */ JsonNode *parent; JsonNode *prev, *next; /* only if parent is an object (NULL otherwise) */ char *key; /* Must be valid UTF-8. */ JsonTag tag; union { /* JSON_BOOL */ bool bool_; /* JSON_STRING */ char *string_; /* Must be valid UTF-8. */ /* JSON_NUMBER */ double number_; /* JSON_ARRAY */ /* JSON_OBJECT */ struct { JsonNode *head, *tail; } children; }; }; /*** Encoding, decoding, and validation ***/ JsonNode *json_decode (const char *json); char *json_encode (const JsonNode *node); char *json_encode_string (const char *str); char *json_stringify (const JsonNode *node, const char *space); void json_delete (JsonNode *node); bool json_validate (const char *json); /*** Lookup and traversal ***/ JsonNode *json_find_element (JsonNode *array, int index); JsonNode *json_find_member (JsonNode *object, const char *key); JsonNode *json_first_child (const JsonNode *node); #define json_foreach(i, object_or_array) \ for ((i) = json_first_child(object_or_array); \ (i) != NULL; \ (i) = (i)->next) /*** Construction and manipulation ***/ JsonNode *json_mknull(void); JsonNode *json_mkbool(bool b); JsonNode *json_mkstring(const char *s); JsonNode *json_mknumber(double n); JsonNode *json_mkarray(void); JsonNode *json_mkobject(void); void json_append_element(JsonNode *array, JsonNode *element); void json_prepend_element(JsonNode *array, JsonNode *element); void json_append_member(JsonNode *object, const char *key, JsonNode *value); void json_prepend_member(JsonNode *object, const char *key, JsonNode *value); void json_remove_from_parent(JsonNode *node); /*** Debugging ***/ /* * Look for structure and encoding problems in a JsonNode or its descendents. * * If a problem is detected, return false, writing a description of the problem * to errmsg (unless errmsg is NULL). */ bool json_check(const JsonNode *node, char errmsg[256]); #endif libsass-3.3.4/src/kwd_arg_macros.hpp000066400000000000000000000010021267254216700174500ustar00rootroot00000000000000#ifndef SASS_KWD_ARG_MACROS_H #define SASS_KWD_ARG_MACROS_H // Example usage: // KWD_ARG_SET(Args) { // KWD_ARG(Args, string, foo); // KWD_ARG(Args, int, bar); // ... // }; // // ... and later ... // // something(Args().foo("hey").bar(3)); #define KWD_ARG_SET(set_name) class set_name #define KWD_ARG(set_name, type, name) \ private: \ type name##_; \ public: \ set_name& name(type name##__) { \ name##_ = name##__; \ return *this; \ } \ type name() { return name##_; } \ private: #endif libsass-3.3.4/src/lexer.cpp000066400000000000000000000133771267254216700156220ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include "lexer.hpp" #include "constants.hpp" namespace Sass { using namespace Constants; namespace Prelexer { //#################################### // BASIC CHARACTER MATCHERS //#################################### // Match standard control chars const char* kwd_at(const char* src) { return exactly<'@'>(src); } const char* kwd_dot(const char* src) { return exactly<'.'>(src); } const char* kwd_comma(const char* src) { return exactly<','>(src); }; const char* kwd_colon(const char* src) { return exactly<':'>(src); }; const char* kwd_star(const char* src) { return exactly<'*'>(src); }; const char* kwd_plus(const char* src) { return exactly<'+'>(src); }; 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_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) { return ( (unsigned(chr) > 127 && unsigned(chr) < 55296) || (unsigned(chr) > 57343 && unsigned(chr) < 65534) || (unsigned(chr) > 65535 && unsigned(chr) < 1114111) ); } // check if char is within a reduced ascii range // valid in a uri (copied from Ruby Sass) bool is_uri_character(const char& chr) { return unsigned(chr) > 41 && unsigned(chr) < 127; } // check if char is within a reduced ascii range // valid for escaping (copied from Ruby Sass) bool is_escapable_character(const char& chr) { return unsigned(chr) > 31 && unsigned(chr) < 127; } // Match word character (look ahead) bool is_character(const char& chr) { // valid alpha, numeric or unicode char (plus hyphen) return is_alnum(chr) || is_unicode(chr) || chr == '-'; } //#################################### // BASIC CLASS MATCHERS //#################################### // 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* character(const char* src) { return is_character(*src) ? src + 1 : 0; } const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; } const char* escapable_character(const char* src) { return is_escapable_character(*src) ? src + 1 : 0; } // Match multiple ctype characters. const char* spaces(const char* src) { return one_plus(src); } const char* digits(const char* src) { return one_plus(src); } // Whitespace handling. const char* no_spaces(const char* src) { return negate< space >(src); } const char* optional_spaces(const char* src) { return zero_plus< space >(src); } // Match any single character. const char* any_char(const char* src) { return *src ? src + 1 : src; } // Match word boundary (zero-width lookahead). const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } // Match linefeed /(?:\n|\r\n?)/ const char* re_linebreak(const char* src) { // end of file or unix linefeed return here if (*src == 0 || *src == '\n') 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 return 0; } // Assert string boundaries (/\Z|\z|\A/) // This is a zero-width positive lookahead 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; } // Assert end_of_file boundary (/\z/) // This is a zero-width positive lookahead const char* end_of_file(const char* src) { // end of file or unix linefeed return here return *src == 0 ? src : 0; } } } libsass-3.3.4/src/lexer.hpp000066400000000000000000000204631267254216700156210ustar00rootroot00000000000000#ifndef SASS_LEXER_H #define SASS_LEXER_H #include namespace Sass { namespace Prelexer { //#################################### // BASIC CHARACTER MATCHERS //#################################### // Match standard control chars const char* kwd_at(const char* src); const char* kwd_dot(const char* src); const char* kwd_comma(const char* src); const char* kwd_colon(const char* src); const char* kwd_star(const char* src); const char* kwd_plus(const char* src); const char* kwd_minus(const char* src); const char* kwd_slash(const char* src); //#################################### // 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_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); // 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* 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); // Match multiple ctype characters. const char* spaces(const char* src); const char* digits(const char* src); // Whitespace handling. const char* no_spaces(const char* src); const char* optional_spaces(const char* src); // Match any single character (/./). const char* any_char(const char* src); // Assert word boundary (/\b/) // Is a zero-width positive lookaheads const char* word_boundary(const char* src); // Match a single linebreak (/(?:\n|\r\n?)/). const char* re_linebreak(const char* src); // Assert string boundaries (/\Z|\z|\A/) // There are zero-width positive lookaheads const char* end_of_line(const char* src); // Assert end_of_file boundary (/\z/) const char* end_of_file(const char* src); // const char* start_of_string(const char* src); // Type definition for prelexer functions typedef const char* (*prelexer)(const char*); //#################################### // BASIC "REGEX" CONSTRUCTORS //#################################### // Match a single character literal. // Regex equivalent: /(?:x)/ template const char* exactly(const char* src) { return *src == chr ? src + 1 : 0; } // Match the full string literal. // Regex equivalent: /(?:literal)/ template const char* exactly(const char* src) { if (str == 0) return 0; const char* pre = str; if (src == 0) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && *src == *pre) { ++src, ++pre; } // did the matcher finish? return *pre == 0 ? src : 0; } // Match the full string literal. // Regex equivalent: /(?:literal)/i // only define lower case alpha chars template const char* insensitive(const char* src) { if (str == 0) return 0; const char* pre = str; if (src == 0) return 0; // there is a small chance that the search string // is longer than the rest of the string to look at while (*pre && (*src == *pre || *src+32 == *pre)) { ++src, ++pre; } // did the matcher finish? return *pre == 0 ? src : 0; } // Match for members of char class. // Regex equivalent: /[axy]/ template const char* class_char(const char* src) { const char* cc = char_class; while (*cc && *src != *cc) ++cc; return *cc ? src + 1 : 0; } // Match for members of char class. // Regex equivalent: /[axy]+/ template const char* class_chars(const char* src) { const char* p = src; while (class_char(p)) ++p; return p == src ? 0 : p; } // Match all except the supplied one. // Regex equivalent: /[^x]/ template const char* any_char_but(const char* src) { return (*src && *src != chr) ? src + 1 : 0; } // Succeeds if the matcher fails. // Aka. zero-width negative lookahead. // Regex equivalent: /(?!literal)/ template const char* negate(const char* src) { return mx(src) ? 0 : src; } // Succeeds if the matcher succeeds. // Aka. zero-width positive lookahead. // Regex equivalent: /(?=literal)/ // just hangs around until we need it template const char* lookahead(const char* src) { return mx(src) ? src : 0; } // Tries supplied matchers in order. // Succeeds if one of them succeeds. // Regex equivalent: /(?:FOO|BAR)/ template const char* alternatives(const char* src) { const char* rslt; if ((rslt = mx(src))) return rslt; return 0; } template const char* alternatives(const char* src) { const char* rslt; if ((rslt = mx1(src))) return rslt; return alternatives(src); } // Tries supplied matchers in order. // Succeeds if all of them succeeds. // Regex equivalent: /(?:FOO)(?:BAR)/ template const char* sequence(const char* src) { const char* rslt = src; if (!(rslt = mx1(rslt))) return 0; return rslt; } template const char* sequence(const char* src) { const char* rslt = src; if (!(rslt = mx1(rslt))) return 0; return sequence(rslt); } // Match a pattern or not. Always succeeds. // Regex equivalent: /(?:literal)?/ template const char* optional(const char* src) { const char* p = mx(src); return p ? p : src; } // Match zero or more of the patterns. // Regex equivalent: /(?:literal)*/ template const char* zero_plus(const char* src) { const char* p = mx(src); while (p) src = p, p = mx(src); return src; } // Match one or more of the patterns. // Regex equivalent: /(?:literal)+/ template const char* one_plus(const char* src) { const char* p = mx(src); if (!p) return 0; while (p) src = p, p = mx(src); return src; } // Match mx non-greedy until delimiter. // Other prelexers are greedy by default. // Regex equivalent: /(?:$mx)*?(?=$delim)\b/ template const char* non_greedy(const char* src) { while (!delim(src)) { const char* p = mx(src); if (p == src) return 0; if (p == 0) return 0; src = p; } return src; } //#################################### // ADVANCED "REGEX" CONSTRUCTORS //#################################### // Match with word boundary rule. // Regex equivalent: /(?:$mx)\b/i template const char* keyword(const char* src) { return sequence < insensitive < str >, word_boundary >(src); } // Match with word boundary rule. // Regex equivalent: /(?:$mx)\b/ template const char* word(const char* src) { return sequence < exactly < str >, word_boundary >(src); } template const char* loosely(const char* src) { return sequence < optional_spaces, exactly < chr > >(src); } template const char* loosely(const char* src) { return sequence < optional_spaces, exactly < str > >(src); } } } #endif libsass-3.3.4/src/listize.cpp000066400000000000000000000044361267254216700161620ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include "listize.hpp" #include "context.hpp" #include "backtrace.hpp" #include "error_handling.hpp" namespace Sass { Listize::Listize(Memory_Manager& mem) : mem(mem) { } Expression* Listize::operator()(Selector_List* sel) { List* l = SASS_MEMORY_NEW(mem, List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { if (!(*sel)[i]) continue; *l << (*sel)[i]->perform(this); } if (l->length()) return l; return SASS_MEMORY_NEW(mem, Null, l->pstate()); } Expression* Listize::operator()(Compound_Selector* sel) { std::string str; for (size_t i = 0, L = sel->length(); i < L; ++i) { Expression* e = (*sel)[i]->perform(this); if (e) str += e->to_string(); } return SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), str); } Expression* Listize::operator()(Complex_Selector* sel) { List* l = SASS_MEMORY_NEW(mem, List, sel->pstate(), 2); Compound_Selector* head = sel->head(); if (head && !head->is_empty_reference()) { Expression* hh = head->perform(this); if (hh) *l << hh; } std::string reference = ! sel->reference() ? "" : sel->reference()->to_string(); switch(sel->combinator()) { case Complex_Selector::PARENT_OF: *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), ">"); break; case Complex_Selector::ADJACENT_TO: *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "+"); break; case Complex_Selector::REFERENCE: *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "/" + reference + "/"); break; case Complex_Selector::PRECEDES: *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "~"); break; case Complex_Selector::ANCESTOR_OF: break; } Complex_Selector* tail = sel->tail(); if (tail) { Expression* tt = tail->perform(this); if (tt && tt->concrete_type() == Expression::LIST) { *l += static_cast(tt); } else if (tt) *l << static_cast(tt); } if (l->length() == 0) return 0; return l; } Expression* Listize::fallback_impl(AST_Node* n) { return dynamic_cast(n); } } libsass-3.3.4/src/listize.hpp000066400000000000000000000012671267254216700161660ustar00rootroot00000000000000#ifndef SASS_LISTIZE_H #define SASS_LISTIZE_H #include #include #include "ast.hpp" #include "context.hpp" #include "operation.hpp" #include "environment.hpp" namespace Sass { typedef Environment Env; struct Backtrace; class Listize : public Operation_CRTP { Memory_Manager& mem; Expression* fallback_impl(AST_Node* n); public: Listize(Memory_Manager&); ~Listize() { } Expression* operator()(Selector_List*); Expression* operator()(Complex_Selector*); Expression* operator()(Compound_Selector*); template Expression* fallback(U x) { return fallback_impl(x); } }; } #endif libsass-3.3.4/src/mapping.hpp000066400000000000000000000005511267254216700161310ustar00rootroot00000000000000#ifndef SASS_MAPPING_H #define SASS_MAPPING_H #include "position.hpp" namespace Sass { struct Mapping { Position original_position; Position generated_position; Mapping(const Position& original_position, const Position& generated_position) : original_position(original_position), generated_position(generated_position) { } }; } #endif libsass-3.3.4/src/memory_manager.cpp000066400000000000000000000035061267254216700174760ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "memory_manager.hpp" namespace Sass { Memory_Manager::Memory_Manager(size_t size) : nodes(std::vector()) { size_t init = size; if (init < 8) init = 8; // reserve some space nodes.reserve(init); } Memory_Manager::~Memory_Manager() { // release memory for all controlled nodes // avoid calling erase for every single node for (size_t i = 0, S = nodes.size(); i < S; ++i) { deallocate(nodes[i]); } // just in case nodes.clear(); } Memory_Object* Memory_Manager::add(Memory_Object* np) { // object has been initialized // it can be "deleted" from now on np->refcount = 1; return np; } bool Memory_Manager::has(Memory_Object* np) { // check if the pointer is controlled under our pool return find(nodes.begin(), nodes.end(), np) != nodes.end(); } Memory_Object* Memory_Manager::allocate(size_t size) { // allocate requested memory void* heap = malloc(size); // init internal refcount status to zero (static_cast(heap))->refcount = 0; // add the memory under our management nodes.push_back(static_cast(heap)); // cast object to its initial type return static_cast(heap); } void Memory_Manager::deallocate(Memory_Object* np) { // only call destructor if initialized if (np->refcount) np->~Memory_Object(); // always free the memory free(np); } void Memory_Manager::remove(Memory_Object* np) { // remove node from pool (no longer active) nodes.erase(find(nodes.begin(), nodes.end(), np)); // you are now in control of the memory } void Memory_Manager::destroy(Memory_Object* np) { // remove from pool remove(np); // release memory deallocate(np); } } libsass-3.3.4/src/memory_manager.hpp000066400000000000000000000031451267254216700175020ustar00rootroot00000000000000#ifndef SASS_MEMORY_MANAGER_H #define SASS_MEMORY_MANAGER_H #include namespace Sass { class Memory_Object { friend class Memory_Manager; long refcount; public: Memory_Object() { refcount = 0; }; virtual ~Memory_Object() {}; }; ///////////////////////////////////////////////////////////////////////////// // A class for tracking allocations of AST_Node objects. The intended usage // is something like: Some_Node* n = new (mem_mgr) Some_Node(...); // Then, at the end of the program, the memory manager will delete all of the // allocated nodes that have been passed to it. // In the future, this class may implement a custom allocator. ///////////////////////////////////////////////////////////////////////////// class Memory_Manager { std::vector nodes; public: Memory_Manager(size_t size = 0); ~Memory_Manager(); bool has(Memory_Object* np); Memory_Object* allocate(size_t size); void deallocate(Memory_Object* np); void remove(Memory_Object* np); void destroy(Memory_Object* np); Memory_Object* add(Memory_Object* np); }; } /////////////////////////////////////////////////////////////////////////////// // Use macros for the allocation task, since overloading operator `new` // has been proven to be flaky under certain compilers (see comment below). /////////////////////////////////////////////////////////////////////////////// #define SASS_MEMORY_NEW(mgr, Class, ...) \ (static_cast(mgr.add(new (mgr.allocate(sizeof(Class))) Class(__VA_ARGS__)))) \ #endif libsass-3.3.4/src/node.cpp000066400000000000000000000244321267254216700154220ustar00rootroot00000000000000#include "sass.hpp" #include #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(Complex_Selector* pSelector, Context& ctx) { NodeDequePtr null; Complex_Selector* pStripped = pSelector->clone(ctx); pStripped->tail(NULL); pStripped->combinator(Complex_Selector::ANCESTOR_OF); Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); if (pSelector) n.got_line_feed = pSelector->has_line_feed(); return n; } Node Node::createCollection() { NodeDequePtr pEmptyCollection = std::make_shared(); return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection); } Node Node::createCollection(const NodeDeque& values) { NodeDequePtr pShallowCopiedCollection = std::make_shared(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* pSelector, NodeDequePtr& pCollection) : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection) { if (pSelector) got_line_feed = pSelector->has_line_feed(); } Node Node::clone(Context& ctx) const { NodeDequePtr pNewCollection = std::make_shared(); if (mpCollection) { for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toClone = *iter; pNewCollection->push_back(toClone.clone(ctx)); } } Node n(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection); n.got_line_feed = got_line_feed; return n; } bool Node::contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const { bool found = false; for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { Node& toTest = *iter; if (nodesEqual(toTest, potentialChild, simpleSelectorOrderDependent)) { found = true; break; } } return found; } bool Node::operator==(const Node& rhs) const { return nodesEqual(*this, rhs, true /*simpleSelectorOrderDependent*/); } bool nodesEqual(const Node& lhs, const Node& rhs, bool simpleSelectorOrderDependent) { if (lhs.type() != rhs.type()) { return false; } if (lhs.isCombinator()) { return lhs.combinator() == rhs.combinator(); } else if (lhs.isNil()) { return true; // no state to check } else if (lhs.isSelector()){ return selectors_equal(*lhs.selector(), *rhs.selector(), simpleSelectorOrderDependent); } else if (lhs.isCollection()) { if (lhs.collection()->size() != rhs.collection()->size()) { return false; } for (NodeDeque::iterator lhsIter = lhs.collection()->begin(), lhsIterEnd = lhs.collection()->end(), rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) { if (!nodesEqual(*lhsIter, *rhsIter, simpleSelectorOrderDependent)) { 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* pToConvert, Context& ctx) { 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* 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(); if (pToConvert->head() == NULL || empty_parent_ref) { } // the first Complex_Selector may contain a dummy head pointer, skip it. if (pToConvert->head() != NULL && !empty_parent_ref) { node.collection()->push_back(Node::createSelector(pToConvert, ctx)); if (has_lf) node.collection()->back().got_line_feed = has_lf; has_lf = false; } 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* nodeToComplexSelector(const Node& toConvert, Context& ctx) { if (toConvert.isNil()) { return NULL; } if (!toConvert.isCollection()) { throw "The node to convert to a Complex_Selector* must be a collection type or nil."; } NodeDeque& childNodes = *toConvert.collection(); std::string noPath(""); Position noPosition(-1, -1, -1); Complex_Selector* pFirst = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL); Complex_Selector* 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()) { pCurrent->tail(child.selector()->clone(ctx)); // JMA - need to clone the selector, because they can end up getting shared across Node collections, and can result in an infinite loop during the call to parentSuperselector() // 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(ctx.mem, 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* fakeHead = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, ParserState("[NODE]"), 1); Parent_Selector* selectorRef = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, ParserState("[NODE]")); fakeHead->elements().push_back(selectorRef); if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed); // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed); pFirst->head(fakeHead); return pFirst; } // A very naive trim function, which removes duplicates in a node // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs Node Node::naiveTrim(Node& seqses, Context& ctx) { std::vector res; std::vector known; NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(), seqsesIterEnd = seqses.collection()->rend(); for (; seqsesIter != seqsesIterEnd; ++seqsesIter) { Node& seqs1 = *seqsesIter; if( seqs1.isSelector() ) { Complex_Selector* sel = seqs1.selector(); std::vector::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-3.3.4/src/node.hpp000066400000000000000000000110621267254216700154220ustar00rootroot00000000000000#ifndef SASS_NODE_H #define SASS_NODE_H #include #include #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 (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 NodeDeque; typedef std::shared_ptr 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* selector() { return mpSelector; } const Complex_Selector* selector() const { return mpSelector; } NodeDequePtr collection() { return mpCollection; } const NodeDequePtr collection() const { return mpCollection; } static Node createCombinator(const Complex_Selector::Combinator& combinator); // This method will clone the selector, stripping off the tail and combinator static Node createSelector(Complex_Selector* pSelector, Context& ctx); static Node createCollection(); static Node createCollection(const NodeDeque& values); static Node createNil(); static Node naiveTrim(Node& seqses, Context& ctx); Node clone(Context& ctx) 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, bool simpleSelectorOrderDependent) 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* pSelector, NodeDequePtr& pCollection); TYPE mType; // TODO: can we union these to save on memory? Complex_Selector::Combinator mCombinator; Complex_Selector* mpSelector; // this is an AST_Node, so it will be handled by the Memory_Manager NodeDequePtr mpCollection; }; #ifdef DEBUG std::ostream& operator<<(std::ostream& os, const Node& node); #endif Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx); Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx); bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent); } #endif libsass-3.3.4/src/operation.hpp000066400000000000000000000246031267254216700165020ustar00rootroot00000000000000#ifndef SASS_OPERATION_H #define SASS_OPERATION_H #include "ast_fwd_decl.hpp" namespace Sass { template class Operation { public: virtual T operator()(AST_Node* x) = 0; virtual ~Operation() { } // statements virtual T operator()(Block* x) = 0; virtual T operator()(Ruleset* x) = 0; virtual T operator()(Propset* x) = 0; virtual T operator()(Bubble* x) = 0; virtual T operator()(Supports_Block* x) = 0; virtual T operator()(Media_Block* x) = 0; virtual T operator()(At_Root_Block* x) = 0; virtual T operator()(At_Rule* x) = 0; virtual T operator()(Keyframe_Rule* x) = 0; virtual T operator()(Declaration* x) = 0; virtual T operator()(Assignment* x) = 0; virtual T operator()(Import* x) = 0; virtual T operator()(Import_Stub* x) = 0; virtual T operator()(Warning* x) = 0; virtual T operator()(Error* x) = 0; virtual T operator()(Debug* x) = 0; virtual T operator()(Comment* x) = 0; virtual T operator()(If* x) = 0; virtual T operator()(For* x) = 0; virtual T operator()(Each* x) = 0; virtual T operator()(While* x) = 0; virtual T operator()(Return* x) = 0; virtual T operator()(Content* x) = 0; virtual T operator()(Extension* x) = 0; virtual T operator()(Definition* x) = 0; virtual T operator()(Mixin_Call* x) = 0; // expressions virtual T operator()(List* x) = 0; virtual T operator()(Map* x) = 0; virtual T operator()(Binary_Expression* x) = 0; virtual T operator()(Unary_Expression* x) = 0; virtual T operator()(Function_Call* x) = 0; virtual T operator()(Function_Call_Schema* x) = 0; virtual T operator()(Custom_Warning* x) = 0; virtual T operator()(Custom_Error* x) = 0; virtual T operator()(Variable* x) = 0; virtual T operator()(Textual* x) = 0; virtual T operator()(Number* x) = 0; virtual T operator()(Color* x) = 0; virtual T operator()(Boolean* x) = 0; virtual T operator()(String_Schema* x) = 0; virtual T operator()(String_Quoted* x) = 0; virtual T operator()(String_Constant* x) = 0; virtual T operator()(Supports_Condition* x) = 0; virtual T operator()(Supports_Operator* x) = 0; virtual T operator()(Supports_Negation* x) = 0; virtual T operator()(Supports_Declaration* x) = 0; virtual T operator()(Supports_Interpolation* x) = 0; virtual T operator()(Media_Query* x) = 0; virtual T operator()(Media_Query_Expression* x) = 0; virtual T operator()(At_Root_Expression* x) = 0; virtual T operator()(Null* x) = 0; virtual T operator()(Parent_Selector* x) = 0; // parameters and arguments virtual T operator()(Parameter* x) = 0; virtual T operator()(Parameters* x) = 0; virtual T operator()(Argument* x) = 0; virtual T operator()(Arguments* x) = 0; // selectors virtual T operator()(Selector_Schema* x) = 0; virtual T operator()(Selector_Placeholder* x) = 0; virtual T operator()(Type_Selector* x) = 0; virtual T operator()(Selector_Qualifier* x) = 0; virtual T operator()(Attribute_Selector* x) = 0; virtual T operator()(Pseudo_Selector* x) = 0; virtual T operator()(Wrapped_Selector* x) = 0; virtual T operator()(Compound_Selector* x) = 0; virtual T operator()(Complex_Selector* x) = 0; virtual T operator()(Selector_List* x) = 0; template T fallback(U x) { return T(); } }; template class Operation_CRTP : public Operation { public: D& impl() { return static_cast(*this); } public: T operator()(AST_Node* x) { return static_cast(this)->fallback(x); } // statements T operator()(Block* x) { return static_cast(this)->fallback(x); } T operator()(Ruleset* x) { return static_cast(this)->fallback(x); } T operator()(Propset* x) { return static_cast(this)->fallback(x); } T operator()(Bubble* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } T operator()(Keyframe_Rule* x) { return static_cast(this)->fallback(x); } T operator()(Declaration* x) { return static_cast(this)->fallback(x); } T operator()(Assignment* x) { return static_cast(this)->fallback(x); } T operator()(Import* x) { return static_cast(this)->fallback(x); } T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } T operator()(Warning* x) { return static_cast(this)->fallback(x); } T operator()(Error* x) { return static_cast(this)->fallback(x); } T operator()(Debug* x) { return static_cast(this)->fallback(x); } T operator()(Comment* x) { return static_cast(this)->fallback(x); } T operator()(If* x) { return static_cast(this)->fallback(x); } T operator()(For* x) { return static_cast(this)->fallback(x); } T operator()(Each* x) { return static_cast(this)->fallback(x); } T operator()(While* x) { return static_cast(this)->fallback(x); } T operator()(Return* x) { return static_cast(this)->fallback(x); } T operator()(Content* x) { return static_cast(this)->fallback(x); } T operator()(Extension* x) { return static_cast(this)->fallback(x); } T operator()(Definition* x) { return static_cast(this)->fallback(x); } T operator()(Mixin_Call* x) { return static_cast(this)->fallback(x); } // expressions T operator()(List* x) { return static_cast(this)->fallback(x); } T operator()(Map* x) { return static_cast(this)->fallback(x); } T operator()(Binary_Expression* x) { return static_cast(this)->fallback(x); } T operator()(Unary_Expression* x) { return static_cast(this)->fallback(x); } T operator()(Function_Call* x) { return static_cast(this)->fallback(x); } T operator()(Function_Call_Schema* x) { return static_cast(this)->fallback(x); } T operator()(Custom_Warning* x) { return static_cast(this)->fallback(x); } T operator()(Custom_Error* x) { return static_cast(this)->fallback(x); } T operator()(Variable* x) { return static_cast(this)->fallback(x); } T operator()(Textual* x) { return static_cast(this)->fallback(x); } T operator()(Number* x) { return static_cast(this)->fallback(x); } T operator()(Color* x) { return static_cast(this)->fallback(x); } T operator()(Boolean* x) { return static_cast(this)->fallback(x); } T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } T operator()(String_Quoted* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Condition* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Operator* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Negation* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Declaration* x) { return static_cast(this)->fallback(x); } T operator()(Supports_Interpolation* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } T operator()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } T operator()(Null* x) { return static_cast(this)->fallback(x); } T operator()(Parent_Selector* x) { return static_cast(this)->fallback(x); } // parameters and arguments T operator()(Parameter* x) { return static_cast(this)->fallback(x); } T operator()(Parameters* x) { return static_cast(this)->fallback(x); } T operator()(Argument* x) { return static_cast(this)->fallback(x); } T operator()(Arguments* x) { return static_cast(this)->fallback(x); } // selectors T operator()(Selector_Schema* x) { return static_cast(this)->fallback(x); } T operator()(Selector_Placeholder* x) { return static_cast(this)->fallback(x); } T operator()(Type_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Selector_Qualifier* x) { return static_cast(this)->fallback(x); } T operator()(Attribute_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Pseudo_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Wrapped_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Compound_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Complex_Selector* x) { return static_cast(this)->fallback(x); } T operator()(Selector_List* x) { return static_cast(this)->fallback(x); } template T fallback(U x) { return T(); } }; } #endif libsass-3.3.4/src/output.cpp000066400000000000000000000242241267254216700160340ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "output.hpp" namespace Sass { Output::Output(Sass_Output_Options& opt) : Inspect(Emitter(opt)), charset(""), top_nodes(0) {} Output::~Output() { } void Output::fallback_impl(AST_Node* n) { return n->perform(this); } void Output::operator()(Number* n) { // use values to_string facility std::string res = n->to_string(opt); // check for a valid unit here // includes result for reporting if (!n->is_valid_css_unit()) { throw Exception::InvalidValue(*n); } // output the final token append_token(res, n); } void Output::operator()(Import* imp) { top_nodes.push_back(imp); } void Output::operator()(Map* m) { std::string dbg(m->to_string(opt)); error(dbg + " isn't a valid CSS value.", m->pstate()); } OutputBuffer Output::get_buffer(void) { Emitter emitter(opt); Inspect inspect(emitter); size_t size_nodes = top_nodes.size(); for (size_t i = 0; i < size_nodes; i++) { top_nodes[i]->perform(&inspect); inspect.append_mandatory_linefeed(); } // flush scheduled outputs // maybe omit semicolon if possible inspect.finalize(wbuf.buffer.size() == 0); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed if (!ends_with(wbuf.buffer, opt.linefeed)) { // if the output is not completely empty if (!wbuf.buffer.empty()) append_string(opt.linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars // static cast to unsigned to handle `char` being signed / unsigned if (static_cast(chr) < 128) continue; // declare the charset if (output_style() != COMPRESSED) charset = "@charset \"UTF-8\";" + std::string(opt.linefeed); else charset = "\xEF\xBB\xBF"; // abort search break; } // add charset as first line, before comments and imports if (!charset.empty()) prepend_string(charset); return wbuf; } 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) { if (buffer().size() == 0) { top_nodes.push_back(c); } else { in_comment = true; append_indentation(); c->text()->perform(this); in_comment = false; if (indentation == 0) { append_mandatory_linefeed(); } else { append_optional_linefeed(); } } } } void Output::operator()(Ruleset* r) { Selector* s = r->selector(); Block* b = r->block(); bool decls = false; // Filter out rulesets that aren't printable (process its children though) if (!Util::isPrintable(r, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast(stm)) { stm->perform(this); } } return; } if (b->has_non_hoistable()) { decls = true; if (output_style() == NESTED) indentation += r->tabs(); if (opt.source_comments) { std::stringstream ss; append_indentation(); ss << "/* line " << r->pstate().line + 1 << ", " << r->pstate().path << " */"; append_string(ss.str()); append_optional_linefeed(); } s->perform(this); append_scope_opener(b); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; bool bPrintExpression = true; // Check print conditions if (typeid(*stm) == typeid(Declaration)) { Declaration* dec = static_cast(stm); if (dec->value()->concrete_type() == Expression::STRING) { String_Constant* valConst = static_cast(dec->value()); std::string val(valConst->value()); if (auto qstr = dynamic_cast(valConst)) { if (!qstr->quote_mark() && val.empty()) { bPrintExpression = false; } } } else if (dec->value()->concrete_type() == Expression::LIST) { List* list = static_cast(dec->value()); bool all_invisible = true; for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) { Expression* item = (*list)[list_i]; if (!item->is_invisible()) all_invisible = false; } if (all_invisible) bPrintExpression = false; } } // Print if OK if (!stm->is_hoistable() && bPrintExpression) { stm->perform(this); } } if (output_style() == NESTED) indentation -= r->tabs(); append_scope_closer(b); } if (b->has_hoistable()) { if (decls) ++indentation; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); } } if (decls) --indentation; } } void Output::operator()(Keyframe_Rule* r) { Block* b = r->block(); Selector* v = r->selector(); if (v) { v->perform(this); } if (!b) { append_colon_separator(); return; } append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { stm->perform(this); if (i < L - 1) append_special_linefeed(); } } for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); } } append_scope_closer(); } void Output::operator()(Supports_Block* f) { if (f->is_invisible()) return; Supports_Condition* c = f->condition(); Block* b = f->block(); // Filter out feature blocks that aren't printable (process its children though) if (!Util::isPrintable(f, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast(stm)) { stm->perform(this); } } return; } if (output_style() == NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); c->perform(this); append_scope_opener(); if (b->has_non_hoistable()) { // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { stm->perform(this); } } append_scope_closer(); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); } } } else { // JMA - not hoisted, just output in order for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; stm->perform(this); if (i < L - 1) append_special_linefeed(); } } if (output_style() == NESTED) indentation -= f->tabs(); append_scope_closer(); } void Output::operator()(Media_Block* m) { if (m->is_invisible()) return; List* q = m->media_queries(); Block* b = m->block(); // Filter out media blocks that aren't printable (process its children though) if (!Util::isPrintable(m, output_style())) { for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast(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; q->perform(this); in_media_block = false; append_scope_opener(); for (size_t i = 0, L = b->length(); i < L; ++i) { if ((*b)[i]) (*b)[i]->perform(this); if (i < L - 1) append_special_linefeed(); } if (output_style() == NESTED) indentation -= m->tabs(); append_scope_closer(); } void Output::operator()(At_Rule* a) { std::string kwd = a->keyword(); Selector* s = a->selector(); Expression* v = a->value(); Block* b = a->block(); append_indentation(); append_token(kwd, a); if (s) { append_mandatory_space(); in_wrapped = true; s->perform(this); in_wrapped = false; } if (v) { append_mandatory_space(); // ruby sass bug? should use options? append_token(v->to_string(/* opt */), v); } if (!b) { append_delimiter(); return; } if (b->is_invisible() || b->length() == 0) { append_optional_space(); return append_string("{}"); } append_scope_opener(); bool format = kwd != "@font-face";; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { stm->perform(this); if (i < L - 1 && format) append_special_linefeed(); } } for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); if (i < L - 1 && format) append_special_linefeed(); } } append_scope_closer(); } void Output::operator()(String_Quoted* s) { if (s->quote_mark()) { append_token(quote(s->value(), s->quote_mark()), s); } else if (!in_comment) { append_token(string_to_output(s->value()), s); } else { append_token(s->value(), 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) { append_token(string_to_output(value), s); } else { append_token(value, s); } } } libsass-3.3.4/src/output.hpp000066400000000000000000000024061267254216700160370ustar00rootroot00000000000000#ifndef SASS_OUTPUT_H #define SASS_OUTPUT_H #include #include #include "util.hpp" #include "inspect.hpp" #include "operation.hpp" 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(); public: Output(Sass_Output_Options& opt); virtual ~Output(); protected: std::string charset; std::vector top_nodes; public: OutputBuffer get_buffer(void); virtual void operator()(Map*); virtual void operator()(Ruleset*); // virtual void operator()(Propset*); virtual void operator()(Supports_Block*); virtual void operator()(Media_Block*); virtual void operator()(At_Rule*); virtual void operator()(Keyframe_Rule*); virtual void operator()(Import*); virtual void operator()(Comment*); virtual void operator()(Number*); virtual void operator()(String_Quoted*); virtual void operator()(String_Constant*); void fallback_impl(AST_Node* n); }; } #endif libsass-3.3.4/src/parser.cpp000066400000000000000000002674741267254216700160100ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #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 #include namespace Sass { using namespace Constants; using namespace Prelexer; Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); p.source = source ? source : beg; p.position = beg ? beg : p.source; p.end = p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); return p; } Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); p.source = source ? source : beg; p.position = beg ? beg : p.source; p.end = end ? end : p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); return p; } Selector_List* Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source) { Parser p = Parser::from_c_str(beg, ctx, pstate, source); // ToDo: ruby sass errors on parent references // ToDo: remap the source-map entries somehow return p.parse_selector_list(false); } bool Parser::peek_newline(const char* start) { return peek_linefeed(start ? start : position) && ! peek_css>(start); } Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); p.source = source ? source : t.begin; p.position = t.begin ? t.begin : p.source; p.end = t.end ? t.end : p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); return p; } /* main entry point to parse root block */ Block* Parser::parse() { bool is_root = false; Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true); read_bom(); // custom headers if (ctx.resources.size() == 1) { is_root = true; ctx.apply_custom_headers(root, path, pstate); } block_stack.push_back(root); /* bool rv = */ parse_block_nodes(is_root); block_stack.pop_back(); // update for end position root->update_pstate(pstate); if (position != end) { css_error("Invalid CSS", " after ", ": expected selector or at-rule, was "); } return root; } // convenience function for block parsing // will create a new block ad-hoc for you // this is the base block parsing function Block* Parser::parse_css_block(bool is_root) { // parse comments before block // lex < optional_css_comments >(); // lex mandatory opener or error out if (!lex_css < exactly<'{'> >()) { css_error("Invalid CSS", " after ", ": expected \"{\", was "); } // create new block and push to the selector stack Block* block = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, is_root); block_stack.push_back(block); if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");; if (!lex_css < exactly<'}'> >()) { css_error("Invalid CSS", " after ", ": expected \"}\", was "); } // update for end position block->update_pstate(pstate); // parse comments after block // lex < optional_css_comments >(); block_stack.pop_back(); return block; } // convenience function for block parsing // will create a new block ad-hoc for you // also updates the `in_at_root` flag Block* Parser::parse_block(bool is_root) { LOCAL_FLAG(in_at_root, is_root); return parse_css_block(is_root); } // the main block parsing function // parses stuff between `{` and `}` bool Parser::parse_block_nodes(bool is_root) { // loop until end of string while (position < end) { // we should be able to refactor this parse_block_comments(); lex < css_whitespace >(); if (lex < exactly<';'> >()) continue; if (peek < end_of_file >()) return true; if (peek < exactly<'}'> >()) return true; if (parse_block_node(is_root)) continue; parse_block_comments(); if (lex_css < exactly<';'> >()) continue; if (peek_css < end_of_file >()) return true; if (peek_css < exactly<'}'> >()) return true; // illegal sass return false; } // return success return true; } // parser for a single node in a block // semicolons must be lexed beforehand bool Parser::parse_block_node(bool is_root) { Block* block = block_stack.back(); parse_block_comments(); // throw away white-space // includes line comments lex < css_whitespace >(); Lookahead lookahead_result; // also parse block comments // first parse everything that is allowed in functions if (lex < variable >(true)) { (*block) << parse_assignment(); } else if (lex < kwd_err >(true)) { (*block) << parse_error(); } else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); } else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); } else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); } else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); } else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); } else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); } else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); } // abort if we are in function context and have nothing parsed yet else if (stack.back() == Scope::Function) { error("Functions can only contain variable declarations and control directives.", pstate); } // parse imports to process later else if (lex < kwd_import >(true)) { Scope parent = stack.empty() ? Scope::Rules : stack.back(); if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) { if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20 error("Import directives may not be used within control directives or mixins.", pstate); } } Import* imp = parse_import(); // if it is a url, we only add the statement if (!imp->urls().empty()) (*block) << imp; // process all resources now (add Import_Stub nodes) for (size_t i = 0, S = imp->incs().size(); i < S; ++i) { (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]); } } else if (lex < kwd_extend >(true)) { if (block->is_root()) { error("Extend directives may only be used within rules.", pstate); } Lookahead lookahead = lookahead_for_include(position); if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was "); Selector* target; if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); else target = parse_selector_list(true); (*block) << SASS_MEMORY_NEW(ctx.mem, Extension, pstate, target); } // selector may contain interpolations which need delayed evaluation else if (!(lookahead_result = lookahead_for_selector(position)).error) { (*block) << parse_ruleset(lookahead_result, is_root); } // parse multiple specific keyword directives else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); } else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); } else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); } else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); } else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); } else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); } else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); } // ignore the @charset directive for now else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); } // generic at keyword (keep last) else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); } else if (block->is_root()) { lex< css_whitespace >(); if (position >= end) return true; css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was "); } // parse a declaration else { // ToDo: how does it handle parse errors? // maybe we are expected to parse something? Declaration* decl = parse_declaration(); decl->tabs(indentation); (*block) << decl; // maybe we have a "sub-block" if (peek< exactly<'{'> >()) { if (decl->is_indented()) ++ indentation; // parse a propset that rides on the declaration's property stack.push_back(Scope::Properties); (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block()); stack.pop_back(); if (decl->is_indented()) -- indentation; } } // something matched return true; } // EO parse_block_nodes // parse imports inside the Import* Parser::parse_import() { Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate); std::vector> to_import; bool first = true; do { while (lex< block_comment >()); if (lex< quoted_string >()) { to_import.push_back(std::pair(std::string(lexed), 0)); } else if (lex< uri_prefix >()) { Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args); if (lex< quoted_string >()) { Expression* the_url = parse_string(); *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url); } else if (String* the_url = parse_url_function_argument()) { *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url); } else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) { Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed); *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url); } else { error("malformed URL", pstate); } if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate); to_import.push_back(std::pair("", result)); } else { if (first) error("@import directive requires a url or quoted path", pstate); else error("expecting another url or quoted path in @import list", pstate); } first = false; } while (lex_css< exactly<','> >()); if (!peek_css,end_of_file>>()) { List* media_queries = parse_media_queries(); imp->media_queries(media_queries); } for(auto location : to_import) { if (location.second) { imp->urls().push_back(location.second); } else if (!ctx.call_importers(unquote(location.first), path, pstate, imp)) { ctx.import_url(imp, location.first, path); } } return imp; } Definition* Parser::parse_definition(Definition::Type which_type) { Scope parent = stack.empty() ? Scope::Rules : stack.back(); if (parent != Scope::Root && parent != Scope::Rules && parent != Scope::Function) { if (which_type == Definition::FUNCTION) { error("Functions may not be defined within control directives or other mixins.", pstate); } else { error("Mixins may not be defined within control directives or other mixins.", pstate); } } std::string which_str(lexed); if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate); std::string name(Util::normalize_underscores(lexed)); if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not")) { error("Invalid function name \"" + name + "\".", pstate); } ParserState source_position_of_def = pstate; Parameters* params = parse_parameters(); if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin); else stack.push_back(Scope::Function); Block* body = parse_block(); stack.pop_back(); Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type); return def; } Parameters* Parser::parse_parameters() { std::string name(lexed); Position position = after_token; Parameters* params = SASS_MEMORY_NEW(ctx.mem, Parameters, pstate); if (lex_css< exactly<'('> >()) { // if there's anything there at all if (!peek_css< exactly<')'> >()) { do (*params) << parse_parameter(); while (lex_css< exactly<','> >()); } if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position); } return params; } Parameter* Parser::parse_parameter() { while (lex< alternatives < spaces, block_comment > >()); lex < variable >(); std::string name(Util::normalize_underscores(lexed)); ParserState pos = pstate; Expression* val = 0; bool is_rest = false; while (lex< alternatives < spaces, block_comment > >()); if (lex< exactly<':'> >()) { // there's a default value while (lex< block_comment >()); val = parse_space_list(); val->is_delayed(false); } else if (lex< exactly< ellipsis > >()) { is_rest = true; } Parameter* p = SASS_MEMORY_NEW(ctx.mem, Parameter, pos, name, val, is_rest); return p; } Arguments* Parser::parse_arguments() { std::string name(lexed); Position position = after_token; Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); if (lex_css< exactly<'('> >()) { // if there's anything there at all if (!peek_css< exactly<')'> >()) { do (*args) << parse_argument(); while (lex_css< exactly<','> >()); } if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position); } return args; } Argument* Parser::parse_argument() { if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) { position += 2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } Argument* arg; if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) { lex_css< variable >(); std::string name(Util::normalize_underscores(lexed)); ParserState p = pstate; lex_css< exactly<':'> >(); Expression* val = parse_space_list(); val->is_delayed(false); arg = SASS_MEMORY_NEW(ctx.mem, Argument, p, val, name); } else { bool is_arglist = false; bool is_keyword = false; Expression* val = parse_space_list(); val->is_delayed(false); List* l = dynamic_cast(val); if (lex_css< exactly< ellipsis > >()) { if (val->concrete_type() == Expression::MAP || ( (l != NULL && l->separator() == SASS_HASH) )) is_keyword = true; else is_arglist = true; } arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword); } return arg; } Assignment* Parser::parse_assignment() { std::string name(Util::normalize_underscores(lexed)); ParserState var_source_position = pstate; if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate); Expression* val; Lookahead lookahead = lookahead_for_value(position); if (lookahead.has_interpolants && lookahead.found) { val = parse_value_schema(lookahead.found); } else { val = parse_list(); } val->is_delayed(false); bool is_default = false; bool is_global = false; while (peek< alternatives < default_flag, global_flag > >()) { if (lex< default_flag >()) is_default = true; else if (lex< global_flag >()) is_global = true; } Assignment* var = SASS_MEMORY_NEW(ctx.mem, Assignment, var_source_position, name, val, is_default, is_global); return var; } // a ruleset connects a selector and a block Ruleset* Parser::parse_ruleset(Lookahead lookahead, bool is_root) { // make sure to move up the the last position lex < optional_css_whitespace >(false, true); // create the connector object (add parts later) Ruleset* ruleset = SASS_MEMORY_NEW(ctx.mem, Ruleset, pstate); // parse selector static or as schema to be evaluated later if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root)); else ruleset->selector(parse_selector_schema(lookahead.found)); // then parse the inner block stack.push_back(Scope::Rules); ruleset->block(parse_block()); stack.pop_back(); // update for end position ruleset->update_pstate(pstate); // inherit is_root from parent block // need this info for sanity checks ruleset->is_root(is_root); // return AST Node return ruleset; } // parse a selector schema that will be evaluated in the eval stage // uses a string schema internally to do the actual schema handling // in the eval stage we will be re-parse it into an actual selector Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector) { // move up to the start lex< optional_spaces >(); const char* i = position; // selector schema re-uses string schema implementation String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); // the selector schema is pretty much just a wrapper for the string schema Selector_Schema* selector_schema = SASS_MEMORY_NEW(ctx.mem, Selector_Schema, pstate, schema); selector_schema->media_block(last_media_block); // process until end while (i < end_of_selector) { // try to parse mutliple interpolants if (const char* p = find_first_in_interval< exactly, block_comment >(i, end_of_selector)) { // accumulate the preceding segment if the position has advanced if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // check if the interpolation only contains white-space (error out) if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } // skip over all nested inner interpolations up to our own delimiter const char* j = skip_over_scopes< exactly, exactly >(p + 2, end_of_selector); // pass inner expression to the parser to resolve nested interpolations Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list(); // set status on the list expression interpolant->is_interpolant(true); // schema->has_interpolants(true); // add to the string schema (*schema) << interpolant; // advance position i = j; } // no more interpolants have been found // add the last segment if there is one else { // make sure to add the last bits of the string up to the end (if any) if (i < end_of_selector) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, end_of_selector)); // exit loop i = end_of_selector; } } // EO until eos // update position position = i; // update for end position selector_schema->update_pstate(pstate); // return parsed result return selector_schema; } // EO parse_selector_schema void Parser::parse_charset_directive() { lex < sequence < quoted_string, optional_spaces, exactly <';'> > >(); } // called after parsing `kwd_include_directive` Mixin_Call* Parser::parse_include_directive() { // lex identifier into `lexed` var lex_identifier(); // may error out // normalize underscores to hyphens std::string name(Util::normalize_underscores(lexed)); // create the initial mixin call object Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, pstate, name, 0, 0); // parse mandatory arguments call->arguments(parse_arguments()); // parse optional block if (peek < exactly <'{'> >()) { call->block(parse_block()); } // return ast node return call; } // EO parse_include_directive // parse a list of complex selectors // this is the main entry point for most Selector_List* Parser::parse_selector_list(bool in_root) { bool reloop = true; bool had_linefeed = false; Complex_Selector* sel = 0; Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate); group->media_block(last_media_block); do { reloop = false; had_linefeed = had_linefeed || peek_newline(); if (peek_css< class_char < selector_list_delims > >()) break; // in case there are superfluous commas at the end // now parse the complex selector sel = parse_complex_selector(in_root); if (!sel) return group; sel->has_line_feed(had_linefeed); had_linefeed = false; while (peek_css< exactly<','> >()) { lex< css_comments >(false); // consume everything up and including the comma 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) << sel; } while (reloop); while (lex_css< kwd_optional >()) { group->is_optional(true); } // update for end position group->update_pstate(pstate); if (sel) sel->last()->has_line_break(false); return group; } // EO parse_selector_list // a complex selector combines a compound selector with another // complex selector, with one of four combinator operations. // the compound selector (head) is optional, since the combinator // can come first in the whole selector sequence (like `> DIV'). Complex_Selector* Parser::parse_complex_selector(bool in_root) { String* reference = 0; lex < block_comment >(); // parse the left hand side Compound_Selector* lhs = 0; // special case if it starts with combinator ([+~>]) if (!peek_css< class_char < selector_combinator_ops > >()) { // parse the left hand side lhs = parse_compound_selector(); } // check for end of file condition if (peek < end_of_file >()) return 0; // parse combinator between lhs and rhs Complex_Selector::Combinator combinator; if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO; else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES; else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF; else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) { // comments are allowed, but not spaces? combinator = Complex_Selector::REFERENCE; if (!lex < re_reference_combinator >()) return 0; reference = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg? } else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF; if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0; // lex < block_comment >(); // source position of a complex selector points to the combinator // ToDo: make sure we update pstate for ancestor of (lex < zero >()); Complex_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, combinator, lhs); sel->media_block(last_media_block); if (combinator == Complex_Selector::REFERENCE) sel->reference(reference); // has linfeed after combinator? sel->has_line_break(peek_newline()); // sel->has_line_feed(has_line_feed); // check if we got the abort condition (ToDo: optimize) if (!peek_css< class_char < complex_selector_delims > >()) { // parse next selector in sequence sel->tail(parse_complex_selector(true)); if (sel->tail()) { // ToDo: move this logic below into tail setter // if (sel->tail()->has_reference()) sel->has_reference(true); if (sel->tail()->has_placeholder()) sel->has_placeholder(true); } } // add a parent selector if we are not in a root // also skip adding parent ref if we only have refs if (!sel->has_parent_ref() && !in_at_root && !in_root) { // create the objects to wrap parent selector reference Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); parent->media_block(last_media_block); Compound_Selector* head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate); head->media_block(last_media_block); // add simple selector (*head) << parent; // selector may not have any head yet if (!sel->head()) { sel->head(head); } // otherwise we need to create a new complex selector and set the old one as its tail else { sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel); sel->media_block(last_media_block); } // peek for linefeed and remember result on head // if (peek_newline()) head->has_line_break(true); } // complex selector return sel; } // EO parse_complex_selector // parse one compound selector, which is basically // a list of simple selectors (directly adjacent) // lex them exactly (without skipping white-space) Compound_Selector* Parser::parse_compound_selector() { // init an empty compound selector wrapper Compound_Selector* seq = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate); seq->media_block(last_media_block); // skip initial white-space lex< css_whitespace >(); // parse list while (true) { // remove all block comments (don't skip white-space) lex< delimited_by< slash_star, star_slash, false > >(false); // parse functional if (match < re_pseudo_selector >()) { (*seq) << parse_simple_selector(); } // parse parent selector else if (lex< exactly<'&'> >(false)) { // this produces a linefeed!? seq->has_parent_reference(true); (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); // parent selector only allowed at start // upcoming Sass may allow also trailing if (seq->length() > 1) { ParserState state(pstate); Simple_Selector* cur = (*seq)[seq->length()-1]; Simple_Selector* 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) << SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed); } // peek for abort conditions else if (peek< spaces >()) break; else if (peek< end_of_file >()) { break; } else if (peek_css < class_char < selector_combinator_ops > >()) break; else if (peek_css < class_char < complex_selector_delims > >()) break; // otherwise parse another simple selector else { Simple_Selector* sel = parse_simple_selector(); if (!sel) return 0; (*seq) << sel; } } if (seq && !peek_css>()) { seq->has_line_break(peek_newline()); } // EO while true return seq; } // EO parse_compound_selector Simple_Selector* Parser::parse_simple_selector() { lex < css_comments >(false); if (lex< alternatives < id_name, class_name > >()) { return SASS_MEMORY_NEW(ctx.mem, Selector_Qualifier, pstate, lexed); } else if (lex< quoted_string >()) { return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, unquote(lexed)); } else if (lex< alternatives < variable, number, static_reference_combinator > >()) { return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed); } else if (peek< pseudo_not >()) { return parse_negated_selector(); } else if (peek< re_pseudo_selector >()) { return parse_pseudo_selector(); } else if (peek< exactly<':'> >()) { return parse_pseudo_selector(); } else if (lex < exactly<'['> >()) { return parse_attribute_selector(); } else if (lex< placeholder >()) { Selector_Placeholder* sel = SASS_MEMORY_NEW(ctx.mem, Selector_Placeholder, pstate, lexed); sel->media_block(last_media_block); return sel; } // failed return 0; } Wrapped_Selector* Parser::parse_negated_selector() { lex< pseudo_not >(); std::string name(lexed); ParserState nsource_position = pstate; Selector* negated = parse_selector_list(true); if (!lex< exactly<')'> >()) { error("negated selector is missing ')'", pstate); } name.erase(name.size() - 1); return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, nsource_position, name, negated); } // a pseudo selector often starts with one or two colons // it can contain more selectors inside parentheses Simple_Selector* Parser::parse_pseudo_selector() { if (lex< sequence< optional < pseudo_prefix >, // we keep the space within the name, strange enough // ToDo: refactor output to schedule the space for it // or do we really want to keep the real white-space? sequence< identifier, optional < block_comment >, exactly<'('> > > >()) { std::string name(lexed); name.erase(name.size() - 1); ParserState p = pstate; // specially parse static stuff // ToDo: really everything static? if (peek_css < sequence < alternatives < static_value, binomial >, optional_css_whitespace, exactly<')'> > >() ) { lex_css< alternatives < static_value, binomial > >(); String_Constant* expr = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); if (expr && lex_css< exactly<')'> >()) { expr->can_compress_whitespace(true); return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, p, name, expr); } } else if (Selector* wrapped = parse_selector_list(true)) { if (wrapped && lex_css< exactly<')'> >()) { return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, p, name, wrapped); } } } // EO if pseudo selector else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) { return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, pstate, lexed); } else if(lex < pseudo_prefix >()) { css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was "); } css_error("Invalid CSS", " after ", ": expected \")\", was "); // unreachable statement return 0; } Attribute_Selector* Parser::parse_attribute_selector() { ParserState p = pstate; if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate); std::string name(lexed); if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, "", 0); if (!lex_css< alternatives< exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match > >()) { error("invalid operator in attribute selector for " + name, pstate); } std::string matcher(lexed); String* value = 0; if (lex_css< identifier >()) { value = SASS_MEMORY_NEW(ctx.mem, String_Constant, p, lexed); } else if (lex_css< quoted_string >()) { value = parse_interpolated_chunk(lexed, true); // needed! } else { error("expected a string constant or identifier in attribute selector for " + name, pstate); } if (!lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate); return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, matcher, value); } /* parse block comment and add to block */ void Parser::parse_block_comments() { Block* block = block_stack.back(); while (lex< block_comment >()) { bool is_important = lexed.begin[2] == '!'; // flag on second param is to skip loosely over comments String* contents = parse_interpolated_chunk(lexed, true); (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important); } } Declaration* Parser::parse_declaration() { String* prop = 0; if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) { prop = parse_identifier_schema(); } else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) { prop = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); prop->is_delayed(true); } else { css_error("Invalid CSS", " after ", ": expected \"}\", was "); } bool is_indented = true; const std::string property(lexed); if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate); lex < css_comments >(false); if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate); if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty if (peek_css< static_value >()) { return SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, parse_static_value()/*, lex()*/); } else { Expression* value; Lookahead lookahead = lookahead_for_value(position); if (lookahead.found) { if (lookahead.has_interpolants) { value = parse_value_schema(lookahead.found); } else { value = parse_list(); } } else { value = parse_list(); if (List* list = dynamic_cast(value)) { if (list->length() == 0 && !peek< exactly <'{'> >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } } } lex < css_comments >(false); auto decl = SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, value/*, lex()*/); decl->is_indented(is_indented); return decl; } } // parse +/- and return false if negative bool Parser::parse_number_prefix() { bool positive = true; while(true) { if (lex < block_comment >()) continue; if (lex < number_prefix >()) continue; if (lex < exactly < '-' > >()) { positive = !positive; continue; } break; } return positive; } Expression* Parser::parse_map() { Expression* key = parse_list(); List* map = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_HASH); if (String_Quoted* str = dynamic_cast(key)) { if (!str->quote_mark() && !str->is_delayed()) { if (const Color* col = name_to_color(str->value())) { Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col); c->pstate(str->pstate()); c->disp(str->value()); key = c; } } } // it's not a map so return the lexed value as a list value if (!lex_css< exactly<':'> >()) { return key; } Expression* value = parse_space_list(); (*map) << key << value; while (lex_css< exactly<','> >()) { // allow trailing commas - #495 if (peek_css< exactly<')'> >(position)) { break; } Expression* key = parse_list(); if (String_Quoted* str = dynamic_cast(key)) { if (!str->quote_mark() && !str->is_delayed()) { if (const Color* col = name_to_color(str->value())) { Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col); c->pstate(str->pstate()); c->disp(str->value()); key = c; } } } if (!(lex< exactly<':'> >())) { error("invalid syntax", pstate); } Expression* value = parse_space_list(); (*map) << key << value; } ParserState ps = map->pstate(); ps.offset = pstate - ps + pstate.offset; map->pstate(ps); return map; } // parse list returns either a space separated list, // a comma separated list or any bare expression found. // so to speak: we unwrap items from lists if possible here! Expression* Parser::parse_list() { // parse list is relly just an alias return parse_comma_list(); } // will return singletons unwrapped Expression* Parser::parse_comma_list() { // check if we have an empty list // return the empty list as such if (peek_css< alternatives < // exactly<'!'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<':'>, end_of_file, exactly, default_flag, global_flag > >(position)) { return SASS_MEMORY_NEW(ctx.mem, List, pstate, 0); } // now try to parse a space list Expression* list = parse_space_list(); // if it's a singleton, return it (don't wrap it) if (!peek_css< exactly<','> >(position)) return list; // if we got so far, we actually do have a comma list List* comma_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_COMMA); // wrap the first expression (*comma_list) << list; while (lex_css< exactly<','> >()) { // check for abort condition if (peek_css< alternatives < exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<':'>, end_of_file, exactly, default_flag, global_flag > >(position) ) { break; } // otherwise add another expression (*comma_list) << parse_space_list(); } // return the list return comma_list; } // EO parse_comma_list // will return singletons unwrapped Expression* Parser::parse_space_list() { Expression* disj1 = parse_disjunction(); // if it's a singleton, return it (don't wrap it) if (peek_css< alternatives < // exactly<'!'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<','>, exactly<':'>, end_of_file, exactly, default_flag, global_flag > >(position) ) { return disj1; } List* space_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_SPACE); (*space_list) << disj1; while (!(peek_css< alternatives < // exactly<'!'>, exactly<';'>, exactly<'}'>, exactly<'{'>, exactly<')'>, exactly<','>, exactly<':'>, end_of_file, exactly, default_flag, global_flag > >(position)) && peek_css< optional_css_whitespace >() != end ) { // the space is parsed implicitly? (*space_list) << parse_disjunction(); } // return the list return space_list; } // EO parse_space_list // parse logical OR operation Expression* Parser::parse_disjunction() { // parse the left hand side conjunction Expression* conj = parse_conjunction(); // parse multiple right hand sides std::vector operands; while (lex_css< kwd_or >()) operands.push_back(parse_conjunction()); // if it's a singleton, return it directly if (operands.size() == 0) return conj; // fold all operands into one binary expression return fold_operands(conj, operands, { Sass_OP::OR }); } // EO parse_disjunction // parse logical AND operation Expression* Parser::parse_conjunction() { // parse the left hand side relation Expression* rel = parse_relation(); // parse multiple right hand sides std::vector operands; while (lex_css< kwd_and >()) operands.push_back(parse_relation()); // if it's a singleton, return it directly if (operands.size() == 0) return rel; // fold all operands into one binary expression return fold_operands(rel, operands, { Sass_OP::AND }); } // EO parse_conjunction // parse comparison operations Expression* Parser::parse_relation() { // parse the left hand side expression Expression* lhs = parse_expression(); std::vector operands; std::vector operators; // if it's a singleton, return it (don't wrap it) while (peek< alternatives < kwd_eq, kwd_neq, kwd_gte, kwd_gt, kwd_lte, kwd_lt > >(position)) { // is directly adjancent to expression? bool left_ws = peek < css_comments >() != NULL; // parse the operator enum Sass_OP op = lex() ? Sass_OP::EQ : lex() ? Sass_OP::NEQ : lex() ? Sass_OP::GTE : lex() ? Sass_OP::LTE : lex() ? Sass_OP::GT : lex() ? Sass_OP::LT // we checked the possibilities on top of fn : Sass_OP::EQ; // is directly adjacent to expression? bool right_ws = peek < css_comments >() != NULL; operators.push_back({ op, left_ws, right_ws }); operands.push_back(parse_expression()); left_ws = peek < css_comments >() != NULL; } // parse the operator return fold_operands(lhs, operands, operators); } // parse_relation // parse expression valid for operations // called from parse_relation // called from parse_for_directive // called from parse_media_expression // parse addition and subtraction operations Expression* Parser::parse_expression() { // parses multiple add and subtract operations // NOTE: make sure that identifiers starting with // NOTE: dashes do NOT count as subtract operation Expression* lhs = parse_operators(); // if it's a singleton, return it (don't wrap it) if (!(peek_css< exactly<'+'> >(position) || // condition is a bit misterious, but some combinations should not be counted as operations (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position)) { return lhs; } std::vector operands; std::vector operators; bool left_ws = peek < css_comments >() != NULL; while ( lex_css< exactly<'+'> >() || ( ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position) && lex_css< sequence< negate< digit >, exactly<'-'> > >() ) ) { bool right_ws = peek < css_comments >() != NULL; operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws }); operands.push_back(parse_operators()); left_ws = peek < css_comments >() != NULL; } if (operands.size() == 0) return lhs; return fold_operands(lhs, operands, operators); } // parse addition and subtraction operations Expression* Parser::parse_operators() { Expression* factor = parse_factor(); // if it's a singleton, return it (don't wrap it) std::vector operands; // factors std::vector operators; // ops // lex operations to apply to lhs const char* left_ws = peek < css_comments >(); while (lex_css< class_char< static_ops > >()) { const char* right_ws = peek < css_comments >(); switch(*lexed.begin) { case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break; case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break; case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break; default: throw std::runtime_error("unknown static op parsed"); break; } operands.push_back(parse_factor()); left_ws = peek < css_comments >(); } // operands and operators to binary expression return fold_operands(factor, operands, operators); } // EO parse_operators // called from parse_operators // called from parse_value_schema Expression* Parser::parse_factor() { lex < css_comments >(false); if (lex_css< exactly<'('> >()) { // parse_map may return a list Expression* value = parse_map(); // lex the expected closing parenthesis if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate); // expression can be evaluated // make sure wrapped lists and division expressions are non-delayed within parentheses if (value->concrete_type() == Expression::LIST) { // List* l = static_cast(value); // if (!l->empty()) (*l)[0]->is_delayed(false); } else if (typeid(*value) == typeid(Binary_Expression)) { Binary_Expression* b = static_cast(value); if (b && b->type() == Sass_OP::DIV) b->set_delayed(false); } return value; } // string may be interpolated // if (lex< quoted_string >()) { // return parse_string(); // } else if (peek< ie_property >()) { return parse_ie_property(); } else if (peek< ie_keyword_arg >()) { return parse_ie_keyword_arg(); } else if (peek< exactly< calc_kwd > >() || peek< exactly< moz_calc_kwd > >() || peek< exactly< ms_calc_kwd > >() || peek< exactly< webkit_calc_kwd > >()) { return parse_calc_function(); } else if (lex < functional_schema >()) { return parse_function_call_schema(); } else if (lex< identifier_schema >()) { String* string = parse_identifier_schema(); if (String_Schema* schema = dynamic_cast(string)) { if (lex < exactly < '(' > >()) { *schema << parse_list(); lex < exactly < ')' > >(); } } return string; } else if (peek< sequence< uri_prefix, W, real_uri_value > >()) { return parse_url_function_string(); } else if (peek< re_functional >()) { return parse_function_call(); } else if (lex< exactly<'+'> >()) { return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor()); } else if (lex< exactly<'-'> >()) { return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor()); } else if (lex< sequence< kwd_not > >()) { return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::NOT, parse_factor()); } else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) { if (parse_number_prefix()) return parse_value(); // prefix is positive return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_value()); } else { return parse_value(); } } // parse one value for a list Expression* Parser::parse_value() { lex< css_comments >(false); if (lex< ampersand >()) { return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); } if (lex< kwd_important >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); } // parse `10%4px` into separated items and not a schema if (lex< sequence < percentage, lookahead < number > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); } if (lex< sequence < number, lookahead< sequence < op, number > > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); } // string may be interpolated if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >()) { return parse_string(); } if (const char* stop = peek< value_schema >()) { return parse_value_schema(stop); } // string may be interpolated if (lex< quoted_string >()) { return parse_string(); } if (lex< kwd_true >()) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); } if (lex< kwd_false >()) { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); } if (lex< kwd_null >()) { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); } if (lex< identifier >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } if (lex< percentage >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); } // match hex number first because 0x000 looks like a number followed by an identifier if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); } if (lex< sequence < exactly <'#'>, identifier > >()) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); } // also handle the 10em- foo special case // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); } if (lex< sequence< static_component, one_plus< strict_identifier > > >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } if (lex< number >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); } if (lex< variable >()) { return SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); } // Special case handling for `%` proceeding an interpolant. if (lex< sequence< exactly<'%'>, optional< percentage > > >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); // unreachable statement return 0; } // this parses interpolation inside other strings // means the result should later be quoted again String* Parser::parse_interpolated_chunk(Token chunk, bool constant) { const char* i = chunk.begin; // see if there any interpolants const char* p = constant ? find_first_in_interval< exactly >(i, chunk.end) : find_first_in_interval< exactly, block_comment >(i, chunk.end); if (!p) { String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end)); if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*'); str_quoted->is_delayed(true); return str_quoted; } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); schema->is_interpolant(true); while (i < chunk.end) { p = constant ? find_first_in_interval< exactly >(i, chunk.end) : find_first_in_interval< exactly, block_comment >(i, chunk.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); } // we need to skip anything inside strings // create a new target in parser/prelexer if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p + 2, chunk.end); // find the closing brace if (j) { --j; // parse the interpolant and accumulate it Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside string constant " + chunk.to_string(), pstate); } } else { // no interpolants left; add the last segment if nonempty // check if we need quotes here (was not sure after merge) if (i < chunk.end) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, chunk.end)); break; } ++ i; } return schema; } String_Constant* Parser::parse_static_expression() { if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) { return parse_static_value(); } return 0; } String_Constant* Parser::parse_static_value() { lex< static_value >(); Token str(lexed); --str.end; --position; String_Constant* str_node = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, str.time_wspace()); str_node->is_delayed(true); return str_node; } String* Parser::parse_string() { return parse_interpolated_chunk(Token(lexed)); } String* Parser::parse_ie_property() { lex< ie_property >(); Token str(lexed); const char* i = str.begin; // see if there any interpolants const char* p = find_first_in_interval< exactly, block_comment >(str.begin, str.end); if (!p) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end)); } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); while (i < str.end) { p = find_first_in_interval< exactly, block_comment >(i, str.end); if (p) { if (i < p) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty } if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p+2; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p+2, str.end); // find the closing brace if (j) { // parse the interpolant and accumulate it Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside IE function " + str.to_string(), pstate); } } else { // no interpolants left; add the last segment if nonempty if (i < str.end) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, str.end)); } break; } } return schema; } String* Parser::parse_ie_keyword_arg() { String_Schema* kwd_arg = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate, 3); if (lex< variable >()) { *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); } else { lex< alternatives< identifier_schema, identifier > >(); *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } lex< exactly<'='> >(); *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); if (peek< variable >()) *kwd_arg << parse_list(); else if (lex< number >()) *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed)); else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); } return kwd_arg; } String_Schema* Parser::parse_value_schema(const char* stop) { // initialize the string schema object to add tokens String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); if (peek>()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* e = 0; const char* ee = end; end = stop; size_t num_items = 0; bool need_space = false; while (position < stop) { // parse space between tokens if (lex< spaces >() && num_items) { need_space = true; } if (need_space) { need_space = false; // (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); } if ((e = peek< re_functional >()) && e < stop) { (*schema) << parse_function_call(); } // lex an interpolant /#{...}/ else if (lex< exactly < hash_lbrace > >()) { // Try to lex static expression first if (peek< exactly< rbrace > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } Expression* ex = 0; if (lex< re_static_expression >()) { ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } else { ex = parse_list(); } ex->is_interpolant(true); (*schema) << ex; // ToDo: no error check here? lex < exactly < rbrace > >(); } // lex some string constants or other valid token // Note: [-+] chars are left over from i.e. `#{3}+3` else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } // lex a quoted string else if (lex< quoted_string >()) { // need_space = true; // if (schema->length()) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); // else need_space = true; (*schema) << parse_string(); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; } if (peek < exactly < '-' > >()) break; } else if (lex< sequence < identifier > >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { // need_space = true; } } // lex (normalized) variable else if (lex< variable >()) { std::string name(Util::normalize_underscores(lexed)); (*schema) << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, name); } // lex percentage value else if (lex< percentage >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); } // lex dimension value else if (lex< dimension >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); } // lex number value else if (lex< number >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); } // lex hex color value else if (lex< sequence < hex, negate < exactly < '-' > > > >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); } else if (lex< sequence < exactly <'#'>, identifier > >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); } // lex a value in parentheses else if (peek< parenthese_scope >()) { (*schema) << parse_factor(); } else { break; } ++num_items; } if (position != stop) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(position, stop)); position = stop; } end = ee; return schema; } // this parses interpolation outside other strings // means the result must not be quoted again later String* Parser::parse_identifier_schema() { Token id(lexed); const char* i = id.begin; // see if there any interpolants const char* p = find_first_in_interval< exactly, block_comment >(id.begin, id.end); if (!p) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end)); } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); while (i < id.end) { p = find_first_in_interval< exactly, block_comment >(i, id.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty const char* o = position; position = i; *schema << parse_value_schema(p); position = o; } // we need to skip anything inside strings // create a new target in parser/prelexer if (peek < sequence < optional_spaces, exactly > >(p+2)) { position = p; css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } const char* j = skip_over_scopes< exactly, exactly >(p+2, id.end); // find the closing brace if (j) { // parse the interpolant and accumulate it Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; // schema->has_interpolants(true); i = j; } else { // throw an error if the interpolant is unterminated error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate); } } else { // no interpolants left; add the last segment if nonempty if (i < end) { const char* o = position; position = i; *schema << parse_value_schema(id.end); position = o; } break; } } return schema; } // calc functions should preserve arguments Function_Call* Parser::parse_calc_function() { lex< identifier >(); std::string name(lexed); ParserState call_pos = pstate; lex< exactly<'('> >(); ParserState arg_pos = pstate; const char* arg_beg = position; parse_list(); const char* arg_end = position; lex< skip_over_scopes < exactly < '(' >, exactly < ')' > > >(); Argument* arg = SASS_MEMORY_NEW(ctx.mem, Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end))); Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, arg_pos); *args << arg; return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args); } String* Parser::parse_url_function_string() { std::string prefix(""); if (lex< uri_prefix >()) { prefix = std::string(lexed); } String* url_string = parse_url_function_argument(); std::string suffix(""); if (lex< real_uri_suffix >()) { suffix = std::string(lexed); } std::string uri(""); if (url_string) { uri = url_string->to_string({ NESTED, 5 }); } if (String_Schema* schema = dynamic_cast(url_string)) { String_Schema* res = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); (*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, prefix); (*res) += schema; (*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, suffix); return res; } else { std::string res = prefix + uri + suffix; return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res); } } String* Parser::parse_url_function_argument() { const char* p = position; std::string uri(""); if (lex< real_uri_value >(false)) { uri = lexed.to_string(); } if (peek< exactly< hash_lbrace > >()) { const char* pp = position; // TODO: error checking for unclosed interpolants while (peek< exactly< hash_lbrace > >(pp)) { pp = sequence< interpolant, real_uri_value >(pp); } position = pp; return parse_interpolated_chunk(Token(p, position)); } else if (uri != "") { std::string res = Util::rtrim(uri); return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res); } return 0; } Function_Call* Parser::parse_function_call() { lex< identifier >(); std::string name(lexed); ParserState call_pos = pstate; Arguments* args = parse_arguments(); return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args); } Function_Call_Schema* Parser::parse_function_call_schema() { String* name = parse_identifier_schema(); ParserState source_position_of_call = pstate; Function_Call_Schema* the_call = SASS_MEMORY_NEW(ctx.mem, Function_Call_Schema, source_position_of_call, name, parse_arguments()); return the_call; } Content* Parser::parse_content_directive() { bool missing_mixin_parent = true; for (auto parent : stack) { if (parent == Scope::Mixin) { missing_mixin_parent = false; break; } } if (missing_mixin_parent) { error("@content may only be used within a mixin", pstate); } return SASS_MEMORY_NEW(ctx.mem, Content, pstate); } If* Parser::parse_if_directive(bool else_if) { stack.push_back(Scope::Control); ParserState if_source_position = pstate; Expression* predicate = parse_list(); predicate->is_delayed(false); Block* block = parse_block(); Block* alternative = 0; // only throw away comment if we parse a case // we want all other comments to be parsed if (lex_css< elseif_directive >()) { alternative = SASS_MEMORY_NEW(ctx.mem, Block, pstate); (*alternative) << parse_if_directive(true); } else if (lex_css< kwd_else_directive >()) { alternative = parse_block(); } stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative); } For* Parser::parse_for_directive() { stack.push_back(Scope::Control); ParserState for_source_position = pstate; lex_variable(); std::string var(Util::normalize_underscores(lexed)); if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate); Expression* lower_bound = parse_expression(); lower_bound->is_delayed(false); bool inclusive = false; if (lex< kwd_through >()) inclusive = true; else if (lex< kwd_to >()) inclusive = false; else error("expected 'through' or 'to' keyword in @for directive", pstate); Expression* upper_bound = parse_expression(); upper_bound->is_delayed(false); Block* body = parse_block(); stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive); } // helper to parse a var token Token Parser::lex_variable() { // peek for dollar sign first if (!peek< exactly <'$'> >()) { css_error("Invalid CSS", " after ", ": expected \"$\", was "); } // we expect a simple identifier as the call name if (!lex< sequence < exactly <'$'>, identifier > >()) { lex< exactly <'$'> >(); // move pstate and position up css_error("Invalid CSS", " after ", ": expected identifier, was "); } // return object return token; } // helper to parse identifier Token Parser::lex_identifier() { // we expect a simple identifier as the call name if (!lex< identifier >()) { // ToDo: pstate wrong? css_error("Invalid CSS", " after ", ": expected identifier, was "); } // return object return token; } Each* Parser::parse_each_directive() { stack.push_back(Scope::Control); ParserState each_source_position = pstate; std::vector vars; lex_variable(); vars.push_back(Util::normalize_underscores(lexed)); while (lex< exactly<','> >()) { if (!lex< variable >()) error("@each directive requires an iteration variable", pstate); vars.push_back(Util::normalize_underscores(lexed)); } if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate); Expression* list = parse_list(); list->is_delayed(false); if (list->concrete_type() == Expression::LIST) { List* l = static_cast(list); for (size_t i = 0, L = l->length(); i < L; ++i) { (*l)[i]->is_delayed(false); } } Block* body = parse_block(); stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body); } // called after parsing `kwd_while_directive` While* Parser::parse_while_directive() { stack.push_back(Scope::Control); // create the initial while call object While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0); // parse mandatory predicate Expression* predicate = parse_list(); predicate->is_delayed(false); call->predicate(predicate); // parse mandatory block call->block(parse_block()); // return ast node stack.pop_back(); // return ast node return call; } // EO parse_while_directive Media_Block* Parser::parse_media_block() { stack.push_back(Scope::Media); Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0); media_block->media_queries(parse_media_queries()); Media_Block* prev_media_block = last_media_block; last_media_block = media_block; media_block->block(parse_css_block()); last_media_block = prev_media_block; stack.pop_back(); return media_block; } List* Parser::parse_media_queries() { List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA); if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query(); while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query(); return media_queries; } // Expression* Parser::parse_media_query() Media_Query* Parser::parse_media_query() { Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate); lex < css_comments >(false); if (lex < kwd_not >()) media_query->is_negated(true); else if (lex < kwd_only >()) media_query->is_restricted(true); lex < css_comments >(false); if (lex < identifier_schema >()) media_query->media_type(parse_identifier_schema()); else if (lex < identifier >()) media_query->media_type(parse_interpolated_chunk(lexed)); else (*media_query) << parse_media_expression(); while (lex_css < kwd_and >()) (*media_query) << parse_media_expression(); if (lex < identifier_schema >()) { String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); *schema << media_query->media_type(); *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); *schema << parse_identifier_schema(); media_query->media_type(schema); } while (lex_css < kwd_and >()) (*media_query) << parse_media_expression(); return media_query; } Media_Query_Expression* Parser::parse_media_expression() { if (lex < identifier_schema >()) { String* ss = parse_identifier_schema(); return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, pstate, ss, 0, true); } if (!lex_css< exactly<'('> >()) { error("media query expression must begin with '('", pstate); } Expression* feature = 0; if (peek_css< exactly<')'> >()) { error("media feature required in media query expression", pstate); } feature = parse_expression(); Expression* expression = 0; if (lex_css< exactly<':'> >()) { expression = parse_list(); } if (!lex_css< exactly<')'> >()) { error("unclosed parenthesis in media query expression", pstate); } return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression); } // lexed after `kwd_supports_directive` // these are very similar to media blocks Supports_Block* Parser::parse_supports_directive() { Supports_Condition* cond = parse_supports_condition(); // create the ast node object for the support queries Supports_Block* query = SASS_MEMORY_NEW(ctx.mem, Supports_Block, pstate, cond); // additional block is mandatory // parse inner block query->block(parse_block()); // return ast node return query; } // parse one query operation // may encounter nested queries Supports_Condition* Parser::parse_supports_condition() { lex < css_whitespace >(); Supports_Condition* cond = parse_supports_negation(); if (!cond) cond = parse_supports_operator(); if (!cond) cond = parse_supports_interpolation(); return cond; } Supports_Condition* Parser::parse_supports_negation() { if (!lex < kwd_not >()) return 0; Supports_Condition* cond = parse_supports_condition_in_parens(); return SASS_MEMORY_NEW(ctx.mem, Supports_Negation, pstate, cond); } Supports_Condition* Parser::parse_supports_operator() { Supports_Condition* cond = parse_supports_condition_in_parens(); if (!cond) return 0; while (true) { Supports_Operator::Operand op = Supports_Operator::OR; if (lex < kwd_and >()) { op = Supports_Operator::AND; } else if(!lex < kwd_or >()) { break; } lex < css_whitespace >(); Supports_Condition* right = parse_supports_condition_in_parens(); // Supports_Condition* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Condition, *static_cast(cond)); cond = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, pstate, cond, right, op); } return cond; } Supports_Condition* Parser::parse_supports_interpolation() { if (!lex < interpolant >()) return 0; String* interp = parse_interpolated_chunk(lexed); if (!interp) return 0; return SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, pstate, interp); } // TODO: This needs some major work. Although feature conditions // look like declarations their semantics differ significantly Supports_Condition* Parser::parse_supports_declaration() { Supports_Condition* cond = 0; // parse something declaration like Declaration* declaration = parse_declaration(); if (!declaration) error("@supports condition expected declaration", pstate); cond = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration, declaration->pstate(), declaration->property(), declaration->value()); // ToDo: maybe we need an additional error condition? return cond; } Supports_Condition* Parser::parse_supports_condition_in_parens() { Supports_Condition* interp = parse_supports_interpolation(); if (interp != 0) return interp; if (!lex < exactly <'('> >()) return 0; lex < css_whitespace >(); Supports_Condition* cond = parse_supports_condition(); if (cond != 0) { if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate); } else { cond = parse_supports_declaration(); if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate); } lex < css_whitespace >(); return cond; } At_Root_Block* Parser::parse_at_root_block() { ParserState at_source_position = pstate; Block* body = 0; At_Root_Expression* expr = 0; Lookahead lookahead_result; LOCAL_FLAG(in_at_root, true); if (lex< exactly<'('> >()) { expr = parse_at_root_expression(); } if (peek < exactly<'{'> >()) { body = parse_block(true); } else if ((lookahead_result = lookahead_for_selector(position)).found) { Ruleset* r = parse_ruleset(lookahead_result, false); body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true); *body << r; } At_Root_Block* at_root = SASS_MEMORY_NEW(ctx.mem, At_Root_Block, at_source_position, body); if (expr) at_root->expression(expr); return at_root; } At_Root_Expression* Parser::parse_at_root_expression() { if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate); if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) { css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was "); } Declaration* declaration = parse_declaration(); List* value = SASS_MEMORY_NEW(ctx.mem, List, declaration->value()->pstate(), 1); if (declaration->value()->concrete_type() == Expression::LIST) { value = static_cast(declaration->value()); } else *value << declaration->value(); At_Root_Expression* cond = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, declaration->pstate(), declaration->property(), value); if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate); return cond; } At_Rule* Parser::parse_at_rule() { std::string kwd(lexed); if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate); At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd); Lookahead lookahead = lookahead_for_include(position); if (lookahead.found && !lookahead.has_interpolants) { at_rule->selector(parse_selector_list(true)); } lex < css_comments >(false); if (lex < static_property >()) { at_rule->value(parse_interpolated_chunk(Token(lexed))); } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) { at_rule->value(parse_list()); } lex < css_comments >(false); if (peek< exactly<'{'> >()) { at_rule->block(parse_block()); } return at_rule; } Warning* Parser::parse_warning() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties.", pstate); } return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list()); } Error* Parser::parse_error() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties.", pstate); } return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list()); } Debug* Parser::parse_debug() { if (stack.back() != Scope::Root && stack.back() != Scope::Function && stack.back() != Scope::Mixin && stack.back() != Scope::Control && stack.back() != Scope::Rules) { error("Illegal nesting: Only properties may be nested beneath properties.", pstate); } return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list()); } Return* Parser::parse_return_directive() { // check that we do not have an empty list (ToDo: check if we got all cases) if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } return SASS_MEMORY_NEW(ctx.mem, Return, pstate, parse_list()); } Lookahead Parser::lookahead_for_selector(const char* start) { // init result struct Lookahead rv = Lookahead(); // get start position const char* p = start ? start : position; // match in one big "regex" rv.error = p; if (const char* q = peek < alternatives < // partial BEM selector sequence < ampersand, one_plus < exactly < '-' > >, word_boundary >, // main selector matching one_plus < alternatives < // consume whitespace and comments spaces, block_comment, line_comment, // match `/deep/` selector (pass-trough) // there is no functionality for it yet schema_reference_combinator, // match selector ops /[*&%,()\[\]]/ class_char < selector_lookahead_ops >, // match selector combinators /[>+~]/ class_char < selector_combinator_ops >, // match attribute compare operators alternatives < exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match >, // main selector match sequence < // allow namespace prefix optional < namespace_schema >, // modifiers prefixes alternatives < sequence < exactly <'#'>, // not for interpolation negate < exactly <'{'> > >, // class match exactly <'.'>, // single or double colon optional < pseudo_prefix > >, // accept hyphens in token one_plus < sequence < // can start with hyphens zero_plus < exactly<'-'> >, // now the main token alternatives < kwd_optional, exactly <'*'>, quoted_string, interpolant, identifier, variable, percentage, binomial, dimension, alnum > > >, // can also end with hyphens zero_plus < exactly<'-'> > > > > > >(p) ) { while (p < q) { // did we have interpolations? if (*p == '#' && *(p+1) == '{') { rv.has_interpolants = true; p = q; break; } ++ p; } // store anyway } // ToDo: remove rv.error = q; rv.position = q; // check expected opening bracket // only after successfull matching if (peek < exactly<'{'> >(q)) rv.found = q; // else if (peek < exactly<';'> >(q)) rv.found = q; // else if (peek < exactly<'}'> >(q)) rv.found = q; if (rv.found || *p == 0) rv.error = 0; } rv.parsable = ! rv.has_interpolants; // return result return rv; } // EO lookahead_for_selector // used in parse_block_nodes and parse_at_rule // ToDo: actual usage is still not really clear to me? Lookahead Parser::lookahead_for_include(const char* start) { // we actually just lookahead for a selector Lookahead rv = lookahead_for_selector(start); // but the "found" rules are different if (const char* p = rv.position) { // check for additional abort condition if (peek < exactly<';'> >(p)) rv.found = p; else if (peek < exactly<'}'> >(p)) rv.found = p; } // return result return rv; } // EO lookahead_for_include // look ahead for a token with interpolation in it // we mostly use the result if there is an interpolation // everything that passes here gets parsed as one schema // meaning it will not be parsed as a space separated list Lookahead Parser::lookahead_for_value(const char* start) { // init result struct Lookahead rv = Lookahead(); // get start position const char* p = start ? start : position; // match in one big "regex" if (const char* q = peek < non_greedy < alternatives < // consume whitespace block_comment, // spaces, // main tokens sequence < interpolant, optional < quoted_string > >, identifier, variable, // issue #442 sequence < parenthese_scope, interpolant, optional < quoted_string > > >, sequence < // optional_spaces, alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > > > >(p) ) { if (p == q) return rv; while (p < q) { // did we have interpolations? if (*p == '#' && *(p+1) == '{') { rv.has_interpolants = true; p = q; break; } ++ p; } // store anyway // ToDo: remove rv.position = q; // check expected opening bracket // only after successful matching if (peek < exactly<'{'> >(q)) rv.found = q; else if (peek < exactly<';'> >(q)) rv.found = q; else if (peek < exactly<'}'> >(q)) rv.found = q; } // return result return rv; } // EO lookahead_for_value void Parser::read_bom() { size_t skip = 0; std::string encoding; bool utf_8 = false; switch ((unsigned char) source[0]) { case 0xEF: skip = check_bom_chars(source, end, utf_8_bom, 3); encoding = "UTF-8"; utf_8 = true; break; case 0xFE: skip = check_bom_chars(source, end, utf_16_bom_be, 2); encoding = "UTF-16 (big endian)"; break; case 0xFF: skip = check_bom_chars(source, end, utf_16_bom_le, 2); skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0); encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)"); break; case 0x00: skip = check_bom_chars(source, end, utf_32_bom_be, 4); encoding = "UTF-32 (big endian)"; break; case 0x2B: skip = check_bom_chars(source, end, utf_7_bom_1, 4) | check_bom_chars(source, end, utf_7_bom_2, 4) | check_bom_chars(source, end, utf_7_bom_3, 4) | check_bom_chars(source, end, utf_7_bom_4, 4) | check_bom_chars(source, end, utf_7_bom_5, 5); encoding = "UTF-7"; break; case 0xF7: skip = check_bom_chars(source, end, utf_1_bom, 3); encoding = "UTF-1"; break; case 0xDD: skip = check_bom_chars(source, end, utf_ebcdic_bom, 4); encoding = "UTF-EBCDIC"; break; case 0x0E: skip = check_bom_chars(source, end, scsu_bom, 3); encoding = "SCSU"; break; case 0xFB: skip = check_bom_chars(source, end, bocu_1_bom, 3); encoding = "BOCU-1"; break; case 0x84: skip = check_bom_chars(source, end, gb_18030_bom, 4); encoding = "GB-18030"; break; } if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate); position += skip; } size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len) { size_t skip = 0; if (src + len > end) return 0; for (size_t i = 0; i < len; ++i, ++skip) { if ((unsigned char) src[i] != bom[i]) return 0; } return skip; } Expression* Parser::fold_operands(Expression* base, std::vector& operands, Operand op) { for (size_t i = 0, S = operands.size(); i < S; ++i) { base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]); Binary_Expression* b = static_cast(base); if (op.operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } else if (b && b->op().operand != Sass_OP::DIV) { b->left()->is_delayed(false); b->right()->is_delayed(false); } } return base; } Expression* Parser::fold_operands(Expression* base, std::vector& operands, std::vector& ops, size_t i) { if (String_Schema* schema = dynamic_cast(base)) { // return schema; if (schema->has_interpolants()) { if (i + 1 < operands.size() && ( (ops[0].operand == Sass_OP::EQ) || (ops[0].operand == Sass_OP::ADD) || (ops[0].operand == Sass_OP::DIV) || (ops[0].operand == Sass_OP::MUL) || (ops[0].operand == Sass_OP::NEQ) || (ops[0].operand == Sass_OP::LT) || (ops[0].operand == Sass_OP::GT) || (ops[0].operand == Sass_OP::LTE) || (ops[0].operand == Sass_OP::GTE) )) { Expression* rhs = fold_operands(operands[0], operands, ops, 1); rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[0], schema, rhs); rhs->set_delayed(false); rhs->is_delayed(true); return rhs; } // return schema; } } for (size_t S = operands.size(); i < S; ++i) { if (String_Schema* schema = dynamic_cast(operands[i])) { if (schema->has_interpolants()) { if (i + 1 < S) { Expression* rhs = fold_operands(operands[i+1], operands, ops, i + 2); rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], schema, rhs); base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, rhs); rhs->is_delayed(true); base->is_delayed(true); return base; } base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); if (ops[i].operand != Sass_OP::DIV) base->is_delayed(true); return base; } else { base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); } } else { base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); } Binary_Expression* b = static_cast(base); if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } else if (b) { b->left()->is_delayed(false); b->right()->is_delayed(false); } } return base; } void Parser::error(std::string msg, Position pos) { throw Exception::InvalidSass(ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg); } // print a css parsing error with actual context information from parsed source void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle) { int max_len = 18; const char* end = this->end; while (*end != 0) ++ end; const char* pos = peek < optional_spaces >(); const char* last_pos(pos); if (last_pos > source) { utf8::prior(last_pos, source); } // backup position to last significant char while (last_pos > source && last_pos < end) { if (!Prelexer::is_space(*last_pos)) break; utf8::prior(last_pos, source); } bool ellipsis_left = false; const char* pos_left(last_pos); const char* end_left(last_pos); utf8::next(pos_left, end); utf8::next(end_left, end); while (pos_left > source) { if (utf8::distance(pos_left, end_left) >= max_len) { utf8::prior(pos_left, source); ellipsis_left = *(pos_left) != '\n' && *(pos_left) != '\r'; utf8::next(pos_left, end); break; } const char* prev = pos_left; utf8::prior(prev, source); if (*prev == '\r') break; if (*prev == '\n') break; pos_left = prev; } if (pos_left < source) { pos_left = source; } bool ellipsis_right = false; const char* end_right(pos); const char* pos_right(pos); while (end_right < end) { if (utf8::distance(pos_right, end_right) > max_len) { ellipsis_left = *(pos_right) != '\n' && *(pos_right) != '\r'; break; } if (*end_right == '\r') break; if (*end_right == '\n') break; utf8::next(end_right, end); } // if (*end_right == 0) end_right ++; std::string left(pos_left, end_left); std::string right(pos_right, end_right); size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0; size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0; if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos); if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis; // now pass new message to the more generic error function error(msg + prefix + quote(left) + middle + quote(right), pstate); } } libsass-3.3.4/src/parser.hpp000066400000000000000000000261401267254216700157740ustar00rootroot00000000000000#ifndef SASS_PARSER_H #define SASS_PARSER_H #include #include #include "ast.hpp" #include "position.hpp" #include "context.hpp" #include "position.hpp" #include "prelexer.hpp" struct Lookahead { const char* found; const char* error; const char* position; bool parsable; bool has_interpolants; }; namespace Sass { class Parser : public ParserState { public: enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules }; Context& ctx; std::vector block_stack; std::vector stack; Media_Block* last_media_block; const char* source; const char* position; const char* end; Position before_token; Position after_token; ParserState pstate; int indentation; Token lexed; bool in_at_root; Parser(Context& ctx, const ParserState& pstate) : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0), source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0) { in_at_root = false; stack.push_back(Scope::Root); } // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]")); static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0); // special static parsers to convert strings into certain selectors static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0); #ifdef __clang__ // lex and peak uses the template parameter to branch on the action, which // triggers clangs tautological comparison on the single-comparison // branches. This is not a bug, just a merging of behaviour into // one function #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wtautological-compare" #endif bool peek_newline(const char* start = 0); // skip over spaces, tabs and line comments template const char* sneak(const char* start = 0) { using namespace Prelexer; // maybe use optional start position from arguments? const char* it_position = start ? start : position; // skip white-space? if (mx == spaces || mx == no_spaces || mx == css_comments || mx == css_whitespace || mx == optional_spaces || mx == optional_css_comments || mx == optional_css_whitespace ) { return it_position; } // skip over spaces, tabs and sass line comments const char* pos = optional_css_whitespace(it_position); // always return a valid position return pos ? pos : it_position; } // peek will only skip over space, tabs and line comment // return the position where the lexer match will occur template const char* match(const char* start = 0) { // match the given prelexer return mx(position); } // peek will only skip over space, tabs and line comment // return the position where the lexer match will occur template const char* peek(const char* start = 0) { // sneak up to the actual token we want to lex // this should skip over white-space if desired const char* it_before_token = sneak < mx >(start); // match the given prelexer const char* match = mx(it_before_token); // check if match is in valid range return match <= end ? match : 0; } // white-space handling is built into the lexer // this way you do not need to parse it yourself // some matchers don't accept certain white-space // we do not support start arg, since we manipulate // sourcemap offset and we modify the position pointer! // lex will only skip over space, tabs and line comment template const char* lex(bool lazy = true, bool force = false) { // position considered before lexed token // we can skip whitespace or comments for // lazy developers (but we need control) const char* it_before_token = position; // sneak up to the actual token we want to lex // this should skip over white-space if desired if (lazy) it_before_token = sneak < mx >(position); // now call matcher to get position after token const char* it_after_token = mx(it_before_token); // check if match is in valid range if (it_after_token > end) return 0; // maybe we want to update the parser state anyway? if (force == false) { // assertion that we got a valid match if (it_after_token == 0) return 0; // assertion that we actually lexed something if (it_after_token == it_before_token) return 0; } // create new lexed token object (holds the parse results) lexed = Token(position, it_before_token, it_after_token); // advance position (add whitespace before current token) before_token = after_token.add(position, it_before_token); // 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?) pstate = ParserState(path, source, lexed, before_token, after_token - before_token); // advance internal char iterator return position = it_after_token; } // lex_css skips over space, tabs, line and block comment // all block comments will be consumed and thrown away // source-map position will point to token after the comment template const char* lex_css() { // copy old token Token prev = lexed; // store previous pointer const char* oldpos = position; Position bt = before_token; Position at = after_token; ParserState op = pstate; // throw away comments // update srcmap position lex < Prelexer::css_comments >(); // now lex a new token const char* pos = lex< mx >(); // maybe restore prev state if (pos == 0) { pstate = op; lexed = prev; position = oldpos; after_token = at; before_token = bt; } // return match return pos; } // all block comments will be skipped and thrown away template const char* peek_css(const char* start = 0) { // now peek a token (skip comments first) return peek< mx >(peek < Prelexer::css_comments >(start)); } #ifdef __clang__ #pragma clang diagnostic pop #endif void error(std::string msg, Position pos); // generate message with given and expected sample // text before and in the middle are configurable void css_error(const std::string& msg, const std::string& prefix = " after ", const std::string& middle = ", was: "); void read_bom(); Block* parse(); Import* parse_import(); Definition* parse_definition(Definition::Type which_type); Parameters* parse_parameters(); Parameter* parse_parameter(); Mixin_Call* parse_include_directive(); Arguments* parse_arguments(); Argument* parse_argument(); Assignment* parse_assignment(); // Propset* parse_propset(); Ruleset* parse_ruleset(Lookahead lookahead, bool is_root = false); Selector_Schema* parse_selector_schema(const char* end_of_selector); Selector_List* parse_selector_list(bool at_root = false); Complex_Selector* parse_complex_selector(bool in_root = true); Compound_Selector* parse_compound_selector(); Simple_Selector* parse_simple_selector(); Wrapped_Selector* parse_negated_selector(); Simple_Selector* parse_pseudo_selector(); Attribute_Selector* parse_attribute_selector(); Block* parse_block(bool is_root = false); Block* parse_css_block(bool is_root = false); bool parse_block_nodes(bool is_root = false); bool parse_block_node(bool is_root = false); bool parse_number_prefix(); Declaration* parse_declaration(); Expression* parse_map_value(); Expression* parse_map(); Expression* parse_list(); Expression* parse_comma_list(); Expression* parse_space_list(); Expression* parse_disjunction(); Expression* parse_conjunction(); Expression* parse_relation(); Expression* parse_expression(); Expression* parse_operators(); Expression* parse_factor(); Expression* parse_value2(); Expression* parse_value(); Function_Call* parse_calc_function(); Function_Call* parse_function_call(); Function_Call_Schema* parse_function_call_schema(); String* parse_url_function_string(); String* parse_url_function_argument(); String* parse_interpolated_chunk(Token, bool constant = false); String* parse_string(); String_Constant* parse_static_expression(); // String_Constant* parse_static_property(); String_Constant* parse_static_value(); String* parse_ie_property(); String* parse_ie_keyword_arg(); String_Schema* parse_value_schema(const char* stop); String* parse_identifier_schema(); // String_Schema* parse_url_schema(); If* parse_if_directive(bool else_if = false); For* parse_for_directive(); Each* parse_each_directive(); While* parse_while_directive(); Return* parse_return_directive(); Content* parse_content_directive(); void parse_charset_directive(); Media_Block* parse_media_block(); List* parse_media_queries(); Media_Query* parse_media_query(); Media_Query_Expression* parse_media_expression(); Supports_Block* parse_supports_directive(); Supports_Condition* parse_supports_condition(); Supports_Condition* parse_supports_negation(); Supports_Condition* parse_supports_operator(); Supports_Condition* parse_supports_interpolation(); Supports_Condition* parse_supports_declaration(); Supports_Condition* parse_supports_condition_in_parens(); At_Root_Block* parse_at_root_block(); At_Root_Expression* parse_at_root_expression(); At_Rule* parse_at_rule(); Warning* parse_warning(); Error* parse_error(); Debug* parse_debug(); // these will throw errors Token lex_variable(); Token lex_identifier(); void parse_block_comments(); Lookahead lookahead_for_value(const char* start = 0); Lookahead lookahead_for_selector(const char* start = 0); Lookahead lookahead_for_include(const char* start = 0); Expression* fold_operands(Expression* base, std::vector& operands, Operand op); Expression* fold_operands(Expression* base, std::vector& operands, std::vector& ops, size_t i = 0); void throw_syntax_error(std::string message, size_t ln = 0); void throw_read_error(std::string message, size_t ln = 0); }; size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len); } #endif libsass-3.3.4/src/paths.hpp000066400000000000000000000031101267254216700156070ustar00rootroot00000000000000#ifndef SASS_PATHS_H #define SASS_PATHS_H #include #include #include template std::string vector_to_string(std::vector 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 std::vector > paths(std::vector > strata, size_t from_end = 0) { if (strata.empty()) { return std::vector >(); } size_t end = strata.size() - from_end; if (end <= 1) { std::vector > starting_points; starting_points.reserve(strata[0].size()); for (size_t i = 0, S = strata[0].size(); i < S; ++i) { std::vector starting_point; starting_point.push_back(strata[0][i]); starting_points.push_back(starting_point); } return starting_points; } std::vector > up_to_here = paths(strata, from_end + 1); std::vector here = strata[end-1]; std::vector > 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 branch = up_to_here[i]; branch.push_back(here[j]); branches.push_back(branch); } } return branches; } } #endif libsass-3.3.4/src/plugins.cpp000066400000000000000000000126601267254216700161560ustar00rootroot00000000000000#ifdef _WIN32 #include #else #include #include #include #include #endif #include "sass.hpp" #include #include "output.hpp" #include "plugins.hpp" namespace Sass { Plugins::Plugins(void) { } Plugins::~Plugins(void) { } // check if plugin is compatible with this version // plugins may be linked static against libsass // we try to be compatible between major versions inline bool compatibility(const char* their_version) { // const char* their_version = "3.1.2"; // first check if anyone has an unknown version const char* our_version = libsass_version(); if (!strcmp(their_version, "[na]")) return false; if (!strcmp(our_version, "[na]")) return false; // find the position of the second dot size_t pos = std::string(our_version).find('.', 0); if (pos != std::string::npos) pos = std::string(our_version).find('.', pos + 1); // if we do not have two dots we fallback to compare complete string if (pos == std::string::npos) { return strcmp(their_version, our_version) ? 0 : 1; } // otherwise only compare up to the second dot (major versions) else { return strncmp(their_version, our_version, pos) ? 0 : 1; } } // load one specific plugin bool Plugins::load_plugin (const std::string& path) { typedef const char* (*__plugin_version__)(void); typedef Sass_Function_List (*__plugin_load_fns__)(void); typedef Sass_Importer_List (*__plugin_load_imps__)(void); if (LOAD_LIB(plugin, path)) { // try to load initial function to query libsass version suppor if (LOAD_LIB_FN(__plugin_version__, plugin_version, "libsass_get_version")) { // get the libsass version of the plugin if (!compatibility(plugin_version())) return false; // try to get import address for "libsass_load_functions" if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions")) { Sass_Function_List fns = plugin_load_functions(); while (fns && *fns) { functions.push_back(*fns); ++ fns; } } // try to get import address for "libsass_load_importers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_importers, "libsass_load_importers")) { Sass_Importer_List imps = plugin_load_importers(); while (imps && *imps) { importers.push_back(*imps); ++ imps; } } // try to get import address for "libsass_load_headers" if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers")) { Sass_Importer_List imps = plugin_load_headers(); while (imps && *imps) { headers.push_back(*imps); ++ imps; } } // success return true; } else { // print debug message to stderr (should not happen) std::cerr << "failed loading 'libsass_support' in <" << path << ">" << std::endl; if (const char* dlsym_error = dlerror()) std::cerr << dlsym_error << std::endl; CLOSE_LIB(plugin); } } else { // print debug message to stderr (should not happen) std::cerr << "failed loading plugin <" << path << ">" << std::endl; if (const char* dlopen_error = dlerror()) std::cerr << dlopen_error << std::endl; } return false; } size_t Plugins::load_plugins(const std::string& path) { // count plugins size_t loaded = 0; #ifdef _WIN32 try { // use wchar (utf16) WIN32_FIND_DATAW data; // trailing slash is guaranteed std::string globsrch(path + "*.dll"); // convert to wide chars (utf16) for system call std::wstring wglobsrch(UTF_8::convert_to_utf16(globsrch)); HANDLE hFile = FindFirstFileW(wglobsrch.c_str(), &data); // check if system called returned a result // ToDo: maybe we should print a debug message if (hFile == INVALID_HANDLE_VALUE) return -1; // read directory while (true) { try { // the system will report the filenames with wide chars (utf16) std::string entry = UTF_8::convert_from_utf16(data.cFileName); // check if file ending matches exactly if (!ends_with(entry, ".dll")) continue; // load the plugin and increase counter if (load_plugin(path + entry)) ++ loaded; // check if there should be more entries if (GetLastError() == ERROR_NO_MORE_FILES) break; // load next entry (check for return type) if (!FindNextFileW(hFile, &data)) break; } catch (...) { // report the error to the console (should not happen) // seems like we got strange data from the system call? std::cerr << "filename in plugin path has invalid utf8?" << std::endl; } } } catch (utf8::invalid_utf8) { // report the error to the console (should not happen) // implementors should make sure to provide valid utf8 std::cerr << "plugin path contains invalid utf8" << std::endl; } #else DIR *dp; struct dirent *dirp; if((dp = opendir(path.c_str())) == NULL) return -1; while ((dirp = readdir(dp)) != NULL) { if (!ends_with(dirp->d_name, ".so")) continue; if (load_plugin(path + dirp->d_name)) ++ loaded; } closedir(dp); #endif return loaded; } } libsass-3.3.4/src/plugins.hpp000066400000000000000000000027551267254216700161670ustar00rootroot00000000000000#ifndef SASS_PLUGINS_H #define SASS_PLUGINS_H #include #include #include "utf8_string.hpp" #include "sass/functions.h" #ifdef _WIN32 #define LOAD_LIB(var, path) HMODULE var = LoadLibraryW(UTF_8::convert_to_utf16(path).c_str()) #define LOAD_LIB_WCHR(var, path_wide_str) HMODULE var = LoadLibraryW(path_wide_str.c_str()) #define LOAD_LIB_FN(type, var, name) type var = (type) GetProcAddress(plugin, name) #define CLOSE_LIB(var) FreeLibrary(var) #ifndef dlerror #define dlerror() 0 #endif #else #define LOAD_LIB(var, path) void* var = dlopen(path.c_str(), RTLD_LAZY) #define LOAD_LIB_FN(type, var, name) type var = (type) dlsym(plugin, name) #define CLOSE_LIB(var) dlclose(var) #endif namespace Sass { class Plugins { public: // c-tor Plugins(void); ~Plugins(void); public: // methods // load one specific plugin bool load_plugin(const std::string& path); // load all plugins from a directory size_t load_plugins(const std::string& path); public: // public accessors const std::vector get_headers(void) { return headers; } const std::vector get_importers(void) { return importers; } const std::vector get_functions(void) { return functions; } private: // private vars std::vector headers; std::vector importers; std::vector functions; }; } #endif libsass-3.3.4/src/position.cpp000066400000000000000000000110651267254216700163370ustar00rootroot00000000000000#include "sass.hpp" #include "position.hpp" namespace Sass { Offset::Offset(const char* string) : line(0), column(0) { *this = inc(string, string + strlen(string)); } Offset::Offset(const std::string& text) : line(0), column(0) { *this = inc(text.c_str(), text.c_str() + text.size()); } Offset::Offset(const size_t line, const size_t column) : line(line), column(column) { } // init/create instance from const char substring Offset Offset::init(const char* beg, const char* end) { Offset offset(0, 0); if (end == 0) { end += strlen(beg); } offset.add(beg, end); return offset; } // increase offset by given string (mostly called by lexer) // increase line counter and count columns on the last line // ToDo: make the col count utf8 aware Offset Offset::add(const char* begin, const char* end) { if (end == 0) return *this; while (begin < end && *begin) { if (*begin == '\n') { ++ line; // start new line column = 0; } else { ++ column; } ++begin; } return *this; } // increase offset by given string (mostly called by lexer) // increase line counter and count columns on the last line Offset Offset::inc(const char* begin, const char* end) const { Offset offset(line, column); offset.add(begin, end); return offset; } bool Offset::operator== (const Offset &pos) const { return line == pos.line && column == pos.column; } bool Offset::operator!= (const Offset &pos) const { return line != pos.line || column != pos.column; } void Offset::operator+= (const Offset &off) { *this = Offset(line + off.line, off.line > 0 ? off.column : column + off.column); } Offset Offset::operator+ (const Offset &off) const { return Offset(line + off.line, off.line > 0 ? off.column : column + off.column); } Offset Offset::operator- (const Offset &off) const { return Offset(line - off.line, off.line == line ? column - off.column : column); } Position::Position(const size_t file) : Offset(0, 0), file(file) { } Position::Position(const size_t file, const Offset& offset) : Offset(offset), file(file) { } Position::Position(const size_t line, const size_t column) : Offset(line, column), file(-1) { } Position::Position(const size_t file, const size_t line, const size_t column) : Offset(line, column), file(file) { } ParserState::ParserState(const char* path, const char* src, const size_t file) : Position(file, 0, 0), path(path), src(src), offset(0, 0), token() { } ParserState::ParserState(const char* path, const char* src, const Position& position, Offset offset) : Position(position), path(path), src(src), offset(offset), token() { } ParserState::ParserState(const char* path, const char* src, const Token& token, const Position& position, Offset offset) : Position(position), path(path), src(src), offset(offset), token(token) { } Position Position::add(const char* begin, const char* end) { Offset::add(begin, end); return *this; } Position Position::inc(const char* begin, const char* end) const { Offset offset(line, column); offset = offset.inc(begin, end); return Position(file, offset); } bool Position::operator== (const Position &pos) const { return file == pos.file && line == pos.line && column == pos.column; } bool Position::operator!= (const Position &pos) const { return file == pos.file || line != pos.line || column != pos.column; } void Position::operator+= (const Offset &off) { *this = Position(file, line + off.line, off.line > 0 ? off.column : column + off.column); } const Position Position::operator+ (const Offset &off) const { return Position(file, line + off.line, off.line > 0 ? off.column : column + off.column); } const Offset Position::operator- (const Offset &off) const { return Offset(line - off.line, off.line == line ? column - off.column : column); } /* not used anymore - remove? std::ostream& operator<<(std::ostream& strm, const Offset& off) { if (off.line == string::npos) strm << "-1:"; else strm << off.line << ":"; if (off.column == string::npos) strm << "-1"; else strm << off.column; return strm; } */ /* not used anymore - remove? std::ostream& operator<<(std::ostream& strm, const Position& pos) { if (pos.file != string::npos) strm << pos.file << ":"; if (pos.line == string::npos) strm << "-1:"; else strm << pos.line << ":"; if (pos.column == string::npos) strm << "-1"; else strm << pos.column; return strm; } */ } libsass-3.3.4/src/position.hpp000066400000000000000000000073151267254216700163470ustar00rootroot00000000000000#ifndef SASS_POSITION_H #define SASS_POSITION_H #include #include // #include namespace Sass { class Offset { public: // c-tor Offset(const char* string); Offset(const std::string& text); Offset(const size_t line, const size_t column); // return new position, incremented by the given string Offset add(const char* begin, const char* end); Offset inc(const char* begin, const char* end) const; // init/create instance from const char substring static Offset init(const char* beg, const char* end); public: // overload operators for position void operator+= (const Offset &pos); bool operator== (const Offset &pos) const; bool operator!= (const Offset &pos) const; Offset operator+ (const Offset &off) const; Offset operator- (const Offset &off) const; public: // overload output stream operator // friend std::ostream& operator<<(std::ostream& strm, const Offset& off); public: Offset off() { return *this; } public: size_t line; size_t column; }; class Position : public Offset { public: // c-tor Position(const size_t file); // line(0), column(0) Position(const size_t file, const Offset& offset); Position(const size_t line, const size_t column); // file(-1) Position(const size_t file, const size_t line, const size_t column); public: // overload operators for position void operator+= (const Offset &off); bool operator== (const Position &pos) const; bool operator!= (const Position &pos) const; const Position operator+ (const Offset &off) const; const Offset operator- (const Offset &off) const; // return new position, incremented by the given string Position add(const char* begin, const char* end); Position inc(const char* begin, const char* end) const; public: // overload output stream operator // friend std::ostream& operator<<(std::ostream& strm, const Position& pos); public: size_t file; }; // Token type for representing lexed chunks of text class Token { public: const char* prefix; const char* begin; const char* end; Token() : prefix(0), begin(0), end(0) { } Token(const char* b, const char* e) : prefix(b), begin(b), end(e) { } Token(const char* str) : prefix(str), begin(str), end(str + strlen(str)) { } Token(const char* p, const char* b, const char* e) : prefix(p), begin(b), end(e) { } size_t length() const { return end - begin; } std::string ws_before() const { return std::string(prefix, begin); } std::string to_string() const { return std::string(begin, end); } std::string time_wspace() const { std::string str(to_string()); std::string whitespaces(" \t\f\v\n\r"); return str.erase(str.find_last_not_of(whitespaces)+1); } operator bool() { return begin && end && begin >= end; } operator std::string() { return to_string(); } bool operator==(Token t) { return to_string() == t.to_string(); } }; class ParserState : public Position { public: // c-tor ParserState(const char* path, const char* src = 0, const size_t file = std::string::npos); ParserState(const char* path, const char* src, const Position& position, Offset offset = Offset(0, 0)); ParserState(const char* path, const char* src, const Token& token, const Position& position, Offset offset = Offset(0, 0)); public: // down casts Offset off() { return *this; } Position pos() { return *this; } ParserState pstate() { return *this; } public: const char* path; const char* src; Offset offset; Token token; }; } #endif libsass-3.3.4/src/prelexer.cpp000066400000000000000000001033001267254216700163130ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include "util.hpp" #include "position.hpp" #include "prelexer.hpp" #include "constants.hpp" namespace Sass { // using namespace Lexer; using namespace Constants; namespace Prelexer { // Match a line comment (/.*?(?=\n|\r\n?|\Z)/. const char* line_comment(const char* src) { return sequence< exactly < slash_slash >, non_greedy< any_char, end_of_line > >(src); } // Match a block comment. const char* block_comment(const char* src) { return sequence< delimited_by< slash_star, star_slash, false > >(src); } /* not use anymore - remove? const char* block_comment_prefix(const char* src) { return exactly(src); } // Match either comment. const char* comment(const char* src) { return line_comment(src); } */ // Match zero plus white-space or line_comments const char* optional_css_whitespace(const char* src) { return zero_plus< alternatives >(src); } const char* css_whitespace(const char* src) { return one_plus< alternatives >(src); } // Match optional_css_whitepace plus block_comments const char* optional_css_comments(const char* src) { return zero_plus< alternatives >(src); } const char* css_comments(const char* src) { return one_plus< alternatives >(src); } // Match one backslash escaped char /\\./ const char* escape_seq(const char* src) { return sequence< exactly<'\\'>, alternatives < minmax_range< 1, 3, xdigit >, any_char >, optional < exactly <' '> > >(src); } // Match identifier start const char* identifier_alpha(const char* src) { return alternatives< unicode_seq, alpha, unicode, exactly<'-'>, exactly<'_'>, NONASCII, ESCAPE, escape_seq >(src); } // Match identifier after start const char* identifier_alnum(const char* src) { return alternatives< unicode_seq, alnum, unicode, exactly<'-'>, exactly<'_'>, NONASCII, ESCAPE, escape_seq >(src); } // Match CSS identifiers. const char* strict_identifier(const char* src) { return sequence< one_plus < strict_identifier_alpha >, zero_plus < strict_identifier_alnum > // word_boundary not needed >(src); } // Match CSS identifiers. const char* identifier(const char* src) { return sequence< zero_plus< exactly<'-'> >, one_plus < identifier_alpha >, zero_plus < identifier_alnum > // word_boundary not needed >(src); } const char* strict_identifier_alpha(const char* src) { return alternatives < alpha, unicode, escape_seq, exactly<'_'> >(src); } const char* strict_identifier_alnum(const char* src) { return alternatives < alnum, unicode, escape_seq, exactly<'_'> >(src); } // Match a single CSS unit const char* one_unit(const char* src) { return sequence < optional < exactly <'-'> >, strict_identifier_alpha, zero_plus < alternatives< strict_identifier_alnum, sequence < one_plus < exactly<'-'> >, strict_identifier_alpha > > > >(src); } // Match numerator/denominator CSS units const char* multiple_units(const char* src) { return sequence < one_unit, zero_plus < sequence < exactly <'*'>, one_unit > > >(src); } // Match complex CSS unit identifiers const char* unit_identifier(const char* src) { return sequence < multiple_units, optional < sequence < exactly <'/'>, multiple_units > > >(src); } const char* identifier_alnums(const char* src) { return one_plus< identifier_alnum >(src); } // Match number prefix ([\+\-]+) const char* number_prefix(const char* src) { return alternatives < exactly < '+' >, sequence < exactly < '-' >, optional_css_whitespace, exactly< '-' > > >(src); } // Match interpolant schemas const char* identifier_schema(const char* src) { return sequence < one_plus < sequence < zero_plus < alternatives < sequence < optional < exactly <'$'> >, identifier >, exactly <'-'> > >, interpolant, zero_plus < alternatives < digits, sequence < optional < exactly <'$'> >, identifier >, quoted_string, exactly<'-'> > > > >, negate < exactly<'%'> > > (src); } // interpolants can be recursive/nested const char* interpolant(const char* src) { return recursive_scopes< exactly, exactly >(src); } // $re_squote = /'(?:$re_itplnt|\\.|[^'])*'/ const char* single_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'\''>, zero_plus < alternatives < // skip escapes sequence < exactly < '\\' >, re_linebreak >, escape_seq, unicode_seq, // skip interpolants interpolant, // skip non delimiters any_char_but < '\'' > > >, exactly <'\''> >(src); } // $re_dquote = /"(?:$re_itp|\\.|[^"])*"/ const char* double_quoted_string(const char* src) { // match a single quoted string, while skipping interpolants return sequence < exactly <'"'>, zero_plus < alternatives < // skip escapes sequence < exactly < '\\' >, re_linebreak >, escape_seq, unicode_seq, // skip interpolants interpolant, // skip non delimiters any_char_but < '"' > > >, exactly <'"'> >(src); } // $re_quoted = /(?:$re_squote|$re_dquote)/ const char* quoted_string(const char* src) { // match a quoted string, while skipping interpolants return alternatives< single_quoted_string, double_quoted_string >(src); } const char* sass_value(const char* src) { return alternatives < quoted_string, identifier, percentage, hex, dimension, number >(src); } // this is basically `one_plus < sass_value >` // takes care to not parse invalid combinations const char* value_combinations(const char* src) { // `2px-2px` is invalid combo bool was_number = false; const char* pos = src; while (src) { if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) { was_number = false; src = pos; } else if (!was_number && !exactly<'+'>(src) && (pos = alternatives < dimension, number >(src))) { was_number = true; src = pos; } else { break; } } return src; } // must be at least one interpolant // can be surrounded by sass values // make sure to never parse (dim)(dim) // since this wrongly consumes `2px-1px` // `2px1px` is valid number (unit `px1px`) const char* value_schema(const char* src) { return sequence < one_plus < sequence < optional < value_combinations >, interpolant, optional < value_combinations > > > >(src); } // Match CSS '@' keywords. const char* at_keyword(const char* src) { return sequence, identifier>(src); } const char* re_reference_combinator(const char* src) { return sequence < optional < sequence < zero_plus < exactly <'-'> >, identifier, exactly <'|'> > >, zero_plus < exactly <'-'> >, identifier >(src); } const char* static_reference_combinator(const char* src) { return sequence < exactly <'/'>, re_reference_combinator, exactly <'/'> >(src); } const char* schema_reference_combinator(const char* src) { return sequence < exactly <'/'>, optional < sequence < css_ip_identifier, exactly <'|'> > >, css_ip_identifier, exactly <'/'> > (src); } const char* kwd_import(const char* src) { return word(src); } const char* kwd_at_root(const char* src) { return word(src); } const char* kwd_with_directive(const char* src) { return word(src); } const char* kwd_without_directive(const char* src) { return word(src); } const char* kwd_media(const char* src) { return word(src); } const char* kwd_supports_directive(const char* src) { return word(src); } const char* kwd_mixin(const char* src) { return word(src); } const char* kwd_function(const char* src) { return word(src); } const char* kwd_return_directive(const char* src) { return word(src); } const char* kwd_include_directive(const char* src) { return word(src); } const char* kwd_content_directive(const char* src) { return word(src); } const char* kwd_charset_directive(const char* src) { return word(src); } const char* kwd_extend(const char* src) { return word(src); } const char* kwd_if_directive(const char* src) { return word(src); } const char* kwd_else_directive(const char* src) { return word(src); } const char* elseif_directive(const char* src) { return sequence< exactly< else_kwd >, optional_css_comments, word< if_after_else_kwd > >(src); } const char* kwd_for_directive(const char* src) { return word(src); } const char* kwd_from(const char* src) { return word(src); } const char* kwd_to(const char* src) { return word(src); } const char* kwd_through(const char* src) { return word(src); } const char* kwd_each_directive(const char* src) { return word(src); } const char* kwd_in(const char* src) { return word(src); } const char* kwd_while_directive(const char* src) { return word(src); } const char* name(const char* src) { return one_plus< alternatives< alnum, exactly<'-'>, exactly<'_'>, exactly<'\\'> > >(src); } const char* kwd_warn(const char* src) { return word(src); } const char* kwd_err(const char* src) { return word(src); } const char* kwd_dbg(const char* src) { return word(src); } /* not used anymore - remove? const char* directive(const char* src) { return sequence< exactly<'@'>, identifier >(src); } */ const char* kwd_null(const char* src) { return word(src); } const char* css_identifier(const char* src) { return sequence < zero_plus < exactly <'-'> >, identifier >(src); } const char* css_ip_identifier(const char* src) { return sequence < zero_plus < exactly <'-'> >, alternatives < identifier, interpolant > >(src); } // Match CSS type selectors const char* namespace_prefix(const char* src) { return sequence < optional < alternatives < exactly <'*'>, css_identifier > >, exactly <'|'>, negate < exactly <'='> > >(src); } // Match CSS type selectors const char* namespace_schema(const char* src) { return sequence < optional < alternatives < exactly <'*'>, css_ip_identifier > >, exactly<'|'>, negate < exactly <'='> > >(src); } const char* hyphens_and_identifier(const char* src) { return sequence< zero_plus< exactly< '-' > >, identifier >(src); } const char* hyphens_and_name(const char* src) { return sequence< zero_plus< exactly< '-' > >, name >(src); } const char* universal(const char* src) { return sequence< optional, exactly<'*'> >(src); } // Match CSS id names. const char* id_name(const char* src) { return sequence, name>(src); } // Match CSS class names. const char* class_name(const char* src) { return sequence, identifier>(src); } // Attribute name in an attribute selector. const char* attribute_name(const char* src) { return alternatives< sequence< optional, identifier>, identifier >(src); } // match placeholder selectors const char* placeholder(const char* src) { return sequence, identifier>(src); } // Match CSS numeric constants. const char* op(const char* src) { return class_char(src); } const char* sign(const char* src) { return class_char(src); } const char* unsigned_number(const char* src) { return alternatives, exactly<'.'>, one_plus >, digits>(src); } const char* number(const char* src) { return sequence< optional, unsigned_number>(src); } const char* coefficient(const char* src) { return alternatives< sequence< optional, digits >, sign >(src); } const char* binomial(const char* src) { return sequence < optional < sign >, optional < digits >, exactly <'n'>, zero_plus < sequence < optional_css_whitespace, sign, optional_css_whitespace, digits > > >(src); } const char* percentage(const char* src) { return sequence< number, exactly<'%'> >(src); } const char* ampersand(const char* src) { return exactly<'&'>(src); } /* not used anymore - remove? const char* em(const char* src) { return sequence< number, exactly >(src); } */ const char* dimension(const char* src) { return sequence(src); } const char* hex(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 4 && len != 7) ? 0 : p; } const char* hexa(const char* src) { const char* p = sequence< exactly<'#'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 4 && len != 7 && len != 9) ? 0 : p; } const char* hex0(const char* src) { const char* p = sequence< exactly<'0'>, exactly<'x'>, one_plus >(src); ptrdiff_t len = p - src; return (len != 5 && len != 8) ? 0 : p; } /* no longer used - remove? const char* rgb_prefix(const char* src) { return word(src); }*/ // Match CSS uri specifiers. const char* uri_prefix(const char* src) { return sequence < exactly < url_kwd >, zero_plus < sequence < exactly <'-'>, one_plus < alpha > > >, exactly <'('> >(src); } // TODO: rename the following two functions /* no longer used - remove? const char* uri(const char* src) { return sequence< exactly, optional, quoted_string, optional, exactly<')'> >(src); }*/ /* no longer used - remove? const char* url_value(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol one_plus< sequence< zero_plus< exactly<'/'> >, filename > >, // one or more folders and/or trailing filename optional< exactly<'/'> > >(src); }*/ /* no longer used - remove? const char* url_schema(const char* src) { return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol filename_schema >(src); // optional trailing slash }*/ // Match CSS "!important" keyword. const char* kwd_important(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match CSS "!optional" keyword. const char* kwd_optional(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match Sass "!default" keyword. const char* default_flag(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match Sass "!global" keyword. const char* global_flag(const char* src) { return sequence< exactly<'!'>, optional_css_whitespace, word >(src); } // Match CSS pseudo-class/element prefixes. const char* pseudo_prefix(const char* src) { return sequence< exactly<':'>, optional< exactly<':'> > >(src); } // Match CSS function call openers. const char* functional_schema(const char* src) { return sequence < one_plus < sequence < zero_plus < alternatives < identifier, exactly <'-'> > >, one_plus < sequence < interpolant, alternatives < digits, identifier, exactly<'+'>, exactly<'-'> > > > > >, negate < exactly <'%'> >, lookahead < exactly <'('> > > (src); } const char* re_nothing(const char* src) { return src; } const char* re_functional(const char* src) { return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } const char* re_pseudo_selector(const char* src) { return sequence< identifier, optional < block_comment >, exactly<'('> >(src); } // Match the CSS negation pseudo-class. const char* pseudo_not(const char* src) { return word< pseudo_not_kwd >(src); } // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src) { return word(src); } const char* odd(const char* src) { return word(src); } // Match CSS attribute-matching operators. const char* exact_match(const char* src) { return exactly<'='>(src); } const char* class_match(const char* src) { return exactly(src); } const char* dash_match(const char* src) { return exactly(src); } const char* prefix_match(const char* src) { return exactly(src); } const char* suffix_match(const char* src) { return exactly(src); } const char* substring_match(const char* src) { return exactly(src); } // Match CSS combinators. /* not used anymore - remove? const char* adjacent_to(const char* src) { return sequence< optional_spaces, exactly<'+'> >(src); } const char* precedes(const char* src) { return sequence< optional_spaces, exactly<'~'> >(src); } const char* parent_of(const char* src) { return sequence< optional_spaces, exactly<'>'> >(src); } const char* ancestor_of(const char* src) { return sequence< spaces, negate< exactly<'{'> > >(src); }*/ // Match SCSS variable names. const char* variable(const char* src) { return sequence, identifier>(src); } // Match Sass boolean keywords. const char* kwd_true(const char* src) { return word(src); } const char* kwd_false(const char* src) { return word(src); } const char* kwd_only(const char* src) { return keyword < only_kwd >(src); } const char* kwd_and(const char* src) { return keyword < and_kwd >(src); } const char* kwd_or(const char* src) { return keyword < or_kwd >(src); } const char* kwd_not(const char* src) { return keyword < not_kwd >(src); } const char* kwd_eq(const char* src) { return exactly(src); } const char* kwd_neq(const char* src) { return exactly(src); } const char* kwd_gt(const char* src) { return exactly(src); } const char* kwd_gte(const char* src) { return exactly(src); } const char* kwd_lt(const char* src) { return exactly(src); } const char* kwd_lte(const char* src) { return exactly(src); } // match specific IE syntax const char* ie_progid(const char* src) { return sequence < word, exactly<':'>, alternatives< identifier_schema, identifier >, zero_plus< sequence< exactly<'.'>, alternatives< identifier_schema, identifier > > >, zero_plus < sequence< exactly<'('>, optional_css_whitespace, optional < sequence< alternatives< variable, identifier_schema, identifier >, optional_css_whitespace, exactly<'='>, optional_css_whitespace, alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >, zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, sequence< alternatives< variable, identifier_schema, identifier >, optional_css_whitespace, exactly<'='>, optional_css_whitespace, alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa > > > > > >, optional_css_whitespace, exactly<')'> > > >(src); } const char* ie_expression(const char* src) { return sequence < word, exactly<'('>, skip_over_scopes< exactly<'('>, exactly<')'> > >(src); } const char* ie_property(const char* src) { return alternatives < ie_expression, ie_progid >(src); } // const char* ie_args(const char* src) { // return sequence< alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by< '(', ')', true> >, // zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src); // } const char* ie_keyword_arg_property(const char* src) { return alternatives < variable, identifier_schema, identifier >(src); } const char* ie_keyword_arg_value(const char* src) { return alternatives < variable, identifier_schema, identifier, quoted_string, number, hexa, sequence < exactly < '(' >, skip_over_scopes < exactly < '(' >, exactly < ')' > > > >(src); } const char* ie_keyword_arg(const char* src) { return sequence < ie_keyword_arg_property, optional_css_whitespace, exactly<'='>, optional_css_whitespace, ie_keyword_arg_value >(src); } // Path matching functions. /* not used anymore - remove? const char* folder(const char* src) { return sequence< zero_plus< any_char_except<'/'> >, exactly<'/'> >(src); } const char* folders(const char* src) { return zero_plus< folder >(src); }*/ /* not used anymore - remove? const char* chunk(const char* src) { char inside_str = 0; const char* p = src; size_t depth = 0; while (true) { if (!*p) { return 0; } else if (!inside_str && (*p == '"' || *p == '\'')) { inside_str = *p; } else if (*p == inside_str && *(p-1) != '\\') { inside_str = 0; } else if (*p == '(' && !inside_str) { ++depth; } else if (*p == ')' && !inside_str) { if (depth == 0) return p; else --depth; } ++p; } // unreachable return 0; } */ // follow the CSS spec more closely and see if this helps us scan URLs correctly /* not used anymore - remove? const char* NL(const char* src) { return alternatives< exactly<'\n'>, sequence< exactly<'\r'>, exactly<'\n'> >, exactly<'\r'>, exactly<'\f'> >(src); }*/ const char* H(const char* src) { return std::isxdigit(*src) ? src+1 : 0; } const char* W(const char* src) { return zero_plus< alternatives< space, exactly< '\t' >, exactly< '\r' >, exactly< '\n' >, exactly< '\f' > > >(src); } const char* UUNICODE(const char* src) { return sequence< exactly<'\\'>, between, optional< W > >(src); } const char* NONASCII(const char* src) { return nonascii(src); } const char* ESCAPE(const char* src) { return alternatives< UUNICODE, sequence< exactly<'\\'>, alternatives< NONASCII, escapable_character > > >(src); } // const char* real_uri_prefix(const char* src) { // return alternatives< // exactly< url_kwd >, // exactly< url_prefix_kwd > // >(src); // } const char* real_uri_suffix(const char* src) { return sequence< W, exactly< ')' > >(src); } const char* real_uri_value(const char* src) { return sequence< non_greedy< alternatives< class_char< real_uri_chars >, uri_character, NONASCII, ESCAPE >, alternatives< real_uri_suffix, exactly< hash_lbrace > > > > (src); } const char* static_string(const char* src) { const char* pos = src; const char * s = quoted_string(pos); Token t(pos, s); const unsigned int p = count_interval< interpolant >(t.begin, t.end); return (p == 0) ? t.end : 0; } const char* unicode_seq(const char* src) { return sequence < alternatives < exactly< 'U' >, exactly< 'u' > >, exactly< '+' >, padded_token < 6, xdigit, exactly < '?' > > >(src); } const char* static_component(const char* src) { return alternatives< identifier, static_string, percentage, hex, exactly<'|'>, // exactly<'+'>, sequence < number, unit_identifier >, number, sequence< exactly<'!'>, word > >(src); } const char* static_property(const char* src) { return sequence < zero_plus< sequence < optional_css_comments, alternatives < exactly<','>, exactly<'('>, exactly<')'>, kwd_optional, quoted_string, interpolant, identifier, percentage, dimension, variable, alnum, sequence < exactly <'\\'>, any_char > > > >, lookahead < sequence < optional_css_comments, alternatives < exactly <';'>, exactly <'}'>, end_of_file > > > >(src); } const char* static_value(const char* src) { return sequence< sequence< static_component, zero_plus< identifier > >, zero_plus < sequence< alternatives< sequence< optional_spaces, alternatives< exactly < '/' >, exactly < ',' >, exactly < ' ' > >, optional_spaces >, spaces >, static_component > >, zero_plus < spaces >, alternatives< exactly<';'>, exactly<'}'> > >(src); } const char* parenthese_scope(const char* src) { return sequence < exactly < '(' >, skip_over_scopes < exactly < '(' >, exactly < ')' > > >(src); } const char* type_selector(const char* src) { return sequence< optional, identifier>(src); } const char* re_type_selector(const char* src) { return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src); } const char* re_type_selector2(const char* src) { return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src); } const char* re_static_expression(const char* src) { return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src); } // lexer special_fn: these functions cannot be overloaded // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i) const char* re_special_fun(const char* src) { return sequence < optional < sequence < exactly <'-'>, one_plus < alternatives < alpha, exactly <'+'>, exactly <'-'> > > > >, alternatives < exactly < calc_fn_kwd >, exactly < expression_kwd >, sequence < sequence < exactly < progid_kwd >, exactly <':'> >, zero_plus < alternatives < char_range <'a', 'z'>, exactly <'.'> > > > > >(src); } } } libsass-3.3.4/src/prelexer.hpp000066400000000000000000000364041267254216700163320ustar00rootroot00000000000000#ifndef SASS_PRELEXER_H #define SASS_PRELEXER_H #include #include "lexer.hpp" namespace Sass { // using namespace Lexer; namespace Prelexer { //#################################### // KEYWORD "REGEX" MATCHERS //#################################### // Match Sass boolean keywords. const char* kwd_true(const char* src); const char* kwd_false(const char* src); const char* kwd_only(const char* src); const char* kwd_and(const char* src); const char* kwd_or(const char* src); const char* kwd_not(const char* src); const char* kwd_eq(const char* src); const char* kwd_neq(const char* src); const char* kwd_gt(const char* src); const char* kwd_gte(const char* src); const char* kwd_lt(const char* src); const char* kwd_lte(const char* src); // Match standard control chars const char* kwd_at(const char* src); const char* kwd_dot(const char* src); const char* kwd_comma(const char* src); const char* kwd_colon(const char* src); const char* kwd_slash(const char* src); const char* kwd_star(const char* src); const char* kwd_plus(const char* src); const char* kwd_minus(const char* src); //#################################### // SPECIAL "REGEX" CONSTRUCTS //#################################### // Match a sequence of characters delimited by the supplied chars. template const char* delimited_by(const char* src) { src = exactly(src); if (!src) return 0; const char* stop; while (1) { if (!*src) return 0; stop = exactly(src); if (stop && (!esc || *(src - 1) != '\\')) return stop; src = stop ? stop : src + 1; } } // skip to delimiter (mx) inside given range // this will savely skip over all quoted strings // recursive skip stuff delimited by start/stop // first start/opener must be consumed already! template const char* skip_over_scopes(const char* src, const char* end) { 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; // has escaped sequence? if (*src == '\\') { ++ src; // skip this (and next) } else if (*src == '"') { in_dquote = ! in_dquote; } else if (*src == '\'') { in_squote = ! in_squote; } else if (in_dquote || in_squote) { // take everything literally } // find another opener inside? else if (const char* pos = start(src)) { ++ level; // increase counter src = pos - 1; // advance position } // look for the closer (maybe final, maybe not) else if (const char* final = stop(src)) { // only close one level? if (level > 0) -- level; // return position at end of stop // delimiter may be multiple chars else return final; // advance position src = final - 1; } // next ++ src; } return 0; } // skip to a skip delimited by parentheses // uses smart `skip_over_scopes` internally const char* parenthese_scope(const char* src); // skip to delimiter (mx) inside given range // this will savely skip over all quoted strings // recursive skip stuff delimited by start/stop // first start/opener must be consumed already! template const char* skip_over_scopes(const char* src) { return skip_over_scopes(src, 0); } // Match a sequence of characters delimited by the supplied chars. template const char* recursive_scopes(const char* src) { // parse opener src = start(src); // abort if not found if (!src) return 0; // parse the rest until final closer return skip_over_scopes(src); } // Match a sequence of characters delimited by the supplied strings. template const char* delimited_by(const char* src) { src = exactly(src); if (!src) return 0; const char* stop; while (1) { if (!*src) return 0; stop = exactly(src); if (stop && (!esc || *(src - 1) != '\\')) return stop; src = stop ? stop : src + 1; } } // Tries to match a certain number of times (between the supplied interval). template const char* between(const char* src) { for (size_t i = 0; i < lo; ++i) { src = mx(src); if (!src) return 0; } for (size_t i = lo; i <= hi; ++i) { const char* new_src = mx(src); if (!new_src) return src; src = new_src; } return src; } // Match a line comment. const char* line_comment(const char* src); const char* line_comment_prefix(const char* src); // Match a block comment. const char* block_comment(const char* src); const char* block_comment_prefix(const char* src); // Match either. const char* comment(const char* src); // Match double- and single-quoted strings. const char* double_quoted_string(const char* src); const char* single_quoted_string(const char* src); const char* quoted_string(const char* src); // Match interpolants. const char* interpolant(const char* src); // Match number prefix ([\+\-]+) const char* number_prefix(const char* src); // Match zero plus white-space or line_comments const char* optional_css_whitespace(const char* src); const char* css_whitespace(const char* src); // Match optional_css_whitepace plus block_comments const char* optional_css_comments(const char* src); const char* css_comments(const char* src); // Match one backslash escaped char const char* escape_seq(const char* src); // Match CSS css variables. const char* custom_property_name(const char* src); // Match a CSS identifier. const char* identifier(const char* src); const char* identifier_alpha(const char* src); const char* identifier_alnum(const char* src); const char* strict_identifier(const char* src); const char* strict_identifier_alpha(const char* src); const char* strict_identifier_alnum(const char* src); // Match a CSS unit identifier. const char* one_unit(const char* src); const char* multiple_units(const char* src); const char* unit_identifier(const char* src); // const char* strict_identifier_alnums(const char* src); // Match reference selector. const char* re_reference_combinator(const char* src); const char* static_reference_combinator(const char* src); const char* schema_reference_combinator(const char* src); // Match interpolant schemas const char* identifier_schema(const char* src); const char* value_schema(const char* src); const char* sass_value(const char* src); // const char* filename(const char* src); // const char* filename_schema(const char* src); // const char* url_schema(const char* src); // const char* url_value(const char* src); const char* vendor_prefix(const char* src); // Match CSS '@' keywords. const char* at_keyword(const char* src); const char* kwd_import(const char* src); const char* kwd_at_root(const char* src); const char* kwd_with_directive(const char* src); const char* kwd_without_directive(const char* src); const char* kwd_media(const char* src); const char* kwd_supports_directive(const char* src); // const char* keyframes(const char* src); // const char* keyf(const char* src); const char* kwd_mixin(const char* src); const char* kwd_function(const char* src); const char* kwd_return_directive(const char* src); const char* kwd_include_directive(const char* src); const char* kwd_content_directive(const char* src); const char* kwd_charset_directive(const char* src); const char* kwd_extend(const char* src); const char* unicode_seq(const char* src); const char* kwd_if_directive(const char* src); const char* kwd_else_directive(const char* src); const char* elseif_directive(const char* src); const char* kwd_for_directive(const char* src); const char* kwd_from(const char* src); const char* kwd_to(const char* src); const char* kwd_through(const char* src); const char* kwd_each_directive(const char* src); const char* kwd_in(const char* src); const char* kwd_while_directive(const char* src); const char* re_nothing(const char* src); const char* re_type_selector2(const char* src); const char* re_special_fun(const char* src); const char* kwd_warn(const char* src); const char* kwd_err(const char* src); const char* kwd_dbg(const char* src); const char* kwd_null(const char* src); const char* re_type_selector(const char* src); const char* re_static_expression(const char* src); // identifier that can start with hyphens const char* css_identifier(const char* src); const char* css_ip_identifier(const char* src); // Match CSS type selectors const char* namespace_schema(const char* src); const char* namespace_prefix(const char* src); const char* type_selector(const char* src); const char* hyphens_and_identifier(const char* src); const char* hyphens_and_name(const char* src); const char* universal(const char* src); // Match CSS id names. const char* id_name(const char* src); // Match CSS class names. const char* class_name(const char* src); // Attribute name in an attribute selector const char* attribute_name(const char* src); // Match placeholder selectors. const char* placeholder(const char* src); // Match CSS numeric constants. const char* op(const char* src); const char* sign(const char* src); const char* unsigned_number(const char* src); const char* number(const char* src); const char* coefficient(const char* src); const char* binomial(const char* src); const char* percentage(const char* src); const char* ampersand(const char* src); const char* dimension(const char* src); const char* hex(const char* src); const char* hexa(const char* src); const char* hex0(const char* src); // const char* rgb_prefix(const char* src); // Match CSS uri specifiers. const char* uri_prefix(const char* src); // Match CSS "!important" keyword. const char* kwd_important(const char* src); // Match CSS "!optional" keyword. const char* kwd_optional(const char* src); // Match Sass "!default" keyword. const char* default_flag(const char* src); const char* global_flag(const char* src); // Match CSS pseudo-class/element prefixes const char* pseudo_prefix(const char* src); // Match CSS function call openers. const char* re_functional(const char* src); const char* re_pseudo_selector(const char* src); const char* functional_schema(const char* src); const char* pseudo_not(const char* src); // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. const char* even(const char* src); const char* odd(const char* src); // Match CSS attribute-matching operators. const char* exact_match(const char* src); const char* class_match(const char* src); const char* dash_match(const char* src); const char* prefix_match(const char* src); const char* suffix_match(const char* src); const char* substring_match(const char* src); // Match CSS combinators. // const char* adjacent_to(const char* src); // const char* precedes(const char* src); // const char* parent_of(const char* src); // const char* ancestor_of(const char* src); // Match SCSS variable names. const char* variable(const char* src); // IE stuff const char* ie_progid(const char* src); const char* ie_expression(const char* src); const char* ie_property(const char* src); const char* ie_keyword_arg(const char* src); const char* ie_keyword_arg_value(const char* src); const char* ie_keyword_arg_property(const char* src); // match url() const char* H(const char* src); const char* W(const char* src); // `UNICODE` makes VS sad const char* UUNICODE(const char* src); const char* NONASCII(const char* src); const char* ESCAPE(const char* src); const char* real_uri_suffix(const char* src); // const char* real_uri_prefix(const char* src); const char* real_uri_value(const char* src); // Path matching functions. // const char* folder(const char* src); // const char* folders(const char* src); const char* static_string(const char* src); const char* static_component(const char* src); const char* static_property(const char* src); const char* static_value(const char* src); // Utility functions for finding and counting characters in a string. template const char* find_first(const char* src) { while (*src && *src != c) ++src; return *src ? src : 0; } template const char* find_first(const char* src) { while (*src && !mx(src)) ++src; return *src ? src : 0; } template const char* find_first_in_interval(const char* beg, const char* end) { bool esc = false; while ((beg < end) && *beg) { if (esc) esc = false; else if (*beg == '\\') esc = true; else if (mx(beg)) return beg; ++beg; } return 0; } template const char* find_first_in_interval(const char* beg, const char* end) { bool esc = false; while ((beg < end) && *beg) { if (esc) esc = false; else if (*beg == '\\') esc = true; else if (const char* pos = skip(beg)) beg = pos; else if (mx(beg)) return beg; ++beg; } return 0; } template unsigned int count_interval(const char* beg, const char* end) { unsigned int counter = 0; bool esc = false; while (beg < end && *beg) { const char* p; if (esc) { esc = false; ++beg; } else if (*beg == '\\') { esc = true; ++beg; } else if ((p = mx(beg))) { ++counter; beg = p; } else { ++beg; } } return counter; } template const char* padded_token(const char* src) { size_t got = 0; const char* pos = src; while (got < size) { if (!mx(pos)) break; ++ pos; ++ got; } while (got < size) { if (!pad(pos)) break; ++ pos; ++ got; } return got ? pos : 0; } template const char* minmax_range(const char* src) { size_t got = 0; const char* pos = src; while (got < max) { if (!mx(pos)) break; ++ pos; ++ got; } if (got < min) return 0; if (got > max) return 0; return pos; } template const char* char_range(const char* src) { if (*src < min) return 0; if (*src > max) return 0; return src + 1; } } } #endif libsass-3.3.4/src/remove_placeholders.cpp000066400000000000000000000045511267254216700205170ustar00rootroot00000000000000#include "sass.hpp" #include "remove_placeholders.hpp" #include "context.hpp" #include "inspect.hpp" #include namespace Sass { Remove_Placeholders::Remove_Placeholders(Context& ctx) : ctx(ctx) { } void Remove_Placeholders::operator()(Block* b) { for (size_t i = 0, L = b->length(); i < L; ++i) { (*b)[i]->perform(this); } } Selector_List* Remove_Placeholders::remove_placeholders(Selector_List* sl) { Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate()); for (size_t i = 0, L = sl->length(); i < L; ++i) { if (!(*sl)[i]->contains_placeholder()) { *new_sl << (*sl)[i]; } } return new_sl; } void Remove_Placeholders::operator()(Ruleset* r) { // Create a new selector group without placeholders Selector_List* sl = static_cast(r->selector()); if (sl) { // Set the new placeholder selector list r->selector(remove_placeholders(sl)); // Remove placeholders in wrapped selectors for (Complex_Selector* cs : *sl) { while (cs) { if (cs->head()) { for (Simple_Selector* ss : *cs->head()) { if (Wrapped_Selector* ws = dynamic_cast(ss)) { if (Selector_List* sl = dynamic_cast(ws->selector())) { Selector_List* clean = remove_placeholders(sl); // also clean superflous parent selectors // probably not really the correct place clean->remove_parent_selectors(); ws->selector(clean); } } } } cs = cs->tail(); } } } // Iterate into child blocks Block* b = r->block(); for (size_t i = 0, L = b->length(); i < L; ++i) { if ((*b)[i]) (*b)[i]->perform(this); } } void Remove_Placeholders::operator()(Media_Block* m) { Block* b = m->block(); for (size_t i = 0, L = b->length(); i < L; ++i) { if ((*b)[i]) (*b)[i]->perform(this); } } void Remove_Placeholders::operator()(At_Rule* a) { if (a->block()) a->block()->perform(this); } } libsass-3.3.4/src/remove_placeholders.hpp000066400000000000000000000013371267254216700205230ustar00rootroot00000000000000#ifndef SASS_REMOVE_PLACEHOLDERS_H #define SASS_REMOVE_PLACEHOLDERS_H #pragma once #include "ast.hpp" #include "operation.hpp" namespace Sass { class Context; class Remove_Placeholders : public Operation_CRTP { Context& ctx; void fallback_impl(AST_Node* n) {} private: Selector_List* remove_placeholders(Selector_List*); public: Remove_Placeholders(Context&); ~Remove_Placeholders() { } void operator()(Block*); void operator()(Ruleset*); void operator()(Media_Block*); void operator()(At_Rule*); template void fallback(U x) { return fallback_impl(x); } }; } #endif libsass-3.3.4/src/sass.cpp000066400000000000000000000017241267254216700154450ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include "sass.h" #include "file.hpp" #include "util.hpp" extern "C" { using namespace Sass; // caller must free the returned memory char* ADDCALL sass_string_quote (const char *str, const char quote_mark) { std::string quoted = quote(str, quote_mark); return sass_strdup(quoted.c_str()); } // caller must free the returned memory char* ADDCALL sass_string_unquote (const char *str) { std::string unquoted = unquote(str); return sass_strdup(unquoted.c_str()); } // Make sure to free the returned value! // Incs array has to be null terminated! char* ADDCALL sass_resolve_file (const char* file, const char* paths[]) { std::string resolved(File::find_file(file, paths)); return sass_strdup(resolved.c_str()); } // Get compiled libsass version const char* ADDCALL libsass_version(void) { return LIBSASS_VERSION; } } libsass-3.3.4/src/sass.hpp000066400000000000000000000063161267254216700154540ustar00rootroot00000000000000// must be the first include in all compile units #ifndef SASS_SASS_H #define SASS_SASS_H // undefine extensions macro to tell sys includes // that we do not want any macros to be exported // mainly fixes an issue on SmartOS (SEC macro) #undef __EXTENSIONS__ #ifdef _MSC_VER #pragma warning(disable : 4005) #endif // aplies to MSVC and MinGW #ifdef _WIN32 // we do not want the ERROR macro # define NOGDI // we do not want the min/max macro # define NOMINMAX // we do not want the IN/OUT macro # define _NO_W32_PSEUDO_MODIFIERS #endif // should we be case insensitive // when dealing with files or paths #ifndef FS_CASE_SENSITIVE # ifdef _WIN32 # define FS_CASE_SENSITIVE 0 # else # define FS_CASE_SENSITIVE 1 # endif #endif // path separation char #ifndef PATH_SEP # ifdef _WIN32 # define PATH_SEP ';' # else # define PATH_SEP ':' # endif #endif // include C-API header #include "sass/base.h" // output behaviours namespace Sass { // create some C++ aliases for the most used options const static Sass_Output_Style NESTED = SASS_STYLE_NESTED; const static Sass_Output_Style COMPACT = SASS_STYLE_COMPACT; const static Sass_Output_Style EXPANDED = SASS_STYLE_EXPANDED; const static Sass_Output_Style COMPRESSED = SASS_STYLE_COMPRESSED; // only used internal to trigger ruby inspect behavior const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT; const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS; }; // input behaviours enum Sass_Input_Style { SASS_CONTEXT_NULL, SASS_CONTEXT_FILE, SASS_CONTEXT_DATA, SASS_CONTEXT_FOLDER }; // simple linked list struct string_list { string_list* next; char* string; }; // sass config options structure struct Sass_Inspect_Options { // Output style for the generated css code // A value from above SASS_STYLE_* constants enum Sass_Output_Style output_style; // Precision for fractional numbers int precision; // initialization list (constructor with defaults) Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED, int precision = 5) : output_style(style), precision(precision) { } }; // sass config options structure struct Sass_Output_Options : Sass_Inspect_Options { // String to be used for indentation const char* indent; // String to be used to for line feeds const char* linefeed; // Emit comments in the generated CSS indicating // the corresponding source line. bool source_comments; // initialization list (constructor with defaults) Sass_Output_Options(struct Sass_Inspect_Options opt, const char* indent = " ", const char* linefeed = "\n", bool source_comments = false) : Sass_Inspect_Options(opt), indent(indent), linefeed(linefeed), source_comments(source_comments) { } // initialization list (constructor with defaults) Sass_Output_Options(Sass_Output_Style style = Sass::NESTED, int precision = 5, const char* indent = " ", const char* linefeed = "\n", bool source_comments = false) : Sass_Inspect_Options(style, precision), indent(indent), linefeed(linefeed), source_comments(source_comments) { } }; #endif libsass-3.3.4/src/sass2scss.cpp000066400000000000000000000564611267254216700164330ustar00rootroot00000000000000/** * sass2scss * Licensed under the MIT License * Copyright (c) Marcel Greter */ #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE #endif // include library #include #include #include #include #include #include #include ///* // // src comments: comments in sass syntax (staring with //) // css comments: multiline comments in css syntax (starting with /*) // // KEEP_COMMENT: keep src comments in the resulting css code // STRIP_COMMENT: strip out all comments (either src or css) // CONVERT_COMMENT: convert all src comments to css comments // //*/ // our own header #include "sass2scss.h" // add namespace for c++ namespace Sass { // return the actual prettify value from options #define PRETTIFY(converter) (converter.options - (converter.options & 248)) // query the options integer to check if the option is enables #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT) #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT) #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT) // some makros to access the indentation stack #define INDENT(converter) (converter.indents.top()) // some makros to query comment parser status #define IS_PARSING(converter) (converter.comment == "") #define IS_COMMENT(converter) (converter.comment != "") #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter)) #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter))) // pretty printer helper function static std::string closer (const converter& converter) { return PRETTIFY(converter) == 0 ? " }" : PRETTIFY(converter) <= 1 ? " }" : "\n" + INDENT(converter) + "}"; } // pretty printer helper function static std::string opener (const converter& converter) { return PRETTIFY(converter) == 0 ? " { " : PRETTIFY(converter) <= 2 ? " {" : "\n" + INDENT(converter) + "{"; } // check if the given string is a pseudo selector // needed to differentiate from sass property syntax static bool isPseudoSelector (std::string& sel) { size_t len = sel.length(); if (len < 1) return false; size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1); if (pos != std::string::npos) sel.erase(pos, std::string::npos); size_t i = sel.length(); while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); } // CSS Level 1 - Recommendation if (sel == ":link") return true; if (sel == ":visited") return true; if (sel == ":active") return true; // CSS Level 2 (Revision 1) - Recommendation if (sel == ":lang") return true; if (sel == ":first-child") return true; if (sel == ":hover") return true; if (sel == ":focus") return true; // disabled - also valid properties // if (sel == ":left") return true; // if (sel == ":right") return true; if (sel == ":first") return true; // Selectors Level 3 - Recommendation if (sel == ":target") return true; if (sel == ":root") return true; if (sel == ":nth-child") return true; if (sel == ":nth-last-of-child") return true; if (sel == ":nth-of-type") return true; if (sel == ":nth-last-of-type") return true; if (sel == ":last-child") return true; if (sel == ":first-of-type") return true; if (sel == ":last-of-type") return true; if (sel == ":only-child") return true; if (sel == ":only-of-type") return true; if (sel == ":empty") return true; if (sel == ":not") return true; // CSS Basic User Interface Module Level 3 - Working Draft if (sel == ":default") return true; if (sel == ":valid") return true; if (sel == ":invalid") return true; if (sel == ":in-range") return true; if (sel == ":out-of-range") return true; if (sel == ":required") return true; if (sel == ":optional") return true; if (sel == ":read-only") return true; if (sel == ":read-write") return true; if (sel == ":dir") return true; if (sel == ":enabled") return true; if (sel == ":disabled") return true; if (sel == ":checked") return true; if (sel == ":indeterminate") return true; if (sel == ":nth-last-child") return true; // Selectors Level 4 - Working Draft if (sel == ":any-link") return true; if (sel == ":local-link") return true; if (sel == ":scope") return true; if (sel == ":active-drop-target") return true; if (sel == ":valid-drop-target") return true; if (sel == ":invalid-drop-target") return true; if (sel == ":current") return true; if (sel == ":past") return true; if (sel == ":future") return true; if (sel == ":placeholder-shown") return true; if (sel == ":user-error") return true; if (sel == ":blank") return true; if (sel == ":nth-match") return true; if (sel == ":nth-last-match") return true; if (sel == ":nth-column") return true; if (sel == ":nth-last-column") return true; if (sel == ":matches") return true; // Fullscreen API - Living Standard if (sel == ":fullscreen") return true; // not a pseudo selector return false; } // check if there is some char data // will ignore everything in comments static bool hasCharData (std::string& sass) { size_t col_pos = 0; while (true) { // try to find some meaningfull char col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos); // there was no meaningfull char found if (col_pos == std::string::npos) return false; // found a multiline comment opener if (sass.substr(col_pos, 2) == "/*") { // find the multiline comment closer col_pos = sass.find("*/", col_pos); // maybe we did not find the closer here if (col_pos == std::string::npos) return false; // skip closer col_pos += 2; } else { return true; } } } // EO hasCharData // find src comment opener // correctly skips quoted strings static size_t findCommentOpener (std::string& sass) { size_t col_pos = 0; bool apoed = false; bool quoted = false; bool comment = false; size_t brackets = 0; while (col_pos != std::string::npos) { // process all interesting chars col_pos = sass.find_first_of("\"\'/\\*()", col_pos); // assertion for valid result if (col_pos != std::string::npos) { char character = sass.at(col_pos); if (character == '(') { if (!quoted && !apoed) brackets ++; } else if (character == ')') { if (!quoted && !apoed) brackets --; } else if (character == '\"') { // invert quote bool if (!apoed && !comment) quoted = !quoted; } else if (character == '\'') { // invert quote bool if (!quoted && !comment) apoed = !apoed; } else if (col_pos > 0 && character == '/') { if (sass.at(col_pos - 1) == '*') { comment = false; } // next needs to be a slash too else if (sass.at(col_pos - 1) == '/') { // only found if not in single or double quote, bracket or comment if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1; } } else if (character == '\\') { // skip next char if in quote if (quoted || apoed) col_pos ++; } // this might be a comment opener else if (col_pos > 0 && character == '*') { // opening a multiline comment if (sass.at(col_pos - 1) == '/') { // we are now in a comment if (!quoted && !apoed) comment = true; } } // skip char col_pos ++; } } // EO while return col_pos; } // EO findCommentOpener // remove multiline comments from sass string // correctly skips quoted strings static std::string removeMultilineComment (std::string &sass) { std::string clean = ""; size_t col_pos = 0; size_t open_pos = 0; size_t close_pos = 0; bool apoed = false; bool quoted = false; bool comment = false; // process sass til string end while (col_pos != std::string::npos) { // process all interesting chars col_pos = sass.find_first_of("\"\'/\\*", col_pos); // assertion for valid result if (col_pos != std::string::npos) { char character = sass.at(col_pos); // found quoted string delimiter if (character == '\"') { if (!apoed && !comment) quoted = !quoted; } else if (character == '\'') { if (!quoted && !comment) apoed = !apoed; } // found possible comment closer else if (character == '/') { // look back to see if it is actually a closer if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*') { close_pos = col_pos + 1; comment = false; } } else if (character == '\\') { // skip escaped char if (quoted || apoed) col_pos ++; } // this might be a comment opener else if (character == '*') { // look back to see if it is actually an opener if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/') { comment = true; open_pos = col_pos - 1; clean += sass.substr(close_pos, open_pos - close_pos); } } // skip char col_pos ++; } } // EO while // add final parts (add half open comment text) if (comment) clean += sass.substr(open_pos); else clean += sass.substr(close_pos); // return string return clean; } // EO removeMultilineComment // right trim a given string std::string rtrim(const std::string &sass) { std::string trimmed = sass; 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; } // EO rtrim // flush whitespace and print additional text, but // only print additional chars and buffer whitespace std::string flush (std::string& sass, converter& converter) { // return flushed std::string scss = ""; // print whitespace buffer scss += PRETTIFY(converter) > 0 ? converter.whitespace : ""; // reset whitespace buffer converter.whitespace = ""; // remove possible newlines from string size_t pos_right = sass.find_last_not_of("\n\r"); if (pos_right == std::string::npos) return scss; // get the linefeeds from the string std::string lfs = sass.substr(pos_right + 1); sass = sass.substr(0, pos_right + 1); // find some source comment opener size_t comment_pos = findCommentOpener(sass); // check if there was a source comment if (comment_pos != std::string::npos) { // convert comment (but only outside other coments) if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter)) { // convert to multiline comment sass.at(comment_pos + 1) = '*'; // add comment node to the whitespace sass += " */"; } // not at line start if (comment_pos > 0) { // also include whitespace before the actual comment opener size_t ws_pos = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, comment_pos - 1); comment_pos = ws_pos == std::string::npos ? 0 : ws_pos + 1; } if (!STRIP_COMMENT(converter)) { // add comment node to the whitespace converter.whitespace += sass.substr(comment_pos); } else { // sass = removeMultilineComments(sass); } // update the actual sass code sass = sass.substr(0, comment_pos); } // add newline as getline discharged it converter.whitespace += lfs + "\n"; // maybe remove any leading whitespace if (PRETTIFY(converter) == 0) { // remove leading whitespace and update string size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE); if (pos_left != std::string::npos) sass = sass.substr(pos_left); } // add flushed data scss += sass; // return string return scss; } // EO flush // process a line of the sass text std::string process (std::string& sass, converter& converter) { // resulting string std::string scss = ""; // strip multi line comments if (STRIP_COMMENT(converter)) { sass = removeMultilineComment(sass); } // right trim input sass = rtrim(sass); // get postion of first meaningfull character in string size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE); // special case for final run if (converter.end_of_file) pos_left = 0; // maybe has only whitespace if (pos_left == std::string::npos) { // just add complete whitespace converter.whitespace += sass + "\n"; } // have meaningfull first char else { // extract and store indentation string std::string indent = sass.substr(0, pos_left); // check if current line starts a comment std::string open = sass.substr(pos_left, 2); // line has less or same indentation // finalize previous open parser context if (indent.length() <= INDENT(converter).length()) { // close multilinie comment if (IS_CSS_COMMENT(converter)) { // check if comments will be stripped anyway if (!STRIP_COMMENT(converter)) scss += " */"; } // close src comment comment else if (IS_SRC_COMMENT(converter)) { // add a newline to avoid closer on same line // this would put the bracket in the comment node // no longer needed since we parse them correctly // if (KEEP_COMMENT(converter)) scss += "\n"; } // close css properties else if (converter.property) { // add closer unless in concat mode if (!converter.comma) { // if there was no colon we have a selector // looks like there were no inner properties if (converter.selector) scss += " {}"; // add final semicolon else if (!converter.semicolon) scss += ";"; } } // reset comment state converter.comment = ""; } // make sure we close every "higher" block while (indent.length() < INDENT(converter).length()) { // pop stacked context converter.indents.pop(); // print close bracket if (IS_PARSING(converter)) { scss += closer(converter); } else { scss += " */"; } // reset comment state converter.comment = ""; } // reset converter state converter.selector = false; // check if we have sass property syntax if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::") { // default to a selector // change back if property found converter.selector = true; // get postion 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) { // get the possible pseudo selector std::string pseudo = sass.substr(pos_left, pos_wspace - pos_left); // get position of the first real property value char // pseudo selectors get this far, but have no actual value size_t pos_value = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_wspace); // assertion check for valid result if (pos_value != std::string::npos) { // only process if not (fallowed by a semicolon or is a pseudo selector) if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo))) { // create new string by interchanging the colon sign for property and value sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace); // try to find a colon in the current line, but only ... size_t pos_colon = sass.find_first_not_of(":", pos_left); // assertion for valid result if (pos_colon != std::string::npos) { // ... after the first word (skip begining 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; } } } } } // terminate some statements immediately else if ( sass.substr(pos_left, 5) == "@warn" || sass.substr(pos_left, 6) == "@debug" || sass.substr(pos_left, 6) == "@error" || sass.substr(pos_left, 8) == "@charset" ) { sass = indent + sass.substr(pos_left); } // replace some specific sass shorthand directives (if not fallowed by a white space character) else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1) { sass = indent + "@mixin " + sass.substr(pos_left + 1); } else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1) { sass = indent + "@include " + sass.substr(pos_left + 1); } // add quotes for import if needed else if (sass.substr(pos_left, 7) == "@import") { // 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) + "\""; } } } } else if ( sass.substr(pos_left, 7) != "@return" && sass.substr(pos_left, 7) != "@extend" && sass.substr(pos_left, 8) != "@include" && sass.substr(pos_left, 8) != "@content" ) { // probably a selector anyway converter.selector = true; // try to find first colon in the current line size_t pos_colon = sass.find_first_of(":", pos_left); // assertion that we have a colon if (pos_colon != std::string::npos) { // it is not a selector if we have a space after a colon if (sass[pos_colon+1] == ' ') converter.selector = false; if (sass[pos_colon+1] == ' ') converter.selector = false; } } // current line has more indentation if (indent.length() >= INDENT(converter).length()) { // not in comment mode if (IS_PARSING(converter)) { // has meaningfull chars if (hasCharData(sass)) { // is probably a property // also true for selectors converter.property = true; } } } // current line has more indentation if (indent.length() > INDENT(converter).length()) { // not in comment mode if (IS_PARSING(converter)) { // had meaningfull chars if (converter.property) { // print block opener scss += opener(converter); // push new stack context converter.indents.push(""); // store block indentation INDENT(converter) = indent; } } // is and will be a src comment else if (!IS_CSS_COMMENT(converter)) { // scss does not allow multiline src comments // therefore add forward slashes to all lines sass.at(INDENT(converter).length()+0) = '/'; // there is an edge case here if indentation // is minimal (will overwrite the fist char) sass.at(INDENT(converter).length()+1) = '/'; // could code around that, but I dont' think // this will ever be the cause for any trouble } } // line is opening a new comment if (open == "/*" || open == "//") { // reset the property state converter.property = false; // close previous comment if (IS_CSS_COMMENT(converter) && open != "") { if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */"; } // force single line comments // into a correct css comment if (CONVERT_COMMENT(converter)) { if (IS_PARSING(converter)) { sass.at(pos_left + 1) = '*'; } } // set comment flag converter.comment = open; } // flush data only under certain conditions if (!( // strip css and src comments if option is set (IS_COMMENT(converter) && STRIP_COMMENT(converter)) || // strip src comment even if strip option is not set // but only if the keep src comment option is not set (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter)) )) { // flush data and buffer whitespace scss += flush(sass, converter); } // get postion of last meaningfull char size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE); // check for invalid result if (pos_right != std::string::npos) { // get the last meaningfull char std::string close = sass.substr(pos_right, 1); // check if next line should be concatenated (list mode) converter.comma = IS_PARSING(converter) && close == ","; converter.semicolon = IS_PARSING(converter) && close == ";"; // check if we have more than // one meaningfull char if (pos_right > 0) { // get the last two chars from string std::string close = sass.substr(pos_right - 1, 2); // update parser status for expicitly closed comment if (close == "*/") converter.comment = ""; } } // EO have meaningfull chars from end } // EO have meaningfull chars from start // return scss return scss; } // EO process // read line with either CR, LF or CR LF format // http://stackoverflow.com/a/6089413/1550314 static std::istream& safeGetline(std::istream& is, std::string& t) { t.clear(); // The characters in the stream are read one-by-one using a std::streambuf. // That is faster than reading them one-by-one using the std::istream. // Code that uses streambuf this way must be guarded by a sentry object. // The sentry object performs various tasks, // such as thread synchronization and updating the stream state. std::istream::sentry se(is, true); std::streambuf* sb = is.rdbuf(); for(;;) { int c = sb->sbumpc(); switch (c) { case '\n': return is; case '\r': if(sb->sgetc() == '\n') sb->sbumpc(); return is; case EOF: // Also handle the case when the last line has no line ending if(t.empty()) is.setstate(std::ios::eofbit); return is; default: t += (char)c; } } } // the main converter function for c++ char* sass2scss (const std::string& sass, const int options) { // local variables std::string line; std::string scss = ""; std::stringstream stream(sass); // create converter variable converter converter; // initialise all options converter.comma = false; converter.property = false; converter.selector = false; converter.semicolon = false; converter.end_of_file = false; converter.comment = ""; converter.whitespace = ""; converter.indents.push(""); converter.options = options; // read line by line and process them while(safeGetline(stream, line) && !stream.eof()) { scss += process(line, converter); } // create mutable string std::string closer = ""; // set the end of file flag converter.end_of_file = true; // process to close all open blocks scss += process(closer, converter); // allocate new memory on the heap // caller has to free it after use char * cstr = (char*) malloc (scss.length() + 1); // create a copy of the string strcpy (cstr, scss.c_str()); // return pointer return &cstr[0]; } // EO sass2scss } // EO namespace // implement for c extern "C" { char* ADDCALL sass2scss (const char* sass, const int options) { return Sass::sass2scss(sass, options); } // Get compiled sass2scss version const char* ADDCALL sass2scss_version(void) { return SASS2SCSS_VERSION; } } libsass-3.3.4/src/sass_context.cpp000066400000000000000000000675361267254216700172260ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include "sass.h" #include "file.hpp" #include "json.hpp" #include "util.hpp" #include "context.hpp" #include "sass_context.hpp" #include "sass_functions.hpp" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" #define LFEED "\n" extern "C" { using namespace Sass; static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { *to = *from; } #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \ { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 0; } #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \ type sass_context_take_##option (struct Sass_Context* ctx) \ { type foo = ctx->option; ctx->option = 0; return foo; } static int handle_errors(Sass_Context* c_ctx) { try { throw; } catch (Exception::Base& e) { std::stringstream msg_stream; std::string cwd(Sass::File::get_cwd()); std::string msg_prefix(e.errtype()); bool got_newline = false; msg_stream << msg_prefix << ": "; const char* msg = e.what(); while(msg && *msg) { if (*msg == '\r') { got_newline = true; } else if (*msg == '\n') { got_newline = true; } else if (got_newline) { msg_stream << std::string(msg_prefix.size() + 2, ' '); got_newline = false; } msg_stream << *msg; ++ msg; } if (!got_newline) msg_stream << "\n"; if (e.import_stack) { for (size_t i = 1; i < e.import_stack->size() - 1; ++i) { std::string path((*e.import_stack)[i]->imp_path); std::string rel_path(Sass::File::abs2rel(path, cwd, cwd)); msg_stream << std::string(msg_prefix.size() + 2, ' '); msg_stream << (i == 1 ? " on line " : " from line "); msg_stream << e.pstate.line+1 << " of " << rel_path << "\n"; } } else { std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); msg_stream << std::string(msg_prefix.size() + 2, ' '); msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n"; } // now create the code trace (ToDo: maybe have util functions?) if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) { size_t line = e.pstate.line; const char* line_beg = e.pstate.src; while (line_beg && *line_beg && line) { if (*line_beg == '\n') -- line; ++ line_beg; } const char* line_end = line_beg; while (line_end && *line_end && *line_end != '\n') { if (*line_end == '\n') break; if (*line_end == '\r') break; line_end ++; } size_t max_left = 42; size_t max_right = 78; size_t move_in = e.pstate.column > max_left ? e.pstate.column - max_left : 0; size_t shorten = (line_end - line_beg) - move_in > max_right ? (line_end - line_beg) - move_in - max_right : 0; msg_stream << ">> " << std::string(line_beg + move_in, line_end - shorten) << "\n"; msg_stream << " " << std::string(e.pstate.column - move_in, '-') << "^\n"; } JsonNode* json_err = json_mkobject(); json_append_member(json_err, "status", json_mknumber(1)); json_append_member(json_err, "file", json_mkstring(e.pstate.path)); json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line+1))); json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column+1))); json_append_member(json_err, "message", json_mkstring(e.what())); json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e.what()); c_ctx->error_status = 1; c_ctx->error_file = sass_strdup(e.pstate.path); c_ctx->error_line = e.pstate.line+1; c_ctx->error_column = e.pstate.column+1; c_ctx->error_src = 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_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(ba.what()); c_ctx->error_status = 2; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } 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_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e.what()); c_ctx->error_status = 3; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } 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_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e.c_str()); c_ctx->error_status = 4; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } 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_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e); c_ctx->error_status = 4; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } 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")); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup("unknown"); c_ctx->error_status = 5; c_ctx->output_string = 0; c_ctx->source_map_string = 0; json_delete(json_err); } return c_ctx->error_status; } // generic compilation function (not exported, use file/data compile instead) static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw() { try { // convert include path linked list to static array struct string_list* inc = c_ctx->include_paths; // very poor loop to get the length of the linked list size_t inc_size = 0; while (inc) { inc_size ++; inc = inc->next; } // create char* array to hold all paths plus null terminator const char** include_paths = (const char**) calloc(inc_size + 1, sizeof(char*)); if (include_paths == 0) throw(std::bad_alloc()); // reset iterator inc = c_ctx->include_paths; // copy over the paths for (size_t i = 0; inc; i++) { include_paths[i] = inc->string; inc = inc->next; } // convert plugin path linked list to static array struct string_list* imp = c_ctx->plugin_paths; // very poor loop to get the length of the linked list size_t imp_size = 0; while (imp) { imp_size ++; imp = imp->next; } // create char* array to hold all paths plus null terminator const char** plugin_paths = (const char**) calloc(imp_size + 1, sizeof(char*)); if (plugin_paths == 0) { free(include_paths); //free include_paths before throw throw(std::bad_alloc()); } // reset iterator imp = c_ctx->plugin_paths; // copy over the paths for (size_t i = 0; imp; i++) { plugin_paths[i] = imp->string; imp = imp->next; } // free intermediate data free(include_paths); free(plugin_paths); // register our custom functions if (c_ctx->c_functions) { auto this_func_data = c_ctx->c_functions; while (this_func_data && *this_func_data) { cpp_ctx->add_c_function(*this_func_data); ++this_func_data; } } // register our custom headers if (c_ctx->c_headers) { auto this_head_data = c_ctx->c_headers; while (this_head_data && *this_head_data) { cpp_ctx->add_c_header(*this_head_data); ++this_head_data; } } // register our custom importers if (c_ctx->c_importers) { auto this_imp_data = c_ctx->c_importers; while (this_imp_data && *this_imp_data) { cpp_ctx->add_c_importer(*this_imp_data); ++this_imp_data; } } // reset error status c_ctx->error_json = 0; c_ctx->error_text = 0; 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_line = std::string::npos; c_ctx->error_column = std::string::npos; // allocate a new compiler instance Sass_Compiler* compiler = (struct Sass_Compiler*) calloc(1, sizeof(struct Sass_Compiler)); compiler->state = SASS_COMPILER_CREATED; // store in sass compiler compiler->c_ctx = c_ctx; compiler->cpp_ctx = cpp_ctx; cpp_ctx->c_compiler = compiler; // use to parse block return compiler; } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } // error return 0; } static Block* sass_parse_block (Sass_Compiler* compiler) throw() { // assert valid pointer if (compiler == 0) return 0; // The cpp context must be set by now Context* cpp_ctx = compiler->cpp_ctx; Sass_Context* c_ctx = compiler->c_ctx; // We will take care to wire up the rest compiler->cpp_ctx->c_compiler = compiler; compiler->state = SASS_COMPILER_PARSED; try { // get input/output path from options std::string input_path = safe_str(c_ctx->input_path); std::string output_path = safe_str(c_ctx->output_path); // parsed root block Block* root = 0; // maybe skip some entries of included files // we do not include stdin for data contexts bool skip = c_ctx->type == SASS_CONTEXT_DATA; // dispatch parse call root = cpp_ctx->parse(); // abort on errors if (!root) return 0; // skip all prefixed files? (ToDo: check srcmap) // IMO source-maps should point to headers already // therefore don't skip it for now. re-enable or // remove completely once this is tested size_t headers = cpp_ctx->head_imports; // copy the included files on to the context (dont forget to free later) if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) throw(std::bad_alloc()); // return parsed block return root; } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } // error return 0; } // generic compilation function (not exported, use file/data compile instead) static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) { // prepare sass compiler with context and options Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx); try { // call each compiler step sass_compiler_parse(compiler); sass_compiler_execute(compiler); } // pass errors to generic error handler catch (...) { handle_errors(c_ctx); } sass_delete_compiler(compiler); return c_ctx->error_status; } inline void init_options (struct Sass_Options* options) { options->precision = 5; options->indent = " "; options->linefeed = LFEED; } Sass_Options* ADDCALL sass_make_options (void) { struct Sass_Options* options = (struct Sass_Options*) calloc(1, sizeof(struct Sass_Options)); if (options == 0) { std::cerr << "Error allocating memory for options" << std::endl; return 0; } init_options(options); return options; } Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path) { 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; init_options(ctx); try { if (input_path == 0) { throw(std::runtime_error("File context created without an input path")); } if (*input_path == 0) { throw(std::runtime_error("File context created with empty input path")); } sass_option_set_input_path(ctx, input_path); } catch (...) { handle_errors(ctx); } return ctx; } Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string) { 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; init_options(ctx); try { if (source_string == 0) { throw(std::runtime_error("Data context created without a source string")); } if (*source_string == 0) { throw(std::runtime_error("Data context created with empty source string")); } ctx->source_string = source_string; } catch (...) { handle_errors(ctx); } return ctx; } struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 0; Context* cpp_ctx = new Data_Context(*data_ctx); return sass_prepare_context(data_ctx, cpp_ctx); } struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { if (file_ctx == 0) return 0; Context* cpp_ctx = new File_Context(*file_ctx); return sass_prepare_context(file_ctx, cpp_ctx); } int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 1; if (data_ctx->error_status) 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) // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } } catch (...) { return handle_errors(data_ctx) | 1; } Context* cpp_ctx = new Data_Context(*data_ctx); return sass_compile_context(data_ctx, cpp_ctx); } int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx) { if (file_ctx == 0) return 1; if (file_ctx->error_status) return file_ctx->error_status; try { if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path")); } if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } } catch (...) { return handle_errors(file_ctx) | 1; } Context* cpp_ctx = new File_Context(*file_ctx); return sass_compile_context(file_ctx, cpp_ctx); } int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler) { if (compiler == 0) return 1; if (compiler->state == SASS_COMPILER_PARSED) return 0; if (compiler->state != SASS_COMPILER_CREATED) return -1; if (compiler->c_ctx == NULL) return 1; if (compiler->cpp_ctx == NULL) return 1; if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; // parse the context we have set up (file or data) compiler->root = sass_parse_block(compiler); // success return 0; } int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler) { if (compiler == 0) return 1; if (compiler->state == SASS_COMPILER_EXECUTED) return 0; if (compiler->state != SASS_COMPILER_PARSED) return -1; if (compiler->c_ctx == NULL) return 1; if (compiler->cpp_ctx == NULL) return 1; if (compiler->root == NULL) return 1; if (compiler->c_ctx->error_status) return compiler->c_ctx->error_status; compiler->state = SASS_COMPILER_EXECUTED; Context* cpp_ctx = compiler->cpp_ctx; Block* root = compiler->root; // compile the parsed root block try { compiler->c_ctx->output_string = cpp_ctx->render(root); } // pass catched errors to generic error handler catch (...) { return handle_errors(compiler->c_ctx) | 1; } // generate source map json and store on context compiler->c_ctx->source_map_string = cpp_ctx->render_srcmap(); // success return 0; } // helper function, not exported, only accessible locally static void sass_clear_options (struct Sass_Options* options) { if (options == 0) return; // Deallocate custom functions if (options->c_functions) { Sass_Function_List this_func_data = options->c_functions; while (this_func_data && *this_func_data) { free(*this_func_data); ++this_func_data; } } // Deallocate custom headers if (options->c_headers) { Sass_Importer_List this_head_data = options->c_headers; while (this_head_data && *this_head_data) { free(*this_head_data); ++this_head_data; } } // Deallocate custom importers if (options->c_importers) { Sass_Importer_List this_imp_data = options->c_importers; while (this_imp_data && *this_imp_data) { free(*this_imp_data); ++this_imp_data; } } // Deallocate inc paths if (options->plugin_paths) { struct string_list* cur; struct string_list* next; cur = options->plugin_paths; while (cur) { next = cur->next; free(cur->string); free(cur); cur = next; } } // Deallocate inc paths if (options->include_paths) { struct string_list* cur; struct string_list* next; cur = options->include_paths; while (cur) { next = cur->next; free(cur->string); free(cur); cur = next; } } // Free custom functions free(options->c_functions); // Free custom importers free(options->c_importers); free(options->c_headers); // Reset our pointers options->c_functions = 0; options->c_importers = 0; options->c_headers = 0; options->plugin_paths = 0; options->include_paths = 0; } // helper function, not exported, only accessible locally // sass_free_context is also defined in old sass_interface static void sass_clear_context (struct Sass_Context* ctx) { if (ctx == 0) return; // release the allocated memory (mostly via sass_strdup) if (ctx->output_string) free(ctx->output_string); if (ctx->source_map_string) free(ctx->source_map_string); if (ctx->error_message) free(ctx->error_message); if (ctx->error_text) free(ctx->error_text); if (ctx->error_json) free(ctx->error_json); if (ctx->error_file) free(ctx->error_file); if (ctx->input_path) free(ctx->input_path); if (ctx->output_path) free(ctx->output_path); if (ctx->plugin_path) free(ctx->plugin_path); if (ctx->include_path) free(ctx->include_path); if (ctx->source_map_file) free(ctx->source_map_file); if (ctx->source_map_root) free(ctx->source_map_root); free_string_array(ctx->included_files); // play safe and reset properties ctx->output_string = 0; ctx->source_map_string = 0; ctx->error_message = 0; ctx->error_text = 0; ctx->error_json = 0; ctx->error_file = 0; ctx->input_path = 0; ctx->output_path = 0; ctx->include_path = 0; ctx->source_map_file = 0; ctx->source_map_root = 0; ctx->included_files = 0; // now clear the options sass_clear_options(ctx); } void ADDCALL sass_delete_compiler (struct Sass_Compiler* compiler) { if (compiler == 0) { return; } Context* cpp_ctx = compiler->cpp_ctx; if (cpp_ctx) delete(cpp_ctx); compiler->cpp_ctx = 0; free(compiler); } // Deallocate all associated memory with file context void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx) { // clear the context and free it sass_clear_context(ctx); free(ctx); } // Deallocate all associated memory with data context void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx) { // clean the source string if it was not passed // we reset this member once we start parsing if (ctx->source_string) free(ctx->source_string); // clear the context and free it sass_clear_context(ctx); free(ctx); } // Getters for sass context from specific implementations struct Sass_Context* ADDCALL sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; } struct Sass_Context* ADDCALL sass_data_context_get_context(struct Sass_Data_Context* ctx) { return ctx; } // Getters for context options from Sass_Context struct Sass_Options* ADDCALL sass_context_get_options(struct Sass_Context* ctx) { return ctx; } struct Sass_Options* ADDCALL sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; } struct Sass_Options* ADDCALL sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; } 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) 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; } // Getters for Sass_Compiler options (query import stack) size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); } Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); } Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx) { return compiler->cpp_ctx->import_stack[idx]; } // Calculate the size of the stored null terminated array size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx) { size_t l = 0; auto i = ctx->included_files; while (i && *i) { ++i; ++l; } return l; } // Create getter and setters for options IMPLEMENT_SASS_OPTION_ACCESSOR(int, precision); IMPLEMENT_SASS_OPTION_ACCESSOR(enum Sass_Output_Style, output_style); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url); IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_importers); IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); // Create getter and setters for context IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_json); 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(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); // Take ownership of memory (value on context is set to 0) IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_json); 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*, output_string); IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string); IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files); // Push function for include paths (no manipulation support for now) void ADDCALL sass_option_push_include_path(struct Sass_Options* options, const char* path) { struct string_list* include_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (include_path == 0) return; include_path->string = path ? sass_strdup(path) : 0; struct string_list* last = options->include_paths; if (!options->include_paths) { options->include_paths = include_path; } else { while (last->next) last = last->next; last->next = include_path; } } // Push function for plugin paths (no manipulation support for now) void ADDCALL sass_option_push_plugin_path(struct Sass_Options* options, const char* path) { struct string_list* plugin_path = (struct string_list*) calloc(1, sizeof(struct string_list)); if (plugin_path == 0) return; plugin_path->string = path ? sass_strdup(path) : 0; struct string_list* last = options->plugin_paths; if (!options->plugin_paths) { options->plugin_paths = plugin_path; } else { while (last->next) last = last->next; last->next = plugin_path; } } } libsass-3.3.4/src/sass_context.hpp000066400000000000000000000052701267254216700172160ustar00rootroot00000000000000#ifndef SASS_SASS_CONTEXT_H #define SASS_SASS_CONTEXT_H #include "sass.h" #include "sass.hpp" #include "context.hpp" #include "ast_fwd_decl.hpp" // sass config options structure struct Sass_Options : Sass_Output_Options { // embed sourceMappingUrl as data uri bool source_map_embed; // embed include contents in maps bool source_map_contents; // Disable sourceMappingUrl in css output bool omit_source_map_url; // Treat source_string as sass (as opposed to scss) bool is_indented_syntax_src; // The input path is used for source map // generation. It can be used to define // something with string compilation or to // overload the input file path. It is // set to "stdin" for data contexts and // to the input file on file contexts. char* input_path; // The output path is used for source map // generation. Libsass will not write to // this file, it is just used to create // information in source-maps etc. char* output_path; // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead? char* include_path; char* plugin_path; // Include paths (linked string list) struct string_list* include_paths; // Plugin paths (linked string list) struct string_list* plugin_paths; // Path to source map file // Enables source map generation // Used to create sourceMappingUrl char* source_map_file; // Directly inserted in source maps char* source_map_root; // Custom functions that can be called from sccs code Sass_Function_List c_functions; // List of custom importers Sass_Importer_List c_importers; // List of custom headers Sass_Importer_List c_headers; }; // base for all contexts struct Sass_Context : Sass_Options { // store context type info enum Sass_Input_Style type; // generated output data char* output_string; // generated source map json char* source_map_string; // error status int error_status; char* error_json; char* error_text; char* error_message; // error position char* error_file; size_t error_line; size_t error_column; const char* error_src; // report imported files char** included_files; }; // struct for file compilation struct Sass_File_Context : Sass_Context { // no additional fields required // input_path is already on options }; // struct for data compilation struct Sass_Data_Context : Sass_Context { // provided source string char* source_string; char* srcmap_string; }; // link c and cpp context struct Sass_Compiler { // progress status Sass_Compiler_State state; // original c context Sass_Context* c_ctx; // Sass::Context Sass::Context* cpp_ctx; // Sass::Block Sass::Block* root; }; #endiflibsass-3.3.4/src/sass_functions.cpp000066400000000000000000000134531267254216700175370ustar00rootroot00000000000000#include "sass.hpp" #include #include "util.hpp" #include "context.hpp" #include "sass/functions.h" #include "sass_functions.hpp" extern "C" { using namespace Sass; Sass_Function_List ADDCALL sass_make_function_list(size_t length) { return (Sass_Function_List) calloc(length + 1, sizeof(Sass_Function_Entry)); } Sass_Function_Entry ADDCALL sass_make_function(const char* signature, Sass_Function_Fn function, void* cookie) { Sass_Function_Entry cb = (Sass_Function_Entry) calloc(1, sizeof(Sass_Function)); if (cb == 0) return 0; cb->signature = signature; cb->function = function; cb->cookie = cookie; return cb; } // Setters and getters for callbacks on function lists Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos) { return list[pos]; } void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb) { list[pos] = cb; } const char* ADDCALL sass_function_get_signature(Sass_Function_Entry cb) { return cb->signature; } Sass_Function_Fn ADDCALL sass_function_get_function(Sass_Function_Entry cb) { return cb->function; } void* ADDCALL sass_function_get_cookie(Sass_Function_Entry cb) { return cb->cookie; } Sass_Importer_Entry ADDCALL sass_make_importer(Sass_Importer_Fn importer, double priority, void* cookie) { Sass_Importer_Entry cb = (Sass_Importer_Entry) calloc(1, sizeof(Sass_Importer)); if (cb == 0) return 0; cb->importer = importer; cb->priority = priority; cb->cookie = cookie; return cb; } Sass_Importer_Fn ADDCALL sass_importer_get_function(Sass_Importer_Entry cb) { return cb->importer; } double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb) { return cb->priority; } void* ADDCALL sass_importer_get_cookie(Sass_Importer_Entry cb) { return cb->cookie; } // Just in case we have some stray import structs void ADDCALL sass_delete_importer (Sass_Importer_Entry cb) { free(cb); } // Creator for sass custom importer function list Sass_Importer_List ADDCALL sass_make_importer_list(size_t length) { return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry)); } Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; } void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; } // Creator for sass custom importer return argument list Sass_Import_List ADDCALL sass_make_import_list(size_t length) { return (Sass_Import**) calloc(length + 1, sizeof(Sass_Import*)); } // 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) 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)); if (v == 0) return 0; v->imp_path = imp_path ? sass_strdup(imp_path) : 0; v->abs_path = abs_path ? sass_strdup(abs_path) : 0; v->source = source; v->srcmap = srcmap; v->error = 0; v->line = -1; v->column = -1; return v; } // Older style, but somehow still valid - keep around or deprecate? Sass_Import_Entry ADDCALL sass_make_import_entry(const char* path, char* source, char* srcmap) { return sass_make_import(path, path, source, srcmap); } // Upgrade a normal import entry to throw an error (original path can be re-used by error reporting) Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* error, size_t line, size_t col) { if (import == 0) return 0; if (import->error) free(import->error); import->error = error ? sass_strdup(error) : 0; import->line = line ? line : -1; import->column = col ? col : -1; return import; } // Setters and getters for entries on the import list void ADDCALL sass_import_set_list_entry(Sass_Import_List list, size_t idx, Sass_Import_Entry entry) { list[idx] = entry; } Sass_Import_Entry ADDCALL sass_import_get_list_entry(Sass_Import_List list, size_t idx) { return list[idx]; } // Deallocator for the allocated memory void ADDCALL sass_delete_import_list(Sass_Import_List list) { Sass_Import_List it = list; if (list == 0) return; while(*list) { sass_delete_import(*list); ++list; } free(it); } // Just in case we have some stray import structs void ADDCALL sass_delete_import(Sass_Import_Entry import) { free(import->imp_path); free(import->abs_path); free(import->source); free(import->srcmap); free(import->error); free(import); } // Getter for import entry const char* ADDCALL sass_import_get_imp_path(Sass_Import_Entry entry) { return entry->imp_path; } const char* ADDCALL sass_import_get_abs_path(Sass_Import_Entry entry) { return entry->abs_path; } const char* ADDCALL sass_import_get_source(Sass_Import_Entry entry) { return entry->source; } const char* ADDCALL sass_import_get_srcmap(Sass_Import_Entry entry) { return entry->srcmap; } // Getter for import error entry size_t ADDCALL sass_import_get_error_line(Sass_Import_Entry entry) { return entry->line; } size_t ADDCALL sass_import_get_error_column(Sass_Import_Entry entry) { return entry->column; } const char* ADDCALL sass_import_get_error_message(Sass_Import_Entry entry) { return entry->error; } // Explicit functions to take ownership of the memory // Resets our own property since we do not know if it is still alive char* ADDCALL sass_import_take_source(Sass_Import_Entry entry) { char* ptr = entry->source; entry->source = 0; return ptr; } char* ADDCALL sass_import_take_srcmap(Sass_Import_Entry entry) { char* ptr = entry->srcmap; entry->srcmap = 0; return ptr; } } libsass-3.3.4/src/sass_functions.hpp000066400000000000000000000012171267254216700175370ustar00rootroot00000000000000#ifndef SASS_SASS_FUNCTIONS_H #define SASS_SASS_FUNCTIONS_H #include "sass.h" // Struct to hold custom function callback struct Sass_Function { const char* signature; Sass_Function_Fn function; void* cookie; }; // External import entry struct Sass_Import { char* imp_path; // path as found in the import statement char *abs_path; // path after importer has resolved it char* source; char* srcmap; // error handling char* error; size_t line; size_t column; }; // Struct to hold importer callback struct Sass_Importer { Sass_Importer_Fn importer; double priority; void* cookie; }; #endiflibsass-3.3.4/src/sass_interface.cpp000066400000000000000000000156731267254216700174750ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include "util.hpp" #include "context.hpp" #include "inspect.hpp" #include "error_handling.hpp" #include "sass/base.h" #include "sass/interface.h" #define LFEED "\n" extern "C" { sass_context* sass_new_context() { return (sass_context*) calloc(1, sizeof(sass_context)); } void sass_free_context(sass_context* ctx) { if (ctx->output_string) free(ctx->output_string); if (ctx->source_map_string) free(ctx->source_map_string); if (ctx->error_message) free(ctx->error_message); if (ctx->c_functions) free(ctx->c_functions); Sass::free_string_array(ctx->included_files); free(ctx); } sass_file_context* sass_new_file_context() { return (sass_file_context*) calloc(1, sizeof(sass_file_context)); } void sass_free_file_context(sass_file_context* ctx) { if (ctx->output_string) free(ctx->output_string); if (ctx->source_map_string) free(ctx->source_map_string); if (ctx->error_message) free(ctx->error_message); if (ctx->c_functions) free(ctx->c_functions); Sass::free_string_array(ctx->included_files); free(ctx); } sass_folder_context* sass_new_folder_context() { return (sass_folder_context*) calloc(1, sizeof(sass_folder_context)); } void sass_free_folder_context(sass_folder_context* ctx) { Sass::free_string_array(ctx->included_files); free(ctx); } int sass_compile(sass_context* c_ctx) { using namespace Sass; try { std::string input_path = safe_str(c_ctx->input_path); int lastindex = static_cast(input_path.find_last_of(".")); std::string output_path; if (!c_ctx->output_path) { if (input_path != "") { output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; } } else { output_path = c_ctx->output_path; } struct Sass_Data_Context opt; Data_Context cpp_ctx(opt); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; while ((this_func_data) && (*this_func_data)) { cpp_ctx.c_functions.push_back(*this_func_data); ++this_func_data; } } Block* root = cpp_ctx.parse(); c_ctx->output_string = cpp_ctx.render(root); c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; if (copy_strings(cpp_ctx.get_included_files(true), &c_ctx->included_files, 1) == NULL) throw(std::bad_alloc()); } catch (Exception::InvalidSass& e) { std::stringstream msg_stream; msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch(std::bad_alloc& ba) { std::stringstream msg_stream; msg_stream << "Unable to allocate memory: " << ba.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (std::exception& e) { std::stringstream msg_stream; msg_stream << "Error: " << e.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (std::string& e) { std::stringstream msg_stream; msg_stream << "Error: " << e << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (...) { // couldn't find the specified file in the include paths; report an error std::stringstream msg_stream; msg_stream << "Unknown error occurred" << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } return 0; } int sass_compile_file(sass_file_context* c_ctx) { using namespace Sass; try { std::string input_path = safe_str(c_ctx->input_path); int lastindex = static_cast(input_path.find_last_of(".")); std::string output_path; if (!c_ctx->output_path) { output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; } else { output_path = c_ctx->output_path; } struct Sass_File_Context opt; File_Context cpp_ctx(opt); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; while ((this_func_data) && (*this_func_data)) { cpp_ctx.c_functions.push_back(*this_func_data); ++this_func_data; } } Block* root = cpp_ctx.parse(); c_ctx->output_string = cpp_ctx.render(root); c_ctx->source_map_string = cpp_ctx.render_srcmap(); c_ctx->error_message = 0; c_ctx->error_status = 0; if (copy_strings(cpp_ctx.get_included_files(false), &c_ctx->included_files) == NULL) throw(std::bad_alloc()); } catch (Exception::InvalidSass& e) { std::stringstream msg_stream; msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch(std::bad_alloc& ba) { std::stringstream msg_stream; msg_stream << "Unable to allocate memory: " << ba.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (std::exception& e) { std::stringstream msg_stream; msg_stream << "Error: " << e.what() << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (std::string& e) { std::stringstream msg_stream; msg_stream << "Error: " << e << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } catch (...) { // couldn't find the specified file in the include paths; report an error std::stringstream msg_stream; msg_stream << "Unknown error occurred" << std::endl; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; c_ctx->source_map_string = 0; } return 0; } int sass_compile_folder(sass_folder_context* c_ctx) { return 1; } } libsass-3.3.4/src/sass_util.cpp000066400000000000000000000100601267254216700164730ustar00rootroot00000000000000#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>] # @return [NodeCollection>] # # @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, Context& ctx) { 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, Context& ctx, 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, ctx, 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-3.3.4/src/sass_util.hpp000066400000000000000000000167571267254216700165230ustar00rootroot00000000000000#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>] # @return [NodeCollection>] # # @example # paths([[1, 2], [3, 4], [5]]) #=> # # [[1, 3, 5], # # [2, 3, 5], # # [1, 4, 5], # # [2, 4, 5]] */ Node paths(const Node& arrs, Context& ctx); /* 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 > 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 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 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(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 Node lcs(Node& x, Node& y, const ComparatorType& comparator, Context& ctx) { 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(newX.collection()->size()) - 1, static_cast(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, Context& ctx, 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 void group_by_to_a(std::vector& enumeration, KeyFunctorType& keyFunc, std::vector > >& arr /*out*/) { std::map order; std::map > grouped; for (typename std::vector::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 newCollection; newCollection.push_back(e); grouped.insert(std::make_pair(key.hash(), newCollection)); } else { std::vector& 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& values = grouped.at(key.hash()); std::pair > grouping = std::make_pair(key, values); arr.push_back(grouping); } } } #endif libsass-3.3.4/src/sass_values.cpp000066400000000000000000000342241267254216700170250ustar00rootroot00000000000000#include "sass.hpp" #include #include #include "util.hpp" #include "eval.hpp" #include "values.hpp" #include "sass/values.h" #include "sass_values.hpp" extern "C" { using namespace Sass; // Return the sass tag for a generic sass value enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; } // Check value for specified type bool ADDCALL sass_value_is_null(const union Sass_Value* v) { return v->unknown.tag == SASS_NULL; } bool ADDCALL sass_value_is_number(const union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; } bool ADDCALL sass_value_is_string(const union Sass_Value* v) { return v->unknown.tag == SASS_STRING; } bool ADDCALL sass_value_is_boolean(const union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; } bool ADDCALL sass_value_is_color(const union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; } bool ADDCALL sass_value_is_list(const union Sass_Value* v) { return v->unknown.tag == SASS_LIST; } bool ADDCALL sass_value_is_map(const union Sass_Value* v) { return v->unknown.tag == SASS_MAP; } bool ADDCALL sass_value_is_error(const union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; } bool ADDCALL sass_value_is_warning(const union Sass_Value* v) { return v->unknown.tag == SASS_WARNING; } // Getters and setters for Sass_Number double ADDCALL sass_number_get_value(const union Sass_Value* v) { return v->number.value; } void ADDCALL sass_number_set_value(union Sass_Value* v, double value) { v->number.value = value; } const char* ADDCALL sass_number_get_unit(const union Sass_Value* v) { return v->number.unit; } void ADDCALL sass_number_set_unit(union Sass_Value* v, char* unit) { v->number.unit = unit; } // Getters and setters for Sass_String const char* ADDCALL sass_string_get_value(const union Sass_Value* v) { return v->string.value; } void ADDCALL sass_string_set_value(union Sass_Value* v, char* value) { v->string.value = value; } bool ADDCALL sass_string_is_quoted(const union Sass_Value* v) { return v->string.quoted; } void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted) { v->string.quoted = quoted; } // Getters and setters for Sass_Boolean bool ADDCALL sass_boolean_get_value(const union Sass_Value* v) { return v->boolean.value; } void ADDCALL sass_boolean_set_value(union Sass_Value* v, bool value) { v->boolean.value = value; } // Getters and setters for Sass_Color double ADDCALL sass_color_get_r(const union Sass_Value* v) { return v->color.r; } void ADDCALL sass_color_set_r(union Sass_Value* v, double r) { v->color.r = r; } double ADDCALL sass_color_get_g(const union Sass_Value* v) { return v->color.g; } void ADDCALL sass_color_set_g(union Sass_Value* v, double g) { v->color.g = g; } double ADDCALL sass_color_get_b(const union Sass_Value* v) { return v->color.b; } void ADDCALL sass_color_set_b(union Sass_Value* v, double b) { v->color.b = b; } double ADDCALL sass_color_get_a(const union Sass_Value* v) { return v->color.a; } void ADDCALL sass_color_set_a(union Sass_Value* v, double a) { v->color.a = a; } // Getters and setters for Sass_List size_t ADDCALL sass_list_get_length(const union Sass_Value* v) { return v->list.length; } enum Sass_Separator ADDCALL sass_list_get_separator(const union Sass_Value* v) { return v->list.separator; } void ADDCALL sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; } // Getters and setters for Sass_List values union Sass_Value* ADDCALL sass_list_get_value(const union Sass_Value* v, size_t i) { return v->list.values[i]; } void ADDCALL sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; } // Getters and setters for Sass_Map size_t ADDCALL sass_map_get_length(const union Sass_Value* v) { return v->map.length; } // Getters and setters for Sass_List keys and values union Sass_Value* ADDCALL sass_map_get_key(const union Sass_Value* v, size_t i) { return v->map.pairs[i].key; } union Sass_Value* ADDCALL sass_map_get_value(const union Sass_Value* v, size_t i) { return v->map.pairs[i].value; } void ADDCALL sass_map_set_key(union Sass_Value* v, size_t i, union Sass_Value* key) { v->map.pairs[i].key = key; } void ADDCALL sass_map_set_value(union Sass_Value* v, size_t i, union Sass_Value* val) { v->map.pairs[i].value = val; } // Getters and setters for Sass_Error char* ADDCALL sass_error_get_message(const union Sass_Value* v) { return v->error.message; }; void ADDCALL sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; }; // Getters and setters for Sass_Warning char* ADDCALL sass_warning_get_message(const union Sass_Value* v) { return v->warning.message; }; void ADDCALL sass_warning_set_message(union Sass_Value* v, char* msg) { v->warning.message = msg; }; // Creator functions for all value types union Sass_Value* ADDCALL sass_make_boolean(bool val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->boolean.tag = SASS_BOOLEAN; v->boolean.value = val; return v; } union Sass_Value* ADDCALL sass_make_number(double val, const char* unit) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->number.tag = SASS_NUMBER; v->number.value = val; v->number.unit = unit ? sass_strdup(unit) : 0; if (v->number.unit == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_color(double r, double g, double b, double a) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->color.tag = SASS_COLOR; v->color.r = r; v->color.g = g; v->color.b = b; v->color.a = a; return v; } union Sass_Value* ADDCALL sass_make_string(const char* val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->string.quoted = false; v->string.tag = SASS_STRING; v->string.value = val ? sass_strdup(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_qstring(const char* val) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->string.quoted = true; v->string.tag = SASS_STRING; v->string.value = val ? sass_strdup(val) : 0; if (v->string.value == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->list.tag = SASS_LIST; v->list.length = len; v->list.separator = sep; v->list.values = (union Sass_Value**) calloc(len, sizeof(union Sass_Value*)); if (v->list.values == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_map(size_t len) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->map.tag = SASS_MAP; v->map.length = len; v->map.pairs = (struct Sass_MapPair*) calloc(len, sizeof(struct Sass_MapPair)); if (v->map.pairs == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_null(void) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->null.tag = SASS_NULL; return v; } union Sass_Value* ADDCALL sass_make_error(const char* msg) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->error.tag = SASS_ERROR; v->error.message = msg ? sass_strdup(msg) : 0; if (v->error.message == 0) { free(v); return 0; } return v; } union Sass_Value* ADDCALL sass_make_warning(const char* msg) { union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value)); if (v == 0) return 0; v->warning.tag = SASS_WARNING; v->warning.message = msg ? sass_strdup(msg) : 0; if (v->warning.message == 0) { free(v); return 0; } return v; } // will free all associated sass values void ADDCALL sass_delete_value(union Sass_Value* val) { size_t i; if (val == 0) return; switch(val->unknown.tag) { case SASS_NULL: { } break; case SASS_BOOLEAN: { } break; case SASS_NUMBER: { free(val->number.unit); } break; case SASS_COLOR: { } break; case SASS_STRING: { free(val->string.value); } break; case SASS_LIST: { for (i=0; ilist.length; i++) { sass_delete_value(val->list.values[i]); } free(val->list.values); } break; case SASS_MAP: { for (i=0; imap.length; i++) { sass_delete_value(val->map.pairs[i].key); sass_delete_value(val->map.pairs[i].value); } free(val->map.pairs); } break; case SASS_ERROR: { free(val->error.message); } break; case SASS_WARNING: { free(val->error.message); } break; } free(val); } // Make a deep cloned copy of the given sass value union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val) { size_t i; if (val == 0) return 0; switch(val->unknown.tag) { case SASS_NULL: { return sass_make_null(); } break; case SASS_BOOLEAN: { return sass_make_boolean(val->boolean.value); } break; case SASS_NUMBER: { return sass_make_number(val->number.value, val->number.unit); } break; case SASS_COLOR: { return sass_make_color(val->color.r, val->color.g, val->color.b, val->color.a); } break; case SASS_STRING: { return sass_string_is_quoted(val) ? sass_make_qstring(val->string.value) : sass_make_string(val->string.value); } break; case SASS_LIST: { union Sass_Value* list = sass_make_list(val->list.length, val->list.separator); for (i = 0; i < list->list.length; i++) { list->list.values[i] = sass_clone_value(val->list.values[i]); } return list; } break; case SASS_MAP: { union Sass_Value* map = sass_make_map(val->map.length); for (i = 0; i < val->map.length; i++) { map->map.pairs[i].key = sass_clone_value(val->map.pairs[i].key); map->map.pairs[i].value = sass_clone_value(val->map.pairs[i].value); } return map; } break; case SASS_ERROR: { return sass_make_error(val->error.message); } break; case SASS_WARNING: { return sass_make_warning(val->warning.message); } break; } return 0; } union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision) { Memory_Manager mem; Value* val = sass_value_to_ast_node(mem, v); Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision); std::string str(val->to_string(options)); return sass_make_qstring(str.c_str()); } union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b) { Sass::Value* rv = 0; Memory_Manager mem; try { Value* lhs = sass_value_to_ast_node(mem, a); Value* rhs = sass_value_to_ast_node(mem, b); struct Sass_Inspect_Options options(NESTED, 5); // see if it's a relational expression switch(op) { case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs)); case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs)); case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs, "gt") && !Eval::eq(lhs, rhs)); case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs, "gte")); case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs, "lt")); case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs, "lte") || Eval::eq(lhs, rhs)); default: break; } if (sass_value_is_number(a) && sass_value_is_number(b)) { const Number* l_n = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); rv = Eval::op_numbers(mem, op, *l_n, *r_n, options); } else if (sass_value_is_number(a) && sass_value_is_color(a)) { const Number* l_n = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); rv = Eval::op_number_color(mem, op, *l_n, *r_c, options); } else if (sass_value_is_color(a) && sass_value_is_number(b)) { const Color* l_c = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); rv = Eval::op_color_number(mem, op, *l_c, *r_n, options); } else if (sass_value_is_color(a) && sass_value_is_color(b)) { const Color* l_c = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); rv = Eval::op_colors(mem, op, *l_c, *r_c, options); } else /* convert other stuff to string and apply operation */ { Value* l_v = dynamic_cast(lhs); Value* r_v = dynamic_cast(rhs); rv = Eval::op_strings(mem, op, *l_v, *r_v, options); } // ToDo: maybe we should should return null value? if (!rv) return sass_make_error("invalid return value"); // convert result back to ast node return ast_node_to_sass_value(rv); } // simply pass the error message back to the caller for now catch (Exception::InvalidSass& e) { return sass_make_error(e.what()); } catch (std::bad_alloc&) { return sass_make_error("memory exhausted"); } catch (std::exception& e) { return sass_make_error(e.what()); } catch (std::string& e) { return sass_make_error(e.c_str()); } catch (const char* e) { return sass_make_error(e); } catch (...) { return sass_make_error("unknown"); } return 0; } } libsass-3.3.4/src/sass_values.hpp000066400000000000000000000025261267254216700170320ustar00rootroot00000000000000#ifndef SASS_SASS_VALUES_H #define SASS_SASS_VALUES_H #include "sass.h" struct Sass_Unknown { enum Sass_Tag tag; }; struct Sass_Boolean { enum Sass_Tag tag; bool value; }; struct Sass_Number { enum Sass_Tag tag; double value; char* unit; }; struct Sass_Color { enum Sass_Tag tag; double r; double g; double b; double a; }; struct Sass_String { enum Sass_Tag tag; bool quoted; char* value; }; struct Sass_List { enum Sass_Tag tag; enum Sass_Separator separator; size_t length; // null terminated "array" union Sass_Value** values; }; struct Sass_Map { enum Sass_Tag tag; size_t length; struct Sass_MapPair* pairs; }; struct Sass_Null { enum Sass_Tag tag; }; struct Sass_Error { enum Sass_Tag tag; char* message; }; struct Sass_Warning { enum Sass_Tag tag; char* message; }; union Sass_Value { struct Sass_Unknown unknown; struct Sass_Boolean boolean; struct Sass_Number number; struct Sass_Color color; struct Sass_String string; struct Sass_List list; struct Sass_Map map; struct Sass_Null null; struct Sass_Error error; struct Sass_Warning warning; }; struct Sass_MapPair { union Sass_Value* key; union Sass_Value* value; }; #endiflibsass-3.3.4/src/source_map.cpp000066400000000000000000000145071267254216700166340ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include #include "ast.hpp" #include "json.hpp" #include "context.hpp" #include "position.hpp" #include "source_map.hpp" namespace Sass { SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { } SourceMap::SourceMap(const std::string& file) : current_position(0, 0, 0), file(file) { } std::string SourceMap::render_srcmap(Context &ctx) { const bool include_sources = ctx.c_options.source_map_contents; const std::vector links = ctx.srcmap_links; const std::vector& sources(ctx.resources); JsonNode* json_srcmap = json_mkobject(); json_append_member(json_srcmap, "version", json_mknumber(3)); // pass-through sourceRoot option if (!ctx.source_map_root.empty()) { JsonNode* root = json_mkstring(ctx.source_map_root.c_str()); json_append_member(json_srcmap, "sourceRoot", root); } const char *include = file.c_str(); JsonNode *json_include = json_mkstring(include); json_append_member(json_srcmap, "file", json_include); JsonNode *json_includes = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { const char *include = links[source_index[i]].c_str(); JsonNode *json_include = json_mkstring(include); json_append_element(json_includes, json_include); } json_append_member(json_srcmap, "sources", json_includes); if (include_sources) { JsonNode *json_contents = json_mkarray(); for (size_t i = 0; i < source_index.size(); ++i) { const Resource& resource(sources[source_index[i]]); JsonNode *json_content = json_mkstring(resource.contents); json_append_element(json_contents, json_content); } if (json_contents->children.head) json_append_member(json_srcmap, "sourcesContent", json_contents); } std::string mappings = serialize_mappings(); JsonNode *json_mappings = json_mkstring(mappings.c_str()); json_append_member(json_srcmap, "mappings", json_mappings); JsonNode *json_names = json_mkarray(); // so far we have no implementation for names // no problem as we do not alter any identifiers json_append_member(json_srcmap, "names", json_names); char *str = json_stringify(json_srcmap, "\t"); std::string result = std::string(str); free(str); json_delete(json_srcmap); return result; } std::string SourceMap::serialize_mappings() { std::string result = ""; size_t previous_generated_line = 0; size_t previous_generated_column = 0; size_t previous_original_line = 0; size_t previous_original_column = 0; size_t previous_original_file = 0; for (size_t i = 0; i < mappings.size(); ++i) { const size_t generated_line = mappings[i].generated_position.line; const size_t generated_column = mappings[i].generated_position.column; const size_t original_line = mappings[i].original_position.line; const size_t original_column = mappings[i].original_position.column; const size_t original_file = mappings[i].original_position.file; if (generated_line != previous_generated_line) { previous_generated_column = 0; if (generated_line > previous_generated_line) { result += std::string(generated_line - previous_generated_line, ';'); previous_generated_line = generated_line; } } else if (i > 0) { result += ","; } // generated column result += base64vlq.encode(static_cast(generated_column) - static_cast(previous_generated_column)); previous_generated_column = generated_column; // file result += base64vlq.encode(static_cast(original_file) - static_cast(previous_original_file)); previous_original_file = original_file; // source line result += base64vlq.encode(static_cast(original_line) - static_cast(previous_original_line)); previous_original_line = original_line; // source column result += base64vlq.encode(static_cast(original_column) - static_cast(previous_original_column)); previous_original_column = original_column; } return result; } void SourceMap::prepend(const OutputBuffer& out) { Offset size(out.smap.current_position); for (Mapping mapping : out.smap.mappings) { if (mapping.generated_position.line > size.line) { throw(std::runtime_error("prepend sourcemap has illegal line")); } if (mapping.generated_position.line == size.line) { if (mapping.generated_position.column > size.column) { throw(std::runtime_error("prepend sourcemap has illegal column")); } } } // will adjust the offset prepend(Offset(out.buffer)); // now add the new mappings VECTOR_UNSHIFT(mappings, out.smap.mappings); } void SourceMap::append(const OutputBuffer& out) { append(Offset(out.buffer)); } void SourceMap::prepend(const Offset& offset) { if (offset.line != 0 || offset.column != 0) { for (Mapping& mapping : mappings) { // move stuff on the first old line if (mapping.generated_position.line == 0) { mapping.generated_position.column += offset.column; } // make place for the new lines mapping.generated_position.line += offset.line; } } if (current_position.line == 0) { current_position.column += offset.column; } current_position.line += offset.line; } void SourceMap::append(const Offset& offset) { current_position += offset; } 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* node) { mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position)); } ParserState SourceMap::remap(const ParserState& pstate) { for (size_t i = 0; i < mappings.size(); ++i) { if ( mappings[i].generated_position.file == pstate.file && mappings[i].generated_position.line == pstate.line && mappings[i].generated_position.column == pstate.column ) return ParserState(pstate.path, pstate.src, mappings[i].original_position, pstate.offset); } return ParserState(pstate.path, pstate.src, Position(-1, -1, -1), Offset(0, 0)); } } libsass-3.3.4/src/source_map.hpp000066400000000000000000000023561267254216700166400ustar00rootroot00000000000000#ifndef SASS_SOURCE_MAP_H #define SASS_SOURCE_MAP_H #include #include #include "ast_fwd_decl.hpp" #include "base64vlq.hpp" #include "position.hpp" #include "mapping.hpp" #define VECTOR_PUSH(vec, ins) vec.insert(vec.end(), ins.begin(), ins.end()) #define VECTOR_UNSHIFT(vec, ins) vec.insert(vec.begin(), ins.begin(), ins.end()) namespace Sass { class Context; class OutputBuffer; class SourceMap { public: std::vector source_index; SourceMap(); SourceMap(const std::string& file); void append(const Offset& offset); void prepend(const Offset& offset); void append(const OutputBuffer& out); void prepend(const OutputBuffer& out); 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); private: std::string serialize_mappings(); std::vector mappings; Position current_position; public: std::string file; private: Base64VLQ base64vlq; }; class OutputBuffer { public: OutputBuffer(void) : buffer(""), smap() { } public: std::string buffer; SourceMap smap; }; } #endif libsass-3.3.4/src/subset_map.hpp000066400000000000000000000102601267254216700166360ustar00rootroot00000000000000#ifndef SASS_SUBSET_MAP_H #define SASS_SUBSET_MAP_H #include #include #include #include #include // #include // #include // template // std::string vector_to_string(std::vector 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 // std::string set_to_string(set v) // { // std::stringstream buffer; // buffer << "["; // typename std::set::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 { template struct triple { F first; S second; T third; triple(const F& f, const S& s, const T& t) : first(f), second(s), third(t) { } }; template triple make_triple(const F& f, const S& s, const T& t) { return triple(f, s, t); } template class Subset_Map { private: std::vector values_; std::map, std::set, size_t> > > hash_; public: void put(const std::vector& s, const V& value); std::vector > > get_kv(const std::vector& s); std::vector get_v(const std::vector& s); bool empty() { return values_.empty(); } void clear() { values_.clear(); hash_.clear(); } const std::vector values(void) { return values_; } }; template void Subset_Map::put(const std::vector& s, const V& value) { if (s.empty()) throw "internal error: subset map keys may not be empty"; size_t index = values_.size(); values_.push_back(value); std::set ss; for (size_t i = 0, S = s.size(); i < S; ++i) { ss.insert(s[i]); } for (size_t i = 0, S = s.size(); i < S; ++i) { hash_[s[i]].push_back(make_triple(s, ss, index)); } } template std::vector > > Subset_Map::get_kv(const std::vector& s) { std::vector sorted = s; sort(sorted.begin(), sorted.end()); std::vector > > indices; for (size_t i = 0, S = s.size(); i < S; ++i) { if (!hash_.count(s[i])) { continue; } std::vector, std::set, size_t> > subsets = hash_[s[i]]; // std::cerr << "length of subsets: " << subsets.size() << std::endl; for (size_t j = 0, T = subsets.size(); j < T; ++j) { if (!includes(sorted.begin(), sorted.end(), subsets[j].second.begin(), subsets[j].second.end())) { // std::cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << std::endl; continue; } indices.push_back(std::make_pair(subsets[j].third, subsets[j].first)); // std::cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << std::endl; } } sort(indices.begin(), indices.end()); typename std::vector > >::iterator indices_end = unique(indices.begin(), indices.end()); indices.resize(distance(indices.begin(), indices_end)); std::vector > > results; for (size_t i = 0, S = indices.size(); i < S; ++i) { results.push_back(std::make_pair(values_[indices[i].first], indices[i].second)); } return results; } template std::vector Subset_Map::get_v(const std::vector& s) { std::vector > > kvs = get_kv(s); std::vector results; for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first); return results; } } #endif libsass-3.3.4/src/support/000077500000000000000000000000001267254216700155005ustar00rootroot00000000000000libsass-3.3.4/src/support/libsass.pc.in000066400000000000000000000003751267254216700200760ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libsass URL: https://github.com/sass/libsass Description: A C implementation of a Sass compiler Version: @VERSION@ Libs: -L${libdir} -lsass Cflags: -I${includedir} libsass-3.3.4/src/to_c.cpp000066400000000000000000000040371267254216700154200ustar00rootroot00000000000000#include "sass.hpp" #include "to_c.hpp" #include "ast.hpp" namespace Sass { union Sass_Value* To_C::fallback_impl(AST_Node* n) { return sass_make_error("unknown type for C-API"); } union Sass_Value* To_C::operator()(Boolean* b) { return sass_make_boolean(b->value()); } union Sass_Value* To_C::operator()(Number* n) { return sass_make_number(n->value(), n->unit().c_str()); } union Sass_Value* To_C::operator()(Custom_Warning* w) { return sass_make_warning(w->message().c_str()); } union Sass_Value* To_C::operator()(Custom_Error* e) { return sass_make_error(e->message().c_str()); } union Sass_Value* To_C::operator()(Color* c) { return sass_make_color(c->r(), c->g(), c->b(), c->a()); } union Sass_Value* To_C::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* To_C::operator()(String_Quoted* s) { return sass_make_qstring(s->value().c_str()); } union Sass_Value* To_C::operator()(List* l) { union Sass_Value* v = sass_make_list(l->length(), l->separator()); 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* 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* a) { union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA); 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* a) { return a->value()->perform(this); } // not strictly necessary because of the fallback union Sass_Value* To_C::operator()(Null* n) { return sass_make_null(); } }; libsass-3.3.4/src/to_c.hpp000066400000000000000000000017741267254216700154320ustar00rootroot00000000000000#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 { // override this to define a catch-all union Sass_Value* fallback_impl(AST_Node* n); public: To_C() { } ~To_C() { } union Sass_Value* operator()(Boolean*); union Sass_Value* operator()(Number*); union Sass_Value* operator()(Color*); union Sass_Value* operator()(String_Constant*); union Sass_Value* operator()(String_Quoted*); union Sass_Value* operator()(Custom_Warning*); union Sass_Value* operator()(Custom_Error*); union Sass_Value* operator()(List*); union Sass_Value* operator()(Map*); union Sass_Value* operator()(Null*); union Sass_Value* operator()(Arguments*); union Sass_Value* operator()(Argument*); // dispatch to fallback implementation union Sass_Value* fallback(AST_Node* x) { return fallback_impl(x); } }; } #endif libsass-3.3.4/src/to_value.cpp000066400000000000000000000043661267254216700163170ustar00rootroot00000000000000#include "sass.hpp" #include "ast.hpp" #include "to_value.hpp" namespace Sass { Value* To_Value::fallback_impl(AST_Node* 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"); // mute warning return 0; } // Custom_Error is a valid value Value* To_Value::operator()(Custom_Error* e) { return e; } // Custom_Warning is a valid value Value* To_Value::operator()(Custom_Warning* w) { return w; } // Boolean is a valid value Value* To_Value::operator()(Boolean* b) { return b; } // Number is a valid value Value* To_Value::operator()(Number* n) { return n; } // Color is a valid value Value* To_Value::operator()(Color* c) { return c; } // String_Constant is a valid value Value* To_Value::operator()(String_Constant* s) { return s; } // String_Quoted is a valid value Value* To_Value::operator()(String_Quoted* s) { return s; } // List is a valid value Value* To_Value::operator()(List* l) { List* ll = SASS_MEMORY_NEW(mem, List, l->pstate(), l->length(), l->separator(), l->is_arglist()); for (size_t i = 0, L = l->length(); i < L; ++i) { *ll << (*l)[i]->perform(this); } return ll; } // Map is a valid value Value* To_Value::operator()(Map* m) { return m; } // Null is a valid value Value* To_Value::operator()(Null* n) { return n; } // Argument returns its value 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* To_Value::operator()(Selector_List* s) { return SASS_MEMORY_NEW(mem, String_Quoted, s->pstate(), s->to_string(ctx.c_options)); } // Binary_Expression is converted to a string Value* To_Value::operator()(Binary_Expression* s) { return SASS_MEMORY_NEW(mem, String_Quoted, s->pstate(), s->to_string(ctx.c_options)); } }; libsass-3.3.4/src/to_value.hpp000066400000000000000000000020751267254216700163170ustar00rootroot00000000000000#ifndef SASS_TO_VALUE_H #define SASS_TO_VALUE_H #include "operation.hpp" #include "sass/values.h" #include "ast_fwd_decl.hpp" namespace Sass { class To_Value : public Operation_CRTP { Value* fallback_impl(AST_Node* n); private: Context& ctx; Memory_Manager& mem; public: To_Value(Context& ctx, Memory_Manager& mem) : ctx(ctx), mem(mem) { } ~To_Value() { } using Operation::operator(); Value* operator()(Argument*); Value* operator()(Boolean*); Value* operator()(Number*); Value* operator()(Color*); Value* operator()(String_Constant*); Value* operator()(String_Quoted*); Value* operator()(Custom_Warning*); Value* operator()(Custom_Error*); Value* operator()(List*); Value* operator()(Map*); Value* operator()(Null*); // convert to string via `To_String` Value* operator()(Selector_List*); Value* operator()(Binary_Expression*); // fallback throws error template Value* fallback(U x) { return fallback_impl(x); } }; } #endif libsass-3.3.4/src/units.cpp000066400000000000000000000164331267254216700156410ustar00rootroot00000000000000#include "sass.hpp" #include #include "units.hpp" namespace Sass { /* the conversion matrix can be readed the following way */ /* if you go down, the factor is for the numerator (multiply) */ /* if you go right, the factor is for the denominator (divide) */ /* and yes, we actually use both, not sure why, but why not!? */ const double size_conversion_factors[6][6] = { /* in cm pc mm pt px */ /* in */ { 1, 2.54, 6, 25.4, 72, 96, }, /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54 }, /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0 }, /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4 }, /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0 }, /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1, } }; const double angle_conversion_factors[4][4] = { /* deg grad rad turn */ /* deg */ { 1, 40.0/36.0, PI/180.0, 1.0/360.0 }, /* grad */ { 36.0/40.0, 1, PI/200.0, 1.0/400.0 }, /* rad */ { 180.0/PI, 200.0/PI, 1, 0.5/PI }, /* turn */ { 360.0, 400.0, 2.0*PI, 1 } }; const double time_conversion_factors[2][2] = { /* s ms */ /* s */ { 1, 1000.0 }, /* ms */ { 1/1000.0, 1 } }; const double frequency_conversion_factors[2][2] = { /* Hz kHz */ /* Hz */ { 1, 1/1000.0 }, /* kHz */ { 1000.0, 1 } }; const double resolution_conversion_factors[3][3] = { /* dpi dpcm dppx */ /* dpi */ { 1, 2.54, 96 }, /* dpcm */ { 1/2.54, 1, 96/2.54 }, /* dppx */ { 1/96.0, 2.54/96, 1 } }; UnitClass get_unit_type(UnitType unit) { switch (unit & 0xFF00) { case UnitClass::LENGTH: return UnitClass::LENGTH; break; case UnitClass::ANGLE: return UnitClass::ANGLE; break; case UnitClass::TIME: return UnitClass::TIME; break; case UnitClass::FREQUENCY: return UnitClass::FREQUENCY; break; case UnitClass::RESOLUTION: return UnitClass::RESOLUTION; break; default: return UnitClass::INCOMMENSURABLE; break; } }; std::string get_unit_class(UnitType unit) { switch (unit & 0xFF00) { case UnitClass::LENGTH: return "LENGTH"; break; case UnitClass::ANGLE: return "ANGLE"; break; case UnitClass::TIME: return "TIME"; break; case UnitClass::FREQUENCY: return "FREQUENCY"; break; case UnitClass::RESOLUTION: return "RESOLUTION"; break; default: return "INCOMMENSURABLE"; break; } }; UnitType string_to_unit(const std::string& s) { // size units if (s == "px") return UnitType::PX; else if (s == "pt") return UnitType::PT; else if (s == "pc") return UnitType::PC; else if (s == "mm") return UnitType::MM; else if (s == "cm") return UnitType::CM; else if (s == "in") return UnitType::IN; // angle units else if (s == "deg") return UnitType::DEG; else if (s == "grad") return UnitType::GRAD; else if (s == "rad") return UnitType::RAD; else if (s == "turn") return UnitType::TURN; // time units else if (s == "s") return UnitType::SEC; else if (s == "ms") return UnitType::MSEC; // frequency units else if (s == "Hz") return UnitType::HERTZ; else if (s == "kHz") return UnitType::KHERTZ; // resolutions units else if (s == "dpi") return UnitType::DPI; else if (s == "dpcm") return UnitType::DPCM; else if (s == "dppx") return UnitType::DPPX; // for unknown units else return UnitType::UNKNOWN; } const char* unit_to_string(UnitType unit) { switch (unit) { // size units case UnitType::PX: return "px"; break; case UnitType::PT: return "pt"; break; case UnitType::PC: return "pc"; break; case UnitType::MM: return "mm"; break; case UnitType::CM: return "cm"; break; case UnitType::IN: return "in"; break; // angle units case UnitType::DEG: return "deg"; break; case UnitType::GRAD: return "grad"; break; case UnitType::RAD: return "rad"; break; case UnitType::TURN: return "turn"; break; // time units case UnitType::SEC: return "s"; break; case UnitType::MSEC: return "ms"; break; // frequency units case UnitType::HERTZ: return "Hz"; break; case UnitType::KHERTZ: return "kHz"; break; // resolutions units case UnitType::DPI: return "dpi"; break; case UnitType::DPCM: return "dpcm"; break; case UnitType::DPPX: return "dppx"; break; // for unknown units default: return ""; break; } } std::string unit_to_class(const std::string& s) { if (s == "px") return "LENGTH"; else if (s == "pt") return "LENGTH"; else if (s == "pc") return "LENGTH"; else if (s == "mm") return "LENGTH"; else if (s == "cm") return "LENGTH"; else if (s == "in") return "LENGTH"; // angle units else if (s == "deg") return "ANGLE"; else if (s == "grad") return "ANGLE"; else if (s == "rad") return "ANGLE"; else if (s == "turn") return "ANGLE"; // time units else if (s == "s") return "TIME"; else if (s == "ms") return "TIME"; // frequency units else if (s == "Hz") return "FREQUENCY"; else if (s == "kHz") return "FREQUENCY"; // resolutions units else if (s == "dpi") return "RESOLUTION"; else if (s == "dpcm") return "RESOLUTION"; else if (s == "dppx") return "RESOLUTION"; // for unknown units return "CUSTOM:" + s; } // throws incompatibleUnits exceptions double conversion_factor(const std::string& s1, const std::string& s2, bool strict) { // assert for same units if (s1 == s2) return 1; // get unit enum from string UnitType u1 = string_to_unit(s1); UnitType u2 = string_to_unit(s2); // query unit group types UnitClass t1 = get_unit_type(u1); UnitClass t2 = get_unit_type(u2); // get absolute offset // used for array acces size_t i1 = u1 - t1; size_t i2 = u2 - t2; // error if units are not of the same group // don't error for multiplication and division if (strict && t1 != t2) throw incompatibleUnits(u1, u2); // only process known units if (u1 != UNKNOWN && u2 != UNKNOWN) { switch (t1) { case UnitClass::LENGTH: return size_conversion_factors[i1][i2]; break; case UnitClass::ANGLE: return angle_conversion_factors[i1][i2]; break; case UnitClass::TIME: return time_conversion_factors[i1][i2]; break; case UnitClass::FREQUENCY: return frequency_conversion_factors[i1][i2]; break; case UnitClass::RESOLUTION: return resolution_conversion_factors[i1][i2]; break; // ToDo: should we throw error here? case UnitClass::INCOMMENSURABLE: return 0; break; } } // fallback return 0; } } libsass-3.3.4/src/units.hpp000066400000000000000000000035121267254216700156400ustar00rootroot00000000000000#ifndef SASS_UNITS_H #define SASS_UNITS_H #include #include #include namespace Sass { const double PI = std::acos(-1); enum UnitClass { LENGTH = 0x000, ANGLE = 0x100, TIME = 0x200, FREQUENCY = 0x300, RESOLUTION = 0x400, INCOMMENSURABLE = 0x500 }; enum UnitType { // size units IN = UnitClass::LENGTH, CM, PC, MM, PT, PX, // angle units DEG = ANGLE, GRAD, RAD, TURN, // time units SEC = TIME, MSEC, // frequency units HERTZ = FREQUENCY, KHERTZ, // resolutions units DPI = RESOLUTION, DPCM, DPPX, // for unknown units UNKNOWN = INCOMMENSURABLE }; extern const double size_conversion_factors[6][6]; extern const double angle_conversion_factors[4][4]; extern const double time_conversion_factors[2][2]; extern const double frequency_conversion_factors[2][2]; extern const double resolution_conversion_factors[3][3]; enum Sass::UnitType string_to_unit(const std::string&); const char* unit_to_string(Sass::UnitType unit); enum Sass::UnitClass get_unit_type(Sass::UnitType unit); std::string get_unit_class(Sass::UnitType unit); std::string unit_to_class(const std::string&); // throws incompatibleUnits exceptions double conversion_factor(const std::string&, const std::string&, bool = true); class incompatibleUnits: public std::exception { public: const char* msg; incompatibleUnits(Sass::UnitType a, Sass::UnitType b) : exception() { std::stringstream ss; ss << "Incompatible units: "; ss << "'" << unit_to_string(a) << "' and "; ss << "'" << unit_to_string(b) << "'"; msg = ss.str().c_str(); } virtual const char* what() const throw() { return msg; } }; } #endif libsass-3.3.4/src/utf8.h000066400000000000000000000027611267254216700150310ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "utf8/checked.h" #include "utf8/unchecked.h" #endif // header guard libsass-3.3.4/src/utf8/000077500000000000000000000000001267254216700146525ustar00rootroot00000000000000libsass-3.3.4/src/utf8/checked.h000066400000000000000000000276141267254216700164230ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" #include namespace utf8 { // Base for the exceptions that may be thrown from the library class exception : public ::std::exception { }; // Exceptions that may be thrown from the library functions. class invalid_code_point : public exception { uint32_t cp; public: invalid_code_point(uint32_t cp) : cp(cp) {} virtual const char* what() const throw() { return "Invalid code point"; } uint32_t code_point() const {return cp;} }; class invalid_utf8 : public exception { uint8_t u8; public: invalid_utf8 (uint8_t u) : u8(u) {} virtual const char* what() const throw() { return "Invalid UTF-8"; } uint8_t utf8_octet() const {return u8;} }; class invalid_utf16 : public exception { uint16_t u16; public: invalid_utf16 (uint16_t u) : u16(u) {} virtual const char* what() const throw() { return "Invalid UTF-16"; } uint16_t utf16_word() const {return u16;} }; class not_enough_room : public exception { public: virtual const char* what() const throw() { return "Not enough space"; } }; /// The library API - functions intended to be called by the users template octet_iterator append(uint32_t cp, octet_iterator result) { if (!utf8::internal::is_code_point_valid(cp)) throw invalid_code_point(cp); if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { octet_iterator sequence_start = start; internal::utf_error err_code = utf8::internal::validate_next(start, end); switch (err_code) { case internal::UTF8_OK : for (octet_iterator it = sequence_start; it != start; ++it) *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: throw not_enough_room(); case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: out = utf8::append (replacement, out); ++start; // just one replacement mark for the sequence while (start != end && utf8::internal::is_trail(*start)) ++start; break; } } return out; } template inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); return utf8::replace_invalid(start, end, out, replacement_marker); } template uint32_t next(octet_iterator& it, octet_iterator end) { uint32_t cp = 0; internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); switch (err_code) { case internal::UTF8_OK : break; case internal::NOT_ENOUGH_ROOM : throw not_enough_room(); case internal::INVALID_LEAD : case internal::INCOMPLETE_SEQUENCE : case internal::OVERLONG_SEQUENCE : throw invalid_utf8(*it); case internal::INVALID_CODE_POINT : throw invalid_code_point(cp); } return cp; } template uint32_t peek_next(octet_iterator it, octet_iterator end) { return utf8::next(it, end); } template uint32_t prior(octet_iterator& it, octet_iterator start) { // can't do much if it == start if (it == start) throw not_enough_room(); octet_iterator end = it; // Go back until we hit either a lead octet or start while (utf8::internal::is_trail(*(--it))) if (it == start) throw invalid_utf8(*it); // error - no lead byte in the sequence return utf8::peek_next(it, end); } /// Deprecated in versions that include "prior" template uint32_t previous(octet_iterator& it, octet_iterator pass_start) { octet_iterator end = it; while (utf8::internal::is_trail(*(--it))) if (it == pass_start) throw invalid_utf8(*it); // error - no lead byte in the sequence octet_iterator temp = it; return utf8::next(temp, end); } template void advance (octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i) utf8::next(it, end); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::next(first, last); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { if (start != end) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); if (utf8::internal::is_trail_surrogate(trail_surrogate)) cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; else throw invalid_utf16(static_cast(trail_surrogate)); } else throw invalid_utf16(static_cast(cp)); } // Lone trail surrogate else if (utf8::internal::is_trail_surrogate(cp)) throw invalid_utf16(static_cast(cp)); result = utf8::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start != end) { uint32_t cp = utf8::next(start, end); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start != end) (*result++) = utf8::next(start, end); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; 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) { if (it < range_start || it > range_end) throw std::out_of_range("Invalid utf-8 iterator position"); } // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::next(temp, range_end); } bool operator == (const iterator& rhs) const { if (range_start != rhs.range_start || range_end != rhs.range_end) throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { utf8::next(it, range_end); return *this; } iterator operator ++ (int) { iterator temp = *this; utf8::next(it, range_end); return temp; } iterator& operator -- () { utf8::prior(it, range_start); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::prior(it, range_start); return temp; } }; // class iterator } // namespace utf8 #endif //header guard libsass-3.3.4/src/utf8/core.h000066400000000000000000000246411267254216700157620ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers // You may need to change them to match your system. // These typedefs have the same names as ones from cstdint, or boost/cstdint typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; // Helper code - not intended to be directly called by the library users. May be changed at any time namespace internal { // Unicode constants // Leading (high) surrogates: 0xd800 - 0xdbff // Trailing (low) surrogates: 0xdc00 - 0xdfff const uint16_t LEAD_SURROGATE_MIN = 0xd800u; const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; template inline uint8_t mask8(octet_type oc) { return static_cast(0xff & oc); } template inline uint16_t mask16(u16_type oc) { return static_cast(0xffff & oc); } template inline bool is_trail(octet_type oc) { return ((utf8::internal::mask8(oc) >> 6) == 0x2); } template inline bool is_lead_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); } template inline bool is_trail_surrogate(u16 cp) { return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_surrogate(u16 cp) { return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); } template inline bool is_code_point_valid(u32 cp) { return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); } template inline typename std::iterator_traits::difference_type sequence_length(octet_iterator lead_it) { uint8_t lead = utf8::internal::mask8(*lead_it); if (lead < 0x80) return 1; else if ((lead >> 5) == 0x6) return 2; else if ((lead >> 4) == 0xe) return 3; else if ((lead >> 3) == 0x1e) return 4; else return 0; } template inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) { if (cp < 0x80) { if (length != 1) return true; } else if (cp < 0x800) { if (length != 2) return true; } else if (cp < 0x10000) { if (length != 3) return true; } return false; } enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; /// Helper for get_sequence_x template utf_error increase_safely(octet_iterator& it, octet_iterator end) { if (++it == end) return NOT_ENOUGH_ROOM; if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; return UTF8_OK; } #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} /// get_sequence_x functions decode utf-8 sequences of the length x template utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); return UTF8_OK; } template utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); return UTF8_OK; } template utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } template utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) { if (it == end) return NOT_ENOUGH_ROOM; code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) code_point += (*it) & 0x3f; return UTF8_OK; } #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR template utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) { // 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; uint32_t cp = 0; // Determine the sequence length based on the lead octet typedef typename std::iterator_traits::difference_type octet_difference_type; const octet_difference_type length = utf8::internal::sequence_length(it); // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); break; case 2: err = utf8::internal::get_sequence_2(it, end, cp); break; case 3: err = utf8::internal::get_sequence_3(it, end, cp); break; case 4: err = utf8::internal::get_sequence_4(it, end, cp); break; } if (err == UTF8_OK) { // Decoding succeeded. Now, security checks... if (utf8::internal::is_code_point_valid(cp)) { if (!utf8::internal::is_overlong_sequence(cp, length)){ // Passed! Return here. code_point = cp; ++it; return UTF8_OK; } else err = OVERLONG_SEQUENCE; } else err = INVALID_CODE_POINT; } // Failure branch - restore the original value of the iterator it = original_it; return err; } template inline utf_error validate_next(octet_iterator& it, octet_iterator end) { uint32_t ignored; return utf8::internal::validate_next(it, end, ignored); } } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark const uint8_t bom[] = {0xef, 0xbb, 0xbf}; template octet_iterator find_invalid(octet_iterator start, octet_iterator end) { octet_iterator result = start; while (result != end) { utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); if (err_code != internal::UTF8_OK) return result; } return result; } template inline bool is_valid(octet_iterator start, octet_iterator end) { return (utf8::find_invalid(start, end) == end); } template inline bool starts_with_bom (octet_iterator it, octet_iterator end) { return ( ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); } //Deprecated in release 2.3 template inline bool is_bom (octet_iterator it) { return ( (utf8::internal::mask8(*it++)) == bom[0] && (utf8::internal::mask8(*it++)) == bom[1] && (utf8::internal::mask8(*it)) == bom[2] ); } } // namespace utf8 #endif // header guard libsass-3.3.4/src/utf8/unchecked.h000066400000000000000000000213131267254216700167540ustar00rootroot00000000000000// Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 #include "core.h" namespace utf8 { namespace unchecked { template octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); } else if (cp < 0x10000) { // three octets *(result++) = static_cast((cp >> 12) | 0xe0); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } else { // four octets *(result++) = static_cast((cp >> 18) | 0xf0); *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); *(result++) = static_cast((cp & 0x3f) | 0x80); } return result; } template uint32_t next(octet_iterator& it) { uint32_t cp = utf8::internal::mask8(*it); typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); switch (length) { case 1: break; case 2: it++; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: ++it; cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); ++it; cp += (utf8::internal::mask8(*it) << 6) & 0xfff; ++it; cp += (*it) & 0x3f; break; } ++it; return cp; } template uint32_t peek_next(octet_iterator it) { return utf8::unchecked::next(it); } template uint32_t prior(octet_iterator& it) { while (utf8::internal::is_trail(*(--it))) ; octet_iterator temp = it; return utf8::unchecked::next(temp); } // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) template inline uint32_t previous(octet_iterator& it) { return utf8::unchecked::prior(it); } template void advance (octet_iterator& it, distance_type n) { for (distance_type i = 0; i < n; ++i) utf8::unchecked::next(it); } template typename std::iterator_traits::difference_type distance (octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) utf8::unchecked::next(first); return dist; } template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first if (utf8::internal::is_lead_surrogate(cp)) { uint32_t trail_surrogate = utf8::internal::mask16(*start++); cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = utf8::unchecked::append(cp, result); } return result; } template u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start < end) { uint32_t cp = utf8::unchecked::next(start); if (cp > 0xffff) { //make a surrogate pair *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); } else *result++ = static_cast(cp); } return result; } template octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) result = utf8::unchecked::append(*(start++), result); return result; } template u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) (*result++) = utf8::unchecked::next(start); return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; public: iterator () {} explicit iterator (const octet_iterator& octet_it): it(octet_it) {} // the default "big three" are OK octet_iterator base () const { return it; } uint32_t operator * () const { octet_iterator temp = it; return utf8::unchecked::next(temp); } bool operator == (const iterator& rhs) const { return (it == rhs.it); } bool operator != (const iterator& rhs) const { return !(operator == (rhs)); } iterator& operator ++ () { ::std::advance(it, utf8::internal::sequence_length(it)); return *this; } iterator operator ++ (int) { iterator temp = *this; ::std::advance(it, utf8::internal::sequence_length(it)); return temp; } iterator& operator -- () { utf8::unchecked::prior(it); return *this; } iterator operator -- (int) { iterator temp = *this; utf8::unchecked::prior(it); return temp; } }; // class iterator } // namespace utf8::unchecked } // namespace utf8 #endif // header guard libsass-3.3.4/src/utf8_string.cpp000066400000000000000000000057021267254216700167500ustar00rootroot00000000000000#include "sass.hpp" #include #include #include #include #include "utf8.h" namespace Sass { namespace UTF_8 { using std::string; // naming conventions: // offset: raw byte offset (0 based) // position: code point offset (0 based) // index: code point offset (1 based or negative) // function that will count the number of code points (utf-8 characters) from the given beginning to the given end size_t code_point_count(const string& str, size_t start, size_t end) { return utf8::distance(str.begin() + start, str.begin() + end); } size_t code_point_count(const string& str) { return utf8::distance(str.begin(), str.end()); } // function that will return the byte offset at a code point position size_t offset_at_position(const string& str, size_t position) { string::const_iterator it = str.begin(); utf8::advance(it, position, str.end()); return distance(str.begin(), it); } // function that returns number of bytes in a character at offset size_t code_point_size_at_offset(const string& str, size_t offset) { // get iterator from string and forward by offset string::const_iterator stop = str.begin() + offset; // check if beyond boundary if (stop == str.end()) return 0; // advance by one code point utf8::advance(stop, 1, str.end()); // calculate offset for code point return stop - str.begin() - offset; } // function that will return a normalized index, given a crazy one size_t normalize_index(int index, size_t len) { long signed_len = static_cast(len); // assuming the index is 1-based // we are returning a 0-based index if (index > 0 && index <= signed_len) { // positive and within string length return index-1; } else if (index > signed_len) { // positive and past string length return len; } else if (index == 0) { return 0; } else if (std::abs((double)index) <= signed_len) { // negative and within string length return index + signed_len; } else { // negative and past string length return 0; } } #ifdef _WIN32 // utf16 functions using std::wstring; // convert from utf16/wide string to utf8 string string convert_from_utf16(const wstring& utf16) { string utf8; // pre-allocate expected memory utf8.reserve(sizeof(utf16)/2); utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(utf8)); return utf8; } // convert from utf8 string to utf16/wide string wstring convert_to_utf16(const string& utf8) { wstring utf16; // pre-allocate expected memory utf16.reserve(code_point_count(utf8)*2); utf8::utf8to16(utf8.begin(), utf8.end(), back_inserter(utf16)); return utf16; } #endif } } libsass-3.3.4/src/utf8_string.hpp000066400000000000000000000022311267254216700167470ustar00rootroot00000000000000#ifndef SASS_UTF8_STRING_H #define SASS_UTF8_STRING_H #include #include "utf8.h" namespace Sass { namespace UTF_8 { // naming conventions: // offset: raw byte offset (0 based) // position: code point offset (0 based) // index: code point offset (1 based or negative) // function that will count the number of code points (utf-8 characters) from the beginning to the given end size_t code_point_count(const std::string& str, size_t start, size_t end); size_t code_point_count(const std::string& str); // function that will return the byte offset of a code point in a size_t offset_at_position(const std::string& str, size_t position); // function that returns number of bytes in a character in a string size_t code_point_size_at_offset(const std::string& str, size_t offset); // function that will return a normalized index, given a crazy one size_t normalize_index(int index, size_t len); #ifdef _WIN32 // functions to handle unicode paths on windows std::string convert_from_utf16(const std::wstring& wstr); std::wstring convert_to_utf16(const std::string& str); #endif } } #endif libsass-3.3.4/src/util.cpp000066400000000000000000000435401267254216700154530ustar00rootroot00000000000000#include "sass.hpp" #include "sass.h" #include "ast.hpp" #include "util.hpp" #include "lexer.hpp" #include "prelexer.hpp" #include "constants.hpp" #include "utf8/checked.h" #include #include namespace Sass { #define out_of_memory() do { \ std::cerr << "Out of memory.\n"; \ exit(EXIT_FAILURE); \ } while (0) double round(double val, size_t precision) { // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val); else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); // work around some compiler issue // cygwin has it not defined in std using namespace std; return ::round(val); } /* Sadly, sass_strdup is not portable. */ char *sass_strdup(const char *str) { char *ret = (char*) malloc(strlen(str) + 1); if (ret == NULL) out_of_memory(); strcpy(ret, str); return ret; } /* Locale unspecific atof function. */ double sass_atof(const char *str) { char separator = *(localeconv()->decimal_point); if(separator != '.'){ // The current locale specifies another // separator. convert the separator to the // one understood by the locale if needed const char *found = strchr(str, '.'); if(found != NULL){ // substitution is required. perform the substitution on a copy // of the string. This is slower but it is thread safe. char *copy = sass_strdup(str); *(copy + (found - str)) = separator; double res = atof(copy); free(copy); return res; } } return atof(str); } // helper for safe access to c_ctx const char* safe_str (const char* str, const char* alt) { return str == NULL ? alt : str; } void free_string_array(char ** arr) { if(!arr) return; char **it = arr; while (it && (*it)) { free(*it); ++it; } free(arr); } char **copy_strings(const std::vector& strings, char*** array, int skip) { int num = static_cast(strings.size()) - skip; char** arr = (char**) calloc(num + 1, sizeof(char*)); if (arr == 0) return *array = (char **)NULL; for(int i = 0; i < num; i++) { arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1)); if (arr[i] == 0) { free_string_array(arr); return *array = (char **)NULL; } std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]); arr[i][strings[i + skip].size()] = '\0'; } arr[num] = 0; return *array = arr; } // read css string (handle multiline DELIM) std::string read_css_string(const std::string& str) { std::string out(""); bool esc = false; for (auto i : str) { if (i == '\\') { esc = ! esc; } else if (esc && i == '\r') { continue; } else if (esc && i == '\n') { out.resize (out.size () - 1); esc = false; continue; } else { esc = false; } out.push_back(i); } if (esc) out += '\\'; return out; } // double escape all escape sequences // keep unescaped quotes and backslashes std::string evacuate_escapes(const std::string& str) { std::string out(""); bool esc = false; for (auto i : str) { if (i == '\\' && !esc) { out += '\\'; out += '\\'; esc = true; } else if (esc && i == '"') { out += '\\'; out += i; esc = false; } else if (esc && i == '\'') { out += '\\'; out += i; esc = false; } else if (esc && i == '\\') { out += '\\'; out += i; esc = false; } else { esc = false; out += i; } } // happens when parsing does not correctly skip // over escaped sequences for ie. interpolations // one example: foo\#{interpolate} // if (esc) out += '\\'; return out; } // bell character is replaces with space std::string string_to_output(const std::string& str) { std::string out(""); bool lf = false; for (auto i : str) { if (i == 10) { out += ' '; lf = true; } else if (!(lf && isspace(i))) { out += i; lf = false; } } return out; } std::string comment_to_string(const std::string& text) { std::string str = ""; size_t has = 0; char prev = 0; bool clean = false; 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 == '*') {} else { clean = false; str += ' '; if (prev == '*' && i == '/') str += "*/"; else str += i; } } else if (i == '\n') { clean = true; } else if (i == '\r') { clean = true; } else { str += i; } prev = i; } if (has) return str; else return text; } // find best quote_mark by detecting if the string contains any single // or double quotes. When a single quote is found, we not we want a double // quote as quote_mark. Otherwise we check if the string cotains any double // quotes, which will trigger the use of single quotes as best quote_mark. char detect_best_quotemark(const char* s, char qm) { // ensure valid fallback quote_mark char quote_mark = qm && qm != '*' ? qm : '"'; while (*s) { // force double quotes as soon // as one single quote is found if (*s == '\'') { return '"'; } // a single does not force quote_mark // maybe we see a double quote later else if (*s == '"') { quote_mark = '\''; } ++ s; } return quote_mark; } std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences) { // not enough room for quotes // no possibility to unquote if (s.length() < 2) return s; char q; bool skipped = false; // this is no guarantee that the unquoting will work // what about whitespace before/after the quote_mark? if (*s.begin() == '"' && *s.rbegin() == '"') q = '"'; else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\''; else return s; std::string unq; unq.reserve(s.length()-2); for (size_t i = 1, L = s.length() - 1; i < L; ++i) { // implement the same strange ruby sass behavior // an escape sequence can also mean a unicode char if (s[i] == '\\' && !skipped) { // remember skipped = true; // skip it // ++ i; // if (i == L) break; // escape length size_t len = 1; // parse as many sequence chars as possible // ToDo: Check if ruby aborts after possible max while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len; // hex string? if (keep_utf8_sequences) { unq.push_back(s[i]); } else if (len > 1) { // convert the extracted hex string to code point value // ToDo: Maybe we could do this without creating a substring uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); if (s[i + len] == ' ') ++ len; // assert invalid code points if (cp == 0) cp = 0xFFFD; // replace bell character // if (cp == 10) cp = 32; // use a very simple approach to convert via utf8 lib // maybe there is a more elegant way; maybe we shoud // convert the whole output from string to a stream!? // allocate memory for utf8 char and convert to utf8 unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); for(size_t m = 0; u[m] && m < 5; m++) unq.push_back(u[m]); // skip some more chars? i += len - 1; skipped = false; } } // check for unexpected delimiter // be strict and throw error back // else if (!skipped && q == s[i]) { // // don't be that strict // return s; // // this basically always means an internal error and not users fault // error("Unescaped delimiter in string to unquote found. [" + s + "]", ParserState("[UNQUOTE]")); // } else { skipped = false; unq.push_back(s[i]); } } if (skipped) { return s; } if (qd) *qd = q; return unq; } std::string quote(const std::string& s, char q) { // autodetect with fallback to given quote q = detect_best_quotemark(s.c_str(), q); // return an empty quoted string if (s.empty()) return std::string(2, q ? q : '"'); std::string quoted; quoted.reserve(s.length()+2); quoted.push_back(q); const char* it = s.c_str(); const char* end = it + strlen(it) + 1; while (*it && it < end) { const char* now = it; if (*it == q) { quoted.push_back('\\'); } else if (*it == '\\') { quoted.push_back('\\'); } int cp = utf8::next(it, end); // in case of \r, check if the next in sequence // is \n and then advance the iterator. if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') { cp = utf8::next(it, end); } if (cp == '\n') { quoted.push_back('\\'); quoted.push_back('a'); // we hope we can remove this flag once we figure out // why ruby sass has these different output behaviors // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ") using namespace Prelexer; if (alternatives < Prelexer::char_range<'a', 'f'>, Prelexer::char_range<'A', 'F'>, Prelexer::char_range<'0', '9'>, space >(it) != NULL) { quoted.push_back(' '); } } else if (cp < 127) { quoted.push_back((char) cp); } else { while (now < it) { quoted.push_back(*now); ++ now; } } } quoted.push_back(q); return quoted; } bool is_hex_doublet(double n) { return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 || n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 || n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB || n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ; } bool is_color_doublet(double r, double g, double b) { return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b); } bool peek_linefeed(const char* start) { using namespace Prelexer; using namespace Constants; return sequence < zero_plus < alternatives < exactly <' '>, exactly <'\t'>, line_comment, block_comment, delimited_by < slash_star, star_slash, false > > >, re_linebreak >(start) != 0; } 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; } // compress a color sixtuplet if possible // input: "#CC9900" -> output: "#C90" std::string normalize_sixtuplet(const std::string& col) { if( col.substr(1, 1) == col.substr(2, 1) && col.substr(3, 1) == col.substr(4, 1) && col.substr(5, 1) == col.substr(6, 1) ) { return std::string("#" + col.substr(1, 1) + col.substr(3, 1) + col.substr(5, 1)); } else { return std::string(col); } } bool isPrintable(Ruleset* r, Sass_Output_Style style) { if (r == NULL) { return false; } Block* b = r->block(); bool hasSelectors = static_cast(r->selector())->length() > 0; if (!hasSelectors) { return false; } bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (dynamic_cast(stm)) { return true; } else if (dynamic_cast(stm)) { Block* pChildBlock = ((Has_Block*)stm)->block(); if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; } } else if (Comment* c = dynamic_cast(stm)) { // keep for uncompressed if (style != COMPRESSED) { hasDeclarations = true; } // output style compressed if (c->is_important()) { hasDeclarations = c->is_important(); } } else if (Declaration* d = dynamic_cast(stm)) { return isPrintable(d, style); } else { hasDeclarations = true; } if (hasDeclarations || hasPrintableChildBlocks) { return true; } } return false; } bool isPrintable(String_Constant* s, Sass_Output_Style style) { return ! s->value().empty(); } bool isPrintable(String_Quoted* s, Sass_Output_Style style) { return true; } bool isPrintable(Declaration* d, Sass_Output_Style style) { Expression* val = d->value(); if (String_Quoted* sq = dynamic_cast(val)) return isPrintable(sq, style); if (String_Constant* sc = dynamic_cast(val)) return isPrintable(sc, style); return true; } bool isPrintable(Supports_Block* f, Sass_Output_Style style) { if (f == NULL) { return false; } Block* b = f->block(); // bool hasSelectors = f->selector() && static_cast(f->selector())->length() > 0; bool hasDeclarations = false; bool hasPrintableChildBlocks = false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0), // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with // a declaration in it). } else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { hasDeclarations = true; } else if (dynamic_cast(stm)) { Block* pChildBlock = ((Has_Block*)stm)->block(); if (isPrintable(pChildBlock, style)) { hasPrintableChildBlocks = true; } } if (hasDeclarations || hasPrintableChildBlocks) { return true; } } return false; } bool isPrintable(Media_Block* m, Sass_Output_Style style) { if (m == 0) return false; Block* b = m->block(); if (b == 0) return false; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (typeid(*stm) == typeid(At_Rule)) return true; if (typeid(*stm) == typeid(Declaration)) return true; if (Has_Block* child = dynamic_cast(stm)) { if (isPrintable(child->block(), style)) return true; } } return false; } bool isPrintable(Block* b, Sass_Output_Style style) { if (b == NULL) { return false; } for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { return true; } else if (typeid(*stm) == typeid(Comment)) { Comment* c = (Comment*) stm; // keep for uncompressed if (style != COMPRESSED) { return true; } // output style compressed if (c->is_important()) { return true; } } else if (typeid(*stm) == typeid(Ruleset)) { Ruleset* r = (Ruleset*) stm; if (isPrintable(r, style)) { return true; } } else if (typeid(*stm) == typeid(Supports_Block)) { Supports_Block* f = (Supports_Block*) stm; if (isPrintable(f, style)) { return true; } } else if (typeid(*stm) == typeid(Media_Block)) { Media_Block* m = (Media_Block*) stm; if (isPrintable(m, style)) { return true; } } else if (dynamic_cast(stm) && isPrintable(((Has_Block*)stm)->block(), style)) { return true; } } return false; } std::string vecJoin(const std::vector& vec, const std::string& sep) { switch (vec.size()) { case 0: return std::string(""); case 1: return vec[0]; default: std::ostringstream os; os << vec[0]; for (size_t i = 1; i < vec.size(); i++) { os << sep << vec[i]; } return os.str(); } } bool isAscii(const char chr) { return unsigned(chr) < 128; } } } libsass-3.3.4/src/util.hpp000066400000000000000000000037611267254216700154610ustar00rootroot00000000000000#ifndef SASS_UTIL_H #define SASS_UTIL_H #include #include #include #include "sass.hpp" #include "sass/base.h" #include "ast_fwd_decl.hpp" #define SASS_ASSERT(cond, msg) assert(cond && msg) namespace Sass { double round(double val, size_t precision = 0); char* sass_strdup(const char* str); double sass_atof(const char* str); const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const std::vector&, char ***, int = 0); std::string read_css_string(const std::string& str); std::string evacuate_escapes(const std::string& str); std::string string_to_output(const std::string& str); std::string comment_to_string(const std::string& text); std::string quote(const std::string&, char q = 0); std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false); char detect_best_quotemark(const char* s, char qm = '"'); bool is_hex_doublet(double n); bool is_color_doublet(double r, double g, double b); bool peek_linefeed(const char* start); namespace Util { std::string rtrim(const std::string& str); std::string normalize_underscores(const std::string& str); std::string normalize_decimals(const std::string& str); std::string normalize_sixtuplet(const std::string& col); std::string vecJoin(const std::vector& vec, const std::string& sep); bool containsAnyPrintableStatements(Block* b); bool isPrintable(Ruleset* r, Sass_Output_Style style = NESTED); bool isPrintable(Supports_Block* r, Sass_Output_Style style = NESTED); bool isPrintable(Media_Block* r, Sass_Output_Style style = NESTED); bool isPrintable(Block* b, Sass_Output_Style style = NESTED); 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); bool isAscii(const char chr); } } #endif libsass-3.3.4/src/values.cpp000066400000000000000000000114501267254216700157700ustar00rootroot00000000000000#include "sass.hpp" #include "sass.h" #include "values.hpp" #include namespace Sass { // convert value from C++ side to C-API union Sass_Value* ast_node_to_sass_value (const Expression* val) { if (val->concrete_type() == Expression::NUMBER) { const Number* res = dynamic_cast(val); return sass_make_number(res->value(), res->unit().c_str()); } else if (val->concrete_type() == Expression::COLOR) { const Color* col = dynamic_cast(val); return sass_make_color(col->r(), col->g(), col->b(), col->a()); } else if (val->concrete_type() == Expression::LIST) { const List* l = dynamic_cast(val); union Sass_Value* list = sass_make_list(l->size(), l->separator()); for (size_t i = 0, L = l->length(); i < L; ++i) { auto val = ast_node_to_sass_value((*l)[i]); sass_list_set_value(list, i, val); } return list; } else if (val->concrete_type() == Expression::MAP) { const Map* m = dynamic_cast(val); union Sass_Value* map = sass_make_map(m->length()); size_t i = 0; for (auto key : m->keys()) { sass_map_set_key(map, i, ast_node_to_sass_value(key)); sass_map_set_value(map, i, ast_node_to_sass_value(m->at(key))); ++ i; } return map; } else if (val->concrete_type() == Expression::NULL_VAL) { return sass_make_null(); } else if (val->concrete_type() == Expression::BOOLEAN) { const Boolean* res = dynamic_cast(val); return sass_make_boolean(res->value()); } else if (val->concrete_type() == Expression::STRING) { if (const String_Quoted* qstr = dynamic_cast(val)) { return sass_make_qstring(qstr->value().c_str()); } else if (const String_Constant* cstr = dynamic_cast(val)) { return sass_make_string(cstr->value().c_str()); } } return sass_make_error("unknown sass value type"); } // convert value from C-API to C++ side Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val) { switch (sass_value_get_tag(val)) { case SASS_NUMBER: return SASS_MEMORY_NEW(mem, Number, ParserState("[C-VALUE]"), sass_number_get_value(val), sass_number_get_unit(val)); break; case SASS_BOOLEAN: return SASS_MEMORY_NEW(mem, Boolean, ParserState("[C-VALUE]"), sass_boolean_get_value(val)); break; case SASS_COLOR: return SASS_MEMORY_NEW(mem, Color, ParserState("[C-VALUE]"), sass_color_get_r(val), sass_color_get_g(val), sass_color_get_b(val), sass_color_get_a(val)); break; case SASS_STRING: if (sass_string_is_quoted(val)) { return SASS_MEMORY_NEW(mem, String_Quoted, ParserState("[C-VALUE]"), sass_string_get_value(val)); } else { return SASS_MEMORY_NEW(mem, String_Constant, ParserState("[C-VALUE]"), sass_string_get_value(val)); } break; case SASS_LIST: { List* l = SASS_MEMORY_NEW(mem, List, ParserState("[C-VALUE]"), sass_list_get_length(val), sass_list_get_separator(val)); for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) { *l << sass_value_to_ast_node(mem, sass_list_get_value(val, i)); } return l; } break; case SASS_MAP: { Map* m = SASS_MEMORY_NEW(mem, Map, ParserState("[C-VALUE]")); for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) { *m << std::make_pair( sass_value_to_ast_node(mem, sass_map_get_key(val, i)), sass_value_to_ast_node(mem, sass_map_get_value(val, i))); } return m; } break; case SASS_NULL: return SASS_MEMORY_NEW(mem, Null, ParserState("[C-VALUE]")); break; case SASS_ERROR: return SASS_MEMORY_NEW(mem, Custom_Error, ParserState("[C-VALUE]"), sass_error_get_message(val)); break; case SASS_WARNING: return SASS_MEMORY_NEW(mem, Custom_Warning, ParserState("[C-VALUE]"), sass_warning_get_message(val)); break; } return 0; } } libsass-3.3.4/src/values.hpp000066400000000000000000000003651267254216700160000ustar00rootroot00000000000000#ifndef SASS_VALUES_H #define SASS_VALUES_H #include "ast.hpp" namespace Sass { union Sass_Value* ast_node_to_sass_value (const Expression* val); Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val); } #endif libsass-3.3.4/test/000077500000000000000000000000001267254216700141545ustar00rootroot00000000000000libsass-3.3.4/test/test_node.cpp000066400000000000000000000041521267254216700166460ustar00rootroot00000000000000#include #include #include "node.hpp" #include "parser.hpp" #define STATIC_ARRAY_SIZE(array) (sizeof((array))/sizeof((array[0]))) namespace Sass { Context ctx = Context::Data(); const char* const ROUNDTRIP_TESTS[] = { NULL, "~", "CMPD", "~ CMPD", "CMPD >", "> > CMPD", "CMPD ~ ~", "> + CMPD1.CMPD2 > ~", "> + CMPD1.CMPD2 CMPD3.CMPD4 > ~", "+ CMPD1 CMPD2 ~ CMPD3 + CMPD4 > CMPD5 > ~" }; static Complex_Selector* createComplexSelector(std::string src) { std::string temp(src); temp += ";"; return (*Parser::from_c_str(temp.c_str(), ctx, "", Position()).parse_selector_list())[0]; } void roundtripTest(const char* toTest) { // Create the initial selector Complex_Selector* pOrigSelector = NULL; if (toTest) { pOrigSelector = createComplexSelector(toTest); } std::string expected(pOrigSelector ? pOrigSelector->to_string() : "NULL"); // Roundtrip the selector into a node and back Node node = complexSelectorToNode(pOrigSelector, ctx); std::stringstream nodeStringStream; nodeStringStream << node; std::string nodeString = nodeStringStream.str(); cout << "ASNODE: " << node << endl; Complex_Selector* pNewSelector = nodeToComplexSelector(node, ctx); // Show the result std::string result(pNewSelector ? pNewSelector->to_string() : "NULL"); cout << "SELECTOR: " << expected << endl; cout << "NEW SELECTOR: " << result << endl; // Test that they are equal using the equality operator assert( (!pOrigSelector && !pNewSelector ) || (pOrigSelector && pNewSelector) ); if (pOrigSelector) { assert( *pOrigSelector == *pNewSelector ); } // Test that they are equal by comparing the string versions of the selectors assert(expected == result); } int main() { for (int index = 0; index < STATIC_ARRAY_SIZE(ROUNDTRIP_TESTS); index++) { const char* const toTest = ROUNDTRIP_TESTS[index]; cout << "\nINPUT STRING: " << (toTest ? toTest : "NULL") << endl; roundtripTest(toTest); } cout << "\nTesting Done.\n"; } } libsass-3.3.4/test/test_paths.cpp000066400000000000000000000007751267254216700170470ustar00rootroot00000000000000#include #include "../paths.hpp" using namespace Sass; template std::vector& operator<<(std::vector& v, const T& e) { v.push_back(e); return v; } int main() { std::vector v1, v2, v3; v1 << 1 << 2; v2 << 3; v3 << 4 << 5 << 6; std::vector > ss; ss << v1 << v2 << v3; std::vector > ps = paths(ss); for (size_t i = 0, S = ps.size(); i < S; ++i) { std::cout << vector_to_string(ps[i]) << std::endl; } return 0; } libsass-3.3.4/test/test_selector_difference.cpp000066400000000000000000000010511267254216700217060ustar00rootroot00000000000000#include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" #include #include using namespace Sass; Context ctx = Context::Data(); Compound_Selector* selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } void diff(std::string s, std::string t) { std::cout << s << " - " << t << " = " << selector(s + ";")->minus(selector(t + ";"), ctx)->to_string() << std::endl; } int main() { diff(".a.b.c", ".c.b"); diff(".a.b.c", ".fludge.b"); return 0; } libsass-3.3.4/test/test_specificity.cpp000066400000000000000000000010271267254216700202320ustar00rootroot00000000000000#include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" #include #include using namespace Sass; Context ctx = Context::Data(); Selector* selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_list(); } void spec(std::string sel) { std::cout << sel << "\t::\t" << selector(sel + ";")->specificity() << std::endl; } int main() { spec("foo bar hux"); spec(".foo .bar hux"); spec("#foo .bar[hux='mux']"); spec("a b c d e f"); return 0; } libsass-3.3.4/test/test_subset_map.cpp000066400000000000000000000301721267254216700200640ustar00rootroot00000000000000#include #include #include #include "../subset_map.hpp" Subset_Map ssm; string toString(std::vector v); string toString(std::vector>> v); void assertEqual(string std::sExpected, std::string sResult); void setup() { ssm.clear(); //@ssm[Set[1, 2]] = "Foo" std::vector s1; s1.push_back("1"); s1.push_back("2"); ssm.put(s1, "Foo"); //@ssm[Set["fizz", "fazz"]] = "Bar" std::vector s2; s2.push_back("fizz"); s2.push_back("fazz"); ssm.put(s2, "Bar"); //@ssm[Set[:foo, :bar]] = "Baz" std::vector s3; s3.push_back(":foo"); s3.push_back(":bar"); ssm.put(s3, "Baz"); //@ssm[Set[:foo, :bar, :baz]] = "Bang" std::vector s4; s4.push_back(":foo"); s4.push_back(":bar"); s4.push_back(":baz"); ssm.put(s4, "Bang"); //@ssm[Set[:bip, :bop, :blip]] = "Qux" std::vector s5; s5.push_back(":bip"); s5.push_back(":bop"); s5.push_back(":blip"); ssm.put(s5, "Qux"); //@ssm[Set[:bip, :bop]] = "Thram" std::vector s6; s6.push_back(":bip"); s6.push_back(":bop"); ssm.put(s6, "Thram"); } void testEqualKeys() { std::cout << "testEqualKeys" << std::endl; //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2]) std::vector k1; k1.push_back("1"); k1.push_back("2"); assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); //assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz"]) std::vector k2; k2.push_back("fizz"); k2.push_back("fazz"); assertEqual("[[Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); std::cout << std::endl; } void testSubsetKeys() { std::cout << "testSubsetKeys" << std::endl; //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2, "fuzz"]) std::vector k1; k1.push_back("1"); k1.push_back("2"); k1.push_back("fuzz"); assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); //assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz", 3]) std::vector k2; k2.push_back("fizz"); k2.push_back("fazz"); k2.push_back("3"); assertEqual("[[Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); std::cout << std::endl; } void testSupersetKeys() { std::cout << "testSupersetKeys" << std::endl; //assert_equal [], @ssm.get(Set[1]) std::vector k1; k1.push_back("1"); assertEqual("[]", toString(ssm.get_kv(k1))); //assert_equal [], @ssm.get(Set[2]) std::vector k2; k2.push_back("2"); assertEqual("[]", toString(ssm.get_kv(k2))); //assert_equal [], @ssm.get(Set["fizz"]) std::vector k3; k3.push_back("fizz"); assertEqual("[]", toString(ssm.get_kv(k3))); //assert_equal [], @ssm.get(Set["fazz"]) std::vector k4; k4.push_back("fazz"); assertEqual("[]", toString(ssm.get_kv(k4))); std::cout << std::endl; } void testDisjointKeys() { std::cout << "testDisjointKeys" << std::endl; //assert_equal [], @ssm.get(Set[3, 4]) std::vector k1; k1.push_back("3"); k1.push_back("4"); assertEqual("[]", toString(ssm.get_kv(k1))); //assert_equal [], @ssm.get(Set["fuzz", "frizz"]) std::vector k2; k2.push_back("fuzz"); k2.push_back("frizz"); assertEqual("[]", toString(ssm.get_kv(k2))); //assert_equal [], @ssm.get(Set["gran", 15]) std::vector k3; k3.push_back("gran"); k3.push_back("15"); assertEqual("[]", toString(ssm.get_kv(k3))); std::cout << std::endl; } void testSemiDisjointKeys() { std::cout << "testSemiDisjointKeys" << std::endl; //assert_equal [], @ssm.get(Set[2, 3]) std::vector k1; k1.push_back("2"); k1.push_back("3"); assertEqual("[]", toString(ssm.get_kv(k1))); //assert_equal [], @ssm.get(Set["fizz", "fuzz"]) std::vector k2; k2.push_back("fizz"); k2.push_back("fuzz"); assertEqual("[]", toString(ssm.get_kv(k2))); //assert_equal [], @ssm.get(Set[1, "fazz"]) std::vector k3; k3.push_back("1"); k3.push_back("fazz"); assertEqual("[]", toString(ssm.get_kv(k3))); std::cout << std::endl; } void testEmptyKeySet() { std::cout << "testEmptyKeySet" << std::endl; //assert_raises(ArgumentError) {@ssm[Set[]] = "Fail"} std::vector s1; try { ssm.put(s1, "Fail"); } catch (const char* &e) { assertEqual("internal error: subset map keys may not be empty", e); } } void testEmptyKeyGet() { std::cout << "testEmptyKeyGet" << std::endl; //assert_equal [], @ssm.get(Set[]) std::vector k1; assertEqual("[]", toString(ssm.get_kv(k1))); std::cout << std::endl; } void testMultipleSubsets() { std::cout << "testMultipleSubsets" << std::endl; //assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, "fizz", "fazz"]) std::vector k1; k1.push_back("1"); k1.push_back("2"); k1.push_back("fizz"); k1.push_back("fazz"); assertEqual("[[Foo, Set[1, 2]], [Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k1))); //assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, 3, "fizz", "fazz", "fuzz"]) std::vector k2; k2.push_back("1"); k2.push_back("2"); k2.push_back("3"); k2.push_back("fizz"); k2.push_back("fazz"); k2.push_back("fuzz"); assertEqual("[[Foo, Set[1, 2]], [Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); //assert_equal [["Baz", Set[:foo, :bar]]], @ssm.get(Set[:foo, :bar]) std::vector k3; k3.push_back(":foo"); k3.push_back(":bar"); assertEqual("[[Baz, Set[:foo, :bar]]]", toString(ssm.get_kv(k3))); //assert_equal [["Baz", Set[:foo, :bar]], ["Bang", Set[:foo, :bar, :baz]]], @ssm.get(Set[:foo, :bar, :baz]) std::vector k4; k4.push_back(":foo"); k4.push_back(":bar"); k4.push_back(":baz"); assertEqual("[[Baz, Set[:foo, :bar]], [Bang, Set[:foo, :bar, :baz]]]", toString(ssm.get_kv(k4))); std::cout << std::endl; } void testBracketBracket() { std::cout << "testBracketBracket" << std::endl; //assert_equal ["Foo"], @ssm[Set[1, 2, "fuzz"]] std::vector k1; k1.push_back("1"); k1.push_back("2"); k1.push_back("fuzz"); assertEqual("[Foo]", toString(ssm.get_v(k1))); //assert_equal ["Baz", "Bang"], @ssm[Set[:foo, :bar, :baz]] std::vector k2; k2.push_back(":foo"); k2.push_back(":bar"); k2.push_back(":baz"); assertEqual("[Baz, Bang]", toString(ssm.get_v(k2))); std::cout << std::endl; } void testKeyOrder() { std::cout << "testEqualKeys" << std::endl; //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[2, 1]) std::vector k1; k1.push_back("2"); k1.push_back("1"); assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); std::cout << std::endl; } void testOrderPreserved() { std::cout << "testOrderPreserved" << std::endl; //@ssm[Set[10, 11, 12]] = 1 std::vector s1; s1.push_back("10"); s1.push_back("11"); s1.push_back("12"); ssm.put(s1, "1"); //@ssm[Set[10, 11]] = 2 std::vector s2; s2.push_back("10"); s2.push_back("11"); ssm.put(s2, "2"); //@ssm[Set[11]] = 3 std::vector s3; s3.push_back("11"); ssm.put(s3, "3"); //@ssm[Set[11, 12]] = 4 std::vector s4; s4.push_back("11"); s4.push_back("12"); ssm.put(s4, "4"); //@ssm[Set[9, 10, 11, 12, 13]] = 5 std::vector s5; s5.push_back("9"); s5.push_back("10"); s5.push_back("11"); s5.push_back("12"); s5.push_back("13"); ssm.put(s5, "5"); //@ssm[Set[10, 13]] = 6 std::vector s6; s6.push_back("10"); s6.push_back("13"); ssm.put(s6, "6"); //assert_equal([[1, Set[10, 11, 12]], [2, Set[10, 11]], [3, Set[11]], [4, Set[11, 12]], [5, Set[9, 10, 11, 12, 13]], [6, Set[10, 13]]], @ssm.get(Set[9, 10, 11, 12, 13])) std::vector k1; k1.push_back("9"); k1.push_back("10"); k1.push_back("11"); k1.push_back("12"); k1.push_back("13"); assertEqual("[[1, Set[10, 11, 12]], [2, Set[10, 11]], [3, Set[11]], [4, Set[11, 12]], [5, Set[9, 10, 11, 12, 13]], [6, Set[10, 13]]]", toString(ssm.get_kv(k1))); std::cout << std::endl; } void testMultipleEqualValues() { std::cout << "testMultipleEqualValues" << std::endl; //@ssm[Set[11, 12]] = 1 std::vector s1; s1.push_back("11"); s1.push_back("12"); ssm.put(s1, "1"); //@ssm[Set[12, 13]] = 2 std::vector s2; s2.push_back("12"); s2.push_back("13"); ssm.put(s2, "2"); //@ssm[Set[13, 14]] = 1 std::vector s3; s3.push_back("13"); s3.push_back("14"); ssm.put(s3, "1"); //@ssm[Set[14, 15]] = 1 std::vector s4; s4.push_back("14"); s4.push_back("15"); ssm.put(s4, "1"); //assert_equal([[1, Set[11, 12]], [2, Set[12, 13]], [1, Set[13, 14]], [1, Set[14, 15]]], @ssm.get(Set[11, 12, 13, 14, 15])) std::vector k1; k1.push_back("11"); k1.push_back("12"); k1.push_back("13"); k1.push_back("14"); k1.push_back("15"); assertEqual("[[1, Set[11, 12]], [2, Set[12, 13]], [1, Set[13, 14]], [1, Set[14, 15]]]", toString(ssm.get_kv(k1))); std::cout << std::endl; } int main() { std::vector s1; s1.push_back("1"); s1.push_back("2"); std::vector s2; s2.push_back("2"); s2.push_back("3"); std::vector s3; s3.push_back("3"); s3.push_back("4"); ssm.put(s1, "value1"); ssm.put(s2, "value2"); ssm.put(s3, "value3"); std::vector s4; s4.push_back("1"); s4.push_back("2"); s4.push_back("3"); std::vector > > fetched(ssm.get_kv(s4)); std::cout << "PRINTING RESULTS:" << std::endl; for (size_t i = 0, S = fetched.size(); i < S; ++i) { std::cout << fetched[i].first << std::endl; } Subset_Map ssm2; ssm2.put(s1, "foo"); ssm2.put(s2, "bar"); ssm2.put(s4, "hux"); std::vector > > fetched2(ssm2.get_kv(s4)); std::cout << std::endl << "PRINTING RESULTS:" << std::endl; for (size_t i = 0, S = fetched2.size(); i < S; ++i) { std::cout << fetched2[i].first << std::endl; } std::cout << "TRYING ON A SELECTOR-LIKE OBJECT" << std::endl; Subset_Map sel_ssm; std::vector target; target.push_back("desk"); target.push_back(".wood"); std::vector actual; actual.push_back("desk"); actual.push_back(".wood"); actual.push_back(".mine"); sel_ssm.put(target, "has-aquarium"); std::vector > > fetched3(sel_ssm.get_kv(actual)); std::cout << "RESULTS:" << std::endl; for (size_t i = 0, S = fetched3.size(); i < S; ++i) { std::cout << fetched3[i].first << std::endl; } std::cout << std::endl; // BEGIN PORTED RUBY TESTS FROM /test/sass/util/subset_map_test.rb setup(); testEqualKeys(); testSubsetKeys(); testSupersetKeys(); testDisjointKeys(); testSemiDisjointKeys(); testEmptyKeySet(); testEmptyKeyGet(); testMultipleSubsets(); testBracketBracket(); testKeyOrder(); setup(); testOrderPreserved(); setup(); testMultipleEqualValues(); return 0; } string toString(std::vector>> v) { std::stringstream buffer; buffer << "["; for (size_t i = 0, S = v.size(); i < S; ++i) { buffer << "[" << v[i].first; buffer << ", Set["; for (size_t j = 0, S = v[i].second.size(); j < S; ++j) { buffer << v[i].second[j]; if (j < S-1) { buffer << ", "; } } buffer << "]]"; if (i < S-1) { buffer << ", "; } } buffer << "]"; return buffer.str(); } string toString(std::vector v) { std::stringstream buffer; buffer << "["; for (size_t i = 0, S = v.size(); i < S; ++i) { buffer << v[i]; if (i < S-1) { buffer << ", "; } } buffer << "]"; return buffer.str(); } void assertEqual(string sExpected, string sResult) { std::cout << "Expected: " << sExpected << std::endl; std::cout << "Result: " << sResult << std::endl; assert(sExpected == sResult); } libsass-3.3.4/test/test_superselector.cpp000066400000000000000000000037441267254216700206260ustar00rootroot00000000000000#include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" #include using namespace Sass; Context ctx = Context(Context::Data()); Compound_Selector* compound_selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } Complex_Selector* complex_selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_complex_selector(false); } void check_compound(std::string s1, std::string s2) { std::cout << "Is " << s1 << " a superselector of " << s2 << "?\t" << compound_selector(s1 + ";")->is_superselector_of(compound_selector(s2 + ";")) << std::endl; } void check_complex(std::string s1, std::string s2) { std::cout << "Is " << s1 << " a superselector of " << s2 << "?\t" << complex_selector(s1 + ";")->is_superselector_of(complex_selector(s2 + ";")) << std::endl; } int main() { check_compound(".foo", ".foo.bar"); check_compound(".foo.bar", ".foo"); check_compound(".foo.bar", "div.foo"); check_compound(".foo", "div.foo"); check_compound("div.foo", ".foo"); check_compound("div.foo", "div.bar.foo"); check_compound("p.foo", "div.bar.foo"); check_compound(".hux", ".mumble"); std::cout << std::endl; check_complex(".foo ~ .bar", ".foo + .bar"); check_complex(".foo .bar", ".foo + .bar"); check_complex(".foo .bar", ".foo > .bar"); check_complex(".foo .bar > .hux", ".foo.a .bar.b > .hux"); check_complex(".foo ~ .bar .hux", ".foo.a + .bar.b > .hux"); check_complex(".foo", ".bar .foo"); check_complex(".foo", ".foo.a"); check_complex(".foo.bar", ".foo"); check_complex(".foo .bar .hux", ".bar .hux"); check_complex(".foo ~ .bar .hux.x", ".foo.a + .bar.b > .hux.y"); check_complex(".foo ~ .bar .hux", ".foo.a + .bar.b > .mumble"); check_complex(".foo + .bar", ".foo ~ .bar"); check_complex("a c e", "a b c d e"); check_complex("c a e", "a b c d e"); return 0; } libsass-3.3.4/test/test_unification.cpp000066400000000000000000000015741267254216700202360ustar00rootroot00000000000000#include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" #include using namespace Sass; Context ctx = Context(Context::Data()); Compound_Selector* selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } void unify(std::string lhs, std::string rhs) { Compound_Selector* unified = selector(lhs + ";")->unify_with(selector(rhs + ";"), ctx); std::cout << lhs << " UNIFIED WITH " << rhs << " =\t" << (unified ? unified->to_string() : "NOTHING") << std::endl; } int main() { unify(".foo", ".foo.bar"); unify("div:nth-of-type(odd)", "div:first-child"); unify("div", "span:whatever"); unify("div", "span"); unify("foo:bar::after", "foo:bar::first-letter"); unify(".foo#bar.hux", ".hux.foo#bar"); unify(".foo#bar.hux", ".hux.foo#baz"); unify("*:blah:fudge", "p:fudge:blah"); return 0; } libsass-3.3.4/version.sh000077500000000000000000000004541267254216700152240ustar00rootroot00000000000000if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION=`git describe --abbrev=4 --dirty --always --tags 2>/dev/null` fi if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION=`cat VERSION 2>/dev/null` fi if test "x$LIBSASS_VERSION" = "x"; then LIBSASS_VERSION="[na]" fi echo $LIBSASS_VERSION libsass-3.3.4/win/000077500000000000000000000000001267254216700137725ustar00rootroot00000000000000libsass-3.3.4/win/libsass.sln000066400000000000000000000024251267254216700161530ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30723.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsass", "libsass.vcxproj", "{E4030474-AFC9-4CC6-BEB6-D846F631502B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|Win64 = Debug|Win64 Release|Win32 = Release|Win32 Release|Win64 = Release|Win64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win32.ActiveCfg = Debug|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win32.Build.0 = Debug|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win64.ActiveCfg = Debug|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Debug|Win64.Build.0 = Debug|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win32.ActiveCfg = Release|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win32.Build.0 = Release|Win32 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win64.ActiveCfg = Release|x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B}.Release|Win64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal libsass-3.3.4/win/libsass.targets000066400000000000000000000154111267254216700170270ustar00rootroot00000000000000 libsass-3.3.4/win/libsass.vcxproj000066400000000000000000000213301267254216700170460ustar00rootroot00000000000000 [NA] ..\src ..\src ..\include %(PreprocessorDefinitions);LIBSASS_VERSION="$(LIBSASS_VERSION)"; Debug Win32 Debug x64 Release Win32 Release x64 {E4030474-AFC9-4CC6-BEB6-D846F631502B} Win32Proj libsass libsass Unicode DynamicLibrary ADD_EXPORTS;$(PreprocessorDefinitions); StaticLibrary v120 v140 true true false true false true true $(SolutionDir)bin\Debug\ $(SolutionDir)bin\Debug\obj\ true $(SolutionDir)bin\Debug\ $(SolutionDir)bin\Debug\obj\ false $(SolutionDir)bin\ $(SolutionDir)bin\obj\ false $(SolutionDir)bin\ $(SolutionDir)bin\obj\ ..\include;%(AdditionalIncludeDirectories) Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true Level3 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console true true true libsass-3.3.4/win/libsass.vcxproj.filters000066400000000000000000000307331267254216700205240ustar00rootroot00000000000000 {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {bb9c270d-e9f5-49bf-afda-771a1a4bb5b7} h;hh;hpp;hxx;hm;in;inl;inc;xsd Include Headers Include Headers Include Headers Include Headers Include Headers Include Headers Include Headers Include Headers Include Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Headers Sources Sources Sources Source Files Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources Sources