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..4701e821 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 @@ -77,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([ @@ -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