pax_global_header 0000666 0000000 0000000 00000000064 12672542167 0014526 g ustar 00root root 0000000 0000000 52 comment=213339a802827fab23cb61b79ad7d592fad33c2d
libsass-3.3.4/ 0000775 0000000 0000000 00000000000 12672542167 0013175 5 ustar 00root root 0000000 0000000 libsass-3.3.4/.editorconfig 0000664 0000000 0000000 00000000444 12672542167 0015654 0 ustar 00root root 0000000 0000000 # 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/.gitattributes 0000664 0000000 0000000 00000000102 12672542167 0016061 0 ustar 00root root 0000000 0000000 # Auto detect text files and perform LF normalization
* text=auto
libsass-3.3.4/.gitignore 0000664 0000000 0000000 00000001515 12672542167 0015167 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000002126 12672542167 0015307 0 ustar 00root root 0000000 0000000 language: 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/COPYING 0000664 0000000 0000000 00000002334 12672542167 0014232 0 ustar 00root root 0000000 0000000
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.am 0000664 0000000 0000000 00000004426 12672542167 0015651 0 ustar 00root root 0000000 0000000 ACLOCAL_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/INSTALL 0000664 0000000 0000000 00000000061 12672542167 0014223 0 ustar 00root root 0000000 0000000 // Autotools requires us to have this file. Boo.
libsass-3.3.4/LICENSE 0000664 0000000 0000000 00000002362 12672542167 0014205 0 ustar 00root root 0000000 0000000
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/Makefile 0000664 0000000 0000000 00000017476 12672542167 0014654 0 ustar 00root root 0000000 0000000 OS ?= $(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.conf 0000664 0000000 0000000 00000001357 12672542167 0015567 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000010024 12672542167 0014711 0 ustar 00root root 0000000 0000000 LibSass
=======
by Aaron Leung ([@akhleung]), Hampton Catlin ([@hcatlin]), Marcel Greter ([@mgreter]) and Michael Mifsud ([@xzyfer])
[](https://travis-ci.org/sass/libsass)
[](https://ci.appveyor.com/project/sass/libsass/branch/master)
[](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE)
[](https://coveralls.io/r/sass/libsass?branch=feature%2Ftest-travis-ci-3)
[](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.md 0000664 0000000 0000000 00000000562 12672542167 0014771 0 ustar 00root root 0000000 0000000 Serious 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.yml 0000664 0000000 0000000 00000004501 12672542167 0015565 0 ustar 00root root 0000000 0000000 os: 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.ac 0000664 0000000 0000000 00000010442 12672542167 0015464 0 ustar 00root root 0000000 0000000 # -*- 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/ 0000775 0000000 0000000 00000000000 12672542167 0014635 5 ustar 00root root 0000000 0000000 libsass-3.3.4/contrib/libsass.spec 0000664 0000000 0000000 00000002322 12672542167 0017150 0 ustar 00root root 0000000 0000000 Name: 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.cpp 0000664 0000000 0000000 00000004105 12672542167 0016637 0 ustar 00root root 0000000 0000000 #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/ 0000775 0000000 0000000 00000000000 12672542167 0014125 5 ustar 00root root 0000000 0000000 libsass-3.3.4/docs/README.md 0000664 0000000 0000000 00000003074 12672542167 0015410 0 ustar 00root root 0000000 0000000 Welcome 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.md 0000664 0000000 0000000 00000002136 12672542167 0020335 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000006263 12672542167 0020523 0 ustar 00root root 0000000 0000000 ```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.md 0000664 0000000 0000000 00000022404 12672542167 0016704 0 ustar 00root root 0000000 0000000 Sass 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.md 0000664 0000000 0000000 00000015302 12672542167 0015764 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000004147 12672542167 0020502 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000000244 12672542167 0020655 0 ustar 00root root 0000000 0000000 ```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.md 0000664 0000000 0000000 00000004246 12672542167 0017051 0 ustar 00root root 0000000 0000000 Sass 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.md 0000664 0000000 0000000 00000007055 12672542167 0020517 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000000645 12672542167 0020676 0 ustar 00root root 0000000 0000000 ```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.md 0000664 0000000 0000000 00000010134 12672542167 0017056 0 ustar 00root root 0000000 0000000 By 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.md 0000664 0000000 0000000 00000000000 12672542167 0017751 0 ustar 00root root 0000000 0000000 libsass-3.3.4/docs/api-value-internal.md 0000664 0000000 0000000 00000002371 12672542167 0020147 0 ustar 00root root 0000000 0000000 ```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.md 0000664 0000000 0000000 00000011404 12672542167 0016332 0 ustar 00root root 0000000 0000000 `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.md 0000664 0000000 0000000 00000001430 12672542167 0017440 0 ustar 00root root 0000000 0000000 To 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.md 0000664 0000000 0000000 00000002401 12672542167 0017446 0 ustar 00root root 0000000 0000000 Here 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.md 0000664 0000000 0000000 00000011305 12672542167 0017650 0 ustar 00root root 0000000 0000000 We 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:

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 "mingwm10.dll、 libgcc_s_dw2-1.dll" , 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.md 0000664 0000000 0000000 00000002612 12672542167 0020455 0 ustar 00root root 0000000 0000000 This 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.md 0000664 0000000 0000000 00000003757 12672542167 0020562 0 ustar 00root root 0000000 0000000 ### 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.md 0000664 0000000 0000000 00000003100 12672542167 0020447 0 ustar 00root root 0000000 0000000 ### 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.md 0000664 0000000 0000000 00000006554 12672542167 0017650 0 ustar 00root root 0000000 0000000 ## 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:

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 "mingwm10.dll、 libgcc_s_dw2-1.dll", 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.md 0000664 0000000 0000000 00000005603 12672542167 0021331 0 ustar 00root root 0000000 0000000 ## 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:

`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.md 0000664 0000000 0000000 00000011367 12672542167 0015556 0 ustar 00root root 0000000 0000000 `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]
[](https://travis-ci.org/sass/libsass)
- [LibSass on AppVeyor (windows)][8]
[](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.md 0000664 0000000 0000000 00000005523 12672542167 0020255 0 ustar 00root root 0000000 0000000 This 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.md 0000664 0000000 0000000 00000002575 12672542167 0017167 0 ustar 00root root 0000000 0000000 First 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.md 0000664 0000000 0000000 00000010027 12672542167 0021601 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000002745 12672542167 0017667 0 ustar 00root root 0000000 0000000 There 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.md 0000664 0000000 0000000 00000003573 12672542167 0016140 0 ustar 00root root 0000000 0000000 Plugins 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.md 0000664 0000000 0000000 00000004631 12672542167 0020155 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000004425 12672542167 0020524 0 ustar 00root root 0000000 0000000 This 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.md 0000664 0000000 0000000 00000001574 12672542167 0015554 0 ustar 00root root 0000000 0000000 ## 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.md 0000664 0000000 0000000 00000003355 12672542167 0015730 0 ustar 00root root 0000000 0000000 This 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.md 0000664 0000000 0000000 00000007575 12672542167 0016113 0 ustar 00root root 0000000 0000000 LibSass 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.rb 0000664 0000000 0000000 00000000235 12672542167 0015170 0 ustar 00root root 0000000 0000000 require 'mkmf'
# .. more stuff
#$LIBPATH.push(Config::CONFIG['libdir'])
$CFLAGS << " #{ENV["CFLAGS"]}"
$LIBS << " #{ENV["LIBS"]}"
create_makefile("libsass")
libsass-3.3.4/include/ 0000775 0000000 0000000 00000000000 12672542167 0014620 5 ustar 00root root 0000000 0000000 libsass-3.3.4/include/sass.h 0000664 0000000 0000000 00000000352 12672542167 0015742 0 ustar 00root root 0000000 0000000 #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/ 0000775 0000000 0000000 00000000000 12672542167 0015571 5 ustar 00root root 0000000 0000000 libsass-3.3.4/include/sass/base.h 0000664 0000000 0000000 00000003451 12672542167 0016657 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000021615 12672542167 0017433 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000011675 12672542167 0017764 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000005240 12672542167 0017703 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000014044 12672542167 0017244 0 ustar 00root root 0000000 0000000 #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.h 0000664 0000000 0000000 00000000165 12672542167 0017431 0 ustar 00root root 0000000 0000000 #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.in 0000664 0000000 0000000 00000000202 12672542167 0020026 0 ustar 00root root 0000000 0000000 #ifndef SASS_VERSION_H
#define SASS_VERSION_H
#ifndef LIBSASS_VERSION
#define LIBSASS_VERSION "@PACKAGE_VERSION@"
#endif
#endif
libsass-3.3.4/include/sass2scss.h 0000664 0000000 0000000 00000004726 12672542167 0016731 0 ustar 00root root 0000000 0000000 /**
* 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
#endif libsass-3.3.4/m4/ 0000775 0000000 0000000 00000000000 12672542167 0013515 5 ustar 00root root 0000000 0000000 libsass-3.3.4/m4/.gitkeep 0000664 0000000 0000000 00000000000 12672542167 0015134 0 ustar 00root root 0000000 0000000 libsass-3.3.4/m4/m4-ax_cxx_compile_stdcxx_11.m4 0000664 0000000 0000000 00000013000 12672542167 0021167 0 ustar 00root root 0000000 0000000 # ============================================================================
# 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/ 0000775 0000000 0000000 00000000000 12672542167 0013766 5 ustar 00root root 0000000 0000000 libsass-3.3.4/res/resource.rc 0000664 0000000 0000000 00000001616 12672542167 0016147 0 ustar 00root root 0000000 0000000 #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
END libsass-3.3.4/script/ 0000775 0000000 0000000 00000000000 12672542167 0014501 5 ustar 00root root 0000000 0000000 libsass-3.3.4/script/bootstrap 0000775 0000000 0000000 00000000274 12672542167 0016447 0 ustar 00root root 0000000 0000000 #!/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/branding 0000775 0000000 0000000 00000000537 12672542167 0016220 0 ustar 00root root 0000000 0000000 #! /bin/bash
echo " "
echo " _ ___ ____ ____ _ ____ ____ "
echo "| | |_ _| __ ) ___| / \ / ___/ ___| "
echo "| | | || _ \___ \ / _ \ \___ \___ \ "
echo "| |___ | || |_) |__) / ___ \ ___) |__) |"
echo "|_____|___|____/____/_/ \_\____/____/ "
echo " "
libsass-3.3.4/script/ci-build-libsass 0000775 0000000 0000000 00000006702 12672542167 0017562 0 ustar 00root root 0000000 0000000 #!/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-compiler 0000775 0000000 0000000 00000000066 12672542167 0020300 0 ustar 00root root 0000000 0000000 #!/bin/bash
gem install minitest
gem install minitap
libsass-3.3.4/script/ci-install-deps 0000775 0000000 0000000 00000000531 12672542167 0017416 0 ustar 00root root 0000000 0000000 #!/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-coverage 0000775 0000000 0000000 00000002207 12672542167 0020125 0 ustar 00root root 0000000 0000000 #!/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/spec 0000775 0000000 0000000 00000000072 12672542167 0015360 0 ustar 00root root 0000000 0000000 #!/bin/bash
script/bootstrap
make $MAKE_OPTS test_build
libsass-3.3.4/script/tap-driver 0000775 0000000 0000000 00000046072 12672542167 0016515 0 ustar 00root root 0000000 0000000 #! /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-runner 0000775 0000000 0000000 00000000017 12672542167 0016520 0 ustar 00root root 0000000 0000000 $@ | tapout tap libsass-3.3.4/src/ 0000775 0000000 0000000 00000000000 12672542167 0013764 5 ustar 00root root 0000000 0000000 libsass-3.3.4/src/GNUmakefile.am 0000664 0000000 0000000 00000002373 12672542167 0016437 0 ustar 00root root 0000000 0000000 ACLOCAL_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.cpp 0000664 0000000 0000000 00000203644 12672542167 0015270 0 ustar 00root root 0000000 0000000 #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.hpp 0000664 0000000 0000000 00000246246 12672542167 0015302 0 ustar 00root root 0000000 0000000 #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