From 0bf8fa45005c51a2bbfb14908391aff0d1b36c45 Mon Sep 17 00:00:00 2001 From: Rodolphe PELLOUX-PRAYER Date: Mon, 20 Oct 2014 17:30:28 +0200 Subject: [PATCH 1/2] Use git submodules for importing libsass. --- .editorconfig | 15 - .gitattributes | 2 - .gitmodules | 3 + COPYING | 25 - INSTALL | 1 - LICENSE | 25 - Makefile | 113 -- Makefile.am | 90 -- ast.cpp | 581 --------- ast.hpp | 1492 ---------------------- ast_def_macros.hpp | 16 - ast_factory.hpp | 83 -- ast_fwd_decl.hpp | 67 - backtrace.hpp | 61 - base64vlq.cpp | 43 - base64vlq.hpp | 28 - bind.cpp | 172 --- bind.hpp | 18 - color_names.hpp | 316 ----- configure.ac | 96 -- constants.cpp | 140 --- constants.hpp | 141 --- context.cpp | 454 ------- context.hpp | 112 -- contextualize.cpp | 157 --- contextualize.hpp | 65 - copy_c_str.cpp | 13 - copy_c_str.hpp | 5 - debug.hpp | 39 - environment.hpp | 75 -- error_handling.cpp | 28 - error_handling.hpp | 28 - eval.cpp | 893 ------------- eval.hpp | 78 -- expand.cpp | 352 ------ expand.hpp | 74 -- extconf.rb | 6 - extend.cpp | 1960 ----------------------------- extend.hpp | 49 - file.cpp | 275 ---- file.hpp | 17 - functions.cpp | 1428 --------------------- functions.hpp | 179 --- inspect.cpp | 633 ---------- inspect.hpp | 98 -- kwd_arg_macros.hpp | 23 - libsass | 1 + m4/.gitkeep | 0 mapping.hpp | 17 - memory_manager.hpp | 54 - node.cpp | 251 ---- node.hpp | 122 -- operation.hpp | 143 --- output_compressed.cpp | 369 ------ output_compressed.hpp | 89 -- output_nested.cpp | 268 ---- output_nested.hpp | 103 -- parser.cpp | 1799 -------------------------- parser.hpp | 249 ---- paths.hpp | 69 - position.hpp | 22 - prelexer.cpp | 621 --------- prelexer.hpp | 503 -------- remove_placeholders.cpp | 59 - remove_placeholders.hpp | 43 - sass.cpp | 169 --- sass.h | 150 --- sass2scss.cpp | 781 ------------ sass2scss.h | 89 -- sass_interface.cpp | 278 ---- sass_interface.h | 96 -- sass_util.cpp | 136 -- sass_util.hpp | 259 ---- script/bootstrap | 10 - script/branding | 10 - script/cibuild | 50 - script/coveralls-debug | 32 - script/install-compiler | 17 - script/spec | 5 - setup.py | 13 +- source_map.cpp | 120 -- source_map.hpp | 47 - subset_map.hpp | 145 --- support/libsass.pc.in | 11 - test-driver | 127 -- test/test_node.cpp | 98 -- test/test_paths.cpp | 29 - test/test_selector_difference.cpp | 28 - test/test_specificity.cpp | 28 - test/test_subset_map.cpp | 472 ------- test/test_superselector.cpp | 71 -- test/test_unification.cpp | 33 - to_c.cpp | 57 - to_c.hpp | 46 - to_string.cpp | 29 - to_string.hpp | 32 - token.hpp | 32 - units.cpp | 44 - units.hpp | 11 - utf8.h | 34 - utf8/checked.h | 327 ----- utf8/core.h | 329 ----- utf8/unchecked.h | 228 ---- utf8_string.cpp | 103 -- utf8_string.hpp | 36 - util.cpp | 119 -- util.hpp | 22 - 107 files changed, 14 insertions(+), 20090 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .gitattributes create mode 100644 .gitmodules delete mode 100644 COPYING delete mode 100644 INSTALL delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 Makefile.am delete mode 100644 ast.cpp delete mode 100644 ast.hpp delete mode 100644 ast_def_macros.hpp delete mode 100644 ast_factory.hpp delete mode 100644 ast_fwd_decl.hpp delete mode 100644 backtrace.hpp delete mode 100644 base64vlq.cpp delete mode 100644 base64vlq.hpp delete mode 100644 bind.cpp delete mode 100644 bind.hpp delete mode 100644 color_names.hpp delete mode 100644 configure.ac delete mode 100644 constants.cpp delete mode 100644 constants.hpp delete mode 100644 context.cpp delete mode 100644 context.hpp delete mode 100644 contextualize.cpp delete mode 100644 contextualize.hpp delete mode 100644 copy_c_str.cpp delete mode 100644 copy_c_str.hpp delete mode 100644 debug.hpp delete mode 100644 environment.hpp delete mode 100644 error_handling.cpp delete mode 100644 error_handling.hpp delete mode 100644 eval.cpp delete mode 100644 eval.hpp delete mode 100644 expand.cpp delete mode 100644 expand.hpp delete mode 100644 extconf.rb delete mode 100644 extend.cpp delete mode 100644 extend.hpp delete mode 100644 file.cpp delete mode 100644 file.hpp delete mode 100644 functions.cpp delete mode 100644 functions.hpp delete mode 100644 inspect.cpp delete mode 100644 inspect.hpp delete mode 100644 kwd_arg_macros.hpp create mode 160000 libsass delete mode 100644 m4/.gitkeep delete mode 100644 mapping.hpp delete mode 100644 memory_manager.hpp delete mode 100644 node.cpp delete mode 100644 node.hpp delete mode 100644 operation.hpp delete mode 100644 output_compressed.cpp delete mode 100644 output_compressed.hpp delete mode 100644 output_nested.cpp delete mode 100644 output_nested.hpp delete mode 100644 parser.cpp delete mode 100644 parser.hpp delete mode 100644 paths.hpp delete mode 100644 position.hpp delete mode 100644 prelexer.cpp delete mode 100644 prelexer.hpp delete mode 100644 remove_placeholders.cpp delete mode 100644 remove_placeholders.hpp delete mode 100644 sass.cpp delete mode 100644 sass.h delete mode 100644 sass2scss.cpp delete mode 100644 sass2scss.h delete mode 100644 sass_interface.cpp delete mode 100644 sass_interface.h delete mode 100644 sass_util.cpp delete mode 100644 sass_util.hpp delete mode 100755 script/bootstrap delete mode 100755 script/branding delete mode 100755 script/cibuild delete mode 100755 script/coveralls-debug delete mode 100755 script/install-compiler delete mode 100755 script/spec delete mode 100644 source_map.cpp delete mode 100644 source_map.hpp delete mode 100644 subset_map.hpp delete mode 100644 support/libsass.pc.in delete mode 100755 test-driver delete mode 100644 test/test_node.cpp delete mode 100644 test/test_paths.cpp delete mode 100644 test/test_selector_difference.cpp delete mode 100644 test/test_specificity.cpp delete mode 100644 test/test_subset_map.cpp delete mode 100644 test/test_superselector.cpp delete mode 100644 test/test_unification.cpp delete mode 100644 to_c.cpp delete mode 100644 to_c.hpp delete mode 100644 to_string.cpp delete mode 100644 to_string.hpp delete mode 100644 token.hpp delete mode 100644 units.cpp delete mode 100644 units.hpp delete mode 100644 utf8.h delete mode 100644 utf8/checked.h delete mode 100644 utf8/core.h delete mode 100644 utf8/unchecked.h delete mode 100644 utf8_string.cpp delete mode 100644 utf8_string.hpp delete mode 100644 util.cpp delete mode 100644 util.hpp diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 15beadef..00000000 --- a/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -# 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, Makefile.am}] -indent_style = tab -indent_size = 4 diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe07704..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..3773af86 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libsass"] + path = libsass + url = https://github.com/sass/libsass.git diff --git a/COPYING b/COPYING deleted file mode 100644 index 8639c111..00000000 --- a/COPYING +++ /dev/null @@ -1,25 +0,0 @@ - -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. diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 92e0156b..00000000 --- a/INSTALL +++ /dev/null @@ -1 +0,0 @@ -// Autotools requires us to have this file. Boo. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8639c111..00000000 --- a/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ - -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. diff --git a/Makefile b/Makefile deleted file mode 100644 index 0a4726cb..00000000 --- a/Makefile +++ /dev/null @@ -1,113 +0,0 @@ -CXX ?= g++ -CXXFLAGS = -std=c++11 -Wall -fPIC -O2 $(EXTRA_CFLAGS) -LDFLAGS = -fPIC $(EXTRA_LDFLAGS) - -ifneq (,$(findstring /cygdrive/,$(PATH))) - UNAME := Cygwin -else -ifneq (,$(findstring WINDOWS,$(PATH))) - UNAME := Windows -else - UNAME := $(shell uname -s) -endif -endif - -ifeq ($(UNAME),Darwin) - CXXFLAGS += -stdlib=libc++ -endif - -PREFIX = /usr/local -LIBDIR = $(PREFIX)/lib - -SASS_SASSC_PATH ?= sassc -SASS_SPEC_PATH ?= sass-spec -SASS_SPEC_SPEC_DIR ?= spec -SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc -RUBY_BIN = ruby - -SOURCES = \ - ast.cpp \ - base64vlq.cpp \ - bind.cpp \ - constants.cpp \ - context.cpp \ - contextualize.cpp \ - copy_c_str.cpp \ - error_handling.cpp \ - eval.cpp \ - expand.cpp \ - extend.cpp \ - file.cpp \ - functions.cpp \ - inspect.cpp \ - node.cpp \ - output_compressed.cpp \ - output_nested.cpp \ - parser.cpp \ - prelexer.cpp \ - remove_placeholders.cpp \ - sass.cpp \ - sass_interface.cpp \ - sass_util.cpp \ - sass2scss.cpp \ - source_map.cpp \ - to_c.cpp \ - to_string.cpp \ - units.cpp \ - utf8_string.cpp \ - util.cpp - -OBJECTS = $(SOURCES:.cpp=.o) - -DEBUG_LVL ?= NONE - -all: static - -debug: LDFLAGS := -g -debug: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) -debug: static - -debug-shared: LDFLAGS := -g -debug-shared: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS)) -debug-shared: shared - -static: libsass.a -shared: libsass.so - -libsass.a: $(OBJECTS) - $(AR) rvs $@ $(OBJECTS) - -libsass.so: $(OBJECTS) - $(CXX) -shared $(LDFLAGS) -o $@ $(OBJECTS) - -%.o: %.cpp - $(CXX) $(CXXFLAGS) -c -o $@ $< - -%: %.o libsass.a - $(CXX) $(CXXFLAGS) -o $@ $+ $(LDFLAGS) - -install: libsass.a - mkdir -p $(DESTDIR)$(LIBDIR)/ - install -pm0755 $< $(DESTDIR)$(LIBDIR)/$< - -install-shared: libsass.so - mkdir -p $(DESTDIR)$(LIBDIR)/ - install -pm0755 $< $(DESTDIR)$(LIBDIR)/$< - -$(SASSC_BIN): libsass.a - cd $(SASS_SASSC_PATH) && $(MAKE) - -test: $(SASSC_BIN) libsass.a - $(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) libsass.a - $(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) libsass.a - $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues - -clean: - rm -f $(OBJECTS) *.a *.so - - -.PHONY: all debug debug-shared static shared bin install install-shared clean diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index adf2896d..00000000 --- a/Makefile.am +++ /dev/null @@ -1,90 +0,0 @@ -ACLOCAL_AMFLAGS = -I m4 -AM_CXXFLAGS = -std=c++11 -Wall -fPIC - -if ENABLE_COVERAGE - AM_CXXFLAGS += -O0 --coverage -else - AM_CXXFLAGS += -O2 -endif - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = support/libsass.pc - -lib_LTLIBRARIES = libsass.la -libsass_la_SOURCES = \ - ast.cpp \ - base64vlq.cpp \ - bind.cpp \ - constants.cpp \ - context.cpp \ - contextualize.cpp \ - copy_c_str.cpp \ - error_handling.cpp \ - eval.cpp \ - expand.cpp \ - extend.cpp \ - file.cpp \ - functions.cpp \ - inspect.cpp \ - node.cpp \ - output_compressed.cpp \ - output_nested.cpp \ - parser.cpp \ - prelexer.cpp \ - remove_placeholders.cpp \ - sass.cpp \ - sass_interface.cpp \ - sass_util.cpp \ - sass2scss.cpp \ - source_map.cpp \ - to_c.cpp \ - to_string.cpp \ - units.cpp \ - utf8_string.cpp \ - util.cpp - -libsass_la_LDFLAGS = -no-undefined -version-info 0:0:0 - -include_HEADERS = sass_interface.h sass.h - -if ENABLE_TESTS - -noinst_PROGRAMS = sass-tester - -sass_tester_SOURCES = $(SASS_SASSC_PATH)/sassc.c -sass_tester_LDADD = libsass.la -sass_tester_LDFLAGS = -no-install -if ENABLE_COVERAGE -sass_tester_LDFLAGS += -static -nodist_EXTRA_sass_tester_SOURCES = non-existent-file-to-force-CXX-linking.cxx -endif - -TESTS = \ - $(SASS_SPEC_PATH)/spec/basic \ - $(SASS_SPEC_PATH)/spec/benchmarks \ - $(SASS_SPEC_PATH)/spec/bourbon \ - $(SASS_SPEC_PATH)/spec/libsass \ - $(SASS_SPEC_PATH)/spec/scss \ - $(SASS_SPEC_PATH)/spec/todo - -LOG_COMPILER = $(RUBY) $(SASS_SPEC_PATH)/sass-spec.rb -AM_LOG_FLAGS = -c ./sass-tester - -SASS_SASSC_PATH ?= sassc -SASS_SPEC_PATH ?= sass-spec -SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc -RUBY_BIN = ruby - -$(SASSC_BIN): libsass.la - cd $(SASS_SASSC_PATH) && $(MAKE) - -test: $(SASSC_BIN) libsass.la - $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH) - -test_build: $(SASSC_BIN) libsass.la - $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH) - -test_issues: $(SASSC_BIN) libsass.la - $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues - -endif diff --git a/ast.cpp b/ast.cpp deleted file mode 100644 index 9c90e2d8..00000000 --- a/ast.cpp +++ /dev/null @@ -1,581 +0,0 @@ -#include "ast.hpp" -#include "context.hpp" -#include "to_string.hpp" -#include -#include -#include - -namespace Sass { - using namespace std; - - bool Compound_Selector::operator<(const Compound_Selector& rhs) const - { - To_String to_string; - // ugly - return const_cast(this)->perform(&to_string) < - const_cast(rhs).perform(&to_string); - } - - bool Complex_Selector::operator<(const Complex_Selector& rhs) const - { - To_String to_string; - return const_cast(this)->perform(&to_string) < - const_cast(rhs).perform(&to_string); - } - - bool Complex_Selector::operator==(const Complex_Selector& rhs) const { - // TODO: We have to access the tail directly using tail_ since ADD_PROPERTY doesn't provide a const version. - - const Complex_Selector* pOne = this; - const Complex_Selector* pTwo = &rhs; - - // Consume any empty references at the beginning of the Complex_Selector - if (pOne->combinator() == Complex_Selector::ANCESTOR_OF && pOne->head()->is_empty_reference()) { - pOne = pOne->tail_; - } - if (pTwo->combinator() == Complex_Selector::ANCESTOR_OF && pTwo->head()->is_empty_reference()) { - pTwo = pTwo->tail_; - } - - while (pOne && pTwo) { - if (pOne->combinator() != pTwo->combinator()) { - return false; - } - - if (*(pOne->head()) != *(pTwo->head())) { - return false; - } - - pOne = pOne->tail_; - pTwo = pTwo->tail_; - } - - return pOne == NULL && pTwo == NULL; - } - - 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; - else unified = (*this)[i]->unify_with(unified, ctx); - } - return unified; - } - - bool Simple_Selector::operator==(const Simple_Selector& rhs) const - { - // Compare the string representations for equality. - - // Cast away const here. To_String should take a const object, but it doesn't. - Simple_Selector* pLHS = const_cast(this); - Simple_Selector* pRHS = const_cast(&rhs); - - To_String to_string; - return pLHS->perform(&to_string) == pRHS->perform(&to_string); - } - - bool Simple_Selector::operator<(const Simple_Selector& rhs) const { - // Use the string representation for ordering. - - // Cast away const here. To_String should take a const object, but it doesn't. - Simple_Selector* pLHS = const_cast(this); - Simple_Selector* pRHS = const_cast(&rhs); - - To_String to_string; - return pLHS->perform(&to_string) < pRHS->perform(&to_string); - } - - Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx) - { - To_String to_string; - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { if (perform(&to_string) == (*rhs)[i]->perform(&to_string)) return rhs; } - - // check for pseudo elements because they need to come last - size_t i, L; - bool found = false; - if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector)) - { - for (i = 0, L = rhs->length(); i < L; ++i) - { - if ((typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) && (*rhs)[L-1]->is_pseudo_element()) - { found = true; break; } - } - } - else - { - for (i = 0, L = rhs->length(); i < L; ++i) - { - if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) - { found = true; break; } - } - } - if (!found) - { - Compound_Selector* cpy = new (ctx.mem) Compound_Selector(*rhs); - (*cpy) << this; - return cpy; - } - Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->path(), rhs->position()); - 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; - } - - 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 = new (ctx.mem) Compound_Selector(rhs->path(), rhs->position()); - (*cpy) << this; - return cpy; - } - - // if this is a universal selector and rhs is not empty, just return the rhs - if (name() == "*") - { return new (ctx.mem) Compound_Selector(*rhs); } - - - Simple_Selector* rhs_0 = (*rhs)[0]; - // otherwise, this is a tag name - if (typeid(*rhs_0) == typeid(Type_Selector)) - { - // if rhs is universal, just return this tagname + rhs's qualifiers - if (static_cast(rhs_0)->name() == "*") - { - Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->path(), rhs->position()); - (*cpy) << this; - for (size_t i = 1, L = rhs->length(); i < L; ++i) - { (*cpy) << (*rhs)[i]; } - return cpy; - } - // if rhs is another tag name and it matches this, return rhs - else if (static_cast(rhs_0)->name() == name()) - { return new (ctx.mem) Compound_Selector(*rhs); } - // else the tag names don't match; return nil - else - { return 0; } - } - // else it's a tag name and a bunch of qualifiers -- just append them - Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->path(), rhs->position()); - (*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; - } - } - 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 Compound_Selector::is_superselector_of(Compound_Selector* rhs) - { - To_String to_string; - - Simple_Selector* lbase = base(); - Simple_Selector* rbase = rhs->base(); - - // Check if pseudo-elements are the same between the selectors - - set lpsuedoset, rpsuedoset; - for (size_t i = 0, L = length(); i < L; ++i) - { - if ((*this)[i]->is_pseudo_element()) { - string pseudo((*this)[i]->perform(&to_string)); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - lpsuedoset.insert(pseudo); - } - } - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { - if ((*rhs)[i]->is_pseudo_element()) { - string pseudo((*rhs)[i]->perform(&to_string)); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - rpsuedoset.insert(pseudo); - } - } - if (lpsuedoset != rpsuedoset) { - return false; - } - - // Check the Simple_Selectors - - set lset, rset; - - if (!lbase) // no lbase; just see if the left-hand qualifiers are a subset of the right-hand selector - { - for (size_t i = 0, L = length(); i < L; ++i) - { lset.insert((*this)[i]->perform(&to_string)); } - for (size_t i = 0, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); - } - else { // there's an lbase - for (size_t i = 1, L = length(); i < L; ++i) - { lset.insert((*this)[i]->perform(&to_string)); } - if (rbase) - { - if (lbase->perform(&to_string) != rbase->perform(&to_string)) // if there's an rbase, make sure they match - { return false; } - else // the bases do match, so compare qualifiers - { - for (size_t i = 1, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } - return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); - } - } - } - // catch-all - return false; - } - - bool Compound_Selector::operator==(const Compound_Selector& rhs) const { - To_String to_string; - - // Check if pseudo-elements are the same between the selectors - - set lpsuedoset, rpsuedoset; - for (size_t i = 0, L = length(); i < L; ++i) - { - if ((*this)[i]->is_pseudo_element()) { - string pseudo((*this)[i]->perform(&to_string)); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - lpsuedoset.insert(pseudo); - } - } - for (size_t i = 0, L = rhs.length(); i < L; ++i) - { - if (rhs[i]->is_pseudo_element()) { - string pseudo(rhs[i]->perform(&to_string)); - pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving - rpsuedoset.insert(pseudo); - } - } - if (lpsuedoset != rpsuedoset) { - return false; - } - - // Check the base - - const Simple_Selector* const lbase = base(); - const Simple_Selector* const rbase = rhs.base(); - - if ((lbase && !rbase) || - (!lbase && rbase) || - ((lbase && rbase) && (*lbase != *rbase))) { - return false; - } - - - // Check the rest of the SimpleSelectors - // Use string representations. We can't create a set of Simple_Selector pointers because std::set == std::set is going to call == - // on the pointers to determine equality. I don't know of a way to pass in a comparison object. The one you can specify as part of - // the template type is used for ordering, but not equality. We also can't just put in non-pointer Simple_Selectors because the - // class is intended to be subclassed, and we'd get splicing. - - set lset, rset; - - for (size_t i = 0, L = length(); i < L; ++i) - { lset.insert((*this)[i]->perform(&to_string)); } - for (size_t i = 0, L = rhs.length(); i < L; ++i) - { rset.insert(rhs[i]->perform(&to_string)); } - - return lset == rset; - } - - 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) - { - if (length() != 1) - { return false; } - return base()->is_superselector_of(rhs); - } - - bool Complex_Selector::is_superselector_of(Complex_Selector* rhs) - { - Complex_Selector* lhs = this; - To_String to_string; - // check for selectors with leading or trailing combinators - if (!lhs->head() || !rhs->head()) - { return false; } - Complex_Selector* l_innermost = lhs->innermost(); - if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !l_innermost->tail()) - { return false; } - Complex_Selector* r_innermost = rhs->innermost(); - if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !r_innermost->tail()) - { 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->base()); } - - 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()->is_superselector_of(marker->head())) - { 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() - { - // TODO: make this iterative - if (!tail()) return 1; - return 1 + tail()->length(); - } - - Compound_Selector* Complex_Selector::base() - { - if (!tail()) return head(); - else return tail()->base(); - } - - Complex_Selector* Complex_Selector::context(Context& ctx) - { - if (!tail()) return 0; - if (!head()) return tail()->context(ctx); - return new (ctx.mem) Complex_Selector(path(), position(), combinator(), head(), tail()->context(ctx)); - } - - Complex_Selector* Complex_Selector::innermost() - { - if (!tail()) return this; - else return tail()->innermost(); - } - - Complex_Selector::Combinator Complex_Selector::clear_innermost() - { - Combinator c; - if (!tail() || tail()->length() == 1) - { 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 = new (ctx.mem) Complex_Selector(*this); - if (tail()) cpy->tail(tail()->clone(ctx)); - return cpy; - } - - Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const - { - Complex_Selector* cpy = new (ctx.mem) Complex_Selector(*this); - - if (head()) { - cpy->head(head()->clone(ctx)); - } - - if (tail()) { - cpy->tail(tail()->cloneFully(ctx)); - } - - return cpy; - } - - Compound_Selector* Compound_Selector::clone(Context& ctx) const - { - Compound_Selector* cpy = new (ctx.mem) Compound_Selector(*this); - return cpy; - } - - - - Selector_Placeholder* Selector::find_placeholder() - { - return 0; - } - - void Selector_List::adjust_after_pushing(Complex_Selector* c) - { - if (c->has_reference()) has_reference(true); - if (c->has_placeholder()) has_placeholder(true); - -#ifdef DEBUG - To_String to_string; - this->mCachedSelector(this->perform(&to_string)); -#endif - } - - Selector_Placeholder* Selector_List::find_placeholder() - { - if (has_placeholder()) { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder(); - } - } - return 0; - } - - Selector_Placeholder* Complex_Selector::find_placeholder() - { - if (has_placeholder()) { - if (head() && head()->has_placeholder()) return head()->find_placeholder(); - else if (tail() && tail()->has_placeholder()) return tail()->find_placeholder(); - } - return 0; - } - - Selector_Placeholder* Compound_Selector::find_placeholder() - { - if (has_placeholder()) { - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder(); - } - // return this; - } - return 0; - } - - Selector_Placeholder* Selector_Placeholder::find_placeholder() - { - return this; - } - - vector Compound_Selector::to_str_vec() - { - To_String to_string; - vector result; - result.reserve(length()); - for (size_t i = 0, L = length(); i < L; ++i) - { result.push_back((*this)[i]->perform(&to_string)); } - return result; - } - - Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx) - { - To_String to_string; - Compound_Selector* result = new (ctx.mem) Compound_Selector(path(), position()); - - // not very efficient because it needs to preserve order - for (size_t i = 0, L = length(); i < L; ++i) - { - bool found = false; - string thisSelector((*this)[i]->perform(&to_string)); - for (size_t j = 0, M = rhs->length(); j < M; ++j) - { - if (thisSelector == (*rhs)[j]->perform(&to_string)) - { - found = true; - break; - } - } - if (!found) (*result) << (*this)[i]; - } - - return result; - } - - void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx) - { - for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) { - this->sources_.insert((*iterator)->clone(ctx)); - } - } - - vector Complex_Selector::to_vector() - { - vector result; - Compound_Selector* h = head(); - Complex_Selector* t = tail(); - if (h) result.push_back(h); - while (t) - { - h = t->head(); - t = t->tail(); - if (h) result.push_back(h); - } - return result; - } - -} - diff --git a/ast.hpp b/ast.hpp deleted file mode 100644 index 3606bb9d..00000000 --- a/ast.hpp +++ /dev/null @@ -1,1492 +0,0 @@ -#define SASS_AST - -#include -#include -#include -#include -#include -#include - -#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 - -#ifndef SASS_CONSTANTS -#include "constants.hpp" -#endif - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -#ifndef SASS_TOKEN -#include "token.hpp" -#endif - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#ifndef SASS -#include "sass.h" -#endif - -#include "units.hpp" - -#ifndef SASS_ERROR_HANDLING -#include "error_handling.hpp" -#endif - -#include "ast_def_macros.hpp" - -#include -#include -#include - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -namespace Sass { - using namespace std; - - ///////////////////////////////////////////////////////////////////////////// - // 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 { - vector elements_; - protected: - virtual void adjust_after_pushing(T element) { } - public: - Vectorized(size_t s = 0) : elements_(vector()) - { elements_.reserve(s); } - virtual ~Vectorized() = 0; - size_t length() const { return elements_.size(); } - bool empty() const { return elements_.empty(); } - T& operator[](size_t i) { return elements_[i]; } - const T& operator[](size_t i) const { return elements_[i]; } - Vectorized& operator<<(T element) - { - 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; - } - vector& elements() { return elements_; } - const vector& elements() const { return elements_; } - vector& elements(vector& e) { elements_ = e; return elements_; } - }; - template - inline Vectorized::~Vectorized() { } - - ////////////////////////////////////////////////////////// - // Abstract base class for all abstract syntax tree nodes. - ////////////////////////////////////////////////////////// - class Block; - class Statement; - class Expression; - class Selector; - class AST_Node { - ADD_PROPERTY(string, path); - ADD_PROPERTY(Position, position); - public: - AST_Node(string path, Position position) : path_(path), position_(position) { } - virtual ~AST_Node() = 0; - // virtual Block* block() { return 0; } - ATTACH_OPERATIONS(); - }; - inline AST_Node::~AST_Node() { } - - ///////////////////////////////////////////////////////////////////////// - // 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: - Statement(string path, Position position) : AST_Node(path, position) { } - virtual ~Statement() = 0; - // needed for rearranging nested rulesets during CSS emission - virtual bool is_hoistable() { return false; } - virtual Block* block() { return 0; } - }; - inline Statement::~Statement() { } - - //////////////////////// - // Blocks of statements. - //////////////////////// - class Block : public Statement, public Vectorized { - ADD_PROPERTY(bool, is_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(string path, Position position, size_t s = 0, bool r = false) - : Statement(path, position), - Vectorized(s), - is_root_(r), has_hoistable_(false), has_non_hoistable_(false) - { } - 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(string path, Position position, Block* b) - : Statement(path, position), block_(b) - { } - 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 Selector; - class Ruleset : public Has_Block { - ADD_PROPERTY(Selector*, selector); - public: - Ruleset(string path, Position position, Selector* s, Block* b) - : Has_Block(path, position, b), selector_(s) - { } - // 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 String; - class Propset : public Has_Block { - ADD_PROPERTY(String*, property_fragment); - public: - Propset(string path, Position position, String* pf, Block* b = 0) - : Has_Block(path, position, b), property_fragment_(pf) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////// - // Media queries. - ///////////////// - class List; - class Media_Block : public Has_Block { - ADD_PROPERTY(List*, media_queries); - ADD_PROPERTY(Selector*, selector); - public: - Media_Block(string path, Position position, List* mqs, Block* b) - : Has_Block(path, position, b), media_queries_(mqs), selector_(0) - { } - bool is_hoistable() { return true; } - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////////////////////////////////////// - // At-rules -- arbitrary directives beginning with "@" that may have an - // optional statement block. - /////////////////////////////////////////////////////////////////////// - class At_Rule : public Has_Block { - ADD_PROPERTY(string, keyword); - ADD_PROPERTY(Selector*, selector); - ADD_PROPERTY(Expression*, value); - public: - At_Rule(string path, Position position, string kwd, Selector* sel = 0, Block* b = 0) - : Has_Block(path, position, b), keyword_(kwd), selector_(sel), value_(0) // set value manually if needed - { } - 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); - public: - Declaration(string path, Position position, - String* prop, Expression* val, bool i = false) - : Statement(path, position), property_(prop), value_(val), is_important_(i) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////// - // Assignments -- variable and value. - ///////////////////////////////////// - class Variable; - class Expression; - class Assignment : public Statement { - ADD_PROPERTY(string, variable); - ADD_PROPERTY(Expression*, value); - ADD_PROPERTY(bool, is_guarded); - ADD_PROPERTY(bool, is_global); - public: - Assignment(string path, Position position, - string var, Expression* val, - bool guarded = false, - bool global = false) - : Statement(path, position), variable_(var), value_(val), is_guarded_(guarded), is_global_(global) - { } - 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 { - vector files_; - vector urls_; - public: - Import(string path, Position position) - : Statement(path, position), - files_(vector()), urls_(vector()) - { } - vector& files() { return files_; } - vector& urls() { return urls_; } - ATTACH_OPERATIONS(); - }; - - class Import_Stub : public Statement { - ADD_PROPERTY(string, file_name); - public: - Import_Stub(string path, Position position, string f) - : Statement(path, position), file_name_(f) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////// - // The Sass `@warn` directive. - ////////////////////////////// - class Warning : public Statement { - ADD_PROPERTY(Expression*, message); - public: - Warning(string path, Position position, Expression* msg) - : Statement(path, position), message_(msg) - { } - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////////// - // CSS comments. These may be interpolated. - /////////////////////////////////////////// - class Comment : public Statement { - ADD_PROPERTY(String*, text); - public: - Comment(string path, Position position, String* txt) - : Statement(path, position), text_(txt) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////// - // The Sass `@if` control directive. - //////////////////////////////////// - class If : public Statement { - ADD_PROPERTY(Expression*, predicate); - ADD_PROPERTY(Block*, consequent); - ADD_PROPERTY(Block*, alternative); - public: - If(string path, Position position, Expression* pred, Block* con, Block* alt = 0) - : Statement(path, position), predicate_(pred), consequent_(con), alternative_(alt) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////// - // The Sass `@for` control directive. - ///////////////////////////////////// - class For : public Has_Block { - ADD_PROPERTY(string, variable); - ADD_PROPERTY(Expression*, lower_bound); - ADD_PROPERTY(Expression*, upper_bound); - ADD_PROPERTY(bool, is_inclusive); - public: - For(string path, Position position, - string var, Expression* lo, Expression* hi, Block* b, bool inc) - : Has_Block(path, position, b), - variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////////////// - // The Sass `@each` control directive. - ////////////////////////////////////// - class Each : public Has_Block { - ADD_PROPERTY(string, variable); - ADD_PROPERTY(Expression*, list); - public: - Each(string path, Position position, string var, Expression* lst, Block* b) - : Has_Block(path, position, b), variable_(var), list_(lst) - { } - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////// - // The Sass `@while` control directive. - /////////////////////////////////////// - class While : public Has_Block { - ADD_PROPERTY(Expression*, predicate); - public: - While(string path, Position position, Expression* pred, Block* b) - : Has_Block(path, position, b), predicate_(pred) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////////////////// - // The @return directive for use inside SassScript functions. - ///////////////////////////////////////////////////////////// - class Return : public Statement { - ADD_PROPERTY(Expression*, value); - public: - Return(string path, Position position, Expression* val) - : Statement(path, position), value_(val) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////// - // The Sass `@extend` directive. - //////////////////////////////// - class Extension : public Statement { - ADD_PROPERTY(Selector*, selector); - public: - Extension(string path, Position position, Selector* s) - : Statement(path, position), selector_(s) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////////////////////////////////// - // Definitions for both mixins and functions. The two cases are distinguished - // by a type tag. - ///////////////////////////////////////////////////////////////////////////// - struct Context; - struct Backtrace; - class Parameters; - typedef Environment Env; - typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, const string&, Position, Backtrace*); - typedef const char* Signature; - class Definition : public Has_Block { - public: - enum Type { MIXIN, FUNCTION }; - ADD_PROPERTY(string, name); - ADD_PROPERTY(Parameters*, parameters); - ADD_PROPERTY(Env*, environment); - ADD_PROPERTY(Type, type); - ADD_PROPERTY(Native_Function, native_function); - ADD_PROPERTY(Sass_C_Function, c_function); - ADD_PROPERTY(void*, cookie); - ADD_PROPERTY(bool, is_overload_stub); - ADD_PROPERTY(Signature, signature); - public: - Definition(string path, - Position position, - string n, - Parameters* params, - Block* b, - Type t) - : Has_Block(path, position, b), - name_(n), - parameters_(params), - environment_(0), - type_(t), - native_function_(0), - c_function_(0), - cookie_(0), - is_overload_stub_(false), - signature_(0) - { } - Definition(string path, - Position position, - Signature sig, - string n, - Parameters* params, - Native_Function func_ptr, - bool overload_stub = false) - : Has_Block(path, position, 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(string path, - Position position, - Signature sig, - string n, - Parameters* params, - Sass_C_Function func_ptr, - void* cookie, - bool whatever, - bool whatever2) - : Has_Block(path, position, 0), - name_(n), - parameters_(params), - environment_(0), - type_(FUNCTION), - native_function_(0), - c_function_(func_ptr), - cookie_(cookie), - is_overload_stub_(false), - signature_(sig) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////////////// - // Mixin calls (i.e., `@include ...`). - ////////////////////////////////////// - class Arguments; - class Mixin_Call : public Has_Block { - ADD_PROPERTY(string, name); - ADD_PROPERTY(Arguments*, arguments); - public: - Mixin_Call(string path, Position position, string n, Arguments* args, Block* b = 0) - : Has_Block(path, position, b), name_(n), arguments_(args) - { } - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////////////////// - // The @content directive for mixin content blocks. - /////////////////////////////////////////////////// - class Content : public Statement { - public: - Content(string path, Position position) : Statement(path, position) { } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////////////////////////////////////////////// - // 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, - NULL_VAL, - NUM_TYPES - }; - private: - // expressions in some contexts shouldn't be evaluated - ADD_PROPERTY(bool, is_delayed); - ADD_PROPERTY(bool, is_interpolant); - ADD_PROPERTY(Concrete_Type, concrete_type); - public: - Expression(string path, Position position, - bool d = false, bool i = false, Concrete_Type ct = NONE) - : AST_Node(path, position), - is_delayed_(d), is_interpolant_(i), concrete_type_(ct) - { } - virtual operator bool() { return true; } - virtual ~Expression() = 0; - virtual string type() { return ""; /* TODO: raise an error? */ } - virtual bool is_invisible() { return false; } - static string type_name() { return ""; } - virtual bool is_false() { return false; } - }; - inline Expression::~Expression() { } - - /////////////////////////////////////////////////////////////////////// - // Lists of values, both comma- and space-separated (distinguished by a - // type-tag.) Also used to represent variable-length argument lists. - /////////////////////////////////////////////////////////////////////// - class List : public Expression, public Vectorized { - public: - enum Separator { SPACE, COMMA }; - private: - ADD_PROPERTY(Separator, separator); - ADD_PROPERTY(bool, is_arglist); - public: - List(string path, Position position, - size_t size = 0, Separator sep = SPACE, bool argl = false) - : Expression(path, position), - Vectorized(size), - separator_(sep), is_arglist_(argl) - { concrete_type(LIST); } - string type() { return is_arglist_ ? "arglist" : "list"; } - static string type_name() { return "list"; } - bool is_invisible() { return !length(); } - Expression* value_at_index(size_t i); - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////////////////////////////////////// - // Key value paris. - /////////////////////////////////////////////////////////////////////// - class KeyValuePair : public AST_Node { - ADD_PROPERTY(Expression*, key); - ADD_PROPERTY(Expression*, value); - public: - KeyValuePair(string p, Position pos, - Expression* key = 0, Expression* value = 0) - : AST_Node(p, pos), key_(key), value_(value) - { - } - ATTACH_OPERATIONS(); - }; - - class Map : public Expression, public Vectorized { - public: - Map(string path, Position position, - size_t size = 0) - : Expression(path, position), - Vectorized(size) - { concrete_type(MAP); } - string type() { return "map"; } - static string type_name() { return "map"; } - bool is_invisible() { return !length(); } - Expression* value_at_index(size_t i); - ATTACH_OPERATIONS(); - }; - - - - ////////////////////////////////////////////////////////////////////////// - // Binary expressions. Represents logical, relational, and arithmetic - // operations. Templatized to avoid large switch statements and repetitive - // subclassing. - ////////////////////////////////////////////////////////////////////////// - class Binary_Expression : public Expression { - public: - enum Type { - 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 - }; - private: - ADD_PROPERTY(Type, type); - ADD_PROPERTY(Expression*, left); - ADD_PROPERTY(Expression*, right); - public: - Binary_Expression(string path, Position position, - Type t, Expression* lhs, Expression* rhs) - : Expression(path, position), type_(t), left_(lhs), right_(rhs) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////////////////////////////// - // Arithmetic negation (logical negation is just an ordinary function call). - //////////////////////////////////////////////////////////////////////////// - class Unary_Expression : public Expression { - public: - enum Type { PLUS, MINUS }; - private: - ADD_PROPERTY(Type, type); - ADD_PROPERTY(Expression*, operand); - public: - Unary_Expression(string path, Position position, Type t, Expression* o) - : Expression(path, position), type_(t), operand_(o) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////// - // Function calls. - ////////////////// - class Function_Call : public Expression { - ADD_PROPERTY(string, name); - ADD_PROPERTY(Arguments*, arguments); - ADD_PROPERTY(void*, cookie); - public: - Function_Call(string path, Position position, string n, Arguments* args, void* cookie) - : Expression(path, position), name_(n), arguments_(args), cookie_(cookie) - { concrete_type(STRING); } - Function_Call(string path, Position position, string n, Arguments* args) - : Expression(path, position), name_(n), arguments_(args), cookie_(0) - { concrete_type(STRING); } - ATTACH_OPERATIONS(); - }; - - ///////////////////////// - // Function call schemas. - ///////////////////////// - class Function_Call_Schema : public Expression { - ADD_PROPERTY(String*, name); - ADD_PROPERTY(Arguments*, arguments); - public: - Function_Call_Schema(string path, Position position, String* n, Arguments* args) - : Expression(path, position), name_(n), arguments_(args) - { concrete_type(STRING); } - ATTACH_OPERATIONS(); - }; - - /////////////////////// - // Variable references. - /////////////////////// - class Variable : public Expression { - ADD_PROPERTY(string, name); - public: - Variable(string path, Position position, string n) - : Expression(path, position), name_(n) - { } - 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_PROPERTY(Type, type); - ADD_PROPERTY(string, value); - public: - Textual(string path, Position position, Type t, string val) - : Expression(path, position, true), type_(t), value_(val) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////// - // Numbers, percentages, dimensions, and colors. - //////////////////////////////////////////////// - class Number : public Expression { - ADD_PROPERTY(double, value); - vector numerator_units_; - vector denominator_units_; - public: - Number(string path, Position position, double val, string u = "") - : Expression(path, position), - value_(val), - numerator_units_(vector()), - denominator_units_(vector()) - { - if (!u.empty()) numerator_units_.push_back(u); - concrete_type(NUMBER); - } - vector& numerator_units() { return numerator_units_; } - vector& denominator_units() { return denominator_units_; } - string type() { return "number"; } - static string type_name() { return "number"; } - string unit() - { - stringstream 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.str(); - } - bool is_unitless() - { return numerator_units_.empty() && denominator_units_.empty(); } - void normalize(string to = "") - { - // (multiple passes because I'm too tired to think up something clever) - // Find a unit to convert everything to, if one isn't provided. - if (to.empty()) { - for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { - string u(numerator_units_[i]); - if (string_to_unit(u) == INCOMMENSURABLE) { - continue; - } - else { - to = u; - break; - } - } - } - if (to.empty()) { - for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { - string u(denominator_units_[i]); - if (string_to_unit(u) == INCOMMENSURABLE) { - continue; - } - else { - to = u; - break; - } - } - } - // Now loop through again and do all the conversions. - for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { - string from(numerator_units_[i]); - if (string_to_unit(from) == INCOMMENSURABLE) continue; - value_ *= conversion_factor(from, to); - numerator_units_[i] = to; - } - for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { - string from(denominator_units_[i]); - if (string_to_unit(from) == INCOMMENSURABLE) continue; - value_ /= conversion_factor(from, to); - denominator_units_[i] = to; - } - // Now divide out identical units in the numerator and denominator. - vector ncopy; - ncopy.reserve(numerator_units_.size()); - for (vector::iterator n = numerator_units_.begin(); - n != numerator_units_.end(); - ++n) { - vector::iterator d = find(denominator_units_.begin(), - denominator_units_.end(), - *n); - if (d != denominator_units_.end()) { - denominator_units_.erase(d); - } - else { - ncopy.push_back(*n); - } - } - numerator_units_ = ncopy; - // Sort the units to make them pretty and, well, normal. - sort(numerator_units_.begin(), numerator_units_.end()); - sort(denominator_units_.begin(), denominator_units_.end()); - } - // useful for making one number compatible with another - string find_convertible_unit() - { - for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) { - string u(numerator_units_[i]); - if (string_to_unit(u) != INCOMMENSURABLE) return u; - } - for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) { - string u(denominator_units_[i]); - if (string_to_unit(u) != INCOMMENSURABLE) return u; - } - return string(); - } - ATTACH_OPERATIONS(); - }; - - ////////// - // Colors. - ////////// - class Color : public Expression { - ADD_PROPERTY(double, r); - ADD_PROPERTY(double, g); - ADD_PROPERTY(double, b); - ADD_PROPERTY(double, a); - ADD_PROPERTY(string, disp); - public: - Color(string path, Position position, double r, double g, double b, double a = 1, const string disp = "") - : Expression(path, position), r_(r), g_(g), b_(b), a_(a), disp_(disp) - { concrete_type(COLOR); } - string type() { return "color"; } - static string type_name() { return "color"; } - ATTACH_OPERATIONS(); - }; - - //////////// - // Booleans. - //////////// - class Boolean : public Expression { - ADD_PROPERTY(bool, value); - public: - Boolean(string path, Position position, bool val) : Expression(path, position), value_(val) - { concrete_type(BOOLEAN); } - virtual operator bool() { return value_; } - string type() { return "bool"; } - static string type_name() { return "bool"; } - virtual bool is_false() { return !value_; } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////////////////////////// - // Abstract base class for Sass string values. Includes interpolated and - // "flat" strings. - //////////////////////////////////////////////////////////////////////// - class String : public Expression { - ADD_PROPERTY(bool, needs_unquoting); - public: - String(string path, Position position, bool unq = false, bool delayed = false) - : Expression(path, position, delayed), needs_unquoting_(unq) - { concrete_type(STRING); } - static string type_name() { return "string"; } - virtual ~String() = 0; - ATTACH_OPERATIONS(); - }; - inline String::~String() { }; - - /////////////////////////////////////////////////////////////////////// - // Interpolated strings. Meant to be reduced to flat strings during the - // evaluation phase. - /////////////////////////////////////////////////////////////////////// - class String_Schema : public String, public Vectorized { - ADD_PROPERTY(char, quote_mark); - public: - String_Schema(string path, Position position, size_t size = 0, bool unq = false, char qm = '\0') - : String(path, position, unq), Vectorized(size), quote_mark_(qm) - { } - string type() { return "string"; } - static string type_name() { return "string"; } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////////// - // Flat strings -- the lowest level of raw textual data. - //////////////////////////////////////////////////////// - class String_Constant : public String { - ADD_PROPERTY(string, value); - public: - String_Constant(string path, Position position, string val, bool unq = false) - : String(path, position, unq, true), value_(val) - { } - String_Constant(string path, Position position, const char* beg, bool unq = false) - : String(path, position, unq, true), value_(string(beg)) - { } - String_Constant(string path, Position position, const char* beg, const char* end, bool unq = false) - : String(path, position, unq, true), value_(string(beg, end-beg)) - { } - String_Constant(string path, Position position, const Token& tok, bool unq = false) - : String(path, position, unq, true), value_(string(tok.begin, tok.end)) - { } - string type() { return "string"; } - static string type_name() { return "string"; } - bool is_quoted() { return value_.length() && (value_[0] == '"' || value_[0] == '\''); } - char quote_mark() { return is_quoted() ? value_[0] : '\0'; } - ATTACH_OPERATIONS(); - }; - - ///////////////// - // Media queries. - ///////////////// - class Media_Query : public Expression, - public Vectorized { - ADD_PROPERTY(String*, media_type); - ADD_PROPERTY(bool, is_negated); - ADD_PROPERTY(bool, is_restricted); - public: - Media_Query(string path, Position position, - String* t = 0, size_t s = 0, bool n = false, bool r = false) - : Expression(path, position), Vectorized(s), - media_type_(t), is_negated_(n), is_restricted_(r) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////// - // Media expressions (for use inside media queries). - //////////////////////////////////////////////////// - class Media_Query_Expression : public Expression { - ADD_PROPERTY(Expression*, feature); - ADD_PROPERTY(Expression*, value); - ADD_PROPERTY(bool, is_interpolated); - public: - Media_Query_Expression(string path, Position position, - Expression* f, Expression* v, bool i = false) - : Expression(path, position), feature_(f), value_(v), is_interpolated_(i) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////// - // The null value. - ////////////////// - class Null : public Expression { - public: - Null(string path, Position position) : Expression(path, position) { concrete_type(NULL_VAL); } - string type() { return "null"; } - static string type_name() { return "null"; } - bool is_invisible() { return true; } - operator bool() { return false; } - bool is_false() { return true; } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////// - // Thunks for delayed evaluation. - ///////////////////////////////// - class Thunk : public Expression { - ADD_PROPERTY(Expression*, expression); - ADD_PROPERTY(Env*, environment); - public: - Thunk(string path, Position position, Expression* exp, Env* env = 0) - : Expression(path, position), expression_(exp), environment_(env) - { } - }; - - ///////////////////////////////////////////////////////// - // Individual parameter objects for mixins and functions. - ///////////////////////////////////////////////////////// - class Parameter : public AST_Node { - ADD_PROPERTY(string, name); - ADD_PROPERTY(Expression*, default_value); - ADD_PROPERTY(bool, is_rest_parameter); - public: - Parameter(string p, Position pos, - string n, Expression* def = 0, bool rest = false) - : AST_Node(p, pos), name_(n), default_value_(def), is_rest_parameter_(rest) - { - if (default_value_ && is_rest_parameter_) { - error("variable-length parameter may not have a default value", path(), position()); - } - } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////////////////////////////// - // Parameter lists -- in their own class to facilitate context-sensitive - // error checking (e.g., ensuring that all optional parameters follow all - // required parameters). - ///////////////////////////////////////////////////////////////////////// - class Parameters : public AST_Node, public Vectorized { - ADD_PROPERTY(bool, has_optional_parameters); - ADD_PROPERTY(bool, has_rest_parameter); - protected: - void adjust_after_pushing(Parameter* p) - { - if (p->default_value()) { - if (has_rest_parameter_) { - error("optional parameters may not be combined with variable-length parameters", p->path(), p->position()); - } - has_optional_parameters_ = true; - } - else if (p->is_rest_parameter()) { - if (has_rest_parameter_) { - error("functions and mixins cannot have more than one variable-length parameter", p->path(), p->position()); - } - if (has_optional_parameters_) { - error("optional parameters may not be combined with variable-length parameters", p->path(), p->position()); - } - has_rest_parameter_ = true; - } - else { - if (has_rest_parameter_) { - error("required parameters must precede variable-length parameters", p->path(), p->position()); - } - if (has_optional_parameters_) { - error("required parameters must precede optional parameters", p->path(), p->position()); - } - } - } - public: - Parameters(string path, Position position) - : AST_Node(path, position), - Vectorized(), - has_optional_parameters_(false), - has_rest_parameter_(false) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////////////// - // Individual argument objects for mixin and function calls. - //////////////////////////////////////////////////////////// - class Argument : public Expression { - ADD_PROPERTY(Expression*, value); - ADD_PROPERTY(string, name); - ADD_PROPERTY(bool, is_rest_argument); - public: - Argument(string p, Position pos, Expression* val, string n = "", bool rest = false) - : Expression(p, pos), value_(val), name_(n), is_rest_argument_(rest) - { - if (!name_.empty() && is_rest_argument_) { - error("variable-length argument may not be passed by name", path(), position()); - } - } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////////////////////////////////////////////////////////////////// - // Additional method on Lists to retrieve values directly or from an encompassed Argument. - ////////////////////////////////////////////////////////////////////////////////////////// - inline Expression* List::value_at_index(size_t i) { return is_arglist_ ? ((Argument*)(*this)[i])->value() : (*this)[i]; } - - - ////////////////////////////////////////////////////////////////////////////////////////// - // Additional method on Maps to retrieve values directly. - ////////////////////////////////////////////////////////////////////////////////////////// - inline Expression* Map::value_at_index(size_t i) { return (*this)[i]->value(); } - - //////////////////////////////////////////////////////////////////////// - // 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); - protected: - void adjust_after_pushing(Argument* a) - { - if (!a->name().empty()) { - if (has_rest_argument_) { - error("named arguments must precede variable-length argument", a->path(), a->position()); - } - 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->path(), a->position()); - } - if (has_named_arguments_) { - error("functions and mixins may not be called with both named arguments and variable-length arguments", a->path(), a->position()); - } - has_rest_argument_ = true; - } - else { - if (has_rest_argument_) { - error("ordinal arguments must precede variable-length arguments", a->path(), a->position()); - } - if (has_named_arguments_) { - error("ordinal arguments must precede named arguments", a->path(), a->position()); - } - } - } - public: - Arguments(string path, Position position) - : Expression(path, position), - Vectorized(), - has_named_arguments_(false), - has_rest_argument_(false) - { } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////// - // Abstract base class for CSS selectors. - ///////////////////////////////////////// - class Selector : public AST_Node { - ADD_PROPERTY(bool, has_reference); - ADD_PROPERTY(bool, has_placeholder); - public: - Selector(string path, Position position, bool r = false, bool h = false) - : AST_Node(path, position), has_reference_(r), has_placeholder_(h) - { } - virtual ~Selector() = 0; - virtual Selector_Placeholder* find_placeholder(); - virtual int specificity() { return Constants::SPECIFICITY_BASE; } - }; - inline Selector::~Selector() { } - - ///////////////////////////////////////////////////////////////////////// - // Interpolated selectors -- the interpolated String will be expanded and - // re-parsed into a normal selector class. - ///////////////////////////////////////////////////////////////////////// - class Selector_Schema : public Selector { - ADD_PROPERTY(String*, contents); - public: - Selector_Schema(string path, Position position, String* c) - : Selector(path, position), contents_(c) - { } - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////// - // Abstract base class for simple selectors. - //////////////////////////////////////////// - class Simple_Selector : public Selector { - public: - Simple_Selector(string path, Position position) - : Selector(path, position) - { } - virtual ~Simple_Selector() = 0; - virtual Compound_Selector* unify_with(Compound_Selector*, Context&); - virtual bool is_pseudo_element() { return false; } - - bool operator==(const Simple_Selector& rhs) const; - inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); } - - bool operator<(const Simple_Selector& rhs) const; - }; - inline Simple_Selector::~Simple_Selector() { } - - ///////////////////////////////////// - // Parent references (i.e., the "&"). - ///////////////////////////////////// - class Selector_Reference : public Simple_Selector { - ADD_PROPERTY(Selector*, selector); - public: - Selector_Reference(string path, Position position, Selector* r = 0) - : Simple_Selector(path, position), selector_(r) - { has_reference(true); } - virtual int specificity() - { - if (selector()) return selector()->specificity(); - else return 0; - } - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////////////////////////////// - // Placeholder selectors (e.g., "%foo") for use in extend-only selectors. - ///////////////////////////////////////////////////////////////////////// - class Selector_Placeholder : public Simple_Selector { - ADD_PROPERTY(string, name); - public: - Selector_Placeholder(string path, Position position, string n) - : Simple_Selector(path, position), name_(n) - { has_placeholder(true); } - virtual Selector_Placeholder* find_placeholder(); - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////////////////////////// - // Type selectors (and the universal selector) -- e.g., div, span, *. - ///////////////////////////////////////////////////////////////////// - class Type_Selector : public Simple_Selector { - ADD_PROPERTY(string, name); - public: - Type_Selector(string path, Position position, string n) - : Simple_Selector(path, position), name_(n) - { } - virtual int specificity() - { - if (name() == "*") return 0; - else return 1; - } - virtual Compound_Selector* unify_with(Compound_Selector*, Context&); - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////// - // Selector qualifiers -- i.e., classes and ids. - //////////////////////////////////////////////// - class Selector_Qualifier : public Simple_Selector { - ADD_PROPERTY(string, name); - public: - Selector_Qualifier(string path, Position position, string n) - : Simple_Selector(path, position), name_(n) - { } - virtual int specificity() - { - if (name()[0] == '#') return Constants::SPECIFICITY_BASE * Constants::SPECIFICITY_BASE; - else return Constants::SPECIFICITY_BASE; - } - virtual Compound_Selector* unify_with(Compound_Selector*, Context&); - ATTACH_OPERATIONS(); - }; - - /////////////////////////////////////////////////// - // Attribute selectors -- e.g., [src*=".jpg"], etc. - /////////////////////////////////////////////////// - class Attribute_Selector : public Simple_Selector { - ADD_PROPERTY(string, name); - ADD_PROPERTY(string, matcher); - ADD_PROPERTY(String*, value); // might be interpolated - public: - Attribute_Selector(string path, Position position, string n, string m, String* v) - : Simple_Selector(path, position), name_(n), matcher_(m), value_(v) - { } - ATTACH_OPERATIONS(); - }; - - ////////////////////////////////////////////////////////////////// - // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc. - ////////////////////////////////////////////////////////////////// - class Pseudo_Selector : public Simple_Selector { - ADD_PROPERTY(string, name); - ADD_PROPERTY(String*, expression); - public: - Pseudo_Selector(string path, Position position, string n, String* expr = 0) - : Simple_Selector(path, position), name_(n), expression_(expr) - { } - virtual int specificity() - { - // TODO: clean up the pseudo-element checking - if (name() == ":before" || name() == "::before" || - name() == ":after" || name() == "::after" || - name() == ":first-line" || name() == "::first-line" || - name() == ":first-letter" || name() == "::first-letter") - return 1; - else - return Constants::SPECIFICITY_BASE; - } - virtual bool is_pseudo_element() - { - if (name() == ":before" || name() == "::before" || - name() == ":after" || name() == "::after" || - name() == ":first-line" || name() == "::first-line" || - name() == ":first-letter" || name() == "::first-letter") { - return true; - } - else { - // If it's not a known pseudo-element, check whether it looks like one. This is similar to the type method on the Pseudo class in ruby sass. - return name().find("::") == 0; - } - } - virtual Compound_Selector* unify_with(Compound_Selector*, Context&); - ATTACH_OPERATIONS(); - }; - - ///////////////////////////////////////////////// - // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir) - ///////////////////////////////////////////////// - class Wrapped_Selector : public Simple_Selector { - ADD_PROPERTY(string, name); - ADD_PROPERTY(Selector*, selector); - public: - Wrapped_Selector(string path, Position position, string n, Selector* sel) - : Simple_Selector(path, position), name_(n), selector_(sel) - { } - ATTACH_OPERATIONS(); - }; - - struct Complex_Selector_Pointer_Compare { - bool operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const; - }; - - //////////////////////////////////////////////////////////////////////////// - // Simple selector sequences. Maintains flags indicating whether it contains - // any parent references or placeholders, to simplify expansion. - //////////////////////////////////////////////////////////////////////////// - typedef set SourcesSet; - class Compound_Selector : public Selector, public Vectorized { - private: - SourcesSet sources_; - protected: - void adjust_after_pushing(Simple_Selector* s) - { - if (s->has_reference()) has_reference(true); - if (s->has_placeholder()) has_placeholder(true); - } - public: - Compound_Selector(string path, Position position, size_t s = 0) - : Selector(path, position), - Vectorized(s) - { } - - Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx); - virtual Selector_Placeholder* find_placeholder(); - Simple_Selector* base() - { - // Implement non-const in terms of const. Safe to const_cast since this method is non-const - return const_cast(static_cast(this)->base()); - } - const Simple_Selector* base() const { - if (length() > 0 && typeid(*(*this)[0]) == typeid(Type_Selector)) - return (*this)[0]; - return 0; - } - bool is_superselector_of(Compound_Selector* rhs); - virtual int specificity() - { - int sum = 0; - for (size_t i = 0, L = length(); i < L; ++i) - { sum += (*this)[i]->specificity(); } - return sum; - } - bool is_empty_reference() - { - return length() == 1 && - typeid(*(*this)[0]) == typeid(Selector_Reference) && - !static_cast((*this)[0])->selector(); - } - vector to_str_vec(); // sometimes need to convert to a flat "by-value" data structure - - bool operator<(const Compound_Selector& rhs) const; - - bool operator==(const Compound_Selector& rhs) const; - inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); } - - SourcesSet& sources() { return sources_; } - void clearSources() { sources_.clear(); } - void mergeSources(SourcesSet& sources, Context& ctx); - - Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s - - Compound_Selector* minus(Compound_Selector* rhs, Context& ctx); - ATTACH_OPERATIONS(); - }; - - //////////////////////////////////////////////////////////////////////////// - // General selectors -- i.e., simple sequences combined with one of the four - // CSS selector combinators (">", "+", "~", and whitespace). Essentially a - // linked list. - //////////////////////////////////////////////////////////////////////////// - struct Context; - class Complex_Selector : public Selector { - public: - enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO }; - private: - ADD_PROPERTY(Combinator, combinator); - ADD_PROPERTY(Compound_Selector*, head); - ADD_PROPERTY(Complex_Selector*, tail); - public: - Complex_Selector(string path, Position position, - Combinator c, - Compound_Selector* h, - Complex_Selector* t) - : Selector(path, position), combinator_(c), head_(h), tail_(t) - { - if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); - if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); - } - Compound_Selector* base(); - Complex_Selector* context(Context&); - Complex_Selector* innermost(); - size_t length(); - bool is_superselector_of(Compound_Selector*); - bool is_superselector_of(Complex_Selector*); - virtual Selector_Placeholder* find_placeholder(); - Combinator clear_innermost(); - void set_innermost(Complex_Selector*, Combinator); - virtual int specificity() const - { - int sum = 0; - if (head()) sum += head()->specificity(); - if (tail()) sum += tail()->specificity(); - return sum; - } - bool operator<(const Complex_Selector& rhs) const; - bool operator==(const Complex_Selector& rhs) const; - inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); } - SourcesSet sources() - { - //s = Set.new - //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)} - //s - - SourcesSet srcs; - - Compound_Selector* pHead = head(); - Complex_Selector* pTail = tail(); - - if (pHead) { - SourcesSet& headSources = pHead->sources(); - srcs.insert(headSources.begin(), headSources.end()); - } - - if (pTail) { - SourcesSet tailSources = pTail->sources(); - srcs.insert(tailSources.begin(), tailSources.end()); - } - - return srcs; - } - void addSources(SourcesSet& sources, Context& ctx) { - // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m} - Complex_Selector* pIter = this; - while (pIter) { - Compound_Selector* pHead = pIter->head(); - - if (pHead) { - pHead->mergeSources(sources, ctx); - } - - pIter = pIter->tail(); - } - } - void clearSources() { - Complex_Selector* pIter = this; - while (pIter) { - Compound_Selector* pHead = pIter->head(); - - if (pHead) { - pHead->clearSources(); - } - - pIter = pIter->tail(); - } - } - Complex_Selector* clone(Context&) const; // does not clone Compound_Selector*s - Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s - vector to_vector(); - ATTACH_OPERATIONS(); - }; - - typedef deque ComplexSelectorDeque; - - /////////////////////////////////// - // Comma-separated selector groups. - /////////////////////////////////// - class Selector_List - : public Selector, public Vectorized { -#ifdef DEBUG - ADD_PROPERTY(string, mCachedSelector); -#endif - protected: - void adjust_after_pushing(Complex_Selector* c); - public: - Selector_List(string path, Position position, size_t s = 0) - : Selector(path, position), Vectorized(s) - { } - virtual Selector_Placeholder* find_placeholder(); - virtual int specificity() - { - int sum = 0; - for (size_t i = 0, L = length(); i < L; ++i) - { sum += (*this)[i]->specificity(); } - return sum; - } - // vector members() { return elements_; } - ATTACH_OPERATIONS(); - }; - - - template - bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) { - // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector - // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but - // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting - // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the - // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the - // members of a SimpleSequence (Compound_Selector)). - // - // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for - // use with Compound_Selector and Complex_Selector objects. - if (simpleSelectorOrderDependent) { - return !(one < two) && !(two < one); - } else { - return one == two; - } - } - -} - - -#ifdef __clang__ - -#pragma clang diagnostic pop - -#endif diff --git a/ast_def_macros.hpp b/ast_def_macros.hpp deleted file mode 100644 index 663e4c40..00000000 --- a/ast_def_macros.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#define ATTACH_OPERATIONS()\ -virtual void perform(Operation* op) { (*op)(this); }\ -virtual AST_Node* perform(Operation* op) { return (*op)(this); }\ -virtual Statement* perform(Operation* op) { return (*op)(this); }\ -virtual Expression* perform(Operation* op) { return (*op)(this); }\ -virtual Selector* perform(Operation* op) { return (*op)(this); }\ -virtual string perform(Operation* op) { return (*op)(this); }\ -virtual Sass_Value perform(Operation* op) { return (*op)(this); } - -#define ADD_PROPERTY(type, name)\ -protected:\ - type name##_;\ -public:\ - type name() const { return name##_; }\ - type name(type name##__) { return name##_ = name##__; }\ -private: diff --git a/ast_factory.hpp b/ast_factory.hpp deleted file mode 100644 index 7cb7ccc4..00000000 --- a/ast_factory.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#define SASS_AST_FACTORY - -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -namespace Sass { - using namespace std; - - class AST_Factory { - vector nodes; - public: - // statements - Block* new_Block(string p, size_t l, size_t s = 0, bool r = false); - Ruleset* new_Ruleset(string p, size_t l, Selector* s, Block* b); - Propset* new_Propset(string p, size_t l, String* pf, Block* b); - Media_Query* new_Media_Query(string p, size_t l, List* q, Block* b); - At_Rule* new_At_Rule(string p, size_t l, string kwd, Selector* sel, Block* b); - Declaration* new_Declaration(string p, size_t l, String* prop, List* vals); - Assignment* new_Assignment(string p, size_t l, string var, Expression* val, bool guarded = false); - Import* new_CSS_Import(string p, size_t l, Function_Call* loc); - Import* new_SASS_Import(string p, size_t l, String* loc); - Warning* new_Warning(string p, size_t l, Expression* msg); - Comment* new_Comment(string p, size_t l, String* txt); - If* new_If(string p, size_t l, Expression* pred, Block* con, Block* alt = 0); - For* new_For(string p, size_t l, string var, Expression* lo, Expression* hi, Block* b, bool inc); - Each* new_Each(string p, size_t l, string var, Expression* lst, Block* b); - While* new_While(string p, size_t l, Expression* pred, Block* b); - Extension* new_Extension(string p, size_t l, Selector* s); - Definition* new_Mixin_Definition(string p, size_t l, string n, Parameters* params, Block* b); - Definition* new_Function_Definition(string p, size_t l, string n, Parameters* params, Block* b); - Mixin_Call* new_Mixin_Call(string p, size_t l, string n, Arguments* args, Block* b = 0); - // expressions - List* new_List(string p, size_t l, size_t size = 0, List::Separator sep = List::space, bool argl = false); - Map* new_Map(string p, size_t l, size_t size = 0); - Binary_Expression* new_And(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Or(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Eq(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Neq(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Gt(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Gte(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Lt(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Lte(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Add(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Sub(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression* new_Mul(string p, size_t l, Expression* lhs, Expression* rhs); - Binary_Expression
* new_Div(string p, size_t l, Expression* lhs, Expression* rhs); - Negation* new_Negation(string p, size_t l, Expression* o); - Function_Call* new_Function_Call(string p, size_t l, String* n, Arguments* args); - Variable* new_Variable(string p, size_t l, string n); - Textual* new_Textual_Number(string p, size_t l, string val); - Textual* new_Textual_Percentage(string p, size_t l, string val); - Textual* new_Textual_Dimension(string p, size_t l, string val); - Textual* new_Textual_Hex(string p, size_t l, string val); - Number* new_Number(string p, size_t l, double val); - Percentage* new_Percentage(string p, size_t l, double val); - Dimension* new_Dimension(string p, size_t l, double val, string unit); - Color* new_Color(string p, size_t l, double r, double g, double b, double a = 1, string disp = ""); - Boolean* new_Boolean(string p, size_t l, bool val); - String_Schema* new_String_Schema(string p, size_t l, size_t size = 0); - String_Constant* new_String_Constant(string p, size_t l, string val); - String_Constant* new_String_Constant(string p, size_t l, const char* beg); - String_Constant* new_String_Constant(string p, size_t l, const char* beg, const char* end); - Media_Expression* new_Media_Expression(string p, size_t l, String* f, Expression* v); - // parameters and arguments - Parameter* new_Parameter(string p, size_t l, string n, Expression* def = 0, bool rest = false); - Parameters* new_Parameters(string p, size_t l); - Argument* new_Argument(string p, size_t l, Expression* val, string n = "", bool rest = false); - Arguments* new_Arguments(string p, size_t l); - // selectors - Selector_Schema* new_Selector_Schema(string p, size_t l, String* c); - Simple_Selector* new_Simple_Selector(string p, size_t l, string c); - Reference_Selector* new_Reference_Selector(string p, size_t l); - Placeholder_Selector* new_Placeholder_Selector(string p, size_t l, string n); - Pseudo_Selector* new_Pseudo_Selector(string p, size_t l, string n, Expression* expr = 0); - Wrapped_Selector* new_Wrapped_Selector(string p, size_t l, string n, Simple_Base* sel); - Compound_Selector* new_Compound_Selector(string p, size_t l, size_t s = 0); - Complex_Selector* new_Complex_Selector(string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel); - Selector_List* new_Selector_List(string p, size_t l, size_t s = 0); - }; -} diff --git a/ast_fwd_decl.hpp b/ast_fwd_decl.hpp deleted file mode 100644 index 7f2cd8ff..00000000 --- a/ast_fwd_decl.hpp +++ /dev/null @@ -1,67 +0,0 @@ -///////////////////////////////////////////// -// Forward declarations for the AST visitors. -///////////////////////////////////////////// -namespace Sass { - - class AST_Node; - // statements - class Statement; - class Block; - class Ruleset; - class Propset; - class Media_Block; - class At_Rule; - class Declaration; - class Assignment; - class Import; - class Import_Stub; - class Warning; - class Comment; - class If; - class For; - class Each; - class While; - class Return; - class Content; - class Extension; - class Definition; - class Mixin_Call; - // expressions - class Expression; - class List; - class Map; - class Binary_Expression; - class Unary_Expression; - class Function_Call; - class Function_Call_Schema; - class Variable; - class Textual; - class Number; - class Color; - class Boolean; - class String_Schema; - class String; - class String_Constant; - class Media_Query; - class Media_Query_Expression; - class Null; - // parameters and arguments - class Parameter; - class Parameters; - class Argument; - class Arguments; - // selectors - class Selector; - class Selector_Schema; - class Selector_Reference; - class Selector_Placeholder; - class Type_Selector; - class Selector_Qualifier; - class Attribute_Selector; - class Pseudo_Selector; - class Wrapped_Selector; - class Compound_Selector; - class Complex_Selector; - class Selector_List; - -} diff --git a/backtrace.hpp b/backtrace.hpp deleted file mode 100644 index 0f770d8a..00000000 --- a/backtrace.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#define SASS_BACKTRACE - -#include - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -namespace Sass { - - using namespace std; - - struct Backtrace { - - Backtrace* parent; - string path; - Position position; - string caller; - - Backtrace(Backtrace* prn, string pth, Position position, string c) - : parent(prn), - path(pth), - position(position), - caller(c) - { } - - string to_string(bool warning = false) - { - stringstream ss; - Backtrace* this_point = this; - - if (!warning) ss << endl << "Backtrace:"; - // the first tracepoint (which is parent-less) is an empty placeholder - while (this_point->parent) { - ss << endl - << "\t" - << (warning ? " " : "") - << this_point->path - << ":" - << this_point->position.line - << this_point->parent->caller; - this_point = this_point->parent; - } - - return ss.str(); - } - - size_t depth() - { - size_t d = 0; - Backtrace* p = parent; - while (p) { - ++d; - p = p->parent; - } - return d-1; - } - - }; - -} diff --git a/base64vlq.cpp b/base64vlq.cpp deleted file mode 100644 index ef4265e8..00000000 --- a/base64vlq.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "base64vlq.hpp" - -namespace Sass { - - string Base64VLQ::encode(const int number) const - { - string encoded = ""; - - int vlq = to_vlq_signed(number); - - do { - int digit = vlq & VLQ_BASE_MASK; - vlq >>= VLQ_BASE_SHIFT; - if (vlq > 0) { - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64_encode(digit); - } while (vlq > 0); - - return encoded; - } - - char Base64VLQ::base64_encode(const int number) const - { - int index = number; - if (index < 0) index = 0; - if (index > 63) index = 63; - return CHARACTERS[index]; - } - - int Base64VLQ::to_vlq_signed(const int number) const - { - return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0; - } - - const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - const int Base64VLQ::VLQ_BASE_SHIFT = 5; - const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT; - const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1; - const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE; - -} diff --git a/base64vlq.hpp b/base64vlq.hpp deleted file mode 100644 index a02380c1..00000000 --- a/base64vlq.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#define SASS_BASE64VLQ - -#include - -namespace Sass { - using std::string; - - class Base64VLQ { - - public: - - string encode(const int number) const; - - private: - - char base64_encode(const int number) const; - - int to_vlq_signed(const int number) const; - - static const char* CHARACTERS; - - static const int VLQ_BASE_SHIFT; - static const int VLQ_BASE; - static const int VLQ_BASE_MASK; - static const int VLQ_CONTINUATION_BIT; - }; - -} diff --git a/bind.cpp b/bind.cpp deleted file mode 100644 index 5fb84aa8..00000000 --- a/bind.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "bind.hpp" -#include "ast.hpp" -#include "context.hpp" -#include "eval.hpp" -#include -#include -#include -#include "to_string.hpp" - -namespace Sass { - using namespace std; - - void bind(string callee, Parameters* ps, Arguments* as, Context& ctx, Env* env, Eval* eval) - { - map param_map; - - // Set up a map to ensure named arguments refer to actual parameters. Also - // eval each default value left-to-right, wrt env, populating env as we go. - for (size_t i = 0, L = ps->length(); i < L; ++i) { - Parameter* p = (*ps)[i]; - param_map[p->name()] = p; - // if (p->default_value()) { - // env->current_frame()[p->name()] = p->default_value()->perform(eval->with(env)); - // } - } - - // plug in all args; if we have leftover params, deal with it later - size_t ip = 0, LP = ps->length(); - size_t ia = 0, LA = as->length(); - while (ia < LA) { - if (ip >= LP) { - stringstream msg; - msg << callee << " only takes " << LP << " arguments; " - << "given " << LA; - error(msg.str(), as->path(), as->position()); - } - Parameter* p = (*ps)[ip]; - Argument* a = (*as)[ia]; - - // If the current parameter is the rest parameter, process and break the loop - if (p->is_rest_parameter()) { - if (a->is_rest_argument()) { - // rest param and rest arg -- just add one to the other - if (env->current_frame_has(p->name())) { - *static_cast(env->current_frame()[p->name()]) - += static_cast(a->value()); - } - else { - env->current_frame()[p->name()] = a->value(); - } - } else { - - // copy all remaining arguments into the rest parameter, preserving names - List* arglist = new (ctx.mem) List(p->path(), - p->position(), - 0, - List::COMMA, - true); - env->current_frame()[p->name()] = arglist; - while (ia < LA) { - a = (*as)[ia]; - (*arglist) << new (ctx.mem) Argument(a->path(), - a->position(), - a->value(), - a->name(), - false); - ++ia; - } - } - ++ip; - break; - } - - // If the current argument is the rest argument, extract a value for processing - else if (a->is_rest_argument()) { - // normal param and rest arg - List* arglist = static_cast(a->value()); - // empty rest arg - treat all args as default values - if (!arglist->length()) { - break; - } - // otherwise move one of the rest args into the param, converting to argument if necessary - if (arglist->is_arglist()) { - a = static_cast((*arglist)[0]); - } else { - Expression* a_to_convert = (*arglist)[0]; - a = new (ctx.mem) Argument(a_to_convert->path(), a_to_convert->position(), a_to_convert, "", false); - } - arglist->elements().erase(arglist->elements().begin()); - if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { - ++ia; - } - } else { - ++ia; - } - - if (a->name().empty()) { - if (env->current_frame_has(p->name())) { - stringstream msg; - msg << "parameter " << p->name() - << " provided more than once in call to " << callee; - error(msg.str(), a->path(), a->position()); - } - // ordinal arg -- bind it to the next param - env->current_frame()[p->name()] = a->value(); - ++ip; - } - else { - // named arg -- bind it to the appropriately named param - if (!param_map.count(a->name())) { - stringstream msg; - msg << callee << " has no parameter named " << a->name(); - error(msg.str(), a->path(), a->position()); - } - if (param_map[a->name()]->is_rest_parameter()) { - stringstream msg; - msg << "argument " << a->name() << " of " << callee - << "cannot be used as named argument"; - error(msg.str(), a->path(), a->position()); - } - if (env->current_frame_has(a->name())) { - stringstream msg; - msg << "parameter " << p->name() - << "provided more than once in call to " << callee; - error(msg.str(), a->path(), a->position()); - } - env->current_frame()[a->name()] = a->value(); - } - } - - // If we make it here, we're out of args but may have leftover params. - // That's only okay if they have default values, or were already bound by - // named arguments, or if it's a single rest-param. - for (size_t i = ip; i < LP; ++i) { - To_String to_string; - Parameter* leftover = (*ps)[i]; - // cerr << "env for default params:" << endl; - // env->print(); - // cerr << "********" << endl; - if (!env->current_frame_has(leftover->name())) { - if (leftover->is_rest_parameter()) { - env->current_frame()[leftover->name()] = new (ctx.mem) List(leftover->path(), - leftover->position(), - 0, - List::COMMA, - true); - } - else if (leftover->default_value()) { - // make sure to eval the default value in the env that we've been populating - Env* old_env = eval->env; - Backtrace* old_bt = eval->backtrace; - Expression* dv = leftover->default_value()->perform(eval->with(env, eval->backtrace)); - eval->env = old_env; - eval->backtrace = old_bt; - // dv->perform(&to_string); - env->current_frame()[leftover->name()] = dv; - } - else { - // param is unbound and has no default value -- error - stringstream msg; - msg << "required parameter " << leftover->name() - << " is missing in call to " << callee; - error(msg.str(), as->path(), as->position()); - } - } - } - - return; - } - - -} diff --git a/bind.hpp b/bind.hpp deleted file mode 100644 index 8999ff86..00000000 --- a/bind.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#define SASS_BIND - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#include - -namespace Sass { - class AST_Node; - class Parameters; - class Arguments; - struct Context; - class Eval; - typedef Environment Env; - - void bind(string caller, Parameters*, Arguments*, Context&, Env*, Eval*); -} diff --git a/color_names.hpp b/color_names.hpp deleted file mode 100644 index 527be9c5..00000000 --- a/color_names.hpp +++ /dev/null @@ -1,316 +0,0 @@ -#define SASS_COLOR_NAMES - -namespace Sass { - - /* - * Color names are processed in order. For color names with alternate aliases - * (i.e. gray & grey), the canonical name should be listed last, so it takes - * precedence. - */ - const char* color_names[] = - { - "aliceblue", - "antiquewhite", - "aqua", - "aquamarine", - "azure", - "beige", - "bisque", - "black", - "blanchedalmond", - "blue", - "blueviolet", - "brown", - "burlywood", - "cadetblue", - "chartreuse", - "chocolate", - "coral", - "cornflowerblue", - "cornsilk", - "crimson", - "cyan", - "darkblue", - "darkcyan", - "darkgoldenrod", - "darkgrey", - "darkgray", - "darkgreen", - "darkkhaki", - "darkmagenta", - "darkolivegreen", - "darkorange", - "darkorchid", - "darkred", - "darksalmon", - "darkseagreen", - "darkslateblue", - "darkslategrey", - "darkslategray", - "darkturquoise", - "darkviolet", - "deeppink", - "deepskyblue", - "dimgrey", - "dimgray", - "dodgerblue", - "firebrick", - "floralwhite", - "forestgreen", - "fuchsia", - "gainsboro", - "ghostwhite", - "gold", - "goldenrod", - "grey", - "gray", - "green", - "greenyellow", - "honeydew", - "hotpink", - "indianred", - "indigo", - "ivory", - "khaki", - "lavender", - "lavenderblush", - "lawngreen", - "lemonchiffon", - "lightblue", - "lightcoral", - "lightcyan", - "lightgoldenrodyellow", - "lightgrey", - "lightgray", - "lightgreen", - "lightpink", - "lightsalmon", - "lightseagreen", - "lightskyblue", - "lightslategrey", - "lightslategray", - "lightsteelblue", - "lightyellow", - "lime", - "limegreen", - "linen", - "magenta", - "maroon", - "mediumaquamarine", - "mediumblue", - "mediumorchid", - "mediumpurple", - "mediumseagreen", - "mediumslateblue", - "mediumspringgreen", - "mediumturquoise", - "mediumvioletred", - "midnightblue", - "mintcream", - "mistyrose", - "moccasin", - "navajowhite", - "navy", - "oldlace", - "olive", - "olivedrab", - "orange", - "orangered", - "orchid", - "palegoldenrod", - "palegreen", - "paleturquoise", - "palevioletred", - "papayawhip", - "peachpuff", - "peru", - "pink", - "plum", - "powderblue", - "purple", - "red", - "rosybrown", - "royalblue", - "saddlebrown", - "salmon", - "sandybrown", - "seagreen", - "seashell", - "sienna", - "silver", - "skyblue", - "slateblue", - "slategrey", - "slategray", - "snow", - "springgreen", - "steelblue", - "tan", - "teal", - "thistle", - "tomato", - "turquoise", - "violet", - "wheat", - "white", - "whitesmoke", - "yellow", - "yellowgreen", - // sentinel value - 0 - }; - - const double color_values[] = - { - 0xf0, 0xf8, 0xff, - 0xfa, 0xeb, 0xd7, - 0x00, 0xff, 0xff, - 0x7f, 0xff, 0xd4, - 0xf0, 0xff, 0xff, - 0xf5, 0xf5, 0xdc, - 0xff, 0xe4, 0xc4, - 0x00, 0x00, 0x00, - 0xff, 0xeb, 0xcd, - 0x00, 0x00, 0xff, - 0x8a, 0x2b, 0xe2, - 0xa5, 0x2a, 0x2a, - 0xde, 0xb8, 0x87, - 0x5f, 0x9e, 0xa0, - 0x7f, 0xff, 0x00, - 0xd2, 0x69, 0x1e, - 0xff, 0x7f, 0x50, - 0x64, 0x95, 0xed, - 0xff, 0xf8, 0xdc, - 0xdc, 0x14, 0x3c, - 0x00, 0xff, 0xff, - 0x00, 0x00, 0x8b, - 0x00, 0x8b, 0x8b, - 0xb8, 0x86, 0x0b, - 0xa9, 0xa9, 0xa9, - 0xa9, 0xa9, 0xa9, - 0x00, 0x64, 0x00, - 0xbd, 0xb7, 0x6b, - 0x8b, 0x00, 0x8b, - 0x55, 0x6b, 0x2f, - 0xff, 0x8c, 0x00, - 0x99, 0x32, 0xcc, - 0x8b, 0x00, 0x00, - 0xe9, 0x96, 0x7a, - 0x8f, 0xbc, 0x8f, - 0x48, 0x3d, 0x8b, - 0x2f, 0x4f, 0x4f, - 0x2f, 0x4f, 0x4f, - 0x00, 0xce, 0xd1, - 0x94, 0x00, 0xd3, - 0xff, 0x14, 0x93, - 0x00, 0xbf, 0xff, - 0x69, 0x69, 0x69, - 0x69, 0x69, 0x69, - 0x1e, 0x90, 0xff, - 0xb2, 0x22, 0x22, - 0xff, 0xfa, 0xf0, - 0x22, 0x8b, 0x22, - 0xff, 0x00, 0xff, - 0xdc, 0xdc, 0xdc, - 0xf8, 0xf8, 0xff, - 0xff, 0xd7, 0x00, - 0xda, 0xa5, 0x20, - 0x80, 0x80, 0x80, - 0x80, 0x80, 0x80, - 0x00, 0x80, 0x00, - 0xad, 0xff, 0x2f, - 0xf0, 0xff, 0xf0, - 0xff, 0x69, 0xb4, - 0xcd, 0x5c, 0x5c, - 0x4b, 0x00, 0x82, - 0xff, 0xff, 0xf0, - 0xf0, 0xe6, 0x8c, - 0xe6, 0xe6, 0xfa, - 0xff, 0xf0, 0xf5, - 0x7c, 0xfc, 0x00, - 0xff, 0xfa, 0xcd, - 0xad, 0xd8, 0xe6, - 0xf0, 0x80, 0x80, - 0xe0, 0xff, 0xff, - 0xfa, 0xfa, 0xd2, - 0xd3, 0xd3, 0xd3, - 0xd3, 0xd3, 0xd3, - 0x90, 0xee, 0x90, - 0xff, 0xb6, 0xc1, - 0xff, 0xa0, 0x7a, - 0x20, 0xb2, 0xaa, - 0x87, 0xce, 0xfa, - 0x77, 0x88, 0x99, - 0x77, 0x88, 0x99, - 0xb0, 0xc4, 0xde, - 0xff, 0xff, 0xe0, - 0x00, 0xff, 0x00, - 0x32, 0xcd, 0x32, - 0xfa, 0xf0, 0xe6, - 0xff, 0x00, 0xff, - 0x80, 0x00, 0x00, - 0x66, 0xcd, 0xaa, - 0x00, 0x00, 0xcd, - 0xba, 0x55, 0xd3, - 0x93, 0x70, 0xdb, - 0x3c, 0xb3, 0x71, - 0x7b, 0x68, 0xee, - 0x00, 0xfa, 0x9a, - 0x48, 0xd1, 0xcc, - 0xc7, 0x15, 0x85, - 0x19, 0x19, 0x70, - 0xf5, 0xff, 0xfa, - 0xff, 0xe4, 0xe1, - 0xff, 0xe4, 0xb5, - 0xff, 0xde, 0xad, - 0x00, 0x00, 0x80, - 0xfd, 0xf5, 0xe6, - 0x80, 0x80, 0x00, - 0x6b, 0x8e, 0x23, - 0xff, 0xa5, 0x00, - 0xff, 0x45, 0x00, - 0xda, 0x70, 0xd6, - 0xee, 0xe8, 0xaa, - 0x98, 0xfb, 0x98, - 0xaf, 0xee, 0xee, - 0xdb, 0x70, 0x93, - 0xff, 0xef, 0xd5, - 0xff, 0xda, 0xb9, - 0xcd, 0x85, 0x3f, - 0xff, 0xc0, 0xcb, - 0xdd, 0xa0, 0xdd, - 0xb0, 0xe0, 0xe6, - 0x80, 0x00, 0x80, - 0xff, 0x00, 0x00, - 0xbc, 0x8f, 0x8f, - 0x41, 0x69, 0xe1, - 0x8b, 0x45, 0x13, - 0xfa, 0x80, 0x72, - 0xf4, 0xa4, 0x60, - 0x2e, 0x8b, 0x57, - 0xff, 0xf5, 0xee, - 0xa0, 0x52, 0x2d, - 0xc0, 0xc0, 0xc0, - 0x87, 0xce, 0xeb, - 0x6a, 0x5a, 0xcd, - 0x70, 0x80, 0x90, - 0x70, 0x80, 0x90, - 0xff, 0xfa, 0xfa, - 0x00, 0xff, 0x7f, - 0x46, 0x82, 0xb4, - 0xd2, 0xb4, 0x8c, - 0x00, 0x80, 0x80, - 0xd8, 0xbf, 0xd8, - 0xff, 0x63, 0x47, - 0x40, 0xe0, 0xd0, - 0xee, 0x82, 0xee, - 0xf5, 0xde, 0xb3, - 0xff, 0xff, 0xff, - 0xf5, 0xf5, 0xf5, - 0xff, 0xff, 0x00, - 0x9a, 0xcd, 0x32, - // sentinel value - 0xfff - }; - -} diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 9deec1e2..00000000 --- a/configure.ac +++ /dev/null @@ -1,96 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ([2.61]) -AC_INIT([libsass], [1.0], [support@moovweb.com]) -AC_CONFIG_SRCDIR([ast.hpp]) -AC_CONFIG_MACRO_DIR([m4]) -AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE([1.10 foreign]) -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -LT_INIT - -# Checks for programs. -AC_PROG_CXX - -AC_LANG([C++]) - -# 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]) - -if test "x$enable_tests" = "xyes"; then - AC_PROG_CC - AC_PROG_AWK - AC_PATH_PROG(RUBY, [ruby]) - - 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) -fi - -AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "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 - if test "x$enable_static" != "xyes"; then - AC_MSG_ERROR([Static libraries are required for --enable-coverage]) - fi - - 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_CONFIG_FILES([Makefile support/libsass.pc]) -AC_OUTPUT diff --git a/constants.cpp b/constants.cpp deleted file mode 100644 index a82b7839..00000000 --- a/constants.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "constants.hpp" - -namespace Sass { - namespace Constants { - extern const int SPECIFICITY_BASE = 1000; - - // hidden variable name for the image path (for the image-url built-in) - extern const char image_path_var[] = "$[image path]"; - - // sass keywords - extern const char import_kwd[] = "@import"; - extern const char mixin_kwd[] = "@mixin"; - extern const char function_kwd[] = "@function"; - extern const char return_kwd[] = "@return"; - extern const char include_kwd[] = "@include"; - extern const char content_kwd[] = "@content"; - extern const char extend_kwd[] = "@extend"; - extern const char if_kwd[] = "@if"; - extern const char else_kwd[] = "@else"; - extern const char if_after_else_kwd[] = "if"; - extern const char for_kwd[] = "@for"; - extern const char from_kwd[] = "from"; - extern const char to_kwd[] = "to"; - extern const char through_kwd[] = "through"; - extern const char each_kwd[] = "@each"; - extern const char in_kwd[] = "in"; - extern const char while_kwd[] = "@while"; - extern const char warn_kwd[] = "@warn"; - extern const char default_kwd[] = "default"; - extern const char global_kwd[] = "global"; - extern const char null_kwd[] = "null"; - extern const char optional_kwd[] = "optional"; - - // css standard units - extern const char em_kwd[] = "em"; - extern const char ex_kwd[] = "ex"; - extern const char px_kwd[] = "px"; - extern const char cm_kwd[] = "cm"; - extern const char mm_kwd[] = "mm"; - extern const char pt_kwd[] = "pt"; - extern const char pc_kwd[] = "pc"; - extern const char deg_kwd[] = "deg"; - extern const char rad_kwd[] = "rad"; - extern const char grad_kwd[] = "grad"; - extern const char ms_kwd[] = "ms"; - extern const char s_kwd[] = "s"; - extern const char Hz_kwd[] = "Hz"; - extern const char kHz_kwd[] = "kHz"; - - // vendor prefixes - extern const char vendor_opera_kwd[] = "-o-"; - extern const char vendor_webkit_kwd[] = "-webkit-"; - extern const char vendor_mozilla_kwd[] = "-moz-"; - extern const char vendor_ms_kwd[] = "-ms-"; - extern const char vendor_khtml_kwd[] = "-khtml-"; - - // css functions and keywords - extern const char charset_kwd[] = "@charset"; - extern const char media_kwd[] = "@media"; - extern const char keyframes_kwd[] = "keyframes"; - extern const char only_kwd[] = "only"; - extern const char rgb_kwd[] = "rgb("; - extern const char url_kwd[] = "url("; - extern const char image_url_kwd[] = "image-url("; - extern const char important_kwd[] = "important"; - extern const char pseudo_not_kwd[] = ":not("; - extern const char even_kwd[] = "even"; - extern const char odd_kwd[] = "odd"; - extern const char progid_kwd[] = "progid"; - extern const char expression_kwd[] = "expression"; - extern const char calc_kwd[] = "calc("; - extern const char moz_calc_kwd[] = "-moz-calc("; - extern const char webkit_calc_kwd[] = "-webkit-calc("; - - // css attribute-matching operators - extern const char tilde_equal[] = "~="; - extern const char pipe_equal[] = "|="; - extern const char caret_equal[] = "^="; - extern const char dollar_equal[] = "$="; - extern const char star_equal[] = "*="; - - // relational & logical operators and constants - extern const char and_kwd[] = "and"; - extern const char or_kwd[] = "or"; - extern const char not_kwd[] = "not"; - extern const char gt[] = ">"; - extern const char gte[] = ">="; - extern const char lt[] = "<"; - extern const char lte[] = "<="; - extern const char eq[] = "=="; - extern const char neq[] = "!="; - extern const char true_kwd[] = "true"; - extern const char false_kwd[] = "false"; - - // miscellaneous punctuation and delimiters - extern const char percent_str[] = "%"; - extern const char empty_str[] = ""; - extern const char slash_slash[] = "//"; - extern const char slash_star[] = "/*"; - extern const char star_slash[] = "*/"; - extern const char hash_lbrace[] = "#{"; - extern const char rbrace[] = "}"; - extern const char rparen[] = ")"; - extern const char sign_chars[] = "-+"; - extern const char hyphen[] = "-"; - extern const char ellipsis[] = "..."; - extern const char url_space_chars[] = " \t\r\n\f"; - extern const char escape_chars[] = " -~"; // need to include unicode spaces too - // type names - extern const char numeric_name[] = "numeric value"; - extern const char number_name[] = "number"; - extern const char percentage_name[] = "percentage"; - extern const char dimension_name[] = "numeric dimension"; - extern const char string_name[] = "string"; - extern const char bool_name[] = "bool"; - extern const char color_name[] = "color"; - extern const char list_name[] = "list"; - extern const char map_name[] = "map"; - extern const char arglist_name[] = "arglist"; - - // byte order marks - // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) - extern const unsigned char utf_8_bom[] = { 0xEF, 0xBB, 0xBF }; - extern const unsigned char utf_16_bom_be[] = { 0xFE, 0xFF }; - extern const unsigned char utf_16_bom_le[] = { 0xFF, 0xFE }; - extern const unsigned char utf_32_bom_be[] = { 0x00, 0x00, 0xFE, 0xFF }; - extern const unsigned char utf_32_bom_le[] = { 0xFF, 0xFE, 0x00, 0x00 }; - extern const unsigned char utf_7_bom_1[] = { 0x2B, 0x2F, 0x76, 0x38 }; - extern const unsigned char utf_7_bom_2[] = { 0x2B, 0x2F, 0x76, 0x39 }; - extern const unsigned char utf_7_bom_3[] = { 0x2B, 0x2F, 0x76, 0x2B }; - extern const unsigned char utf_7_bom_4[] = { 0x2B, 0x2F, 0x76, 0x2F }; - extern const unsigned char utf_7_bom_5[] = { 0x2B, 0x2F, 0x76, 0x38, 0x2D }; - extern const unsigned char utf_1_bom[] = { 0xF7, 0x64, 0x4C }; - extern const unsigned char utf_ebcdic_bom[] = { 0xDD, 0x73, 0x66, 0x73 }; - extern const unsigned char scsu_bom[] = { 0x0E, 0xFE, 0xFF }; - extern const unsigned char bocu_1_bom[] = { 0xFB, 0xEE, 0x28 }; - extern const unsigned char gb_18030_bom[] = { 0x84, 0x31, 0x95, 0x33 }; - - } -} diff --git a/constants.hpp b/constants.hpp deleted file mode 100644 index 4602950e..00000000 --- a/constants.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#define SASS_CONSTANTS - -namespace Sass { - namespace Constants { - extern const int SPECIFICITY_BASE; - - // hidden variable name for the image path (for the image-url built-in) - extern const char image_path_var[]; - - // sass keywords - extern const char import_kwd[]; - extern const char mixin_kwd[]; - extern const char function_kwd[]; - extern const char return_kwd[]; - extern const char include_kwd[]; - extern const char content_kwd[]; - extern const char extend_kwd[]; - extern const char if_kwd[]; - extern const char else_kwd[]; - extern const char if_after_else_kwd[]; - extern const char for_kwd[]; - extern const char from_kwd[]; - extern const char to_kwd[]; - extern const char through_kwd[]; - extern const char each_kwd[]; - extern const char in_kwd[]; - extern const char while_kwd[]; - extern const char warn_kwd[]; - extern const char default_kwd[]; - extern const char global_kwd[]; - extern const char null_kwd[]; - extern const char optional_kwd[]; - - // css standard units - extern const char em_kwd[]; - extern const char ex_kwd[]; - extern const char px_kwd[]; - extern const char cm_kwd[]; - extern const char mm_kwd[]; - extern const char pt_kwd[]; - extern const char pc_kwd[]; - extern const char deg_kwd[]; - extern const char rad_kwd[]; - extern const char grad_kwd[]; - extern const char ms_kwd[]; - extern const char s_kwd[]; - extern const char Hz_kwd[]; - extern const char kHz_kwd[]; - - // vendor prefixes - extern const char vendor_opera_kwd[]; - extern const char vendor_webkit_kwd[]; - extern const char vendor_mozilla_kwd[]; - extern const char vendor_ms_kwd[]; - extern const char vendor_khtml_kwd[]; - - // css functions and keywords - extern const char charset_kwd[]; - extern const char media_kwd[]; - extern const char keyframes_kwd[]; - extern const char only_kwd[]; - extern const char rgb_kwd[]; - extern const char url_kwd[]; - extern const char image_url_kwd[]; - extern const char important_kwd[]; - extern const char pseudo_not_kwd[]; - extern const char even_kwd[]; - extern const char odd_kwd[]; - extern const char progid_kwd[]; - extern const char expression_kwd[]; - extern const char calc_kwd[]; - extern const char moz_calc_kwd[]; - extern const char webkit_calc_kwd[]; - - // css attribute-matching operators - extern const char tilde_equal[]; - extern const char pipe_equal[]; - extern const char caret_equal[]; - extern const char dollar_equal[]; - extern const char star_equal[]; - - // relational & logical operators and constants - extern const char and_kwd[]; - extern const char or_kwd[]; - extern const char not_kwd[]; - extern const char gt[]; - extern const char gte[]; - extern const char lt[]; - extern const char lte[]; - extern const char eq[]; - extern const char neq[]; - extern const char true_kwd[]; - extern const char false_kwd[]; - - // miscellaneous punctuation and delimiters - extern const char percent_str[]; - extern const char empty_str[]; - extern const char slash_slash[]; - extern const char slash_star[]; - extern const char star_slash[]; - extern const char hash_lbrace[]; - extern const char rbrace[]; - extern const char rparen[]; - extern const char sign_chars[]; - extern const char hyphen[]; - extern const char ellipsis[]; - extern const char url_space_chars[]; - extern const char escape_chars[]; - - // type names - extern const char numeric_name[]; - extern const char number_name[]; - extern const char percentage_name[]; - extern const char dimension_name[]; - extern const char string_name[]; - extern const char bool_name[]; - extern const char color_name[]; - extern const char list_name[]; - extern const char map_name[]; - extern const char arglist_name[]; - - // byte order marks - // (taken from http://en.wikipedia.org/wiki/Byte_order_mark) - extern const unsigned char utf_8_bom[]; - extern const unsigned char utf_16_bom_be[]; - extern const unsigned char utf_16_bom_le[]; - extern const unsigned char utf_32_bom_be[]; - extern const unsigned char utf_32_bom_le[]; - extern const unsigned char utf_7_bom_1[]; - extern const unsigned char utf_7_bom_2[]; - extern const unsigned char utf_7_bom_3[]; - extern const unsigned char utf_7_bom_4[]; - extern const unsigned char utf_7_bom_5[]; - extern const unsigned char utf_1_bom[]; - extern const unsigned char utf_ebcdic_bom[]; - extern const unsigned char scsu_bom[]; - extern const unsigned char bocu_1_bom[]; - extern const unsigned char gb_18030_bom[]; - - } -} diff --git a/context.cpp b/context.cpp deleted file mode 100644 index 6efd70f1..00000000 --- a/context.cpp +++ /dev/null @@ -1,454 +0,0 @@ -#ifdef _WIN32 -#include -#define getcwd _getcwd -#define PATH_SEP ';' -#else -#include -#define PATH_SEP ':' -#endif - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#include "context.hpp" -#include "constants.hpp" -#include "parser.hpp" -#include "file.hpp" -#include "inspect.hpp" -#include "output_nested.hpp" -#include "output_compressed.hpp" -#include "expand.hpp" -#include "eval.hpp" -#include "contextualize.hpp" -#include "extend.hpp" -#include "remove_placeholders.hpp" -#include "copy_c_str.hpp" -#include "color_names.hpp" -#include "functions.hpp" -#include "backtrace.hpp" -#include "sass2scss.h" - -#ifndef SASS_PRELEXER -#include "prelexer.hpp" -#endif - -#include -#include -#include -#include - -namespace Sass { - using namespace Constants; - using namespace File; - using std::cerr; - using std::endl; - - Context::Context(Context::Data initializers) - : mem(Memory_Manager()), - source_c_str (initializers.source_c_str()), - sources (vector()), - include_paths (initializers.include_paths()), - queue (vector >()), - style_sheets (map()), - source_map (resolve_relative_path(initializers.output_path(), initializers.source_map_file(), get_cwd())), - c_functions (vector()), - image_path (initializers.image_path()), - output_path (make_canonical_path(initializers.output_path())), - source_comments (initializers.source_comments()), - output_style (initializers.output_style()), - source_map_file (make_canonical_path(initializers.source_map_file())), - omit_source_map_url (initializers.omit_source_map_url()), - is_indented_syntax_src (initializers.is_indented_syntax_src()), - names_to_colors (map()), - colors_to_names (map()), - precision (initializers.precision()), - subset_map (Subset_Map >()) - { - cwd = get_cwd(); - - collect_include_paths(initializers.include_paths_c_str()); - collect_include_paths(initializers.include_paths_array()); - - setup_color_map(); - - string entry_point = initializers.entry_point(); - if (!entry_point.empty()) { - string result(add_file(entry_point)); - if (result.empty()) { - throw "File to read not found or unreadable: " + entry_point; - } - } - } - - Context::~Context() - { for (size_t i = 0; i < sources.size(); ++i) delete[] sources[i]; } - - void Context::setup_color_map() - { - size_t i = 0; - while (color_names[i]) { - string name(color_names[i]); - Color* value = new (mem) Color("[COLOR TABLE]", Position(), - color_values[i*3], - color_values[i*3+1], - color_values[i*3+2]); - names_to_colors[name] = value; - int numval = color_values[i*3]*0x10000; - numval += color_values[i*3+1]*0x100; - numval += color_values[i*3+2]; - colors_to_names[numval] = name; - ++i; - } - } - - void Context::collect_include_paths(const char* paths_str) - { - include_paths.push_back(cwd); - - if (paths_str) { - const char* beg = paths_str; - const char* end = Prelexer::find_first(beg); - - while (end) { - string path(beg, end - beg); - if (!path.empty()) { - if (*path.rbegin() != '/') path += '/'; - include_paths.push_back(path); - } - beg = end + 1; - end = Prelexer::find_first(beg); - } - - string path(beg); - if (!path.empty()) { - if (*path.rbegin() != '/') path += '/'; - include_paths.push_back(path); - } - } - } - - void Context::collect_include_paths(const char* paths_array[]) - { - include_paths.push_back(get_cwd()); - if (*include_paths.back().rbegin() != '/') include_paths.back() += '/'; - - // if (paths_array) { - // for (size_t i = 0; paths_array[i]; ++i) { - // string path(paths_array[i]); - // if (!path.empty()) { - // if (*path.rbegin() != '/') path += '/'; - // include_paths.push_back(path); - // } - // } - // } - } - - string Context::add_file(string path) - { - using namespace File; - char* contents = 0; - string real_path; - path = make_canonical_path(path); - for (size_t i = 0, S = include_paths.size(); i < S; ++i) { - string full_path(join_paths(include_paths[i], path)); - included_files.push_back(full_path); - if (style_sheets.count(full_path)) return full_path; - contents = resolve_and_load(full_path, real_path); - if (contents) { - sources.push_back(contents); - included_files.push_back(real_path); - queue.push_back(make_pair(full_path, contents)); - source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); - style_sheets[full_path] = 0; - return full_path; - } - } - return string(); - } - - string Context::add_file(string dir, string rel_filepath) - { - using namespace File; - char* contents = 0; - string real_path; - rel_filepath = make_canonical_path(rel_filepath); - string full_path(join_paths(dir, rel_filepath)); - if (style_sheets.count(full_path)) return full_path; - contents = resolve_and_load(full_path, real_path); - if (contents) { - sources.push_back(contents); - included_files.push_back(real_path); - queue.push_back(make_pair(full_path, contents)); - source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); - style_sheets[full_path] = 0; - return full_path; - } - for (size_t i = 0, S = include_paths.size(); i < S; ++i) { - string full_path(join_paths(include_paths[i], rel_filepath)); - if (style_sheets.count(full_path)) return full_path; - contents = resolve_and_load(full_path, real_path); - if (contents) { - sources.push_back(contents); - included_files.push_back(real_path); - queue.push_back(make_pair(full_path, contents)); - source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); - style_sheets[full_path] = 0; - return full_path; - } - } - return string(); - } - - void register_function(Context&, Signature sig, Native_Function f, Env* env); - void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env); - void register_overload_stub(Context&, string name, Env* env); - void register_built_in_functions(Context&, Env* env); - void register_c_functions(Context&, Env* env, Sass_C_Function_Descriptor*); - void register_c_function(Context&, Env* env, Sass_C_Function_Descriptor); - - char* Context::compile_file() - { - Block* root = 0; - for (size_t i = 0; i < queue.size(); ++i) { - Parser p(Parser::from_c_str(queue[i].second, *this, queue[i].first, Position(1 + i, 1, 1))); - Block* ast = p.parse(); - if (i == 0) root = ast; - style_sheets[queue[i].first] = ast; - } - Env tge; - Backtrace backtrace(0, "", Position(), ""); - register_built_in_functions(*this, &tge); - for (size_t i = 0, S = c_functions.size(); i < S; ++i) { - register_c_function(*this, &tge, c_functions[i]); - } - Eval eval(*this, &tge, &backtrace); - Contextualize contextualize(*this, &eval, &tge, &backtrace); - Expand expand(*this, &eval, &contextualize, &tge, &backtrace); - // Inspect inspect(this); - // Output_Nested output_nested(*this); - - root = root->perform(&expand)->block(); - if (!subset_map.empty()) { - Extend extend(*this, subset_map); - root->perform(&extend); - } - - Remove_Placeholders remove_placeholders(*this); - root->perform(&remove_placeholders); - - char* result = 0; - switch (output_style) { - case COMPRESSED: { - Output_Compressed output_compressed(this); - root->perform(&output_compressed); - string output = output_compressed.get_buffer(); - if (source_map_file != "" && !omit_source_map_url) { - output += format_source_mapping_url(source_map_file); - } - result = copy_c_str(output.c_str()); - } break; - - default: { - Output_Nested output_nested(source_comments, this); - root->perform(&output_nested); - string output = output_nested.get_buffer(); - if (source_map_file != "" && !omit_source_map_url) { - output += "\n" + format_source_mapping_url(source_map_file); - } - result = copy_c_str(output.c_str()); - - } break; - } - - return result; - } - - string Context::format_source_mapping_url(const string& file) const - { - return "/*# sourceMappingURL=" + resolve_relative_path(file, output_path, cwd) + " */"; - } - - char* Context::generate_source_map() - { - if (source_map_file == "") return 0; - char* result = 0; - string map = source_map.generate_source_map(); - result = copy_c_str(map.c_str()); - return result; - } - - // allow to optionally overwrite the input path - // default argument for input_path is string("stdin") - // usefull to influence the source-map generating etc. - char* Context::compile_string(const string& input_path) - { - if (!source_c_str) return 0; - queue.clear(); - if(is_indented_syntax_src) { - char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1); - queue.push_back(make_pair(input_path, contents)); - source_map.files.push_back(input_path); - char * compiled = compile_file(); - delete[] contents; - return compiled; - } - queue.push_back(make_pair(input_path, source_c_str)); - source_map.files.push_back(input_path); - return compile_file(); - } - - std::vector Context::get_included_files() - { - std::sort(included_files.begin(), included_files.end()); - included_files.erase( std::unique( included_files.begin(), included_files.end() ), included_files.end()); - return included_files; - } - - string Context::get_cwd() - { - const size_t wd_len = 1024; - char wd[wd_len]; - string cwd = getcwd(wd, wd_len); -#ifdef _WIN32 - //convert backslashes to forward slashes - replace(cwd.begin(), cwd.end(), '\\', '/'); -#endif - if (cwd[cwd.length() - 1] != '/') cwd += '/'; - return cwd; - } - - void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) - { - Definition* def = make_native_function(sig, f, ctx); - def->environment(env); - (*env)[def->name() + "[f]"] = def; - } - - void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env) - { - Definition* def = make_native_function(sig, f, ctx); - stringstream ss; - ss << def->name() << "[f]" << arity; - def->environment(env); - (*env)[ss.str()] = def; - } - - void register_overload_stub(Context& ctx, string name, Env* env) - { - Definition* stub = new (ctx.mem) Definition("[built-in function]", - Position(), - 0, - name, - 0, - 0, - true); - (*env)[name + "[f]"] = stub; - } - - - void register_built_in_functions(Context& ctx, Env* env) - { - using namespace Functions; - // RGB Functions - register_function(ctx, rgb_sig, rgb, env); - register_overload_stub(ctx, "rgba", env); - register_function(ctx, rgba_4_sig, rgba_4, 4, env); - register_function(ctx, rgba_2_sig, rgba_2, 2, env); - register_function(ctx, red_sig, red, env); - register_function(ctx, green_sig, green, env); - register_function(ctx, blue_sig, blue, env); - register_function(ctx, mix_sig, mix, env); - // HSL Functions - register_function(ctx, hsl_sig, hsl, env); - register_function(ctx, hsla_sig, hsla, env); - register_function(ctx, hue_sig, hue, env); - register_function(ctx, saturation_sig, saturation, env); - register_function(ctx, lightness_sig, lightness, env); - register_function(ctx, adjust_hue_sig, adjust_hue, env); - register_function(ctx, lighten_sig, lighten, env); - register_function(ctx, darken_sig, darken, env); - register_function(ctx, saturate_sig, saturate, env); - register_function(ctx, desaturate_sig, desaturate, env); - register_function(ctx, grayscale_sig, grayscale, env); - register_function(ctx, complement_sig, complement, env); - register_function(ctx, invert_sig, invert, env); - // Opacity Functions - register_function(ctx, alpha_sig, alpha, env); - register_function(ctx, opacity_sig, alpha, env); - register_function(ctx, opacify_sig, opacify, env); - register_function(ctx, fade_in_sig, opacify, env); - register_function(ctx, transparentize_sig, transparentize, env); - register_function(ctx, fade_out_sig, transparentize, env); - // Other Color Functions - register_function(ctx, adjust_color_sig, adjust_color, env); - register_function(ctx, scale_color_sig, scale_color, env); - register_function(ctx, change_color_sig, change_color, env); - register_function(ctx, ie_hex_str_sig, ie_hex_str, env); - // String Functions - register_function(ctx, unquote_sig, sass_unquote, env); - register_function(ctx, quote_sig, sass_quote, env); - register_function(ctx, str_length_sig, str_length, env); - register_function(ctx, str_insert_sig, str_insert, env); - register_function(ctx, str_index_sig, str_index, env); - register_function(ctx, str_slice_sig, str_slice, env); - register_function(ctx, to_upper_case_sig, to_upper_case, env); - register_function(ctx, to_lower_case_sig, to_lower_case, env); - // Number Functions - register_function(ctx, percentage_sig, percentage, env); - register_function(ctx, round_sig, round, env); - register_function(ctx, ceil_sig, ceil, env); - register_function(ctx, floor_sig, floor, env); - register_function(ctx, abs_sig, abs, env); - register_function(ctx, min_sig, min, env); - register_function(ctx, max_sig, max, env); - // List Functions - register_function(ctx, length_sig, length, env); - register_function(ctx, nth_sig, nth, env); - register_function(ctx, index_sig, index, env); - register_function(ctx, join_sig, join, env); - register_function(ctx, append_sig, append, env); - register_function(ctx, compact_sig, compact, env); - register_function(ctx, zip_sig, zip, env); - register_function(ctx, list_separator_sig, list_separator, env); - // Map Functions - register_function(ctx, map_get_sig, map_get, env); - register_function(ctx, map_merge_sig, map_merge, env); - register_function(ctx, map_remove_sig, map_remove, env); - register_function(ctx, map_keys_sig, map_keys, env); - register_function(ctx, map_values_sig, map_values, env); - register_function(ctx, map_has_key_sig, map_has_key, env); - register_function(ctx, keywords_sig, keywords, env); - // Introspection Functions - register_function(ctx, type_of_sig, type_of, env); - register_function(ctx, unit_sig, unit, env); - register_function(ctx, unitless_sig, unitless, env); - register_function(ctx, comparable_sig, comparable, env); - register_function(ctx, variable_exists_sig, variable_exists, env); - register_function(ctx, global_variable_exists_sig, global_variable_exists, env); - register_function(ctx, function_exists_sig, function_exists, env); - register_function(ctx, mixin_exists_sig, mixin_exists, env); - register_function(ctx, call_sig, call, env); - // Boolean Functions - register_function(ctx, not_sig, sass_not, env); - register_function(ctx, if_sig, sass_if, env); - // Path Functions - register_function(ctx, image_url_sig, image_url, env); - } - - void register_c_functions(Context& ctx, Env* env, Sass_C_Function_Descriptor* descrs) - { - while (descrs->signature && descrs->function) { - register_c_function(ctx, env, *descrs); - ++descrs; - } - } - void register_c_function(Context& ctx, Env* env, Sass_C_Function_Descriptor descr) - { - Definition* def = make_c_function(descr.signature, descr.function, descr.cookie, ctx); - def->environment(env); - (*env)[def->name() + "[f]"] = def; - } - - -} diff --git a/context.hpp b/context.hpp deleted file mode 100644 index 5f530655..00000000 --- a/context.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#define SASS_CONTEXT - -#include -#include -#include -#include "kwd_arg_macros.hpp" - -#ifndef SASS_MEMORY_MANAGER -#include "memory_manager.hpp" -#endif - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#ifndef SASS_SOURCE_MAP -#include "source_map.hpp" -#endif - -#ifndef SASS_SUBSET_MAP -#include "subset_map.hpp" -#endif - -struct Sass_C_Function_Descriptor; - -namespace Sass { - using namespace std; - class AST_Node; - class Block; - class Expression; - class Color; - struct Backtrace; - // typedef const char* Signature; - // struct Context; - // typedef Environment Env; - // typedef Expression* (*Native_Function)(Env&, Context&, Signature, string, size_t); - - enum Output_Style { NESTED, EXPANDED, COMPACT, COMPRESSED, FORMATTED }; - - struct Context { - Memory_Manager mem; - - const char* source_c_str; - vector sources; // c-strs containing Sass file contents - vector include_paths; - vector > queue; // queue of files to be parsed - map style_sheets; // map of paths to ASTs - SourceMap source_map; - vector c_functions; - - string image_path; // for the image-url Sass function - string output_path; // for relative paths to the output - bool source_comments; // for inline debug comments in css output - Output_Style output_style; // output style for the generated css code - string source_map_file; // path to source map file (enables feature) - bool omit_source_map_url; // disable source map comment in css output - bool is_indented_syntax_src; // treat source string as sass - - map names_to_colors; - map colors_to_names; - - size_t precision; // precision for outputting fractional numbers - - KWD_ARG_SET(Data) { - KWD_ARG(Data, const char*, source_c_str); - KWD_ARG(Data, string, entry_point); - KWD_ARG(Data, string, output_path); - KWD_ARG(Data, string, image_path); - KWD_ARG(Data, const char*, include_paths_c_str); - KWD_ARG(Data, const char**, include_paths_array); - KWD_ARG(Data, vector, include_paths); - KWD_ARG(Data, bool, source_comments); - KWD_ARG(Data, Output_Style, output_style); - KWD_ARG(Data, string, source_map_file); - KWD_ARG(Data, bool, omit_source_map_url); - KWD_ARG(Data, bool, is_indented_syntax_src); - KWD_ARG(Data, size_t, precision); - }; - - Context(Data); - ~Context(); - void collect_include_paths(const char* paths_str); - void collect_include_paths(const char* paths_array[]); - void setup_color_map(); - string add_file(string); - string add_file(string, string); - // allow to optionally overwrite the input path - // default argument for input_path is string("stdin") - // usefull to influence the source-map generating etc. - char* compile_string(const string& input_path = "stdin"); - char* compile_file(); - char* generate_source_map(); - - std::vector get_included_files(); - - private: - string format_source_mapping_url(const string& file) const; - string get_cwd(); - - vector included_files; - string cwd; - - // void register_built_in_functions(Env* env); - // void register_function(Signature sig, Native_Function f, Env* env); - // void register_function(Signature sig, Native_Function f, size_t arity, Env* env); - // void register_overload_stub(string name, Env* env); - - public: - Subset_Map > subset_map; - }; - -} diff --git a/contextualize.cpp b/contextualize.cpp deleted file mode 100644 index a0b79014..00000000 --- a/contextualize.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "contextualize.hpp" -#include "ast.hpp" -#include "eval.hpp" -#include "backtrace.hpp" -#include "to_string.hpp" -#include "parser.hpp" - -namespace Sass { - - Contextualize::Contextualize(Context& ctx, Eval* eval, Env* env, Backtrace* bt, Selector* placeholder, Selector* extender) - : ctx(ctx), eval(eval), env(env), parent(0), backtrace(bt), placeholder(placeholder), extender(extender) - { } - - Contextualize::~Contextualize() { } - - Selector* Contextualize::fallback_impl(AST_Node* n) - { return parent; } - - Contextualize* Contextualize::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex) - { - parent = s; - env = e; - backtrace = bt; - placeholder = p; - extender = ex; - return this; - } - - Selector* Contextualize::operator()(Selector_Schema* s) - { - To_String to_string; - string result_str(s->contents()->perform(eval->with(env, backtrace))->perform(&to_string)); - result_str += '{'; // the parser looks for a brace to end the selector - Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->path(), s->position()).parse_selector_group(); - return result_sel->perform(this); - } - - Selector* Contextualize::operator()(Selector_List* s) - { - Selector_List* p = static_cast(parent); - Selector_List* ss = 0; - if (p) { - ss = new (ctx.mem) Selector_List(s->path(), s->position(), p->length() * s->length()); - for (size_t i = 0, L = p->length(); i < L; ++i) { - for (size_t j = 0, L = s->length(); j < L; ++j) { - parent = (*p)[i]; - Complex_Selector* comb = static_cast((*s)[j]->perform(this)); - if (comb) *ss << comb; - } - } - } - else { - ss = new (ctx.mem) Selector_List(s->path(), s->position(), s->length()); - for (size_t j = 0, L = s->length(); j < L; ++j) { - Complex_Selector* comb = static_cast((*s)[j]->perform(this)); - if (comb) *ss << comb; - } - } - return ss->length() ? ss : 0; - } - - Selector* Contextualize::operator()(Complex_Selector* s) - { - To_String to_string; - Complex_Selector* ss = new (ctx.mem) Complex_Selector(*s); - Compound_Selector* new_head = 0; - Complex_Selector* new_tail = 0; - if (ss->head()) { - new_head = static_cast(s->head()->perform(this)); - ss->head(new_head); - } - if (ss->tail()) { - new_tail = static_cast(s->tail()->perform(this)); - ss->tail(new_tail); - } - if ((new_head && new_head->has_placeholder()) || (new_tail && new_tail->has_placeholder())) { - ss->has_placeholder(true); - } - else { - ss->has_placeholder(false); - } - if (!ss->head() && ss->combinator() == Complex_Selector::ANCESTOR_OF) { - return ss->tail(); - } - else { - return ss; - } - } - - Selector* Contextualize::operator()(Compound_Selector* s) - { - To_String to_string; - if (placeholder && extender && s->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; - } - Compound_Selector* ss = new (ctx.mem) Compound_Selector(s->path(), s->position(), s->length()); - for (size_t i = 0, L = s->length(); i < L; ++i) { - Simple_Selector* simp = static_cast((*s)[i]->perform(this)); - if (simp) *ss << simp; - } - return ss->length() ? ss : 0; - } - - Selector* Contextualize::operator()(Wrapped_Selector* s) - { - Selector* old_parent = parent; - parent = 0; - Wrapped_Selector* neg = new (ctx.mem) Wrapped_Selector(s->path(), - s->position(), - s->name(), - s->selector()->perform(this)); - parent = old_parent; - return neg; - } - - Selector* Contextualize::operator()(Pseudo_Selector* s) - { return s; } - - Selector* Contextualize::operator()(Attribute_Selector* s) - { - // the value might be interpolated; evaluate it - String* v = s->value(); - if (v && eval) { - v = static_cast(v->perform(eval->with(env, backtrace))); - } - Attribute_Selector* ss = new (ctx.mem) Attribute_Selector(*s); - ss->value(v); - return ss; - } - - Selector* Contextualize::operator()(Selector_Qualifier* s) - { return s; } - - Selector* Contextualize::operator()(Type_Selector* s) - { return s; } - - Selector* Contextualize::operator()(Selector_Placeholder* p) - { - To_String to_string; - if (placeholder && extender && p->perform(&to_string) == placeholder->perform(&to_string)) { - return extender; - } - else { - return p; - } - } - - Selector* Contextualize::operator()(Selector_Reference* s) - { - if (!parent) return 0; - Selector_Reference* ss = new (ctx.mem) Selector_Reference(*s); - ss->selector(parent); - return ss; - } - - -} diff --git a/contextualize.hpp b/contextualize.hpp deleted file mode 100644 index 8f3abb79..00000000 --- a/contextualize.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#define SASS_CONTEXTUALIZE - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -namespace Sass { - class AST_Node; - class Selector; - class Selector_Schema; - class Selector_List; - class Complex_Selector; - class Compound_Selector; - class Wrapped_Selector; - class Pseudo_Selector; - class Attribute_Selector; - class Selector_Qualifier; - class Type_Selector; - class Selector_Placeholder; - class Selector_Reference; - class Simple_Selector; - struct Context; - class Eval; - struct Backtrace; - - typedef Environment Env; - - class Contextualize : public Operation_CRTP { - - Context& ctx; - Eval* eval; - Env* env; - Selector* parent; - Backtrace* backtrace; - - Selector* fallback_impl(AST_Node* n); - - public: - Selector* placeholder; - Selector* extender; - Contextualize(Context&, Eval*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0); - virtual ~Contextualize(); - Contextualize* with(Selector*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0); - using Operation::operator(); - - Selector* operator()(Selector_Schema*); - Selector* operator()(Selector_List*); - Selector* operator()(Complex_Selector*); - Selector* operator()(Compound_Selector*); - Selector* operator()(Wrapped_Selector*); - Selector* operator()(Pseudo_Selector*); - Selector* operator()(Attribute_Selector*); - Selector* operator()(Selector_Qualifier*); - Selector* operator()(Type_Selector*); - Selector* operator()(Selector_Placeholder*); - Selector* operator()(Selector_Reference*); - - template - Selector* fallback(U x) { return fallback_impl(x); } - }; -} diff --git a/copy_c_str.cpp b/copy_c_str.cpp deleted file mode 100644 index b4d61cb9..00000000 --- a/copy_c_str.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -namespace Sass { - using namespace std; - - char* copy_c_str(const char* orig) - { - char* copy = (char*) malloc(sizeof(char) * strlen(orig) + 1); - strcpy(copy, orig); - return copy; - } -} diff --git a/copy_c_str.hpp b/copy_c_str.hpp deleted file mode 100644 index c3f633fa..00000000 --- a/copy_c_str.hpp +++ /dev/null @@ -1,5 +0,0 @@ -namespace Sass { - - char* copy_c_str(const char*); - -} diff --git a/debug.hpp b/debug.hpp deleted file mode 100644 index 880bc9b3..00000000 --- a/debug.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H - -#include - -enum dbg_lvl_t : uint32_t { - NONE = 0, - TRIM = 1, - CHUNKS = 2, - SUBWEAVE = 4, - WEAVE = 8, - EXTEND_COMPOUND = 16, - EXTEND_COMPLEX = 32, - LCS = 64, - EXTEND_OBJECT = 128, - ALL = UINT32_MAX -}; - -#ifdef DEBUG - -#ifndef DEBUG_LVL -const uint32_t debug_lvl = UINT32_MAX; -#else -const uint32_t debug_lvl = (DEBUG_LVL); -#endif // DEBUG_LVL - -#define DEBUG_PRINT(lvl, x) if((lvl) & debug_lvl) { std::cerr << x; } -#define DEBUG_PRINTLN(lvl, x) if((lvl) & debug_lvl) { std::cerr << x << std::endl; } -#define DEBUG_EXEC(lvl, x) if((lvl) & debug_lvl) { x; } - -#else // DEBUG - -#define DEBUG_PRINT(lvl, x) -#define DEBUG_PRINTLN(lvl, x) -#define DEBUG_EXEC(lvl, x) - -#endif // DEBUG - -#endif // DEBUG_H diff --git a/environment.hpp b/environment.hpp deleted file mode 100644 index 34929fe6..00000000 --- a/environment.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#define SASS_ENVIRONMENT - -#include -#include -#include "ast_def_macros.hpp" -#include - -namespace Sass { - using std::string; - using std::map; - using std::cerr; - using std::endl; - - template - class Environment { - // TODO: test with unordered_map - map current_frame_; - ADD_PROPERTY(Environment*, parent); - - public: - Environment() : current_frame_(map()), parent_(0) { } - - map& current_frame() { return current_frame_; } - - void link(Environment& env) { parent_ = &env; } - void link(Environment* env) { parent_ = env; } - - bool has(const string key) const - { - if (current_frame_.count(key)) return true; - else if (parent_) return parent_->has(key); - else return false; - } - - bool current_frame_has(const string key) const - { return current_frame_.count(key); } - - Environment* grandparent() const - { - if(parent_ && parent_->parent_) return parent_->parent_; - else return 0; - } - - bool global_frame_has(const string key) const - { - if(parent_ && !grandparent()) { - return has(key); - } - else if(parent_) { - return parent_->global_frame_has(key); - } - else { - return false; - } - } - - T& operator[](const string key) - { - if (current_frame_.count(key)) return current_frame_[key]; - else if (parent_) return (*parent_)[key]; - else return current_frame_[key]; - } - - void print() - { - for (typename map::iterator i = current_frame_.begin(); i != current_frame_.end(); ++i) { - cerr << i->first << endl; - } - if (parent_) { - cerr << "---" << endl; - parent_->print(); - } - } - }; -} diff --git a/error_handling.cpp b/error_handling.cpp deleted file mode 100644 index 8f09cfc0..00000000 --- a/error_handling.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SASS_ERROR_HANDLING -#include "error_handling.hpp" -#endif - -#include "backtrace.hpp" -#include "prelexer.hpp" - -namespace Sass { - - Error::Error(Type type, string path, Position position, string message) - : type(type), path(path), position(position), message(message) - { } - - void error(string msg, string path, Position position) - { throw Error(Error::syntax, path, position, msg); } - - void error(string msg, string path, Position position, Backtrace* bt) - { - if (!path.empty() && Prelexer::string_constant(path.c_str())) - path = path.substr(1, path.size() - 1); - - Backtrace top(bt, path, position, ""); - msg += top.to_string(); - - throw Error(Error::syntax, path, position, msg); - } - -} diff --git a/error_handling.hpp b/error_handling.hpp deleted file mode 100644 index 03fcc13e..00000000 --- a/error_handling.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#define SASS_ERROR_HANDLING -#include - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Backtrace; - - struct Error { - enum Type { read, write, syntax, evaluation }; - - Type type; - string path; - Position position; - string message; - - Error(Type type, string path, Position position, string message); - - }; - - void error(string msg, string path, Position position); - void error(string msg, string path, Position position, Backtrace* bt); - -} diff --git a/eval.cpp b/eval.cpp deleted file mode 100644 index 1d27914d..00000000 --- a/eval.cpp +++ /dev/null @@ -1,893 +0,0 @@ -#include "eval.hpp" -#include "ast.hpp" -#include "bind.hpp" -#include "to_string.hpp" -#include "inspect.hpp" -#include "to_c.hpp" -#include "context.hpp" -#include "backtrace.hpp" -#include "prelexer.hpp" - -#include -#include -#include -#include -#include - -namespace Sass { - using namespace std; - - inline double add(double x, double y) { return x + y; } - inline double sub(double x, double y) { return x - y; } - inline double mul(double x, double y) { return x * y; } - inline double div(double x, double y) { return x / y; } // x/0 checked by caller - typedef double (*bop)(double, double); - bop ops[Binary_Expression::NUM_OPS] = { - 0, 0, // and, or - 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte - add, sub, mul, div, fmod - }; - - Eval::Eval(Context& ctx, Env* env, Backtrace* bt) - : ctx(ctx), env(env), backtrace(bt) { } - Eval::~Eval() { } - - Eval* Eval::with(Env* e, Backtrace* bt) // for setting the env before eval'ing an expression - { - env = e; - backtrace = bt; - return this; - } - - Expression* Eval::operator()(Block* b) - { - Expression* val = 0; - for (size_t i = 0, L = b->length(); i < L; ++i) { - val = (*b)[i]->perform(this); - if (val) return val; - } - return val; - } - - Expression* Eval::operator()(Assignment* a) - { - string var(a->variable()); - if (env->has(var)) { - if(!a->is_guarded()) (*env)[var] = a->value()->perform(this); - } - else { - env->current_frame()[var] = a->value()->perform(this); - } - return 0; - } - - Expression* Eval::operator()(If* i) - { - if (*i->predicate()->perform(this)) { - return i->consequent()->perform(this); - } - else { - Block* alt = i->alternative(); - if (alt) return alt->perform(this); - } - return 0; - } - - Expression* Eval::operator()(For* f) - { - string variable(f->variable()); - Expression* low = f->lower_bound()->perform(this); - if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->path(), low->position()); - } - Expression* high = f->upper_bound()->perform(this); - if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->path(), high->position()); - } - double lo = static_cast(low)->value(); - double hi = static_cast(high)->value(); - if (f->is_inclusive()) ++hi; - Env new_env; - new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), lo); - new_env.link(env); - env = &new_env; - Block* body = f->block(); - Expression* val = 0; - for (double i = lo; - i < hi; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) { - val = body->perform(this); - if (val) break; - } - env = new_env.parent(); - return val; - } - - Expression* Eval::operator()(Each* e) - { - string variable(e->variable()); - Expression* expr = e->list()->perform(this); - List* list = 0; - if (expr->concrete_type() != Expression::LIST) { - list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA); - *list << expr; - } - else { - list = static_cast(expr); - } - Env new_env; - new_env[variable] = 0; - new_env.link(env); - env = &new_env; - Block* body = e->block(); - Expression* val = 0; - for (size_t i = 0, L = list->length(); i < L; ++i) { - (*env)[variable] = (*list)[i]; - val = body->perform(this); - if (val) break; - } - env = new_env.parent(); - return val; - } - - Expression* Eval::operator()(While* w) - { - Expression* pred = w->predicate(); - Block* body = w->block(); - while (*pred->perform(this)) { - Expression* val = body->perform(this); - if (val) return val; - } - return 0; - } - - Expression* Eval::operator()(Return* r) - { - return r->value()->perform(this); - } - - Expression* Eval::operator()(Warning* w) - { - Expression* message = w->message()->perform(this); - To_String to_string; - string prefix("WARNING: "); - string result(unquote(message->perform(&to_string))); - cerr << prefix << result; - Backtrace top(backtrace, w->path(), w->position(), ""); - cerr << top.to_string(true); - cerr << endl << endl; - return 0; - } - - Expression* Eval::operator()(List* l) - { - List* ll = new (ctx.mem) List(l->path(), - l->position(), - l->length(), - l->separator(), - l->is_arglist()); - for (size_t i = 0, L = l->length(); i < L; ++i) { - *ll << (*l)[i]->perform(this); - } - return ll; - } - - Expression* Eval::operator()(Map* m) - { - Map* mm = new (ctx.mem) Map(m->path(), - m->position(), - m->length()); - for (size_t i = 0, L = m->length(); i < L; ++i) { - KeyValuePair* kvp = new (ctx.mem) KeyValuePair(m->path(), - m->position(), - (*m)[i]->key()->perform(this), - (*m)[i]->value()->perform(this)); - *mm << kvp; - } - return mm; - } - - // -- only need to define two comparisons, and the rest can be implemented in terms of them - bool eq(Expression*, Expression*, Context&, Eval*); - bool lt(Expression*, Expression*, Context&); - // -- arithmetic on the combinations that matter - Expression* op_numbers(Context&, Binary_Expression*, Expression*, Expression*); - Expression* op_number_color(Context&, Binary_Expression::Type, Expression*, Expression*); - Expression* op_color_number(Context&, Binary_Expression::Type, Expression*, Expression*); - Expression* op_colors(Context&, Binary_Expression::Type, Expression*, Expression*); - Expression* op_strings(Context&, Binary_Expression::Type, Expression*, Expression*); - - Expression* Eval::operator()(Binary_Expression* b) - { - Binary_Expression::Type op_type = b->type(); - // don't eval delayed expressions (the '/' when used as a separator) - if (op_type == Binary_Expression::DIV && b->is_delayed()) return b; - // the logical connectives need to short-circuit - Expression* lhs = b->left()->perform(this); - switch (op_type) { - case Binary_Expression::AND: - return *lhs ? b->right()->perform(this) : lhs; - break; - - case Binary_Expression::OR: - return *lhs ? lhs : b->right()->perform(this); - break; - - default: - break; - } - // not a logical connective, so go ahead and eval the rhs - Expression* rhs = b->right()->perform(this); - - // see if it's a relational expression - switch(op_type) { - case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->path(), b->position(), eq(lhs, rhs, ctx)); - case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->path(), b->position(), !eq(lhs, rhs, ctx)); - case Binary_Expression::GT: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx)); - case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx)); - case Binary_Expression::LT: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx)); - case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx)); - - default: break; - } - - Expression::Concrete_Type l_type = lhs->concrete_type(); - Expression::Concrete_Type r_type = rhs->concrete_type(); - - if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { - return op_numbers(ctx, b, lhs, rhs); - } - if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { - return op_number_color(ctx, op_type, lhs, rhs); - } - if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { - return op_color_number(ctx, op_type, lhs, rhs); - } - if (l_type == Expression::COLOR && r_type == Expression::COLOR) { - return op_colors(ctx, op_type, lhs, rhs); - } - return op_strings(ctx, op_type, lhs, rhs); - } - - Expression* Eval::operator()(Unary_Expression* u) - { - Expression* operand = u->operand()->perform(this); - if (operand->concrete_type() == Expression::NUMBER) { - Number* result = new (ctx.mem) Number(*static_cast(operand)); - result->value(u->type() == Unary_Expression::MINUS - ? -result->value() - : result->value()); - return result; - } - else { - To_String to_string; - // Special cases: +/- variables which evaluate to null ouput just +/-, - // but +/- null itself outputs the string - if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) { - u->operand(new (ctx.mem) String_Constant(u->path(), u->position(), "")); - } - String_Constant* result = new (ctx.mem) String_Constant(u->path(), - u->position(), - u->perform(&to_string)); - return result; - } - // unreachable - return u; - } - - Expression* Eval::operator()(Function_Call* c) - { - string full_name(c->name() + "[f]"); - Arguments* args = c->arguments(); - if (full_name != "if[f]") { - args = static_cast(args->perform(this)); - } - - // if it doesn't exist, just pass it through as a literal - if (!env->has(full_name)) { - Function_Call* lit = new (ctx.mem) Function_Call(c->path(), - c->position(), - c->name(), - args); - To_String to_string; - return new (ctx.mem) String_Constant(c->path(), - c->position(), - lit->perform(&to_string)); - } - - Expression* result = c; - Definition* def = static_cast((*env)[full_name]); - Block* body = def->block(); - Native_Function func = def->native_function(); - Sass_C_Function c_func = def->c_function(); - - if (full_name != "if[f]") { - for (size_t i = 0, L = args->length(); i < L; ++i) { - (*args)[i]->value((*args)[i]->value()->perform(this)); - } - } - - Parameters* params = def->parameters(); - Env new_env; - new_env.link(def->environment()); - // bind("function " + c->name(), params, args, ctx, &new_env, this); - // Env* old_env = env; - // env = &new_env; - - // Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); - // backtrace = &here; - - // if it's user-defined, eval the body - if (body) { - - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; - - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = body->perform(this); - if (!result) { - error(string("function ") + c->name() + " did not return a value", c->path(), c->position()); - } - backtrace = here.parent; - env = old_env; - } - // if it's native, invoke the underlying CPP function - else if (func) { - - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; - - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = func(*env, *old_env, ctx, def->signature(), c->path(), c->position(), backtrace); - - backtrace = here.parent; - env = old_env; - } - // else if it's a user-defined c function - else if (c_func) { - - bind("function " + c->name(), params, args, ctx, &new_env, this); - Env* old_env = env; - env = &new_env; - - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); - backtrace = &here; - - To_C to_c; - Sass_Value c_val = c_func(args->perform(&to_c), def->cookie()); - if (c_val.unknown.tag == SASS_ERROR) { - error("error in C function " + c->name() + ": " + c_val.error.message, c->path(), c->position(), backtrace); - } - result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position()); - - backtrace = here.parent; - env = old_env; - } - // else it's an overloaded native function; resolve it - else if (def->is_overload_stub()) { - size_t arity = args->length(); - stringstream ss; - ss << full_name << arity; - string resolved_name(ss.str()); - if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->path(), c->position()); - Definition* resolved_def = static_cast((*env)[resolved_name]); - params = resolved_def->parameters(); - Env newer_env; - newer_env.link(resolved_def->environment()); - bind("function " + c->name(), params, args, ctx, &newer_env, this); - Env* old_env = env; - env = &newer_env; - - Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); - backtrace = &here; - - result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->path(), c->position(), backtrace); - - backtrace = here.parent; - env = old_env; - } - - // backtrace = here.parent; - // env = old_env; - result->position(c->position()); - return result; - } - - Expression* Eval::operator()(Function_Call_Schema* s) - { - Expression* evaluated_name = s->name()->perform(this); - Expression* evaluated_args = s->arguments()->perform(this); - String_Schema* ss = new (ctx.mem) String_Schema(s->path(), s->position(), 2); - (*ss) << evaluated_name << evaluated_args; - return ss->perform(this); - } - - Expression* Eval::operator()(Variable* v) - { - To_String to_string; - string name(v->name()); - Expression* value = 0; - if (env->has(name)) value = static_cast((*env)[name]); - else error("unbound variable " + v->name(), v->path(), v->position()); - // cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << endl; - if (typeid(*value) == typeid(Argument)) value = static_cast(value)->value(); - - // cerr << "\ttype is now: " << typeid(*value).name() << endl << endl; - return value; - } - - Expression* Eval::operator()(Textual* t) - { - using Prelexer::number; - Expression* result = 0; - switch (t->type()) - { - case Textual::NUMBER: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(t->value().c_str())); - break; - case Textual::PERCENTAGE: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(t->value().c_str()), - "%"); - break; - case Textual::DIMENSION: - result = new (ctx.mem) Number(t->path(), - t->position(), - atof(t->value().c_str()), - Token(number(t->value().c_str()))); - break; - case Textual::HEX: { - string hext(t->value().substr(1)); // chop off the '#' - if (hext.length() == 6) { - string r(hext.substr(0,2)); - string g(hext.substr(2,2)); - string b(hext.substr(4,2)); - result = new (ctx.mem) Color(t->path(), - t->position(), - static_cast(strtol(r.c_str(), NULL, 16)), - static_cast(strtol(g.c_str(), NULL, 16)), - static_cast(strtol(b.c_str(), NULL, 16)), - 1, - t->value()); - } - else { - result = new (ctx.mem) Color(t->path(), - t->position(), - static_cast(strtol(string(2,hext[0]).c_str(), NULL, 16)), - static_cast(strtol(string(2,hext[1]).c_str(), NULL, 16)), - static_cast(strtol(string(2,hext[2]).c_str(), NULL, 16)), - 1, - t->value()); - } - } break; - } - return result; - } - - Expression* Eval::operator()(Number* n) - { - return n; - } - - Expression* Eval::operator()(Boolean* b) - { - return b; - } - - char is_quoted(string str) - { - size_t len = str.length(); - if (len < 2) return 0; - if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) { - return str[0]; - } - else { - return 0; - } - } - - Expression* Eval::operator()(String_Schema* s) - { - string acc; - To_String to_string(0); - for (size_t i = 0, L = s->length(); i < L; ++i) { - string chunk((*s)[i]->perform(this)->perform(&to_string)); - if (((s->quote_mark() && is_quoted(chunk)) || !s->quote_mark()) && (*s)[i]->is_interpolant()) { // some redundancy in that test - acc += unquote(chunk); - } - else { - acc += chunk; - } - } - return new (ctx.mem) String_Constant(s->path(), - s->position(), - acc); - } - - Expression* Eval::operator()(String_Constant* s) - { - if (!s->is_delayed() && ctx.names_to_colors.count(s->value())) { - Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]); - c->path(s->path()); - c->position(s->position()); - c->disp(s->value()); - return c; - } - return s; - } - - Expression* Eval::operator()(Media_Query* q) - { - String* t = q->media_type(); - t = static_cast(t ? t->perform(this) : 0); - Media_Query* qq = new (ctx.mem) Media_Query(q->path(), - q->position(), - t, - q->length(), - q->is_negated(), - q->is_restricted()); - for (size_t i = 0, L = q->length(); i < L; ++i) { - *qq << static_cast((*q)[i]->perform(this)); - } - return qq; - } - - Expression* Eval::operator()(Media_Query_Expression* e) - { - Expression* feature = e->feature(); - feature = (feature ? feature->perform(this) : 0); - Expression* value = e->value(); - value = (value ? value->perform(this) : 0); - return new (ctx.mem) Media_Query_Expression(e->path(), - e->position(), - feature, - value, - e->is_interpolated()); - } - - Expression* Eval::operator()(Null* n) - { - return n; - } - - Expression* Eval::operator()(Argument* a) - { - Expression* val = a->value(); - val->is_delayed(false); - val = val->perform(this); - val->is_delayed(false); - if (a->is_rest_argument() && (val->concrete_type() != Expression::LIST)) { - List* wrapper = new (ctx.mem) List(val->path(), - val->position(), - 0, - List::COMMA, - true); - *wrapper << val; - val = wrapper; - } - return new (ctx.mem) Argument(a->path(), - a->position(), - val, - a->name(), - a->is_rest_argument()); - } - - Expression* Eval::operator()(Arguments* a) - { - Arguments* aa = new (ctx.mem) Arguments(a->path(), a->position()); - for (size_t i = 0, L = a->length(); i < L; ++i) { - *aa << static_cast((*a)[i]->perform(this)); - } - return aa; - } - - inline Expression* Eval::fallback_impl(AST_Node* n) - { - return static_cast(n); - } - - // All the binary helpers. - - bool eq(Expression* lhs, Expression* rhs, Context& ctx) - { - Expression::Concrete_Type ltype = lhs->concrete_type(); - Expression::Concrete_Type rtype = rhs->concrete_type(); - if (ltype != rtype) return false; - switch (ltype) { - - case Expression::BOOLEAN: { - return static_cast(lhs)->value() == - static_cast(rhs)->value(); - } break; - - case Expression::NUMBER: { - Number* l = static_cast(lhs); - Number* r = static_cast(rhs); - Number tmp_r(*r); - tmp_r.normalize(l->find_convertible_unit()); - return l->unit() == tmp_r.unit() && l->value() == tmp_r.value() - ? true - : false; - } break; - - case Expression::COLOR: { - Color* l = static_cast(lhs); - Color* r = static_cast(rhs); - return l->r() == r->r() && - l->g() == r->g() && - l->b() == r->b() && - l->a() == r->a(); - } break; - - case Expression::STRING: { - return unquote(static_cast(lhs)->value()) == - unquote(static_cast(rhs)->value()); - } break; - - case Expression::LIST: { - List* l = static_cast(lhs); - List* r = static_cast(rhs); - if (l->length() != r->length()) return false; - if (l->separator() != r->separator()) return false; - for (size_t i = 0, L = l->length(); i < L; ++i) { - if (!eq((*l)[i], (*r)[i], ctx)) return false; - } - return true; - } break; - - case Expression::MAP: { - Map* l = static_cast(lhs); - Map* r = static_cast(rhs); - if (l->length() != r->length()) return false; - for (size_t i = 0, L = l->length(); i < L; ++i) { - if (!eq((*l)[i]->key(), (*r)[i]->key(), ctx)) return false; - if (!eq((*l)[i]->value(), (*r)[i]->value(), ctx)) return false; - } - return true; - } break; - case Expression::NULL_VAL: { - return true; - } break; - - default: break; - } - return false; - } - - bool lt(Expression* lhs, Expression* rhs, Context& ctx) - { - if (lhs->concrete_type() != Expression::NUMBER || - rhs->concrete_type() != Expression::NUMBER) - error("may only compare numbers", lhs->path(), lhs->position()); - Number* l = static_cast(lhs); - Number* r = static_cast(rhs); - Number tmp_r(*r); - tmp_r.normalize(l->find_convertible_unit()); - string l_unit(l->unit()); - string r_unit(tmp_r.unit()); - if (!l_unit.empty() && !r_unit.empty() && l->unit() != tmp_r.unit()) { - error("cannot compare numbers with incompatible units", l->path(), l->position()); - } - return l->value() < tmp_r.value(); - } - - Expression* op_numbers(Context& ctx, Binary_Expression* b, Expression* lhs, Expression* rhs) - { - Number* l = static_cast(lhs); - Number* r = static_cast(rhs); - double lv = l->value(); - double rv = r->value(); - Binary_Expression::Type op = b->type(); - if (op == Binary_Expression::DIV && !rv) { - return new (ctx.mem) String_Constant(l->path(), b->position(), "Infinity"); - } - if (op == Binary_Expression::MOD && !rv) { - error("division by zero", r->path(), r->position()); - } - - Number tmp(*r); - tmp.normalize(l->find_convertible_unit()); - string l_unit(l->unit()); - string r_unit(tmp.unit()); - if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() && - (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) { - error("cannot add or subtract numbers with incompatible units", l->path(), l->position()); - } - Number* v = new (ctx.mem) Number(*l); - v->position(b->position()); - if (l_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) { - v->numerator_units() = r->numerator_units(); - v->denominator_units() = r->denominator_units(); - } - - v->value(ops[op](lv, rv)); - if (op == Binary_Expression::MUL) { - for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) { - v->numerator_units().push_back(r->numerator_units()[i]); - } - for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) { - v->denominator_units().push_back(r->denominator_units()[i]); - } - } - else if (op == Binary_Expression::DIV) { - for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) { - v->denominator_units().push_back(r->numerator_units()[i]); - } - for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) { - v->numerator_units().push_back(r->denominator_units()[i]); - } - } - v->normalize(); - return v; - } - - Expression* op_number_color(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) - { - Number* l = static_cast(lhs); - Color* r = static_cast(rhs); - // TODO: currently SASS converts colors to standard form when adding to strings; - // when https://github.com/nex3/sass/issues/363 is added this can be removed to - // preserve the original value - r->disp(""); - double lv = l->value(); - switch (op) { - case Binary_Expression::ADD: - case Binary_Expression::MUL: { - return new (ctx.mem) Color(l->path(), - l->position(), - ops[op](lv, r->r()), - ops[op](lv, r->g()), - ops[op](lv, r->b()), - r->a()); - } break; - case Binary_Expression::SUB: - case Binary_Expression::DIV: { - string sep(op == Binary_Expression::SUB ? "-" : "/"); - To_String to_string; - return new (ctx.mem) String_Constant(l->path(), - l->position(), - l->perform(&to_string) - + sep - + r->perform(&to_string)); - } break; - case Binary_Expression::MOD: { - error("cannot divide a number by a color", r->path(), r->position()); - } break; - default: break; // caller should ensure that we don't get here - } - // unreachable - return l; - } - - Expression* op_color_number(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) - { - Color* l = static_cast(lhs); - Number* r = static_cast(rhs); - double rv = r->value(); - if (op == Binary_Expression::DIV && !rv) error("division by zero", r->path(), r->position()); - return new (ctx.mem) Color(l->path(), - l->position(), - ops[op](l->r(), rv), - ops[op](l->g(), rv), - ops[op](l->b(), rv), - l->a()); - } - - Expression* op_colors(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) - { - Color* l = static_cast(lhs); - Color* r = static_cast(rhs); - if (l->a() != r->a()) { - error("alpha channels must be equal when combining colors", r->path(), r->position()); - } - if ((op == Binary_Expression::DIV || op == Binary_Expression::MOD) && - (!r->r() || !r->g() ||!r->b())) { - error("division by zero", r->path(), r->position()); - } - return new (ctx.mem) Color(l->path(), - l->position(), - ops[op](l->r(), r->r()), - ops[op](l->g(), r->g()), - ops[op](l->b(), r->b()), - l->a()); - } - - Expression* op_strings(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression*rhs) - { - To_String to_string; - Expression::Concrete_Type ltype = lhs->concrete_type(); - Expression::Concrete_Type rtype = rhs->concrete_type(); - - // TODO: currently SASS converts colors to standard form when adding to strings; - // when https://github.com/nex3/sass/issues/363 is added this can be removed to - // preserve the original value - if (ltype == Expression::COLOR) ((Sass::Color*)lhs)->disp(""); - if (rtype == Expression::COLOR) ((Sass::Color*)rhs)->disp(""); - - string lstr(lhs->perform(&to_string)); - string rstr(rhs->perform(&to_string)); - bool unquoted = false; - if (ltype == Expression::STRING && lstr[0] != '"' && lstr[0] != '\'') unquoted = true; - if (ltype == Expression::STRING && !lhs->is_delayed() && ctx.names_to_colors.count(lstr) && - rtype == Expression::STRING && !rhs->is_delayed() && ctx.names_to_colors.count(rstr)) { - return op_colors(ctx, op, ctx.names_to_colors[lstr], ctx.names_to_colors[rstr]); - } - else if (ltype == Expression::STRING && !lhs->is_delayed() && ctx.names_to_colors.count(lstr) && - rtype == Expression::NUMBER) { - return op_color_number(ctx, op, ctx.names_to_colors[lstr], rhs); - } - else if (ltype == Expression::NUMBER && - rtype == Expression::STRING && !rhs->is_delayed() && ctx.names_to_colors.count(rstr)) { - return op_number_color(ctx, op, rhs, ctx.names_to_colors[rstr]); - } - if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->path(), lhs->position()); - if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->path(), lhs->position()); - string sep; - switch (op) { - case Binary_Expression::SUB: sep = "-"; break; - case Binary_Expression::DIV: sep = "/"; break; - default: break; - } - char q = '\0'; - if (lstr[0] == '"' || lstr[0] == '\'') q = lstr[0]; - else if (rstr[0] == '"' || rstr[0] == '\'') q = rstr[0]; - string result(unquote(lstr) + sep + unquote(rstr)); - return new (ctx.mem) String_Constant(lhs->path(), - lhs->position(), - unquoted ? result : quote(result, q)); - } - - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path, Position position) - { - using std::strlen; - using std::strcpy; - Expression* e = 0; - switch (v.unknown.tag) { - case SASS_BOOLEAN: { - e = new (ctx.mem) Boolean(path, position, v.boolean.value); - } break; - case SASS_NUMBER: { - e = new (ctx.mem) Number(path, position, v.number.value, v.number.unit); - } break; - case SASS_COLOR: { - e = new (ctx.mem) Color(path, position, v.color.r, v.color.g, v.color.b, v.color.a); - } break; - case SASS_STRING: { - e = new (ctx.mem) String_Constant(path, position, v.string.value); - } break; - case SASS_LIST: { - List* l = new (ctx.mem) List(path, position, v.list.length, v.list.separator == SASS_COMMA ? List::COMMA : List::SPACE); - for (size_t i = 0, L = v.list.length; i < L; ++i) { - *l << cval_to_astnode(v.list.values[i], ctx, backtrace, path, position); - } - e = l; - } break; - case SASS_MAP: { - Map* m = new (ctx.mem) Map(path, position, v.map.length); - for (size_t i = 0, L = v.map.length; i < L; ++i) { - *m << new (ctx.mem) KeyValuePair(path, position, - cval_to_astnode(v.map.pairs[i].key, ctx, backtrace, path, position), - cval_to_astnode(v.map.pairs[i].value, ctx, backtrace, path, position)); - } - e = m; - } break; - case SASS_NULL: { - e = new (ctx.mem) Null(path, position); - } break; - case SASS_ERROR: { - error("error in C function: " + string(v.error.message), path, position, backtrace); - } break; - } - return e; - } - -} diff --git a/eval.hpp b/eval.hpp deleted file mode 100644 index 767955d5..00000000 --- a/eval.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#define SASS_EVAL - -#include - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#ifndef SASS -#include "sass.h" -#endif - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Context; - typedef Environment Env; - struct Backtrace; - - class Eval : public Operation_CRTP { - - Context& ctx; - - Expression* fallback_impl(AST_Node* n); - - public: - Env* env; - Backtrace* backtrace; - Eval(Context&, Env*, Backtrace*); - virtual ~Eval(); - Eval* with(Env* e, Backtrace* bt); // for setting the env before eval'ing an expression - using Operation::operator(); - - // for evaluating function bodies - Expression* operator()(Block*); - Expression* operator()(Assignment*); - Expression* operator()(If*); - Expression* operator()(For*); - Expression* operator()(Each*); - Expression* operator()(While*); - Expression* operator()(Return*); - Expression* operator()(Warning*); - - Expression* operator()(List*); - Expression* operator()(Map*); - Expression* operator()(Binary_Expression*); - Expression* operator()(Unary_Expression*); - Expression* operator()(Function_Call*); - Expression* operator()(Function_Call_Schema*); - Expression* operator()(Variable*); - Expression* operator()(Textual*); - Expression* operator()(Number*); - Expression* operator()(Boolean*); - Expression* operator()(String_Schema*); - Expression* operator()(String_Constant*); - Expression* operator()(Media_Query*); - Expression* operator()(Media_Query_Expression*); - Expression* operator()(Null*); - Expression* operator()(Argument*); - Expression* operator()(Arguments*); - - template - Expression* fallback(U x) { return fallback_impl(x); } - }; - - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path = "", Position position = Position()); - - bool eq(Expression*, Expression*, Context&); - bool lt(Expression*, Expression*, Context&); -} diff --git a/expand.cpp b/expand.cpp deleted file mode 100644 index 0aa15094..00000000 --- a/expand.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include "expand.hpp" -#include "bind.hpp" -#include "eval.hpp" -#include "contextualize.hpp" -#include "to_string.hpp" -#include "backtrace.hpp" - -#include -#include - -#ifndef SASS_CONTEXT -#include "context.hpp" -#endif - -#include "parser.hpp" - -namespace Sass { - - Expand::Expand(Context& ctx, Eval* eval, Contextualize* contextualize, Env* env, Backtrace* bt) - : ctx(ctx), - eval(eval), - contextualize(contextualize), - env(env), - block_stack(vector()), - property_stack(vector()), - selector_stack(vector()), - backtrace(bt) - { selector_stack.push_back(0); } - - Statement* Expand::operator()(Block* b) - { - Env new_env; - new_env.link(*env); - env = &new_env; - Block* bb = new (ctx.mem) Block(b->path(), b->position(), b->length(), b->is_root()); - block_stack.push_back(bb); - append_block(b); - block_stack.pop_back(); - env = env->parent(); - return bb; - } - - Statement* Expand::operator()(Ruleset* r) - { - To_String to_string; - // if (selector_stack.back()) cerr << "expanding " << selector_stack.back()->perform(&to_string) << " and " << r->selector()->perform(&to_string) << endl; - Selector* sel_ctx = r->selector()->perform(contextualize->with(selector_stack.back(), env, backtrace)); - // re-parse in order to restructure parent nodes correctly - sel_ctx = Parser::from_c_str((sel_ctx->perform(&to_string) + ";").c_str(), ctx, r->selector()->path(), r->selector()->position()).parse_selector_group(); - selector_stack.push_back(sel_ctx); - Ruleset* rr = new (ctx.mem) Ruleset(r->path(), - r->position(), - sel_ctx, - r->block()->perform(this)->block()); - selector_stack.pop_back(); - return rr; - } - - Statement* Expand::operator()(Propset* p) - { - property_stack.push_back(p->property_fragment()); - Block* expanded_block = p->block()->perform(this)->block(); - - Block* current_block = block_stack.back(); - for (size_t i = 0, L = expanded_block->length(); i < L; ++i) { - Statement* stm = (*expanded_block)[i]; - if (typeid(*stm) == typeid(Declaration)) { - Declaration* dec = static_cast(stm); - String_Schema* combined_prop = new (ctx.mem) String_Schema(p->path(), p->position()); - if (!property_stack.empty()) { - *combined_prop << property_stack.back() - << new (ctx.mem) String_Constant(p->path(), p->position(), "-") - << dec->property(); // TODO: eval the prop into a string constant - } - else { - *combined_prop << dec->property(); - } - dec->property(combined_prop); - *current_block << dec; - } - else { - error("contents of namespaced properties must result in style declarations only", stm->path(), stm->position(), backtrace); - } - } - - property_stack.pop_back(); - - return 0; - } - - Statement* Expand::operator()(Media_Block* m) - { - Expression* media_queries = m->media_queries()->perform(eval->with(env, backtrace)); - Media_Block* mm = new (ctx.mem) Media_Block(m->path(), - m->position(), - static_cast(media_queries), - m->block()->perform(this)->block()); - mm->selector(selector_stack.back()); - return mm; - } - - Statement* Expand::operator()(At_Rule* a) - { - Block* ab = a->block(); - selector_stack.push_back(0); - Selector* as = a->selector(); - Expression* av = a->value(); - if (as) as = as->perform(contextualize->with(0, env, backtrace)); - else if (av) av = av->perform(eval->with(env, backtrace)); - Block* bb = ab ? ab->perform(this)->block() : 0; - At_Rule* aa = new (ctx.mem) At_Rule(a->path(), - a->position(), - a->keyword(), - as, - bb); - if (av) aa->value(av); - selector_stack.pop_back(); - return aa; - } - - Statement* Expand::operator()(Declaration* d) - { - String* old_p = d->property(); - String* new_p = static_cast(old_p->perform(eval->with(env, backtrace))); - return new (ctx.mem) Declaration(d->path(), - d->position(), - new_p, - d->value()->perform(eval->with(env, backtrace)), - d->is_important()); - } - - Statement* Expand::operator()(Assignment* a) - { - string var(a->variable()); - if (env->has(var)) { - if(!a->is_guarded()) (*env)[var] = a->value()->perform(eval->with(env, backtrace)); - } - else { - env->current_frame()[var] = a->value()->perform(eval->with(env, backtrace)); - } - return 0; - } - - Statement* Expand::operator()(Import* imp) - { - Import* result = new (ctx.mem) Import(imp->path(), imp->position()); - for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) { - result->urls().push_back(imp->urls()[i]->perform(eval->with(env, backtrace))); - } - return result; - } - - Statement* Expand::operator()(Import_Stub* i) - { - append_block(ctx.style_sheets[i->file_name()]); - return 0; - } - - Statement* Expand::operator()(Warning* w) - { - // eval handles this too, because warnings may occur in functions - w->perform(eval->with(env, backtrace)); - return 0; - } - - Statement* Expand::operator()(Comment* c) - { - // TODO: eval the text, once we're parsing/storing it as a String_Schema - return new (ctx.mem) Comment(c->path(), c->position(), static_cast(c->text()->perform(eval->with(env, backtrace)))); - } - - Statement* Expand::operator()(If* i) - { - if (*i->predicate()->perform(eval->with(env, backtrace))) { - append_block(i->consequent()); - } - else { - Block* alt = i->alternative(); - if (alt) append_block(alt); - } - return 0; - } - - Statement* Expand::operator()(For* f) - { - string variable(f->variable()); - Expression* low = f->lower_bound()->perform(eval->with(env, backtrace)); - if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->path(), low->position(), backtrace); - } - Expression* high = f->upper_bound()->perform(eval->with(env, backtrace)); - if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->path(), high->position(), backtrace); - } - double lo = static_cast(low)->value(); - double hi = static_cast(high)->value(); - if (f->is_inclusive()) ++hi; - Env new_env; - new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), lo); - new_env.link(env); - env = &new_env; - Block* body = f->block(); - for (double i = lo; - i < hi; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) { - append_block(body); - } - env = new_env.parent(); - return 0; - } - - Statement* Expand::operator()(Each* e) - { - string variable(e->variable()); - Expression* expr = e->list()->perform(eval->with(env, backtrace)); - List* list = 0; - if (expr->concrete_type() != Expression::LIST) { - list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA); - *list << expr; - } - else { - list = static_cast(expr); - } - Env new_env; - new_env[variable] = 0; - new_env.link(env); - env = &new_env; - Block* body = e->block(); - for (size_t i = 0, L = list->length(); i < L; ++i) { - (*env)[variable] = (*list)[i]->perform(eval->with(env, backtrace)); - append_block(body); - } - env = new_env.parent(); - return 0; - } - - Statement* Expand::operator()(While* w) - { - Expression* pred = w->predicate(); - Block* body = w->block(); - while (*pred->perform(eval->with(env, backtrace))) { - append_block(body); - } - return 0; - } - - Statement* Expand::operator()(Return* r) - { - error("@return may only be used within a function", r->path(), r->position(), backtrace); - return 0; - } - - Statement* Expand::operator()(Extension* e) - { - To_String to_string; - Selector_List* extender = static_cast(selector_stack.back()); - if (!extender) return 0; - Selector_List* extendee = static_cast(e->selector()->perform(contextualize->with(0, env, backtrace))); - if (extendee->length() != 1) { - error("selector groups may not be extended", extendee->path(), extendee->position(), backtrace); - } - Complex_Selector* c = (*extendee)[0]; - if (!c->head() || c->tail()) { - error("nested selectors may not be extended", c->path(), c->position(), backtrace); - } - Compound_Selector* s = c->head(); - - // // need to convert the compound selector into a by-value data structure - // vector target_vec; - // for (size_t i = 0, L = s->length(); i < L; ++i) - // { target_vec.push_back((*s)[i]->perform(&to_string)); } - - for (size_t i = 0, L = extender->length(); i < L; ++i) { - // let's test this out - // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << s->perform(&to_string) << endl; - ctx.subset_map.put(s->to_str_vec(), make_pair((*extender)[i], s)); - } - return 0; - } - - Statement* Expand::operator()(Definition* d) - { - Definition* dd = new (ctx.mem) Definition(*d); - env->current_frame()[d->name() + - (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd; - // set the static link so we can have lexical scoping - dd->environment(env); - return 0; - } - - Statement* Expand::operator()(Mixin_Call* c) - { - string full_name(c->name() + "[m]"); - if (!env->has(full_name)) { - error("no mixin named " + c->name(), c->path(), c->position(), backtrace); - } - Definition* def = static_cast((*env)[full_name]); - Block* body = def->block(); - Parameters* params = def->parameters(); - Arguments* args = static_cast(c->arguments() - ->perform(eval->with(env, backtrace))); - Backtrace here(backtrace, c->path(), c->position(), ", in mixin `" + c->name() + "`"); - backtrace = &here; - Env new_env; - new_env.link(def->environment()); - if (c->block()) { - // represent mixin content blocks as thunks/closures - Definition* thunk = new (ctx.mem) Definition(c->path(), - c->position(), - "@content", - new (ctx.mem) Parameters(c->path(), c->position()), - c->block(), - Definition::MIXIN); - thunk->environment(env); - new_env.current_frame()["@content[m]"] = thunk; - } - bind("mixin " + c->name(), params, args, ctx, &new_env, eval); - Env* old_env = env; - env = &new_env; - append_block(body); - env = old_env; - backtrace = here.parent; - return 0; - } - - Statement* Expand::operator()(Content* c) - { - // convert @content directives into mixin calls to the underlying thunk - if (!env->has("@content[m]")) return 0; - Mixin_Call* call = new (ctx.mem) Mixin_Call(c->path(), - c->position(), - "@content", - new (ctx.mem) Arguments(c->path(), c->position())); - return call->perform(this); - } - - inline Statement* Expand::fallback_impl(AST_Node* n) - { - error("unknown internal error; please contact the LibSass maintainers", n->path(), n->position(), backtrace); - String_Constant* msg = new (ctx.mem) String_Constant("", Position(), string("`Expand` doesn't handle ") + typeid(*n).name()); - return new (ctx.mem) Warning("", Position(), msg); - } - - inline void Expand::append_block(Block* b) - { - Block* current_block = block_stack.back(); - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* ith = (*b)[i]->perform(this); - if (ith) *current_block << ith; - } - } -} diff --git a/expand.hpp b/expand.hpp deleted file mode 100644 index ff4414a6..00000000 --- a/expand.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#define SASS_EXPAND - -#include -#include -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Context; - class Eval; - class Contextualize; - typedef Environment Env; - struct Backtrace; - - class Expand : public Operation_CRTP { - - Context& ctx; - Eval* eval; - Contextualize* contextualize; - Env* env; - vector block_stack; - vector property_stack; - vector selector_stack; - Backtrace* backtrace; - - Statement* fallback_impl(AST_Node* n); - - public: - Expand(Context&, Eval*, Contextualize*, Env*, Backtrace*); - virtual ~Expand() { } - - using Operation::operator(); - - Statement* operator()(Block*); - Statement* operator()(Ruleset*); - Statement* operator()(Propset*); - Statement* operator()(Media_Block*); - Statement* operator()(At_Rule*); - Statement* operator()(Declaration*); - Statement* operator()(Assignment*); - Statement* operator()(Import*); - Statement* operator()(Import_Stub*); - Statement* operator()(Warning*); - Statement* operator()(Comment*); - Statement* operator()(If*); - Statement* operator()(For*); - Statement* operator()(Each*); - Statement* operator()(While*); - Statement* operator()(Return*); - Statement* operator()(Extension*); - Statement* operator()(Definition*); - Statement* operator()(Mixin_Call*); - Statement* operator()(Content*); - - template - Statement* fallback(U x) { return fallback_impl(x); } - - void append_block(Block*); - }; - -} diff --git a/extconf.rb b/extconf.rb deleted file mode 100644 index 3e6d00bc..00000000 --- a/extconf.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'mkmf' -# .. more stuff -#$LIBPATH.push(Config::CONFIG['libdir']) -$CFLAGS << " #{ENV["CFLAGS"]}" -$LIBS << " #{ENV["LIBS"]}" -create_makefile("libsass") diff --git a/extend.cpp b/extend.cpp deleted file mode 100644 index aee5163d..00000000 --- a/extend.cpp +++ /dev/null @@ -1,1960 +0,0 @@ -#include "extend.hpp" -#include "context.hpp" -#include "contextualize.hpp" -#include "to_string.hpp" -#include "backtrace.hpp" -#include "paths.hpp" -#include "parser.hpp" -#ifndef SASS_AST -#include "node.hpp" -#endif -#include "sass_util.hpp" -#include "debug.hpp" -#include -#include - -/* - NOTES: - - - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which - is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what - ruby already outputs (to make comparisons easier). - - - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing. - Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass. - The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually - used for the porting. - - - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby - output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to - the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see - something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment. - - - Coding conventions in this file (these may need to be changed before merging back into master) - - Very basic hungarian notation: - p prefix for pointers (pSelector) - no prefix for value types and references (selector) - - Use STL iterators where possible - - prefer verbose naming over terse naming - - use typedefs for STL container types for make maintenance easier - - - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators - in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby - sass causes this to be necessary. - - - GLOBAL TODOS: - - - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode. - - - consider making the extend* functions member functions to avoid passing around ctx and subsetMap map around. This has the - drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and - can cause additional compile time dependencies. - - - mark the helper methods in this file static to given them compilation unit linkage. - - - implement parent directive matching - - - fix compilation warnings for unused Extend members if we really don't need those references anymore. - */ - - -namespace Sass { - - - typedef pair ExtensionPair; - typedef vector SubsetMapEntries; - - - -#ifdef DEBUG - - // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp - ostream& operator<<(ostream& os, const Complex_Selector::Combinator combinator) { - switch (combinator) { - case Complex_Selector::ANCESTOR_OF: os << "\" \""; break; - case Complex_Selector::PARENT_OF: os << "\">\""; break; - case Complex_Selector::PRECEDES: os << "\"~\""; break; - case Complex_Selector::ADJACENT_TO: os << "\"+\""; break; - } - - return os; - } - - - ostream& operator<<(ostream& os, Compound_Selector& compoundSelector) { - To_String to_string; - os << compoundSelector.perform(&to_string); - return os; - } - - - // Print a string representation of a Compound_Selector - static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) { - To_String to_string; - - if (message) { - cerr << message; - } - - if (pCompoundSelector) { - cerr << *pCompoundSelector; - } else { - cerr << "NULL"; - } - - if (newline) { - cerr << endl; - } - } - - - ostream& operator<<(ostream& os, Complex_Selector& complexSelector) { - To_String to_string; - - os << "["; - Complex_Selector* pIter = &complexSelector; - bool first = true; - while (pIter) { - if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) { - if (!first) { - os << ", "; - } - first = false; - os << pIter->combinator(); - } - - if (!first) { - os << ", "; - } - first = false; - - if (pIter->head()) { - os << pIter->head()->perform(&to_string); - } else { - os << "NULL_HEAD"; - } - - pIter = pIter->tail(); - } - os << "]"; - - return os; - } - - - // Print a string representation of a Complex_Selector - static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) { - To_String to_string; - - if (message) { - cerr << message; - } - - if (pComplexSelector) { - cerr << *pComplexSelector; - } else { - cerr << "NULL"; - } - - if (newline) { - cerr << endl; - } - } - - - // Print a string representation of a SourcesSet - static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) { - To_String to_string; - - if (message) { - cerr << message; - } - - // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on - // the differences we see when debug printing. - typedef deque SourceStrings; - SourceStrings sourceStrings; - for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) { - Complex_Selector* pSource = *iterator; - stringstream sstream; - sstream << complexSelectorToNode(pSource, ctx); - sourceStrings.push_back(sstream.str()); - } - - // Sort to get consistent output - std::sort(sourceStrings.begin(), sourceStrings.end()); - - cerr << "SourcesSet["; - for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) { - string source = *iterator; - if (iterator != sourceStrings.begin()) { - cerr << ", "; - } - cerr << source; - } - cerr << "]"; - - if (newline) { - cerr << endl; - } - } - - - ostream& operator<<(ostream& os, SubsetMapEntries& entries) { - os << "SUBSET_MAP_ENTRIES["; - - for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) { - Complex_Selector* pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge) - Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend - - if (iterator != entries.begin()) { - os << ", "; - } - - os << "("; - - if (pExtComplexSelector) { - cerr << *pExtComplexSelector; - } else { - cerr << "NULL"; - } - - os << " -> "; - - if (pExtCompoundSelector) { - cerr << *pExtCompoundSelector; - } else { - cerr << "NULL"; - } - - os << ")"; - - } - - os << "]"; - - return os; - } - - -#endif - - - static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo, Context& ctx) { - // TODO: figure out a better way to create a Complex_Selector from scratch - // TODO: There's got to be a better way. This got ugly quick... - Position noPosition; - Type_Selector fakeParent("", noPosition, "temp"); - Compound_Selector fakeHead("", noPosition, 1 /*size*/); - fakeHead.elements().push_back(&fakeParent); - Complex_Selector fakeParentContainer("", noPosition, Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); - - - pOne->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); - pTwo->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); - - bool isSuperselector = pOne->is_superselector_of(pTwo); - - pOne->clear_innermost(); - pTwo->clear_innermost(); - - return isSuperselector; - } - - - void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out, Context& ctx) { - for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { - Node& child = *iter; - out.push_back(nodeToComplexSelector(child, ctx)); - } - } - - Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque, Context& ctx) { - Node result = Node::createCollection(); - - for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) { - Complex_Selector* pChild = *iter; - result.collection()->push_back(complexSelectorToNode(pChild, ctx)); - } - - return result; - } - - - class LcsCollectionComparator { - public: - LcsCollectionComparator(Context& ctx) : mCtx(ctx) {} - - Context& mCtx; - - bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& pOut) const { - /* - This code is based on the following block from ruby sass' subweave - do |s1, s2| - next s1 if s1 == s2 - next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence) - next s2 if parent_superselector?(s1, s2) - next s1 if parent_superselector?(s2, s1) - end - */ - - if (selectors_equal(*pOne, *pTwo, true /*simpleSelectorOrderDependent*/)) { - pOut = pOne; - return true; - } - - if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) { - return false; - } - - if (parentSuperselector(pOne, pTwo, mCtx)) { - pOut = pTwo; - return true; - } - - if (parentSuperselector(pTwo, pOne, mCtx)) { - pOut = pOne; - return true; - } - - return false; - } - }; - - - /* - This is the equivalent of ruby's Sass::Util.lcs_backtrace. - - # Computes a single longest common subsequence for arrays x and y. - # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS - */ - void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) { - //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j) - // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output - - if (i == 0 || j == 0) { - DEBUG_PRINTLN(LCS, "RETURNING EMPTY") - return; - } - - - Complex_Selector* pCompareOut = NULL; - if (comparator(x[i], y[j], pCompareOut)) { - DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE") - lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out); - out.push_back(pCompareOut); - return; - } - - if (c[i][j - 1] > c[i - 1][j]) { - DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE") - lcs_backtrace(c, x, y, i, j - 1, comparator, out); - return; - } - - DEBUG_PRINTLN(LCS, "FINAL RETURN") - lcs_backtrace(c, x, y, i - 1, j, comparator, out); - return; - } - - /* - This is the equivalent of ruby's Sass::Util.lcs_table. - - # Calculates the memoization table for the Least Common Subsequence algorithm. - # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS - */ - void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) { - //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y) - // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output - - LCSTable c(x.size(), vector(y.size())); - - // These shouldn't be necessary since the vector will be initialized to 0 already. - // x.size.times {|i| c[i][0] = 0} - // y.size.times {|j| c[0][j] = 0} - - for (size_t i = 1; i < x.size(); i++) { - for (size_t j = 1; j < y.size(); j++) { - Complex_Selector* pCompareOut = NULL; - - if (comparator(x[i], y[j], pCompareOut)) { - c[i][j] = c[i - 1][j - 1] + 1; - } else { - c[i][j] = max(c[i][j - 1], c[i - 1][j]); - } - } - } - - out = c; - } - - /* - This is the equivalent of ruby's Sass::Util.lcs. - - # Computes a single longest common subsequence for `x` and `y`. - # If there are more than one longest common subsequences, - # the one returned is that which starts first in `x`. - - # @param x [NodeCollection] - # @param y [NodeCollection] - # @comparator An equality check between elements of `x` and `y`. - # @return [NodeCollection] The LCS - - http://en.wikipedia.org/wiki/Longest_common_subsequence_problem - */ - void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, Context& ctx, ComplexSelectorDeque& out) { - //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y) - // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output - - x.push_front(NULL); - y.push_front(NULL); - - LCSTable table; - lcs_table(x, y, comparator, table); - - return lcs_backtrace(table, x, y, x.size() - 1, y.size() - 1, comparator, out); - } - - - /* - This is the equivalent of ruby's Sequence.trim. - - The following is the modified version of the ruby code that was more portable to C++. You - should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. - - # Avoid truly horrific quadratic behavior. TODO: I think there - # may be a way to get perfect trimming without going quadratic. - return seqses if seqses.size > 100 - - # Keep the results in a separate array so we can be sure we aren't - # comparing against an already-trimmed selector. This ensures that two - # identical selectors don't mutually trim one another. - result = seqses.dup - - # This is n^2 on the sequences, but only comparing between - # separate sequences should limit the quadratic behavior. - seqses.each_with_index do |seqs1, i| - tempResult = [] - - for seq1 in seqs1 do - max_spec = 0 - for seq in _sources(seq1) do - max_spec = [max_spec, seq.specificity].max - end - - - isMoreSpecificOuter = false - for seqs2 in result do - if seqs1.equal?(seqs2) then - next - end - - # Second Law of Extend: the specificity of a generated selector - # should never be less than the specificity of the extending - # selector. - # - # See https://github.com/nex3/sass/issues/324. - isMoreSpecificInner = false - for seq2 in seqs2 do - isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1) - if isMoreSpecificInner then - break - end - end - - if isMoreSpecificInner then - isMoreSpecificOuter = true - break - end - end - - if !isMoreSpecificOuter then - tempResult.push(seq1) - end - end - - result[i] = tempResult - - end - - result - */ - /* - - IMPROVEMENT: We could probably work directly in the output trimmed deque. - */ - static Node trim(Node& seqses, Context& ctx) { - // See the comments in the above ruby code before embarking on understanding this function. - - // Avoid poor performance in extreme cases. - if (seqses.collection()->size() > 100) { - return seqses; - } - - - DEBUG_PRINTLN(TRIM, "TRIM: " << seqses) - - - Node result = Node::createCollection(); - result.plus(seqses); - - DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result) - - // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're - // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track - // of the index manually. - int toTrimIndex = 0; - - for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) { - Node& seqs1 = *seqsesIter; - - DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex) - - Node tempResult = Node::createCollection(); - - for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) { - Node& seq1 = *seqs1Iter; - - Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx); - - // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code - // for a good description of sources. - // - // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test. - // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We - // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My - // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely - // a guess though. - int maxSpecificity = 0; - SourcesSet sources = pSeq1->sources(); - - DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1) - DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: ")) - - for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) { - const Complex_Selector* const pCurrentSelector = *sourcesSetIterator; - maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity()); - } - - DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity) - - bool isMoreSpecificOuter = false; - - int resultIndex = 0; - - for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) { - Node& seqs2 = *resultIter; - - DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1) - DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2) - - // Do not compare the same sequence to itself. The ruby call we're trying to - // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision. - // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is - // derived from seqses and seqs2 is derived from result. - if (seqs1.collection() == seqs2.collection()) { - DEBUG_PRINTLN(TRIM, "CONTINUE") - continue; - } - - bool isMoreSpecificInner = false; - - for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) { - Node& seq2 = *seqs2Iter; - - Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx); - - DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity()) - DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false")) - DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false")) - - isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1); - - if (isMoreSpecificInner) { - DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC") - break; - } - } - - // If we found something more specific, we're done. Let the outer loop know and stop iterating. - if (isMoreSpecificInner) { - isMoreSpecificOuter = true; - break; - } - - resultIndex++; - } - - if (!isMoreSpecificOuter) { - DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1) - tempResult.collection()->push_back(seq1); - } - - } - - DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result) - DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult) - (*result.collection())[toTrimIndex] = tempResult; - - toTrimIndex++; - - DEBUG_PRINTLN(TRIM, "RESULT: " << result) - } - - return result; - } - - - - static bool parentSuperselector(const Node& one, const Node& two, Context& ctx) { - // TODO: figure out a better way to create a Complex_Selector from scratch - // TODO: There's got to be a better way. This got ugly quick... - Position noPosition; - Type_Selector fakeParent("", noPosition, "temp"); - Compound_Selector fakeHead("", noPosition, 1 /*size*/); - fakeHead.elements().push_back(&fakeParent); - Complex_Selector fakeParentContainer("", noPosition, Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/); - - Complex_Selector* pOneWithFakeParent = nodeToComplexSelector(one, ctx); - pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); - Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx); - pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF); - - return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent); - } - - - class ParentSuperselectorChunker { - public: - ParentSuperselectorChunker(Node& lcs, Context& ctx) : mLcs(lcs), mCtx(ctx) {} - Node& mLcs; - Context& mCtx; - - bool operator()(const Node& seq) const { - // {|s| parent_superselector?(s.first, lcs.first)} - return parentSuperselector(seq.collection()->front(), mLcs.collection()->front(), mCtx); - } - }; - - class SubweaveEmptyChunker { - public: - bool operator()(const Node& seq) const { - // {|s| s.empty?} - - return seq.collection()->empty(); - } - }; - - /* - # Takes initial subsequences of `seq1` and `seq2` and returns all - # orderings of those subsequences. The initial subsequences are determined - # by a block. - # - # Destructively removes the initial subsequences of `seq1` and `seq2`. - # - # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|` - # denoting the boundary of the initial subsequence), this would return - # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and - # `(3 4 5)`. - # - # @param seq1 [Array] - # @param seq2 [Array] - # @yield [a] Used to determine when to cut off the initial subsequences. - # Called repeatedly for each sequence until it returns true. - # @yieldparam a [Array] A final subsequence of one input sequence after - # cutting off some initial subsequence. - # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence - # here. - # @return [Array] All possible orderings of the initial subsequences. - def chunks(seq1, seq2) - chunk1 = [] - chunk1 << seq1.shift until yield seq1 - chunk2 = [] - chunk2 << seq2.shift until yield seq2 - return [] if chunk1.empty? && chunk2.empty? - return [chunk2] if chunk1.empty? - return [chunk1] if chunk2.empty? - [chunk1 + chunk2, chunk2 + chunk1] - end - */ - template - static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) { - Node chunk1 = Node::createCollection(); - while (!chunker(seq1)) { - chunk1.collection()->push_back(seq1.collection()->front()); - seq1.collection()->pop_front(); - } - - Node chunk2 = Node::createCollection(); - while (!chunker(seq2)) { - chunk2.collection()->push_back(seq2.collection()->front()); - seq2.collection()->pop_front(); - } - - if (chunk1.collection()->empty() && chunk2.collection()->empty()) { - DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY") - return Node::createCollection(); - } - - if (chunk1.collection()->empty()) { - Node chunk2Wrapper = Node::createCollection(); - chunk2Wrapper.collection()->push_back(chunk2); - DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY") - return chunk2Wrapper; - } - - if (chunk2.collection()->empty()) { - Node chunk1Wrapper = Node::createCollection(); - chunk1Wrapper.collection()->push_back(chunk1); - DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY") - return chunk1Wrapper; - } - - Node perms = Node::createCollection(); - - Node firstPermutation = Node::createCollection(); - firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end()); - firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end()); - perms.collection()->push_back(firstPermutation); - - Node secondPermutation = Node::createCollection(); - secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end()); - secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end()); - perms.collection()->push_back(secondPermutation); - - DEBUG_PRINTLN(CHUNKS, "RETURNING PERM") - - return perms; - } - - - static Node groupSelectors(Node& seq, Context& ctx) { - Node newSeq = Node::createCollection(); - - Node tail = Node::createCollection(); - tail.plus(seq); - - while (!tail.collection()->empty()) { - Node head = Node::createCollection(); - - do { - head.collection()->push_back(tail.collection()->front()); - tail.collection()->pop_front(); - } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator())); - - newSeq.collection()->push_back(head); - } - - return newSeq; - } - - - static void getAndRemoveInitialOps(Node& seq, Node& ops) { - NodeDeque& seqCollection = *(seq.collection()); - NodeDeque& opsCollection = *(ops.collection()); - - while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) { - opsCollection.push_back(seqCollection.front()); - seqCollection.pop_front(); - } - } - - - static void getAndRemoveFinalOps(Node& seq, Node& ops) { - NodeDeque& seqCollection = *(seq.collection()); - NodeDeque& opsCollection = *(ops.collection()); - - while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) { - opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code - seqCollection.pop_back(); - } - } - - - /* - def merge_initial_ops(seq1, seq2) - ops1, ops2 = [], [] - ops1 << seq1.shift while seq1.first.is_a?(String) - ops2 << seq2.shift while seq2.first.is_a?(String) - - newline = false - newline ||= !!ops1.shift if ops1.first == "\n" - newline ||= !!ops2.shift if ops2.first == "\n" - - # If neither sequence is a subsequence of the other, they cannot be - # merged successfully - lcs = Sass::Util.lcs(ops1, ops2) - return unless lcs == ops1 || lcs == ops2 - return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2) - end - */ - static Node mergeInitialOps(Node& seq1, Node& seq2, Context& ctx) { - Node ops1 = Node::createCollection(); - Node ops2 = Node::createCollection(); - - getAndRemoveInitialOps(seq1, ops1); - getAndRemoveInitialOps(seq2, ops2); - - // TODO: Do we have this information available to us? - // newline = false - // newline ||= !!ops1.shift if ops1.first == "\n" - // newline ||= !!ops2.shift if ops2.first == "\n" - - // If neither sequence is a subsequence of the other, they cannot be merged successfully - DefaultLcsComparator lcsDefaultComparator; - Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx); - - if (!(opsLcs == ops1 || opsLcs == ops2)) { - return Node::createNil(); - } - - // TODO: more newline logic - // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2) - - return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2); - } - - - /* - def merge_final_ops(seq1, seq2, res = []) - - - # This code looks complicated, but it's actually just a bunch of special - # cases for interactions between different combinators. - op1, op2 = ops1.first, ops2.first - if op1 && op2 - sel1 = seq1.pop - sel2 = seq2.pop - if op1 == '~' && op2 == '~' - if sel1.superselector?(sel2) - res.unshift sel2, '~' - elsif sel2.superselector?(sel1) - res.unshift sel1, '~' - else - merged = sel1.unify(sel2.members, sel2.subject?) - res.unshift [ - [sel1, '~', sel2, '~'], - [sel2, '~', sel1, '~'], - ([merged, '~'] if merged) - ].compact - end - elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~') - if op1 == '~' - tilde_sel, plus_sel = sel1, sel2 - else - tilde_sel, plus_sel = sel2, sel1 - end - - if tilde_sel.superselector?(plus_sel) - res.unshift plus_sel, '+' - else - merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?) - res.unshift [ - [tilde_sel, '~', plus_sel, '+'], - ([merged, '+'] if merged) - ].compact - end - elsif op1 == '>' && %w[~ +].include?(op2) - res.unshift sel2, op2 - seq1.push sel1, op1 - elsif op2 == '>' && %w[~ +].include?(op1) - res.unshift sel1, op1 - seq2.push sel2, op2 - elsif op1 == op2 - return unless merged = sel1.unify(sel2.members, sel2.subject?) - res.unshift merged, op1 - else - # Unknown selector combinators can't be unified - return - end - return merge_final_ops(seq1, seq2, res) - elsif op1 - seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last) - res.unshift seq1.pop, op1 - return merge_final_ops(seq1, seq2, res) - else # op2 - seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last) - res.unshift seq2.pop, op2 - return merge_final_ops(seq1, seq2, res) - end - end - */ - static Node mergeFinalOps(Node& seq1, Node& seq2, Context& ctx, Node& res) { - - Node ops1 = Node::createCollection(); - Node ops2 = Node::createCollection(); - - getAndRemoveFinalOps(seq1, ops1); - getAndRemoveFinalOps(seq2, ops2); - - // TODO: do we have newlines to remove? - // ops1.reject! {|o| o == "\n"} - // ops2.reject! {|o| o == "\n"} - - if (ops1.collection()->empty() && ops2.collection()->empty()) { - return res; - } - - if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) { - DefaultLcsComparator lcsDefaultComparator; - Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx); - - // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up. - - if (!(opsLcs == ops1 || opsLcs == ops2)) { - return Node::createNil(); - } - - if (ops1.collection()->size() > ops2.collection()->size()) { - res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend()); - } else { - res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend()); - } - - return res; - } - - if (!ops1.collection()->empty() && !ops2.collection()->empty()) { - - Node op1 = ops1.collection()->front(); - Node op2 = ops2.collection()->front(); - - Node sel1 = seq1.collection()->back(); - seq1.collection()->pop_back(); - - Node sel2 = seq2.collection()->back(); - seq2.collection()->pop_back(); - - if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) { - - if (sel1.selector()->is_superselector_of(sel2.selector())) { - - res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/); - res.collection()->push_front(sel2); - - } else if (sel2.selector()->is_superselector_of(sel1.selector())) { - - res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/); - res.collection()->push_front(sel1); - - } else { - - DEBUG_PRINTLN(ALL, "sel1: " << sel1) - DEBUG_PRINTLN(ALL, "sel2: " << sel2) - - Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result - // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?) - Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx); - pMergedWrapper->head(pMerged); - - DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: ")) - - Node newRes = Node::createCollection(); - - Node firstPerm = Node::createCollection(); - firstPerm.collection()->push_back(sel1); - firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - firstPerm.collection()->push_back(sel2); - firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - newRes.collection()->push_back(firstPerm); - - Node secondPerm = Node::createCollection(); - secondPerm.collection()->push_back(sel2); - secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - secondPerm.collection()->push_back(sel1); - secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - newRes.collection()->push_back(secondPerm); - - if (pMerged) { - Node mergedPerm = Node::createCollection(); - mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx)); - mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - newRes.collection()->push_back(mergedPerm); - } - - res.collection()->push_front(newRes); - - DEBUG_PRINTLN(ALL, "RESULT: " << res) - - } - - } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) { - - Node tildeSel = sel1; - Node tildeOp = op1; - Node plusSel = sel2; - Node plusOp = op2; - if (op1.combinator() != Complex_Selector::PRECEDES) { - tildeSel = sel2; - tildeOp = op2; - plusSel = sel1; - plusOp = op1; - } - - if (tildeSel.selector()->is_superselector_of(plusSel.selector())) { - - res.collection()->push_front(plusOp); - res.collection()->push_front(plusSel); - - } else { - - DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel) - DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel) - - Complex_Selector* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result - // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?) - Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx); - pMergedWrapper->head(pMerged); - - DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: ")) - - Node newRes = Node::createCollection(); - - Node firstPerm = Node::createCollection(); - firstPerm.collection()->push_back(tildeSel); - firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES)); - firstPerm.collection()->push_back(plusSel); - firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO)); - newRes.collection()->push_back(firstPerm); - - if (pMerged) { - Node mergedPerm = Node::createCollection(); - mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx)); - mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO)); - newRes.collection()->push_back(mergedPerm); - } - - res.collection()->push_front(newRes); - - DEBUG_PRINTLN(ALL, "RESULT: " << res) - - } - } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) { - - res.collection()->push_front(op2); - res.collection()->push_front(sel2); - - seq2.collection()->push_back(sel1); - seq2.collection()->push_back(op1); - - } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) { - - res.collection()->push_front(op1); - res.collection()->push_front(sel1); - - seq2.collection()->push_back(sel2); - seq2.collection()->push_back(op2); - - } else if (op1.combinator() == op2.combinator()) { - - DEBUG_PRINTLN(ALL, "sel1: " << sel1) - DEBUG_PRINTLN(ALL, "sel2: " << sel2) - - Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result - // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?) - Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx); - pMergedWrapper->head(pMerged); - - DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: ")) - - if (!pMerged) { - return Node::createNil(); - } - - res.collection()->push_front(op1); - res.collection()->push_front(Node::createSelector(pMergedWrapper, ctx)); - - DEBUG_PRINTLN(ALL, "RESULT: " << res) - - } else { - return Node::createNil(); - } - - return mergeFinalOps(seq1, seq2, ctx, res); - - } else if (!ops1.collection()->empty()) { - - Node op1 = ops1.collection()->front(); - - if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) { - seq2.collection()->pop_back(); - } - - // TODO: consider unshift(NodeCollection, Node) - res.collection()->push_front(op1); - res.collection()->push_front(seq1.collection()->back()); - seq1.collection()->pop_back(); - - return mergeFinalOps(seq1, seq2, ctx, res); - - } else { // !ops2.collection()->empty() - - Node op2 = ops2.collection()->front(); - - if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) { - seq1.collection()->pop_back(); - } - - res.collection()->push_front(op2); - res.collection()->push_front(seq2.collection()->back()); - seq2.collection()->pop_back(); - - return mergeFinalOps(seq1, seq2, ctx, res); - - } - - } - - - /* - This is the equivalent of ruby's Sequence.subweave. - - Here is the original subweave code for reference during porting. - - def subweave(seq1, seq2) - return [seq2] if seq1.empty? - return [seq1] if seq2.empty? - - seq1, seq2 = seq1.dup, seq2.dup - return unless init = merge_initial_ops(seq1, seq2) - return unless fin = merge_final_ops(seq1, seq2) - seq1 = group_selectors(seq1) - seq2 = group_selectors(seq2) - lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2| - next s1 if s1 == s2 - next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence) - next s2 if parent_superselector?(s1, s2) - next s1 if parent_superselector?(s2, s1) - end - - diff = [[init]] - until lcs.empty? - diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift] - seq1.shift - seq2.shift - end - diff << chunks(seq1, seq2) {|s| s.empty?} - diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]} - diff.reject! {|c| c.empty?} - - result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)} - - result - end - */ - static Node subweave(Node& one, Node& two, Context& ctx) { - // Check for the simple cases - if (one.collection()->size() == 0) { - Node out = Node::createCollection(); - out.collection()->push_back(two); - return out; - } - if (two.collection()->size() == 0) { - Node out = Node::createCollection(); - out.collection()->push_back(one); - return out; - } - - - - Node seq1 = Node::createCollection(); - seq1.plus(one); - Node seq2 = Node::createCollection(); - seq2.plus(two); - - DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1) - DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2) - - Node init = mergeInitialOps(seq1, seq2, ctx); - if (init.isNil()) { - return Node::createNil(); - } - - DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init) - - Node res = Node::createCollection(); - Node fin = mergeFinalOps(seq1, seq2, ctx, res); - if (fin.isNil()) { - return Node::createNil(); - } - - DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin) - - - // Moving this line up since fin isn't modified between now and when it happened before - // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]} - - for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end(); - finIter != finEndIter; ++finIter) { - - Node& childNode = *finIter; - - if (!childNode.isCollection()) { - Node wrapper = Node::createCollection(); - wrapper.collection()->push_back(childNode); - childNode = wrapper; - } - - } - - DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin) - - - - Node groupSeq1 = groupSelectors(seq1, ctx); - DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1) - - Node groupSeq2 = groupSelectors(seq2, ctx); - DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2) - - - ComplexSelectorDeque groupSeq1Converted; - nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx); - - ComplexSelectorDeque groupSeq2Converted; - nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx); - - ComplexSelectorDeque out; - LcsCollectionComparator collectionComparator(ctx); - lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out); - Node seqLcs = complexSelectorDequeToNode(out, ctx); - - DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs) - - - Node initWrapper = Node::createCollection(); - initWrapper.collection()->push_back(init); - Node diff = Node::createCollection(); - diff.collection()->push_back(initWrapper); - - DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff) - - - while (!seqLcs.collection()->empty()) { - ParentSuperselectorChunker superselectorChunker(seqLcs, ctx); - Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker); - diff.collection()->push_back(chunksResult); - - Node lcsWrapper = Node::createCollection(); - lcsWrapper.collection()->push_back(seqLcs.collection()->front()); - seqLcs.collection()->pop_front(); - diff.collection()->push_back(lcsWrapper); - - groupSeq1.collection()->pop_front(); - groupSeq2.collection()->pop_front(); - } - - DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff) - - - DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2) - - - SubweaveEmptyChunker emptyChunker; - Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker); - diff.collection()->push_back(chunksResult); - - - DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff) - - - diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end()); - - DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff) - - // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection) - Node diffFiltered = Node::createCollection(); - for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end(); - diffIter != diffEndIter; ++diffIter) { - Node& node = *diffIter; - if (node.collection() && !node.collection()->empty()) { - diffFiltered.collection()->push_back(node); - } - } - diff = diffFiltered; - - DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff) - - - Node pathsResult = paths(diff, ctx); - - DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult) - - - // We're flattening in place - for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end(); - pathsIter != pathsEndIter; ++pathsIter) { - - Node& child = *pathsIter; - child = flatten(child, ctx); - } - - DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult) - - - /* - TODO: implement - rejected = mapped.reject {|p| path_has_two_subjects?(p)} - $stderr.puts "REJECTED: #{rejected}" - */ - - - return pathsResult; - - } - /* - // disabled to avoid clang warning [-Wunused-function] - static Node subweaveNaive(const Node& one, const Node& two, Context& ctx) { - Node out = Node::createCollection(); - - // Check for the simple cases - if (one.isNil()) { - out.collection()->push_back(two.clone(ctx)); - } else if (two.isNil()) { - out.collection()->push_back(one.clone(ctx)); - } else { - // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B - // See https://gist.github.com/nex3/7609394 for details. - - Node firstPerm = one.clone(ctx); - Node twoCloned = two.clone(ctx); - firstPerm.plus(twoCloned); - out.collection()->push_back(firstPerm); - - Node secondPerm = two.clone(ctx); - Node oneCloned = one.clone(ctx); - secondPerm.plus(oneCloned ); - out.collection()->push_back(secondPerm); - } - - return out; - } - */ - - - /* - This is the equivalent of ruby's Sequence.weave. - - The following is the modified version of the ruby code that was more portable to C++. You - should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. - - def weave(path) - # This function works by moving through the selector path left-to-right, - # building all possible prefixes simultaneously. These prefixes are - # `befores`, while the remaining parenthesized suffixes is `afters`. - befores = [[]] - afters = path.dup - - until afters.empty? - current = afters.shift.dup - last_current = [current.pop] - - tempResult = [] - - for before in befores do - sub = subweave(before, current) - if sub.nil? - next - end - - for seqs in sub do - tempResult.push(seqs + last_current) - end - end - - befores = tempResult - - end - - return befores - end - */ - /* - def weave(path) - befores = [[]] - afters = path.dup - - until afters.empty? - current = afters.shift.dup - - last_current = [current.pop] - - - tempResult = [] - - for before in befores do - sub = subweave(before, current) - - if sub.nil? - next [] - end - - - for seqs in sub do - toPush = seqs + last_current - - tempResult.push(seqs + last_current) - end - - end - - befores = tempResult - - end - - return befores - end - */ - static Node weave(Node& path, Context& ctx) { - - DEBUG_PRINTLN(WEAVE, "WEAVE: " << path) - - Node befores = Node::createCollection(); - befores.collection()->push_back(Node::createCollection()); - - Node afters = Node::createCollection(); - afters.plus(path); - - while (!afters.collection()->empty()) { - Node current = afters.collection()->front().clone(ctx); - afters.collection()->pop_front(); - DEBUG_PRINTLN(WEAVE, "CURRENT: " << current) - - Node last_current = Node::createCollection(); - last_current.collection()->push_back(current.collection()->back()); - current.collection()->pop_back(); - DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current) - DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current) - - Node tempResult = Node::createCollection(); - - for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) { - Node& before = *beforesIter; - - Node sub = subweave(before, current, ctx); - - DEBUG_PRINTLN(WEAVE, "SUB: " << sub) - - if (sub.isNil()) { - return Node::createCollection(); - } - - for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) { - Node& seqs = *subIter; - - Node toPush = Node::createCollection(); - toPush.plus(seqs); - toPush.plus(last_current); - - tempResult.collection()->push_back(toPush); - - } - } - - befores = tempResult; - - } - - return befores; - } - - - - // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively - // call extendComplexSelector again. - static Node extendComplexSelector( - Complex_Selector* pComplexSelector, - Context& ctx, - ExtensionSubsetMap& subsetMap, - set seen); - - - - /* - This is the equivalent of ruby's SimpleSequence.do_extend. - - // TODO: I think I have some modified ruby code to put here. Check. - */ - /* - ISSUES: - - Previous TODO: Do we need to group the results by extender? - - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?) - - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything... - - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing... - */ - template - class GroupByToAFunctor { - public: - KeyType operator()(ExtensionPair& extPair) const { - Complex_Selector* pSelector = extPair.first; - return *pSelector; - } - }; - static Node extendCompoundSelector( - Compound_Selector* pSelector, - Context& ctx, - ExtensionSubsetMap& subsetMap, - set seen) { - - DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: ")) - - Node extendedSelectors = Node::createCollection(); - - To_String to_string; - - SubsetMapEntries entries = subsetMap.get_v(pSelector->to_str_vec()); - - - typedef vector > > GroupedByToAResult; - - GroupByToAFunctor extPairKeyFunctor; - GroupedByToAResult arr; - group_by_to_a(entries, extPairKeyFunctor, arr); - - - typedef pair SelsNewSeqPair; - typedef vector SelsNewSeqPairCollection; - - - SelsNewSeqPairCollection holder; - - - for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) { - pair >& groupedPair = *groupedIter; - - Complex_Selector& seq = groupedPair.first; - vector& group = groupedPair.second; - -// DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: ")) - - - Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->path(), pSelector->position()); - for (vector::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) { - ExtensionPair& pair = *groupIter; - Compound_Selector* pCompound = pair.second; - for (size_t index = 0; index < pCompound->length(); index++) { - Simple_Selector* pSimpleSelector = (*pCompound)[index]; - (*pSels) << pSimpleSelector; - } - } - - - -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: ")) - - - Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge) - Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions - - - - // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL? - Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx); - - -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: ")) - - - Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->base(); - Compound_Selector* pUnifiedSelector = NULL; - - if (!pInnermostCompoundSelector) { - pInnermostCompoundSelector = new (ctx.mem) Compound_Selector(pSelector->path(), pSelector->position()); - } - - pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx); - -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: ")) -// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: ")) - - if (!pUnifiedSelector || pUnifiedSelector->length() == 0) { - continue; - } - - - - // TODO: implement the parent directive match (if necessary based on test failures) - // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none? - - - - - // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just - // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more - // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered - // out and aren't operated on. - Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx); - Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->path(), pSelector->position(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL); - Complex_Selector::Combinator combinator = pNewSelector->clear_innermost(); - pNewSelector->set_innermost(pNewInnerMost, combinator); - -#ifdef DEBUG - SourcesSet debugSet; - debugSet = pNewSelector->sources(); - if (debugSet.size() > 0) { - throw "The new selector should start with no sources. Something needs to be cloned to fix this."; - } - debugSet = pExtComplexSelector->sources(); - if (debugSet.size() > 0) { - throw "The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this."; - } -#endif - - - - // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending. - DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx)) - - DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, ctx, "SOURCES NEW SEQ BEGIN: ")) - - SourcesSet newSourcesSet = pSelector->sources(); - DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES THIS EXTEND: ")) - - newSourcesSet.insert(pExtComplexSelector); - DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES WITH NEW SOURCE: ")) - - pNewSelector->addSources(newSourcesSet, ctx); - - DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet newSet = pNewSelector->sources(); printSourcesSet(newSet, ctx, "SOURCES ON NEW SELECTOR AFTER ADD: ")) - DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: ")) - - - - holder.push_back(make_pair(pSels, pNewSelector)); - } - - - for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) { - SelsNewSeqPair& pair = *holderIter; - - Compound_Selector* pSels = pair.first; - Complex_Selector* pNewSelector = pair.second; - - - if (seen.find(*pSels) != seen.end()) { - continue; - } - - - set recurseSeen(seen); - recurseSeen.insert(*pSels); - - - DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx)) - - Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subsetMap, recurseSeen); - - DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors) - - for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end(); - iterator != endIterator; ++iterator) { - Node& newSelector = *iterator; - -// DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << extendedSelectors) -// DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)); - - if (!extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)) { -// DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR") - extendedSelectors.collection()->push_back(newSelector); - } - } - } - - DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: ")) - - return extendedSelectors; - } - - - static bool complexSelectorHasExtension( - Complex_Selector* pComplexSelector, - Context& ctx, - ExtensionSubsetMap& subsetMap) { - - bool hasExtension = false; - - Complex_Selector* pIter = pComplexSelector; - - while (!hasExtension && pIter) { - Compound_Selector* pHead = pIter->head(); - - if (pHead) { - SubsetMapEntries entries = subsetMap.get_v(pHead->to_str_vec()); - - hasExtension = entries.size() > 0; - } - - pIter = pIter->tail(); - } - - return hasExtension; - } - - - /* - This is the equivalent of ruby's Sequence.do_extend. - - // TODO: I think I have some modified ruby code to put here. Check. - */ - /* - ISSUES: - - check to automatically include combinators doesn't transfer over to libsass' data model where - the combinator and compound selector are one unit - next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence) - */ - static Node extendComplexSelector( - Complex_Selector* pComplexSelector, - Context& ctx, - ExtensionSubsetMap& subsetMap, - set seen) { - - Node complexSelector = complexSelectorToNode(pComplexSelector, ctx); - - DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector) - - Node extendedNotExpanded = Node::createCollection(); - - for (NodeDeque::iterator complexSelIter = complexSelector.collection()->begin(), complexSelIterEnd = complexSelector.collection()->end(); complexSelIter != complexSelIterEnd; ++complexSelIter) { - Node& sseqOrOp = *complexSelIter; - - DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp) - - // If it's not a selector (meaning it's a combinator), just include it automatically - if (!sseqOrOp.isSelector()) { - // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node - // with one collection child. The collection child represents a Complex_Selector that is only a combinator. - Node outer = Node::createCollection(); - Node inner = Node::createCollection(); - outer.collection()->push_back(inner); - inner.collection()->push_back(sseqOrOp); - extendedNotExpanded.collection()->push_back(outer); - continue; - } - - Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head(); - - Node extended = extendCompoundSelector(pCompoundSelector, ctx, subsetMap, seen); - - DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended) - - - // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence - // due to the member mapping: choices = extended.map {|seq| seq.members} - Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector(); - - bool isSuperselector = false; - for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end(); - iterator != endIterator; ++iterator) { - Node& childNode = *iterator; - Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx); - if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) { - isSuperselector = true; - break; - } - } - - if (!isSuperselector) { - extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx)); - } - - DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended) - - // Aggregate our current extensions - extendedNotExpanded.collection()->push_back(extended); - } - - - DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << extendedNotExpanded) - - - - // Ruby Equivalent: paths - Node paths = Sass::paths(extendedNotExpanded, ctx); - - DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths) - - - - // Ruby Equivalent: weave - Node weaves = Node::createCollection(); - - for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) { - Node& path = *pathsIter; - Node weaved = weave(path, ctx); - weaves.collection()->push_back(weaved); - } - - DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves) - - - - // Ruby Equivalent: trim - Node trimmed = trim(weaves, ctx); - - DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed) - - - // Ruby Equivalent: flatten - Node extendedSelectors = flatten(trimmed, ctx, 1); - - DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors) - - - DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector) - - - return extendedSelectors; - } - - - - /* - This is the equivalent of ruby's CommaSequence.do_extend. - */ - static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) { - - To_String to_string; - - Selector_List* pNewSelectors = new (ctx.mem) Selector_List(pSelectorList->path(), pSelectorList->position(), pSelectorList->length()); - - extendedSomething = false; - - for (size_t index = 0, length = pSelectorList->length(); index < length; index++) { - Complex_Selector* pSelector = (*pSelectorList)[index]; - - // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that. - // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to - // run through the extend code (which does a data model transformation), check if there is anything to extend before doing - // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps - // when debugging). - if (!complexSelectorHasExtension(pSelector, ctx, subsetMap)) { - *pNewSelectors << pSelector; - continue; - } - - extendedSomething = true; - - set seen; - Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen); - - if (!pSelector->has_placeholder()) { - if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) { - *pNewSelectors << pSelector; - } - } - - for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) { - Node& childNode = *iterator; - *pNewSelectors << nodeToComplexSelector(childNode, ctx); - } - } - - return pNewSelectors; - - } - - - bool shouldExtendBlock(Block* b) { - - // If a block is empty, there's no reason to extend it since any rules placed on this block - // won't have any output. The main benefit of this is for structures like: - // - // .a { - // .b { - // x: y; - // } - // } - // - // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b). - // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since - // there are no child statements. However .a .b should have extensions applied. - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - - if (typeid(*stm) == typeid(Ruleset)) { - // Do nothing. This doesn't count as a statement that causes extension since we'll iterate over this rule set in a future visit and try to extend it. - } - else { - return true; - } - } - - return false; - - } - - - // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change. - template - static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subsetMap) { - To_String to_string; - - DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast(pObject->selector())->perform(&to_string)) - - // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions - // of doing so, so for now, let's just not extend things that won't be output later. - if (!shouldExtendBlock(pObject->block())) { - DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT") - return; - } - - bool extendedSomething = false; - Selector_List* pNewSelectorList = extendSelectorList(static_cast(pObject->selector()), ctx, subsetMap, extendedSomething); - - if (extendedSomething && pNewSelectorList) { - DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) - DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string)) - - // re-parse in order to restructure expanded placeholder nodes correctly. - // - // TODO: I don't know if this is needed, but it was in the original C++ implementation, so I kept it. Try running the tests without re-parsing. - pObject->selector( - Parser::from_c_str( - (pNewSelectorList->perform(&to_string) + ";").c_str(), - ctx, - pNewSelectorList->path(), - pNewSelectorList->position() - ).parse_selector_group() - ); - } else { - DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING") - } - } - - - - Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm) - : ctx(ctx), subset_map(ssm) - { } - - void Extend::operator()(Block* b) - { - for (size_t i = 0, L = b->length(); i < L; ++i) { - (*b)[i]->perform(this); - } - } - - void Extend::operator()(Ruleset* pRuleset) - { - extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map); - - pRuleset->block()->perform(this); - } - - void Extend::operator()(Media_Block* pMediaBlock) - { - if (pMediaBlock->selector()) { - extendObjectWithSelectorAndBlock(pMediaBlock, ctx, subset_map); - } - - pMediaBlock->block()->perform(this); - } - - void Extend::operator()(At_Rule* a) - { - if (a->block()) a->block()->perform(this); - } -} diff --git a/extend.hpp b/extend.hpp deleted file mode 100644 index 374bc539..00000000 --- a/extend.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#define SASS_EXTEND - -#include -#include -#include -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -#ifndef SASS_SUBSET_MAP -#include "subset_map.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Context; - - typedef Subset_Map > ExtensionSubsetMap; - - class Extend : public Operation_CRTP { - - Context& ctx; - ExtensionSubsetMap& subset_map; - - void fallback_impl(AST_Node* n) { }; - - public: - Extend(Context&, ExtensionSubsetMap&); - virtual ~Extend() { } - - using Operation::operator(); - - void operator()(Block*); - void operator()(Ruleset*); - void operator()(Media_Block*); - void operator()(At_Rule*); - - template - void fallback(U x) { return fallback_impl(x); } - }; - -} diff --git a/file.cpp b/file.cpp deleted file mode 100644 index 6cffef3e..00000000 --- a/file.cpp +++ /dev/null @@ -1,275 +0,0 @@ -#ifdef _WIN32 -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#endif - -#ifndef FS_CASE_SENSITIVE -#ifdef _WIN32 -#define FS_CASE_SENSITIVE 0 -#else -#define FS_CASE_SENSITIVE 1 -#endif -#endif - -#include -#include -#include -#include -#include -#include "file.hpp" -#include "context.hpp" -#include "utf8_string.hpp" -#include "sass2scss.h" - -#ifdef _WIN32 -#include -#endif - -namespace Sass { - namespace File { - using namespace std; - - // no physical check on filesystem - // only a logical cleanup of a path - string make_canonical_path (string path) - { - - // declarations - size_t pos; - - #ifdef _WIN32 - //convert backslashes to forward slashes - replace(path.begin(), path.end(), '\\', '/'); - #endif - - pos = 0; // remove all self references inside the path string - while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2); - - pos = 0; // remove all leading and trailing self references - while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2); - while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2); - - pos = 0; // collapse multiple delimiters into a single one - while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1); - - return path; - - } - - size_t find_last_folder_separator(const string& path, size_t limit = string::npos) - { - size_t pos = string::npos; - size_t pos_p = path.find_last_of('/', limit); - #ifdef _WIN32 - size_t pos_w = path.find_last_of('\\', limit); - #else - size_t pos_w = string::npos; - #endif - if (pos_p != string::npos && pos_w != string::npos) { - pos = max(pos_p, pos_w); - } - else if (pos_p != string::npos) { - pos = pos_p; - } - else { - pos = pos_w; - } - return pos; - } - - string base_name(string path) - { - size_t pos = find_last_folder_separator(path); - if (pos == string::npos) return path; - else return path.substr(pos+1); - } - - string dir_name(string path) - { - size_t pos = find_last_folder_separator(path); - if (pos == string::npos) return ""; - else return path.substr(0, pos+1); - } - - string join_paths(string l, string r) - { - if (l.empty()) return r; - if (r.empty()) return l; - if (is_absolute_path(r)) return r; - - if (l[l.length()-1] != '/') l += '/'; - - while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) { - r = r.substr(3); - size_t pos = find_last_folder_separator(l, l.length() - 2); - l = l.substr(0, pos == string::npos ? pos : pos + 1); - } - - return l + r; - } - - bool is_absolute_path(const string& path) - { - if (path[0] == '/') return true; - // TODO: UN-HACKIFY THIS - #ifdef _WIN32 - if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true; - #endif - return false; - } - - string make_absolute_path(const string& path, const string& cwd) - { - return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path))); - } - - string resolve_relative_path(const string& uri, const string& base, const string& cwd) - { - - string absolute_uri = make_absolute_path(uri, cwd); - string absolute_base = make_absolute_path(base, cwd); - - string stripped_uri = ""; - string stripped_base = ""; - - size_t index = 0; - size_t minSize = min(absolute_uri.size(), absolute_base.size()); - for (size_t i = 0; i < minSize; ++i) { - #ifdef FS_CASE_SENSITIVE - if (absolute_uri[i] != absolute_base[i]) break; - #else - // compare the charactes in a case insensitive manner - // windows fs is only case insensitive in ascii ranges - if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break; - #endif - if (absolute_uri[i] == '/') index = i + 1; - } - for (size_t i = index; i < absolute_uri.size(); ++i) { - stripped_uri += absolute_uri[i]; - } - for (size_t i = index; i < absolute_base.size(); ++i) { - stripped_base += absolute_base[i]; - } - - size_t left = 0; - size_t directories = 0; - for (size_t right = 0; right < stripped_base.size(); ++right) { - if (stripped_base[right] == '/') { - if (stripped_base.substr(left, 2) != "..") { - ++directories; - } - else if (directories > 1) { - --directories; - } - else { - directories = 0; - } - left = right + 1; - } - } - - string result = ""; - for (size_t i = 0; i < directories; ++i) { - result += "../"; - } - result += stripped_uri; - - return result; - } - - char* resolve_and_load(string path, string& real_path) - { - // Resolution order for ambiguous imports: - // (1) filename as given - // (2) underscore + given - // (3) underscore + given + extension - // (4) given + extension - char* contents = 0; - real_path = path; - // if the file isn't found with the given filename ... - if (!(contents = read_file(real_path))) { - string dir(dir_name(path)); - string base(base_name(path)); - string _base("_" + base); - real_path = dir + _base; - // if the file isn't found with '_' + filename ... - if (!(contents = read_file(real_path))) { - string _base_scss(_base + ".scss"); - real_path = dir + _base_scss; - // if the file isn't found with '_' + filename + ".scss" ... - if (!(contents = read_file(real_path))) { - string _base_sass(_base + ".sass"); - real_path = dir + _base_sass; - // if the file isn't found with '_' + filename + ".sass" ... - if (!(contents = read_file(real_path))) { - string base_scss(base + ".scss"); - real_path = dir + base_scss; - // if the file isn't found with filename + ".scss" ... - if (!(contents = read_file(real_path))) { - string base_sass(base + ".sass"); - real_path = dir + base_sass; - // if the file isn't found with filename + ".sass" ... - if (!(contents = read_file(real_path))) { - // default back to scss version - real_path = dir + base_scss; - } - } - } - } - } - } -#ifdef _WIN32 - // convert Windows backslashes to URL forward slashes - replace(real_path.begin(), real_path.end(), '\\', '/'); -#endif - return contents; - } - - char* read_file(string path) - { - struct stat st; - -#ifdef _WIN32 - BYTE* pBuffer; - DWORD dwBytes; - // windows unicode filepaths are encoded in utf16 - wstring wpath = UTF_8::convert_to_utf16(path); - HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (hFile == INVALID_HANDLE_VALUE) return 0; - DWORD dwFileLength = GetFileSize(hFile, NULL); - if (dwFileLength == INVALID_FILE_SIZE) return 0; - pBuffer = new BYTE[dwFileLength + 1]; - ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL); - pBuffer[dwFileLength] = '\0'; - CloseHandle(hFile); - // just convert from unsigned char* - char* contents = (char*) pBuffer; -#else - if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0; - ifstream file(path.c_str(), ios::in | ios::binary | ios::ate); - char* contents = 0; - if (file.is_open()) { - size_t size = file.tellg(); - contents = new char[size + 1]; // extra byte for the null char - file.seekg(0, ios::beg); - file.read(contents, size); - contents[size] = '\0'; - file.close(); - } -#endif - string extension; - if (path.length() > 5) { - extension = path.substr(path.length() - 5, 5); - } - for(size_t i=0; i - -namespace Sass { - using namespace std; - struct Context; - namespace File { - string base_name(string); - string dir_name(string); - string join_paths(string, string); - bool is_absolute_path(const string& path); - string make_canonical_path (string path); - string make_absolute_path(const string& path, const string& cwd); - string resolve_relative_path(const string& uri, const string& base, const string& cwd); - char* resolve_and_load(string path, string& real_path); - char* read_file(string path); - } -} diff --git a/functions.cpp b/functions.cpp deleted file mode 100644 index 6f619be9..00000000 --- a/functions.cpp +++ /dev/null @@ -1,1428 +0,0 @@ -#include "functions.hpp" -#include "ast.hpp" -#include "context.hpp" -#include "backtrace.hpp" -#include "parser.hpp" -#include "constants.hpp" -#include "to_string.hpp" -#include "inspect.hpp" -#include "eval.hpp" -#include "util.hpp" -#include "utf8_string.hpp" -#include "utf8.h" - -#include -#include -#include -#include -#include -#include -#include - -#define ARG(argname, argtype) get_arg(argname, env, sig, path, position, backtrace) -#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, path, position, lo, hi, backtrace) -#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, path, position, backtrace, ctx) - -namespace Sass { - using std::stringstream; - using std::endl; - - Definition* make_native_function(Signature sig, Native_Function f, Context& ctx) - { - Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]"); - sig_parser.lex(); - string name(Util::normalize_underscores(sig_parser.lexed)); - Parameters* params = sig_parser.parse_parameters(); - return new (ctx.mem) Definition("[built-in function]", - Position(), - sig, - name, - params, - f, - false); - } - - Definition* make_c_function(Signature sig, Sass_C_Function f, void* cookie, Context& ctx) - { - Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]"); - sig_parser.lex(); - string name(Util::normalize_underscores(sig_parser.lexed)); - Parameters* params = sig_parser.parse_parameters(); - return new (ctx.mem) Definition("[c function]", - Position(), - sig, - name, - params, - f, - cookie, - false, true); - } - - namespace Functions { - - template - T* get_arg(const string& argname, Env& env, Signature sig, const string& path, Position position, Backtrace* backtrace) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - T* val = dynamic_cast(env[argname]); - if (!val) { - string msg("argument `"); - msg += argname; - msg += "` of `"; - msg += sig; - msg += "` must be a "; - msg += T::type_name(); - error(msg, path, position, backtrace); - } - return val; - } - - Map* get_arg_m(const string& argname, Env& env, Signature sig, const string& path, Position position, Backtrace* backtrace, Context& ctx) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Map* val = dynamic_cast(env[argname]); - if (val) return val; - - List* lval = dynamic_cast(env[argname]); - if (lval && lval->length() == 0) return new (ctx.mem) Map(path, position, 1); - - // fallback on get_arg for error handling - val = get_arg(argname, env, sig, path, position, backtrace); - return val; - } - - Number* get_arg_r(const string& argname, Env& env, Signature sig, const string& path, Position position, double lo, double hi, Backtrace* backtrace) - { - // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number* val = get_arg(argname, env, sig, path, position, backtrace); - double v = val->value(); - if (!(lo <= v && v <= hi)) { - stringstream msg; - msg << "argument `" << argname << "` of `" << sig << "` must be between "; - msg << lo << " and " << hi; - error(msg.str(), path, position, backtrace); - } - return val; - } - - //////////////// - // RGB FUNCTIONS - //////////////// - - Signature rgb_sig = "rgb($red, $green, $blue)"; - BUILT_IN(rgb) - { - return new (ctx.mem) Color(path, - position, - ARGR("$red", Number, 0, 255)->value(), - ARGR("$green", Number, 0, 255)->value(), - ARGR("$blue", Number, 0, 255)->value()); - } - - Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)"; - BUILT_IN(rgba_4) - { - return new (ctx.mem) Color(path, - position, - ARGR("$red", Number, 0, 255)->value(), - ARGR("$green", Number, 0, 255)->value(), - ARGR("$blue", Number, 0, 255)->value(), - ARGR("$alpha", Number, 0, 1)->value()); - } - - Signature rgba_2_sig = "rgba($color, $alpha)"; - BUILT_IN(rgba_2) - { - Color* c_arg = ARG("$color", Color); - Color* new_c = new (ctx.mem) Color(*c_arg); - new_c->a(ARGR("$alpha", Number, 0, 1)->value()); - new_c->disp(""); - return new_c; - } - - Signature red_sig = "red($color)"; - BUILT_IN(red) - { return new (ctx.mem) Number(path, position, ARG("$color", Color)->r()); } - - Signature green_sig = "green($color)"; - BUILT_IN(green) - { return new (ctx.mem) Number(path, position, ARG("$color", Color)->g()); } - - Signature blue_sig = "blue($color)"; - BUILT_IN(blue) - { return new (ctx.mem) Number(path, position, ARG("$color", Color)->b()); } - - Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; - BUILT_IN(mix) - { - Color* color1 = ARG("$color-1", Color); - Color* color2 = ARG("$color-2", Color); - Number* weight = ARGR("$weight", Number, 0, 100); - - double p = weight->value()/100; - double w = 2*p - 1; - double a = color1->a() - color2->a(); - - double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0; - double w2 = 1 - w1; - - return new (ctx.mem) Color(path, - position, - std::floor(w1*color1->r() + w2*color2->r()), - std::floor(w1*color1->g() + w2*color2->g()), - std::floor(w1*color1->b() + w2*color2->b()), - color1->a()*p + color2->a()*(1-p)); - } - - //////////////// - // HSL FUNCTIONS - //////////////// - - // RGB to HSL helper function - struct HSL { double h; double s; double l; }; - HSL rgb_to_hsl(double r, double g, double b) - { - - // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV - r /= 255.0; g /= 255.0; b /= 255.0; - - double max = std::max(r, std::max(g, b)); - double min = std::min(r, std::min(g, b)); - double del = max - min; - - double h = 0, s = 0, l = (max + min) / 2.0; - - if (max == min) { - h = s = 0; // achromatic - } - else { - if (l < 0.5) s = del / (2.0 * l); - else s = del / (2.0 - 2.0 * l); - - if (r == max) h = 60 * (g - b) / del; - else if (g == max) h = 60 * (b - r) / del + 120; - else if (b == max) h = 60 * (r - g) / del + 240; - } - - HSL hsl_struct; - hsl_struct.h = h; - hsl_struct.s = s * 100; - hsl_struct.l = l * 100; - - return hsl_struct; - } - - // hue to RGB helper function - double h_to_rgb(double m1, double m2, double h) { - if (h < 0) h += 1; - if (h > 1) h -= 1; - if (h*6.0 < 1) return m1 + (m2 - m1)*h*6; - if (h*2.0 < 1) return m2; - if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6; - return m1; - } - - Color* hsla_impl(double h, double s, double l, double a, Context& ctx, const string& path, Position position) - { - h /= 360.0; - s /= 100.0; - l /= 100.0; - - // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color. - double m2; - if (l <= 0.5) m2 = l*(s+1.0); - else m2 = (l+s)-(l*s); - double m1 = (l*2)-m2; - // round the results -- consider moving this into the Color constructor - double r = (h_to_rgb(m1, m2, h+1.0/3.0) * 255.0); - double g = (h_to_rgb(m1, m2, h) * 255.0); - double b = (h_to_rgb(m1, m2, h-1.0/3.0) * 255.0); - - return new (ctx.mem) Color(path, position, r, g, b, a); - } - - Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; - BUILT_IN(hsl) - { - return hsla_impl(ARG("$hue", Number)->value(), - ARGR("$saturation", Number, 0, 100)->value(), - ARGR("$lightness", Number, 0, 100)->value(), - 1.0, - ctx, - path, - position); - } - - Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; - BUILT_IN(hsla) - { - return hsla_impl(ARG("$hue", Number)->value(), - ARGR("$saturation", Number, 0, 100)->value(), - ARGR("$lightness", Number, 0, 100)->value(), - ARGR("$alpha", Number, 0, 1)->value(), - ctx, - path, - position); - } - - Signature hue_sig = "hue($color)"; - BUILT_IN(hue) - { - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return new (ctx.mem) Number(path, position, hsl_color.h, "deg"); - } - - Signature saturation_sig = "saturation($color)"; - BUILT_IN(saturation) - { - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return new (ctx.mem) Number(path, position, hsl_color.s, "%"); - } - - Signature lightness_sig = "lightness($color)"; - BUILT_IN(lightness) - { - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return new (ctx.mem) Number(path, position, hsl_color.l, "%"); - } - - Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; - BUILT_IN(adjust_hue) - { - Color* rgb_color = ARG("$color", Color); - Number* degrees = ARG("$degrees", Number); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h + degrees->value(), - hsl_color.s, - hsl_color.l, - rgb_color->a(), - ctx, - path, - position); - } - - Signature lighten_sig = "lighten($color, $amount)"; - BUILT_IN(lighten) - { - Color* rgb_color = ARG("$color", Color); - Number* amount = ARGR("$amount", Number, 0, 100); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - //Check lightness is not negative before lighten it - double hslcolorL = hsl_color.l; - if (hslcolorL < 0) { - hslcolorL = 0; - } - - return hsla_impl(hsl_color.h, - hsl_color.s, - hslcolorL + amount->value(), - rgb_color->a(), - ctx, - path, - position); - } - - Signature darken_sig = "darken($color, $amount)"; - BUILT_IN(darken) - { - Color* rgb_color = ARG("$color", Color); - Number* amount = ARGR("$amount", Number, 0, 100); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - - //Check lightness if not over 100, before darken it - double hslcolorL = hsl_color.l; - if (hslcolorL > 100) { - hslcolorL = 100; - } - - return hsla_impl(hsl_color.h, - hsl_color.s, - hslcolorL - amount->value(), - rgb_color->a(), - ctx, - path, - position); - } - - Signature saturate_sig = "saturate($color, $amount: false)"; - BUILT_IN(saturate) - { - // CSS3 filter function overload: pass literal through directly - Number* amount = dynamic_cast(env["$amount"]); - if (!amount) { - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(path, position, "saturate(" + env["$color"]->perform(&to_string) + ")"); - } - - ARGR("$amount", Number, 0, 100); - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - //Check saturation is not negative before saturate it - double hslcolorS = hsl_color.s; - if (hslcolorS < 0) { - hslcolorS = 0; - } - - - return hsla_impl(hsl_color.h, - hslcolorS + amount->value(), - hsl_color.l, - rgb_color->a(), - ctx, - path, - position); - } - - Signature desaturate_sig = "desaturate($color, $amount)"; - BUILT_IN(desaturate) - { - Color* rgb_color = ARG("$color", Color); - Number* amount = ARGR("$amount", Number, 0, 100); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - //Check saturation is not over 100 before desaturate it - double hslcolorS = hsl_color.s; - if (hslcolorS > 100) { - hslcolorS = 100; - } - - return hsla_impl(hsl_color.h, - hslcolorS - amount->value(), - hsl_color.l, - rgb_color->a(), - ctx, - path, - position); - } - - Signature grayscale_sig = "grayscale($color)"; - BUILT_IN(grayscale) - { - // CSS3 filter function overload: pass literal through directly - Number* amount = dynamic_cast(env["$color"]); - if (amount) { - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(path, position, "grayscale(" + amount->perform(&to_string) + ")"); - } - - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h, - 0.0, - hsl_color.l, - rgb_color->a(), - ctx, - path, - position); - } - - Signature complement_sig = "complement($color)"; - BUILT_IN(complement) - { - Color* rgb_color = ARG("$color", Color); - HSL hsl_color = rgb_to_hsl(rgb_color->r(), - rgb_color->g(), - rgb_color->b()); - return hsla_impl(hsl_color.h - 180.0, - hsl_color.s, - hsl_color.l, - rgb_color->a(), - ctx, - path, - position); - } - - Signature invert_sig = "invert($color)"; - BUILT_IN(invert) - { - // CSS3 filter function overload: pass literal through directly - Number* amount = dynamic_cast(env["$color"]); - if (amount) { - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(path, position, "invert(" + amount->perform(&to_string) + ")"); - } - - Color* rgb_color = ARG("$color", Color); - return new (ctx.mem) Color(path, - position, - 255 - rgb_color->r(), - 255 - rgb_color->g(), - 255 - rgb_color->b(), - rgb_color->a()); - } - - //////////////////// - // OPACITY FUNCTIONS - //////////////////// - Signature alpha_sig = "alpha($color)"; - Signature opacity_sig = "opacity($color)"; - BUILT_IN(alpha) - { - String_Constant* ie_kwd = dynamic_cast(env["$color"]); - if (ie_kwd) { - return new (ctx.mem) String_Constant(path, position, "alpha(" + ie_kwd->value() + ")"); - } - - // CSS3 filter function overload: pass literal through directly - Number* amount = dynamic_cast(env["$color"]); - if (amount) { - To_String to_string(&ctx); - return new (ctx.mem) String_Constant(path, position, "opacity(" + amount->perform(&to_string) + ")"); - } - - return new (ctx.mem) Number(path, position, ARG("$color", Color)->a()); - } - - Signature opacify_sig = "opacify($color, $amount)"; - Signature fade_in_sig = "fade-in($color, $amount)"; - BUILT_IN(opacify) - { - Color* color = ARG("$color", Color); - double alpha = color->a() + ARGR("$amount", Number, 0, 1)->value(); - return new (ctx.mem) Color(path, - position, - color->r(), - color->g(), - color->b(), - alpha > 1.0 ? 1.0 : alpha); - } - - Signature transparentize_sig = "transparentize($color, $amount)"; - Signature fade_out_sig = "fade-out($color, $amount)"; - BUILT_IN(transparentize) - { - Color* color = ARG("$color", Color); - double alpha = color->a() - ARGR("$amount", Number, 0, 1)->value(); - return new (ctx.mem) Color(path, - position, - color->r(), - color->g(), - color->b(), - alpha < 0.0 ? 0.0 : alpha); - } - - //////////////////////// - // OTHER COLOR FUNCTIONS - //////////////////////// - - Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(adjust_color) - { - Color* color = ARG("$color", Color); - Number* r = dynamic_cast(env["$red"]); - Number* g = dynamic_cast(env["$green"]); - Number* b = dynamic_cast(env["$blue"]); - Number* h = dynamic_cast(env["$hue"]); - Number* s = dynamic_cast(env["$saturation"]); - Number* l = dynamic_cast(env["$lightness"]); - Number* a = dynamic_cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `adjust-color`", path, position); - } - if (rgb) { - return new (ctx.mem) Color(path, - position, - color->r() + (r ? r->value() : 0), - color->g() + (g ? g->value() : 0), - color->b() + (b ? b->value() : 0), - color->a() + (a ? a->value() : 0)); - } - if (hsl) { - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - return hsla_impl(hsl_struct.h + (h ? h->value() : 0), - hsl_struct.s + (s ? s->value() : 0), - hsl_struct.l + (l ? l->value() : 0), - color->a() + (a ? a->value() : 0), - ctx, - path, - position); - } - if (a) { - return new (ctx.mem) Color(path, - position, - color->r(), - color->g(), - color->b(), - color->a() + (a ? a->value() : 0)); - } - error("not enough arguments for `adjust-color`", path, position); - // unreachable - return color; - } - - Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(scale_color) - { - Color* color = ARG("$color", Color); - Number* r = dynamic_cast(env["$red"]); - Number* g = dynamic_cast(env["$green"]); - Number* b = dynamic_cast(env["$blue"]); - Number* h = dynamic_cast(env["$hue"]); - Number* s = dynamic_cast(env["$saturation"]); - Number* l = dynamic_cast(env["$lightness"]); - Number* a = dynamic_cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `scale-color`", path, position); - } - if (rgb) { - double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - return new (ctx.mem) Color(path, - position, - color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()), - color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()), - color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()), - color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); - } - if (hsl) { - double hscale = (h ? ARGR("$hue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double sscale = (s ? ARGR("$saturation", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double lscale = (l ? ARGR("$lightness", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h); - hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s); - hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l); - double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->r()); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position); - } - if (a) { - double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; - return new (ctx.mem) Color(path, - position, - color->r(), - color->g(), - color->b(), - color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); - } - error("not enough arguments for `scale-color`", path, position); - // unreachable - return color; - } - - Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)"; - BUILT_IN(change_color) - { - Color* color = ARG("$color", Color); - Number* r = dynamic_cast(env["$red"]); - Number* g = dynamic_cast(env["$green"]); - Number* b = dynamic_cast(env["$blue"]); - Number* h = dynamic_cast(env["$hue"]); - Number* s = dynamic_cast(env["$saturation"]); - Number* l = dynamic_cast(env["$lightness"]); - Number* a = dynamic_cast(env["$alpha"]); - - bool rgb = r || g || b; - bool hsl = h || s || l; - - if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `change-color`", path, position); - } - if (rgb) { - return new (ctx.mem) Color(path, - position, - r ? ARGR("$red", Number, 0, 255)->value() : color->r(), - g ? ARGR("$green", Number, 0, 255)->value() : color->g(), - b ? ARGR("$blue", Number, 0, 255)->value() : color->b(), - a ? ARGR("$alpha", Number, 0, 255)->value() : color->a()); - } - if (hsl) { - HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b()); - if (h) hsl_struct.h = static_cast(((static_cast(h->value()) % 360) + 360) % 360) / 360.0; - if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value(); - if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value(); - double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position); - } - if (a) { - double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); - return new (ctx.mem) Color(path, - position, - color->r(), - color->g(), - color->b(), - alpha); - } - error("not enough arguments for `change-color`", path, position); - // unreachable - return color; - } - - template - static double cap_channel(double c) { - if (c > range) return range; - else if (c < 0) return 0; - else return c; - } - - Signature ie_hex_str_sig = "ie-hex-str($color)"; - BUILT_IN(ie_hex_str) - { - Color* c = ARG("$color", Color); - double r = cap_channel<0xff>(c->r()); - double g = cap_channel<0xff>(c->g()); - double b = cap_channel<0xff>(c->b()); - double a = cap_channel<1> (c->a()) * 255; - - stringstream ss; - ss << '#' << std::setw(2) << std::setfill('0'); - ss << std::hex << std::setw(2) << static_cast(std::floor(a+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(r+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(g+0.5)); - ss << std::hex << std::setw(2) << static_cast(std::floor(b+0.5)); - - string result(ss.str()); - for (size_t i = 0, L = result.length(); i < L; ++i) { - result[i] = std::toupper(result[i]); - } - return new (ctx.mem) String_Constant(path, position, result); - } - - /////////////////// - // STRING FUNCTIONS - /////////////////// - - Signature unquote_sig = "unquote($string)"; - BUILT_IN(sass_unquote) - { - To_String to_string; - AST_Node* arg = env["$string"]; - string str(unquote(arg->perform(&to_string))); - String_Constant* result = new (ctx.mem) String_Constant(path, position, str); - result->is_delayed(true); - return result; - } - - Signature quote_sig = "quote($string)"; - BUILT_IN(sass_quote) - { - To_String to_string; - AST_Node* arg = env["$string"]; - string str(quote(arg->perform(&to_string), '"')); - String_Constant* result = new (ctx.mem) String_Constant(path, position, str); - result->is_delayed(true); - return result; - } - - - Signature str_length_sig = "str-length($string)"; - BUILT_IN(str_length) - { - size_t len = string::npos; - try { - String_Constant* s = ARG("$string", String_Constant); - string str = s->value(); - size_t length_of_s = str.size(); - size_t i = 0; - - if (s->is_quoted()) { - ++i; - --length_of_s; - } - - len = UTF_8::code_point_count(str, i, length_of_s); - - } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, path, position, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, path, position, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, path, position, backtrace); - } - // return something even if we had an error (-1) - return new (ctx.mem) Number(path, position, len); - } - - Signature str_insert_sig = "str-insert($string, $insert, $index)"; - BUILT_IN(str_insert) - { - string str; - try { - String_Constant* s = ARG("$string", String_Constant); - str = s->value(); - char quotemark = s->quote_mark(); - str = unquote(str); - String_Constant* i = ARG("$insert", String_Constant); - string ins = i->value(); - ins = unquote(ins); - Number* ind = ARG("$index", Number); - double index = ind->value(); - size_t len = UTF_8::code_point_count(str, 0, str.size()); - - if (index > 0 && index <= len) { - // positive and within string length - str.insert(UTF_8::offset_at_position(str, index - 1), ins); - } - else if (index > len) { - // positive and past string length - str += ins; - } - else if (index == 0) { - str = ins + str; - } - else if (std::abs(index) <= len) { - // negative and within string length - index += len + 1; - str.insert(UTF_8::offset_at_position(str, index), ins); - } - else { - // negative and past string length - str = ins + str; - } - - if (quotemark) { - str = quote(str, quotemark); - } - } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, path, position, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, path, position, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, path, position, backtrace); - } - return new (ctx.mem) String_Constant(path, position, str); - } - - Signature str_index_sig = "str-index($string, $substring)"; - BUILT_IN(str_index) - { - size_t index = string::npos; - try { - String_Constant* s = ARG("$string", String_Constant); - String_Constant* t = ARG("$substring", String_Constant); - string str = s->value(); - str = unquote(str); - string substr = t->value(); - substr = unquote(substr); - - size_t c_index = str.find(substr); - if(c_index == string::npos) { - return new (ctx.mem) Null(path, position); - } - index = UTF_8::code_point_count(str, 0, c_index) + 1; - } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, path, position, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, path, position, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, path, position, backtrace); - } - // return something even if we had an error (-1) - return new (ctx.mem) Number(path, position, index); - } - - Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)"; - BUILT_IN(str_slice) - { - string newstr; - try { - String_Constant* s = ARG("$string", String_Constant); - Number* n = ARG("$start-at", Number); - Number* m = ARG("$end-at", Number); - - string str = s->value(); - char quotemark = s->quote_mark(); - str = unquote(str); - - // normalize into 0-based indices - size_t start = UTF_8::offset_at_position(str, UTF_8::normalize_index(n->value(), UTF_8::code_point_count(str))); - size_t end = UTF_8::offset_at_position(str, UTF_8::normalize_index(m->value(), UTF_8::code_point_count(str))); - - if(start - end == 0) { - newstr = str.substr(start, end - start); - } else { - newstr = str.substr(start, end - start + UTF_8::code_point_size_at_offset(str, end)); - } - if(quotemark) { - newstr = quote(newstr, quotemark); - } - } - catch (utf8::invalid_code_point) { - string msg("utf8::invalid_code_point"); - error(msg, path, position, backtrace); - } - catch (utf8::not_enough_room) { - string msg("utf8::not_enough_room"); - error(msg, path, position, backtrace); - } - catch (utf8::invalid_utf8) { - string msg("utf8::invalid_utf8"); - error(msg, path, position, backtrace); - } - return new (ctx.mem) String_Constant(path, position, newstr); - } - - Signature to_upper_case_sig = "to-upper-case($string)"; - BUILT_IN(to_upper_case) - { - String_Constant* s = ARG("$string", String_Constant); - string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (isascii(str[i])) { - str[i] = std::toupper(str[i]); - } - } - - return new (ctx.mem) String_Constant(path, position, str); - } - - Signature to_lower_case_sig = "to-lower-case($string)"; - BUILT_IN(to_lower_case) - { - String_Constant* s = ARG("$string", String_Constant); - string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (isascii(str[i])) { - str[i] = std::tolower(str[i]); - } - } - - return new (ctx.mem) String_Constant(path, position, str); - } - - /////////////////// - // NUMBER FUNCTIONS - /////////////////// - - Signature percentage_sig = "percentage($value)"; - BUILT_IN(percentage) - { - Number* n = ARG("$value", Number); - if (!n->is_unitless()) error("argument $value of `" + string(sig) + "` must be unitless", path, position); - return new (ctx.mem) Number(path, position, n->value() * 100, "%"); - } - - Signature round_sig = "round($value)"; - BUILT_IN(round) - { - Number* n = ARG("$value", Number); - Number* r = new (ctx.mem) Number(*n); - r->path(path); - r->position(position); - r->value(std::floor(r->value() + 0.5)); - return r; - } - - Signature ceil_sig = "ceil($value)"; - BUILT_IN(ceil) - { - Number* n = ARG("$value", Number); - Number* r = new (ctx.mem) Number(*n); - r->path(path); - r->position(position); - r->value(std::ceil(r->value())); - return r; - } - - Signature floor_sig = "floor($value)"; - BUILT_IN(floor) - { - Number* n = ARG("$value", Number); - Number* r = new (ctx.mem) Number(*n); - r->path(path); - r->position(position); - r->value(std::floor(r->value())); - return r; - } - - Signature abs_sig = "abs($value)"; - BUILT_IN(abs) - { - Number* n = ARG("$value", Number); - Number* r = new (ctx.mem) Number(*n); - r->path(path); - r->position(position); - r->value(std::abs(r->value())); - return r; - } - - Signature min_sig = "min($x1, $x2...)"; - BUILT_IN(min) - { - Number* x1 = ARG("$x1", Number); - List* arglist = ARG("$x2", List); - Number* least = x1; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Number* xi = dynamic_cast(arglist->value_at_index(i)); - if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position); - if (lt(xi, least, ctx)) least = xi; - } - return least; - } - - Signature max_sig = "max($x1, $x2...)"; - BUILT_IN(max) - { - Number* x1 = ARG("$x1", Number); - List* arglist = ARG("$x2", List); - Number* greatest = x1; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Number* xi = dynamic_cast(arglist->value_at_index(i)); - if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position); - if (lt(greatest, xi, ctx)) greatest = xi; - } - return greatest; - } - - ///////////////// - // LIST FUNCTIONS - ///////////////// - - Signature length_sig = "length($list)"; - BUILT_IN(length) - { - Expression* v = ARG("$list", Expression); - if (v->concrete_type() == Expression::MAP) { - Map* map = dynamic_cast(env["$list"]); - return new (ctx.mem) Number(path, - position, - map ? map->length() : 1); - } - - List* list = dynamic_cast(env["$list"]); - return new (ctx.mem) Number(path, - position, - list ? list->length() : 1); - } - - Signature nth_sig = "nth($list, $n)"; - BUILT_IN(nth) - { - List* l = dynamic_cast(env["$list"]); - Number* n = ARG("$n", Number); - if (n->value() == 0) error("argument `$n` of `" + string(sig) + "` must be non-zero", path, position); - // if the argument isn't a list, then wrap it in a singleton list - if (!l) { - l = new (ctx.mem) List(path, position, 1); - *l << ARG("$list", Expression); - } - if (l->empty()) error("argument `$list` of `" + string(sig) + "` must not be empty", path, position); - double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); - if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + string(sig) + "`", path, position); - return l->value_at_index(index); - } - - Signature index_sig = "index($list, $value)"; - BUILT_IN(index) - { - List* l = dynamic_cast(env["$list"]); - Expression* v = ARG("$value", Expression); - if (!l) { - l = new (ctx.mem) List(path, position, 1); - *l << ARG("$list", Expression); - } - for (size_t i = 0, L = l->length(); i < L; ++i) { - if (eq(l->value_at_index(i), v, ctx)) return new (ctx.mem) Number(path, position, i+1); - } - return new (ctx.mem) Null(path, position); - } - - Signature join_sig = "join($list1, $list2, $separator: auto)"; - BUILT_IN(join) - { - List* l1 = dynamic_cast(env["$list1"]); - List* l2 = dynamic_cast(env["$list2"]); - String_Constant* sep = ARG("$separator", String_Constant); - List::Separator sep_val = (l1 ? l1->separator() : List::SPACE); - if (!l1) { - l1 = new (ctx.mem) List(path, position, 1); - *l1 << ARG("$list1", Expression); - sep_val = (l2 ? l2->separator() : List::SPACE); - } - if (!l2) { - l2 = new (ctx.mem) List(path, position, 1); - *l2 << ARG("$list2", Expression); - } - size_t len = l1->length() + l2->length(); - string sep_str = unquote(sep->value()); - if (sep_str == "space") sep_val = List::SPACE; - else if (sep_str == "comma") sep_val = List::COMMA; - else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position); - List* result = new (ctx.mem) List(path, position, len, sep_val); - *result += l1; - *result += l2; - return result; - } - - Signature append_sig = "append($list, $val, $separator: auto)"; - BUILT_IN(append) - { - List* l = dynamic_cast(env["$list"]); - Expression* v = ARG("$val", Expression); - String_Constant* sep = ARG("$separator", String_Constant); - if (!l) { - l = new (ctx.mem) List(path, position, 1); - *l << ARG("$list", Expression); - } - List* result = new (ctx.mem) List(path, position, l->length() + 1, l->separator()); - string sep_str(unquote(sep->value())); - if (sep_str == "space") result->separator(List::SPACE); - else if (sep_str == "comma") result->separator(List::COMMA); - else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position); - *result += l; - *result << v; - return result; - } - - Signature zip_sig = "zip($lists...)"; - BUILT_IN(zip) - { - List* arglist = new (ctx.mem) List(*ARG("$lists", List)); - size_t shortest = 0; - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - List* ith = dynamic_cast(arglist->value_at_index(i)); - if (!ith) { - ith = new (ctx.mem) List(path, position, 1); - *ith << arglist->value_at_index(i); - if (arglist->is_arglist()) { - ((Argument*)(*arglist)[i])->value(ith); - } else { - (*arglist)[i] = ith; - } - } - shortest = (i ? std::min(shortest, ith->length()) : ith->length()); - } - List* zippers = new (ctx.mem) List(path, position, shortest, List::COMMA); - size_t L = arglist->length(); - for (size_t i = 0; i < shortest; ++i) { - List* zipper = new (ctx.mem) List(path, position, L); - for (size_t j = 0; j < L; ++j) { - *zipper << (*static_cast(arglist->value_at_index(j)))[i]; - } - *zippers << zipper; - } - return zippers; - } - - Signature compact_sig = "compact($values...)"; - BUILT_IN(compact) - { - List* arglist = ARG("$values", List); - List::Separator sep = List::COMMA; - if (arglist->length() == 1) { - Expression* the_arg = arglist->value_at_index(0); - arglist = dynamic_cast(the_arg); - if (!arglist) { - List* result = new (ctx.mem) List(path, position, 1, List::COMMA); - *result << the_arg; - return result; - } - sep = arglist->separator(); - } - List* result = new (ctx.mem) List(path, position, 0, sep); - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Boolean* ith = dynamic_cast(arglist->value_at_index(i)); - if (ith && ith->value() == false) continue; - *result << arglist->value_at_index(i); - } - return result; - } - - Signature list_separator_sig = "list_separator($list)"; - BUILT_IN(list_separator) - { - List* l = dynamic_cast(env["$list"]); - if (!l) { - l = new (ctx.mem) List(path, position, 1); - *l << ARG("$list", Expression); - } - return new (ctx.mem) String_Constant(path, - position, - l->separator() == List::COMMA ? "comma" : "space"); - } - - ///////////////// - // MAP FUNCTIONS - ///////////////// - - Signature map_get_sig = "map-get($map, $key)"; - BUILT_IN(map_get) - { - Map* m = ARGM("$map", Map, ctx); - Expression* v = ARG("$key", Expression); - if (!m || m->empty()) return new (ctx.mem) Null(path, position); - for (size_t i = 0, L = m->length(); i < L; ++i) { - if (eq((*m)[i]->key(), v, ctx)) return m->value_at_index(i); - } - return new (ctx.mem) Null(path, position); - } - - Signature map_has_key_sig = "map-has-key($map, $key)"; - BUILT_IN(map_has_key) - { - Map* m = ARGM("$map", Map, ctx); - Expression* v = ARG("$key", Expression); - if (!m || m->empty()) return new (ctx.mem) Boolean(path, position, false); - for (size_t i = 0, L = m->length(); i < L; ++i) { - if (eq((*m)[i]->key(), v, ctx)) return new (ctx.mem) Boolean(path, position, true); - } - return new (ctx.mem) Boolean(path, position, false); - } - - Signature map_keys_sig = "map-keys($map)"; - BUILT_IN(map_keys) - { - Map* m = ARGM("$map", Map, ctx); - List* result = new (ctx.mem) List(path, position, 1, List::COMMA); - if (!m || m->empty()) return result; - for (size_t i = 0, L = m->length(); i < L; ++i) { - *result << (*m)[i]->key(); - } - return result; - } - - Signature map_values_sig = "map-values($map)"; - BUILT_IN(map_values) - { - Map* m = ARGM("$map", Map, ctx); - List* result = new (ctx.mem) List(path, position, 1, List::COMMA); - if (!m || m->empty()) return result; - for (size_t i = 0, L = m->length(); i < L; ++i) { - *result << (*m)[i]->value(); - } - return result; - } - - Signature map_merge_sig = "map-merge($map1, $map2)"; - BUILT_IN(map_merge) - { - Map* m1 = ARGM("$map1", Map, ctx); - Map* m2 = ARGM("$map2", Map, ctx); - - size_t len = m1->length() + m2->length(); - Map* result = new (ctx.mem) Map(path, position, len); - *result += m1; - *result += m2; - return result; - } - - Signature map_remove_sig = "map-remove($map, $keys...)"; - BUILT_IN(map_remove) - { - bool remove; - Map* m = ARGM("$map", Map, ctx); - List* arglist = ARG("$keys", List); - Map* result = new (ctx.mem) Map(path, position, 1); - for (size_t i = 0, L = m->length(); i < L; ++i) { - remove = false; - for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) { - remove = eq((*m)[i]->key(), arglist->value_at_index(j), ctx); - } - if (!remove) *result << (*m)[i]; - } - return result; - } - - Signature keywords_sig = "keywords($args)"; - BUILT_IN(keywords) - { - List* arglist = new (ctx.mem) List(*ARG("$args", List)); - Map* result = new (ctx.mem) Map(path, position, 1); - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - string name = string(((Argument*)(*arglist)[i])->name()); - string sanitized_name = string(name, 1); - *result << new (ctx.mem) KeyValuePair(path, - position, - new (ctx.mem) String_Constant(path, position, sanitized_name), - ((Argument*)(*arglist)[i])->value()); - } - return result; - } - - ////////////////////////// - // INTROSPECTION FUNCTIONS - ////////////////////////// - - Signature type_of_sig = "type-of($value)"; - BUILT_IN(type_of) - { - Expression* v = ARG("$value", Expression); - if (v->concrete_type() == Expression::STRING) { - To_String to_string; - string str(v->perform(&to_string)); - if (ctx.names_to_colors.count(str)) { - return new (ctx.mem) String_Constant(path, position, "color"); - } - } - return new (ctx.mem) String_Constant(path, position, ARG("$value", Expression)->type()); - } - - Signature unit_sig = "unit($number)"; - BUILT_IN(unit) - { return new (ctx.mem) String_Constant(path, position, quote(ARG("$number", Number)->unit(), '"')); } - - Signature unitless_sig = "unitless($number)"; - BUILT_IN(unitless) - { return new (ctx.mem) Boolean(path, position, ARG("$number", Number)->is_unitless()); } - - Signature comparable_sig = "comparable($number-1, $number-2)"; - BUILT_IN(comparable) - { - Number* n1 = ARG("$number-1", Number); - Number* n2 = ARG("$number-2", Number); - if (n1->is_unitless() || n2->is_unitless()) { - return new (ctx.mem) Boolean(path, position, true); - } - Number tmp_n2(*n2); - tmp_n2.normalize(n1->find_convertible_unit()); - return new (ctx.mem) Boolean(path, position, n1->unit() == tmp_n2.unit()); - } - - Signature variable_exists_sig = "variable-exists($name)"; - BUILT_IN(variable_exists) - { - string s = unquote(ARG("$name", String_Constant)->value()); - - if(d_env.has("$"+s)) { - return new (ctx.mem) Boolean(path, position, true); - } - else { - return new (ctx.mem) Boolean(path, position, false); - } - } - - Signature global_variable_exists_sig = "global-variable-exists($name)"; - BUILT_IN(global_variable_exists) - { - string s = unquote(ARG("$name", String_Constant)->value()); - - if(d_env.global_frame_has("$"+s)) { - return new (ctx.mem) Boolean(path, position, true); - } - else { - return new (ctx.mem) Boolean(path, position, false); - } - } - - Signature function_exists_sig = "function-exists($name)"; - BUILT_IN(function_exists) - { - string s = unquote(ARG("$name", String_Constant)->value()); - - if(d_env.global_frame_has(s+"[f]")) { - return new (ctx.mem) Boolean(path, position, true); - } - else { - return new (ctx.mem) Boolean(path, position, false); - } - } - - Signature mixin_exists_sig = "mixin-exists($name)"; - BUILT_IN(mixin_exists) - { - string s = unquote(ARG("$name", String_Constant)->value()); - - if(d_env.global_frame_has(s+"[m]")) { - return new (ctx.mem) Boolean(path, position, true); - } - else { - return new (ctx.mem) Boolean(path, position, false); - } - } - - Signature call_sig = "call($name, $args...)"; - BUILT_IN(call) - { - string name = unquote(ARG("$name", String_Constant)->value()); - List* arglist = new (ctx.mem) List(*ARG("$args", List)); - - Arguments* args = new (ctx.mem) Arguments(path, position); - for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Argument* arg = new (ctx.mem) Argument(path, position, arglist->value_at_index(i)); - *args << arg; - } - Function_Call* func = new (ctx.mem) Function_Call(path, position, name, args); - Eval eval(ctx, &d_env, backtrace); - return func->perform(&eval); - - } - - //////////////////// - // BOOLEAN FUNCTIONS - //////////////////// - - Signature not_sig = "not($value)"; - BUILT_IN(sass_not) - { return new (ctx.mem) Boolean(path, position, ARG("$value", Expression)->is_false()); } - - Signature if_sig = "if($condition, $if-true, $if-false)"; - // BUILT_IN(sass_if) - // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); } - BUILT_IN(sass_if) - { - Eval eval(ctx, &d_env, backtrace); - bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false(); - if (is_true) { - return ARG("$if-true", Expression)->perform(&eval); - } - else { - return ARG("$if-false", Expression)->perform(&eval); - } - } - - //////////////// - // URL FUNCTIONS - //////////////// - - Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)"; - BUILT_IN(image_url) - { - String_Constant* ipath = ARG("$path", String_Constant); - bool only_path = !ARG("$only-path", Expression)->is_false(); - string full_path(quote(ctx.image_path + "/" + unquote(ipath->value()), '"')); - if (!only_path) full_path = "url(" + full_path + ")"; - return new (ctx.mem) String_Constant(path, position, full_path); - } - - } -} diff --git a/functions.hpp b/functions.hpp deleted file mode 100644 index a01d6598..00000000 --- a/functions.hpp +++ /dev/null @@ -1,179 +0,0 @@ -#define SASS_FUNCTIONS - -#ifndef SASS_ENVIRONMENT -#include "environment.hpp" -#endif - -#ifndef SASS -#include "sass.h" -#endif - -#include - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -#define BUILT_IN(name) Expression*\ -name(Env& env, Env& d_env, Context& ctx, Signature sig, const string& path, Position position, Backtrace* backtrace) - -namespace Sass { - struct Context; - struct Backtrace; - class AST_Node; - class Expression; - class Definition; - typedef Environment Env; - typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, const string&, Position, Backtrace*); - - Definition* make_native_function(Signature, Native_Function, Context&); - Definition* make_c_function(Signature sig, Sass_C_Function f, void* cookie, Context& ctx); - - namespace Functions { - - extern Signature rgb_sig; - extern Signature rgba_4_sig; - extern Signature rgba_2_sig; - extern Signature red_sig; - extern Signature green_sig; - extern Signature blue_sig; - extern Signature mix_sig; - extern Signature hsl_sig; - extern Signature hsla_sig; - extern Signature hue_sig; - extern Signature saturation_sig; - extern Signature lightness_sig; - extern Signature adjust_hue_sig; - extern Signature lighten_sig; - extern Signature darken_sig; - extern Signature saturate_sig; - extern Signature desaturate_sig; - extern Signature grayscale_sig; - extern Signature complement_sig; - extern Signature invert_sig; - extern Signature alpha_sig; - extern Signature opacity_sig; - extern Signature opacify_sig; - extern Signature fade_in_sig; - extern Signature transparentize_sig; - extern Signature fade_out_sig; - extern Signature adjust_color_sig; - extern Signature scale_color_sig; - extern Signature change_color_sig; - extern Signature ie_hex_str_sig; - extern Signature unquote_sig; - extern Signature quote_sig; - extern Signature str_length_sig; - extern Signature str_insert_sig; - extern Signature str_index_sig; - extern Signature str_slice_sig; - extern Signature to_upper_case_sig; - extern Signature to_lower_case_sig; - extern Signature percentage_sig; - extern Signature round_sig; - extern Signature ceil_sig; - extern Signature floor_sig; - extern Signature abs_sig; - extern Signature min_sig; - extern Signature max_sig; - extern Signature length_sig; - extern Signature nth_sig; - extern Signature index_sig; - extern Signature join_sig; - extern Signature append_sig; - extern Signature zip_sig; - extern Signature compact_sig; - extern Signature list_separator_sig; - extern Signature type_of_sig; - extern Signature unit_sig; - extern Signature unitless_sig; - extern Signature comparable_sig; - extern Signature variable_exists_sig; - extern Signature global_variable_exists_sig; - extern Signature function_exists_sig; - extern Signature mixin_exists_sig; - extern Signature call_sig; - extern Signature not_sig; - extern Signature if_sig; - extern Signature image_url_sig; - extern Signature map_get_sig; - extern Signature map_merge_sig; - extern Signature map_remove_sig; - extern Signature map_keys_sig; - extern Signature map_values_sig; - extern Signature map_has_key_sig; - extern Signature keywords_sig; - - BUILT_IN(rgb); - BUILT_IN(rgba_4); - BUILT_IN(rgba_2); - BUILT_IN(red); - BUILT_IN(green); - BUILT_IN(blue); - BUILT_IN(mix); - BUILT_IN(hsl); - BUILT_IN(hsla); - BUILT_IN(hue); - BUILT_IN(saturation); - BUILT_IN(lightness); - BUILT_IN(adjust_hue); - BUILT_IN(lighten); - BUILT_IN(darken); - BUILT_IN(saturate); - BUILT_IN(desaturate); - BUILT_IN(grayscale); - BUILT_IN(complement); - BUILT_IN(invert); - BUILT_IN(alpha); - BUILT_IN(opacify); - BUILT_IN(transparentize); - BUILT_IN(adjust_color); - BUILT_IN(scale_color); - BUILT_IN(change_color); - BUILT_IN(ie_hex_str); - BUILT_IN(sass_unquote); - BUILT_IN(sass_quote); - BUILT_IN(str_length); - BUILT_IN(str_insert); - BUILT_IN(str_index); - BUILT_IN(str_slice); - BUILT_IN(to_upper_case); - BUILT_IN(to_lower_case); - BUILT_IN(percentage); - BUILT_IN(round); - BUILT_IN(ceil); - BUILT_IN(floor); - BUILT_IN(abs); - BUILT_IN(min); - BUILT_IN(max); - BUILT_IN(length); - BUILT_IN(nth); - BUILT_IN(index); - BUILT_IN(join); - BUILT_IN(append); - BUILT_IN(zip); - BUILT_IN(compact); - BUILT_IN(list_separator); - BUILT_IN(type_of); - BUILT_IN(unit); - BUILT_IN(unitless); - BUILT_IN(comparable); - BUILT_IN(variable_exists); - BUILT_IN(global_variable_exists); - BUILT_IN(function_exists); - BUILT_IN(mixin_exists); - BUILT_IN(call); - BUILT_IN(sass_not); - BUILT_IN(sass_if); - BUILT_IN(image_url); - BUILT_IN(map_get); - BUILT_IN(map_merge); - BUILT_IN(map_remove); - BUILT_IN(map_keys); - BUILT_IN(map_values); - BUILT_IN(map_has_key); - BUILT_IN(keywords); - - } -} diff --git a/inspect.cpp b/inspect.cpp deleted file mode 100644 index 18f12d7f..00000000 --- a/inspect.cpp +++ /dev/null @@ -1,633 +0,0 @@ -#include "inspect.hpp" -#include "ast.hpp" -#include "context.hpp" -#include -#include -#include - -namespace Sass { - using namespace std; - - Inspect::Inspect(Context* ctx) : buffer(""), indentation(0), ctx(ctx) { } - Inspect::~Inspect() { } - - // statements - void Inspect::operator()(Block* block) - { - if (!block->is_root()) { - append_to_buffer(" {\n"); - ++indentation; - } - for (size_t i = 0, L = block->length(); i < L; ++i) { - indent(); - (*block)[i]->perform(this); - // extra newline at the end of top-level statements - if (block->is_root()) append_to_buffer("\n"); - append_to_buffer("\n"); - } - if (!block->is_root()) { - --indentation; - indent(); - append_to_buffer("}"); - } - // remove extra newline that gets added after the last top-level block - if (block->is_root()) { - size_t l = buffer.length(); - if (l > 2 && buffer[l-1] == '\n' && buffer[l-2] == '\n') { - buffer.erase(l-1); - if (ctx) ctx->source_map.remove_line(); - } - } - } - - void Inspect::operator()(Ruleset* ruleset) - { - ruleset->selector()->perform(this); - ruleset->block()->perform(this); - } - - void Inspect::operator()(Propset* propset) - { - propset->property_fragment()->perform(this); - append_to_buffer(": "); - propset->block()->perform(this); - } - - void Inspect::operator()(Media_Block* media_block) - { - if (ctx) ctx->source_map.add_mapping(media_block); - append_to_buffer("@media "); - media_block->media_queries()->perform(this); - media_block->block()->perform(this); - } - - void Inspect::operator()(At_Rule* at_rule) - { - append_to_buffer(at_rule->keyword()); - if (at_rule->selector()) { - append_to_buffer(" "); - at_rule->selector()->perform(this); - } - if (at_rule->block()) { - at_rule->block()->perform(this); - } - else { - append_to_buffer(";"); - } - } - - void Inspect::operator()(Declaration* dec) - { - if (dec->value()->concrete_type() == Expression::NULL_VAL) return; - if (ctx) ctx->source_map.add_mapping(dec->property()); - dec->property()->perform(this); - append_to_buffer(": "); - if (ctx) ctx->source_map.add_mapping(dec->value()); - dec->value()->perform(this); - if (dec->is_important()) append_to_buffer(" !important"); - append_to_buffer(";"); - } - - void Inspect::operator()(Assignment* assn) - { - append_to_buffer(assn->variable()); - append_to_buffer(": "); - assn->value()->perform(this); - if (assn->is_guarded()) append_to_buffer(" !default"); - append_to_buffer(";"); - } - - void Inspect::operator()(Import* import) - { - if (!import->urls().empty()) { - if (ctx) ctx->source_map.add_mapping(import); - append_to_buffer("@import "); - import->urls().front()->perform(this); - append_to_buffer(";"); - for (size_t i = 1, S = import->urls().size(); i < S; ++i) { - append_to_buffer("\n"); - if (ctx) ctx->source_map.add_mapping(import); - append_to_buffer("@import "); - import->urls()[i]->perform(this); - append_to_buffer(";"); - } - } - } - - void Inspect::operator()(Import_Stub* import) - { - if (ctx) ctx->source_map.add_mapping(import); - append_to_buffer("@import "); - append_to_buffer(import->file_name()); - append_to_buffer(";"); - } - - void Inspect::operator()(Warning* warning) - { - if (ctx) ctx->source_map.add_mapping(warning); - append_to_buffer("@warn "); - warning->message()->perform(this); - append_to_buffer(";"); - } - - void Inspect::operator()(Comment* comment) - { - comment->text()->perform(this); - } - - void Inspect::operator()(If* cond) - { - append_to_buffer("@if "); - cond->predicate()->perform(this); - cond->consequent()->perform(this); - if (cond->alternative()) { - append_to_buffer("\n"); - indent(); - append_to_buffer("else"); - cond->alternative()->perform(this); - } - } - - void Inspect::operator()(For* loop) - { - append_to_buffer("@for "); - append_to_buffer(loop->variable()); - append_to_buffer(" from "); - loop->lower_bound()->perform(this); - append_to_buffer((loop->is_inclusive() ? " through " : " to ")); - loop->upper_bound()->perform(this); - loop->block()->perform(this); - } - - void Inspect::operator()(Each* loop) - { - append_to_buffer("@each "); - append_to_buffer(loop->variable()); - append_to_buffer(" in "); - loop->list()->perform(this); - loop->block()->perform(this); - } - - void Inspect::operator()(While* loop) - { - append_to_buffer("@while "); - loop->predicate()->perform(this); - loop->block()->perform(this); - } - - void Inspect::operator()(Return* ret) - { - append_to_buffer("@return "); - ret->value()->perform(this); - append_to_buffer(";"); - } - - void Inspect::operator()(Extension* extend) - { - append_to_buffer("@extend "); - extend->selector()->perform(this); - append_to_buffer(";"); - } - - void Inspect::operator()(Definition* def) - { - if (def->type() == Definition::MIXIN) { - append_to_buffer("@mixin "); - } else { - append_to_buffer("@function "); - } - append_to_buffer(def->name()); - def->parameters()->perform(this); - def->block()->perform(this); - } - - void Inspect::operator()(Mixin_Call* call) - { - append_to_buffer(string("@include ") += call->name()); - if (call->arguments()) { - call->arguments()->perform(this); - } - if (call->block()) { - append_to_buffer(" "); - call->block()->perform(this); - } - if (!call->block()) append_to_buffer(";"); - } - - void Inspect::operator()(Content* content) - { - if (ctx) ctx->source_map.add_mapping(content); - append_to_buffer("@content;"); - } - - void Inspect::operator()(List* list) - { - string sep(list->separator() == List::SPACE ? " " : ", "); - if (list->empty()) return; - bool items_output = false; - for (size_t i = 0, L = list->length(); i < L; ++i) { - Expression* list_item = (*list)[i]; - if (list_item->is_invisible()) { - continue; - } - if (items_output) append_to_buffer(sep); - list_item->perform(this); - items_output = true; - } - } - - void Inspect::operator()(Binary_Expression* expr) - { - expr->left()->perform(this); - switch (expr->type()) { - case Binary_Expression::AND: append_to_buffer(" and "); break; - case Binary_Expression::OR: append_to_buffer(" or "); break; - case Binary_Expression::EQ: append_to_buffer(" == "); break; - case Binary_Expression::NEQ: append_to_buffer(" != "); break; - case Binary_Expression::GT: append_to_buffer(" > "); break; - case Binary_Expression::GTE: append_to_buffer(" >= "); break; - case Binary_Expression::LT: append_to_buffer(" < "); break; - case Binary_Expression::LTE: append_to_buffer(" <= "); break; - case Binary_Expression::ADD: append_to_buffer(" + "); break; - case Binary_Expression::SUB: append_to_buffer(" - "); break; - case Binary_Expression::MUL: append_to_buffer(" * "); break; - case Binary_Expression::DIV: append_to_buffer("/"); break; - case Binary_Expression::MOD: append_to_buffer(" % "); break; - default: break; // shouldn't get here - } - expr->right()->perform(this); - } - - void Inspect::operator()(Unary_Expression* expr) - { - if (expr->type() == Unary_Expression::PLUS) append_to_buffer("+"); - else append_to_buffer("-"); - expr->operand()->perform(this); - } - - void Inspect::operator()(Function_Call* call) - { - append_to_buffer(call->name()); - call->arguments()->perform(this); - } - - void Inspect::operator()(Function_Call_Schema* call) - { - call->name()->perform(this); - call->arguments()->perform(this); - } - - void Inspect::operator()(Variable* var) - { - append_to_buffer(var->name()); - } - - void Inspect::operator()(Textual* txt) - { - append_to_buffer(txt->value()); - } - - // helper functions for serializing numbers - // string frac_to_string(double f, size_t p) { - // stringstream ss; - // ss.setf(ios::fixed, ios::floatfield); - // ss.precision(p); - // ss << f; - // string result(ss.str().substr(f < 0 ? 2 : 1)); - // size_t i = result.size() - 1; - // while (result[i] == '0') --i; - // result = result.substr(0, i+1); - // return result; - // } - // string double_to_string(double d, size_t p) { - // stringstream ss; - // double ipart; - // double fpart = std::modf(d, &ipart); - // ss << ipart; - // if (fpart != 0) ss << frac_to_string(fpart, 5); - // return ss.str(); - // } - - void Inspect::operator()(Number* n) - { - stringstream ss; - ss.precision(ctx ? ctx->precision : 5); - ss << fixed << n->value(); - string d(ss.str()); - for (size_t i = d.length()-1; d[i] == '0'; --i) { - d.resize(d.length()-1); - } - if (d[d.length()-1] == '.') d.resize(d.length()-1); - if (n->numerator_units().size() > 1 || n->denominator_units().size() > 0) { - error(d + n->unit() + " is not a valid CSS value", n->path(), n->position()); - } - append_to_buffer(d == "-0" ? "0" : d); - append_to_buffer(n->unit()); - } - - // helper function for serializing colors - template - static double cap_channel(double c) { - if (c > range) return range; - else if (c < 0) return 0; - else return c; - } - - void Inspect::operator()(Color* c) - { - stringstream ss; - double r = round(cap_channel<0xff>(c->r())); - double g = round(cap_channel<0xff>(c->g())); - double b = round(cap_channel<0xff>(c->b())); - double a = cap_channel<1> (c->a()); - - // retain the originally specified color definition if unchanged - if (!c->disp().empty()) { - ss << c->disp(); - } - else if (a >= 1) { - // see if it's a named color - int numval = r * 0x10000; - numval += g * 0x100; - numval += b; - if (ctx && ctx->colors_to_names.count(numval)) { - ss << ctx->colors_to_names[numval]; - } - else { - // otherwise output the hex triplet - ss << '#' << setw(2) << setfill('0'); - ss << hex << setw(2) << static_cast(r); - ss << hex << setw(2) << static_cast(g); - ss << hex << setw(2) << static_cast(b); - } - } - else { - ss << "rgba("; - ss << static_cast(r) << ", "; - ss << static_cast(g) << ", "; - ss << static_cast(b) << ", "; - ss << a << ')'; - } - append_to_buffer(ss.str()); - } - - void Inspect::operator()(Boolean* b) - { - append_to_buffer(b->value() ? "true" : "false"); - } - - void Inspect::operator()(String_Schema* ss) - { - // Evaluation should turn these into String_Constants, so this method is - // only for inspection purposes. - for (size_t i = 0, L = ss->length(); i < L; ++i) { - if ((*ss)[i]->is_interpolant()) append_to_buffer("#{"); - (*ss)[i]->perform(this); - if ((*ss)[i]->is_interpolant()) append_to_buffer("}"); - } - } - - void Inspect::operator()(String_Constant* s) - { - append_to_buffer(s->needs_unquoting() ? unquote(s->value()) : s->value()); - } - - void Inspect::operator()(Media_Query* mq) - { - size_t i = 0; - if (mq->media_type()) { - if (mq->is_negated()) append_to_buffer("not "); - else if (mq->is_restricted()) append_to_buffer("only "); - mq->media_type()->perform(this); - } - else { - (*mq)[i++]->perform(this); - } - for (size_t L = mq->length(); i < L; ++i) { - append_to_buffer(" and "); - (*mq)[i]->perform(this); - } - } - - void Inspect::operator()(Media_Query_Expression* mqe) - { - if (mqe->is_interpolated()) { - mqe->feature()->perform(this); - } - else { - append_to_buffer("("); - mqe->feature()->perform(this); - if (mqe->value()) { - append_to_buffer(": "); - mqe->value()->perform(this); - } - append_to_buffer(")"); - } - } - - void Inspect::operator()(Null* n) - { - append_to_buffer("null"); - } - - // parameters and arguments - void Inspect::operator()(Parameter* p) - { - append_to_buffer(p->name()); - if (p->default_value()) { - append_to_buffer(": "); - p->default_value()->perform(this); - } - else if (p->is_rest_parameter()) { - append_to_buffer("..."); - } - } - - void Inspect::operator()(Parameters* p) - { - append_to_buffer("("); - if (!p->empty()) { - (*p)[0]->perform(this); - for (size_t i = 1, L = p->length(); i < L; ++i) { - append_to_buffer(", "); - (*p)[i]->perform(this); - } - } - append_to_buffer(")"); - } - - void Inspect::operator()(Argument* a) - { - if (!a->name().empty()) { - append_to_buffer(a->name()); - append_to_buffer(": "); - } - // Special case: argument nulls can be ignored - if (a->value()->concrete_type() == Expression::NULL_VAL) { - return; - } - a->value()->perform(this); - if (a->is_rest_argument()) { - append_to_buffer("..."); - } - } - - void Inspect::operator()(Arguments* a) - { - append_to_buffer("("); - if (!a->empty()) { - (*a)[0]->perform(this); - for (size_t i = 1, L = a->length(); i < L; ++i) { - append_to_buffer(", "); - (*a)[i]->perform(this); - } - } - append_to_buffer(")"); - } - - // selectors - void Inspect::operator()(Selector_Schema* s) - { - s->contents()->perform(this); - } - - void Inspect::operator()(Selector_Reference* ref) - { - if (ref->selector()) ref->selector()->perform(this); - else append_to_buffer("&"); - } - - void Inspect::operator()(Selector_Placeholder* s) - { - append_to_buffer(s->name()); - } - - void Inspect::operator()(Type_Selector* s) - { - if (ctx) ctx->source_map.add_mapping(s); - append_to_buffer(s->name()); - } - - void Inspect::operator()(Selector_Qualifier* s) - { - if (ctx) ctx->source_map.add_mapping(s); - append_to_buffer(s->name()); - } - - void Inspect::operator()(Attribute_Selector* s) - { - if (ctx) ctx->source_map.add_mapping(s); - append_to_buffer("["); - append_to_buffer(s->name()); - if (!s->matcher().empty()) { - append_to_buffer(s->matcher()); - if (s->value()) { - s->value()->perform(this); - } - // append_to_buffer(s->value()); - } - append_to_buffer("]"); - } - - void Inspect::operator()(Pseudo_Selector* s) - { - if (ctx) ctx->source_map.add_mapping(s); - append_to_buffer(s->name()); - if (s->expression()) { - s->expression()->perform(this); - append_to_buffer(")"); - } - } - - void Inspect::operator()(Wrapped_Selector* s) - { - if (ctx) ctx->source_map.add_mapping(s); - append_to_buffer(s->name()); - s->selector()->perform(this); - append_to_buffer(")"); - } - - void Inspect::operator()(Compound_Selector* s) - { - for (size_t i = 0, L = s->length(); i < L; ++i) { - (*s)[i]->perform(this); - } - } - - void Inspect::operator()(Complex_Selector* c) - { - Compound_Selector* head = c->head(); - Complex_Selector* tail = c->tail(); - Complex_Selector::Combinator comb = c->combinator(); - if (head && !head->is_empty_reference()) head->perform(this); - if (head && !head->is_empty_reference() && tail) append_to_buffer(" "); - switch (comb) { - case Complex_Selector::ANCESTOR_OF: break; - case Complex_Selector::PARENT_OF: append_to_buffer(">"); break; - case Complex_Selector::PRECEDES: append_to_buffer("~"); break; - case Complex_Selector::ADJACENT_TO: append_to_buffer("+"); break; - } - if (tail && comb != Complex_Selector::ANCESTOR_OF) { - append_to_buffer(" "); - } - if (tail) tail->perform(this); - } - - void Inspect::operator()(Selector_List* g) - { - if (g->empty()) return; - (*g)[0]->perform(this); - for (size_t i = 1, L = g->length(); i < L; ++i) { - append_to_buffer(", "); - (*g)[i]->perform(this); - } - } - - inline void Inspect::fallback_impl(AST_Node* n) - { } - - void Inspect::indent() - { append_to_buffer(string(2*indentation, ' ')); } - - string unquote(const string& s) - { - if (s.empty()) return ""; - if (s.length() == 1) { - if (s[0] == '"' || s[0] == '\'') return ""; - } - char q; - if (*s.begin() == '"' && *s.rbegin() == '"') q = '"'; - else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\''; - else return s; - string t; - t.reserve(s.length()-2); - for (size_t i = 1, L = s.length()-1; i < L; ++i) { - // if we see a quote, we need to remove the preceding backslash from t - if (s[i] == q) t.erase(t.length()-1); - t.push_back(s[i]); - } - return t; - } - - string quote(const string& s, char q) - { - if (s.empty()) return string(2, q); - if (!q || s[0] == '"' || s[0] == '\'') return s; - string t; - t.reserve(s.length()+2); - t.push_back(q); - for (size_t i = 0, L = s.length(); i < L; ++i) { - if (s[i] == q) t.push_back('\\'); - t.push_back(s[i]); - } - t.push_back(q); - return t; - } - - void Inspect::append_to_buffer(const string& text) - { - buffer += text; - if (ctx) ctx->source_map.update_column(text); - } - -} diff --git a/inspect.hpp b/inspect.hpp deleted file mode 100644 index e4f5ad76..00000000 --- a/inspect.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#include - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -// #ifndef SASS_TO_STRING -// #include "to_string.hpp" -// #endif - -namespace Sass { - using namespace std; - struct Context; - - class Inspect : public Operation_CRTP { - // import all the class-specific methods and override as desired - using Operation_CRTP::operator(); - - // To_String* to_string; - string buffer; - size_t indentation; - Context* ctx; - void indent(); - - void fallback_impl(AST_Node* n); - - void append_to_buffer(const string& text); - - public: - - Inspect(Context* ctx = 0); - virtual ~Inspect(); - - string get_buffer() { return buffer; } - - // statements - virtual void operator()(Block*); - virtual void operator()(Ruleset*); - virtual void operator()(Propset*); - virtual void operator()(Media_Block*); - virtual void operator()(At_Rule*); - virtual void operator()(Declaration*); - virtual void operator()(Assignment*); - virtual void operator()(Import*); - virtual void operator()(Import_Stub*); - virtual void operator()(Warning*); - virtual void operator()(Comment*); - virtual void operator()(If*); - virtual void operator()(For*); - virtual void operator()(Each*); - virtual void operator()(While*); - virtual void operator()(Return*); - virtual void operator()(Extension*); - virtual void operator()(Definition*); - virtual void operator()(Mixin_Call*); - virtual void operator()(Content*); - // expressions - virtual void operator()(List*); - virtual void operator()(Binary_Expression*); - virtual void operator()(Unary_Expression*); - virtual void operator()(Function_Call*); - virtual void operator()(Function_Call_Schema*); - virtual void operator()(Variable*); - virtual void operator()(Textual*); - virtual void operator()(Number*); - virtual void operator()(Color*); - virtual void operator()(Boolean*); - virtual void operator()(String_Schema*); - virtual void operator()(String_Constant*); - virtual void operator()(Media_Query*); - virtual void operator()(Media_Query_Expression*); - virtual void operator()(Null*); - // parameters and arguments - virtual void operator()(Parameter*); - virtual void operator()(Parameters*); - virtual void operator()(Argument*); - virtual void operator()(Arguments*); - // selectors - virtual void operator()(Selector_Schema*); - virtual void operator()(Selector_Reference*); - virtual void operator()(Selector_Placeholder*); - virtual void operator()(Type_Selector*); - virtual void operator()(Selector_Qualifier*); - virtual void operator()(Attribute_Selector*); - virtual void operator()(Pseudo_Selector*); - virtual void operator()(Wrapped_Selector*); - virtual void operator()(Compound_Selector*); - virtual void operator()(Complex_Selector*); - virtual void operator()(Selector_List*); - - template - void fallback(U x) { fallback_impl(x); } - }; - - string unquote(const string&); - string quote(const string&, char); - -} diff --git a/kwd_arg_macros.hpp b/kwd_arg_macros.hpp deleted file mode 100644 index 3227ac6c..00000000 --- a/kwd_arg_macros.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Example usage: -// KWD_ARG_SET(Args) { -// KWD_ARG(Args, string, foo); -// KWD_ARG(Args, int, bar); -// ... -// }; -// -// ... and later ... -// -// something(Args().foo("hey").bar(3)); - -#define KWD_ARG_SET(set_name) class set_name - -#define KWD_ARG(set_name, type, name) \ -private: \ - type name##_; \ -public: \ - set_name& name(type name##__) { \ - name##_ = name##__; \ - return *this; \ - } \ - type name() { return name##_; } \ -private: diff --git a/libsass b/libsass new file mode 160000 index 00000000..f660edea --- /dev/null +++ b/libsass @@ -0,0 +1 @@ +Subproject commit f660edea1f747950ad0ddedd07f794d377b70132 diff --git a/m4/.gitkeep b/m4/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/mapping.hpp b/mapping.hpp deleted file mode 100644 index dc39718f..00000000 --- a/mapping.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#define SASS_MAPPING - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -namespace Sass { - - struct Mapping { - Position original_position; - Position generated_position; - - Mapping(const Position& original_position, const Position& generated_position) - : original_position(original_position), generated_position(generated_position) { } - }; - -} diff --git a/memory_manager.hpp b/memory_manager.hpp deleted file mode 100644 index 3aba8da6..00000000 --- a/memory_manager.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#define SASS_MEMORY_MANAGER - -#include -#include -using namespace std; - -namespace Sass { - ///////////////////////////////////////////////////////////////////////////// - // A class for tracking allocations of AST_Node objects. The intended usage - // is something like: Some_Node* n = new (mem_mgr) Some_Node(...); - // Then, at the end of the program, the memory manager will delete all of the - // allocated nodes that have been passed to it. - // In the future, this class may implement a custom allocator. - ///////////////////////////////////////////////////////////////////////////// - template - class Memory_Manager { - vector nodes; - - public: - Memory_Manager(size_t size = 0) : nodes(vector()) - { nodes.reserve(size); } - - ~Memory_Manager() - { - for (size_t i = 0, S = nodes.size(); i < S; ++i) { - // cout << "deleting " << typeid(*nodes[i]).name() << endl; - delete nodes[i]; - } - } - - T* operator()(T* np) - { - nodes.push_back(np); - // cout << "registering " << typeid(*np).name() << endl; - return np; - } - - void remove(T* np) - { - nodes.erase(find(nodes.begin(), nodes.end(), np)); - } - }; -} - -template -inline void* operator new(size_t size, Sass::Memory_Manager& mem_mgr) -{ return mem_mgr(static_cast(operator new(size))); } - -template -inline void operator delete(void *np, Sass::Memory_Manager& mem_mgr) -{ - mem_mgr.remove(reinterpret_cast(np)); - operator delete(np); -} diff --git a/node.cpp b/node.cpp deleted file mode 100644 index cdb1d33a..00000000 --- a/node.cpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "node.hpp" -#include "to_string.hpp" -#include "context.hpp" -#include "parser.hpp" - -namespace Sass { - - - Node Node::createCombinator(const Complex_Selector::Combinator& combinator) { - NodeDequePtr null; - return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/); - } - - - Node Node::createSelector(Complex_Selector* pSelector, Context& ctx) { - NodeDequePtr null; - - Complex_Selector* pStripped = pSelector->clone(ctx); - pStripped->tail(NULL); - pStripped->combinator(Complex_Selector::ANCESTOR_OF); - - return Node(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/); - } - - - Node Node::createCollection() { - NodeDequePtr pEmptyCollection = make_shared(); - return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection); - } - - - Node Node::createCollection(const NodeDeque& values) { - NodeDequePtr pShallowCopiedCollection = make_shared(values); - return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pShallowCopiedCollection); - } - - - Node Node::createNil() { - NodeDequePtr null; - return Node(NIL, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, null /*pCollection*/); - } - - - Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection) : - mType(type), - mCombinator(combinator), - mpSelector(pSelector), - mpCollection(pCollection) {} - - - Node Node::clone(Context& ctx) const { - NodeDequePtr pNewCollection = make_shared(); - if (mpCollection) { - for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { - Node& toClone = *iter; - pNewCollection->push_back(toClone.clone(ctx)); - } - } - - return Node(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection); - } - - - bool Node::contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const { - bool found = false; - - for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) { - Node& toTest = *iter; - - if (nodesEqual(toTest, potentialChild, simpleSelectorOrderDependent)) { - found = true; - break; - } - } - - return found; - } - - - bool Node::operator==(const Node& rhs) const { - return nodesEqual(*this, rhs, true /*simpleSelectorOrderDependent*/); - } - - - bool nodesEqual(const Node& lhs, const Node& rhs, bool simpleSelectorOrderDependent) { - if (lhs.type() != rhs.type()) { - return false; - } - - if (lhs.isCombinator()) { - - return lhs.combinator() == rhs.combinator(); - - } else if (lhs.isNil()) { - - return true; // no state to check - - } else if (lhs.isSelector()){ - - return selectors_equal(*lhs.selector(), *rhs.selector(), simpleSelectorOrderDependent); - - } else if (lhs.isCollection()) { - - if (lhs.collection()->size() != rhs.collection()->size()) { - return false; - } - - for (NodeDeque::iterator lhsIter = lhs.collection()->begin(), lhsIterEnd = lhs.collection()->end(), - rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) { - - if (!nodesEqual(*lhsIter, *rhsIter, simpleSelectorOrderDependent)) { - return false; - } - - } - - return true; - - } - - // We shouldn't get here. - throw "Comparing unknown node types. A new type was probably added and this method wasn't implemented for it."; - } - - - void Node::plus(Node& rhs) { - if (!this->isCollection() || !rhs.isCollection()) { - throw "Both the current node and rhs must be collections."; - } - this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end()); - } - - - ostream& operator<<(ostream& os, const Node& node) { - - if (node.isCombinator()) { - - switch (node.combinator()) { - case Complex_Selector::ANCESTOR_OF: os << "\" \""; break; - case Complex_Selector::PARENT_OF: os << "\">\""; break; - case Complex_Selector::PRECEDES: os << "\"~\""; break; - case Complex_Selector::ADJACENT_TO: os << "\"+\""; break; - } - - } else if (node.isNil()) { - - os << "nil"; - - } else if (node.isSelector()){ - - To_String to_string; - os << node.selector()->head()->perform(&to_string); - - } else if (node.isCollection()) { - - os << "["; - - for (NodeDeque::iterator iter = node.collection()->begin(), iterBegin = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) { - if (iter != iterBegin) { - os << ", "; - } - - os << (*iter); - } - - os << "]"; - - } - - return os; - - } - - - Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx) { - if (pToConvert == NULL) { - return Node::createNil(); - } - - Node node = Node::createCollection(); - - while (pToConvert) { - - // the first Complex_Selector may contain a dummy head pointer, skip it. - if (pToConvert->head() != NULL && !pToConvert->head()->is_empty_reference()) { - node.collection()->push_back(Node::createSelector(pToConvert, ctx)); - } - - if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) { - node.collection()->push_back(Node::createCombinator(pToConvert->combinator())); - } - - pToConvert = pToConvert->tail(); - } - - return node; - } - - - Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx) { - if (toConvert.isNil()) { - return NULL; - } - - - if (!toConvert.isCollection()) { - throw "The node to convert to a Complex_Selector* must be a collection type or nil."; - } - - - NodeDeque& childNodes = *toConvert.collection(); - - string noPath(""); - Position noPosition; - Complex_Selector* pFirst = new (ctx.mem) Complex_Selector(noPath, noPosition, Complex_Selector::ANCESTOR_OF, NULL, NULL); - Complex_Selector* pCurrent = pFirst; - - for (NodeDeque::iterator childIter = childNodes.begin(), childIterEnd = childNodes.end(); childIter != childIterEnd; childIter++) { - - Node& child = *childIter; - - if (child.isSelector()) { - pCurrent->tail(child.selector()->clone(ctx)); // JMA - need to clone the selector, because they can end up getting shared across Node collections, and can result in an infinite loop during the call to parentSuperselector() - pCurrent = pCurrent->tail(); - } else if (child.isCombinator()) { - pCurrent->combinator(child.combinator()); - - // if the next node is also a combinator, create another Complex_Selector to hold it so it doesn't replace the current combinator - if (childIter+1 != childIterEnd) { - Node& nextNode = *(childIter+1); - if (nextNode.isCombinator()) { - pCurrent->tail(new (ctx.mem) Complex_Selector(noPath, noPosition, Complex_Selector::ANCESTOR_OF, NULL, NULL)); - pCurrent = pCurrent->tail(); - } - } - } else { - throw "The node to convert's children must be only combinators or selectors."; - } - } - - // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass - Compound_Selector* fakeHead = new (ctx.mem) Compound_Selector(noPath, noPosition, 1); - Selector_Reference* selectorRef = new (ctx.mem) Selector_Reference(noPath, noPosition, NULL); - fakeHead->elements().push_back(selectorRef); - pFirst->head(fakeHead); - - return pFirst; - } - - -} diff --git a/node.hpp b/node.hpp deleted file mode 100644 index 83e9731e..00000000 --- a/node.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#define SASS_NODE - - -#include -#include -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - - -namespace Sass { - - - using namespace std; - - - struct Context; - - /* - There are a lot of stumbling blocks when trying to port the ruby extend code to C++. The biggest is the choice of - data type. The ruby code will pretty seamlessly switch types between an Array (libsass' - equivalent is the Complex_Selector) to a Sequence, which contains more metadata about the sequence than just the - selector info. They also have the ability to have arbitrary nestings of arrays like [1, [2]], which is hard to - implement using Array equivalents in C++ (like the deque or vector). They also have the ability to include nil - in the arrays, like [1, nil, 3], which has potential semantic differences than an empty array [1, [], 3]. To be - able to represent all of these as unique cases, we need to create a tree of variant objects. The tree nature allows - the inconsistent nesting levels. The variant nature (while making some of the C++ code uglier) allows the code to - more closely match the ruby code, which is a huge benefit when attempting to implement an complex algorithm like - the Extend operator. - - Note that the current libsass data model also pairs the combinator with the Complex_Selector that follows it, but - ruby sass has no such restriction, so we attempt to create a data structure that can handle them split apart. - */ - - class Node; - typedef deque NodeDeque; - typedef shared_ptr NodeDequePtr; - - class Node { - public: - enum TYPE { - SELECTOR, - COMBINATOR, - COLLECTION, - NIL - }; - - TYPE type() const { return mType; } - bool isCombinator() const { return mType == COMBINATOR; } - bool isSelector() const { return mType == SELECTOR; } - bool isCollection() const { return mType == COLLECTION; } - bool isNil() const { return mType == NIL; } - - Complex_Selector::Combinator combinator() const { return mCombinator; } - - Complex_Selector* selector() { return mpSelector; } - const Complex_Selector* selector() const { return mpSelector; } - - NodeDequePtr collection() { return mpCollection; } - const NodeDequePtr collection() const { return mpCollection; } - - static Node createCombinator(const Complex_Selector::Combinator& combinator); - - // This method will clone the selector, stripping off the tail and combinator - static Node createSelector(Complex_Selector* pSelector, Context& ctx); - - static Node createCollection(); - static Node createCollection(const NodeDeque& values); - - static Node createNil(); - - Node clone(Context& ctx) const; - - bool operator==(const Node& rhs) const; - inline bool operator!=(const Node& rhs) const { return !(*this == rhs); } - - - /* - COLLECTION FUNCTIONS - - Most types don't need any helper methods (nil and combinator due to their simplicity and - selector due to the fact that we leverage the non-node selector code on the Complex_Selector - whereever possible). The following methods are intended to be called on Node objects whose - type is COLLECTION only. - */ - - // rhs and this must be node collections. Shallow copy the nodes from rhs to the end of this. - // This function DOES NOT remove the nodes from rhs. - void plus(Node& rhs); - - // potentialChild must be a node collection of selectors/combinators. this must be a collection - // of collections of nodes/combinators. This method checks if potentialChild is a child of this - // Node. - bool contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const; - - private: - // Private constructor; Use the static methods (like createCombinator and createSelector) - // to instantiate this object. This is more expressive, and it allows us to break apart each - // case into separate functions. - Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection); - - TYPE mType; - - // TODO: can we union these to save on memory? - Complex_Selector::Combinator mCombinator; - Complex_Selector* mpSelector; // this is an AST_Node, so it will be handled by the Memory_Manager - NodeDequePtr mpCollection; - }; - - - ostream& operator<<(ostream& os, const Node& node); - - - Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx); - Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx); - - - bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent); - -} diff --git a/operation.hpp b/operation.hpp deleted file mode 100644 index 153834c7..00000000 --- a/operation.hpp +++ /dev/null @@ -1,143 +0,0 @@ -#define SASS_OPERATION - -#include "ast_fwd_decl.hpp" - -#include -using namespace std; -#include - -namespace Sass { - - template - class Operation { - public: - virtual T operator()(AST_Node* x) = 0; - virtual ~Operation() { } - // statements - virtual T operator()(Block* x) = 0; - virtual T operator()(Ruleset* x) = 0; - virtual T operator()(Propset* x) = 0; - virtual T operator()(Media_Block* x) = 0; - virtual T operator()(At_Rule* x) = 0; - virtual T operator()(Declaration* x) = 0; - virtual T operator()(Assignment* x) = 0; - virtual T operator()(Import* x) = 0; - virtual T operator()(Import_Stub* x) = 0; - virtual T operator()(Warning* x) = 0; - virtual T operator()(Comment* x) = 0; - virtual T operator()(If* x) = 0; - virtual T operator()(For* x) = 0; - virtual T operator()(Each* x) = 0; - virtual T operator()(While* x) = 0; - virtual T operator()(Return* x) = 0; - virtual T operator()(Content* x) = 0; - virtual T operator()(Extension* x) = 0; - virtual T operator()(Definition* x) = 0; - virtual T operator()(Mixin_Call* x) = 0; - // expressions - virtual T operator()(List* x) = 0; - virtual T operator()(Map* x) = 0; - virtual T operator()(Binary_Expression* x) = 0; - virtual T operator()(Unary_Expression* x) = 0; - virtual T operator()(Function_Call* x) = 0; - virtual T operator()(Function_Call_Schema* x) = 0; - virtual T operator()(Variable* x) = 0; - virtual T operator()(Textual* x) = 0; - virtual T operator()(Number* x) = 0; - virtual T operator()(Color* x) = 0; - virtual T operator()(Boolean* x) = 0; - virtual T operator()(String_Schema* x) = 0; - virtual T operator()(String_Constant* x) = 0; - virtual T operator()(Media_Query* x) = 0; - virtual T operator()(Media_Query_Expression* x) = 0; - virtual T operator()(Null* x) = 0; - // parameters and arguments - virtual T operator()(Parameter* x) = 0; - virtual T operator()(Parameters* x) = 0; - virtual T operator()(Argument* x) = 0; - virtual T operator()(Arguments* x) = 0; - // selectors - virtual T operator()(Selector_Schema* x) = 0; - virtual T operator()(Selector_Reference* x) = 0; - virtual T operator()(Selector_Placeholder* x) = 0; - virtual T operator()(Type_Selector* x) = 0; - virtual T operator()(Selector_Qualifier* x) = 0; - virtual T operator()(Attribute_Selector* x) = 0; - virtual T operator()(Pseudo_Selector* x) = 0; - virtual T operator()(Wrapped_Selector* x) = 0; - virtual T operator()(Compound_Selector* x) = 0; - virtual T operator()(Complex_Selector* x) = 0; - virtual T operator()(Selector_List* x) = 0; - - template - T fallback(U x) { return T(); } - }; - - template - class Operation_CRTP : public Operation { - public: - virtual T operator()(AST_Node* x) { return static_cast(this)->fallback(x); } - virtual ~Operation_CRTP() = 0; - // statements - virtual T operator()(Block* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Ruleset* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Propset* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } - virtual T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Declaration* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Assignment* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Import* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Warning* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Comment* x) { return static_cast(this)->fallback(x); } - virtual T operator()(If* x) { return static_cast(this)->fallback(x); } - virtual T operator()(For* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Each* x) { return static_cast(this)->fallback(x); } - virtual T operator()(While* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Return* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Content* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Extension* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Definition* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Mixin_Call* x) { return static_cast(this)->fallback(x); } - // expressions - virtual T operator()(List* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Map* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Binary_Expression* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Unary_Expression* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Function_Call* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Function_Call_Schema* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Variable* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Textual* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Number* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Color* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Boolean* x) { return static_cast(this)->fallback(x); } - virtual T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } - virtual T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Null* x) { return static_cast(this)->fallback(x); } - // parameters and arguments - virtual T operator()(Parameter* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Parameters* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Argument* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Arguments* x) { return static_cast(this)->fallback(x); } - // selectors - virtual T operator()(Selector_Schema* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Selector_Reference* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Selector_Placeholder* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Type_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Selector_Qualifier* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Attribute_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Pseudo_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Wrapped_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Compound_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Complex_Selector* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Selector_List* x) { return static_cast(this)->fallback(x); } - - template - T fallback(U x) { return T(); } - }; - template - inline Operation_CRTP::~Operation_CRTP() { } - -} diff --git a/output_compressed.cpp b/output_compressed.cpp deleted file mode 100644 index 055de753..00000000 --- a/output_compressed.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "output_compressed.hpp" -#include "inspect.hpp" -#include "ast.hpp" -#include "context.hpp" -#include "to_string.hpp" -#include "util.hpp" -#include -#include - -namespace Sass { - using namespace std; - - Output_Compressed::Output_Compressed(Context* ctx) : buffer(""), rendered_imports(""), ctx(ctx) { } - Output_Compressed::~Output_Compressed() { } - - inline void Output_Compressed::fallback_impl(AST_Node* n) - { - Inspect i(ctx); - n->perform(&i); - buffer += i.get_buffer(); - } - - void Output_Compressed::operator()(Import* imp) - { - Inspect insp(ctx); - imp->perform(&insp); - rendered_imports += insp.get_buffer(); - } - - void Output_Compressed::operator()(Block* b) - { - if (!b->is_root()) return; - for (size_t i = 0, L = b->length(); i < L; ++i) { - (*b)[i]->perform(this); - } - } - - void Output_Compressed::operator()(Ruleset* r) - { - Selector* s = r->selector(); - Block* b = r->block(); - - // Filter out rulesets that aren't printable (process its children though) - if (!Util::isPrintable(r)) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { - stm->perform(this); - } - } - return; - } - - if (b->has_non_hoistable()) { - s->perform(this); - append_singleline_part_to_buffer("{"); - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - stm->perform(this); - } - } - size_t l = buffer.length(); - if (l > 0 && buffer.at(l - 1) == ';') buffer.erase(l - 1); - append_singleline_part_to_buffer("}"); - } - - if (b->has_hoistable()) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - } - } - - void Output_Compressed::operator()(Media_Block* m) - { - List* q = m->media_queries(); - Block* b = m->block(); - - // Filter out media blocks that aren't printable (process its children though) - if (!Util::isPrintable(m)) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { - stm->perform(this); - } - } - return; - } - - ctx->source_map.add_mapping(m); - append_singleline_part_to_buffer("@media "); - q->perform(this); - append_singleline_part_to_buffer("{"); - - Selector* e = m->selector(); - if (e && b->has_non_hoistable()) { - // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable - e->perform(this); - append_singleline_part_to_buffer("{"); - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - stm->perform(this); - } - } - - append_singleline_part_to_buffer("}"); - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - } - else { - // JMA - not hoisted, just output in order - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - stm->perform(this); - } - } - } - - append_singleline_part_to_buffer("}"); - } - - void Output_Compressed::operator()(At_Rule* a) - { - string kwd = a->keyword(); - Selector* s = a->selector(); - Expression* v = a->value(); - Block* b = a->block(); - - append_singleline_part_to_buffer(kwd); - if (s) { - append_singleline_part_to_buffer(" "); - s->perform(this); - } - else if (v) { - append_singleline_part_to_buffer(" "); - v->perform(this); - } - - if (!b) { - append_singleline_part_to_buffer(";"); - return; - } - - append_singleline_part_to_buffer("{"); - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - stm->perform(this); - } - } - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - - append_singleline_part_to_buffer("}"); - } - - void Output_Compressed::operator()(Declaration* d) - { - bool bPrintExpression = true; - // Check print conditions - if (d->value()->concrete_type() == Expression::NULL_VAL) { - bPrintExpression = false; - } - if (d->value()->concrete_type() == Expression::STRING) { - String_Constant* valConst = static_cast(d->value()); - string val(valConst->value()); - if (val.empty()) { - bPrintExpression = false; - } - } - // Print if OK - if(bPrintExpression) { - if (ctx) ctx->source_map.add_mapping(d->property()); - d->property()->perform(this); - append_singleline_part_to_buffer(":"); - if (ctx) ctx->source_map.add_mapping(d->value()); - d->value()->perform(this); - if (d->is_important()) append_singleline_part_to_buffer("!important"); - append_singleline_part_to_buffer(";"); - } - } - - void Output_Compressed::operator()(Comment* c) - { - To_String to_string; - string txt = c->text()->perform(&to_string); - if(txt[2] != '!') { - return; - } - else { - Inspect i(ctx); - c->perform(&i); - buffer += i.get_buffer(); - } - } - - void Output_Compressed::operator()(List* list) - { - string sep(list->separator() == List::SPACE ? " " : ","); - if (list->empty()) return; - Expression* first = (*list)[0]; - bool first_invisible = first->is_invisible(); - if (!first_invisible) first->perform(this); - for (size_t i = 1, L = list->length(); i < L; ++i) { - Expression* next = (*list)[i]; - bool next_invisible = next->is_invisible(); - if (i == 1 && !first_invisible && !next_invisible) append_singleline_part_to_buffer(sep); - else if (!next_invisible) append_singleline_part_to_buffer(sep); - next->perform(this); - } - } - - // helper function for serializing colors - template - static double cap_channel(double c) { - if (c > range) return range; - else if (c < 0) return 0; - else return c; - } - - void Output_Compressed::operator()(Color* c) - { - stringstream ss; - double r = round(cap_channel<0xff>(c->r())); - double g = round(cap_channel<0xff>(c->g())); - double b = round(cap_channel<0xff>(c->b())); - double a = cap_channel<1> (c->a()); - - // retain the originally specified color definition if unchanged - if (!c->disp().empty()) { - ss << c->disp(); - } - else if (a >= 1) { - // see if it's a named color - int numval = r * 0x10000; - numval += g * 0x100; - numval += b; - if (ctx && ctx->colors_to_names.count(numval)) { - ss << ctx->colors_to_names[numval]; - } - else { - // otherwise output the hex triplet - ss << '#' << setw(2) << setfill('0'); - ss << hex << setw(2) << static_cast(r); - ss << hex << setw(2) << static_cast(g); - ss << hex << setw(2) << static_cast(b); - } - } - else { - ss << "rgba("; - ss << static_cast(r) << ","; - ss << static_cast(g) << ","; - ss << static_cast(b) << ","; - ss << a << ')'; - } - append_singleline_part_to_buffer(ss.str()); - } - - void Output_Compressed::operator()(Media_Query_Expression* mqe) - { - if (mqe->is_interpolated()) { - mqe->feature()->perform(this); - } - else { - append_singleline_part_to_buffer("("); - mqe->feature()->perform(this); - if (mqe->value()) { - append_singleline_part_to_buffer(":"); - mqe->value()->perform(this); - } - append_singleline_part_to_buffer(")"); - } - } - - void Output_Compressed::operator()(Null* n) - { - // noop - } - - void Output_Compressed::operator()(Argument* a) - { - if (!a->name().empty()) { - append_singleline_part_to_buffer(a->name()); - append_singleline_part_to_buffer(":"); - } - a->value()->perform(this); - if (a->is_rest_argument()) { - append_singleline_part_to_buffer("..."); - } - } - - void Output_Compressed::operator()(Arguments* a) - { - append_singleline_part_to_buffer("("); - if (!a->empty()) { - (*a)[0]->perform(this); - for (size_t i = 1, L = a->length(); i < L; ++i) { - append_singleline_part_to_buffer(","); - (*a)[i]->perform(this); - } - } - append_singleline_part_to_buffer(")"); - } - - void Output_Compressed::operator()(Complex_Selector* c) - { - Compound_Selector* head = c->head(); - Complex_Selector* tail = c->tail(); - Complex_Selector::Combinator comb = c->combinator(); - if (head && head->is_empty_reference() && tail) - { - tail->perform(this); - return; - } - if (head && !head->is_empty_reference()) head->perform(this); - switch (comb) { - case Complex_Selector::ANCESTOR_OF: - if (tail) append_singleline_part_to_buffer(" "); - break; - case Complex_Selector::PARENT_OF: - append_singleline_part_to_buffer(">"); - break; - case Complex_Selector::PRECEDES: - // Apparently need to preserve spaces around this combinator? - if (head && !head->is_empty_reference()) append_singleline_part_to_buffer(" "); - append_singleline_part_to_buffer("~"); - if (tail) append_singleline_part_to_buffer(" "); - break; - case Complex_Selector::ADJACENT_TO: - append_singleline_part_to_buffer("+"); - break; - } - if (tail) tail->perform(this); - } - - void Output_Compressed::operator()(Selector_List* g) - { - if (g->empty()) return; - (*g)[0]->perform(this); - for (size_t i = 1, L = g->length(); i < L; ++i) { - append_singleline_part_to_buffer(","); - (*g)[i]->perform(this); - } - } - - void Output_Compressed::append_singleline_part_to_buffer(const string& text) - { - buffer += text; - if (ctx) ctx->source_map.update_column(text); - } - -} diff --git a/output_compressed.hpp b/output_compressed.hpp deleted file mode 100644 index ab987ffe..00000000 --- a/output_compressed.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#include - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Context; - - class Output_Compressed : public Operation_CRTP { - // import all the class-specific methods and override as desired - using Operation_CRTP::operator(); - - string buffer; - string rendered_imports; - Context* ctx; - - void fallback_impl(AST_Node* n); - - void append_singleline_part_to_buffer(const string& text); - - public: - Output_Compressed(Context* ctx = 0); - virtual ~Output_Compressed(); - - string get_buffer() { return rendered_imports + buffer; } - - // statements - virtual void operator()(Block*); - virtual void operator()(Ruleset*); - // virtual void operator()(Propset*); - virtual void operator()(Media_Block*); - virtual void operator()(At_Rule*); - virtual void operator()(Declaration*); - // virtual void operator()(Assignment*); - virtual void operator()(Import*); - // virtual void operator()(Import_Stub*); - // virtual void operator()(Warning*); - virtual void operator()(Comment*); - // virtual void operator()(If*); - // virtual void operator()(For*); - // virtual void operator()(Each*); - // virtual void operator()(While*); - // virtual void operator()(Return*); - // virtual void operator()(Extension*); - // virtual void operator()(Definition*); - // virtual void operator()(Mixin_Call*); - // virtual void operator()(Content*); - // // expressions - virtual void operator()(List*); - // virtual void operator()(Binary_Expression*); - // virtual void operator()(Unary_Expression*); - // virtual void operator()(Function_Call*); - // virtual void operator()(Function_Call_Schema*); - // virtual void operator()(Variable*); - // virtual void operator()(Textual*); - // virtual void operator()(Number*); - virtual void operator()(Color*); - // virtual void operator()(Boolean*); - // virtual void operator()(String_Schema*); - // virtual void operator()(String_Constant* x); - // virtual void operator()(Media_Query*); - virtual void operator()(Media_Query_Expression*); - virtual void operator()(Null*); - // // parameters and arguments - // virtual void operator()(Parameter*); - // virtual void operator()(Parameters*); - virtual void operator()(Argument*); - virtual void operator()(Arguments*); - // // selectors - // virtual void operator()(Selector_Schema*); - // virtual void operator()(Selector_Reference*); - // virtual void operator()(Selector_Placeholder*); - // virtual void operator()(Type_Selector*); - // virtual void operator()(Selector_Qualifier*); - // virtual void operator()(Attribute_Selector*); - // virtual void operator()(Pseudo_Selector*); - // virtual void operator()(Wrapped_Selector*); - // virtual void operator()(Compound_Selector*); - virtual void operator()(Complex_Selector*); - virtual void operator()(Selector_List*); - - template - void fallback(U x) { fallback_impl(x); } - }; - -} diff --git a/output_nested.cpp b/output_nested.cpp deleted file mode 100644 index 6ead1bf2..00000000 --- a/output_nested.cpp +++ /dev/null @@ -1,268 +0,0 @@ -#include "output_nested.hpp" -#include "inspect.hpp" -#include "ast.hpp" -#include "context.hpp" -#include "to_string.hpp" -#include "util.hpp" -#include -#include -#include - -namespace Sass { - using namespace std; - - Output_Nested::Output_Nested(bool source_comments, Context* ctx) - : buffer(""), rendered_imports(""), indentation(0), source_comments(source_comments), ctx(ctx) - { } - Output_Nested::~Output_Nested() { } - - inline void Output_Nested::fallback_impl(AST_Node* n) - { - Inspect i(ctx); - n->perform(&i); - buffer += i.get_buffer(); - } - - void Output_Nested::operator()(Import* imp) - { - Inspect insp(ctx); - imp->perform(&insp); - if (!rendered_imports.empty()) { - rendered_imports += "\n"; - } - rendered_imports += insp.get_buffer(); - } - - void Output_Nested::operator()(Block* b) - { - if (!b->is_root()) return; - for (size_t i = 0, L = b->length(); i < L; ++i) { - size_t old_len = buffer.length(); - (*b)[i]->perform(this); - if (i < L-1 && old_len < buffer.length()) append_to_buffer("\n"); - } - } - - void Output_Nested::operator()(Ruleset* r) - { - Selector* s = r->selector(); - Block* b = r->block(); - bool decls = false; - - // disabled to avoid clang warning [-Wunused-function] - // Selector_List* sl = static_cast(s); - - // Filter out rulesets that aren't printable (process its children though) - if (!Util::isPrintable(r)) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { - stm->perform(this); - } - } - return; - } - - if (b->has_non_hoistable()) { - decls = true; - indent(); - if (source_comments) { - stringstream ss; - ss << "/* line " << r->position().line << ", " << r->path() << " */" << endl; - append_to_buffer(ss.str()); - indent(); - } - s->perform(this); - append_to_buffer(" {\n"); - ++indentation; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - bool bPrintExpression = true; - // Check print conditions - if (typeid(*stm) == typeid(Declaration)) { - Declaration* dec = static_cast(stm); - if (dec->value()->concrete_type() == Expression::STRING) { - String_Constant* valConst = static_cast(dec->value()); - string val(valConst->value()); - if (val.empty()) { - bPrintExpression = false; - } - } - else if (dec->value()->concrete_type() == Expression::LIST) { - List* list = static_cast(dec->value()); - bool all_invisible = true; - for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) { - Expression* item = (*list)[list_i]; - if (!item->is_invisible()) all_invisible = false; - } - if (all_invisible) bPrintExpression = false; - } - } - // Print if OK - if (!stm->is_hoistable() && bPrintExpression) { - if (!stm->block()) indent(); - stm->perform(this); - append_to_buffer("\n"); - } - } - --indentation; - buffer.erase(buffer.length()-1); - if (ctx) ctx->source_map.remove_line(); - append_to_buffer(" }\n"); - } - - if (b->has_hoistable()) { - if (decls) ++indentation; - // indent(); - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - if (decls) --indentation; - } - } - - void Output_Nested::operator()(Media_Block* m) - { - List* q = m->media_queries(); - Block* b = m->block(); - - // Filter out media blocks that aren't printable (process its children though) - if (!Util::isPrintable(m)) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { - stm->perform(this); - } - } - return; - } - - indent(); - ctx->source_map.add_mapping(m); - append_to_buffer("@media "); - q->perform(this); - append_to_buffer(" {\n"); - - Selector* e = m->selector(); - if (e && b->has_non_hoistable()) { - // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable - ++indentation; - indent(); - e->perform(this); - append_to_buffer(" {\n"); - - ++indentation; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - if (!stm->block()) indent(); - stm->perform(this); - append_to_buffer("\n"); - } - } - --indentation; - - buffer.erase(buffer.length()-1); - if (ctx) ctx->source_map.remove_line(); - append_to_buffer(" }\n"); - --indentation; - - ++indentation; - ++indentation; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - } - } - --indentation; - --indentation; - } - else { - // JMA - not hoisted, just output in order - ++indentation; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - if (!stm->block()) indent(); - } - stm->perform(this); - append_to_buffer("\n"); - } - --indentation; - } - - buffer.erase(buffer.length()-1); - if (ctx) ctx->source_map.remove_line(); - append_to_buffer(" }\n"); - } - - void Output_Nested::operator()(At_Rule* a) - { - string kwd = a->keyword(); - Selector* s = a->selector(); - Expression* v = a->value(); - Block* b = a->block(); - bool decls = false; - - // indent(); - append_to_buffer(kwd); - if (s) { - append_to_buffer(" "); - s->perform(this); - } - else if (v) { - append_to_buffer(" "); - v->perform(this); - } - - if (!b) { - append_to_buffer(";"); - return; - } - - append_to_buffer(" {\n"); - ++indentation; - decls = true; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable()) { - if (!stm->block()) indent(); - stm->perform(this); - append_to_buffer("\n"); - } - } - --indentation; - - if (decls) ++indentation; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (stm->is_hoistable()) { - stm->perform(this); - append_to_buffer("\n"); - } - } - if (decls) --indentation; - - buffer.erase(buffer.length()-1); - if (ctx) ctx->source_map.remove_line(); - if (b->has_hoistable()) { - buffer.erase(buffer.length()-1); - if (ctx) ctx->source_map.remove_line(); - } - append_to_buffer(" }\n"); - } - - void Output_Nested::indent() - { append_to_buffer(string(2*indentation, ' ')); } - - void Output_Nested::append_to_buffer(const string& text) - { - buffer += text; - if (ctx) ctx->source_map.update_column(text); - } - -} diff --git a/output_nested.hpp b/output_nested.hpp deleted file mode 100644 index 6e62461e..00000000 --- a/output_nested.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#include - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -// #ifndef SASS_TO_STRING -// #include "to_string.hpp" -// #endif - -namespace Sass { - using namespace std; - struct Context; - - class Output_Nested : public Operation_CRTP { - // import all the class-specific methods and override as desired - using Operation_CRTP::operator(); - - string buffer; - string rendered_imports; - size_t indentation; - bool source_comments; - Context* ctx; - void indent(); - - void fallback_impl(AST_Node* n); - - void append_to_buffer(const string& text); - - public: - - Output_Nested(bool source_comments = false, Context* ctx = 0); - virtual ~Output_Nested(); - - string get_buffer() { - if (!rendered_imports.empty() && !buffer.empty()) { - rendered_imports += "\n"; - } - return rendered_imports + buffer; - } - - // statements - virtual void operator()(Block*); - virtual void operator()(Ruleset*); - // virtual void operator()(Propset*); - virtual void operator()(Media_Block*); - virtual void operator()(At_Rule*); - // virtual void operator()(Declaration*); - // virtual void operator()(Assignment*); - virtual void operator()(Import*); - // virtual void operator()(Import_Stub*); - // virtual void operator()(Warning*); - // virtual void operator()(Comment*); - // virtual void operator()(If*); - // virtual void operator()(For*); - // virtual void operator()(Each*); - // virtual void operator()(While*); - // virtual void operator()(Return*); - // virtual void operator()(Extension*); - // virtual void operator()(Definition*); - // virtual void operator()(Mixin_Call*); - // virtual void operator()(Content*); - // // expressions - // virtual void operator()(List*); - // virtual void operator()(Binary_Expression*); - // virtual void operator()(Unary_Expression*); - // virtual void operator()(Function_Call*); - // virtual void operator()(Function_Call_Schema*); - // virtual void operator()(Variable*); - // virtual void operator()(Textual*); - // virtual void operator()(Number*); - // virtual void operator()(Color*); - // virtual void operator()(Boolean*); - // virtual void operator()(String_Schema*); - // virtual void operator()(String_Constant* x); - // virtual void operator()(Media_Query*); - // virtual void operator()(Media_Query_Expression*); - // // parameters and arguments - // virtual void operator()(Parameter*); - // virtual void operator()(Parameters*); - // virtual void operator()(Argument*); - // virtual void operator()(Arguments*); - // // selectors - // virtual void operator()(Selector_Schema*); - // virtual void operator()(Selector_Reference*); - // virtual void operator()(Selector_Placeholder*); - // virtual void operator()(Type_Selector*); - // virtual void operator()(Selector_Qualifier*); - // virtual void operator()(Attribute_Selector*); - // virtual void operator()(Pseudo_Selector*); - // virtual void operator()(Wrapped_Selector*); - // virtual void operator()(Compound_Selector*); - // virtual void operator()(Complex_Selector*); - // virtual void operator()(Selector_List*); - - template - void fallback(U x) { fallback_impl(x); } - }; - - string unquote(const string&); - string quote(const string&, char); - -} diff --git a/parser.cpp b/parser.cpp deleted file mode 100644 index 34411c3c..00000000 --- a/parser.cpp +++ /dev/null @@ -1,1799 +0,0 @@ -#include -#include -#include "parser.hpp" -#include "file.hpp" -#include "inspect.hpp" -#include "to_string.hpp" -#include "constants.hpp" -#include "util.hpp" - -#ifndef SASS_PRELEXER -#include "prelexer.hpp" -#endif - -#include - -namespace Sass { - using namespace std; - using namespace Constants; - - Parser Parser::from_c_str(const char* str, Context& ctx, string path, Position source_position) - { - Parser p(ctx, path, source_position); - p.source = str; - p.position = p.source; - p.end = str + strlen(str); - return p; - } - - Parser Parser::from_token(Token t, Context& ctx, string path, Position source_position) - { - Parser p(ctx, path, source_position); - p.source = t.begin; - p.position = p.source; - p.end = t.end; - return p; - } - - Block* Parser::parse() - { - Block* root = new (ctx.mem) Block(path, source_position); - root->is_root(true); - read_bom(); - lex< optional_spaces >(); - Selector_Lookahead lookahead_result; - while (position < end) { - if (lex< block_comment >()) { - String* contents = parse_interpolated_chunk(lexed); - Comment* comment = new (ctx.mem) Comment(path, source_position, contents); - (*root) << comment; - } - else if (peek< import >()) { - Import* imp = parse_import(); - if (!imp->urls().empty()) (*root) << imp; - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*root) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]); - } - } - if (!lex< exactly<';'> >()) error("top-level @import directive must be terminated by ';'"); - } - else if (peek< mixin >() || peek< function >()) { - (*root) << parse_definition(); - } - else if (peek< variable >()) { - (*root) << parse_assignment(); - if (!lex< exactly<';'> >()) error("top-level variable binding must be terminated by ';'"); - } - else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { - (*root) << parse_propset(); - } - else if (peek< include >() /* || peek< exactly<'+'> >() */) { - Mixin_Call* mixin_call = parse_mixin_call(); - (*root) << mixin_call; - if (!mixin_call->block() && !lex< exactly<';'> >()) error("top-level @include directive must be terminated by ';'"); - } - else if (peek< if_directive >()) { - (*root) << parse_if_directive(); - } - else if (peek< for_directive >()) { - (*root) << parse_for_directive(); - } - else if (peek< each_directive >()) { - (*root) << parse_each_directive(); - } - else if (peek< while_directive >()) { - (*root) << parse_while_directive(); - } - else if (peek< media >()) { - (*root) << parse_media_block(); - } - else if (peek< warn >()) { - (*root) << parse_warning(); - if (!lex< exactly<';'> >()) error("top-level @warn directive must be terminated by ';'"); - } - // ignore the @charset directive for now - else if (lex< exactly< charset_kwd > >()) { - lex< string_constant >(); - lex< exactly<';'> >(); - } - else if (peek< at_keyword >()) { - At_Rule* at_rule = parse_at_rule(); - (*root) << at_rule; - if (!at_rule->block() && !lex< exactly<';'> >()) error("top-level directive must be terminated by ';'"); - } - else if ((lookahead_result = lookahead_for_selector(position)).found) { - (*root) << parse_ruleset(lookahead_result); - } - else { - lex< spaces_and_comments >(); - if (position >= end) break; - error("invalid top-level expression"); - } - lex< optional_spaces >(); - } - return root; - } - - Import* Parser::parse_import() - { - lex< import >(); - Import* imp = new (ctx.mem) Import(path, source_position); - bool first = true; - do { - if (lex< string_constant >()) { - string import_path(lexed); - string extension; - if (import_path.length() > 6) { // 2 quote marks + the 4 chars in .css - // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end - extension = import_path.substr(import_path.length() - 5, 4); - } - if (extension == ".css") { - String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path, true); - Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc); - Arguments* loc_args = new (ctx.mem) Arguments(path, source_position); - (*loc_args) << loc_arg; - Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args); - imp->urls().push_back(new_url); - } - else { - string current_dir = File::dir_name(path); - string resolved(ctx.add_file(current_dir, unquote(import_path))); - if (resolved.empty()) error("file to import not found or unreadable: " + import_path + "\nCurrent dir: " + current_dir); - imp->files().push_back(resolved); - } - } - else if (peek< uri_prefix >()) { - imp->urls().push_back(parse_value()); - } - else { - if (first) error("@import directive requires a url or quoted path"); - else error("expecting another url or quoted path in @import list"); - } - first = false; - } while (lex< exactly<','> >()); - return imp; - } - - Definition* Parser::parse_definition() - { - Definition::Type which_type = Definition::MIXIN; - if (lex< mixin >()) which_type = Definition::MIXIN; - else if (lex< function >()) which_type = Definition::FUNCTION; - string which_str(lexed); - if (!lex< identifier >()) error("invalid name in " + which_str + " definition"); - string name(Util::normalize_underscores(lexed)); - Position source_position_of_def = source_position; - Parameters* params = parse_parameters(); - if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'"); - if (which_type == Definition::MIXIN) stack.push_back(mixin_def); - else stack.push_back(function_def); - Block* body = parse_block(); - stack.pop_back(); - Definition* def = new (ctx.mem) Definition(path, source_position_of_def, name, params, body, which_type); - return def; - } - - Parameters* Parser::parse_parameters() - { - string name(lexed); // for the error message - Parameters* params = new (ctx.mem) Parameters(path, source_position); - if (lex< exactly<'('> >()) { - // if there's anything there at all - if (!peek< exactly<')'> >()) { - do (*params) << parse_parameter(); - while (lex< exactly<','> >()); - } - if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name); - } - return params; - } - - Parameter* Parser::parse_parameter() - { - lex< variable >(); - string name(Util::normalize_underscores(lexed)); - Position pos = source_position; - Expression* val = 0; - bool is_rest = false; - if (lex< exactly<':'> >()) { // there's a default value - val = parse_space_list(); - val->is_delayed(false); - } - else if (lex< exactly< ellipsis > >()) { - is_rest = true; - } - Parameter* p = new (ctx.mem) Parameter(path, pos, name, val, is_rest); - return p; - } - - Mixin_Call* Parser::parse_mixin_call() - { - lex< include >() /* || lex< exactly<'+'> >() */; - if (!lex< identifier >()) error("invalid name in @include directive"); - Position source_position_of_call = source_position; - string name(Util::normalize_underscores(lexed)); - Arguments* args = parse_arguments(); - Block* content = 0; - if (peek< exactly<'{'> >()) { - content = parse_block(); - } - Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, source_position_of_call, name, args, content); - return the_call; - } - - Arguments* Parser::parse_arguments() - { - string name(lexed); - Arguments* args = new (ctx.mem) Arguments(path, source_position); - - if (lex< exactly<'('> >()) { - // if there's anything there at all - if (!peek< exactly<')'> >()) { - do (*args) << parse_argument(); - while (lex< exactly<','> >()); - } - if (!lex< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name); - } - - return args; - } - - Argument* Parser::parse_argument() - { - Argument* arg; - if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) { - lex< variable >(); - string name(Util::normalize_underscores(lexed)); - Position p = source_position; - lex< exactly<':'> >(); - Expression* val = parse_space_list(); - val->is_delayed(false); - arg = new (ctx.mem) Argument(path, p, val, name); - } - else { - bool is_arglist = false; - Expression* val = parse_space_list(); - val->is_delayed(false); - if (lex< exactly< ellipsis > >()) { - is_arglist = true; - } - arg = new (ctx.mem) Argument(path, source_position, val, "", is_arglist); - } - return arg; - } - - Assignment* Parser::parse_assignment() - { - lex< variable >(); - string name(Util::normalize_underscores(lexed)); - Position var_source_position = source_position; - if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement"); - Expression* val = parse_list(); - val->is_delayed(false); - bool is_guarded = lex< default_flag >(); - bool is_global = lex< global_flag >(); - Assignment* var = new (ctx.mem) Assignment(path, var_source_position, name, val, is_guarded, is_global); - return var; - } - - Propset* Parser::parse_propset() - { - String* property_segment; - if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) { - property_segment = parse_identifier_schema(); - } - else { - lex< sequence< optional< exactly<'*'> >, identifier > >(); - property_segment = new (ctx.mem) String_Constant(path, source_position, lexed); - } - Propset* propset = new (ctx.mem) Propset(path, source_position, property_segment); - lex< exactly<':'> >(); - - if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property"); - - propset->block(parse_block()); - - return propset; - } - - Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead) - { - Selector* sel; - if (lookahead.has_interpolants) { - sel = parse_selector_schema(lookahead.found); - } - else { - sel = parse_selector_group(); - } - Position r_source_position = source_position; - if (!peek< exactly<'{'> >()) error("expected a '{' after the selector"); - Block* block = parse_block(); - Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_source_position, sel, block); - return ruleset; - } - - Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector) - { - lex< optional_spaces >(); - const char* i = position; - const char* p; - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - - while (i < end_of_selector) { - p = find_first_in_interval< exactly >(i, end_of_selector); - if (p) { - // accumulate the preceding segment if there is one - if (i < p) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); - // find the end of the interpolant and parse it - const char* j = find_first_in_interval< exactly >(p, end_of_selector); - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - i = j + 1; - } - else { // no interpolants left; add the last segment if there is one - if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, end_of_selector)); - break; - } - } - position = end_of_selector; - return new (ctx.mem) Selector_Schema(path, source_position, schema); - } - - Selector_List* Parser::parse_selector_group() - { - To_String to_string; - Selector_List* group = new (ctx.mem) Selector_List(path, source_position); - do { - if (peek< exactly<'{'> >() || - peek< exactly<'}'> >() || - peek< exactly<')'> >() || - peek< exactly<';'> >()) - break; // in case there are superfluous commas at the end - Complex_Selector* comb = parse_selector_combination(); - if (!comb->has_reference()) { - Position sel_source_position = source_position; - Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_source_position); - Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_source_position); - (*ref_wrap) << ref; - if (!comb->head()) { - comb->head(ref_wrap); - comb->has_reference(true); - } - else { - comb = new (ctx.mem) Complex_Selector(path, sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb); - comb->has_reference(true); - } - } - (*group) << comb; - } - while (lex< one_plus< sequence< spaces_and_comments, exactly<','> > > >()); - while (lex< optional >()); // JMA - ignore optional flag if it follows the selector group - return group; - } - - Complex_Selector* Parser::parse_selector_combination() - { - Position sel_source_position = Position(); - Compound_Selector* lhs; - if (peek< exactly<'+'> >() || - peek< exactly<'~'> >() || - peek< exactly<'>'> >()) { - // no selector before the combinator - lhs = 0; - } - else { - lhs = parse_simple_selector_sequence(); - sel_source_position = source_position; - } - - Complex_Selector::Combinator cmb; - if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO; - else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES; - else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF; - else cmb = Complex_Selector::ANCESTOR_OF; - - Complex_Selector* rhs; - if (peek< exactly<','> >() || - peek< exactly<')'> >() || - peek< exactly<'{'> >() || - peek< exactly<'}'> >() || - peek< exactly<';'> >() || - peek< optional >()) { - // no selector after the combinator - rhs = 0; - } - else { - rhs = parse_selector_combination(); - sel_source_position = source_position; - } - if (!sel_source_position.line) sel_source_position = source_position; - return new (ctx.mem) Complex_Selector(path, sel_source_position, cmb, lhs, rhs); - } - - Compound_Selector* Parser::parse_simple_selector_sequence() - { - Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, source_position); - bool sawsomething = false; - if (lex< exactly<'&'> >()) { - // if you see a & - (*seq) << new (ctx.mem) Selector_Reference(path, source_position); - sawsomething = true; - // if you see a space after a &, then you're done - if(lex< spaces >()) { - return seq; - } - } - if (sawsomething && lex< sequence< negate< functional >, alternatives< hyphens_and_identifier, universal, string_constant, dimension, percentage, number > > >()) { - // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning - (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed); - } else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, string_constant, dimension, percentage, number > > >()) { - // if you see a type selector - (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed); - sawsomething = true; - } - if (!sawsomething) { - // don't blindly do this if you saw a & or selector - (*seq) << parse_simple_selector(); - } - - while (!peek< spaces >(position) && - !(peek < exactly<'+'> >(position) || - peek < exactly<'~'> >(position) || - peek < exactly<'>'> >(position) || - peek < exactly<','> >(position) || - peek < exactly<')'> >(position) || - peek < exactly<'{'> >(position) || - peek < exactly<'}'> >(position) || - peek < exactly<';'> >(position))) { - (*seq) << parse_simple_selector(); - } - return seq; - } - - Simple_Selector* Parser::parse_simple_selector() - { - if (lex< id_name >() || lex< class_name >()) { - return new (ctx.mem) Selector_Qualifier(path, source_position, lexed); - } - else if (lex< string_constant >() || lex< number >()) { - return new (ctx.mem) Type_Selector(path, source_position, lexed); - } - else if (peek< pseudo_not >()) { - return parse_negated_selector(); - } - else if (peek< exactly<':'> >(position) || peek< functional >()) { - return parse_pseudo_selector(); - } - else if (peek< exactly<'['> >(position)) { - return parse_attribute_selector(); - } - else if (lex< placeholder >()) { - return new (ctx.mem) Selector_Placeholder(path, source_position, lexed); - } - else { - error("invalid selector after " + lexed.to_string()); - } - // unreachable statement - return 0; - } - - Wrapped_Selector* Parser::parse_negated_selector() - { - lex< pseudo_not >(); - string name(lexed); - Position nsource_position = source_position; - Selector* negated = parse_selector_group(); - if (!lex< exactly<')'> >()) { - error("negated selector is missing ')'"); - } - return new (ctx.mem) Wrapped_Selector(path, nsource_position, name, negated); - } - - Simple_Selector* Parser::parse_pseudo_selector() { - if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) { - string name(lexed); - String* expr = 0; - Position p = source_position; - Selector* wrapped = 0; - if (lex< alternatives< even, odd > >()) { - expr = new (ctx.mem) String_Constant(path, p, lexed); - } - else if (peek< binomial >(position)) { - lex< sequence< optional< coefficient >, exactly<'n'> > >(); - String_Constant* var_coef = new (ctx.mem) String_Constant(path, p, lexed); - lex< sign >(); - String_Constant* op = new (ctx.mem) String_Constant(path, p, lexed); - // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB); - lex< digits >(); - String_Constant* constant = new (ctx.mem) String_Constant(path, p, lexed); - // expr = new (ctx.mem) Binary_Expression(path, p, op, var_coef, constant); - String_Schema* schema = new (ctx.mem) String_Schema(path, p, 3); - *schema << var_coef << op << constant; - expr = schema; - } - else if (peek< sequence< optional, - optional, - exactly<'n'>, - spaces_and_comments, - exactly<')'> > >()) { - lex< sequence< optional, - optional, - exactly<'n'> > >(); - expr = new (ctx.mem) String_Constant(path, p, lexed); - } - else if (lex< sequence< optional, digits > >()) { - expr = new (ctx.mem) String_Constant(path, p, lexed); - } - else if (peek< sequence< identifier, spaces_and_comments, exactly<')'> > >()) { - lex< identifier >(); - expr = new (ctx.mem) String_Constant(path, p, lexed); - } - else if (lex< string_constant >()) { - expr = new (ctx.mem) String_Constant(path, p, lexed); - } - else if (peek< exactly<')'> >()) { - expr = new (ctx.mem) String_Constant(path, p, ""); - } - else { - wrapped = parse_selector_group(); - } - if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)"); - if (wrapped) { - return new (ctx.mem) Wrapped_Selector(path, p, name, wrapped); - } - return new (ctx.mem) Pseudo_Selector(path, p, name, expr); - } - else if (lex < sequence< pseudo_prefix, identifier > >()) { - return new (ctx.mem) Pseudo_Selector(path, source_position, lexed); - } - else { - error("unrecognized pseudo-class or pseudo-element"); - } - // unreachable statement - return 0; - } - - Attribute_Selector* Parser::parse_attribute_selector() - { - lex< exactly<'['> >(); - Position p = source_position; - if (!lex< attribute_name >()) error("invalid attribute name in attribute selector"); - string name(lexed); - if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, p, name, "", 0); - if (!lex< alternatives< exact_match, class_match, dash_match, - prefix_match, suffix_match, substring_match > >()) { - error("invalid operator in attribute selector for " + name); - } - string matcher(lexed); - - String* value = 0; - if (lex< identifier >()) { - value = new (ctx.mem) String_Constant(path, p, lexed, true); - } - else if (lex< string_constant >()) { - value = parse_interpolated_chunk(lexed); - } - else { - error("expected a string constant or identifier in attribute selector for " + name); - } - - if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name); - return new (ctx.mem) Attribute_Selector(path, p, name, matcher, value); - } - - Block* Parser::parse_block() - { - lex< exactly<'{'> >(); - bool semicolon = false; - Selector_Lookahead lookahead_result; - Block* block = new (ctx.mem) Block(path, source_position); - - // JMA - ensure that a block containing only block_comments is parsed - while (lex< block_comment >()) { - String* contents = parse_interpolated_chunk(lexed); - Comment* comment = new (ctx.mem) Comment(path, source_position, contents); - (*block) << comment; - } - - while (!lex< exactly<'}'> >()) { - if (semicolon) { - if (!lex< exactly<';'> >()) { - error("non-terminal statement or declaration must end with ';'"); - } - semicolon = false; - while (lex< block_comment >()) { - String* contents = parse_interpolated_chunk(lexed); - Comment* comment = new (ctx.mem) Comment(path, source_position, contents); - (*block) << comment; - } - if (lex< exactly<'}'> >()) break; - } - if (lex< block_comment >()) { - String* contents = parse_interpolated_chunk(lexed); - Comment* comment = new (ctx.mem) Comment(path, source_position, contents); - (*block) << comment; - } - else if (peek< import >(position)) { - if (stack.back() == mixin_def || stack.back() == function_def) { - lex< import >(); // to adjust the source_position number - error("@import directives are not allowed inside mixins and functions"); - } - Import* imp = parse_import(); - if (!imp->urls().empty()) (*block) << imp; - if (!imp->files().empty()) { - for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*block) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]); - } - } - semicolon = true; - } - else if (lex< variable >()) { - (*block) << parse_assignment(); - semicolon = true; - } - else if (peek< if_directive >()) { - (*block) << parse_if_directive(); - } - else if (peek< for_directive >()) { - (*block) << parse_for_directive(); - } - else if (peek< each_directive >()) { - (*block) << parse_each_directive(); - } - else if (peek < while_directive >()) { - (*block) << parse_while_directive(); - } - else if (lex < return_directive >()) { - (*block) << new (ctx.mem) Return(path, source_position, parse_list()); - semicolon = true; - } - else if (peek< warn >()) { - (*block) << parse_warning(); - semicolon = true; - } - else if (stack.back() == function_def) { - error("only variable declarations and control directives are allowed inside functions"); - } - else if (peek< mixin >() || peek< function >()) { - (*block) << parse_definition(); - } - else if (peek< include >(position)) { - Mixin_Call* the_call = parse_mixin_call(); - (*block) << the_call; - // don't need a semicolon after a content block - semicolon = (the_call->block()) ? false : true; - } - else if (lex< content >()) { - if (stack.back() != mixin_def) { - error("@content may only be used within a mixin"); - } - (*block) << new (ctx.mem) Content(path, source_position); - semicolon = true; - } - /* - else if (peek< exactly<'+'> >()) { - (*block) << parse_mixin_call(); - semicolon = true; - } - */ - else if (lex< extend >()) { - Selector_Lookahead lookahead = lookahead_for_extension_target(position); - if (!lookahead.found) error("invalid selector for @extend"); - Selector* target; - if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); - else target = parse_selector_group(); - (*block) << new (ctx.mem) Extension(path, source_position, target); - semicolon = true; - } - else if (peek< media >()) { - (*block) << parse_media_block(); - } - // ignore the @charset directive for now - else if (lex< exactly< charset_kwd > >()) { - lex< string_constant >(); - lex< exactly<';'> >(); - } - else if (peek< at_keyword >()) { - At_Rule* at_rule = parse_at_rule(); - (*block) << at_rule; - if (!at_rule->block()) semicolon = true; - } - else if ((lookahead_result = lookahead_for_selector(position)).found) { - (*block) << parse_ruleset(lookahead_result); - } - else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) { - (*block) << parse_propset(); - } - else if (!peek< exactly<';'> >()) { - if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) { - (*block) << parse_propset(); - } - else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) { - (*block) << parse_propset(); - } - else { - Declaration* decl = parse_declaration(); - (*block) << decl; - if (peek< exactly<'{'> >()) { - // parse a propset that rides on the declaration's property - Propset* ps = new (ctx.mem) Propset(path, source_position, decl->property(), parse_block()); - (*block) << ps; - } - else { - // finish and let the semicolon get munched - semicolon = true; - } - } - } - else lex< exactly<';'> >(); - while (lex< block_comment >()) { - String* contents = parse_interpolated_chunk(lexed); - Comment* comment = new (ctx.mem) Comment(path, source_position, contents); - (*block) << comment; - } - } - return block; - } - - Declaration* Parser::parse_declaration() { - String* prop = 0; - if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) { - prop = parse_identifier_schema(); - } - else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) { - prop = new (ctx.mem) String_Constant(path, source_position, lexed); - } - else if (lex< custom_property_name >()) { - prop = new (ctx.mem) String_Constant(path, source_position, lexed); - } - else { - error("invalid property name"); - } - if (!lex< exactly<':'> >()) error("property \"" + string(lexed) + "\" must be followed by a ':'"); - if (peek< exactly<';'> >()) error("style declaration must contain a value"); - Expression* list = parse_list(); - return new (ctx.mem) Declaration(path, prop->position(), prop, list/*, lex()*/); - } - - Expression* Parser::parse_map() - { - if (!(peek< map_key >(position))) - { return parse_list(); } - - lex< exactly<'('> >(); - - // empty maps so treat it like an empty list - if (peek< exactly<')'> >(position)) - { - lex< exactly<')'> >(); - return new (ctx.mem) List(path, source_position, 0); - } - - Expression* key = parse_list(); - - // it's not a map so return the lexed value as a list value - if (!lex< exactly<':'> >()) - { - if (!lex< exactly<')'> >()) error("unclosed parenthesis"); - return key; - } - - Expression* value; - if (peek< map_key >(position)) - { - value = parse_map(); - } else { - value = parse_space_list(); - } - - KeyValuePair* pair = new (ctx.mem) KeyValuePair(path, source_position, key, value); - - Map* map = new (ctx.mem) Map(path, source_position, 1); - (*map) << pair; - - while (lex< exactly<','> >()) - { - // allow trailing commas - #495 - if (peek< exactly<')'> >(position)) - { break; } - - Expression* key = parse_list(); - - if (!(lex< exactly<':'> >())) - { error("invalid syntax"); } - - Expression* value; - if (peek< map_key >(position)) - { - value = parse_map(); - } else { - value = parse_space_list(); - } - - (*map) << new (ctx.mem) KeyValuePair(path, source_position, key, value); - } - - if (!lex< exactly<')'> >()) error("unclosed parenthesis 3"); - - return map; - } - - Expression* Parser::parse_list() - { - return parse_comma_list(); - } - - Expression* Parser::parse_comma_list() - { - if (//peek< exactly<'!'> >(position) || - peek< exactly<';'> >(position) || - peek< exactly<'}'> >(position) || - peek< exactly<'{'> >(position) || - peek< exactly<')'> >(position) || - //peek< exactly<':'> >(position) || - peek< exactly >(position)) - { return new (ctx.mem) List(path, source_position, 0); } - Expression* list1 = parse_space_list(); - // if it's a singleton, return it directly; don't wrap it - if (!peek< exactly<','> >(position)) return list1; - - List* comma_list = new (ctx.mem) List(path, source_position, 2, List::COMMA); - (*comma_list) << list1; - - while (lex< exactly<','> >()) - { - if (//peek< exactly<'!'> >(position) || - peek< exactly<';'> >(position) || - peek< exactly<'}'> >(position) || - peek< exactly<'{'> >(position) || - peek< exactly<')'> >(position) || - //peek< exactly<':'> >(position) || - peek< exactly >(position)) { - break; - } - Expression* list = parse_space_list(); - (*comma_list) << list; - } - - return comma_list; - } - - Expression* Parser::parse_space_list() - { - Expression* disj1; - if (peek< map_key >(position)) disj1 = parse_map(); - else disj1 = parse_disjunction(); - // if it's a singleton, return it directly; don't wrap it - if (//peek< exactly<'!'> >(position) || - peek< exactly<';'> >(position) || - peek< exactly<'}'> >(position) || - peek< exactly<'{'> >(position) || - peek< exactly<')'> >(position) || - peek< exactly<','> >(position) || - peek< exactly<':'> >(position) || - peek< exactly >(position) || - peek< default_flag >(position) || - peek< global_flag >(position)) - { return disj1; } - - List* space_list = new (ctx.mem) List(path, source_position, 2, List::SPACE); - (*space_list) << disj1; - - while (!(//peek< exactly<'!'> >(position) || - peek< exactly<';'> >(position) || - peek< exactly<'}'> >(position) || - peek< exactly<'{'> >(position) || - peek< exactly<')'> >(position) || - peek< exactly<','> >(position) || - peek< exactly<':'> >(position) || - peek< exactly >(position) || - peek< default_flag >(position) || - peek< global_flag >(position))) - { - (*space_list) << parse_disjunction(); - } - - return space_list; - } - - Expression* Parser::parse_disjunction() - { - Expression* conj1 = parse_conjunction(); - // if it's a singleton, return it directly; don't wrap it - if (!peek< sequence< or_op, negate< identifier > > >()) return conj1; - - vector operands; - while (lex< sequence< or_op, negate< identifier > > >()) - operands.push_back(parse_conjunction()); - - return fold_operands(conj1, operands, Binary_Expression::OR); - } - - Expression* Parser::parse_conjunction() - { - Expression* rel1 = parse_relation(); - // if it's a singleton, return it directly; don't wrap it - if (!peek< sequence< and_op, negate< identifier > > >()) return rel1; - - vector operands; - while (lex< sequence< and_op, negate< identifier > > >()) - operands.push_back(parse_relation()); - - return fold_operands(rel1, operands, Binary_Expression::AND); - } - - Expression* Parser::parse_relation() - { - Expression* expr1 = parse_expression(); - // if it's a singleton, return it directly; don't wrap it - if (!(peek< eq_op >(position) || - peek< neq_op >(position) || - peek< gte_op >(position) || - peek< gt_op >(position) || - peek< lte_op >(position) || - peek< lt_op >(position))) - { return expr1; } - - Binary_Expression::Type op - = lex() ? Binary_Expression::EQ - : lex() ? Binary_Expression::NEQ - : lex() ? Binary_Expression::GTE - : lex() ? Binary_Expression::LTE - : lex() ? Binary_Expression::GT - : lex() ? Binary_Expression::LT - : Binary_Expression::LT; // whatever - - Expression* expr2 = parse_expression(); - - return new (ctx.mem) Binary_Expression(path, expr1->position(), op, expr1, expr2); - } - - Expression* Parser::parse_expression() - { - Expression* term1 = parse_term(); - // if it's a singleton, return it directly; don't wrap it - if (!(peek< exactly<'+'> >(position) || - peek< sequence< negate< number >, exactly<'-'> > >(position)) || - peek< identifier >(position)) - { return term1; } - - vector operands; - vector operators; - while (lex< exactly<'+'> >() || lex< sequence< negate< number >, exactly<'-'> > >()) { - operators.push_back(lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB); - operands.push_back(parse_term()); - } - - return fold_operands(term1, operands, operators); - } - - Expression* Parser::parse_term() - { - Expression* fact1 = parse_factor(); - // if it's a singleton, return it directly; don't wrap it - if (!(peek< exactly<'*'> >(position) || - peek< exactly<'/'> >(position) || - peek< exactly<'%'> >(position))) - { return fact1; } - - vector operands; - vector operators; - while (lex< exactly<'*'> >() || lex< exactly<'/'> >() || lex< exactly<'%'> >()) { - if (lexed == "*") operators.push_back(Binary_Expression::MUL); - else if (lexed == "/") operators.push_back(Binary_Expression::DIV); - else operators.push_back(Binary_Expression::MOD); - operands.push_back(parse_factor()); - } - - return fold_operands(fact1, operands, operators); - } - - Expression* Parser::parse_factor() - { - if (lex< exactly<'('> >()) { - Expression* value = parse_list(); - if (!lex< exactly<')'> >()) error("unclosed parenthesis"); - value->is_delayed(false); - // make sure wrapped lists and division expressions are non-delayed within parentheses - if (value->concrete_type() == Expression::LIST) { - List* l = static_cast(value); - if (!l->empty()) (*l)[0]->is_delayed(false); - } else if (typeid(*value) == typeid(Binary_Expression)) { - Binary_Expression* b = static_cast(value); - Binary_Expression* lhs = static_cast(b->left()); - if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false); - } - return value; - } - else if (peek< ie_stuff >()) { - return parse_ie_stuff(); - } - else if (peek< ie_keyword_arg >()) { - String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, source_position, 3); - if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); - else { - lex< alternatives< identifier_schema, identifier > >(); - *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); - } - lex< exactly<'='> >(); - *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); - if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); - else { - lex< alternatives< identifier_schema, identifier, number, hex > >(); - *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); - } - return kwd_arg; - } - else if (peek< exactly< calc_kwd > >() || - peek< exactly< moz_calc_kwd > >() || - peek< exactly< webkit_calc_kwd > >()) { - return parse_calc_function(); - } - else if (peek< functional_schema >()) { - return parse_function_call_schema(); - } - else if (peek< identifier_schema >()) { - return parse_identifier_schema(); - } - else if (peek< functional >() && !peek< uri_prefix >()) { - return parse_function_call(); - } - else if (lex< sequence< exactly<'+'>, spaces_and_comments, negate< number > > >()) { - return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::PLUS, parse_factor()); - } - else if (lex< sequence< exactly<'-'>, spaces_and_comments, negate< number> > >()) { - return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::MINUS, parse_factor()); - } - else { - return parse_value(); - } - } - - Expression* Parser::parse_value() - { - if (lex< uri_prefix >()) { - Arguments* args = new (ctx.mem) Arguments(path, source_position); - Function_Call* result = new (ctx.mem) Function_Call(path, source_position, "url", args); - const char* here = position; - Position here_p = source_position; - // Try to parse a SassScript expression. If it succeeds and we can munch - // a matching rparen, then that's our url. If we can't munch a matching - // rparen, or if the attempt to parse an expression fails, then try to - // munch a regular CSS url. - try { - // special case -- if there's a comment, treat it as part of a URL - lex(); - if (peek() || peek()) error("comment in URL"); // doesn't really matter what we throw - Expression* expr = parse_list(); - if (!lex< exactly<')'> >()) error("dangling expression in URL"); // doesn't really matter what we throw - Argument* arg = new (ctx.mem) Argument(path, expr->position(), expr); - *args << arg; - return result; - } - catch (Error& err) { - // back up so we can try again - position = here; - source_position = here_p; - } - lex< spaces >(); - if (lex< url >()) { - String* the_url = parse_interpolated_chunk(lexed); - Argument* arg = new (ctx.mem) Argument(path, the_url->position(), the_url); - *args << arg; - } - else { - error("malformed URL"); - } - if (!lex< exactly<')'> >()) error("URI is missing ')'"); - return result; - } - - if (lex< important >()) - { return new (ctx.mem) String_Constant(path, source_position, "!important"); } - - if (lex< value_schema >()) - { return Parser::from_token(lexed, ctx, path, source_position).parse_value_schema(); } - - if (lex< sequence< true_val, negate< identifier > > >()) - { return new (ctx.mem) Boolean(path, source_position, true); } - - if (lex< sequence< false_val, negate< identifier > > >()) - { return new (ctx.mem) Boolean(path, source_position, false); } - - if (lex< sequence< null, negate< identifier > > >()) - { return new (ctx.mem) Null(path, source_position); } - - if (lex< identifier >()) { - String_Constant* str = new (ctx.mem) String_Constant(path, source_position, lexed); - str->is_delayed(true); - return str; - } - - if (lex< percentage >()) - { return new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); } - - if (lex< dimension >()) - { return new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); } - - if (lex< number >()) - { return new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); } - - if (lex< hex >()) - { return new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); } - - if (peek< string_constant >()) - { return parse_string(); } - - if (lex< variable >()) - { return new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); } - - error("error reading values after " + lexed.to_string()); - - // unreachable statement - return 0; - } - - String* Parser::parse_interpolated_chunk(Token chunk) - { - const char* i = chunk.begin; - // see if there any interpolants - const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(chunk.begin, chunk.end); - if (!p) { - String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, chunk); - str_node->is_delayed(true); - return str_node; - } - - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - schema->quote_mark(*chunk.begin); - while (i < chunk.end) { - p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, chunk.end); - if (p) { - if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty - } - const char* j = find_first_in_interval< exactly >(p, chunk.end); // find the closing brace - if (j) { - // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - i = j+1; - } - else { - // throw an error if the interpolant is unterminated - error("unterminated interpolant inside string constant " + chunk.to_string()); - } - } - else { // no interpolants left; add the last segment if nonempty - if (i < chunk.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, chunk.end)); - break; - } - } - return schema; - } - - String* Parser::parse_string() - { - lex< string_constant >(); - Token str(lexed); - return parse_interpolated_chunk(str); - // const char* i = str.begin; - // // see if there any interpolants - // const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(str.begin, str.end); - // if (!p) { - // String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str); - // str_node->is_delayed(true); - // return str_node; - // } - - // String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - // schema->quote_mark(*str.begin); - // while (i < str.end) { - // p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, str.end); - // if (p) { - // if (i < p) { - // (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty - // } - // const char* j = find_first_in_interval< exactly >(p, str.end); // find the closing brace - // if (j) { - // // parse the interpolant and accumulate it - // Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); - // interp_node->is_interpolant(true); - // (*schema) << interp_node; - // i = j+1; - // } - // else { - // // throw an error if the interpolant is unterminated - // error("unterminated interpolant inside string constant " + str.to_string()); - // } - // } - // else { // no interpolants left; add the last segment if nonempty - // if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end)); - // break; - // } - // } - // return schema; - } - - String* Parser::parse_ie_stuff() - { - lex< ie_stuff >(); - Token str(lexed); - --str.end; - --position; - const char* i = str.begin; - // see if there any interpolants - const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(str.begin, str.end); - if (!p) { - String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str); - str_node->is_delayed(true); - return str_node; - } - - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - while (i < str.end) { - p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, str.end); - if (p) { - if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty - } - const char* j = find_first_in_interval< exactly >(p, str.end); // find the closing brace - if (j) { - // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - i = j+1; - } - else { - // throw an error if the interpolant is unterminated - error("unterminated interpolant inside IE function " + str.to_string()); - } - } - else { // no interpolants left; add the last segment if nonempty - if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end)); - break; - } - } - return schema; - } - - String_Schema* Parser::parse_value_schema() - { - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - size_t num_items = 0; - while (position < end) { - if (lex< interpolant >()) { - Token insides(Token(lexed.begin + 2, lexed.end - 1)); - Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - } - else if (lex< identifier >()) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); - } - else if (lex< percentage >()) { - (*schema) << new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); - } - else if (lex< dimension >()) { - (*schema) << new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); - } - else if (lex< number >()) { - (*schema) << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); - } - else if (lex< hex >()) { - (*schema) << new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); - } - else if (lex< string_constant >()) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); - if (!num_items) schema->quote_mark(*lexed.begin); - } - else if (lex< variable >()) { - (*schema) << new (ctx.mem) Variable(path, source_position, Util::normalize_underscores(lexed)); - } - else { - error("error parsing interpolated value"); - } - ++num_items; - } - return schema; - } - - String_Schema* Parser::parse_url_schema() - { - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - - while (position < end) { - if (position[0] == '/') { - lexed = Token(position, position+1); - (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); - ++position; - } - else if (lex< interpolant >()) { - Token insides(Token(lexed.begin + 2, lexed.end - 1)); - Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - } - else if (lex< sequence< identifier, exactly<':'> > >()) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); - } - else if (lex< filename >()) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); - } - else { - error("error parsing interpolated url"); - } - } - return schema; - } - - String* Parser::parse_identifier_schema() - { - lex< sequence< optional< exactly<'*'> >, identifier_schema > >(); - Token id(lexed); - const char* i = id.begin; - // see if there any interpolants - const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(id.begin, id.end); - if (!p) { - return new (ctx.mem) String_Constant(path, source_position, id); - } - - String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); - while (i < id.end) { - p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, id.end); - if (p) { - if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty - } - const char* j = find_first_in_interval< exactly >(p, id.end); // find the closing brace - if (j) { - // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); - interp_node->is_interpolant(true); - (*schema) << interp_node; - i = j+1; - } - else { - // throw an error if the interpolant is unterminated - error("unterminated interpolant inside interpolated identifier " + id.to_string()); - } - } - else { // no interpolants left; add the last segment if nonempty - if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, id.end)); - break; - } - } - return schema; - } - - Function_Call* Parser::parse_calc_function() - { - lex< identifier >(); - string name(lexed); - Position call_pos = source_position; - lex< exactly<'('> >(); - Position arg_pos = source_position; - const char* arg_beg = position; - parse_list(); - const char* arg_end = position; - lex< exactly<')'> >(); - - Argument* arg = new (ctx.mem) Argument(path, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end))); - Arguments* args = new (ctx.mem) Arguments(path, arg_pos); - *args << arg; - return new (ctx.mem) Function_Call(path, call_pos, name, args); - } - - Function_Call* Parser::parse_function_call() - { - lex< identifier >(); - string name(Util::normalize_underscores(lexed)); - Position source_position_of_call = source_position; - - Function_Call* the_call = new (ctx.mem) Function_Call(path, source_position_of_call, name, parse_arguments()); - return the_call; - } - - Function_Call_Schema* Parser::parse_function_call_schema() - { - String* name = parse_identifier_schema(); - Position source_position_of_call = source_position; - - Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, source_position_of_call, name, parse_arguments()); - return the_call; - } - - If* Parser::parse_if_directive(bool else_if) - { - lex< if_directive >() || (else_if && lex< exactly >()); - Position if_source_position = source_position; - Expression* predicate = parse_list(); - predicate->is_delayed(false); - if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if"); - Block* consequent = parse_block(); - Block* alternative = 0; - if (lex< else_directive >()) { - if (peek< exactly >()) { - alternative = new (ctx.mem) Block(path, source_position); - (*alternative) << parse_if_directive(true); - } - else if (!peek< exactly<'{'> >()) { - error("expected '{' after @else"); - } - else { - alternative = parse_block(); - } - } - return new (ctx.mem) If(path, if_source_position, predicate, consequent, alternative); - } - - For* Parser::parse_for_directive() - { - lex< for_directive >(); - Position for_source_position = source_position; - if (!lex< variable >()) error("@for directive requires an iteration variable"); - string var(Util::normalize_underscores(lexed)); - if (!lex< from >()) error("expected 'from' keyword in @for directive"); - Expression* lower_bound = parse_expression(); - lower_bound->is_delayed(false); - bool inclusive = false; - if (lex< through >()) inclusive = true; - else if (lex< to >()) inclusive = false; - else error("expected 'through' or 'to' keyword in @for directive"); - Expression* upper_bound = parse_expression(); - upper_bound->is_delayed(false); - if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive"); - Block* body = parse_block(); - return new (ctx.mem) For(path, for_source_position, var, lower_bound, upper_bound, body, inclusive); - } - - Each* Parser::parse_each_directive() - { - lex < each_directive >(); - Position each_source_position = source_position; - if (!lex< variable >()) error("@each directive requires an iteration variable"); - string var(Util::normalize_underscores(lexed)); - if (!lex< in >()) error("expected 'in' keyword in @each directive"); - Expression* list = parse_list(); - list->is_delayed(false); - if (list->concrete_type() == Expression::LIST) { - List* l = static_cast(list); - for (size_t i = 0, L = l->length(); i < L; ++i) { - (*l)[i]->is_delayed(false); - } - } - if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive"); - Block* body = parse_block(); - return new (ctx.mem) Each(path, each_source_position, var, list, body); - } - - While* Parser::parse_while_directive() - { - lex< while_directive >(); - Position while_source_position = source_position; - Expression* predicate = parse_list(); - predicate->is_delayed(false); - Block* body = parse_block(); - return new (ctx.mem) While(path, while_source_position, predicate, body); - } - - Media_Block* Parser::parse_media_block() - { - lex< media >(); - Position media_source_position = source_position; - - List* media_queries = parse_media_queries(); - - if (!peek< exactly<'{'> >()) { - error("expected '{' in media query"); - } - Block* block = parse_block(); - - return new (ctx.mem) Media_Block(path, media_source_position, media_queries, block); - } - - List* Parser::parse_media_queries() - { - List* media_queries = new (ctx.mem) List(path, source_position, 0, List::COMMA); - if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query(); - while (lex< exactly<','> >()) (*media_queries) << parse_media_query(); - return media_queries; - } - - // Expression* Parser::parse_media_query() - Media_Query* Parser::parse_media_query() - { - Media_Query* media_query = new (ctx.mem) Media_Query(path, source_position); - - if (lex< exactly< not_kwd > >()) media_query->is_negated(true); - else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true); - - if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema()); - else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, source_position, lexed)); - else (*media_query) << parse_media_expression(); - - while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression(); - - return media_query; - } - - Media_Query_Expression* Parser::parse_media_expression() - { - if (peek< identifier_schema >()) { - String* ss = parse_identifier_schema(); - return new (ctx.mem) Media_Query_Expression(path, source_position, ss, 0, true); - } - if (!lex< exactly<'('> >()) { - error("media query expression must begin with '('"); - } - Expression* feature = 0; - if (peek< exactly<')'> >()) { - error("media feature required in media query expression"); - } - feature = parse_expression(); - Expression* expression = 0; - if (lex< exactly<':'> >()) { - expression = parse_list(); - } - if (!lex< exactly<')'> >()) { - error("unclosed parenthesis in media query expression"); - } - return new (ctx.mem) Media_Query_Expression(path, feature->position(), feature, expression); - } - - At_Rule* Parser::parse_at_rule() - { - lex(); - string kwd(lexed); - Position at_source_position = source_position; - Selector* sel = 0; - Expression* val = 0; - Selector_Lookahead lookahead = lookahead_for_extension_target(position); - if (lookahead.found) { - if (lookahead.has_interpolants) { - sel = parse_selector_schema(lookahead.found); - } - else { - sel = parse_selector_group(); - } - } - else if (!(peek >() || peek >() || peek >())) { - val = parse_list(); - } - Block* body = 0; - if (peek< exactly<'{'> >()) body = parse_block(); - At_Rule* rule = new (ctx.mem) At_Rule(path, at_source_position, kwd, sel, body); - if (!sel) rule->value(val); - return rule; - } - - Warning* Parser::parse_warning() - { - lex< warn >(); - return new (ctx.mem) Warning(path, source_position, parse_list()); - } - - Selector_Lookahead Parser::lookahead_for_selector(const char* start) - { - const char* p = start ? start : position; - const char* q; - bool saw_stuff = false; - bool saw_interpolant = false; - - while ((q = peek< identifier >(p)) || - (q = peek< hyphens_and_identifier >(p)) || - (q = peek< type_selector >(p)) || - (q = peek< id_name >(p)) || - (q = peek< class_name >(p)) || - (q = peek< sequence< pseudo_prefix, identifier > >(p)) || - (q = peek< percentage >(p)) || - (q = peek< dimension >(p)) || - (q = peek< string_constant >(p)) || - (q = peek< exactly<'*'> >(p)) || - (q = peek< exactly<'('> >(p)) || - (q = peek< exactly<')'> >(p)) || - (q = peek< exactly<'['> >(p)) || - (q = peek< exactly<']'> >(p)) || - (q = peek< exactly<'+'> >(p)) || - (q = peek< exactly<'~'> >(p)) || - (q = peek< exactly<'>'> >(p)) || - (q = peek< exactly<','> >(p)) || - (q = peek< binomial >(p)) || - (q = peek< sequence< optional, - optional, - exactly<'n'> > >(p)) || - (q = peek< sequence< optional, - digits > >(p)) || - (q = peek< number >(p)) || - (q = peek< exactly<'&'> >(p)) || - (q = peek< exactly<'%'> >(p)) || - (q = peek< alternatives >(p)) || - (q = peek< sequence< exactly<'.'>, interpolant > >(p)) || - (q = peek< sequence< exactly<'#'>, interpolant > >(p)) || - (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || - (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || - (q = peek< interpolant >(p))) { - saw_stuff = true; - p = q; - if (*(p - 1) == '}') saw_interpolant = true; - } - - Selector_Lookahead result; - result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0; - result.has_interpolants = saw_interpolant; - - return result; - } - - Selector_Lookahead Parser::lookahead_for_extension_target(const char* start) - { - const char* p = start ? start : position; - const char* q; - bool saw_interpolant = false; - bool saw_stuff = false; - - while ((q = peek< identifier >(p)) || - (q = peek< type_selector >(p)) || - (q = peek< id_name >(p)) || - (q = peek< class_name >(p)) || - (q = peek< sequence< pseudo_prefix, identifier > >(p)) || - (q = peek< percentage >(p)) || - (q = peek< dimension >(p)) || - (q = peek< string_constant >(p)) || - (q = peek< exactly<'*'> >(p)) || - (q = peek< exactly<'('> >(p)) || - (q = peek< exactly<')'> >(p)) || - (q = peek< exactly<'['> >(p)) || - (q = peek< exactly<']'> >(p)) || - (q = peek< exactly<'+'> >(p)) || - (q = peek< exactly<'~'> >(p)) || - (q = peek< exactly<'>'> >(p)) || - (q = peek< exactly<','> >(p)) || - (q = peek< binomial >(p)) || - (q = peek< sequence< optional, - optional, - exactly<'n'> > >(p)) || - (q = peek< sequence< optional, - digits > >(p)) || - (q = peek< number >(p)) || - (q = peek< exactly<'&'> >(p)) || - (q = peek< exactly<'%'> >(p)) || - (q = peek< alternatives >(p)) || - (q = peek< sequence< exactly<'.'>, interpolant > >(p)) || - (q = peek< sequence< exactly<'#'>, interpolant > >(p)) || - (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) || - (q = peek< sequence< pseudo_prefix, interpolant > >(p)) || - (q = peek< interpolant >(p)) || - (q = peek< optional >(p))) { - p = q; - if (*(p - 1) == '}') saw_interpolant = true; - saw_stuff = true; - } - - Selector_Lookahead result; - result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0; - result.has_interpolants = saw_interpolant; - - return result; - } - - void Parser::read_bom() - { - size_t skip = 0; - string encoding; - bool utf_8 = false; - switch ((unsigned char) source[0]) { - case 0xEF: - skip = check_bom_chars(source, end, utf_8_bom, 3); - encoding = "UTF-8"; - utf_8 = true; - break; - case 0xFE: - skip = check_bom_chars(source, end, utf_16_bom_be, 2); - encoding = "UTF-16 (big endian)"; - break; - case 0xFF: - skip = check_bom_chars(source, end, utf_16_bom_le, 2); - skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0); - encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)"); - break; - case 0x00: - skip = check_bom_chars(source, end, utf_32_bom_be, 4); - encoding = "UTF-32 (big endian)"; - break; - case 0x2B: - skip = check_bom_chars(source, end, utf_7_bom_1, 4) - | check_bom_chars(source, end, utf_7_bom_2, 4) - | check_bom_chars(source, end, utf_7_bom_3, 4) - | check_bom_chars(source, end, utf_7_bom_4, 4) - | check_bom_chars(source, end, utf_7_bom_5, 5); - encoding = "UTF-7"; - break; - case 0xF7: - skip = check_bom_chars(source, end, utf_1_bom, 3); - encoding = "UTF-1"; - break; - case 0xDD: - skip = check_bom_chars(source, end, utf_ebcdic_bom, 4); - encoding = "UTF-EBCDIC"; - break; - case 0x0E: - skip = check_bom_chars(source, end, scsu_bom, 3); - encoding = "SCSU"; - break; - case 0xFB: - skip = check_bom_chars(source, end, bocu_1_bom, 3); - encoding = "BOCU-1"; - break; - case 0x84: - skip = check_bom_chars(source, end, gb_18030_bom, 4); - encoding = "GB-18030"; - break; - } - if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding); - position += skip; - } - - size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len) - { - size_t skip = 0; - if (src + len > end) return 0; - for (size_t i = 0; i < len; ++i, ++skip) { - if ((unsigned char) src[i] != bom[i]) return 0; - } - return skip; - } - - - Expression* Parser::fold_operands(Expression* base, vector& operands, Binary_Expression::Type op) - { - for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = new (ctx.mem) Binary_Expression(path, source_position, op, base, operands[i]); - Binary_Expression* b = static_cast(base); - if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { - base->is_delayed(true); - } - else { - b->left()->is_delayed(false); - b->right()->is_delayed(false); - } - } - return base; - } - - Expression* Parser::fold_operands(Expression* base, vector& operands, vector& ops) - { - for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = new (ctx.mem) Binary_Expression(path, base->position(), ops[i], base, operands[i]); - Binary_Expression* b = static_cast(base); - if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { - base->is_delayed(true); - } - else { - b->left()->is_delayed(false); - b->right()->is_delayed(false); - } - } - return base; - } - - void Parser::error(string msg, Position pos) - { - throw Error(Error::syntax, path, pos.line ? pos : source_position, msg); - } - -} diff --git a/parser.hpp b/parser.hpp deleted file mode 100644 index 51b89221..00000000 --- a/parser.hpp +++ /dev/null @@ -1,249 +0,0 @@ -#define SASS_PARSER - -#include -#include - -#ifndef SASS_PRELEXER -#include "prelexer.hpp" -#endif - -#ifndef SASS_TOKEN -#include "token.hpp" -#endif - -#ifndef SASS_CONTEXT -#include "context.hpp" -#endif - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#ifndef SASS_POSITION -#include "position.hpp" -#endif - -#include - -struct Selector_Lookahead { - const char* found; - bool has_interpolants; -}; - -namespace Sass { - using std::string; - using std::vector; - using std::map; - using namespace Prelexer; - - class Parser { - public: - class AST_Node; - - enum Syntactic_Context { nothing, mixin_def, function_def }; - - Context& ctx; - vector stack; - const char* source; - const char* position; - const char* end; - string path; - size_t column; - Position source_position; - - - Token lexed; - - Parser(Context& ctx, string path, Position source_position) - : ctx(ctx), stack(vector()), - source(0), position(0), end(0), path(path), column(1), source_position(source_position) - { stack.push_back(nothing); } - - static Parser from_string(string src, Context& ctx, string path = "", Position source_position = Position()); - static Parser from_c_str(const char* src, Context& ctx, string path = "", Position source_position = Position()); - static Parser from_token(Token t, Context& ctx, string path = "", Position source_position = Position()); - -#ifdef __clang__ - - // lex and peak uses the template parameter to branch on the action, which - // triggers clangs tautological comparison on the single-comparison - // branches. This is not a bug, just a merging of behaviour into - // one function - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wtautological-compare" - -#endif - - template - const char* peek(const char* start = 0) - { - if (!start) start = position; - const char* after_whitespace; - if (mx == block_comment) { - after_whitespace = // start; - zero_plus< alternatives >(start); - } - else if (/*mx == ancestor_of ||*/ mx == no_spaces) { - after_whitespace = position; - } - else if (mx == spaces || mx == ancestor_of) { - after_whitespace = mx(start); - if (after_whitespace) { - return after_whitespace; - } - else { - return 0; - } - } - else if (mx == optional_spaces) { - after_whitespace = optional_spaces(start); - } - else if (mx == line_comment_prefix || mx == block_comment_prefix) { - after_whitespace = position; - } - else { - after_whitespace = spaces_and_comments(start); - } - const char* after_token = mx(after_whitespace); - if (after_token) { - return after_token; - } - else { - return 0; - } - } - - template - const char* lex() - { - const char* after_whitespace; - if (mx == block_comment) { - after_whitespace = // position; - zero_plus< alternatives >(position); - } - else if (mx == url) { - after_whitespace = position; - } - else if (mx == ancestor_of || mx == no_spaces) { - after_whitespace = position; - } - else if (mx == spaces) { - after_whitespace = spaces(position); - if (after_whitespace) { - source_position.line += count_interval<'\n'>(position, after_whitespace); - lexed = Token(position, after_whitespace); - return position = after_whitespace; - } - else { - return 0; - } - } - else if (mx == optional_spaces) { - after_whitespace = optional_spaces(position); - } - else { - after_whitespace = spaces_and_comments(position); - } - const char* after_token = mx(after_whitespace); - if (after_token) { - size_t previous_line = source_position.line; - source_position.line += count_interval<'\n'>(position, after_token); - - size_t whitespace = 0; - const char* ptr = after_whitespace - 1; - while (ptr >= position) { - if (*ptr == '\n') - break; - whitespace++; - ptr--; - } - if (previous_line != source_position.line) { - column = 1; - } - - source_position.column = column + whitespace; - column += after_token - after_whitespace + whitespace; - lexed = Token(after_whitespace, after_token); - - return position = after_token; - } - else { - return 0; - } - } - -#ifdef __clang__ - -#pragma clang diagnostic pop - -#endif - - void error(string msg, Position pos = Position()); - void read_bom(); - - Block* parse(); - Import* parse_import(); - Definition* parse_definition(); - Parameters* parse_parameters(); - Parameter* parse_parameter(); - Mixin_Call* parse_mixin_call(); - Arguments* parse_arguments(); - Argument* parse_argument(); - Assignment* parse_assignment(); - Propset* parse_propset(); - Ruleset* parse_ruleset(Selector_Lookahead lookahead); - Selector_Schema* parse_selector_schema(const char* end_of_selector); - Selector_List* parse_selector_group(); - Complex_Selector* parse_selector_combination(); - Compound_Selector* parse_simple_selector_sequence(); - Simple_Selector* parse_simple_selector(); - Wrapped_Selector* parse_negated_selector(); - Simple_Selector* parse_pseudo_selector(); - Attribute_Selector* parse_attribute_selector(); - Block* parse_block(); - Declaration* parse_declaration(); - Expression* parse_map_value(); - Expression* parse_map(); - Expression* parse_list(); - Expression* parse_comma_list(); - Expression* parse_space_list(); - Expression* parse_disjunction(); - Expression* parse_conjunction(); - Expression* parse_relation(); - Expression* parse_expression(); - Expression* parse_term(); - Expression* parse_factor(); - Expression* parse_value(); - Function_Call* parse_calc_function(); - Function_Call* parse_function_call(); - Function_Call_Schema* parse_function_call_schema(); - String* parse_interpolated_chunk(Token); - String* parse_string(); - String* parse_ie_stuff(); - String_Schema* parse_value_schema(); - String* parse_identifier_schema(); - String_Schema* parse_url_schema(); - If* parse_if_directive(bool else_if = false); - For* parse_for_directive(); - Each* parse_each_directive(); - While* parse_while_directive(); - Media_Block* parse_media_block(); - List* parse_media_queries(); - Media_Query* parse_media_query(); - Media_Query_Expression* parse_media_expression(); - At_Rule* parse_at_rule(); - Warning* parse_warning(); - - Selector_Lookahead lookahead_for_selector(const char* start = 0); - Selector_Lookahead lookahead_for_extension_target(const char* start = 0); - - Expression* fold_operands(Expression* base, vector& operands, Binary_Expression::Type op); - Expression* fold_operands(Expression* base, vector& operands, vector& ops); - - void throw_syntax_error(string message, size_t ln = 0); - void throw_read_error(string message, size_t ln = 0); - }; - - size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len); -} diff --git a/paths.hpp b/paths.hpp deleted file mode 100644 index cfe46b2f..00000000 --- a/paths.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include -#include - -using namespace std; - -template -string vector_to_string(vector v) -{ - stringstream buffer; - buffer << "["; - - if (!v.empty()) - { buffer << v[0]; } - else - { buffer << "]"; } - - if (v.size() == 1) - { buffer << "]"; } - else - { - for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i]; - buffer << "]"; - } - - return buffer.str(); -} - -namespace Sass { - - using namespace std; - - template - vector > paths(vector > strata, size_t from_end = 0) - { - if (strata.empty()) { - return vector >(); - } - - size_t end = strata.size() - from_end; - if (end <= 1) { - vector > starting_points; - starting_points.reserve(strata[0].size()); - for (size_t i = 0, S = strata[0].size(); i < S; ++i) { - vector starting_point; - starting_point.push_back(strata[0][i]); - starting_points.push_back(starting_point); - } - return starting_points; - } - - vector > up_to_here = paths(strata, from_end + 1); - vector here = strata[end-1]; - - vector > branches; - branches.reserve(up_to_here.size() * here.size()); - for (size_t i = 0, S1 = up_to_here.size(); i < S1; ++i) { - for (size_t j = 0, S2 = here.size(); j < S2; ++j) { - vector branch = up_to_here[i]; - branch.push_back(here[j]); - branches.push_back(branch); - } - } - - return branches; - } - -} diff --git a/position.hpp b/position.hpp deleted file mode 100644 index a9086b33..00000000 --- a/position.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#define SASS_POSITION - -#include - -namespace Sass { - - struct Position { - size_t file; - size_t line; - size_t column; - - Position() - : file(0), line(0), column(0) { } - - Position(const size_t file, const size_t line, const size_t column) - : file(file), line(line), column(column) { } - - Position(const size_t line, const size_t column) - : file(0), line(line), column(column) { } - }; - -} diff --git a/prelexer.cpp b/prelexer.cpp deleted file mode 100644 index 16b17adf..00000000 --- a/prelexer.cpp +++ /dev/null @@ -1,621 +0,0 @@ -#include -#include -#include -#include "constants.hpp" -#include "prelexer.hpp" - - -namespace Sass { - using namespace Constants; - - namespace Prelexer { - using std::ptrdiff_t; - // Matches zero characters (always succeeds without consuming input). - const char* epsilon(char *src) { - return src; - } - // Matches the empty string. - const char* empty(char *src) { - return *src ? 0 : src; - } - - // Match any single character. - const char* any_char(const char* src) { return *src ? src+1 : src; } - - // Match a single character satisfying the ctype predicates. - const char* space(const char* src) { return std::isspace(*src) ? src+1 : 0; } - const char* alpha(const char* src) { return std::isalpha(*src) || !isascii(*src) ? src+1 : 0; } - const char* digit(const char* src) { return std::isdigit(*src) ? src+1 : 0; } - const char* xdigit(const char* src) { return std::isxdigit(*src) ? src+1 : 0; } - const char* alnum(const char* src) { return std::isalnum(*src) || !isascii(*src) ? src+1 : 0; } - const char* punct(const char* src) { return std::ispunct(*src) ? src+1 : 0; } - // Match multiple ctype characters. - const char* spaces(const char* src) { return one_plus(src); } - const char* alphas(const char* src) { return one_plus(src); } - const char* digits(const char* src) { return one_plus(src); } - const char* xdigits(const char* src) { return one_plus(src); } - const char* alnums(const char* src) { return one_plus(src); } - const char* puncts(const char* src) { return one_plus(src); } - - // Match a line comment. - const char* line_comment(const char* src) { return to_endl(src); } - // Match a line comment prefix. - const char* line_comment_prefix(const char* src) { return exactly(src); } - - - // Match a block comment. - const char* block_comment(const char* src) { - return sequence< optional_spaces, delimited_by >(src); - } - const char* block_comment_prefix(const char* src) { - return exactly(src); - } - // Match either comment. - const char* comment(const char* src) { - return alternatives(src); - } - - const char* newline(const char* src) { - return - alternatives< - exactly<'\n'>, - sequence< exactly<'\r'>, exactly<'\n'> >, - exactly<'\r'>, - exactly<'\f'> - >(src); - } - - const char* whitespace(const char* src) { - return - alternatives< - newline, - exactly<' '>, - exactly<'\t'> - >(src); - } - - const char* escape(const char* src) { - return - sequence< - exactly<'\\'>, - any_char - >(src); - } - - // Match double- and single-quoted strings. - const char* double_quoted_string(const char* src) { - src = exactly<'"'>(src); - if (!src) return 0; - const char* p; - while (1) { - if (!*src) return 0; - if((p = escape(src))) { - src = p; - continue; - } - else if((p = exactly<'"'>(src))) { - return p; - } - else { - ++src; - } - } - return 0; - } - const char* single_quoted_string(const char* src) { - src = exactly<'\''>(src); - if (!src) return 0; - const char* p; - while (1) { - if (!*src) return 0; - if((p = escape(src))) { - src = p; - continue; - } - else if((p = exactly<'\''>(src))) { - return p; - } - else { - ++src; - } - } - return 0; - } - const char* string_constant(const char* src) { - return alternatives(src); - } - // Match interpolants. - - - const char* interpolant(const char* src) { - return delimited_by(src); - } - - // Whitespace handling. - const char* optional_spaces(const char* src) { return optional(src); } - const char* optional_comment(const char* src) { return optional(src); } - const char* spaces_and_comments(const char* src) { - return zero_plus< alternatives >(src); - } - const char* no_spaces(const char* src) { - return negate< spaces >(src); - } - - const char* backslash_something(const char* src) { - return sequence< exactly<'\\'>, any_char >(src); - } - - // Match CSS identifiers. - const char* identifier(const char* src) { - return sequence< optional< exactly<'-'> >, - alternatives< alpha, exactly<'_'>, backslash_something >, - zero_plus< alternatives< alnum, - exactly<'-'>, - exactly<'_'>, - backslash_something > > >(src); - } - - // Match CSS selectors. - const char* sel_ident(const char* src) { - return sequence< optional< alternatives< exactly<'-'>, exactly<'|'> > >, - alternatives< alpha, exactly<'_'>, backslash_something, exactly<'|'> >, - zero_plus< alternatives< alnum, - exactly<'-'>, - exactly<'_'>, - exactly<'|'>, - backslash_something > > >(src); - } - - const char* map_key(const char* src) { - return sequence< spaces_and_comments, - one_plus< sequence< exactly<'('>, spaces_and_comments > >, - one_plus< alternatives< identifier, string_constant, variable, spaces > >, - spaces_and_comments, - zero_plus< exactly<')'> >, - spaces_and_comments, - exactly<':'> >(src); - } - - // Match CSS css variables. - const char* custom_property_name(const char* src) { - return sequence< exactly<'-'>, exactly<'-'>, identifier >(src); - } - - // Match interpolant schemas - const char* identifier_schema(const char* src) { - // follows this pattern: (x*ix*)+ ... well, not quite - return one_plus< sequence< zero_plus< alternatives< identifier, exactly<'-'> > >, - interpolant, - zero_plus< alternatives< identifier, number, exactly<'-'> > > > >(src); - } - const char* value_schema(const char* src) { - // follows this pattern: ([xyz]*i[xyz]*)+ - return one_plus< sequence< zero_plus< alternatives< identifier, percentage, dimension, hex, number, string_constant > >, - interpolant, - zero_plus< alternatives< identifier, percentage, dimension, hex, number, string_constant > > > >(src); - } - const char* filename_schema(const char* src) { - return one_plus< sequence< zero_plus< alternatives< identifier, number, exactly<'.'>, exactly<'/'> > >, - interpolant, - zero_plus< alternatives< identifier, number, exactly<'.'>, exactly<'/'> > > > >(src); - } - - const char* filename(const char* src) { - return one_plus< alternatives< identifier, number, exactly<'.'> > >(src); - } - - // Match CSS '@' keywords. - const char* at_keyword(const char* src) { - return sequence, identifier>(src); - } - - const char* import(const char* src) { - return exactly(src); - } - - const char* media(const char* src) { - return exactly(src); - } - - const char* keyframes(const char* src) { - return sequence< exactly<'@'>, optional< vendor_prefix >, exactly< keyframes_kwd > >(src); - } - - const char* vendor_prefix(const char* src) { - return alternatives< exactly< vendor_opera_kwd >, exactly< vendor_webkit_kwd >, exactly< vendor_mozilla_kwd >, exactly< vendor_ms_kwd >, exactly< vendor_khtml_kwd > >(src); - } - - const char* keyf(const char* src) { - return one_plus< alternatives< to, from, percentage > >(src); - } - - const char* mixin(const char* src) { - return exactly(src); - } - - const char* function(const char* src) { - return exactly(src); - } - - const char* return_directive(const char* src) { - return exactly(src); - } - - const char* include(const char* src) { - return exactly(src); - } - - const char* content(const char* src) { - return exactly(src); - } - - const char* extend(const char* src) { - return exactly(src); - } - - - const char* if_directive(const char* src) { - return exactly(src); - } - - const char* else_directive(const char* src) { - return exactly(src); - } - const char* elseif_directive(const char* src) { - return sequence< else_directive, - spaces_and_comments, - exactly< if_after_else_kwd > >(src); - } - - const char* for_directive(const char* src) { - return exactly(src); - } - - const char* from(const char* src) { - return exactly(src); - } - - const char* to(const char* src) { - return exactly(src); - } - - const char* through(const char* src) { - return exactly(src); - } - - const char* each_directive(const char* src) { - return exactly(src); - } - - const char* in(const char* src) { - return exactly(src); - } - - const char* while_directive(const char* src) { - return exactly(src); - } - - const char* name(const char* src) { - return one_plus< alternatives< alnum, - exactly<'-'>, - exactly<'_'> > >(src); - } - - const char* warn(const char* src) { - return exactly(src); - } - - const char* directive(const char* src) { - return sequence< exactly<'@'>, identifier >(src); - } - - const char* null(const char* src) { - return exactly(src); - } - - // Match CSS type selectors - const char* namespace_prefix(const char* src) { - return sequence< optional< alternatives< identifier, exactly<'*'> > >, - exactly<'|'> >(src); - } - const char* type_selector(const char* src) { - return sequence< optional, identifier>(src); - } - const char* hyphens_and_identifier(const char* src) { - return sequence< zero_plus< exactly< '-' > >, identifier >(src); - } - const char* universal(const char* src) { - return sequence< optional, exactly<'*'> >(src); - } - // Match CSS id names. - const char* id_name(const char* src) { - return sequence, name>(src); - } - // Match CSS class names. - const char* class_name(const char* src) { - return sequence, identifier>(src); - } - // Attribute name in an attribute selector. - const char* attribute_name(const char* src) { - return alternatives< sequence< optional, identifier>, - identifier >(src); - } - // match placeholder selectors - const char* placeholder(const char* src) { - return sequence, identifier>(src); - } - // Match CSS numeric constants. - - const char* sign(const char* src) { - return class_char(src); - } - const char* unsigned_number(const char* src) { - return alternatives, - exactly<'.'>, - one_plus >, - digits>(src); - } - const char* number(const char* src) { - return sequence< optional, unsigned_number>(src); - } - const char* coefficient(const char* src) { - return alternatives< sequence< optional, digits >, - sign >(src); - } - const char* binomial(const char* src) { - return sequence< optional, - optional, - exactly<'n'>, optional_spaces, - sign, optional_spaces, - digits >(src); - } - const char* percentage(const char* src) { - return sequence< number, exactly<'%'> >(src); - } - - const char* em(const char* src) { - return sequence< number, exactly >(src); - } - const char* dimension(const char* src) { - return sequence(src); - } - const char* hex(const char* src) { - const char* p = sequence< exactly<'#'>, one_plus >(src); - ptrdiff_t len = p - src; - return (len != 4 && len != 7) ? 0 : p; - } - - const char* rgb_prefix(const char* src) { - return exactly(src); - } - // Match CSS uri specifiers. - - const char* uri_prefix(const char* src) { - return exactly(src); - } - // TODO: rename the following two functions - const char* uri(const char* src) { - return sequence< exactly, - optional, - string_constant, - optional, - exactly<')'> >(src); - } - const char* url_value(const char* src) { - return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol - one_plus< sequence< zero_plus< exactly<'/'> >, filename > >, // one or more folders and/or trailing filename - optional< exactly<'/'> > >(src); - } - const char* url_schema(const char* src) { - return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol - filename_schema >(src); // optional trailing slash - } - // Match CSS "!important" keyword. - const char* important(const char* src) { - return sequence< exactly<'!'>, - spaces_and_comments, - exactly >(src); - } - // Match CSS "!optional" keyword. - const char* optional(const char* src) { - return sequence< exactly<'!'>, - spaces_and_comments, - exactly >(src); - } - // Match Sass "!default" keyword. - const char* default_flag(const char* src) { - return sequence< exactly<'!'>, - spaces_and_comments, - exactly >(src); - } - // Match Sass "!global" keyword. - const char* global_flag(const char* src) { - return sequence< exactly<'!'>, - spaces_and_comments, - exactly >(src); - } - // Match CSS pseudo-class/element prefixes. - const char* pseudo_prefix(const char* src) { - return sequence< exactly<':'>, optional< exactly<':'> > >(src); - } - // Match CSS function call openers. - const char* functional_schema(const char* src) { - return sequence< identifier_schema, exactly<'('> >(src); - } - const char* functional(const char* src) { - return sequence< identifier, exactly<'('> >(src); - } - // Match the CSS negation pseudo-class. - const char* pseudo_not(const char* src) { - return exactly< pseudo_not_kwd >(src); - } - // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. - const char* even(const char* src) { - return exactly(src); - } - const char* odd(const char* src) { - return exactly(src); - } - // Match CSS attribute-matching operators. - const char* exact_match(const char* src) { return exactly<'='>(src); } - const char* class_match(const char* src) { return exactly(src); } - const char* dash_match(const char* src) { return exactly(src); } - const char* prefix_match(const char* src) { return exactly(src); } - const char* suffix_match(const char* src) { return exactly(src); } - const char* substring_match(const char* src) { return exactly(src); } - // Match CSS combinators. - const char* adjacent_to(const char* src) { - return sequence< optional_spaces, exactly<'+'> >(src); - } - const char* precedes(const char* src) { - return sequence< optional_spaces, exactly<'~'> >(src); - } - const char* parent_of(const char* src) { - return sequence< optional_spaces, exactly<'>'> >(src); - } - const char* ancestor_of(const char* src) { - return sequence< spaces, negate< exactly<'{'> > >(src); - } - - // Match SCSS variable names. - const char* variable(const char* src) { - return sequence, name>(src); - } - - // Match Sass boolean keywords. - const char* true_val(const char* src) { - return exactly(src); - } - const char* false_val(const char* src) { - return exactly(src); - } - const char* and_op(const char* src) { - return exactly(src); - } - const char* or_op(const char* src) { - return exactly(src); - } - const char* not_op(const char* src) { - return exactly(src); - } - const char* eq_op(const char* src) { - return exactly(src); - } - const char* neq_op(const char* src) { - return exactly(src); - } - const char* gt_op(const char* src) { - return exactly(src); - } - const char* gte_op(const char* src) { - return exactly(src); - } - const char* lt_op(const char* src) { - return exactly(src); - } - const char* lte_op(const char* src) { - return exactly(src); - } - - // match specific IE syntax - const char* ie_progid(const char* src) { - return sequence < exactly, exactly<':'>, alternatives< identifier_schema, identifier >, one_plus< sequence< exactly<'.'>, alternatives< identifier_schema, identifier > > > >(src); - } - const char* ie_expression(const char* src) { - return exactly(src); - } - // match any IE syntax - const char* ie_stuff(const char* src) { - return sequence< alternatives < ie_expression, ie_progid >, delimited_by<'(', ';', true> >(src); - } - - // const char* ie_args(const char* src) { - // return sequence< alternatives< ie_keyword_arg, value_schema, string_constant, interpolant, number, identifier, delimited_by< '(', ')', true> >, - // zero_plus< sequence< spaces_and_comments, exactly<','>, spaces_and_comments, alternatives< ie_keyword_arg, value_schema, string_constant, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src); - // } - - const char* ie_keyword_arg(const char* src) { - return sequence< alternatives< variable, identifier_schema, identifier >, spaces_and_comments, exactly<'='>, spaces_and_comments, alternatives< variable, identifier_schema, identifier, number, hex > >(src); - } - - // Path matching functions. - const char* folder(const char* src) { - return sequence< zero_plus< any_char_except<'/'> >, - exactly<'/'> >(src); - } - const char* folders(const char* src) { - return zero_plus< folder >(src); - } - - const char* chunk(const char* src) { - char inside_str = 0; - const char* p = src; - size_t depth = 0; - while (true) { - if (!*p) { - return 0; - } - else if (!inside_str && (*p == '"' || *p == '\'')) { - inside_str = *p; - } - else if (*p == inside_str && *(p-1) != '\\') { - inside_str = 0; - } - else if (*p == '(' && !inside_str) { - ++depth; - } - else if (*p == ')' && !inside_str) { - if (depth == 0) return p; - else --depth; - } - ++p; - } - // unreachable - return 0; - } - - // follow the CSS spec more closely and see if this helps us scan URLs correctly - const char* NL(const char* src) { - return alternatives< exactly<'\n'>, - sequence< exactly<'\r'>, exactly<'\n'> >, - exactly<'\r'>, - exactly<'\f'> >(src); - } - - const char* H(const char* src) { - return std::isxdigit(*src) ? src+1 : 0; - } - - const char* unicode(const char* src) { - return sequence< exactly<'\\'>, - between, - optional< class_char > >(src); - } - - const char* ESCAPE(const char* src) { - return alternatives< unicode, class_char >(src); - } - - const char* url(const char* src) { - // using (more or less) the algorithm described at this url: - // http://www.w3.org/TR/css3-syntax/#consume-a-url-token - const char* pos = src; - pos = zero_plus(pos); - if (*pos == '"' || *pos == '\'') return string_constant(pos); // let the parser handle the rparen - while (*pos != ')') { - if (space(pos)) { - ++pos; - continue; - } - if (*pos == '\\') { - pos = ESCAPE(pos); - if (!pos) return 0; // invalid escape sequence - continue; - } - if (*pos == '"' || *pos == '\'' || *pos == '(') return 0; - ++pos; - } - return pos; - } - } -} diff --git a/prelexer.hpp b/prelexer.hpp deleted file mode 100644 index 66f5b2ee..00000000 --- a/prelexer.hpp +++ /dev/null @@ -1,503 +0,0 @@ -#define SASS_PRELEXER - -namespace Sass { - namespace Prelexer { - - typedef int (*ctype_predicate)(int); - typedef const char* (*prelexer)(const char*); - - // Match a single character literal. - template - const char* exactly(const char* src) { - return *src == pre ? src + 1 : 0; - } - - // Match a string constant. - template - const char* exactly(const char* src) { - const char* pre = prefix; - while (*pre && *src == *pre) ++src, ++pre; - return *pre ? 0 : src; - } - - // Match a single character that satifies the supplied ctype predicate. - template - const char* class_char(const char* src) { - return pred(*src) ? src + 1 : 0; - } - - // Match a single character that is a member of the supplied class. - template - const char* class_char(const char* src) { - const char* cc = char_class; - while (*cc && *src != *cc) ++cc; - return *cc ? src + 1 : 0; - } - - // Match a sequence of characters that all satisfy the supplied ctype predicate. - template - const char* class_chars(const char* src) { - const char* p = src; - while (pred(*p)) ++p; - return p == src ? 0 : p; - } - - // Match a sequence of characters that are all members of the supplied class. - template - const char* class_chars(const char* src) { - const char* p = src; - while (class_char(p)) ++p; - return p == src ? 0 : p; - } - - // Match a sequence of characters up to the next newline. - template - const char* to_endl(const char* src) { - if (!(src = exactly(src))) return 0; - while (*src && *src != '\n') ++src; - return src; - } - - // Match a sequence of characters delimited by the supplied chars. - template - const char* delimited_by(const char* src) { - src = exactly(src); - if (!src) return 0; - const char* stop; - while (1) { - if (!*src) return 0; - stop = exactly(src); - if (stop && (!esc || *(src - 1) != '\\')) return stop; - src = stop ? stop : src + 1; - } - } - - // Match a sequence of characters delimited by the supplied strings. - template - const char* delimited_by(const char* src) { - src = exactly(src); - if (!src) return 0; - const char* stop; - while (1) { - if (!*src) return 0; - stop = exactly(src); - if (stop && (!esc || *(src - 1) != '\\')) return stop; - src = stop ? stop : src + 1; - } - } - - // Match any single character. - const char* any_char(const char* src); - // Match any single character except the supplied one. - template - const char* any_char_except(const char* src) { - return (*src && *src != c) ? src+1 : 0; - } - - // Matches zero characters (always succeeds without consuming input). - const char* epsilon(const char*); - - // Matches the empty string. - const char* empty(const char*); - - // Succeeds of the supplied matcher fails, and vice versa. - template - const char* negate(const char* src) { - return mx(src) ? 0 : src; - } - - // Tries to match a certain number of times (between the supplied interval). - template - const char* between(const char* src) { - for (size_t i = 0; i < lo; ++i) { - src = mx(src); - if (!src) return 0; - } - for (size_t i = lo; i <= hi; ++i) { - const char* new_src = mx(src); - if (!new_src) return src; - src = new_src; - } - return src; - } - - // Tries the matchers in sequence and returns the first match (or none) - template - const char* alternatives(const char* src) { - const char* rslt; - (rslt = mx1(src)) || (rslt = mx2(src)); - return rslt; - } - - // Same as above, but with 3 arguments. - template - const char* alternatives(const char* src) { - const char* rslt; - (rslt = mx1(src)) || (rslt = mx2(src)) || (rslt = mx3(src)); - return rslt; - } - - // Same as above, but with 4 arguments. - template - const char* alternatives(const char* src) { - const char* rslt; - (rslt = mx1(src)) || (rslt = mx2(src)) || - (rslt = mx3(src)) || (rslt = mx4(src)); - return rslt; - } - - // Same as above, but with 5 arguments. - template - const char* alternatives(const char* src) { - const char* rslt; - (rslt = mx1(src)) || (rslt = mx2(src)) || (rslt = mx3(src)) || - (rslt = mx4(src)) || (rslt = mx5(src)); - return rslt; - } - - // Same as above, but with 6 arguments. - template - const char* alternatives(const char* src) { - const char* rslt; - (rslt = mx1(src)) || (rslt = mx2(src)) || (rslt = mx3(src)) || - (rslt = mx4(src)) || (rslt = mx5(src)) || (rslt = mx6(src)); - return rslt; - } - - // Same as above, but with 7 arguments. - template - const char* alternatives(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) || (rslt = mx2(rslt)) || - (rslt = mx3(rslt)) || (rslt = mx4(rslt)) || - (rslt = mx5(rslt)) || (rslt = mx6(rslt)) || - (rslt = mx7(rslt)); - return rslt; - } - - // Same as above, but with 8 arguments. - template - const char* alternatives(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) || (rslt = mx2(rslt)) || - (rslt = mx3(rslt)) || (rslt = mx4(rslt)) || - (rslt = mx5(rslt)) || (rslt = mx6(rslt)) || - (rslt = mx7(rslt)) || (rslt = mx8(rslt)); - return rslt; - } - - // Tries the matchers in sequence and succeeds if they all succeed. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)); - return rslt; - } - - // Same as above, but with 3 arguments. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)) && (rslt = mx3(rslt)); - return rslt; - } - - // Same as above, but with 4 arguments. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)) && - (rslt = mx3(rslt)) && (rslt = mx4(rslt)); - return rslt; - } - - // Same as above, but with 5 arguments. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)) && - (rslt = mx3(rslt)) && (rslt = mx4(rslt)) && - (rslt = mx5(rslt)); - return rslt; - } - - // Same as above, but with 6 arguments. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)) && - (rslt = mx3(rslt)) && (rslt = mx4(rslt)) && - (rslt = mx5(rslt)) && (rslt = mx6(rslt)); - return rslt; - } - - // Same as above, but with 7 arguments. - template - const char* sequence(const char* src) { - const char* rslt = src; - (rslt = mx1(rslt)) && (rslt = mx2(rslt)) && - (rslt = mx3(rslt)) && (rslt = mx4(rslt)) && - (rslt = mx5(rslt)) && (rslt = mx6(rslt)) && - (rslt = mx7(rslt)); - return rslt; - } - - // Match a pattern or not. Always succeeds. - template - const char* optional(const char* src) { - const char* p = mx(src); - return p ? p : src; - } - - // Match zero or more of the supplied pattern - template - const char* zero_plus(const char* src) { - const char* p = mx(src); - while (p) src = p, p = mx(src); - return src; - } - - // Match one or more of the supplied pattern - template - const char* one_plus(const char* src) { - const char* p = mx(src); - if (!p) return 0; - while (p) src = p, p = mx(src); - return src; - } - - // Match a single character satisfying the ctype predicates. - const char* space(const char* src); - const char* alpha(const char* src); - const char* digit(const char* src); - const char* xdigit(const char* src); - const char* alnum(const char* src); - const char* punct(const char* src); - // Match multiple ctype characters. - const char* spaces(const char* src); - const char* alphas(const char* src); - const char* digits(const char* src); - const char* xdigits(const char* src); - const char* alnums(const char* src); - const char* puncts(const char* src); - - // Match a line comment. - const char* line_comment(const char* src); - const char* line_comment_prefix(const char* src); - - // Match a block comment. - const char* block_comment(const char* src); - const char* block_comment_prefix(const char* src); - // Match either. - const char* comment(const char* src); - // Match double- and single-quoted strings. - const char* double_quoted_string(const char* src); - const char* single_quoted_string(const char* src); - const char* string_constant(const char* src); - // Match interpolants. - const char* interpolant(const char* src); - - // Whitespace handling. - const char* optional_spaces(const char* src); - const char* optional_comment(const char* src); - const char* spaces_and_comments(const char* src); - const char* no_spaces(const char* src); - - const char* backslash_something(const char* src); - - const char* map_key(const char* src); - - // Match CSS css variables. - const char* custom_property_name(const char* src); - // Match a CSS identifier. - const char* identifier(const char* src); - // Match selector names. - const char* sel_ident(const char* src); - // Match interpolant schemas - const char* identifier_schema(const char* src); - const char* value_schema(const char* src); - const char* filename(const char* src); - const char* filename_schema(const char* src); - const char* url_schema(const char* src); - const char* url_value(const char* src); - const char* vendor_prefix(const char* src); - // Match CSS '@' keywords. - const char* at_keyword(const char* src); - const char* import(const char* src); - const char* media(const char* src); - const char* keyframes(const char* src); - const char* keyf(const char* src); - const char* mixin(const char* src); - const char* function(const char* src); - const char* return_directive(const char* src); - const char* include(const char* src); - const char* content(const char* src); - const char* extend(const char* src); - - const char* if_directive(const char* src); - const char* else_directive(const char* src); - const char* elseif_directive(const char* src); - - const char* for_directive(const char* src); - const char* from(const char* src); - const char* to(const char* src); - const char* through(const char* src); - - const char* each_directive(const char* src); - const char* in(const char* src); - - const char* while_directive(const char* src); - - const char* warn(const char* src); - - const char* directive(const char* src); - const char* at_keyword(const char* src); - - const char* null(const char* src); - - // Match CSS type selectors - const char* namespace_prefix(const char* src); - const char* type_selector(const char* src); - const char* hyphens_and_identifier(const char* src); - const char* universal(const char* src); - // Match CSS id names. - const char* id_name(const char* src); - // Match CSS class names. - const char* class_name(const char* src); - // Attribute name in an attribute selector - const char* attribute_name(const char* src); - // Match placeholder selectors. - const char* placeholder(const char* src); - // Match CSS numeric constants. - const char* sign(const char* src); - const char* unsigned_number(const char* src); - const char* number(const char* src); - const char* coefficient(const char* src); - const char* binomial(const char* src); - const char* percentage(const char* src); - const char* dimension(const char* src); - const char* hex(const char* src); - const char* rgb_prefix(const char* src); - // Match CSS uri specifiers. - const char* uri_prefix(const char* src); - const char* uri(const char* src); - const char* url(const char* src); - // Match CSS "!important" keyword. - const char* important(const char* src); - // Match CSS "!optional" keyword. - const char* optional(const char* src); - // Match Sass "!default" keyword. - const char* default_flag(const char* src); - const char* global_flag(const char* src); - // Match CSS pseudo-class/element prefixes - const char* pseudo_prefix(const char* src); - // Match CSS function call openers. - const char* functional(const char* src); - const char* functional_schema(const char* src); - const char* pseudo_not(const char* src); - // Match CSS 'odd' and 'even' keywords for functional pseudo-classes. - const char* even(const char* src); - const char* odd(const char* src); - // Match CSS attribute-matching operators. - const char* exact_match(const char* src); - const char* class_match(const char* src); - const char* dash_match(const char* src); - const char* prefix_match(const char* src); - const char* suffix_match(const char* src); - const char* substring_match(const char* src); - // Match CSS combinators. - const char* adjacent_to(const char* src); - const char* precedes(const char* src); - const char* parent_of(const char* src); - const char* ancestor_of(const char* src); - - // Match SCSS variable names. - const char* variable(const char* src); - - // Match Sass boolean keywords. - const char* true_val(const char* src); - const char* false_val(const char* src); - const char* and_op(const char* src); - const char* or_op(const char* src); - const char* not_op(const char* src); - const char* eq_op(const char* src); - const char* neq_op(const char* src); - const char* gt_op(const char* src); - const char* gte_op(const char* src); - const char* lt_op(const char* src); - const char* lte_op(const char* src); - - // IE stuff - const char* ie_stuff(const char* src); - const char* ie_args(const char* src); - const char* ie_keyword_arg(const char* src); - - // match urls - const char* url(const char* src); - - // Path matching functions. - const char* folder(const char* src); - const char* folders(const char* src); - - // Utility functions for finding and counting characters in a string. - template - const char* find_first(const char* src) { - while (*src && *src != c) ++src; - return *src ? src : 0; - } - template - const char* find_first(const char* src) { - while (*src && !mx(src)) ++src; - return *src ? src : 0; - } - template - const char* find_first_in_interval(const char* beg, const char* end) { - while ((beg < end) && *beg) { - if (mx(beg)) return beg; - ++beg; - } - return 0; - } - template - unsigned int count_interval(const char* beg, const char* end) { - unsigned int counter = 0; - while (beg < end && *beg) { - if (*beg == c) ++counter; - ++beg; - } - return counter; - } - template - unsigned int count_interval(const char* beg, const char* end) { - unsigned int counter = 0; - while (beg < end && *beg) { - const char* p; - if ((p = mx(beg))) { - ++counter; - beg = p; - } - else { - ++beg; - } - } - return counter; - } - - const char* chunk(const char* src); - } -} diff --git a/remove_placeholders.cpp b/remove_placeholders.cpp deleted file mode 100644 index 36586743..00000000 --- a/remove_placeholders.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "remove_placeholders.hpp" -#include "context.hpp" -#include "inspect.hpp" -#include "to_string.hpp" -#include - -namespace Sass { - - Remove_Placeholders::Remove_Placeholders(Context& ctx) - : ctx(ctx) - { } - - template - void Remove_Placeholders::clean_selector_list(T r) { - - // Create a new selector group without placeholders - Selector_List* sl = static_cast(r->selector()); - - if (sl) { - Selector_List* new_sl = new (ctx.mem) Selector_List(sl->path(), sl->position()); - - for (size_t i = 0, L = sl->length(); i < L; ++i) { - if (!(*sl)[i]->has_placeholder()) { - *new_sl << (*sl)[i]; - } - } - - // Set the new placeholder selector list - r->selector(new_sl); - } - - // Iterate into child blocks - Block* b = r->block(); - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - stm->perform(this); - } - } - - void Remove_Placeholders::operator()(Block* b) { - for (size_t i = 0, L = b->length(); i < L; ++i) { - (*b)[i]->perform(this); - } - } - - void Remove_Placeholders::operator()(Ruleset* r) { - clean_selector_list(r); - } - - void Remove_Placeholders::operator()(Media_Block* m) { - clean_selector_list(m); - } - - void Remove_Placeholders::operator()(At_Rule* a) { - if (a->block()) a->block()->perform(this); - } - -} diff --git a/remove_placeholders.hpp b/remove_placeholders.hpp deleted file mode 100644 index e81cc1ec..00000000 --- a/remove_placeholders.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -namespace Sass { - - using namespace std; - - struct Context; - - class Remove_Placeholders : public Operation_CRTP { - - Context& ctx; - - void fallback_impl(AST_Node* n) {}; - - public: - Remove_Placeholders(Context&); - virtual ~Remove_Placeholders() { } - - using Operation::operator(); - - void operator()(Block*); - void operator()(Ruleset*); - void operator()(Media_Block*); - void operator()(At_Rule*); - - template - void clean_selector_list(T r); - - template - void fallback(U x) { return fallback_impl(x); } - }; - -} diff --git a/sass.cpp b/sass.cpp deleted file mode 100644 index fe36202c..00000000 --- a/sass.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#include - -#ifndef SASS -#include "sass.h" -#endif - -#include "context.hpp" - -#ifndef SASS_ERROR_HANDLING -#include "error_handling.hpp" -#endif - -extern "C" { - using namespace std; - - struct Sass_Context* make_sass_context() - { return (Sass_Context*) calloc(1, sizeof(Sass_Context)); } - - void free_sass_context(struct Sass_Context* ctx) - { - if (ctx->output_string) free(ctx->output_string); - if (ctx->error_message) free(ctx->error_message); - free(ctx); - } - - namespace Sass { - enum Sass_Source { FILE_SOURCE, STRING_SOURCE }; - - static void compile_sass(struct Sass_Context* c_ctx, - Sass::Sass_Source src_option) - { - using namespace Sass; - try { - Context cpp_ctx( - Context::Data().source_c_str (c_ctx->input_string) - - .entry_point (c_ctx->input_path ? - c_ctx->input_path : - "") - - .output_style ((Output_Style) - c_ctx->output_style) - - .source_comments (c_ctx->source_comments) - .source_map_file (c_ctx->source_map_file) - .omit_source_map_url (c_ctx->omit_source_map_url) - - .image_path (c_ctx->image_path ? - c_ctx->image_path : - "") - - .output_path (c_ctx->output_path ? - c_ctx->output_path : - "") - - .include_paths_c_str (c_ctx->include_paths_string) - .include_paths_array (/*c_ctx->include_paths_array*/0) - .include_paths (vector()) - .precision (c_ctx->precision ? c_ctx->precision : 5) - ); - if (src_option == FILE_SOURCE) cpp_ctx.compile_file(); - else cpp_ctx.compile_string(); - c_ctx->error_message = 0; - c_ctx->error_status = 0; - } - catch (Error& e) { - stringstream msg_stream; - msg_stream << e.path << ":" << e.position.line << ": error: " << e.message << endl; - string msg(msg_stream.str()); - char* msg_str = (char*) malloc(msg.size() + 1); - strcpy(msg_str, msg.c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->error_message = msg_str; - } - catch (bad_alloc& ba) { - stringstream msg_stream; - msg_stream << "Unable to allocate memory: " << ba.what() << endl; - string msg(msg_stream.str()); - char* msg_str = (char*) malloc(msg.size() + 1); - strcpy(msg_str, msg.c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->error_message = msg_str; - } - } - } - - void compile_sass_file(struct Sass_Context* c_ctx) - { Sass::compile_sass(c_ctx, Sass::FILE_SOURCE); } - - void compile_sass_string(struct Sass_Context* c_ctx) - { Sass::compile_sass(c_ctx, Sass::STRING_SOURCE); } - - - union Sass_Value make_sass_boolean(int val) - { - union Sass_Value v; - v.boolean.tag = SASS_BOOLEAN; - v.boolean.value = val; - return v; - } - - union Sass_Value make_sass_number(double val, const char* unit) - { - union Sass_Value v; - v.number.tag = SASS_NUMBER; - v.number.value = val; - v.number.unit = strdup(unit); - return v; - } - - union Sass_Value make_sass_color(double r, double g, double b, double a) - { - union Sass_Value v; - v.color.tag = SASS_COLOR; - v.color.r = r; - v.color.g = g; - v.color.b = b; - v.color.a = a; - return v; - } - - union Sass_Value make_sass_string(const char* val) - { - union Sass_Value v; - v.string.tag = SASS_STRING; - v.string.value = strdup(val); - return v; - } - - union Sass_Value make_sass_list(size_t len, enum Sass_Separator sep) - { - union Sass_Value v; - v.list.tag = SASS_LIST; - v.list.length = len; - v.list.separator = sep; - v.list.values = (union Sass_Value*) malloc(sizeof(union Sass_Value)*len); - return v; - } - - union Sass_Value make_sass_map(size_t len) - { - union Sass_Value v; - v.map.tag = SASS_MAP; - v.map.length = len; - v.map.pairs = (struct Sass_KeyValuePair*) malloc(sizeof(struct Sass_KeyValuePair)*len); - return v; - } - - union Sass_Value make_sass_null() - { - union Sass_Value v; - v.null.tag = SASS_NULL; - return v; - } - - union Sass_Value make_sass_error(const char* msg) - { - union Sass_Value v; - v.error.tag = SASS_ERROR; - v.error.message = strdup(msg); - return v; - } - -} diff --git a/sass.h b/sass.h deleted file mode 100644 index 197eb06f..00000000 --- a/sass.h +++ /dev/null @@ -1,150 +0,0 @@ -#define SASS - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SASS_OUTPUT_NESTED 0 -#define SASS_OUTPUT_EXPANDED 1 -#define SASS_OUTPUT_COMPACT 2 -#define SASS_OUTPUT_COMPRESSED 3 -#define SASS_OUTPUT_FORMATTED 4 - -struct Sass_Context { - const char* input_path; - const char* input_string; - char* output_string; - - int error_status; - char* error_message; - - int output_style; - bool source_comments; - const char* source_map_file; - bool omit_source_map_url; - const char* image_path; - const char* output_path; - const char* include_paths_string; - const char** include_paths_array; - int precision; -}; - -struct Sass_Context* make_sass_context (); -void free_sass_context (struct Sass_Context*); -void compile_sass_file (struct Sass_Context*); -void compile_sass_string (struct Sass_Context*); - -// type tags for Sass values -enum Sass_Tag { - SASS_BOOLEAN, - SASS_NUMBER, - SASS_COLOR, - SASS_STRING, - SASS_LIST, - SASS_MAP, - SASS_NULL, - SASS_ERROR -}; - -// tags for denoting Sass list separators -enum Sass_Separator { - SASS_COMMA, - SASS_SPACE -}; - -// Component structs for the Sass value union type. -// Not meant to be used directly. -struct Sass_Unknown { - enum Sass_Tag tag; -}; - -struct Sass_Boolean { - enum Sass_Tag tag; - int 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; -}; - -union Sass_Value; - -struct Sass_List { - enum Sass_Tag tag; - enum Sass_Separator separator; - size_t length; - union Sass_Value* values; -}; - -struct Sass_KeyValuePair; -struct Sass_Map { - enum Sass_Tag tag; - size_t length; - struct Sass_KeyValuePair* pairs; -}; - -struct Sass_Null { - enum Sass_Tag tag; -}; - -struct Sass_Error { - enum Sass_Tag tag; - char* message; -}; - -// represention of Sass values in C -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_KeyValuePair { - union Sass_Value key; - union Sass_Value value; -}; - -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); - -typedef union Sass_Value(*Sass_C_Function)(union Sass_Value, void *cookie); - -struct Sass_C_Function_Descriptor { - const char* signature; - Sass_C_Function function; - void *cookie; -}; - -#ifdef __cplusplus -} -#endif diff --git a/sass2scss.cpp b/sass2scss.cpp deleted file mode 100644 index b4171be9..00000000 --- a/sass2scss.cpp +++ /dev/null @@ -1,781 +0,0 @@ -// include library -#include -#include -#include -#include -#include - -///* -// -// src comments: comments in sass syntax (staring with //) -// css comments: multiline comments in css syntax (starting with /*) -// -// KEEP_COMMENT: keep src comments in the resulting css code -// STRIP_COMMENT: strip out all comments (either src or css) -// CONVERT_COMMENT: convert all src comments to css comments -// -//*/ - -// our own header -#include "sass2scss.h" - -// using std::string -using namespace std; - -// add namespace for c++ -namespace Sass -{ - - // return the actual prettify value from options - #define PRETTIFY(converter) (converter.options - (converter.options & 248)) - // query the options integer to check if the option is enables - #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT) - #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT) - #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT) - - // some makros to access the indentation stack - #define INDENT(converter) (converter.indents.top()) - - // some makros to query comment parser status - #define IS_PARSING(converter) (converter.comment == "") - #define IS_COMMENT(converter) (converter.comment != "") - #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter)) - #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter))) - - // pretty printer helper function - static string closer (const converter& converter) - { - return PRETTIFY(converter) == 0 ? " }" : - PRETTIFY(converter) <= 1 ? " }" : - "\n" + INDENT(converter) + "}"; - } - - // pretty printer helper function - static string opener (const converter& converter) - { - return PRETTIFY(converter) == 0 ? " { " : - PRETTIFY(converter) <= 2 ? " {" : - "\n" + INDENT(converter) + "{"; - } - - // check if the given string is a pseudo selector - // needed to differentiate from sass property syntax - static bool isPseudoSelector (string& sel) - { - - size_t len = sel.length(); - if (len < 1) return false; - size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1); - if (pos != string::npos) sel.erase(pos, string::npos); - size_t i = sel.length(); - while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); } - - // CSS Level 1 - Recommendation - if (sel == ":link") return true; - if (sel == ":visited") return true; - if (sel == ":active") return true; - - // CSS Level 2 (Revision 1) - Recommendation - if (sel == ":lang") return true; - if (sel == ":first-child") return true; - if (sel == ":hover") return true; - if (sel == ":focus") return true; - // disabled - also valid properties - // if (sel == ":left") return true; - // if (sel == ":right") return true; - if (sel == ":first") return true; - - // Selectors Level 3 - Recommendation - if (sel == ":target") return true; - if (sel == ":root") return true; - if (sel == ":nth-child") return true; - if (sel == ":nth-last-of-child") return true; - if (sel == ":nth-of-type") return true; - if (sel == ":nth-last-of-type") return true; - if (sel == ":last-child") return true; - if (sel == ":first-of-type") return true; - if (sel == ":last-of-type") return true; - if (sel == ":only-child") return true; - if (sel == ":only-of-type") return true; - if (sel == ":empty") return true; - if (sel == ":not") return true; - - // CSS Basic User Interface Module Level 3 - Working Draft - if (sel == ":default") return true; - if (sel == ":valid") return true; - if (sel == ":invalid") return true; - if (sel == ":in-range") return true; - if (sel == ":out-of-range") return true; - if (sel == ":required") return true; - if (sel == ":optional") return true; - if (sel == ":read-only") return true; - if (sel == ":read-write") return true; - if (sel == ":dir") return true; - if (sel == ":enabled") return true; - if (sel == ":disabled") return true; - if (sel == ":checked") return true; - if (sel == ":indeterminate") return true; - if (sel == ":nth-last-child") return true; - - // Selectors Level 4 - Working Draft - if (sel == ":any-link") return true; - if (sel == ":local-link") return true; - if (sel == ":scope") return true; - if (sel == ":active-drop-target") return true; - if (sel == ":valid-drop-target") return true; - if (sel == ":invalid-drop-target") return true; - if (sel == ":current") return true; - if (sel == ":past") return true; - if (sel == ":future") return true; - if (sel == ":placeholder-shown") return true; - if (sel == ":user-error") return true; - if (sel == ":blank") return true; - if (sel == ":nth-match") return true; - if (sel == ":nth-last-match") return true; - if (sel == ":nth-column") return true; - if (sel == ":nth-last-column") return true; - if (sel == ":matches") return true; - - // Fullscreen API - Living Standard - if (sel == ":fullscreen") return true; - - // not a pseudo selector - return false; - - } - - // check if there is some char data - // will ignore everything in comments - static bool hasCharData (string& sass) - { - - size_t col_pos = 0; - - while (true) - { - - // try to find some meaningfull char - col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos); - - // there was no meaningfull char found - if (col_pos == string::npos) return false; - - // found a multiline comment opener - if (sass.substr(col_pos, 2) == "/*") - { - // find the multiline comment closer - col_pos = sass.find("*/", col_pos); - // maybe we did not find the closer here - if (col_pos == string::npos) return false; - // skip closer - col_pos += 2; - } - else - { - return true; - } - - } - - } - // EO hasCharData - - // find src comment opener - // correctly skips quoted strings - static size_t findCommentOpener (string& sass) - { - - size_t col_pos = 0; - bool apoed = false; - bool quoted = false; - bool comment = false; - size_t brackets = 0; - - while (col_pos != string::npos) - { - - // process all interesting chars - col_pos = sass.find_first_of("\"\'/\\*()", col_pos); - - // assertion for valid result - if (col_pos != string::npos) - { - - if (sass.at(col_pos) == '(') - { - if (!quoted && !apoed) brackets ++; - } - else if (sass.at(col_pos) == ')') - { - if (!quoted && !apoed) brackets --; - } - else if (sass.at(col_pos) == '\"') - { - // invert quote bool - if (!apoed && !comment) quoted = !quoted; - } - else if (sass.at(col_pos) == '\'') - { - // invert quote bool - if (!quoted && !comment) apoed = !apoed; - } - else if (col_pos > 0 && sass.at(col_pos) == '/') - { - if (sass.at(col_pos - 1) == '*') - { - comment = false; - } - // next needs to be a slash too - else if (sass.at(col_pos - 1) == '/') - { - // only found if not in single or double quote, bracket or comment - if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1; - } - } - else if (sass.at(col_pos) == '\\') - { - // skip next char if in quote - if (quoted || apoed) col_pos ++; - } - // this might be a comment opener - else if (col_pos > 0 && sass.at(col_pos) == '*') - { - // opening a multiline comment - if (sass.at(col_pos - 1) == '/') - { - // we are now in a comment - if (!quoted && !apoed) comment = true; - } - } - - // skip char - col_pos ++; - - } - - } - // EO while - - return col_pos; - - } - // EO findCommentOpener - - // remove multiline comments from sass string - // correctly skips quoted strings - static string removeMultilineComment (string &sass) - { - - string clean = ""; - size_t col_pos = 0; - size_t open_pos = 0; - size_t close_pos = 0; - bool apoed = false; - bool quoted = false; - bool comment = false; - - // process sass til string end - while (col_pos != string::npos) - { - - // process all interesting chars - col_pos = sass.find_first_of("\"\'/\\*", col_pos); - - // assertion for valid result - if (col_pos != string::npos) - { - - // found quoted string delimiter - if (sass.at(col_pos) == '\"') - { - if (!apoed && !comment) quoted = !quoted; - } - else if (sass.at(col_pos) == '\'') - { - if (!quoted && !comment) apoed = !apoed; - } - // found possible comment closer - else if (sass.at(col_pos) == '/') - { - // look back to see if it is actually a closer - if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*') - { - close_pos = col_pos + 1; comment = false; - } - } - else if (sass.at(col_pos) == '\\') - { - // skip escaped char - if (quoted || apoed) col_pos ++; - } - // this might be a comment opener - else if (sass.at(col_pos) == '*') - { - // look back to see if it is actually an opener - if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/') - { - comment = true; open_pos = col_pos - 1; - clean += sass.substr(close_pos, open_pos - close_pos); - } - } - - // skip char - col_pos ++; - - } - - } - // EO while - - // add final parts (add half open comment text) - if (comment) clean += sass.substr(open_pos); - else clean += sass.substr(close_pos); - - // return string - return clean; - - } - // EO removeMultilineComment - - // right trim a given string - string rtrim(const string &sass) - { - string trimmed = sass; - size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r"); - if (pos_ws != string::npos) - { trimmed.erase(pos_ws + 1); } - else { trimmed.clear(); } - return trimmed; - } - // EO rtrim - - // flush whitespace and print additional text, but - // only print additional chars and buffer whitespace - string flush (string& sass, converter& converter) - { - - // return flushed - string scss = ""; - - // print whitespace buffer - scss += PRETTIFY(converter) > 0 ? - converter.whitespace : ""; - // reset whitespace buffer - converter.whitespace = ""; - - // remove possible newlines from string - size_t pos_right = sass.find_last_not_of("\n\r"); - if (pos_right == string::npos) return scss; - - // get the linefeeds from the string - string lfs = sass.substr(pos_right + 1); - sass = sass.substr(0, pos_right + 1); - - // find some source comment opener - size_t comment_pos = findCommentOpener(sass); - // check if there was a source comment - if (comment_pos != string::npos) - { - // convert comment (but only outside other coments) - if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter)) - { - // convert to multiline comment - sass.at(comment_pos + 1) = '*'; - // add comment node to the whitespace - sass += " */"; - } - // not at line start - if (comment_pos > 0) - { - // also include whitespace before the actual comment opener - size_t ws_pos = sass.find_last_not_of(" \t\n\v\f\r", comment_pos - 1); - comment_pos = ws_pos == string::npos ? 0 : ws_pos + 1; - } - if (!STRIP_COMMENT(converter)) - { - // add comment node to the whitespace - converter.whitespace += sass.substr(comment_pos); - } - else - { - // sass = removeMultilineComments(sass); - } - // update the actual sass code - sass = sass.substr(0, comment_pos); - } - - // add newline as getline discharged it - converter.whitespace += lfs + "\n"; - - // maybe remove any leading whitespace - if (PRETTIFY(converter) == 0) - { - // remove leading whitespace and update string - size_t pos_left = sass.find_first_not_of(" \t\n\v\f\r"); - if (pos_left != string::npos) sass = sass.substr(pos_left); - } - - // add flushed data - scss += sass; - - // return string - return scss; - - } - // EO flush - - // process a line of the sass text - string process (string& sass, converter& converter) - { - - // resulting string - string scss = ""; - - // strip multi line comments - if (STRIP_COMMENT(converter)) - { - sass = removeMultilineComment(sass); - } - - // right trim input - sass = rtrim(sass); - - // get postion of first meaningfull character in string - size_t pos_left = sass.find_first_not_of(" \t\n\v\f\r"); - - // special case for final run - if (converter.end_of_file) pos_left = 0; - - // maybe has only whitespace - if (pos_left == string::npos) - { - // just add complete whitespace - converter.whitespace += sass + "\n"; - } - // have meaningfull first char - else - { - - // extract and store indentation string - string indent = sass.substr(0, pos_left); - - // check if current line starts a comment - string open = sass.substr(pos_left, 2); - - // line has less or same indentation - // finalize previous open parser context - if (indent.length() <= INDENT(converter).length()) - { - - // close multilinie comment - if (IS_CSS_COMMENT(converter)) - { - // check if comments will be stripped anyway - if (!STRIP_COMMENT(converter)) scss += " */"; - } - // close src comment comment - else if (IS_SRC_COMMENT(converter)) - { - // add a newline to avoid closer on same line - // this would put the bracket in the comment node - // no longer needed since we parse them correctly - // if (KEEP_COMMENT(converter)) scss += "\n"; - } - // close css properties - else if (converter.property) - { - // add closer unless in concat mode - if (!converter.comma) - { - // if there was no colon we have a selector - // looks like there were no inner properties - if (converter.selector) scss += " {}"; - // add final semicolon - else if (!converter.semicolon) scss += ";"; - } - } - - // reset comment state - converter.comment = ""; - - } - - // make sure we close every "higher" block - while (indent.length() < INDENT(converter).length()) - { - // pop stacked context - converter.indents.pop(); - // print close bracket - if (IS_PARSING(converter)) - { scss += closer(converter); } - else { scss += " */"; } - // reset comment state - converter.comment = ""; - } - - // reset converter state - converter.selector = false; - - // check if we have sass property syntax - if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::") - { - - // default to a selector - // change back if property found - converter.selector = true; - // get postion of first whitespace char - size_t pos_wspace = sass.find_first_of(" \t\n\v\f\r", pos_left); - // assertion check for valid result - if (pos_wspace != string::npos) - { - // get the possible pseudo selector - string pseudo = sass.substr(pos_left, pos_wspace - pos_left); - // get position of the first real property value char - // pseudo selectors get this far, but have no actual value - size_t pos_value = sass.find_first_not_of(" \t\n\v\f\r", pos_wspace); - // assertion check for valid result - if (pos_value != string::npos) - { - // only process if not (fallowed by a semicolon or is a pseudo selector) - if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo))) - { - // create new string by interchanging the colon sign for property and value - sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace); - // try to find a colon in the current line, but only ... - size_t pos_colon = sass.find_first_not_of(":", pos_left); - // assertion for valid result - if (pos_colon != string::npos) - { - // ... after the first word (skip begining colons) - pos_colon = sass.find_first_of(":", pos_colon); - // it is a selector if there was no colon found - converter.selector = pos_colon == string::npos; - } - } - } - } - - } - - // replace some specific sass shorthand directives (if not fallowed by a white space character) - else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(" \t\n\v\f\r", pos_left) != pos_left + 1) - { sass = indent + "@mixin " + sass.substr(pos_left + 1); } - else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(" \t\n\v\f\r", pos_left) != pos_left + 1) - { sass = indent + "@include " + sass.substr(pos_left + 1); } - - // add quotes for import if needed - else if (sass.substr(pos_left, 7) == "@import") - { - // get positions for the actual import url - size_t pos_import = sass.find_first_of(" \t\n\v\f\r", pos_left + 7); - size_t pos_quote = sass.find_first_not_of(" \t\n\v\f\r", pos_import); - // check if the url is quoted - if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'") - { - // get position of the last char on the line - size_t pos_end = sass.find_last_not_of(" \t\n\v\f\r"); - // assertion check for valid result - if (pos_end != string::npos) - { - // add quotes around the full line after the import statement - sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\""; - } - } - - } - else if ( - sass.substr(pos_left, 7) != "@return" && - sass.substr(pos_left, 7) != "@extend" && - sass.substr(pos_left, 8) != "@content" - ) { - - // try to find a colon in the current line, but only ... - size_t pos_colon = sass.find_first_not_of(":", pos_left); - // assertion for valid result - if (pos_colon != string::npos) - { - // ... after the first word (skip begining colons) - pos_colon = sass.find_first_of(":", pos_colon); - // it is a selector if there was no colon found - converter.selector = pos_colon == string::npos; - } - - } - - // current line has more indentation - if (indent.length() >= INDENT(converter).length()) - { - // not in comment mode - if (IS_PARSING(converter)) - { - // has meaningfull chars - if (hasCharData(sass)) - { - // is probably a property - // also true for selectors - converter.property = true; - } - } - } - // current line has more indentation - if (indent.length() > INDENT(converter).length()) - { - // not in comment mode - if (IS_PARSING(converter)) - { - // had meaningfull chars - if (converter.property) - { - // print block opener - scss += opener(converter); - // push new stack context - converter.indents.push(""); - // store block indentation - INDENT(converter) = indent; - } - } - // is and will be a src comment - else if (!IS_CSS_COMMENT(converter)) - { - // scss does not allow multiline src comments - // therefore add forward slashes to all lines - sass.at(INDENT(converter).length()+0) = '/'; - // there is an edge case here if indentation - // is minimal (will overwrite the fist char) - sass.at(INDENT(converter).length()+1) = '/'; - // could code around that, but I dont' think - // this will ever be the cause for any trouble - } - } - - // line is opening a new comment - if (open == "/*" || open == "//") - { - // reset the property state - converter.property = false; - // close previous comment - if (IS_CSS_COMMENT(converter) && open != "") - { - if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */"; - } - // force single line comments - // into a correct css comment - if (CONVERT_COMMENT(converter)) - { - if (IS_PARSING(converter)) - { sass.at(pos_left + 1) = '*'; } - } - // set comment flag - converter.comment = open; - - } - - // flush data only under certain conditions - if (!( - // strip css and src comments if option is set - (IS_COMMENT(converter) && STRIP_COMMENT(converter)) || - // strip src comment even if strip option is not set - // but only if the keep src comment option is not set - (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter)) - )) - { - // flush data and buffer whitespace - scss += flush(sass, converter); - } - - // get postion of last meaningfull char - size_t pos_right = sass.find_last_not_of(" \t\n\v\f\r"); - - // check for invalid result - if (pos_right != string::npos) - { - - // get the last meaningfull char - string close = sass.substr(pos_right, 1); - - // check if next line should be concatenated (list mode) - converter.comma = IS_PARSING(converter) && close == ","; - converter.semicolon = IS_PARSING(converter) && close == ";"; - - // check if we have more than - // one meaningfull char - if (pos_right > 0) - { - - // get the last two chars from string - string close = sass.substr(pos_right - 1, 2); - // update parser status for expicitly closed comment - if (close == "*/") converter.comment = ""; - - } - - } - // EO have meaningfull chars from end - - } - // EO have meaningfull chars from start - - // return scss - return scss; - - } - // EO process - - // the main converter function for c++ - char* sass2scss (const string sass, const int options) - { - - // local variables - string line; - string scss = ""; - const char delim = '\n'; - stringstream stream(sass); - - // create converter variable - converter converter; - // initialise all options - converter.comma = false; - converter.property = false; - converter.selector = false; - converter.semicolon = false; - converter.end_of_file = false; - converter.comment = ""; - converter.whitespace = ""; - converter.indents.push(""); - converter.options = options; - - // read line by line and process them - while(std::getline(stream, line, delim)) - { scss += process(line, converter); } - - // create mutable string - string closer = ""; - // set the end of file flag - converter.end_of_file = true; - // process to close all open blocks - scss += process(closer, converter); - - // allocate new memory on the heap - // caller has to free it after use - char * cstr = new char [scss.length()+1]; - // create a copy of the string - strcpy (cstr, scss.c_str()); - // return pointer - return &cstr[0]; - - } - // EO sass2scss - -} -// EO namespace - -// implement for c -extern "C" -{ - - char* sass2scss (const char* sass, const int options) - { - return Sass::sass2scss(sass, options); - } - -} diff --git a/sass2scss.h b/sass2scss.h deleted file mode 100644 index 97beeb47..00000000 --- a/sass2scss.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifdef __cplusplus - -#include -#include -#include -#include -#include - -#ifndef VERSION -#define VERSION "[NA]" -#endif - -// using std::string -using namespace std; - -// add namespace for c++ -namespace Sass -{ - - // define version from arguments - // compile with g++ -DVERSION="\"vX.X.X\"" - const string SASS2SCSS_VERSION = VERSION; - - // 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; - - // 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 - string comment; - // flag end of file - bool end_of_file; - // whitespace buffer - string whitespace; - // context/block stack - stack indents; - }; - - // function only available in c++ code - char* sass2scss (const 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 - char* sass2scss (const char* sass, const int options); - -#ifdef __cplusplus -} -#endif diff --git a/sass_interface.cpp b/sass_interface.cpp deleted file mode 100644 index 8a897c2f..00000000 --- a/sass_interface.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#ifdef _WIN32 -#include -#else -#include -#endif - -#include "sass_interface.h" -#include "context.hpp" -#include "inspect.hpp" - -#ifndef SASS_ERROR_HANDLING -#include "error_handling.hpp" -#endif - -#include -#include -#include -#include -#include -#include - -extern "C" { - using namespace std; - - sass_context* sass_new_context() - { return (sass_context*) calloc(1, sizeof(sass_context)); } - - void free_string_array(char ** arr, int num) { - if(!arr) - return; - - for(int i = 0; i < num; i++) { - free(arr[i]); - } - - free(arr); - } - - void sass_free_context(sass_context* ctx) - { - if (ctx->output_string) free(ctx->output_string); - if (ctx->source_map_string) free(ctx->source_map_string); - if (ctx->error_message) free(ctx->error_message); - - free_string_array(ctx->included_files, ctx->num_included_files); - - free(ctx); - } - - sass_file_context* sass_new_file_context() - { return (sass_file_context*) calloc(1, sizeof(sass_file_context)); } - - void sass_free_file_context(sass_file_context* ctx) - { - if (ctx->output_string) free(ctx->output_string); - if (ctx->source_map_string) free(ctx->source_map_string); - if (ctx->error_message) free(ctx->error_message); - - free_string_array(ctx->included_files, ctx->num_included_files); - - free(ctx); - } - - sass_folder_context* sass_new_folder_context() - { return (sass_folder_context*) calloc(1, sizeof(sass_folder_context)); } - - void sass_free_folder_context(sass_folder_context* ctx) - { - free_string_array(ctx->included_files, ctx->num_included_files); - free(ctx); - } - - void copy_strings(const std::vector& strings, char*** array, int* n) { - int num = strings.size(); - char** arr = (char**) malloc(sizeof(char*)* num); - - for(int i = 0; i < num; i++) { - arr[i] = (char*) malloc(sizeof(char) * strings[i].size() + 1); - std::copy(strings[i].begin(), strings[i].end(), arr[i]); - arr[i][strings[i].size()] = '\0'; - } - - *array = arr; - *n = num; - } - - // helper for safe access to c_ctx - const char* safe_str (const char* str) { - return str == NULL ? "" : str; - } - - int sass_compile(sass_context* c_ctx) - { - using namespace Sass; - try { - string input_path = safe_str(c_ctx->input_path); - int lastindex = input_path.find_last_of("."); - string output_path; - if (!c_ctx->output_path) { - if (input_path != "") { - output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; - } - } - else { - output_path = c_ctx->output_path; - } - Context cpp_ctx( - Context::Data().source_c_str(c_ctx->source_string) - .output_path(output_path) - .output_style((Output_Style) c_ctx->options.output_style) - .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .image_path(safe_str(c_ctx->options.image_path)) - .include_paths_c_str(c_ctx->options.include_paths) - .include_paths_array(0) - .include_paths(vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - ); - if (c_ctx->c_functions) { - struct Sass_C_Function_Descriptor* this_func_data = c_ctx->c_functions; - while (this_func_data->signature && this_func_data->function) { - cpp_ctx.c_functions.push_back(*this_func_data); - ++this_func_data; - } - } - // by checking c_ctx->input_path, implementors can pass in an empty string - c_ctx->output_string = c_ctx->input_path ? cpp_ctx.compile_string(input_path) : - cpp_ctx.compile_string(); - c_ctx->source_map_string = cpp_ctx.generate_source_map(); - c_ctx->error_message = 0; - c_ctx->error_status = 0; - - copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files, &c_ctx->num_included_files); - } - catch (Error& e) { - stringstream msg_stream; - msg_stream << e.path << ":" << e.position.line << ": " << e.message << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch(bad_alloc& ba) { - stringstream msg_stream; - msg_stream << "Unable to allocate memory: " << ba.what() << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (std::exception& e) { - stringstream msg_stream; - msg_stream << "Error: " << e.what() << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (string& e) { - stringstream msg_stream; - msg_stream << "Error: " << e << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (...) { - // couldn't find the specified file in the include paths; report an error - stringstream msg_stream; - msg_stream << "Unknown error occurred" << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - return 0; - } - - int sass_compile_file(sass_file_context* c_ctx) - { - using namespace Sass; - try { - string input_path = safe_str(c_ctx->input_path); - int lastindex = input_path.find_last_of("."); - string output_path; - if (!c_ctx->output_path) { - output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css"; - } - else { - output_path = c_ctx->output_path; - } - Context cpp_ctx( - Context::Data().entry_point(input_path) - .output_path(output_path) - .output_style((Output_Style) c_ctx->options.output_style) - .source_comments(c_ctx->options.source_comments) - .source_map_file(safe_str(c_ctx->options.source_map_file)) - .omit_source_map_url(c_ctx->options.omit_source_map_url) - .image_path(safe_str(c_ctx->options.image_path)) - .include_paths_c_str(c_ctx->options.include_paths) - .include_paths_array(0) - .include_paths(vector()) - .precision(c_ctx->options.precision ? c_ctx->options.precision : 5) - ); - if (c_ctx->c_functions) { - struct Sass_C_Function_Descriptor* this_func_data = c_ctx->c_functions; - while (this_func_data->signature && this_func_data->function) { - cpp_ctx.c_functions.push_back(*this_func_data); - ++this_func_data; - } - } - c_ctx->output_string = cpp_ctx.compile_file(); - c_ctx->source_map_string = cpp_ctx.generate_source_map(); - c_ctx->error_message = 0; - c_ctx->error_status = 0; - - copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files, &c_ctx->num_included_files); - } - catch (Error& e) { - stringstream msg_stream; - msg_stream << e.path << ":" << e.position.line << ": " << e.message << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch(bad_alloc& ba) { - stringstream msg_stream; - msg_stream << "Unable to allocate memory: " << ba.what() << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (std::exception& e) { - stringstream msg_stream; - msg_stream << "Error: " << e.what() << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (string& e) { - stringstream msg_stream; - msg_stream << "Error: " << e << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - catch (...) { - // couldn't find the specified file in the include paths; report an error - stringstream msg_stream; - msg_stream << "Unknown error occurred" << endl; - c_ctx->error_message = strdup(msg_stream.str().c_str()); - c_ctx->error_status = 1; - c_ctx->output_string = 0; - c_ctx->source_map_string = 0; - } - return 0; - } - - int sass_compile_folder(sass_folder_context* c_ctx) - { - return 1; - } - - const char* quote (const char *str, const char quotemark) { - return Sass::quote(str, quotemark).c_str(); - } - - const char* unquote (const char *str) { - return Sass::unquote(str).c_str(); - } - -} diff --git a/sass_interface.h b/sass_interface.h deleted file mode 100644 index dbad3f74..00000000 --- a/sass_interface.h +++ /dev/null @@ -1,96 +0,0 @@ -#define SASS_INTERFACE - -#include "sass.h" -#include -#include "sass2scss.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define SASS_STYLE_NESTED 0 -#define SASS_STYLE_EXPANDED 1 -#define SASS_STYLE_COMPACT 2 -#define SASS_STYLE_COMPRESSED 3 - -// 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; - // 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; - // For the image-url Sass function - const char* image_path; - // Precision for outputting fractional numbers - int precision; -}; - -struct sass_context { - const char* input_path; - const char* output_path; - const char* source_string; - char* output_string; - char* source_map_string; - struct sass_options options; - int error_status; - char* error_message; - struct Sass_C_Function_Descriptor* 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; - struct Sass_C_Function_Descriptor* 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; - struct Sass_C_Function_Descriptor* 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); - -const char* quote (const char *str, const char quotemark); -const char* unquote (const char *str); - -#ifdef __cplusplus -} -#endif diff --git a/sass_util.cpp b/sass_util.cpp deleted file mode 100644 index e6bb1176..00000000 --- a/sass_util.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef SASS_AST -#include "node.hpp" -#endif - -#include "to_string.hpp" - - -namespace Sass { - - - /* - This is the equivalent of ruby's Sass::Util.paths. - - # Return an array of all possible paths through the given arrays. - # - # @param arrs [NodeCollection>] - # @return [NodeCollection>] - # - # @example - # paths([[1, 2], [3, 4], [5]]) #=> - # # [[1, 3, 5], - # # [2, 3, 5], - # # [1, 4, 5], - # # [2, 4, 5]] - - The following is the modified version of the ruby code that was more portable to C++. You - should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. - - def paths(arrs) - // I changed the inject and maps to an iterative approach to make it easier to implement in C++ - loopStart = [[]] - - for arr in arrs do - permutations = [] - for e in arr do - for path in loopStart do - permutations.push(path + [e]) - end - end - loopStart = permutations - end - end - */ - Node paths(const Node& arrs, Context& ctx) { - To_String to_string; - - Node loopStart = Node::createCollection(); - loopStart.collection()->push_back(Node::createCollection()); - - for (NodeDeque::iterator arrsIter = arrs.collection()->begin(), arrsEndIter = arrs.collection()->end(); - arrsIter != arrsEndIter; ++arrsIter) { - - Node& arr = *arrsIter; - - Node permutations = Node::createCollection(); - - for (NodeDeque::iterator arrIter = arr.collection()->begin(), arrIterEnd = arr.collection()->end(); - arrIter != arrIterEnd; ++arrIter) { - - Node& e = *arrIter; - - for (NodeDeque::iterator loopStartIter = loopStart.collection()->begin(), loopStartIterEnd = loopStart.collection()->end(); - loopStartIter != loopStartIterEnd; ++loopStartIter) { - - Node& path = *loopStartIter; - - Node newPermutation = Node::createCollection(); - newPermutation.plus(path); - newPermutation.collection()->push_back(e); - - permutations.collection()->push_back(newPermutation); - } - } - - loopStart = permutations; - } - - return loopStart; - } - - - /* - This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten. - Sass::Util.flatten requires the number of levels to flatten, while - [].flatten doesn't and will flatten the entire array. This function - supports both. - - # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened - # - # @param arr [NodeCollection] The array to flatten - # @param n [int] The number of levels to flatten - # @return [NodeCollection] The flattened array - - The following is the modified version of the ruby code that was more portable to C++. You - should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. - - def flatten(arr, n = -1) - if n != -1 and n == 0 then - return arr - end - - flattened = [] - - for e in arr do - if e.is_a?(Array) then - flattened.concat(flatten(e, n - 1)) - else - flattened << e - end - end - - return flattened - end - */ - Node flatten(const Node& arr, Context& ctx, int n = -1) { - if (n != -1 && n == 0) { - return arr; - } - - Node flattened = Node::createCollection(); - - for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end(); - iter != iterEnd; iter++) { - Node& e = *iter; - - if (e.isCollection()) { - Node recurseFlattened = flatten(e, ctx, n - 1); - flattened.collection()->insert(flattened.collection()->end(), recurseFlattened.collection()->begin(), recurseFlattened.collection()->end()); - } else { - flattened.collection()->push_back(e); - } - } - - return flattened; - } -} \ No newline at end of file diff --git a/sass_util.hpp b/sass_util.hpp deleted file mode 100644 index bab44e9b..00000000 --- a/sass_util.hpp +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#include "node.hpp" -#include "debug.hpp" - - -namespace Sass { - - - using namespace std; - - - /* - This is for ports of functions in the Sass:Util module. - */ - - - /* - # Return a Node collection of all possible paths through the given Node collection of Node collections. - # - # @param arrs [NodeCollection>] - # @return [NodeCollection>] - # - # @example - # paths([[1, 2], [3, 4], [5]]) #=> - # # [[1, 3, 5], - # # [2, 3, 5], - # # [1, 4, 5], - # # [2, 4, 5]] - */ - Node paths(const Node& arrs, Context& ctx); - - - /* - This class is a default implementation of a Node comparator that can be passed to the lcs function below. - It uses operator== for equality comparision. It then returns one if the Nodes are equal. - */ - class DefaultLcsComparator { - public: - bool operator()(const Node& one, const Node& two, Node& out) const { - // TODO: Is this the correct C++ interpretation? - // block ||= proc {|a, b| a == b && a} - if (one == two) { - out = one; - return true; - } - - return false; - } - }; - - - typedef vector > LCSTable; - - - /* - This is the equivalent of ruby's Sass::Util.lcs_backtrace. - - # Computes a single longest common subsequence for arrays x and y. - # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS - */ - template - Node lcs_backtrace(const LCSTable& c, const Node& x, const Node& y, int i, int j, const ComparatorType& comparator) { - DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j) - - if (i == 0 || j == 0) { - DEBUG_PRINTLN(LCS, "RETURNING EMPTY") - return Node::createCollection(); - } - - NodeDeque& xChildren = *(x.collection()); - NodeDeque& yChildren = *(y.collection()); - - Node compareOut = Node::createNil(); - if (comparator(xChildren[i], yChildren[j], compareOut)) { - DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE") - Node result = lcs_backtrace(c, x, y, i - 1, j - 1, comparator); - result.collection()->push_back(compareOut); - return result; - } - - if (c[i][j - 1] > c[i - 1][j]) { - DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE") - return lcs_backtrace(c, x, y, i, j - 1, comparator); - } - - DEBUG_PRINTLN(LCS, "FINAL RETURN") - return lcs_backtrace(c, x, y, i - 1, j, comparator); - } - - - /* - This is the equivalent of ruby's Sass::Util.lcs_table. - - # Calculates the memoization table for the Least Common Subsequence algorithm. - # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS - */ - template - void lcs_table(const Node& x, const Node& y, const ComparatorType& comparator, LCSTable& out) { - DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y) - - NodeDeque& xChildren = *(x.collection()); - NodeDeque& yChildren = *(y.collection()); - - LCSTable c(xChildren.size(), vector(yChildren.size())); - - // These shouldn't be necessary since the vector will be initialized to 0 already. - // x.size.times {|i| c[i][0] = 0} - // y.size.times {|j| c[0][j] = 0} - - for (size_t i = 1; i < xChildren.size(); i++) { - for (size_t j = 1; j < yChildren.size(); j++) { - Node compareOut = Node::createNil(); - - if (comparator(xChildren[i], yChildren[j], compareOut)) { - c[i][j] = c[i - 1][j - 1] + 1; - } else { - c[i][j] = max(c[i][j - 1], c[i - 1][j]); - } - } - } - - out = c; - } - - - /* - This is the equivalent of ruby's Sass::Util.lcs. - - # Computes a single longest common subsequence for `x` and `y`. - # If there are more than one longest common subsequences, - # the one returned is that which starts first in `x`. - - # @param x [NodeCollection] - # @param y [NodeCollection] - # @comparator An equality check between elements of `x` and `y`. - # @return [NodeCollection] The LCS - - http://en.wikipedia.org/wiki/Longest_common_subsequence_problem - */ - template - Node lcs(Node& x, Node& y, const ComparatorType& comparator, Context& ctx) { - DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y) - - Node newX = Node::createCollection(); - newX.collection()->push_back(Node::createNil()); - newX.plus(x); - - Node newY = Node::createCollection(); - newY.collection()->push_back(Node::createNil()); - newY.plus(y); - - LCSTable table; - lcs_table(newX, newY, comparator, table); - - return lcs_backtrace(table, newX, newY, newX.collection()->size() - 1, newY.collection()->size() - 1, comparator); - } - - - /* - This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten. - Sass::Util.flatten requires the number of levels to flatten, while - [].flatten doesn't and will flatten the entire array. This function - supports both. - - # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened - # - # @param arr [NodeCollection] The array to flatten - # @param n [int] The number of levels to flatten - # @return [NodeCollection] The flattened array - */ - Node flatten(const Node& arr, Context& ctx, int n = -1); - - - /* - This is the equivalent of ruby's Sass::Util.group_by_to_a. - - # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed - # order. Unlike [#hash_to_a], the resulting order isn't sorted key order; - # instead, it's the same order as `#group_by` has under Ruby 1.9 (key - # appearance order). - # - # @param enum [Enumerable] - # @return [Array<[Object, Array]>] An array of pairs. - - TODO: update @param and @return once I know what those are. - - The following is the modified version of the ruby code that was more portable to C++. You - should be able to drop it into ruby 3.2.19 and get the same results from ruby sass. - - def group_by_to_a(enum, &block) - order = {} - - arr = [] - - grouped = {} - - for e in enum do - key = block[e] - unless order.include?(key) - order[key] = order.size - end - - if not grouped.has_key?(key) then - grouped[key] = [e] - else - grouped[key].push(e) - end - end - - grouped.each do |key, vals| - arr[order[key]] = [key, vals] - end - - arr - end - - */ - template - void group_by_to_a(vector& enumeration, KeyFunctorType& keyFunc, vector > >& arr /*out*/) { - - map order; - - map > grouped; - - for (typename vector::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) { - EnumType& e = *enumIter; - - KeyType key = keyFunc(e); - - if (grouped.find(key) == grouped.end()) { - order.insert(make_pair(order.size(), key)); - - vector newCollection; - newCollection.push_back(e); - grouped.insert(make_pair(key, newCollection)); - } else { - vector& collection = grouped.at(key); - collection.push_back(e); - } - } - - for (unsigned int index = 0; index < order.size(); index++) { - KeyType& key = order.at(index); - vector& values = grouped.at(key); - - pair > grouping = make_pair(key, values); - - arr.push_back(grouping); - } - } - - -} diff --git a/script/bootstrap b/script/bootstrap deleted file mode 100755 index ef1df334..00000000 --- a/script/bootstrap +++ /dev/null @@ -1,10 +0,0 @@ -#!/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 diff --git a/script/branding b/script/branding deleted file mode 100755 index cd8cb2a5..00000000 --- a/script/branding +++ /dev/null @@ -1,10 +0,0 @@ -#! /bin/bash - -echo " " -echo " _ ___ ____ ____ _ ____ ____ " -echo "| | |_ _| __ ) ___| / \ / ___/ ___| " -echo "| | | || _ \___ \ / _ \ \___ \___ \ " -echo "| |___ | || |_) |__) / ___ \ ___) |__) |" -echo "|_____|___|____/____/_/ \_\____/____/ " -echo " " - diff --git a/script/cibuild b/script/cibuild deleted file mode 100755 index c0e53826..00000000 --- a/script/cibuild +++ /dev/null @@ -1,50 +0,0 @@ -#!/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$TRAVIS_BUILD_DIR" == "x" ]; then export TRAVIS_BUILD_DIR=$(pwd)/build; 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 - -if [ "x$CXX" == "xclang++" ]; then - COVERAGE="--enable-coverage" - export EXTRA_CFLAGS="-O0 --coverage" - export EXTRA_LDFLAGS="-O0 --coverage" -else - COVERAGE="--enable-coverage" - export EXTRA_CFLAGS="-O0 --coverage" - export EXTRA_LDFLAGS="-O0 --coverage" -fi - -export MAKE_OPTS="-j5 V=1" - -if [ "x$AUTOTOOLS" == "xyes" ]; then - - echo -en 'travis_fold:start:configure\r' - autoreconf -i - ./configure --enable-tests $COVERAGE \ - --with-sassc-dir=$SASS_SASSC_PATH \ - --with-sass-spec-dir=$SASS_SPEC_PATH \ - --prefix=$TRAVIS_BUILD_DIR - echo -en 'travis_fold:end:configure\r' - - make $MAKE_OPTS install - - # sassc expects file to be in our root - echo "moving libsass.a in-place for sassc" - echo cp $TRAVIS_BUILD_DIR/lib/libsass.a $(pwd) - cp $TRAVIS_BUILD_DIR/lib/libsass.a $(pwd) - -else - - make $MAKE_OPTS $SASS_SASSC_PATH/bin/sassc - -fi - -script/spec diff --git a/script/coveralls-debug b/script/coveralls-debug deleted file mode 100755 index cb4f77ed..00000000 --- a/script/coveralls-debug +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use JSON; -use File::Slurp; -my $json = JSON->new; -my $file = read_file('coveralls.json', { binmode => ':utf8' }); -my $rv = $json->decode($file); -my $sources = $rv->{'source_files'}; -print STDERR join ", ", keys %{$rv}, "\n"; -foreach my $source (sort { - $a->{'name'} cmp $b->{'name'} -} @{$sources}) -{ - my $sum = 0; - my $undefs = 0; - my $coverages = $source->{'coverage'}; - foreach my $coverage (@{$coverages}) - { - if (defined $coverage) - { $sum += $coverage } - else { $undefs ++; } - } - if ($sum > 0) - { - print STDERR $source->{'name'}; - print STDERR " [sum: $sum]"; - print STDERR " [undefs: $undefs]"; - print STDERR "\n"; - } -} diff --git a/script/install-compiler b/script/install-compiler deleted file mode 100755 index c8bbe8c4..00000000 --- a/script/install-compiler +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -if [ "x$CXX" = "xg++" ]; then - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -qq - sudo apt-get install -qq g++-4.8 - sudo rm /usr/bin/cpp - sudo rm /usr/bin/gcc - sudo rm /usr/bin/c++ - sudo rm /usr/bin/g++ - sudo rm /usr/bin/gcov - sudo ln -s /usr/bin/cpp-4.8 /usr/bin/cpp - sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc - sudo ln -s /usr/bin/c++-4.8 /usr/bin/c++ - sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++ - sudo ln -s /usr/bin/gcov-4.8 /usr/bin/gcov -fi diff --git a/script/spec b/script/spec deleted file mode 100755 index d0b864a1..00000000 --- a/script/spec +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -script/bootstrap - -make $MAKE_OPTS test_build diff --git a/setup.py b/setup.py index 39539fd8..6d918402 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ use_setuptools() from setuptools import Extension, setup +LIBSASS_DIR = 'libsass' MAKEFILE_SOURCES_LIST_RE = re.compile(r''' (?: ^ | \n ) (?: libsass_la_ )? SOURCES [ \t]* = [ \t]* @@ -29,7 +30,9 @@ libsass_sources = set() -for makefilename in 'Makefile', 'Makefile.am': +for makefilename in [ + os.path.join(LIBSASS_DIR, 'Makefile'), + os.path.join(LIBSASS_DIR, 'Makefile.am')]: with open(makefilename) as makefile: sources_match = MAKEFILE_SOURCES_LIST_RE.search(makefile.read()) sources_list = sources_match.group('sources').replace('\\\n', ' ') @@ -37,12 +40,14 @@ libsass_sources = list(libsass_sources) libsass_headers = [ - 'sass_interface.h', 'sass.h', 'win32/unistd.h' + os.path.join(LIBSASS_DIR, 'sass_interface.h'), + os.path.join(LIBSASS_DIR, 'sass.h'), + os.path.join(LIBSASS_DIR, 'win32', 'unistd.h'), ] libsass_headers.extend(glob.glob('*.hpp')) include_dirs = ['utf8'] sources = ['pysass.cpp'] -sources.extend(libsass_sources) +sources.extend([os.path.join(LIBSASS_DIR, s) for s in libsass_sources]) if sys.platform == 'win32': from distutils.msvc9compiler import get_build_version @@ -93,6 +98,8 @@ def spawn(self, cmd): sass_extension = Extension( '_sass', sources, + library_dirs=['./libsass'], + include_dirs=['.', 'libsass'], depends=libsass_headers, extra_compile_args=['-c', '-O2'] + flags, extra_link_args=link_flags, diff --git a/source_map.cpp b/source_map.cpp deleted file mode 100644 index 675d73ed..00000000 --- a/source_map.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "source_map.hpp" - -#ifndef SASS_CONTEXT -#include "context.hpp" -#endif - -#include -#include -#include -#include - -namespace Sass { - using std::ptrdiff_t; - SourceMap::SourceMap(const string& file) : current_position(Position(1, 1)), file(file) { } - - // taken from http://stackoverflow.com/a/7725289/1550314 - std::string encodeJsonString(const std::string& input) { - std::ostringstream ss; - for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) { - switch (*iter) { - case '\\': ss << "\\\\"; break; - case '"': ss << "\\\""; break; - case '\b': ss << "\\b"; break; - case '\f': ss << "\\f"; break; - case '\n': ss << "\\n"; break; - case '\r': ss << "\\r"; break; - case '\t': ss << "\\t"; break; - // is a legal escape in JSON - case '/': ss << "\\/"; break; - default: ss << *iter; break; - } - } - - return ss.str(); - } - - string SourceMap::generate_source_map() { - string result = "{\n"; - result += " \"version\": 3,\n"; - result += " \"file\": \"" + encodeJsonString(file) + "\",\n"; - result += " \"sources\": ["; - for (size_t i = 0; i < files.size(); ++i) { - result+="\"" + encodeJsonString(files[i]) + "\","; - } - if (!files.empty()) result.erase(result.length() - 1); - result += "],\n"; - result += " \"names\": [],\n"; - result += " \"mappings\": \"" + serialize_mappings() + "\"\n"; - result += "}"; - - return result; - } - - - string SourceMap::serialize_mappings() { - string result = ""; - - size_t previous_generated_line = 0; - size_t previous_generated_column = 0; - size_t previous_original_line = 0; - size_t previous_original_column = 0; - size_t previous_original_file = 0; - for (size_t i = 0; i < mappings.size(); ++i) { - const size_t generated_line = mappings[i].generated_position.line - 1; - const size_t generated_column = mappings[i].generated_position.column - 1; - const size_t original_line = mappings[i].original_position.line - 1; - const size_t original_column = mappings[i].original_position.column - 1; - const size_t original_file = mappings[i].original_position.file - 1; - - if (generated_line != previous_generated_line) { - previous_generated_column = 0; - while (generated_line != previous_generated_line) { - result += ";"; - previous_generated_line += 1; - } - } - else { - if (i > 0) result += ","; - } - - // generated column - result += base64vlq.encode(generated_column - previous_generated_column); - previous_generated_column = generated_column; - // file - result += base64vlq.encode(original_file - previous_original_file); - previous_original_file = original_file; - // source line - result += base64vlq.encode(original_line - previous_original_line); - previous_original_line = original_line; - // source column - result += base64vlq.encode(original_column - previous_original_column); - previous_original_column = original_column; - } - - return result; - } - - void SourceMap::remove_line() - { - current_position.line -= 1; - current_position.column = 1; - } - - void SourceMap::update_column(const string& str) - { - const ptrdiff_t new_line_count = std::count(str.begin(), str.end(), '\n'); - current_position.line += new_line_count; - if (new_line_count >= 1) { - current_position.column = str.size() - str.find_last_of('\n'); - } else { - current_position.column += str.size(); - } - } - - void SourceMap::add_mapping(AST_Node* node) - { - mappings.push_back(Mapping(node->position(), current_position)); - } - -} diff --git a/source_map.hpp b/source_map.hpp deleted file mode 100644 index 258c60b1..00000000 --- a/source_map.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#define SASS_SOURCE_MAP - -#include - -#ifndef SASS_MAPPING -#include "mapping.hpp" -#endif - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#ifndef SASSS_BASE64VLQ -#include "base64vlq.hpp" -#endif - - - -namespace Sass { - using std::vector; - - struct Context; - - class SourceMap { - - public: - vector files; - - SourceMap(const string& file); - - void remove_line(); - void update_column(const string& str); - void add_mapping(AST_Node* node); - - string generate_source_map(); - - private: - - string serialize_mappings(); - - vector mappings; - Position current_position; - string file; - Base64VLQ base64vlq; - }; - -} diff --git a/subset_map.hpp b/subset_map.hpp deleted file mode 100644 index e0965795..00000000 --- a/subset_map.hpp +++ /dev/null @@ -1,145 +0,0 @@ -#define SASS_SUBSET_MAP - -#include -#include -#include -#include -#include -#include -#include - -// using namespace std; - -// template -// string vector_to_string(vector v) -// { -// stringstream buffer; -// buffer << "["; - -// if (!v.empty()) -// { buffer << v[0]; } -// else -// { buffer << "]"; } - -// if (v.size() == 1) -// { buffer << "]"; } -// else -// { -// for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i]; -// buffer << "]"; -// } - -// return buffer.str(); -// } - -// template -// string set_to_string(set v) -// { -// stringstream buffer; -// buffer << "["; -// typename set::iterator i = v.begin(); -// if (!v.empty()) -// { buffer << *i; } -// else -// { buffer << "]"; } - -// if (v.size() == 1) -// { buffer << "]"; } -// else -// { -// for (++i; i != v.end(); ++i) buffer << ", " << *i; -// buffer << "]"; -// } - -// return buffer.str(); -// } - -namespace Sass { - using namespace std; - - template - struct triple { - F first; - S second; - T third; - - triple(const F& f, const S& s, const T& t) : first(f), second(s), third(t) { } - }; - - template - triple make_triple(const F& f, const S& s, const T& t) - { return triple(f, s, t); } - - template - class Subset_Map { - private: - vector values_; - map, set, size_t> > > hash_; - public: - void put(const vector& s, const V& value); - vector > > get_kv(const vector& s); - vector get_v(const vector& s); - bool empty() { return values_.empty(); } - void clear() { values_.clear(); hash_.clear(); } - }; - - template - void Subset_Map::put(const vector& s, const V& value) - { - if (s.empty()) throw "internal error: subset map keys may not be empty"; - size_t index = values_.size(); - values_.push_back(value); - set ss; - for (size_t i = 0, S = s.size(); i < S; ++i) - { ss.insert(s[i]); } - for (size_t i = 0, S = s.size(); i < S; ++i) - { - hash_[s[i]]; - hash_[s[i]].push_back(make_triple(s, ss, index)); - } - } - - template - vector > > Subset_Map::get_kv(const vector& s) - { - vector sorted = s; - sort(sorted.begin(), sorted.end()); - vector > > indices; - for (size_t i = 0, S = s.size(); i < S; ++i) { - // cerr << "looking for " << s[i] << endl; - if (!hash_.count(s[i])) { - // cerr << "didn't find " << s[i] << endl; - continue; - } - vector, set, size_t> > subsets = hash_[s[i]]; - // cerr << "length of subsets: " << subsets.size() << endl; - for (size_t j = 0, T = subsets.size(); j < T; ++j) { - if (!includes(sorted.begin(), sorted.end(), subsets[j].second.begin(), subsets[j].second.end())) { - // cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << endl; - continue; - } - indices.push_back(make_pair(subsets[j].third, subsets[j].first)); - // cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << endl; - } - } - sort(indices.begin(), indices.end()); - typename vector > >::iterator indices_end = unique(indices.begin(), indices.end()); - indices.resize(distance(indices.begin(), indices_end)); - - vector > > results; - for (size_t i = 0, S = indices.size(); i < S; ++i) { - results.push_back(make_pair(values_[indices[i].first], indices[i].second)); - } - return results; - } - - template - vector Subset_Map::get_v(const vector& s) - { - vector > > kvs = get_kv(s); - vector results; - for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first); - return results; - } - -} diff --git a/support/libsass.pc.in b/support/libsass.pc.in deleted file mode 100644 index d201bfaa..00000000 --- a/support/libsass.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: libsass -URL: https://github.com/sass/libsass -Description: A C implementation of a Sass compiler -Version: @VERSION@ -Libs: -L${libdir} -lsass -Cflags: -I${includedir} diff --git a/test-driver b/test-driver deleted file mode 100755 index 32bf39e8..00000000 --- a/test-driver +++ /dev/null @@ -1,127 +0,0 @@ -#! /bin/sh -# test-driver - basic testsuite driver script. - -scriptversion=2012-06-27.10; # UTC - -# 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 -# . - -# Make unconditional expansion of undefined variables an error. This -# helps a lot in preventing typo-related bugs. -set -u - -usage_error () -{ - echo "$0: $*" >&2 - print_usage >&2 - exit 2 -} - -print_usage () -{ - cat <$log_file 2>&1 -estatus=$? -if test $enable_hard_errors = no && test $estatus -eq 99; then - estatus=1 -fi - -case $estatus:$expect_failure in - 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; - 0:*) col=$grn res=PASS recheck=no gcopy=no;; - 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; - 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; - *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; - *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -esac - -# Report outcome to console. -echo "${col}${res}${std}: $test_name" - -# Register the test result, and other relevant metadata. -echo ":test-result: $res" > $trs_file -echo ":global-test-result: $res" >> $trs_file -echo ":recheck: $recheck" >> $trs_file -echo ":copy-in-global-log: $gcopy" >> $trs_file - -# 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: diff --git a/test/test_node.cpp b/test/test_node.cpp deleted file mode 100644 index fb6ee2c9..00000000 --- a/test/test_node.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#include "node.hpp" -#include "to_string.hpp" -#include "parser.hpp" - - -#define STATIC_ARRAY_SIZE(array) (sizeof((array))/sizeof((array[0]))) - - -namespace Sass { - - Context ctx = Context::Data(); - - To_String to_string; - - - const char* const ROUNDTRIP_TESTS[] = { - NULL, - "~", - "CMPD", - "~ CMPD", - "CMPD >", - "> > CMPD", - "CMPD ~ ~", - "> + CMPD1.CMPD2 > ~", - "> + CMPD1.CMPD2 CMPD3.CMPD4 > ~", - "+ CMPD1 CMPD2 ~ CMPD3 + CMPD4 > CMPD5 > ~" - }; - - - - static Complex_Selector* createComplexSelector(string src) { - string temp(src); - temp += ";"; - return (*Parser::from_c_str(temp.c_str(), ctx, "", Position()).parse_selector_group())[0]; - } - - - void roundtripTest(const char* toTest) { - - // Create the initial selector - - Complex_Selector* pOrigSelector = NULL; - if (toTest) { - pOrigSelector = createComplexSelector(toTest); - } - - string expected(pOrigSelector ? pOrigSelector->perform(&to_string) : "NULL"); - - - // Roundtrip the selector into a node and back - - Node node = complexSelectorToNode(pOrigSelector, ctx); - - stringstream nodeStringStream; - nodeStringStream << node; - string nodeString = nodeStringStream.str(); - cout << "ASNODE: " << node << endl; - - Complex_Selector* pNewSelector = nodeToComplexSelector(node, ctx); - - // Show the result - - string result(pNewSelector ? pNewSelector->perform(&to_string) : "NULL"); - - cout << "SELECTOR: " << expected << endl; - cout << "NEW SELECTOR: " << result << endl; - - - // Test that they are equal using the equality operator - - assert( (!pOrigSelector && !pNewSelector ) || (pOrigSelector && pNewSelector) ); - if (pOrigSelector) { - assert( *pOrigSelector == *pNewSelector ); - } - - - // Test that they are equal by comparing the string versions of the selectors - - assert(expected == result); - - } - - - int main() { - for (int index = 0; index < STATIC_ARRAY_SIZE(ROUNDTRIP_TESTS); index++) { - const char* const toTest = ROUNDTRIP_TESTS[index]; - cout << "\nINPUT STRING: " << (toTest ? toTest : "NULL") << endl; - roundtripTest(toTest); - } - - cout << "\nTesting Done.\n"; - } - - -} \ No newline at end of file diff --git a/test/test_paths.cpp b/test/test_paths.cpp deleted file mode 100644 index 3c72595b..00000000 --- a/test/test_paths.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include "../paths.hpp" - -using namespace std; -using namespace Sass; - -template -vector& operator<<(vector& v, const T& e) -{ - v.push_back(e); - return v; -} - -int main() -{ - vector v1, v2, v3; - v1 << 1 << 2; - v2 << 3; - v3 << 4 << 5 << 6; - - vector > ss; - ss << v1 << v2 << v3; - - vector > ps = paths(ss); - for (size_t i = 0, S = ps.size(); i < S; ++i) { - cout << vector_to_string(ps[i]) << endl; - } - return 0; -} diff --git a/test/test_selector_difference.cpp b/test/test_selector_difference.cpp deleted file mode 100644 index 9f6e4652..00000000 --- a/test/test_selector_difference.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "../ast.hpp" -#include "../context.hpp" -#include "../parser.hpp" -#include "../to_string.hpp" -#include -#include - -using namespace std; -using namespace Sass; - -Context ctx = Context::Data(); -To_String to_string; - -Compound_Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } - -void diff(string s, string t) -{ - cout << s << " - " << t << " = " << selector(s + ";")->minus(selector(t + ";"), ctx)->perform(&to_string) << endl; -} - -int main() -{ - diff(".a.b.c", ".c.b"); - diff(".a.b.c", ".fludge.b"); - - return 0; -} diff --git a/test/test_specificity.cpp b/test/test_specificity.cpp deleted file mode 100644 index c0e15a7a..00000000 --- a/test/test_specificity.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "../ast.hpp" -#include "../context.hpp" -#include "../parser.hpp" -#include "../to_string.hpp" -#include -#include - -using namespace std; -using namespace Sass; - -Context ctx = Context::Data(); -To_String to_string; - -Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_group(); } - -void spec(string sel) -{ cout << sel << "\t::\t" << selector(sel + ";")->specificity() << endl; } - -int main() -{ - spec("foo bar hux"); - spec(".foo .bar hux"); - spec("#foo .bar[hux='mux']"); - spec("a b c d e f"); - - return 0; -} diff --git a/test/test_subset_map.cpp b/test/test_subset_map.cpp deleted file mode 100644 index 14994210..00000000 --- a/test/test_subset_map.cpp +++ /dev/null @@ -1,472 +0,0 @@ -#include -#include -#include -#include "../subset_map.hpp" - -Subset_Map ssm; - -string toString(vector v); -string toString(vector>> v); -void assertEqual(string sExpected, string sResult); - -void setup() { - ssm.clear(); - - //@ssm[Set[1, 2]] = "Foo" - vector s1; - s1.push_back("1"); - s1.push_back("2"); - ssm.put(s1, "Foo"); - - //@ssm[Set["fizz", "fazz"]] = "Bar" - vector s2; - s2.push_back("fizz"); - s2.push_back("fazz"); - ssm.put(s2, "Bar"); - - //@ssm[Set[:foo, :bar]] = "Baz" - vector s3; - s3.push_back(":foo"); - s3.push_back(":bar"); - ssm.put(s3, "Baz"); - - //@ssm[Set[:foo, :bar, :baz]] = "Bang" - vector s4; - s4.push_back(":foo"); - s4.push_back(":bar"); - s4.push_back(":baz"); - ssm.put(s4, "Bang"); - - //@ssm[Set[:bip, :bop, :blip]] = "Qux" - vector s5; - s5.push_back(":bip"); - s5.push_back(":bop"); - s5.push_back(":blip"); - ssm.put(s5, "Qux"); - - //@ssm[Set[:bip, :bop]] = "Thram" - vector s6; - s6.push_back(":bip"); - s6.push_back(":bop"); - ssm.put(s6, "Thram"); -} - -void testEqualKeys() { - cout << "testEqualKeys" << endl; - - //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2]) - vector k1; - k1.push_back("1"); - k1.push_back("2"); - assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); - - //assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz"]) - vector k2; - k2.push_back("fizz"); - k2.push_back("fazz"); - assertEqual("[[Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); - - cout << endl; -} - -void testSubsetKeys() { - cout << "testSubsetKeys" << endl; - - //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[1, 2, "fuzz"]) - vector k1; - k1.push_back("1"); - k1.push_back("2"); - k1.push_back("fuzz"); - assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); - - //assert_equal [["Bar", Set["fizz", "fazz"]]], @ssm.get(Set["fizz", "fazz", 3]) - vector k2; - k2.push_back("fizz"); - k2.push_back("fazz"); - k2.push_back("3"); - assertEqual("[[Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); - - cout << endl; -} - -void testSupersetKeys() { - cout << "testSupersetKeys" << endl; - - //assert_equal [], @ssm.get(Set[1]) - vector k1; - k1.push_back("1"); - assertEqual("[]", toString(ssm.get_kv(k1))); - - //assert_equal [], @ssm.get(Set[2]) - vector k2; - k2.push_back("2"); - assertEqual("[]", toString(ssm.get_kv(k2))); - - //assert_equal [], @ssm.get(Set["fizz"]) - vector k3; - k3.push_back("fizz"); - assertEqual("[]", toString(ssm.get_kv(k3))); - - //assert_equal [], @ssm.get(Set["fazz"]) - vector k4; - k4.push_back("fazz"); - assertEqual("[]", toString(ssm.get_kv(k4))); - - cout << endl; -} - -void testDisjointKeys() { - cout << "testDisjointKeys" << endl; - - //assert_equal [], @ssm.get(Set[3, 4]) - vector k1; - k1.push_back("3"); - k1.push_back("4"); - assertEqual("[]", toString(ssm.get_kv(k1))); - - //assert_equal [], @ssm.get(Set["fuzz", "frizz"]) - vector k2; - k2.push_back("fuzz"); - k2.push_back("frizz"); - assertEqual("[]", toString(ssm.get_kv(k2))); - - //assert_equal [], @ssm.get(Set["gran", 15]) - vector k3; - k3.push_back("gran"); - k3.push_back("15"); - assertEqual("[]", toString(ssm.get_kv(k3))); - - cout << endl; -} - -void testSemiDisjointKeys() { - cout << "testSemiDisjointKeys" << endl; - - //assert_equal [], @ssm.get(Set[2, 3]) - vector k1; - k1.push_back("2"); - k1.push_back("3"); - assertEqual("[]", toString(ssm.get_kv(k1))); - - //assert_equal [], @ssm.get(Set["fizz", "fuzz"]) - vector k2; - k2.push_back("fizz"); - k2.push_back("fuzz"); - assertEqual("[]", toString(ssm.get_kv(k2))); - - //assert_equal [], @ssm.get(Set[1, "fazz"]) - vector k3; - k3.push_back("1"); - k3.push_back("fazz"); - assertEqual("[]", toString(ssm.get_kv(k3))); - - cout << endl; -} - -void testEmptyKeySet() { - cout << "testEmptyKeySet" << endl; - - //assert_raises(ArgumentError) {@ssm[Set[]] = "Fail"} - vector s1; - try { - ssm.put(s1, "Fail"); - } - catch (const char* &e) { - assertEqual("internal error: subset map keys may not be empty", e); - } -} - -void testEmptyKeyGet() { - cout << "testEmptyKeyGet" << endl; - - //assert_equal [], @ssm.get(Set[]) - vector k1; - assertEqual("[]", toString(ssm.get_kv(k1))); - - cout << endl; -} -void testMultipleSubsets() { - cout << "testMultipleSubsets" << endl; - - //assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, "fizz", "fazz"]) - vector k1; - k1.push_back("1"); - k1.push_back("2"); - k1.push_back("fizz"); - k1.push_back("fazz"); - assertEqual("[[Foo, Set[1, 2]], [Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k1))); - - //assert_equal [["Foo", Set[1, 2]], ["Bar", Set["fizz", "fazz"]]], @ssm.get(Set[1, 2, 3, "fizz", "fazz", "fuzz"]) - vector k2; - k2.push_back("1"); - k2.push_back("2"); - k2.push_back("3"); - k2.push_back("fizz"); - k2.push_back("fazz"); - k2.push_back("fuzz"); - assertEqual("[[Foo, Set[1, 2]], [Bar, Set[fizz, fazz]]]", toString(ssm.get_kv(k2))); - - //assert_equal [["Baz", Set[:foo, :bar]]], @ssm.get(Set[:foo, :bar]) - vector k3; - k3.push_back(":foo"); - k3.push_back(":bar"); - assertEqual("[[Baz, Set[:foo, :bar]]]", toString(ssm.get_kv(k3))); - - //assert_equal [["Baz", Set[:foo, :bar]], ["Bang", Set[:foo, :bar, :baz]]], @ssm.get(Set[:foo, :bar, :baz]) - vector k4; - k4.push_back(":foo"); - k4.push_back(":bar"); - k4.push_back(":baz"); - assertEqual("[[Baz, Set[:foo, :bar]], [Bang, Set[:foo, :bar, :baz]]]", toString(ssm.get_kv(k4))); - - cout << endl; -} -void testBracketBracket() { - cout << "testBracketBracket" << endl; - - //assert_equal ["Foo"], @ssm[Set[1, 2, "fuzz"]] - vector k1; - k1.push_back("1"); - k1.push_back("2"); - k1.push_back("fuzz"); - assertEqual("[Foo]", toString(ssm.get_v(k1))); - - //assert_equal ["Baz", "Bang"], @ssm[Set[:foo, :bar, :baz]] - vector k2; - k2.push_back(":foo"); - k2.push_back(":bar"); - k2.push_back(":baz"); - assertEqual("[Baz, Bang]", toString(ssm.get_v(k2))); - - cout << endl; -} - -void testKeyOrder() { - cout << "testEqualKeys" << endl; - - //assert_equal [["Foo", Set[1, 2]]], @ssm.get(Set[2, 1]) - vector k1; - k1.push_back("2"); - k1.push_back("1"); - assertEqual("[[Foo, Set[1, 2]]]", toString(ssm.get_kv(k1))); - - cout << endl; -} - -void testOrderPreserved() { - cout << "testOrderPreserved" << endl; - //@ssm[Set[10, 11, 12]] = 1 - vector s1; - s1.push_back("10"); - s1.push_back("11"); - s1.push_back("12"); - ssm.put(s1, "1"); - - //@ssm[Set[10, 11]] = 2 - vector s2; - s2.push_back("10"); - s2.push_back("11"); - ssm.put(s2, "2"); - - //@ssm[Set[11]] = 3 - vector s3; - s3.push_back("11"); - ssm.put(s3, "3"); - - //@ssm[Set[11, 12]] = 4 - vector s4; - s4.push_back("11"); - s4.push_back("12"); - ssm.put(s4, "4"); - - //@ssm[Set[9, 10, 11, 12, 13]] = 5 - vector s5; - s5.push_back("9"); - s5.push_back("10"); - s5.push_back("11"); - s5.push_back("12"); - s5.push_back("13"); - ssm.put(s5, "5"); - - //@ssm[Set[10, 13]] = 6 - vector s6; - s6.push_back("10"); - s6.push_back("13"); - ssm.put(s6, "6"); - - //assert_equal([[1, Set[10, 11, 12]], [2, Set[10, 11]], [3, Set[11]], [4, Set[11, 12]], [5, Set[9, 10, 11, 12, 13]], [6, Set[10, 13]]], @ssm.get(Set[9, 10, 11, 12, 13])) - vector k1; - k1.push_back("9"); - k1.push_back("10"); - k1.push_back("11"); - k1.push_back("12"); - k1.push_back("13"); - assertEqual("[[1, Set[10, 11, 12]], [2, Set[10, 11]], [3, Set[11]], [4, Set[11, 12]], [5, Set[9, 10, 11, 12, 13]], [6, Set[10, 13]]]", toString(ssm.get_kv(k1))); - - cout << endl; -} -void testMultipleEqualValues() { - cout << "testMultipleEqualValues" << endl; - //@ssm[Set[11, 12]] = 1 - vector s1; - s1.push_back("11"); - s1.push_back("12"); - ssm.put(s1, "1"); - - //@ssm[Set[12, 13]] = 2 - vector s2; - s2.push_back("12"); - s2.push_back("13"); - ssm.put(s2, "2"); - - //@ssm[Set[13, 14]] = 1 - vector s3; - s3.push_back("13"); - s3.push_back("14"); - ssm.put(s3, "1"); - - //@ssm[Set[14, 15]] = 1 - vector s4; - s4.push_back("14"); - s4.push_back("15"); - ssm.put(s4, "1"); - - //assert_equal([[1, Set[11, 12]], [2, Set[12, 13]], [1, Set[13, 14]], [1, Set[14, 15]]], @ssm.get(Set[11, 12, 13, 14, 15])) - vector k1; - k1.push_back("11"); - k1.push_back("12"); - k1.push_back("13"); - k1.push_back("14"); - k1.push_back("15"); - assertEqual("[[1, Set[11, 12]], [2, Set[12, 13]], [1, Set[13, 14]], [1, Set[14, 15]]]", toString(ssm.get_kv(k1))); - - cout << endl; -} - -int main() -{ - vector s1; - s1.push_back("1"); - s1.push_back("2"); - - vector s2; - s2.push_back("2"); - s2.push_back("3"); - - vector s3; - s3.push_back("3"); - s3.push_back("4"); - - ssm.put(s1, "value1"); - ssm.put(s2, "value2"); - ssm.put(s3, "value3"); - - vector s4; - s4.push_back("1"); - s4.push_back("2"); - s4.push_back("3"); - - vector > > fetched(ssm.get_kv(s4)); - - cout << "PRINTING RESULTS:" << endl; - for (size_t i = 0, S = fetched.size(); i < S; ++i) { - cout << fetched[i].first << endl; - } - - Subset_Map ssm2; - ssm2.put(s1, "foo"); - ssm2.put(s2, "bar"); - ssm2.put(s4, "hux"); - - vector > > fetched2(ssm2.get_kv(s4)); - - cout << endl << "PRINTING RESULTS:" << endl; - for (size_t i = 0, S = fetched2.size(); i < S; ++i) { - cout << fetched2[i].first << endl; - } - - cout << "TRYING ON A SELECTOR-LIKE OBJECT" << endl; - - Subset_Map sel_ssm; - vector target; - target.push_back("desk"); - target.push_back(".wood"); - - vector actual; - actual.push_back("desk"); - actual.push_back(".wood"); - actual.push_back(".mine"); - - sel_ssm.put(target, "has-aquarium"); - vector > > fetched3(sel_ssm.get_kv(actual)); - cout << "RESULTS:" << endl; - for (size_t i = 0, S = fetched3.size(); i < S; ++i) { - cout << fetched3[i].first << endl; - } - - cout << endl; - - // BEGIN PORTED RUBY TESTS FROM /test/sass/util/subset_map_test.rb - - setup(); - testEqualKeys(); - testSubsetKeys(); - testSupersetKeys(); - testDisjointKeys(); - testSemiDisjointKeys(); - testEmptyKeySet(); - testEmptyKeyGet(); - testMultipleSubsets(); - testBracketBracket(); - testKeyOrder(); - - setup(); - testOrderPreserved(); - - setup(); - testMultipleEqualValues(); - - return 0; -} - -string toString(vector>> v) -{ - stringstream buffer; - buffer << "["; - for (size_t i = 0, S = v.size(); i < S; ++i) { - buffer << "[" << v[i].first; - buffer << ", Set["; - for (size_t j = 0, S = v[i].second.size(); j < S; ++j) { - buffer << v[i].second[j]; - if (j < S-1) { - buffer << ", "; - } - } - buffer << "]]"; - if (i < S-1) { - buffer << ", "; - } - } - buffer << "]"; - return buffer.str(); -} - -string toString(vector v) -{ - stringstream buffer; - buffer << "["; - for (size_t i = 0, S = v.size(); i < S; ++i) { - buffer << v[i]; - if (i < S-1) { - buffer << ", "; - } - } - buffer << "]"; - return buffer.str(); -} - -void assertEqual(string sExpected, string sResult) { - cout << "Expected: " << sExpected << endl; - cout << "Result: " << sResult << endl; - assert(sExpected == sResult); -} diff --git a/test/test_superselector.cpp b/test/test_superselector.cpp deleted file mode 100644 index 48c85355..00000000 --- a/test/test_superselector.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "../ast.hpp" -#include "../context.hpp" -#include "../parser.hpp" -#include "../to_string.hpp" -#include - -using namespace Sass; - -Context ctx = Context(Context::Data()); -To_String to_string; - -Compound_Selector* compound_selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } - -Complex_Selector* complex_selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_combination(); } - -void check_compound(string s1, string s2) -{ - cout << "Is " - << s1 - << " a superselector of " - << s2 - << "?\t" - << compound_selector(s1 + ";")->is_superselector_of(compound_selector(s2 + ";")) - << endl; -} - -void check_complex(string s1, string s2) -{ - cout << "Is " - << s1 - << " a superselector of " - << s2 - << "?\t" - << complex_selector(s1 + ";")->is_superselector_of(complex_selector(s2 + ";")) - << endl; -} - -int main() -{ - check_compound(".foo", ".foo.bar"); - check_compound(".foo.bar", ".foo"); - check_compound(".foo.bar", "div.foo"); - check_compound(".foo", "div.foo"); - check_compound("div.foo", ".foo"); - check_compound("div.foo", "div.bar.foo"); - check_compound("p.foo", "div.bar.foo"); - check_compound(".hux", ".mumble"); - - cout << endl; - - check_complex(".foo ~ .bar", ".foo + .bar"); - check_complex(".foo .bar", ".foo + .bar"); - check_complex(".foo .bar", ".foo > .bar"); - check_complex(".foo .bar > .hux", ".foo.a .bar.b > .hux"); - check_complex(".foo ~ .bar .hux", ".foo.a + .bar.b > .hux"); - check_complex(".foo", ".bar .foo"); - check_complex(".foo", ".foo.a"); - check_complex(".foo.bar", ".foo"); - check_complex(".foo .bar .hux", ".bar .hux"); - check_complex(".foo ~ .bar .hux.x", ".foo.a + .bar.b > .hux.y"); - check_complex(".foo ~ .bar .hux", ".foo.a + .bar.b > .mumble"); - check_complex(".foo + .bar", ".foo ~ .bar"); - check_complex("a c e", "a b c d e"); - check_complex("c a e", "a b c d e"); - - return 0; -} - - diff --git a/test/test_unification.cpp b/test/test_unification.cpp deleted file mode 100644 index 4d957e4b..00000000 --- a/test/test_unification.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "../ast.hpp" -#include "../context.hpp" -#include "../parser.hpp" -#include "../to_string.hpp" -#include - -using namespace Sass; - -Context ctx = Context(Context::Data()); -To_String to_string; - -Compound_Selector* selector(string src) -{ return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_simple_selector_sequence(); } - -void unify(string lhs, string rhs) -{ - Compound_Selector* unified = selector(lhs + ";")->unify_with(selector(rhs + ";"), ctx); - cout << lhs << " UNIFIED WITH " << rhs << " =\t" << (unified ? unified->perform(&to_string) : "NOTHING") << endl; -} - -int main() -{ - unify(".foo", ".foo.bar"); - unify("div:nth-of-type(odd)", "div:first-child"); - unify("div", "span:whatever"); - unify("div", "span"); - unify("foo:bar::after", "foo:bar::first-letter"); - unify(".foo#bar.hux", ".hux.foo#bar"); - unify(".foo#bar.hux", ".hux.foo#baz"); - unify("*:blah:fudge", "p:fudge:blah"); - - return 0; -} diff --git a/to_c.cpp b/to_c.cpp deleted file mode 100644 index 3bfb544d..00000000 --- a/to_c.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "to_c.hpp" -#include "ast.hpp" - -namespace Sass { - using namespace std; - - Sass_Value To_C::fallback_impl(AST_Node* n) - { return make_sass_null(); } - - Sass_Value To_C::operator()(Boolean* b) - { return make_sass_boolean(b->value()); } - - Sass_Value To_C::operator()(Number* n) - { return make_sass_number(n->value(), n->unit().c_str()); } - - Sass_Value To_C::operator()(Color* c) - { return make_sass_color(c->r(), c->g(), c->b(), c->a()); } - - Sass_Value To_C::operator()(String_Constant* s) - { return make_sass_string(s->value().c_str()); } - - Sass_Value To_C::operator()(List* l) - { - Sass_Value v = make_sass_list(l->length(), l->separator() == List::COMMA ? SASS_COMMA : SASS_SPACE); - for (size_t i = 0, L = l->length(); i < L; ++i) { - v.list.values[i] = (*l)[i]->perform(this); - } - return v; - } - - Sass_Value To_C::operator()(Map* m) - { - Sass_Value v = make_sass_map(m->length()); - for (size_t i = 0, L = m->length(); i < L; ++i) { - v.map.pairs[i].key = (*m)[i]->key()->perform(this); - v.map.pairs[i].value = (*m)[i]->value()->perform(this); - } - return v; - } - - Sass_Value To_C::operator()(Arguments* a) - { - Sass_Value v = make_sass_list(a->length(), SASS_COMMA); - for (size_t i = 0, L = a->length(); i < L; ++i) { - v.list.values[i] = (*a)[i]->perform(this); - } - return v; - } - - Sass_Value To_C::operator()(Argument* a) - { return a->value()->perform(this); } - - // not strictly necessary because of the fallback - Sass_Value To_C::operator()(Null* n) - { return make_sass_null(); } - -}; diff --git a/to_c.hpp b/to_c.hpp deleted file mode 100644 index a78e63f6..00000000 --- a/to_c.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#define SASS_TO_C - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -#ifndef SASS -#include "sass.h" -#endif - -namespace Sass { - using namespace std; - - class AST_Node; - class Boolean; - class Number; - class String_Constant; - class List; - class Map; - class Null; - - class To_C : public Operation_CRTP { - - Sass_Value fallback_impl(AST_Node* n); - - public: - - To_C() { } - virtual ~To_C() { } - using Operation::operator(); - - Sass_Value operator()(Boolean*); - Sass_Value operator()(Number*); - Sass_Value operator()(Color*); - Sass_Value operator()(String_Constant*); - Sass_Value operator()(List*); - Sass_Value operator()(Map*); - Sass_Value operator()(Null*); - Sass_Value operator()(Arguments*); - Sass_Value operator()(Argument*); - - template - Sass_Value fallback(U x) { return fallback_impl(x); } - }; - -} diff --git a/to_string.cpp b/to_string.cpp deleted file mode 100644 index a6d72ad1..00000000 --- a/to_string.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include - -#ifndef SASS_TO_STRING -#include "to_string.hpp" -#endif - -#include "inspect.hpp" -#include "ast.hpp" -#include "context.hpp" -#include - -namespace Sass { - using namespace std; - - To_String::To_String(Context* ctx) : ctx(ctx) { } - To_String::~To_String() { } - - inline string To_String::fallback_impl(AST_Node* n) - { - Inspect i(ctx); - n->perform(&i); - return i.get_buffer(); - } - - inline string To_String::operator()(Null* n) - { return ""; } -} diff --git a/to_string.hpp b/to_string.hpp deleted file mode 100644 index 94aca727..00000000 --- a/to_string.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#define SASS_TO_STRING - -#include - -#ifndef SASS_OPERATION -#include "operation.hpp" -#endif - -namespace Sass { - using namespace std; - - struct Context; - class Null; - - class To_String : public Operation_CRTP { - // import all the class-specific methods and override as desired - using Operation::operator(); - // override this to define a catch-all - string fallback_impl(AST_Node* n); - - Context* ctx; - - public: - To_String(Context* ctx = 0); - virtual ~To_String(); - - string operator()(Null* n); - - template - string fallback(U n) { return fallback_impl(n); } - }; -} diff --git a/token.hpp b/token.hpp deleted file mode 100644 index d7b615fc..00000000 --- a/token.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#define SASS_TOKEN - -#include -#include -#include - -namespace Sass { - using namespace std; - - // Token type for representing lexed chunks of text - struct Token { - - const char* begin; - const char* end; - - Token() : begin(0), end(0) { } - Token(const char* s) : begin(s), end(s + strlen(s)) { } - Token(const char* b, const char* e) : begin(b), end(e) { } - - size_t length() const { return end - begin; } - string to_string() const { return string(begin, end - begin); } - - string unquote() const; - void unquote_to_stream(stringstream& buf) const; - - operator bool() { return begin && end && begin >= end; } - operator string() { return to_string(); } - - bool operator==(Token t) { return to_string() == t.to_string(); } - }; - -} diff --git a/units.cpp b/units.cpp deleted file mode 100644 index a71b35a5..00000000 --- a/units.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "units.hpp" - -namespace Sass { - - double conversion_factors[6][6] = { - /* in cm pc mm pt px */ - /* in */ { 1, 2.54, 6, 25.4, 72, 96 }, - /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54 }, - /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0 }, - /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4 }, - /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0 }, - /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1 } - }; - - Unit string_to_unit(const string& s) - { - if (s == "in") return IN; - else if (s == "cm") return CM; - else if (s == "pc") return PC; - else if (s == "mm") return MM; - else if (s == "pt") return PT; - else if (s == "px") return PX; - else return INCOMMENSURABLE; - } - - double conversion_factor(const string& s1, const string& s2) - { - Unit u1 = string_to_unit(s1); - Unit u2 = string_to_unit(s2); - double factor; - if (u1 == INCOMMENSURABLE || u2 == INCOMMENSURABLE) - factor = 0; - else - factor = conversion_factors[u1][u2]; - return factor; - } - - double convert(double n, const string& from, const string& to) - { - double factor = conversion_factor(from, to); - return factor ? factor * n : n; - } - -} diff --git a/units.hpp b/units.hpp deleted file mode 100644 index d1ea8cfe..00000000 --- a/units.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace Sass { - using namespace std; - - enum Unit { IN, CM, PC, MM, PT, PX, INCOMMENSURABLE }; - extern double conversion_factors[6][6]; - Unit string_to_unit(const string&); - double conversion_factor(const string&, const string&); - double convert(double, const string&, const string&); -} diff --git a/utf8.h b/utf8.h deleted file mode 100644 index 82b13f59..00000000 --- a/utf8.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2006 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include "utf8/checked.h" -#include "utf8/unchecked.h" - -#endif // header guard diff --git a/utf8/checked.h b/utf8/checked.h deleted file mode 100644 index 13311551..00000000 --- a/utf8/checked.h +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2006 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include "core.h" -#include - -namespace utf8 -{ - // Base for the exceptions that may be thrown from the library - class exception : public ::std::exception { - }; - - // Exceptions that may be thrown from the library functions. - class invalid_code_point : public exception { - uint32_t cp; - public: - invalid_code_point(uint32_t cp) : cp(cp) {} - virtual const char* what() const throw() { return "Invalid code point"; } - uint32_t code_point() const {return cp;} - }; - - class invalid_utf8 : public exception { - uint8_t u8; - public: - invalid_utf8 (uint8_t u) : u8(u) {} - virtual const char* what() const throw() { return "Invalid UTF-8"; } - uint8_t utf8_octet() const {return u8;} - }; - - class invalid_utf16 : public exception { - uint16_t u16; - public: - invalid_utf16 (uint16_t u) : u16(u) {} - virtual const char* what() const throw() { return "Invalid UTF-16"; } - uint16_t utf16_word() const {return u16;} - }; - - class not_enough_room : public exception { - public: - virtual const char* what() const throw() { return "Not enough space"; } - }; - - /// The library API - functions intended to be called by the users - - template - octet_iterator append(uint32_t cp, octet_iterator result) - { - if (!utf8::internal::is_code_point_valid(cp)) - throw invalid_code_point(cp); - - if (cp < 0x80) // one octet - *(result++) = static_cast(cp); - else if (cp < 0x800) { // two octets - *(result++) = static_cast((cp >> 6) | 0xc0); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else if (cp < 0x10000) { // three octets - *(result++) = static_cast((cp >> 12) | 0xe0); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else { // four octets - *(result++) = static_cast((cp >> 18) | 0xf0); - *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - return result; - } - - template - output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) - { - while (start != end) { - octet_iterator sequence_start = start; - internal::utf_error err_code = utf8::internal::validate_next(start, end); - switch (err_code) { - case internal::UTF8_OK : - for (octet_iterator it = sequence_start; it != start; ++it) - *out++ = *it; - break; - case internal::NOT_ENOUGH_ROOM: - throw not_enough_room(); - case internal::INVALID_LEAD: - out = utf8::append (replacement, out); - ++start; - break; - case internal::INCOMPLETE_SEQUENCE: - case internal::OVERLONG_SEQUENCE: - case internal::INVALID_CODE_POINT: - out = utf8::append (replacement, out); - ++start; - // just one replacement mark for the sequence - while (start != end && utf8::internal::is_trail(*start)) - ++start; - break; - } - } - return out; - } - - template - inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) - { - static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); - return utf8::replace_invalid(start, end, out, replacement_marker); - } - - template - uint32_t next(octet_iterator& it, octet_iterator end) - { - uint32_t cp = 0; - internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); - switch (err_code) { - case internal::UTF8_OK : - break; - case internal::NOT_ENOUGH_ROOM : - throw not_enough_room(); - case internal::INVALID_LEAD : - case internal::INCOMPLETE_SEQUENCE : - case internal::OVERLONG_SEQUENCE : - throw invalid_utf8(*it); - case internal::INVALID_CODE_POINT : - throw invalid_code_point(cp); - } - return cp; - } - - template - uint32_t peek_next(octet_iterator it, octet_iterator end) - { - return utf8::next(it, end); - } - - template - uint32_t prior(octet_iterator& it, octet_iterator start) - { - // can't do much if it == start - if (it == start) - throw not_enough_room(); - - octet_iterator end = it; - // Go back until we hit either a lead octet or start - while (utf8::internal::is_trail(*(--it))) - if (it == start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - return utf8::peek_next(it, end); - } - - /// Deprecated in versions that include "prior" - template - uint32_t previous(octet_iterator& it, octet_iterator pass_start) - { - octet_iterator end = it; - while (utf8::internal::is_trail(*(--it))) - if (it == pass_start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - octet_iterator temp = it; - return utf8::next(temp, end); - } - - template - void advance (octet_iterator& it, distance_type n, octet_iterator end) - { - for (distance_type i = 0; i < n; ++i) - utf8::next(it, end); - } - - template - typename std::iterator_traits::difference_type - distance (octet_iterator first, octet_iterator last) - { - typename std::iterator_traits::difference_type dist; - for (dist = 0; first < last; ++dist) - utf8::next(first, last); - return dist; - } - - template - octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) - { - while (start != end) { - uint32_t cp = utf8::internal::mask16(*start++); - // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) { - if (start != end) { - uint32_t trail_surrogate = utf8::internal::mask16(*start++); - if (utf8::internal::is_trail_surrogate(trail_surrogate)) - cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; - else - throw invalid_utf16(static_cast(trail_surrogate)); - } - else - throw invalid_utf16(static_cast(cp)); - - } - // Lone trail surrogate - else if (utf8::internal::is_trail_surrogate(cp)) - throw invalid_utf16(static_cast(cp)); - - result = utf8::append(cp, result); - } - return result; - } - - template - u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) - { - while (start != end) { - uint32_t cp = utf8::next(start, end); - if (cp > 0xffff) { //make a surrogate pair - *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); - *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); - } - else - *result++ = static_cast(cp); - } - return result; - } - - template - octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) - { - while (start != end) - result = utf8::append(*(start++), result); - - return result; - } - - template - u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) - { - while (start != end) - (*result++) = utf8::next(start, end); - - return result; - } - - // The iterator class - template - class iterator : public std::iterator { - octet_iterator it; - octet_iterator range_start; - octet_iterator range_end; - public: - iterator () {} - explicit iterator (const octet_iterator& octet_it, - const octet_iterator& range_start, - const octet_iterator& range_end) : - it(octet_it), range_start(range_start), range_end(range_end) - { - if (it < range_start || it > range_end) - throw std::out_of_range("Invalid utf-8 iterator position"); - } - // the default "big three" are OK - octet_iterator base () const { return it; } - uint32_t operator * () const - { - octet_iterator temp = it; - return utf8::next(temp, range_end); - } - bool operator == (const iterator& rhs) const - { - if (range_start != rhs.range_start || range_end != rhs.range_end) - throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); - return (it == rhs.it); - } - bool operator != (const iterator& rhs) const - { - return !(operator == (rhs)); - } - iterator& operator ++ () - { - utf8::next(it, range_end); - return *this; - } - iterator operator ++ (int) - { - iterator temp = *this; - utf8::next(it, range_end); - return temp; - } - iterator& operator -- () - { - utf8::prior(it, range_start); - return *this; - } - iterator operator -- (int) - { - iterator temp = *this; - utf8::prior(it, range_start); - return temp; - } - }; // class iterator - -} // namespace utf8 - -#endif //header guard - - diff --git a/utf8/core.h b/utf8/core.h deleted file mode 100644 index 693d388c..00000000 --- a/utf8/core.h +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2006 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include - -namespace utf8 -{ - // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers - // You may need to change them to match your system. - // These typedefs have the same names as ones from cstdint, or boost/cstdint - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; - -// Helper code - not intended to be directly called by the library users. May be changed at any time -namespace internal -{ - // Unicode constants - // Leading (high) surrogates: 0xd800 - 0xdbff - // Trailing (low) surrogates: 0xdc00 - 0xdfff - const uint16_t LEAD_SURROGATE_MIN = 0xd800u; - const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; - const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; - const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; - const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); - const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; - - // Maximum valid value for a Unicode code point - const uint32_t CODE_POINT_MAX = 0x0010ffffu; - - template - inline uint8_t mask8(octet_type oc) - { - return static_cast(0xff & oc); - } - template - inline uint16_t mask16(u16_type oc) - { - return static_cast(0xffff & oc); - } - template - inline bool is_trail(octet_type oc) - { - return ((utf8::internal::mask8(oc) >> 6) == 0x2); - } - - template - inline bool is_lead_surrogate(u16 cp) - { - return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); - } - - template - inline bool is_trail_surrogate(u16 cp) - { - return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } - - template - inline bool is_surrogate(u16 cp) - { - return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); - } - - template - inline bool is_code_point_valid(u32 cp) - { - return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); - } - - template - inline typename std::iterator_traits::difference_type - sequence_length(octet_iterator lead_it) - { - uint8_t lead = utf8::internal::mask8(*lead_it); - if (lead < 0x80) - return 1; - else if ((lead >> 5) == 0x6) - return 2; - else if ((lead >> 4) == 0xe) - return 3; - else if ((lead >> 3) == 0x1e) - return 4; - else - return 0; - } - - template - inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) - { - if (cp < 0x80) { - if (length != 1) - return true; - } - else if (cp < 0x800) { - if (length != 2) - return true; - } - else if (cp < 0x10000) { - if (length != 3) - return true; - } - - return false; - } - - enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; - - /// Helper for get_sequence_x - template - utf_error increase_safely(octet_iterator& it, octet_iterator end) - { - if (++it == end) - return NOT_ENOUGH_ROOM; - - if (!utf8::internal::is_trail(*it)) - return INCOMPLETE_SEQUENCE; - - return UTF8_OK; - } - - #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} - - /// get_sequence_x functions decode utf-8 sequences of the length x - template - utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - return UTF8_OK; - } - - template - utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); - - return UTF8_OK; - } - - template - utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (*it) & 0x3f; - - return UTF8_OK; - } - - template - utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - if (it == end) - return NOT_ENOUGH_ROOM; - - code_point = utf8::internal::mask8(*it); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; - - UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) - - code_point += (*it) & 0x3f; - - return UTF8_OK; - } - - #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR - - template - utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) - { - // Save the original value of it so we can go back in case of failure - // Of course, it does not make much sense with i.e. stream iterators - octet_iterator original_it = it; - - uint32_t cp = 0; - // Determine the sequence length based on the lead octet - typedef typename std::iterator_traits::difference_type octet_difference_type; - const octet_difference_type length = utf8::internal::sequence_length(it); - - // Get trail octets and calculate the code point - utf_error err = UTF8_OK; - switch (length) { - case 0: - return INVALID_LEAD; - case 1: - err = utf8::internal::get_sequence_1(it, end, cp); - break; - case 2: - err = utf8::internal::get_sequence_2(it, end, cp); - break; - case 3: - err = utf8::internal::get_sequence_3(it, end, cp); - break; - case 4: - err = utf8::internal::get_sequence_4(it, end, cp); - break; - } - - if (err == UTF8_OK) { - // Decoding succeeded. Now, security checks... - if (utf8::internal::is_code_point_valid(cp)) { - if (!utf8::internal::is_overlong_sequence(cp, length)){ - // Passed! Return here. - code_point = cp; - ++it; - return UTF8_OK; - } - else - err = OVERLONG_SEQUENCE; - } - else - err = INVALID_CODE_POINT; - } - - // Failure branch - restore the original value of the iterator - it = original_it; - return err; - } - - template - inline utf_error validate_next(octet_iterator& it, octet_iterator end) { - uint32_t ignored; - return utf8::internal::validate_next(it, end, ignored); - } - -} // namespace internal - - /// The library API - functions intended to be called by the users - - // Byte order mark - const uint8_t bom[] = {0xef, 0xbb, 0xbf}; - - template - octet_iterator find_invalid(octet_iterator start, octet_iterator end) - { - octet_iterator result = start; - while (result != end) { - utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); - if (err_code != internal::UTF8_OK) - return result; - } - return result; - } - - template - inline bool is_valid(octet_iterator start, octet_iterator end) - { - return (utf8::find_invalid(start, end) == end); - } - - template - inline bool starts_with_bom (octet_iterator it, octet_iterator end) - { - return ( - ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && - ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && - ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) - ); - } - - //Deprecated in release 2.3 - template - inline bool is_bom (octet_iterator it) - { - return ( - (utf8::internal::mask8(*it++)) == bom[0] && - (utf8::internal::mask8(*it++)) == bom[1] && - (utf8::internal::mask8(*it)) == bom[2] - ); - } -} // namespace utf8 - -#endif // header guard - - diff --git a/utf8/unchecked.h b/utf8/unchecked.h deleted file mode 100644 index cb242716..00000000 --- a/utf8/unchecked.h +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2006 Nemanja Trifunovic - -/* -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. -*/ - - -#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 -#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 - -#include "core.h" - -namespace utf8 -{ - namespace unchecked - { - template - octet_iterator append(uint32_t cp, octet_iterator result) - { - if (cp < 0x80) // one octet - *(result++) = static_cast(cp); - else if (cp < 0x800) { // two octets - *(result++) = static_cast((cp >> 6) | 0xc0); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else if (cp < 0x10000) { // three octets - *(result++) = static_cast((cp >> 12) | 0xe0); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - else { // four octets - *(result++) = static_cast((cp >> 18) | 0xf0); - *(result++) = static_cast(((cp >> 12) & 0x3f)| 0x80); - *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); - *(result++) = static_cast((cp & 0x3f) | 0x80); - } - return result; - } - - template - uint32_t next(octet_iterator& it) - { - uint32_t cp = utf8::internal::mask8(*it); - typename std::iterator_traits::difference_type length = utf8::internal::sequence_length(it); - switch (length) { - case 1: - break; - case 2: - it++; - cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); - break; - case 3: - ++it; - cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); - ++it; - cp += (*it) & 0x3f; - break; - case 4: - ++it; - cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); - ++it; - cp += (utf8::internal::mask8(*it) << 6) & 0xfff; - ++it; - cp += (*it) & 0x3f; - break; - } - ++it; - return cp; - } - - template - uint32_t peek_next(octet_iterator it) - { - return utf8::unchecked::next(it); - } - - template - uint32_t prior(octet_iterator& it) - { - while (utf8::internal::is_trail(*(--it))) ; - octet_iterator temp = it; - return utf8::unchecked::next(temp); - } - - // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) - template - inline uint32_t previous(octet_iterator& it) - { - return utf8::unchecked::prior(it); - } - - template - void advance (octet_iterator& it, distance_type n) - { - for (distance_type i = 0; i < n; ++i) - utf8::unchecked::next(it); - } - - template - typename std::iterator_traits::difference_type - distance (octet_iterator first, octet_iterator last) - { - typename std::iterator_traits::difference_type dist; - for (dist = 0; first < last; ++dist) - utf8::unchecked::next(first); - return dist; - } - - template - octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) - { - while (start != end) { - uint32_t cp = utf8::internal::mask16(*start++); - // Take care of surrogate pairs first - if (utf8::internal::is_lead_surrogate(cp)) { - uint32_t trail_surrogate = utf8::internal::mask16(*start++); - cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; - } - result = utf8::unchecked::append(cp, result); - } - return result; - } - - template - u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) - { - while (start < end) { - uint32_t cp = utf8::unchecked::next(start); - if (cp > 0xffff) { //make a surrogate pair - *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); - *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); - } - else - *result++ = static_cast(cp); - } - return result; - } - - template - octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) - { - while (start != end) - result = utf8::unchecked::append(*(start++), result); - - return result; - } - - template - u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) - { - while (start < end) - (*result++) = utf8::unchecked::next(start); - - return result; - } - - // The iterator class - template - class iterator : public std::iterator { - octet_iterator it; - public: - iterator () {} - explicit iterator (const octet_iterator& octet_it): it(octet_it) {} - // the default "big three" are OK - octet_iterator base () const { return it; } - uint32_t operator * () const - { - octet_iterator temp = it; - return utf8::unchecked::next(temp); - } - bool operator == (const iterator& rhs) const - { - return (it == rhs.it); - } - bool operator != (const iterator& rhs) const - { - return !(operator == (rhs)); - } - iterator& operator ++ () - { - ::std::advance(it, utf8::internal::sequence_length(it)); - return *this; - } - iterator operator ++ (int) - { - iterator temp = *this; - ::std::advance(it, utf8::internal::sequence_length(it)); - return temp; - } - iterator& operator -- () - { - utf8::unchecked::prior(it); - return *this; - } - iterator operator -- (int) - { - iterator temp = *this; - utf8::unchecked::prior(it); - return temp; - } - }; // class iterator - - } // namespace utf8::unchecked -} // namespace utf8 - - -#endif // header guard - diff --git a/utf8_string.cpp b/utf8_string.cpp deleted file mode 100644 index 03074cbd..00000000 --- a/utf8_string.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef SASS_UTF8_STRING -#define SASS_UTF8_STRING - -#include -#include -#include -#include -#include - -#include "utf8.h" - -namespace Sass { - namespace UTF_8 { - using std::string; - - // naming conventions: - // offset: raw byte offset (0 based) - // position: code point offset (0 based) - // index: code point offset (1 based or negative) - - // function that will count the number of code points (utf-8 characters) from the given beginning to the given end - size_t code_point_count(const string& str, size_t start, size_t end) { - return utf8::distance(str.begin() + start, str.begin() + end); - } - - size_t code_point_count(const string& str) { - return utf8::distance(str.begin(), str.end()); - } - - // function that will return the byte offset at a code point position - size_t offset_at_position(const string& str, size_t position) { - string::const_iterator it = str.begin(); - utf8::advance(it, position, str.end()); - return distance(str.begin(), it); - } - - // function that returns number of bytes in a character at offset - size_t code_point_size_at_offset(const string& str, size_t offset) { - // get iterator from string and forward by offset - string::const_iterator stop = str.begin() + offset; - // check if beyond boundary - if (stop == str.end()) return 0; - // advance by one code point - utf8::advance(stop, 1, str.end()); - // calculate offset for code point - return stop - str.begin() - offset; - } - - // function that will return a normalized index, given a crazy one - size_t normalize_index(int index, size_t len) { - long signed_len = len; - // assuming the index is 1-based - // we are returning a 0-based index - if (index > 0 && index <= signed_len) { - // positive and within string length - return index-1; - } - else if (index > signed_len) { - // positive and past string length - return len; - } - else if (index == 0) { - return 0; - } - else if (std::abs((double)index) <= signed_len) { - // negative and within string length - return index + signed_len; - } - else { - // negative and past string length - return 0; - } - } - - // utf16 functions - using std::wstring; - - // convert from utf16/wide string to utf8 string - string convert_from_utf16(const wstring& utf16) - { - string utf8; - // pre-allocate expected memory - utf8.reserve(sizeof(utf16)/2); - utf8::utf16to8(utf16.begin(), utf16.end(), - back_inserter(utf8)); - return utf8; - } - - // convert from utf8 string to utf16/wide string - wstring convert_to_utf16(const string& utf8) - { - wstring utf16; - // pre-allocate expected memory - utf16.reserve(code_point_count(utf8)*2); - utf8::utf8to16(utf8.begin(), utf8.end(), - back_inserter(utf16)); - return utf16; - } - - } -} - -#endif diff --git a/utf8_string.hpp b/utf8_string.hpp deleted file mode 100644 index 89493927..00000000 --- a/utf8_string.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SASS_UTF8_STRING -#define SASS_UTF8_STRING - -#include - -namespace Sass { - namespace UTF_8 { - - // naming conventions: - // offset: raw byte offset (0 based) - // position: code point offset (0 based) - // index: code point offset (1 based or negative) - - // function that will count the number of code points (utf-8 characters) from the beginning to the given end - size_t code_point_count(const string& str, size_t start, size_t end); - size_t code_point_count(const string& str); - - // function that will return the byte offset of a code point in a - size_t offset_at_position(const string& str, size_t position); - - // function that returns number of bytes in a character in a string - size_t code_point_size_at_offset(const string& str, size_t offset); - - // function that will return a normalized index, given a crazy one - size_t normalize_index(int index, size_t len); - - #ifdef _WIN32 - // functions to handle unicode paths on windows - string convert_from_utf16(const wstring& wstr); - wstring convert_to_utf16(const string& str); - #endif - - } -} - -#endif diff --git a/util.cpp b/util.cpp deleted file mode 100644 index a19054a0..00000000 --- a/util.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "util.hpp" - -namespace Sass { - namespace Util { - using std::string; - - string normalize_underscores(const string& str) { - string normalized = str; - for(size_t i = 0, L = normalized.length(); i < L; ++i) { - if(normalized[i] == '_') { - normalized[i] = '-'; - } - } - return normalized; - } - - bool isPrintable(Ruleset* r) { - if (r == NULL) { - return false; - } - - Block* b = r->block(); - - bool hasSelectors = static_cast(r->selector())->length() > 0; - - if (!hasSelectors) { - return false; - } - - bool hasDeclarations = false; - bool hasPrintableChildBlocks = false; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (dynamic_cast(stm)) { - Block* pChildBlock = ((Has_Block*)stm)->block(); - if (isPrintable(pChildBlock)) { - hasPrintableChildBlocks = true; - } - } else { - hasDeclarations = true; - } - - if (hasDeclarations || hasPrintableChildBlocks) { - return true; - } - } - - return false; - } - - bool isPrintable(Media_Block* m) { - if (m == NULL) { - return false; - } - - Block* b = m->block(); - - bool hasSelectors = m->selector() && static_cast(m->selector())->length() > 0; - - bool hasDeclarations = false; - bool hasPrintableChildBlocks = false; - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (!stm->is_hoistable() && m->selector() != NULL && !hasSelectors) { - // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0), - // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector - // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with - // a declaration in it). - } - else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { - hasDeclarations = true; - } - else if (dynamic_cast(stm)) { - Block* pChildBlock = ((Has_Block*)stm)->block(); - if (isPrintable(pChildBlock)) { - hasPrintableChildBlocks = true; - } - } - - if (hasDeclarations || hasPrintableChildBlocks) { - return true; - } - } - - return false; - } - - bool isPrintable(Block* b) { - if (b == NULL) { - return false; - } - - for (size_t i = 0, L = b->length(); i < L; ++i) { - Statement* stm = (*b)[i]; - if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) { - return true; - } - else if (typeid(*stm) == typeid(Ruleset)) { - Ruleset* r = (Ruleset*) stm; - if (isPrintable(r)) { - return true; - } - } - else if (typeid(*stm) == typeid(Media_Block)) { - Media_Block* m = (Media_Block*) stm; - if (isPrintable(m)) { - return true; - } - } - else if (dynamic_cast(stm) && isPrintable(((Has_Block*)stm)->block())) { - return true; - } - } - - return false; - } - - } -} diff --git a/util.hpp b/util.hpp deleted file mode 100644 index d7a6686a..00000000 --- a/util.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SASS_UTIL -#define SASS_UTIL - -#ifndef SASS_AST -#include "ast.hpp" -#endif - -#include -namespace Sass { - namespace Util { - - std::string normalize_underscores(const std::string& str); - - bool containsAnyPrintableStatements(Block* b); - - bool isPrintable(Ruleset* r); - bool isPrintable(Media_Block* r); - bool isPrintable(Block* b); - - } -} -#endif From fe10b92e59df26a8949a67a448129f40d7116c78 Mon Sep 17 00:00:00 2001 From: Rodolphe PELLOUX-PRAYER Date: Mon, 20 Oct 2014 17:42:13 +0200 Subject: [PATCH 2/2] Remove useless -xc++ flag. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6d918402..4701e821 100644 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ def spawn(self, cmd): flags = ['-I' + os.path.abspath('win32')] link_flags = [] else: - flags = ['-fPIC', '-xc++', '-std=c++11', '-Wall', '-Wno-parentheses'] + flags = ['-fPIC', '-std=c++11', '-Wall', '-Wno-parentheses'] platform.mac_ver() if platform.system() == 'Darwin': flags.extend([