diff -Nru libsass-3.3.2/appveyor.yml libsass-3.3.4/appveyor.yml --- libsass-3.3.2/appveyor.yml 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/appveyor.yml 2016-03-17 14:51:35.000000000 +0000 @@ -1,4 +1,4 @@ -os: Visual Studio 2015 +os: Visual Studio 2013 environment: CTEST_OUTPUT_ON_FAILURE: 1 @@ -27,9 +27,7 @@ if ($env:Compiler -eq "mingw" -AND -Not (Test-Path "C:\mingw64")) { # Install MinGW. $file = "x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z" - $url = "https://bintray.com/artifact/download/drewwells/generic/" - $url += $file - Invoke-WebRequest -UserAgent wget -Uri $url -OutFile $file + wget https://bintray.com/artifact/download/drewwells/generic/$file -OutFile $file &7z x -oC:\ $file > $null } - set PATH=C:\mingw64\bin;%PATH% @@ -53,18 +51,28 @@ test_script: - ps: | - $PRNR = [System.Environment]::ExpandEnvironmentVariables("%APPVEYOR_PULL_REQUEST_NUMBER%") - if ($PRNR -ne "") { + $PRNR = $env:APPVEYOR_PULL_REQUEST_NUMBER + if ($PRNR) { echo "Fetching info for PR $PRNR" - $request = (New-Object System.Net.WebClient) - $request.headers['User-Agent'] = "Mozilla/5.0" - $request.DownloadFile( "https://api.github.com/repos/sass/libsass/pulls/$PRNR", 'pr.json') - $json = [IO.File]::ReadAllText('pr.json') + wget https://api.github.com/repos/sass/libsass/pulls/$PRNR -OutFile pr.json + $json = cat pr.json -Raw $SPEC_PR = [regex]::match($json,'sass\/sass-spec(#|\/pull\/)([0-9]+)').Groups[2].Value - if ($SPEC_PR -ne "") { + if ($SPEC_PR) { echo "Checkout sass spec PR $SPEC_PR" git -C sass-spec fetch -q -u origin pull/$SPEC_PR/head:ci-spec-pr-$SPEC_PR git -C sass-spec checkout -q --force ci-spec-pr-$SPEC_PR } } ruby sass-spec/sass-spec.rb -c $env:TargetPath -s --ignore-todo sass-spec/spec + + Write-Host "Explicitly testing the case when cwd has Cyrillic characters: " -nonewline + # See comments in gh-1774 for details. + $env:TargetPath = Join-Path $pwd.Path $env:TargetPath + cd sass-spec/spec/libsass/Sáss-UŢF8/ + &$env:TargetPath ./input.scss 2>&1>$null + if(-not($?)) { + echo "Failed!" + exit 1 + } else { + echo "Success!" + } diff -Nru libsass-3.3.2/debian/changelog libsass-3.3.4/debian/changelog --- libsass-3.3.2/debian/changelog 2015-11-30 19:26:27.000000000 +0000 +++ libsass-3.3.4/debian/changelog 2016-03-21 21:09:32.000000000 +0000 @@ -1,5 +1,44 @@ +libsass (3.3.4-1) unstable; urgency=medium + + [ upstream ] + * New release. + Closes: Bug#818896. Thanks to Frederic Bonnard. + + [ Jonas Smedegaard ] + * Drop CDBS get-orig-source target: Use "gbp import-orig --uscan" + instead. + * Update watch file: + + Bump file format to version 4. + + Use github pattern from ducmentation. + * Update copyright info: + + Use License-Grant for GPL with exception. + + Drop unneeded disclaimers. + + Use license shortname FSFUL~Boost. + + Change main copyright holder. + + -- Jonas Smedegaard Mon, 21 Mar 2016 22:09:20 +0100 + +libsass (3.3.3-1) unstable; urgency=medium + + [ upstream ] + * New upstream release. + Thanks to Raphaël (see bug#694733). + + [ Jonas Smedegaard ] + * Declare compliance with Debian Policy 3.9.7. + * Modernize Vcs-Git field: Use https protocol. + * Update copyright info: + + Extend copyright for main upstream author to cover recent years. + + Extend copyright of packaging to cover current year. + + -- Jonas Smedegaard Tue, 08 Mar 2016 01:18:47 +0100 + libsass (3.3.2-1) unstable; urgency=medium + [ upstream ] + * New upstream release(s). + + [ Jonas Smedegaard ] * Update copyright info: + Fix typo in License shortname. + Cover newly added files. diff -Nru libsass-3.3.2/debian/control libsass-3.3.4/debian/control --- libsass-3.3.2/debian/control 2015-11-30 19:15:19.000000000 +0000 +++ libsass-3.3.4/debian/control 2016-03-21 21:04:46.000000000 +0000 @@ -12,9 +12,9 @@ d-shlibs (>= 0.50) Maintainer: Debian Sass team Uploaders: Jonas Smedegaard -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Homepage: https://libsass.org/ -Vcs-Git: git://anonscm.debian.org/pkg-sass/libsass.git +Vcs-Git: https://anonscm.debian.org/git/pkg-sass/libsass.git Vcs-Browser: https://anonscm.debian.org/cgit/pkg-sass/libsass.git Package: libsass0 diff -Nru libsass-3.3.2/debian/control.in libsass-3.3.4/debian/control.in --- libsass-3.3.2/debian/control.in 2015-11-30 19:28:19.000000000 +0000 +++ libsass-3.3.4/debian/control.in 2016-03-21 21:11:58.000000000 +0000 @@ -4,9 +4,9 @@ Build-Depends: @cdbs@ Maintainer: Debian Sass team Uploaders: Jonas Smedegaard -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Homepage: https://libsass.org/ -Vcs-Git: git://anonscm.debian.org/pkg-sass/libsass.git +Vcs-Git: https://anonscm.debian.org/git/pkg-sass/libsass.git Vcs-Browser: https://anonscm.debian.org/cgit/pkg-sass/libsass.git Package: libsass0 diff -Nru libsass-3.3.2/debian/control.in.in libsass-3.3.4/debian/control.in.in --- libsass-3.3.2/debian/control.in.in 2015-11-30 18:19:52.000000000 +0000 +++ libsass-3.3.4/debian/control.in.in 2016-03-08 00:13:42.000000000 +0000 @@ -4,9 +4,9 @@ Build-Depends: @cdbs@ Maintainer: Debian Sass team Uploaders: Jonas Smedegaard -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Homepage: https://libsass.org/ -Vcs-Git: git://anonscm.debian.org/pkg-sass/libsass.git +Vcs-Git: https://anonscm.debian.org/git/pkg-sass/libsass.git Vcs-Browser: https://anonscm.debian.org/cgit/pkg-sass/libsass.git Package: __LIBPKG__ diff -Nru libsass-3.3.2/debian/copyright libsass-3.3.4/debian/copyright --- libsass-3.3.2/debian/copyright 2015-11-30 19:08:52.000000000 +0000 +++ libsass-3.3.4/debian/copyright 2016-03-21 21:05:10.000000000 +0000 @@ -6,7 +6,7 @@ Files: * Copyright: - © 2012, Hampton Catlin + © 2012-2016, the Sass Open Source Foundation License: Expat Files: utf8.h @@ -46,20 +46,12 @@ test-driver Copyright: © 2011-2013, Free Software Foundation, Inc -License: GPL-2+ with Autoconf exception +License-Grant: 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 . - . +License: GPL-2+ with Autoconf exception 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 @@ -78,12 +70,6 @@ License: public-domain This file has no copyright assigned and is placed in the Public Domain. This file is a part of the w64 mingw-runtime package. - . - The w64 mingw-runtime package and its code is distributed in the hope - that it will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, - EXPRESSED OR IMPLIED ARE HEREBY DISCLAIMED. This includes but is not - limited to warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. Files: m4/m4-ax_cxx_compile_stdcxx_11.m4 Copyright: @@ -91,15 +77,11 @@ 2012, Zack Weinberg 2013, Roy Stogner 2014-2015, Google Inc. -License: - Copying and distribution of this file, with or without modification, - are permitted in any medium without royalty provided the copyright - notice and this notice are preserved. This file is offered as-is, - without any warranty. +License: FSFUL~Boost Files: debian/* Copyright: - © 2015, Jonas Smedegaard + © 2015-2016, Jonas Smedegaard License-Grant: 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 @@ -118,14 +100,6 @@ . 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. License: BSL-1.0 Permission is hereby granted, free of charge, to any person or @@ -156,15 +130,6 @@ Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. - . - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL - WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR - BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES - OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, - ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - SOFTWARE. License: BSD-2-clause Redistribution and use in source and binary forms, with or without @@ -190,5 +155,11 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +License: FSFUL~Boost + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. This file is offered as-is, + without any warranty. + License: GPL-3+ License-Reference: /usr/share/common-licenses/GPL-3 diff -Nru libsass-3.3.2/debian/copyright_hints libsass-3.3.4/debian/copyright_hints --- libsass-3.3.2/debian/copyright_hints 2015-11-30 19:15:18.000000000 +0000 +++ libsass-3.3.4/debian/copyright_hints 2016-03-21 21:04:45.000000000 +0000 @@ -140,6 +140,7 @@ src/remove_placeholders.cpp src/remove_placeholders.hpp src/sass.cpp + src/sass.hpp src/sass_context.cpp src/sass_context.hpp src/sass_functions.cpp @@ -155,8 +156,6 @@ src/support/libsass.pc.in src/to_c.cpp src/to_c.hpp - src/to_string.cpp - src/to_string.hpp src/to_value.cpp src/to_value.hpp src/units.cpp @@ -198,12 +197,6 @@ License: Expat FIXME -Files: COPYING - LICENSE -Copyright: 2012, Hampton Catlin -License: Expat - FIXME - Files: include/sass2scss.h src/sass2scss.cpp Copyright: Marcel Greter @@ -215,13 +208,23 @@ License: BSL-1 FIXME +Files: COPYING +Copyright: 2012, Hampton Catlin +License: Expat + FIXME + +Files: LICENSE +Copyright: 2012-2016, the Sass Open Source Foundation +License: Expat + FIXME + Files: script/tap-driver Copyright: 2011-2013, Free Software Foundation, Inc License: GPL-2+ FIXME Files: debian/rules -Copyright: 2015, Jonas Smedegaard +Copyright: 2015-2016, Jonas Smedegaard License: GPL-3+ FIXME @@ -229,7 +232,7 @@ Copyright: 2008, Benjamin Kosnik 2012, Zack Weinberg 2013, Roy Stogner - 2014-2015, Google Inc.; contributed by Alexey Sokolov + 2014-2015, Google Inc.; contributed Alexey Sokolov License: UNKNOWN FIXME diff -Nru libsass-3.3.2/debian/rules libsass-3.3.4/debian/rules --- libsass-3.3.2/debian/rules 2015-11-30 19:26:06.000000000 +0000 +++ libsass-3.3.4/debian/rules 2016-03-21 21:08:36.000000000 +0000 @@ -1,6 +1,6 @@ #!/usr/bin/make -f # -*- mode: makefile; coding: utf-8 -*- -# Copyright © 2015 Jonas Smedegaard +# Copyright © 2015-2016 Jonas Smedegaard # Description: Main Debian packaging script for LibSass # # This program is free software; you can redistribute it and/or modify @@ -27,7 +27,6 @@ DEB_AUTO_UPDATE_AUTOCONF = , DEB_AUTO_UPDATE_AUTOMAKE = , DEB_AUTO_UPDATE_AUTOHEADER = , -include /usr/share/cdbs/1/rules/upstream-tarball.mk include /usr/share/cdbs/1/rules/utils.mk include /usr/share/cdbs/1/class/autotools.mk include /usr/share/cdbs/1/rules/debhelper.mk @@ -39,9 +38,6 @@ libpkg = lib$(stem)$(abi) devpkg = lib$(stem)-dev -DEB_UPSTREAM_URL = https://github.com/sass/libsass/archive -DEB_UPSTREAM_TARBALL_BASENAME = $(DEB_UPSTREAM_TARBALL_VERSION) - # Build-depend unversioned on debhelper # TODO: Drop when adopted in cdbs CDBS_BUILD_DEPENDS_rules_debhelper_v9 = debhelper diff -Nru libsass-3.3.2/debian/watch libsass-3.3.4/debian/watch --- libsass-3.3.2/debian/watch 2015-11-30 18:19:52.000000000 +0000 +++ libsass-3.3.4/debian/watch 2016-03-21 19:10:56.000000000 +0000 @@ -1,2 +1,4 @@ -version=3 -https://github.com/sass/libsass/tags .*/archive/v?(\d[\d\.]+).tar.gz +version=4 +opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%libsass-$1.tar.gz%" \ + https://github.com/sass/libsass/tags \ + (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate diff -Nru libsass-3.3.2/docs/api-importer-example.md libsass-3.3.4/docs/api-importer-example.md --- libsass-3.3.2/docs/api-importer-example.md 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/docs/api-importer-example.md 2016-03-17 14:51:35.000000000 +0000 @@ -75,7 +75,7 @@ // swallows »@import "http://…"« pass-through // (arguably a bug) Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); + list[0] = sass_make_import_entry(path, 0, 0); return list; } @@ -83,7 +83,7 @@ // return an error to halt execution Sass_Import_List list = sass_make_import_list(1); const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); + list[0] = sass_make_import_entry(path, 0, 0); sass_import_set_error(list[0], strdup(message), 0, 0); return list; } @@ -110,1236 +110,3 @@ return list; } ``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` -## Example importer.c - -```C -#include -#include -#include "sass/context.h" - -Sass_Import_List sass_importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) -{ - // get the cookie from importer descriptor - void* cookie = sass_importer_get_cookie(cb); - Sass_Import_List list = sass_make_import_list(2); - const char* local = "local { color: green; }"; - const char* remote = "remote { color: red; }"; - list[0] = sass_make_import_entry("/tmp/styles.scss", strdup(local), 0); - list[1] = sass_make_import_entry("http://www.example.com", strdup(remote), 0); - return list; -} - -int main( int argc, const char* argv[] ) -{ - - // get the input file from first argument or use default - const char* input = argc > 1 ? argv[1] : "styles.scss"; - - // create the file context and get all related structs - struct Sass_File_Context* file_ctx = sass_make_file_context(input); - struct Sass_Context* ctx = sass_file_context_get_context(file_ctx); - struct Sass_Options* ctx_opt = sass_context_get_options(ctx); - - // allocate custom importer - Sass_Importer_Entry c_imp = - sass_make_importer(sass_importer, 0, 0); - // create list for all custom importers - Sass_Importer_List imp_list = sass_make_importer_list(1); - // put the only importer on to the list - sass_importer_set_list_entry(imp_list, 0, c_imp); - // register list on to the context options - sass_option_set_c_importers(ctx_opt, imp_list); - // context is set up, call the compile step now - int status = sass_compile_file_context(file_ctx); - - // print the result or the error to the stdout - if (status == 0) puts(sass_context_get_output_string(ctx)); - else puts(sass_context_get_error_message(ctx)); - - // release allocated memory - sass_delete_file_context(file_ctx); - - // exit status - return status; - -} -``` - -Compile importer.c - -```bash -gcc -c importer.c -o importer.o -gcc -o importer importer.o -lsass -echo "@import 'foobar';" > importer.scss -./importer importer.scss -``` - -## Importer Behavior Examples - -```C -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the import request - return NULL; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass handle the request - // swallows »@import "http://…"« pass-through - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(url, 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // return an error to halt execution - Sass_Import_List list = sass_make_import_list(1); - const char* message = "some error message"; - list[0] = sass_make_import_entry(url, 0, 0); - sass_import_set_error(list[0], strdup(message), 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // let LibSass load the file identifed by the importer - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry("/tmp/file.scss", 0, 0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(0); - return list; -} - -Sass_Import_List importer(const char* path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { - // completely hide the import - // (arguably a bug) - Sass_Import_List list = sass_make_import_list(1); - list[0] = sass_make_import_entry(0, 0, 0); - return list; -} -``` - diff -Nru libsass-3.3.2/docs/build-with-autotools.md libsass-3.3.4/docs/build-with-autotools.md --- libsass-3.3.2/docs/build-with-autotools.md 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/docs/build-with-autotools.md 2016-03-17 14:51:35.000000000 +0000 @@ -7,6 +7,16 @@ git clone https://github.com/sass/sass-spec.git libsass/sass-spec ``` +### Prerequisites + +In order to run autotools you need a few tools installed on your system. +```bash +yum install automake libtool # RedHat Linux +emerge -a automake libtool # Gentoo Linux +pkgin install automake libtool # SmartOS +``` + + ### Create configure script ```bash cd libsass diff -Nru libsass-3.3.2/docs/build-with-makefiles.md libsass-3.3.4/docs/build-with-makefiles.md --- libsass-3.3.2/docs/build-with-makefiles.md 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/docs/build-with-makefiles.md 2016-03-17 14:51:35.000000000 +0000 @@ -32,6 +32,24 @@ libsass.a libsass.so ``` +### Install onto the system + +We recommend to use [autotools to install](build-with-autotools.md) libsass onto the +system, since that brings all the benefits of using libtools as the main install method. +If you still want to install libsass via the makefile, you need to make sure that gnu +`install` utility (or compatible) is installed on your system. +```bash +yum install coreutils # RedHat Linux +emerge -a coreutils # Gentoo Linux +pkgin install coreutils # SmartOS +``` + +You can set the install location by setting `PREFIX` +```bash +PREFIX="/opt/local" make install +``` + + ### Compling sassc ```bash diff -Nru libsass-3.3.2/.editorconfig libsass-3.3.4/.editorconfig --- libsass-3.3.2/.editorconfig 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/.editorconfig 2016-03-17 14:51:35.000000000 +0000 @@ -10,6 +10,6 @@ indent_style = spaces indent_size = 2 -[{Makefile, Makefile.am}] +[{Makefile, GNUmakefile.am}] indent_style = tab indent_size = 4 diff -Nru libsass-3.3.2/.gitignore libsass-3.3.4/.gitignore --- libsass-3.3.2/.gitignore 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/.gitignore 2016-03-17 14:51:35.000000000 +0000 @@ -50,6 +50,7 @@ *.a *.suo *.sdf +*.opendb *.opensdf a.out libsass.js diff -Nru libsass-3.3.2/include/sass/base.h libsass-3.3.4/include/sass/base.h --- libsass-3.3.2/include/sass/base.h 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/include/sass/base.h 2016-03-17 14:51:35.000000000 +0000 @@ -56,7 +56,10 @@ SASS_STYLE_NESTED, SASS_STYLE_EXPANDED, SASS_STYLE_COMPACT, - SASS_STYLE_COMPRESSED + SASS_STYLE_COMPRESSED, + // only used internaly + SASS_STYLE_INSPECT, + SASS_STYLE_TO_SASS }; // Some convenient string helper function diff -Nru libsass-3.3.2/include/sass/values.h libsass-3.3.4/include/sass/values.h --- libsass-3.3.2/include/sass/values.h 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/include/sass/values.h 2016-03-17 14:51:35.000000000 +0000 @@ -29,7 +29,8 @@ // Tags for denoting Sass list separators enum Sass_Separator { SASS_COMMA, - SASS_SPACE + SASS_SPACE, + SASS_HASH }; // Value Operators diff -Nru libsass-3.3.2/LICENSE libsass-3.3.4/LICENSE --- libsass-3.3.2/LICENSE 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/LICENSE 2016-03-17 14:51:35.000000000 +0000 @@ -1,5 +1,5 @@ -Copyright (C) 2012 by Hampton Catlin +Copyright (C) 2012-2016 by the Sass Open Source Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff -Nru libsass-3.3.2/Makefile libsass-3.3.4/Makefile --- libsass-3.3.2/Makefile 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/Makefile 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +OS ?= $(shell uname -s) CC ?= gcc CXX ?= g++ RM ?= rm -f @@ -5,6 +6,11 @@ MKDIR ?= mkdir RMDIR ?= rmdir WINDRES ?= windres +# Solaris/Illumos flavors +# ginstall from coreutils +ifeq ($(OS),SunOS) +INSTALL ?= ginstall +endif INSTALL ?= install CFLAGS ?= -Wall CXXFLAGS ?= -Wall @@ -20,14 +26,14 @@ ifneq (,$(findstring /cygdrive/,$(PATH))) UNAME := Cygwin else - ifneq (,$(findstring WINDOWS,$(PATH))) + ifneq (,$(findstring Windows_NT,$(OS))) UNAME := Windows else ifneq (,$(findstring mingw32,$(MAKE))) - UNAME := MinGW + UNAME := Windows else ifneq (,$(findstring MINGW32,$(shell uname -s))) - UNAME = MinGW + UNAME = Windows else UNAME := $(shell uname -s) endif @@ -57,7 +63,7 @@ endif # enable mandatory flag -ifeq (MinGW,$(UNAME)) +ifeq (Windows,$(UNAME)) ifneq ($(BUILD),shared) STATIC_ALL ?= 1 endif @@ -117,7 +123,7 @@ LDFLAGS += -stdlib=libc++ endif -ifneq (MinGW,$(UNAME)) +ifneq (Windows,$(UNAME)) ifneq (FreeBSD,$(UNAME)) LDFLAGS += -ldl LDLIBS += -ldl @@ -128,14 +134,17 @@ BUILD = static endif -ifeq (,$(PREFIX)) - ifeq (,$(TRAVIS_BUILD_DIR)) - PREFIX = /usr/local +ifeq (,$(TRAVIS_BUILD_DIR)) + ifeq ($(OS),SunOS) + PREFIX ?= /opt/local else - PREFIX = $(TRAVIS_BUILD_DIR) + PREFIX ?= /usr/local endif +else + PREFIX ?= $(TRAVIS_BUILD_DIR) endif + SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASS_SPEC_SPEC_DIR ?= spec @@ -145,7 +154,7 @@ LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so -ifeq (MinGW,$(UNAME)) +ifeq (Windows,$(UNAME)) ifeq (shared,$(BUILD)) CFLAGS += -D ADD_EXPORTS CXXFLAGS += -D ADD_EXPORTS @@ -159,9 +168,6 @@ endif endif -ifeq (MinGW,$(UNAME)) - SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe -endif ifeq (Windows,$(UNAME)) SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe endif @@ -171,7 +177,7 @@ RESOURCES = STATICLIB = lib/libsass.a SHAREDLIB = lib/libsass.so -ifeq (MinGW,$(UNAME)) +ifeq (Windows,$(UNAME)) RESOURCES += res/resource.rc SHAREDLIB = lib/libsass.dll ifeq (shared,$(BUILD)) diff -Nru libsass-3.3.2/Makefile.conf libsass-3.3.4/Makefile.conf --- libsass-3.3.2/Makefile.conf 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/Makefile.conf 2016-03-17 14:51:35.000000000 +0000 @@ -37,7 +37,6 @@ sass2scss.cpp \ source_map.cpp \ to_c.cpp \ - to_string.cpp \ to_value.cpp \ units.cpp \ utf8_string.cpp \ diff -Nru libsass-3.3.2/Readme.md libsass-3.3.4/Readme.md --- libsass-3.3.2/Readme.md 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/Readme.md 2016-03-17 14:51:35.000000000 +0000 @@ -1,9 +1,9 @@ LibSass ======= -by Aaron Leung ([@akhleung]) and Hampton Catlin ([@hcatlin]) +by Aaron Leung ([@akhleung]), Hampton Catlin ([@hcatlin]), Marcel Greter ([@mgreter]) and Michael Mifsud ([@xzyfer]) -[![Linux CI](https://travis-ci.org/sass/libsass.png?branch=master)](https://travis-ci.org/sass/libsass) +[![Linux CI](https://travis-ci.org/sass/libsass.svg?branch=master)](https://travis-ci.org/sass/libsass) [![Windows CI](https://ci.appveyor.com/api/projects/status/github/sass/libsass?svg=true)](https://ci.appveyor.com/project/sass/libsass/branch/master) [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=283068)](https://www.bountysource.com/trackers/283068-libsass?utm_source=283068&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Coverage Status](https://img.shields.io/coveralls/sass/libsass.svg)](https://coveralls.io/r/sass/libsass?branch=feature%2Ftest-travis-ci-3) @@ -13,7 +13,7 @@ LibSass is just a library, but if you want to RUN LibSass, then go to https://github.com/sass/sassc or -https://github.com/sass/ruby-libsass or +https://github.com/sass/sassc-ruby or [find your local implementer](docs/implementations.md). LibSass requires GCC 4.6+ or Clang/LLVM. If your OS is older, this version may not compile. @@ -68,20 +68,16 @@ awesome features to CSS. Sass was the first language of its kind and by far the most mature and up to date codebase. -Sass was originally created by the co-creator of this library, -Hampton Catlin ([@hcatlin]). The extension and continuing evolution -of the language has all been the result of years of work by Natalie -Weizenbaum ([@nex3]) and Chris Eppstein ([@chriseppstein]). +Sass was originally concieved of by the co-creator of this library, +Hampton Catlin ([@hcatlin]). Most of the language has been the result of years +of work by Natalie Weizenbaum ([@nex3]) and Chris Eppstein ([@chriseppstein]). For more information about Sass itself, please visit http://sass-lang.com -Contribution Agreement ----------------------- +Initial development of libsass by Aaron Leung and Hampton Catlin was supported by [Moovweb](http://www.moovweb.com). -Any contribution to the project are seen as copyright assigned to Hampton Catlin, a -human on the planet earth. Your contribution warrants that you have the right to -assign copyright on your work. The intention here is to ensure that the project -remains totally free (liberal, like). +Licensing +--------- Our MIT license is designed to be as simple, and liberal as possible. @@ -89,7 +85,8 @@ [@akhleung]: https://github.com/akhleung [@chriseppstein]: https://github.com/chriseppstein [@nex3]: https://github.com/nex3 +[@mgreter]: https://github.com/mgreter +[@xzyfer]: https://github.com/xzyfer sass2scss was originally written by [Marcel Greter](@mgreter) and he happily agreed to have it merged into the project. - diff -Nru libsass-3.3.2/src/ast.cpp libsass-3.3.4/src/ast.cpp --- libsass-3.3.2/src/ast.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/ast.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,8 +1,9 @@ +#include "sass.hpp" #include "ast.hpp" #include "context.hpp" #include "node.hpp" #include "extend.hpp" -#include "to_string.hpp" +#include "emitter.hpp" #include "color_maps.hpp" #include #include @@ -58,7 +59,10 @@ bool Compound_Selector::has_parent_ref() { - return has_parent_reference(); + for (Simple_Selector* s : *this) { + if (s->has_parent_ref()) return true; + } + return false; } bool Complex_Selector::has_parent_ref() @@ -205,10 +209,8 @@ bool Simple_Selector::operator== (const Simple_Selector& rhs) const { - const Attribute_Selector* ll = dynamic_cast(this); - const Attribute_Selector* rr = dynamic_cast(&rhs); - if (ll && rr) return *ll == *rr; - + if (const Wrapped_Selector* lw = dynamic_cast(this)) return *lw == rhs; + if (const Attribute_Selector* la = dynamic_cast(this)) return *la == rhs; if (is_ns_eq(ns(), rhs.ns())) { return name() == rhs.name(); } return ns() == rhs.ns(); @@ -216,10 +218,8 @@ bool Simple_Selector::operator< (const Simple_Selector& rhs) const { - const Attribute_Selector* ll = dynamic_cast(this); - const Attribute_Selector* rr = dynamic_cast(&rhs); - if (ll && rr) return *ll < *rr; - + if (const Wrapped_Selector* lw = dynamic_cast(this)) return *lw < rhs; + if (const Attribute_Selector* la = dynamic_cast(this)) return *la < rhs; if (is_ns_eq(ns(), rhs.ns())) { return name() < rhs.name(); } return ns() < rhs.ns(); @@ -280,9 +280,8 @@ Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx) { - To_String to_string(&ctx); for (size_t i = 0, L = rhs->length(); i < L; ++i) - { if (perform(&to_string) == (*rhs)[i]->perform(&to_string)) return rhs; } + { if (to_string(ctx.c_options) == (*rhs)[i]->to_string(ctx.c_options)) return rhs; } // check for pseudo elements because they are always last size_t i, L; @@ -291,7 +290,7 @@ { 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()) + if ((dynamic_cast((*rhs)[i]) || dynamic_cast((*rhs)[i])) && (*rhs)[L-1]->is_pseudo_element()) { found = true; break; } } } @@ -299,7 +298,7 @@ { for (i = 0, L = rhs->length(); i < L; ++i) { - if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) + if (dynamic_cast((*rhs)[i]) || dynamic_cast((*rhs)[i])) { found = true; break; } } } @@ -500,6 +499,26 @@ return ns() == rhs.ns(); } + bool Wrapped_Selector::operator< (const Wrapped_Selector& rhs) const + { + if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name()) + { return *(selector()) < *(rhs.selector()); } + if (is_ns_eq(ns(), rhs.ns())) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + + bool Wrapped_Selector::operator< (const Simple_Selector& rhs) const + { + if (const Wrapped_Selector* w = dynamic_cast(&rhs)) + { + return *this < *w; + } + if (is_ns_eq(ns(), rhs.ns())) + { return name() < rhs.name(); } + return ns() < rhs.ns(); + } + bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub) { if (this->name() != sub->name()) return false; @@ -531,8 +550,6 @@ bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping) { - To_String to_string; - Compound_Selector* lhs = this; Simple_Selector* lbase = lhs->base(); Simple_Selector* rbase = rhs->base(); @@ -543,7 +560,7 @@ for (size_t i = 0, L = length(); i < L; ++i) { if ((*this)[i]->is_pseudo_element()) { - std::string pseudo((*this)[i]->perform(&to_string)); + std::string pseudo((*this)[i]->to_string()); pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving lpsuedoset.insert(pseudo); } @@ -551,7 +568,7 @@ for (size_t i = 0, L = rhs->length(); i < L; ++i) { if ((*rhs)[i]->is_pseudo_element()) { - std::string pseudo((*rhs)[i]->perform(&to_string)); + std::string pseudo((*rhs)[i]->to_string()); pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving rpsuedoset.insert(pseudo); } @@ -564,11 +581,11 @@ if (lbase && rbase) { - if (lbase->perform(&to_string) == rbase->perform(&to_string)) { + if (lbase->to_string() == rbase->to_string()) { for (size_t i = 1, L = length(); i < L; ++i) - { lset.insert((*this)[i]->perform(&to_string)); } + { lset.insert((*this)[i]->to_string()); } for (size_t i = 1, L = rhs->length(); i < L; ++i) - { rset.insert((*rhs)[i]->perform(&to_string)); } + { rset.insert((*rhs)[i]->to_string()); } return includes(rset.begin(), rset.end(), lset.begin(), lset.end()); } return false; @@ -602,13 +619,13 @@ if (wrapped->name() == wrapped_r->name()) { if (wrapped->is_superselector_of(wrapped_r)) { continue; - rset.insert(lhs->perform(&to_string)); + rset.insert(lhs->to_string()); }} } } // match from here on as strings - lset.insert(lhs->perform(&to_string)); + lset.insert(lhs->to_string()); } for (size_t n = 0, nL = rhs->length(); n < nL; ++n) @@ -631,7 +648,7 @@ } } } - rset.insert(r->perform(&to_string)); + rset.insert(r->to_string()); } //for (auto l : lset) { cerr << "l: " << l << endl; } @@ -767,7 +784,6 @@ bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping) { Complex_Selector* lhs = this; - To_String to_string; // check for selectors with leading or trailing combinators if (!lhs->head() || !rhs->head()) { return false; } @@ -793,7 +809,7 @@ if (lhs_tail->combinator() != rhs_tail->combinator()) return false; if (lhs_tail->head() && !rhs_tail->head()) return false; if (!lhs_tail->head() && rhs_tail->head()) return false; - if (lhs_tail->head() && lhs_tail->head()) { + if (lhs_tail->head() && rhs_tail->head()) { if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false; } } @@ -933,6 +949,7 @@ Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx) { + if (!this->has_parent_ref()) return this; Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); @@ -955,11 +972,12 @@ if (head && head->length() > 0) { + Selector_List* retval = 0; // we have a parent selector in a simple compound list // mix parent complex selector into the compound list if (dynamic_cast((*head)[0])) { + retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); if (parents && parents->length()) { - Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); if (tails && tails->length() > 0) { for (size_t n = 0, nL = tails->length(); n < nL; ++n) { for (size_t i = 0, iL = parents->length(); i < iL; ++i) { @@ -998,11 +1016,9 @@ *retval << s; } } - return retval; } // have no parent but some tails else { - Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); if (tails && tails->length() > 0) { for (size_t n = 0, nL = tails->length(); n < nL; ++n) { Complex_Selector* cpy = this->clone(ctx); @@ -1023,14 +1039,23 @@ if (!cpy->head()->length()) cpy->head(0); *retval << cpy->skip_empty_reference(); } - return retval; } } // no parent selector in head else { - return this->tails(ctx, tails); + retval = this->tails(ctx, tails); + } + + for (Simple_Selector* ss : *head) { + if (Wrapped_Selector* ws = dynamic_cast(ss)) { + if (Selector_List* sl = dynamic_cast(ws->selector())) { + if (parents) ws->selector(sl->parentize(parents, ctx)); + } + } } + return retval; + } // has no head else { @@ -1061,18 +1086,16 @@ Complex_Selector* Complex_Selector::first() { // declare variables used in loop - Complex_Selector* cur = this->tail_; - const Compound_Selector* head = head_; + Complex_Selector* cur = this; + const Compound_Selector* head; // processing loop while (cur) { // get the head head = cur->head_; - // check for single parent ref - if (head && head->length() == 1) - { - // abort (and return) if it is not a parent selector - if (!dynamic_cast((*head)[0])) break; + // abort (and return) if it is not a parent selector + if (!head || head->length() != 1 || !dynamic_cast((*head)[0])) { + break; } // advance to next cur = cur->tail_; @@ -1141,6 +1164,7 @@ Complex_Selector* Complex_Selector::clone(Context& ctx) const { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); + cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); if (tail()) cpy->tail(tail()->clone(ctx)); return cpy; @@ -1149,7 +1173,8 @@ Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const { Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this); - + cpy->is_optional(this->is_optional()); + cpy->media_block(this->media_block()); if (head()) { cpy->head(head()->clone(ctx)); } @@ -1164,13 +1189,16 @@ Compound_Selector* Compound_Selector::clone(Context& ctx) const { Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this); + cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); + cpy->extended(this->extended()); return cpy; } Selector_List* Selector_List::clone(Context& ctx) const { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this); + cpy->is_optional(this->is_optional()); cpy->media_block(this->media_block()); return cpy; } @@ -1178,6 +1206,8 @@ Selector_List* Selector_List::cloneFully(Context& ctx) const { Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate()); + cpy->is_optional(this->is_optional()); + cpy->media_block(this->media_block()); for (size_t i = 0, L = length(); i < L; ++i) { *cpy << (*this)[i]->cloneFully(ctx); } @@ -1213,9 +1243,17 @@ } } + bool Selector_List::has_parent_ref() + { + for (Complex_Selector* s : *this) { + if (s->has_parent_ref()) return true; + } + return false; + } + void Selector_List::adjust_after_pushing(Complex_Selector* c) { - if (c->has_reference()) has_reference(true); + // if (c->has_reference()) has_reference(true); } // it's a superselector if every selector of the right side @@ -1287,8 +1325,8 @@ return final_result; } - void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) { - To_String to_string; + void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) + { Selector_List* extender = this; for (auto complex_sel : extendee->elements()) { @@ -1322,17 +1360,15 @@ std::vector Compound_Selector::to_str_vec() { - To_String to_string; std::vector result; result.reserve(length()); for (size_t i = 0, L = length(); i < L; ++i) - { result.push_back((*this)[i]->perform(&to_string)); } + { result.push_back((*this)[i]->to_string()); } return result; } Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx) { - To_String to_string(&ctx); Compound_Selector* result = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate()); // result->has_parent_reference(has_parent_reference()); @@ -1340,10 +1376,10 @@ for (size_t i = 0, L = length(); i < L; ++i) { bool found = false; - std::string thisSelector((*this)[i]->perform(&to_string)); + std::string thisSelector((*this)[i]->to_string(ctx.c_options)); for (size_t j = 0, M = rhs->length(); j < M; ++j) { - if (thisSelector == (*rhs)[j]->perform(&to_string)) + if (thisSelector == (*rhs)[j]->to_string(ctx.c_options)) { found = true; break; @@ -1362,6 +1398,36 @@ } } + Argument* Arguments::get_rest_argument() + { + Argument* arg = 0; + if (this->has_rest_argument()) { + for (auto a : this->elements()) { + if (a->is_rest_argument()) { + arg = a; + break; + } + } + } + + return arg; + } + + Argument* Arguments::get_keyword_argument() + { + Argument* arg = 0; + if (this->has_keyword_argument()) { + for (auto a : this->elements()) { + if (a->is_keyword_argument()) { + arg = a; + break; + } + } + } + + return arg; + } + void Arguments::adjust_after_pushing(Argument* a) { if (!a->name().empty()) { @@ -1453,7 +1519,13 @@ return u; } - bool Number::is_unitless() + bool Number::is_valid_css_unit() const + { + return numerator_units().size() <= 1 && + denominator_units().size() == 0; + } + + bool Number::is_unitless() const { return numerator_units_.empty() && denominator_units_.empty(); } void Number::normalize(const std::string& prefered, bool strict) @@ -1541,10 +1613,128 @@ } - void Number::convert(const std::string& prefered, bool strict) + // this does not cover all cases (multiple prefered units) + double Number::convert_factor(const Number& n) const + { + + // first make sure same units cancel each other out + // it seems that a map table will fit nicely to do this + // we basically construct exponents for each unit class + // std::map exponents; + // initialize by summing up occurences in unit vectors + // for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[unit_to_class(numerator_units_[i])]; + // for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[unit_to_class(denominator_units_[i])]; + + std::vector l_miss_nums(0); + std::vector l_miss_dens(0); + // create copy since we need these for state keeping + std::vector r_nums(n.numerator_units_); + std::vector r_dens(n.denominator_units_); + + std::vector::const_iterator l_num_it = numerator_units_.begin(); + std::vector::const_iterator l_num_end = numerator_units_.end(); + + bool l_unitless = is_unitless(); + bool r_unitless = n.is_unitless(); + + // overall conversion + double factor = 1; + + // process all left numerators + while (l_num_it != l_num_end) + { + // get and increment afterwards + const std::string l_num = *(l_num_it ++); + + std::vector::iterator r_num_it = r_nums.begin(); + std::vector::iterator r_num_end = r_nums.end(); + + bool found = false; + // search for compatible numerator + while (r_num_it != r_num_end) + { + // get and increment afterwards + const std::string r_num = *(r_num_it); + // get possible converstion factor for units + double conversion = conversion_factor(l_num, r_num, false); + // skip incompatible numerator + if (conversion == 0) { + ++ r_num_it; + continue; + } + // apply to global factor + factor *= conversion; + // remove item from vector + r_nums.erase(r_num_it); + // found numerator + found = true; + break; + } + // maybe we did not find any + // left numerator is leftover + if (!found) l_miss_nums.push_back(l_num); + } + + std::vector::const_iterator l_den_it = denominator_units_.begin(); + std::vector::const_iterator l_den_end = denominator_units_.end(); + + // process all left denominators + while (l_den_it != l_den_end) + { + // get and increment afterwards + const std::string l_den = *(l_den_it ++); + + std::vector::iterator r_den_it = r_dens.begin(); + std::vector::iterator r_den_end = r_dens.end(); + + bool found = false; + // search for compatible denominator + while (r_den_it != r_den_end) + { + // get and increment afterwards + const std::string r_den = *(r_den_it); + // get possible converstion factor for units + double conversion = conversion_factor(l_den, r_den, false); + // skip incompatible denominator + if (conversion == 0) { + ++ r_den_it; + continue; + } + // apply to global factor + factor *= conversion; + // remove item from vector + r_dens.erase(r_den_it); + // found denominator + found = true; + break; + } + // maybe we did not find any + // left denominator is leftover + if (!found) l_miss_dens.push_back(l_den); + } + + // check left-overs (ToDo: might cancel out) + if (l_miss_nums.size() > 0 && !r_unitless) { + throw Exception::IncompatibleUnits(n, *this); + } + if (l_miss_dens.size() > 0 && !r_unitless) { + throw Exception::IncompatibleUnits(n, *this); + } + if (r_nums.size() > 0 && !l_unitless) { + throw Exception::IncompatibleUnits(n, *this); + } + if (r_dens.size() > 0 && !l_unitless) { + throw Exception::IncompatibleUnits(n, *this); + } + + return factor; + } + + // this does not cover all cases (multiple prefered units) + bool Number::convert(const std::string& prefered, bool strict) { - // abort if unit is empty - if (prefered.empty()) return; + // no conversion if unit is empty + if (prefered.empty()) return true; // first make sure same units cancel each other out // it seems that a map table will fit nicely to do this @@ -1626,6 +1816,9 @@ // best precision this way value_ *= factor; + // success? + return true; + } // useful for making one number compatible with another @@ -1661,6 +1854,12 @@ bool Number::operator== (const Expression& rhs) const { if (const Number* r = dynamic_cast(&rhs)) { + size_t lhs_units = numerator_units_.size() + denominator_units_.size(); + size_t rhs_units = r->numerator_units_.size() + r->denominator_units_.size(); + // unitless and only having one unit seems equivalent (will change in future) + if (!lhs_units || !rhs_units) { + return std::fabs(value() - r->value()) < NUMBER_EPSILON; + } return (numerator_units_ == r->numerator_units_) && (denominator_units_ == r->denominator_units_) && std::fabs(value() - r->value()) < NUMBER_EPSILON; @@ -1670,11 +1869,18 @@ bool Number::operator< (const Number& rhs) const { + size_t lhs_units = numerator_units_.size() + denominator_units_.size(); + size_t rhs_units = rhs.numerator_units_.size() + rhs.denominator_units_.size(); + // unitless and only having one unit seems equivalent (will change in future) + if (!lhs_units || !rhs_units) { + return value() < rhs.value(); + } + Number tmp_r(rhs); tmp_r.normalize(find_convertible_unit()); std::string l_unit(unit()); std::string r_unit(tmp_r.unit()); - if (!l_unit.empty() && !r_unit.empty() && unit() != tmp_r.unit()) { + if (unit() != tmp_r.unit()) { error("cannot compare numbers with incompatible units", pstate()); } return value() < tmp_r.value(); @@ -1690,6 +1896,10 @@ return false; } + bool String_Constant::is_invisible() const { + return value_.empty() && quote_mark_ == 0; + } + bool String_Constant::operator== (const Expression& rhs) const { if (const String_Quoted* qstr = dynamic_cast(&rhs)) { @@ -1700,6 +1910,15 @@ return false; } + bool String_Schema::is_left_interpolant(void) const + { + return length() && first()->is_left_interpolant(); + } + bool String_Schema::is_right_interpolant(void) const + { + return length() && last()->is_right_interpolant(); + } + bool String_Schema::operator== (const Expression& rhs) const { if (const String_Schema* r = dynamic_cast(&rhs)) { @@ -1789,330 +2008,39 @@ else { return &sass_null; } } - std::string Map::to_string(bool compressed, int precision) const - { - std::string res(""); - if (empty()) return res; - if (is_invisible()) return res; - bool items_output = false; - for (auto key : keys()) { - if (key->is_invisible()) continue; - if (at(key)->is_invisible()) continue; - if (items_output) res += compressed ? "," : ", "; - Value* v_key = dynamic_cast(key); - Value* v_val = dynamic_cast(at(key)); - if (v_key) res += v_key->to_string(compressed, precision); - res += compressed ? ":" : ": "; - if (v_val) res += v_val->to_string(compressed, precision); - items_output = true; - } - return res; - } - - std::string List::to_string(bool compressed, int precision) const - { - std::string res(""); - if (empty()) return res; - if (is_invisible()) return res; - bool items_output = false; - std::string sep = separator() == SASS_COMMA ? "," : " "; - if (!compressed && sep == ",") sep += " "; - for (size_t i = 0, L = size(); i < L; ++i) { - Expression* item = (*this)[i]; - if (item->is_invisible()) continue; - if (items_output) res += sep; - if (Value* v_val = dynamic_cast(item)) - { res += v_val->to_string(compressed, precision); } - items_output = true; - } - return res; - } - - std::string String_Schema::to_string(bool compressed, int precision) const - { - std::string res(""); - for (size_t i = 0, L = length(); i < L; ++i) { - if ((*this)[i]->is_interpolant()) res += "#{"; - if (Value* val = dynamic_cast((*this)[i])) - { res += val->to_string(compressed, precision); } - if ((*this)[i]->is_interpolant()) res += "}"; - } - return res; - } - - std::string Null::to_string(bool compressed, int precision) const - { - return "null"; - } - - std::string Boolean::to_string(bool compressed, int precision) const - { - return value_ ? "true" : "false"; - } - - // 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; - } - - std::string Color::to_string(bool compressed, int precision) const - { - std::stringstream ss; - - // original color name - // maybe an unknown token - std::string name = disp(); - - // resolved color - std::string res_name = name; - - double r = Sass::round(cap_channel<0xff>(r_)); - double g = Sass::round(cap_channel<0xff>(g_)); - double b = Sass::round(cap_channel<0xff>(b_)); - double a = cap_channel<1> (a_); - - // get color from given name (if one was given at all) - if (name != "" && name_to_color(name)) { - const Color* n = name_to_color(name); - r = Sass::round(cap_channel<0xff>(n->r())); - g = Sass::round(cap_channel<0xff>(n->g())); - b = Sass::round(cap_channel<0xff>(n->b())); - a = cap_channel<1> (n->a()); - } - // otherwise get the possible resolved color name - else { - double numval = r * 0x10000 + g * 0x100 + b; - if (color_to_name(numval)) - res_name = color_to_name(numval); - } - - std::stringstream hexlet; - hexlet << '#' << std::setw(1) << std::setfill('0'); - // create a short color hexlet if there is any need for it - if (compressed && is_color_doublet(r, g, b) && a == 1) { - hexlet << std::hex << std::setw(1) << (static_cast(r) >> 4); - hexlet << std::hex << std::setw(1) << (static_cast(g) >> 4); - hexlet << std::hex << std::setw(1) << (static_cast(b) >> 4); - } else { - hexlet << std::hex << std::setw(2) << static_cast(r); - hexlet << std::hex << std::setw(2) << static_cast(g); - hexlet << std::hex << std::setw(2) << static_cast(b); - } - - if (compressed && !this->is_delayed()) name = ""; - - // retain the originally specified color definition if unchanged - if (name != "") { - ss << name; - } - else if (r == 0 && g == 0 && b == 0 && a == 0) { - ss << "transparent"; - } - else if (a >= 1) { - if (res_name != "") { - if (compressed && hexlet.str().size() < res_name.size()) { - ss << hexlet.str(); - } else { - ss << res_name; - } - } - else { - ss << hexlet.str(); - } - } - else { - ss << "rgba("; - ss << static_cast(r) << ","; - if (!compressed) ss << " "; - ss << static_cast(g) << ","; - if (!compressed) ss << " "; - ss << static_cast(b) << ","; - if (!compressed) ss << " "; - ss << a << ')'; - } - - return ss.str(); - - } - - std::string Number::to_string(bool compressed, int precision) const + bool Binary_Expression::is_left_interpolant(void) const { - - std::string res; - - // check if the fractional part of the value equals to zero - // neat trick from http://stackoverflow.com/a/1521682/1550314 - // double int_part; bool is_int = modf(value, &int_part) == 0.0; - - // this all cannot be done with one run only, since fixed - // output differs from normal output and regular output - // can contain scientific notation which we do not want! - - // first sample - std::stringstream ss; - ss.precision(12); - ss << value_; - - // check if we got scientific notation in result - if (ss.str().find_first_of("e") != std::string::npos) { - ss.clear(); ss.str(std::string()); - ss.precision(std::max(12, precision)); - ss << std::fixed << value_; - } - - std::string tmp = ss.str(); - size_t pos_point = tmp.find_first_of(".,"); - size_t pos_fract = tmp.find_last_not_of("0"); - bool is_int = pos_point == pos_fract || - pos_point == std::string::npos; - - // reset stream for another run - ss.clear(); ss.str(std::string()); - - // take a shortcut for integers - if (is_int) - { - ss.precision(0); - ss << std::fixed << value_; - res = std::string(ss.str()); - } - // process floats - else - { - // do we have have too much precision? - if (pos_fract < precision + pos_point) - { precision = (int)(pos_fract - pos_point); } - // round value again - ss.precision(precision); - ss << std::fixed << value_; - res = std::string(ss.str()); - // maybe we truncated up to decimal point - size_t pos = res.find_last_not_of("0"); - bool at_dec_point = res[pos] == '.' || - res[pos] == ','; - // don't leave a blank point - if (at_dec_point) ++ pos; - res.resize (pos + 1); - } - - // some final cosmetics - if (res == "-0.0") res.erase(0, 1); - else if (res == "-0") res.erase(0, 1); - else if (res == "") res = "0"; - - // add unit now - res += unit(); - - // and return - return res; - + return is_interpolant() || (left() && left()->is_left_interpolant()); } - - std::string String_Quoted::to_string(bool compressed, int precision) const + bool Binary_Expression::is_right_interpolant(void) const { - return quote_mark_ ? quote(value_, quote_mark_, true) : value_; + return is_interpolant() || (right() && right()->is_right_interpolant()); } - std::string String_Constant::to_string(bool compressed, int precision) const + std::string AST_Node::to_string(Sass_Inspect_Options opt) const { - return quote_mark_ ? quote(value_, quote_mark_, true) : value_; + Sass_Output_Options out(opt); + Emitter emitter(out); + Inspect i(emitter); + i.in_declaration = true; + // ToDo: inspect should be const + const_cast(this)->perform(&i); + return i.get_buffer(); } - std::string Custom_Error::to_string(bool compressed, int precision) const - { - return message(); - } - std::string Custom_Warning::to_string(bool compressed, int precision) const + std::string AST_Node::to_string() const { - return message(); + return to_string({ NESTED, 5 }); } - std::string Selector_List::to_string(bool compressed, int precision) const + std::string String_Quoted::inspect() const { - std::string str(""); - auto end = this->end(); - auto start = this->begin(); - while (start < end && *start) { - Complex_Selector* sel = *start; - if (!str.empty()) str += ", "; - str += sel->to_string(compressed, precision); - ++ start; - } - return str; + return quote(value_, '*'); } - std::string Compound_Selector::to_string(bool compressed, int precision) const + std::string String_Constant::inspect() const { - std::string str(""); - auto end = this->end(); - auto start = this->begin(); - while (start < end && *start) { - Simple_Selector* sel = *start; - str += sel->to_string(compressed, precision); - ++ start; - } - return str; - } - - std::string Complex_Selector::to_string(bool compressed, int precision) const - { - // first render head and tail if they are available - std::string str_head(head() ? head()->to_string(compressed, precision) : ""); - std::string str_tail(tail() ? tail()->to_string(compressed, precision) : ""); - std::string str_ref(reference() ? reference()->to_string(compressed, precision) : ""); - // combinator in between - std::string str_op(""); - // use a switch statement - switch (combinator()) { - case ANCESTOR_OF: str_op = " "; break; - case PARENT_OF: str_op = ">"; break; - case PRECEDES: str_op = "~"; break; - case ADJACENT_TO: str_op = "+"; break; - case REFERENCE: str_op = "/" + str_ref + "/"; break; - } - // prettify for non ancestors - if (combinator() != ANCESTOR_OF) { - // no spaces needed for compressed - if (compressed == false) { - // make sure we add some spaces where needed - if (str_tail != "") str_op += " "; - if (str_head != "") str_head += " "; - } - } - // is ancestor with no tail - else if (str_tail == "") { - str_op = ""; // superflous - } - // now build the final result - return str_head + str_op + str_tail; - } - - std::string Selector_Schema::to_string(bool compressed, int precision) const - { - return contents()->to_string(compressed, precision); - } - - std::string Parent_Selector::to_string(bool compressed, int precision) const - { - return "&"; - } - - std::string Attribute_Selector::to_string(bool compressed, int precision) const - { - std::string val(value() ? value()->to_string(compressed, precision) : ""); - return "[" + this->ns_name() + this->matcher() + val + "]"; - } - - std::string Wrapped_Selector::to_string(bool compressed, int precision) const - { - // first render the - std::string main(this->Simple_Selector::to_string(compressed, precision)); - std::string wrapped(selector() ? selector()->to_string(compressed, precision) : ""); - // now build the final result - return main + "(" + wrapped + ")"; + return quote(value_, '*'); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -2131,4 +2059,3 @@ } } - diff -Nru libsass-3.3.2/src/ast.hpp libsass-3.3.4/src/ast.hpp --- libsass-3.3.2/src/ast.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/ast.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -44,15 +44,29 @@ #include "error_handling.hpp" #include "ast_def_macros.hpp" #include "ast_fwd_decl.hpp" -#include "to_string.hpp" #include "source_map.hpp" #include "sass.h" namespace Sass { + // ToDo: should this really be hardcoded + // Note: most methods follow precision option const double NUMBER_EPSILON = 0.00000000000001; + // ToDo: where does this fit best? + // We don't share this with C-API? + class Operand { + public: + Operand(Sass_OP operand, bool ws_before = false, bool ws_after = false) + : operand(operand), ws_before(ws_before), ws_after(ws_after) + { } + public: + enum Sass_OP operand; + bool ws_before; + bool ws_after; + }; + // from boost (functional/hash): // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html // Boost Software License - Version 1.0 @@ -74,6 +88,11 @@ : pstate_(pstate) { } virtual ~AST_Node() = 0; + virtual size_t hash() { return 0; } + virtual std::string inspect() const { return to_string({ INSPECT, 5 }); } + virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); } + virtual std::string to_string(Sass_Inspect_Options opt) const; + virtual std::string to_string() const; // virtual Block* block() { return 0; } public: void update_pstate(const ParserState& pstate); @@ -129,10 +148,27 @@ virtual bool is_false() { return false; } virtual bool operator== (const Expression& rhs) const { return false; } virtual void set_delayed(bool delayed) { is_delayed(delayed); } + virtual bool has_interpolant() const { return is_interpolant(); } + virtual bool is_left_interpolant() const { return is_interpolant(); } + virtual bool is_right_interpolant() const { return is_interpolant(); } + virtual std::string inspect() const { return to_string({ INSPECT, 5 }); } + virtual std::string to_sass() const { return to_string({ TO_SASS, 5 }); } virtual size_t hash() { return 0; } }; ////////////////////////////////////////////////////////////////////// + // Still just an expression, but with a to_string method + ////////////////////////////////////////////////////////////////////// + class PreValue : public Expression { + public: + PreValue(ParserState pstate, + bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE) + : Expression(pstate, d, e, i, ct) + { } + virtual ~PreValue() { } + }; + + ////////////////////////////////////////////////////////////////////// // base class for values that support operations ////////////////////////////////////////////////////////////////////// class Value : public Expression { @@ -142,7 +178,6 @@ : Expression(pstate, d, e, i, ct) { } virtual bool operator== (const Expression& rhs) const = 0; - virtual std::string to_string(bool compressed = false, int precision = 5) const = 0; }; } @@ -189,8 +224,8 @@ virtual ~Vectorized() = 0; size_t length() const { return elements_.size(); } bool empty() const { return elements_.empty(); } - T last() { return elements_.back(); } - T first() { return elements_.front(); } + T last() const { return elements_.back(); } + T first() const { return elements_.front(); } T& operator[](size_t i) { return elements_[i]; } virtual const T& at(size_t i) const { return elements_.at(i); } const T& operator[](size_t i) const { return elements_[i]; } @@ -216,6 +251,16 @@ const std::vector& elements() const { return elements_; } std::vector& elements(std::vector& e) { elements_ = e; return elements_; } + virtual size_t hash() + { + if (hash_ == 0) { + for (T& el : elements_) { + hash_combine(hash_, el->hash()); + } + } + return hash_; + } + typename std::vector::iterator end() { return elements_.end(); } typename std::vector::iterator begin() { return elements_.begin(); } typename std::vector::const_iterator end() const { return elements_.end(); } @@ -820,8 +865,8 @@ std::string type() { return is_arglist_ ? "arglist" : "list"; } static std::string type_name() { return "list"; } const char* sep_string(bool compressed = false) const { - return separator() == SASS_COMMA ? - (compressed ? "," : ", ") : " "; + return separator() == SASS_SPACE ? + " " : (compressed ? "," : ", "); } bool is_invisible() const { return empty(); } Expression* value_at_index(size_t i); @@ -846,7 +891,6 @@ } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -879,29 +923,49 @@ } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; + inline static const std::string sass_op_to_name(enum Sass_OP op) { + switch (op) { + case AND: return "and"; break; + case OR: return "or"; break; + case EQ: return "eq"; break; + case NEQ: return "neq"; break; + case GT: return "gt"; break; + case GTE: return "gte"; break; + case LT: return "lt"; break; + case LTE: return "lte"; break; + case ADD: return "plus"; break; + case SUB: return "sub"; break; + case MUL: return "times"; break; + case DIV: return "div"; break; + case MOD: return "mod"; break; + // this is only used internally! + case NUM_OPS: return "[OPS]"; break; + default: return "invalid"; break; + } + } + ////////////////////////////////////////////////////////////////////////// // Binary expressions. Represents logical, relational, and arithmetic // operations. Templatized to avoid large switch statements and repetitive // subclassing. ////////////////////////////////////////////////////////////////////////// - class Binary_Expression : public Expression { + class Binary_Expression : public PreValue { private: - ADD_HASHED(enum Sass_OP, type) + ADD_HASHED(Operand, op) ADD_HASHED(Expression*, left) ADD_HASHED(Expression*, right) size_t hash_; public: Binary_Expression(ParserState pstate, - enum Sass_OP t, Expression* lhs, Expression* rhs) - : Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0) + Operand op, Expression* lhs, Expression* rhs) + : PreValue(pstate), op_(op), left_(lhs), right_(rhs), hash_(0) { } const std::string type_name() { - switch (type_) { + switch (type()) { case AND: return "and"; break; case OR: return "or"; break; case EQ: return "eq"; break; @@ -915,10 +979,38 @@ case MUL: return "mul"; break; case DIV: return "div"; break; case MOD: return "mod"; break; - case NUM_OPS: return "num_ops"; break; + // this is only used internally! + case NUM_OPS: return "[OPS]"; break; + default: return "invalid"; break; + } + } + const std::string separator() { + switch (type()) { + case AND: return "&&"; break; + case OR: return "||"; break; + case EQ: return "=="; break; + case NEQ: return "!="; break; + case GT: return ">"; break; + case GTE: return ">="; break; + case LT: return "<"; break; + case LTE: return "<="; break; + case ADD: return "+"; break; + case SUB: return "-"; break; + case MUL: return "*"; break; + case DIV: return "/"; break; + case MOD: return "%"; break; + // this is only used internally! + case NUM_OPS: return "[OPS]"; break; default: return "invalid"; break; } } + bool is_left_interpolant(void) const; + bool is_right_interpolant(void) const; + bool has_interpolant() const + { + return is_left_interpolant() || + is_right_interpolant(); + } virtual void set_delayed(bool delayed) { right()->set_delayed(delayed); @@ -944,12 +1036,13 @@ virtual size_t hash() { if (hash_ == 0) { - hash_ = std::hash()(type_); + hash_ = std::hash()(type()); hash_combine(hash_, left()->hash()); hash_combine(hash_, right()->hash()); } return hash_; } + enum Sass_OP type() const { return op_.operand; } ATTACH_OPERATIONS() }; @@ -1065,23 +1158,27 @@ has_rest_argument_(false), has_keyword_argument_(false) { } + + Argument* get_rest_argument(); + Argument* get_keyword_argument(); + ATTACH_OPERATIONS() }; ////////////////// // Function calls. ////////////////// - class Function_Call : public Expression { + class Function_Call : public PreValue { ADD_HASHED(std::string, name) ADD_HASHED(Arguments*, arguments) ADD_PROPERTY(void*, cookie) size_t hash_; public: Function_Call(ParserState pstate, std::string n, Arguments* args, void* cookie) - : Expression(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0) + : PreValue(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0) { concrete_type(STRING); } Function_Call(ParserState pstate, std::string n, Arguments* args) - : Expression(pstate), name_(n), arguments_(args), cookie_(0), hash_(0) + : PreValue(pstate), name_(n), arguments_(args), cookie_(0), hash_(0) { concrete_type(STRING); } virtual bool operator==(const Expression& rhs) const @@ -1131,11 +1228,11 @@ /////////////////////// // Variable references. /////////////////////// - class Variable : public Expression { + class Variable : public PreValue { ADD_PROPERTY(std::string, name) public: Variable(ParserState pstate, std::string n) - : Expression(pstate), name_(n) + : PreValue(pstate), name_(n) { } virtual bool operator==(const Expression& rhs) const @@ -1214,7 +1311,8 @@ size_t hash_; public: Number(ParserState pstate, double val, std::string u = "", bool zero = true); - bool zero() { return zero_; } + bool zero() { return zero_; } + bool is_valid_css_unit() const; std::vector& numerator_units() { return numerator_units_; } std::vector& denominator_units() { return denominator_units_; } const std::vector& numerator_units() const { return numerator_units_; } @@ -1223,8 +1321,9 @@ static std::string type_name() { return "number"; } std::string unit() const; - bool is_unitless(); - void convert(const std::string& unit = "", bool strict = false); + bool is_unitless() const; + double convert_factor(const Number&) const; + bool convert(const std::string& unit = "", bool strict = false); void normalize(const std::string& unit = "", bool strict = false); // useful for making one number compatible with another std::string find_convertible_unit() const; @@ -1243,7 +1342,6 @@ virtual bool operator< (const Number& rhs) const; virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1256,12 +1354,11 @@ ADD_HASHED(double, g) ADD_HASHED(double, b) ADD_HASHED(double, a) - ADD_PROPERTY(bool, sixtuplet) ADD_PROPERTY(std::string, disp) size_t hash_; public: - Color(ParserState pstate, double r, double g, double b, double a = 1, bool sixtuplet = true, const std::string disp = "") - : Value(pstate), r_(r), g_(g), b_(b), a_(a), sixtuplet_(sixtuplet), disp_(disp), + Color(ParserState pstate, double r, double g, double b, double a = 1, const std::string disp = "") + : Value(pstate), r_(r), g_(g), b_(b), a_(a), disp_(disp), hash_(0) { concrete_type(COLOR); } std::string type() { return "color"; } @@ -1279,7 +1376,6 @@ } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1294,7 +1390,6 @@ : Value(pstate), message_(msg) { concrete_type(C_ERROR); } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1308,7 +1403,6 @@ : Value(pstate), message_(msg) { concrete_type(C_WARNING); } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1337,7 +1431,6 @@ } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1355,7 +1448,6 @@ static std::string type_name() { return "string"; } virtual ~String() = 0; virtual bool operator==(const Expression& rhs) const = 0; - virtual std::string to_string(bool compressed = false, int precision = 5) const = 0; ATTACH_OPERATIONS() }; inline String::~String() { }; @@ -1365,15 +1457,25 @@ // evaluation phase. /////////////////////////////////////////////////////////////////////// class String_Schema : public String, public Vectorized { - ADD_PROPERTY(bool, has_interpolants) + // ADD_PROPERTY(bool, has_interpolants) size_t hash_; public: String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false) - : String(pstate), Vectorized(size), has_interpolants_(has_interpolants), hash_(0) + : String(pstate), Vectorized(size), hash_(0) { concrete_type(STRING); } std::string type() { return "string"; } static std::string type_name() { return "string"; } + bool is_left_interpolant(void) const; + bool is_right_interpolant(void) const; + // void has_interpolants(bool tc) { } + bool has_interpolants() { + for (auto el : elements()) { + if (el->is_interpolant()) return true; + } + return false; + } + virtual size_t hash() { if (hash_ == 0) { @@ -1384,7 +1486,6 @@ } virtual bool operator==(const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1413,6 +1514,7 @@ { } std::string type() { return "string"; } static std::string type_name() { return "string"; } + virtual bool is_invisible() const; virtual size_t hash() { @@ -1423,7 +1525,7 @@ } virtual bool operator==(const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; + virtual std::string inspect() const; // quotes are forced on inspection // static char auto_quote() { return '*'; } static char double_quote() { return '"'; } @@ -1444,7 +1546,7 @@ if (q && quote_mark_) quote_mark_ = q; } virtual bool operator==(const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; + virtual std::string inspect() const; // quotes are forced on inspection ATTACH_OPERATIONS() }; @@ -1581,8 +1683,7 @@ { } bool exclude(std::string str) { - To_String to_string; - bool with = feature() && unquote(feature()->perform(&to_string)).compare("with") == 0; + bool with = feature() && unquote(feature()->to_string()).compare("with") == 0; List* l = static_cast(value()); std::string v; @@ -1591,7 +1692,7 @@ if (!l || l->length() == 0) return str.compare("rule") != 0; for (size_t i = 0, L = l->length(); i < L; ++i) { - v = unquote((*l)[i]->perform(&to_string)); + v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return false; } return true; @@ -1601,7 +1702,7 @@ if (!l || !l->length()) return str.compare("rule") == 0; for (size_t i = 0, L = l->length(); i < L; ++i) { - v = unquote((*l)[i]->perform(&to_string)); + v = unquote((*l)[i]->to_string()); if (v.compare("all") == 0 || v == str) return true; } return false; @@ -1665,7 +1766,6 @@ } virtual bool operator== (const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1747,7 +1847,7 @@ // Abstract base class for CSS selectors. ///////////////////////////////////////// class Selector : public Expression { - ADD_PROPERTY(bool, has_reference) + // ADD_PROPERTY(bool, has_reference) ADD_PROPERTY(bool, has_placeholder) // line break before list separator ADD_PROPERTY(bool, has_line_feed) @@ -1757,21 +1857,27 @@ ADD_PROPERTY(bool, is_optional) // parent block pointers ADD_PROPERTY(Media_Block*, media_block) + protected: + size_t hash_; public: Selector(ParserState pstate, bool r = false, bool h = false) : Expression(pstate), - has_reference_(r), + // has_reference_(r), has_placeholder_(h), has_line_feed_(false), has_line_break_(false), is_optional_(false), - media_block_(0) + media_block_(0), + hash_(0) { concrete_type(SELECTOR); } virtual ~Selector() = 0; + virtual size_t hash() = 0; + virtual bool has_parent_ref() { + return false; + } virtual unsigned long specificity() { return Constants::Specificity_Universal; } - virtual std::string to_string(bool compressed = false, int precision = 5) const = 0; }; inline Selector::~Selector() { } @@ -1786,7 +1892,12 @@ Selector_Schema(ParserState pstate, String* c) : Selector(pstate), contents_(c), at_root_(false) { } - virtual std::string to_string(bool compressed = false, int precision = 5) const; + virtual size_t hash() { + if (hash_ == 0) { + hash_combine(hash_, contents_->hash()); + } + return hash_; + } ATTACH_OPERATIONS() }; @@ -1816,6 +1927,15 @@ name += ns_ + "|"; return name + name_; } + virtual size_t hash() + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(ns())); + hash_combine(hash_, std::hash()(name())); + } + return hash_; + } // namespace query functions bool is_universal_ns() const { @@ -1856,7 +1976,6 @@ bool operator<(const Simple_Selector& rhs) const; // default implementation should work for most of the simple selectors (otherwise overload) - virtual std::string to_string(bool compressed = false, int precision = 5) const { return this->ns_name(); }; ATTACH_OPERATIONS(); }; inline Simple_Selector::~Simple_Selector() { } @@ -1872,7 +1991,7 @@ public: Parent_Selector(ParserState pstate) : Simple_Selector(pstate, "&") - { has_reference(true); } + { /* has_reference(true); */ } virtual bool has_parent_ref() { return true; }; virtual unsigned long specificity() { @@ -1880,7 +1999,6 @@ } std::string type() { return "selector"; } static std::string type_name() { return "selector"; } - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1944,6 +2062,15 @@ Attribute_Selector(ParserState pstate, std::string n, std::string m, String* v) : Simple_Selector(pstate, n), matcher_(m), value_(v) { } + virtual size_t hash() + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + hash_combine(hash_, std::hash()(matcher())); + if (value_) hash_combine(hash_, value_->hash()); + } + return hash_; + } virtual unsigned long specificity() { return Constants::Specificity_Attr; @@ -1952,7 +2079,6 @@ bool operator==(const Attribute_Selector& rhs) const; bool operator<(const Simple_Selector& rhs) const; bool operator<(const Attribute_Selector& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -1971,6 +2097,7 @@ name == ":first-letter"; } + // Pseudo Selector cannot have any namespace? class Pseudo_Selector : public Simple_Selector { ADD_PROPERTY(String*, expression) public: @@ -1999,6 +2126,14 @@ return (name_[0] == ':' && name_[1] == ':') || is_pseudo_class_element(name_); } + virtual size_t hash() + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (expression_) hash_combine(hash_, expression_->hash()); + } + return hash_; + } virtual unsigned long specificity() { if (is_pseudo_element()) @@ -2018,16 +2153,30 @@ Wrapped_Selector(ParserState pstate, std::string n, Selector* sel) : Simple_Selector(pstate, n), selector_(sel) { } + virtual bool has_parent_ref() { + // if (has_reference()) return true; + if (!selector()) return false; + return selector()->has_parent_ref(); + } virtual bool is_superselector_of(Wrapped_Selector* sub); // Selectors inside the negation pseudo-class are counted like any // other, but the negation itself does not count as a pseudo-class. + virtual size_t hash() + { + if (hash_ == 0) { + hash_combine(hash_, Simple_Selector::hash()); + if (selector_) hash_combine(hash_, selector_->hash()); + } + return hash_; + } virtual unsigned long specificity() { return selector_ ? selector_->specificity() : 0; } bool operator==(const Simple_Selector& rhs) const; bool operator==(const Wrapped_Selector& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; + bool operator<(const Simple_Selector& rhs) const; + bool operator<(const Wrapped_Selector& rhs) const; ATTACH_OPERATIONS() }; @@ -2043,17 +2192,19 @@ class Compound_Selector : public Selector, public Vectorized { private: SourcesSet sources_; + ADD_PROPERTY(bool, extended); ADD_PROPERTY(bool, has_parent_reference); protected: void adjust_after_pushing(Simple_Selector* s) { - if (s->has_reference()) has_reference(true); + // if (s->has_reference()) has_reference(true); if (s->has_placeholder()) has_placeholder(true); } public: Compound_Selector(ParserState pstate, size_t s = 0) : Selector(pstate), Vectorized(s), + extended_(false), has_parent_reference_(false) { } bool contains_placeholder() { @@ -2079,14 +2230,22 @@ } const Simple_Selector* base() const { if (length() == 0) return 0; - if (typeid(*(*this)[0]) == typeid(Type_Selector)) + // ToDo: why is this needed? + if (dynamic_cast((*this)[0])) return (*this)[0]; -// else cerr << "SERIOUSELY " << "\n"; return 0; } virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapped = ""); virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapped = ""); virtual bool is_superselector_of(Selector_List* sub, std::string wrapped = ""); + virtual size_t hash() + { + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + if (length()) hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; + } virtual unsigned long specificity() { int sum = 0; @@ -2098,7 +2257,7 @@ bool is_empty_reference() { return length() == 1 && - typeid(*(*this)[0]) == typeid(Parent_Selector); + dynamic_cast((*this)[0]); } std::vector to_str_vec(); // sometimes need to convert to a flat "by-value" data structure @@ -2114,7 +2273,6 @@ Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s Compound_Selector* minus(Compound_Selector* rhs, Context& ctx); - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -2147,7 +2305,7 @@ head_(h), tail_(t), reference_(r) { - if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); + // if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); } virtual bool has_parent_ref(); @@ -2202,6 +2360,16 @@ Combinator clear_innermost(); void append(Context&, Complex_Selector*); void set_innermost(Complex_Selector*, Combinator); + virtual size_t hash() + { + if (hash_ == 0) { + hash_combine(hash_, std::hash()(SELECTOR)); + hash_combine(hash_, std::hash()(combinator_)); + if (head_) hash_combine(hash_, head_->hash()); + if (tail_) hash_combine(hash_, tail_->hash()); + } + return hash_; + } virtual unsigned long specificity() const { int sum = 0; @@ -2263,7 +2431,6 @@ Complex_Selector* clone(Context&) const; // does not clone Compound_Selector*s Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s // std::vector to_vector(); - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; @@ -2284,6 +2451,7 @@ std::string type() { return "list"; } // remove parent selector references // basically unwraps parsed selectors + virtual bool has_parent_ref(); void remove_parent_selectors(); // virtual Selector_Placeholder* find_placeholder(); Selector_List* parentize(Selector_List* parents, Context& ctx); @@ -2292,6 +2460,14 @@ virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = ""); Selector_List* unify_with(Selector_List*, Context&); void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&); + virtual size_t hash() + { + if (Selector::hash_ == 0) { + hash_combine(Selector::hash_, std::hash()(SELECTOR)); + hash_combine(Selector::hash_, Vectorized::hash()); + } + return Selector::hash_; + } virtual unsigned long specificity() { unsigned long sum = 0; @@ -2309,7 +2485,6 @@ virtual bool operator==(const Selector_List& rhs) const; // Selector Lists can be compared to comma lists virtual bool operator==(const Expression& rhs) const; - virtual std::string to_string(bool compressed = false, int precision = 5) const; ATTACH_OPERATIONS() }; diff -Nru libsass-3.3.2/src/base64vlq.cpp libsass-3.3.4/src/base64vlq.cpp --- libsass-3.3.2/src/base64vlq.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/base64vlq.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "base64vlq.hpp" namespace Sass { diff -Nru libsass-3.3.2/src/bind.cpp libsass-3.3.4/src/bind.cpp --- libsass-3.3.2/src/bind.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/bind.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "bind.hpp" #include "ast.hpp" #include "context.hpp" @@ -5,14 +6,14 @@ #include #include #include -#include "to_string.hpp" namespace Sass { void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval) { std::string callee(type + " " + name); - Listize listize(*ctx); + + Listize listize(ctx->mem); std::map param_map; for (size_t i = 0, L = as->length(); i < L; ++i) { @@ -60,18 +61,9 @@ if (p->is_rest_parameter()) { // The next argument by coincidence provides a rest argument if (a->is_rest_argument()) { + // We should always get a list for rest arguments if (List* rest = dynamic_cast(a->value())) { - // arg contains a list - List* args = rest; - // make sure it's an arglist - if (rest->is_arglist()) { - // can pass it through as it was - env->local_frame()[p->name()] = args; - } - // create a new list and wrap each item as an argument - // otherwise we will not be able to fetch it again - else { // create a new list object for wrapped items List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), @@ -80,17 +72,20 @@ true); // wrap each item from list as an argument for (Expression* item : rest->elements()) { - (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, - item->pstate(), - item, - "", - false, - false); + if (Argument* arg = dynamic_cast(item)) { + (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); + } else { + (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, + item->pstate(), + item, + "", + false, + false); + } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; } - } // invalid state else { throw std::runtime_error("invalid state"); @@ -127,27 +122,27 @@ List* ls = dynamic_cast(a->value()); // skip any list completely if empty if (ls && ls->empty() && a->is_rest_argument()) continue; - // flatten all nested arglists - if (ls && ls->is_arglist()) { - for (size_t i = 0, L = ls->size(); i < L; ++i) { - // already have a wrapped argument - if (Argument* arg = dynamic_cast((*ls)[i])) { - (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); - } - // wrap all other value types into Argument - else { + + if (Argument* arg = dynamic_cast(a->value())) { + (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); + } + // check if we have rest argument + else if (a->is_rest_argument()) { + // preserve the list separator from rest args + if (List* rest = dynamic_cast(a->value())) { + arglist->separator(rest->separator()); + + for (size_t i = 0, L = rest->size(); i < L; ++i) { (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, - (*ls)[i]->pstate(), - (*ls)[i], + (*rest)[i]->pstate(), + (*rest)[i], "", false, false); } } - } - // already have a wrapped argument - else if (Argument* arg = dynamic_cast(a->value())) { - (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg); + // no more arguments + break; } // wrap all other value types into Argument else { @@ -158,15 +153,6 @@ false, false); } - // check if we have rest argument - if (a->is_rest_argument()) { - // preserve the list separator from rest args - if (List* rest = dynamic_cast(a->value())) { - arglist->separator(rest->separator()); - } - // no more arguments - break; - } } // assign new arglist to environment env->local_frame()[p->name()] = arglist; @@ -185,7 +171,7 @@ if (!arglist->length()) { break; } else { - if (arglist->length() + ia > LP && !ps->has_rest_parameter()) { + if (arglist->length() > LP - ip && !ps->has_rest_parameter()) { int arg_count = (arglist->length() + LA - 1); std::stringstream msg; msg << callee << " takes " << LP; @@ -193,6 +179,10 @@ msg << " but " << arg_count; msg << (arg_count == 1 ? " was passed" : " were passed."); deprecated_bind(msg.str(), as->pstate()); + + while (arglist->length() > LP - ip) { + arglist->elements().erase(arglist->elements().end() - 1); + } } } // otherwise move one of the rest args into the param, converting to argument if necessary @@ -209,6 +199,7 @@ if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) { ++ia; } + } else if (a->is_keyword_argument()) { Map* argmap = static_cast(a->value()); @@ -267,7 +258,6 @@ // 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(ctx); Parameter* leftover = (*ps)[i]; // cerr << "env for default params:" << endl; // env->print(); diff -Nru libsass-3.3.2/src/bind.hpp libsass-3.3.4/src/bind.hpp --- libsass-3.3.2/src/bind.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/bind.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -2,6 +2,7 @@ #define SASS_BIND_H #include +#include "listize.hpp" #include "environment.hpp" namespace Sass { diff -Nru libsass-3.3.2/src/color_maps.cpp libsass-3.3.4/src/color_maps.cpp --- libsass-3.3.2/src/color_maps.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/color_maps.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "ast.hpp" #include "color_maps.hpp" diff -Nru libsass-3.3.2/src/constants.cpp libsass-3.3.4/src/constants.cpp --- libsass-3.3.2/src/constants.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/constants.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "constants.hpp" namespace Sass { @@ -78,7 +79,7 @@ extern const char keyframes_kwd[] = "keyframes"; extern const char only_kwd[] = "only"; extern const char rgb_kwd[] = "rgb("; - extern const char url_kwd[] = "url("; + extern const char url_kwd[] = "url"; // extern const char url_prefix_kwd[] = "url-prefix("; extern const char important_kwd[] = "important"; extern const char pseudo_not_kwd[] = ":not("; @@ -86,6 +87,7 @@ extern const char odd_kwd[] = "odd"; extern const char progid_kwd[] = "progid"; extern const char expression_kwd[] = "expression"; + extern const char calc_fn_kwd[] = "calc"; extern const char calc_kwd[] = "calc("; extern const char moz_calc_kwd[] = "-moz-calc("; extern const char webkit_calc_kwd[] = "-webkit-calc("; @@ -124,6 +126,7 @@ extern const char rbrace[] = "}"; extern const char rparen[] = ")"; extern const char sign_chars[] = "-+"; + extern const char op_chars[] = "-+"; extern const char hyphen[] = "-"; extern const char ellipsis[] = "..."; // extern const char url_space_chars[] = " \t\r\n\f"; diff -Nru libsass-3.3.2/src/constants.hpp libsass-3.3.4/src/constants.hpp --- libsass-3.3.2/src/constants.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/constants.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -81,7 +81,6 @@ extern const char rgb_kwd[]; extern const char url_kwd[]; // extern const char url_prefix_kwd[]; - extern const char image_url_kwd[]; extern const char important_kwd[]; extern const char pseudo_not_kwd[]; extern const char even_kwd[]; @@ -89,6 +88,7 @@ extern const char progid_kwd[]; extern const char expression_kwd[]; extern const char calc_kwd[]; + extern const char calc_fn_kwd[]; extern const char moz_calc_kwd[]; extern const char webkit_calc_kwd[]; extern const char ms_calc_kwd[]; @@ -126,6 +126,7 @@ extern const char rbrace[]; extern const char rparen[]; extern const char sign_chars[]; + extern const char op_chars[]; extern const char hyphen[]; extern const char ellipsis[]; // extern const char url_space_chars[]; diff -Nru libsass-3.3.2/src/context.cpp libsass-3.3.4/src/context.cpp --- libsass-3.3.2/src/context.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/context.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,9 +1,4 @@ -#ifdef _WIN32 -#define PATH_SEP ';' -#else -#define PATH_SEP ':' -#endif - +#include "sass.hpp" #include #include #include @@ -28,6 +23,7 @@ #include "extend.hpp" #include "remove_placeholders.hpp" #include "functions.hpp" +#include "sass_functions.hpp" #include "backtrace.hpp" #include "sass2scss.h" #include "prelexer.hpp" @@ -62,13 +58,14 @@ return safe_path == "" ? "stdout" : safe_path; } - Context::Context(struct Sass_Context* c_ctx) + Context::Context(struct Sass_Context& c_ctx) : CWD(File::get_cwd()), + c_options(c_ctx), entry_path(""), head_imports(0), mem(Memory_Manager()), plugins(), - emitter(this), + emitter(c_options), strings(), resources(), @@ -76,19 +73,17 @@ subset_map(), import_stack(), - c_options (c_ctx), - c_headers (std::vector()), c_importers (std::vector()), c_functions (std::vector()), - indent (safe_str(c_options->indent, " ")), - linefeed (safe_str(c_options->linefeed, "\n")), + indent (safe_str(c_options.indent, " ")), + linefeed (safe_str(c_options.linefeed, "\n")), - input_path (make_canonical_path(safe_input(c_options->input_path))), - output_path (make_canonical_path(safe_output(c_options->output_path, input_path))), - source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))), - source_map_root (make_canonical_path(safe_str(c_options->source_map_root, ""))) + input_path (make_canonical_path(safe_input(c_options.input_path))), + output_path (make_canonical_path(safe_output(c_options.output_path, input_path))), + source_map_file (make_canonical_path(safe_str(c_options.source_map_file, ""))), + source_map_root (make_canonical_path(safe_str(c_options.source_map_root, ""))) { @@ -96,10 +91,10 @@ include_paths.push_back(CWD); // collect more paths from different options - collect_include_paths(sass_option_get_include_path(c_options)); - // collect_include_paths(initializers.include_paths_array()); - collect_plugin_paths(sass_option_get_plugin_path(c_options)); - // collect_plugin_paths(initializers.plugin_paths_array()); + collect_include_paths(c_options.include_path); + // collect_include_paths(c_options.include_paths); + collect_plugin_paths(c_options.plugin_path); + // collect_plugin_paths(c_options.plugin_paths); // load plugins and register custom behaviors for(auto plug : plugin_paths) plugins.load_plugins(plug); @@ -254,7 +249,7 @@ // register include with resolved path and its content // memory of the resources will be freed by us on exit - void Context::register_resource(const Include& inc, const Resource& res) + void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate) { // do not parse same resource twice @@ -297,6 +292,24 @@ strings.push_back(sass_strdup(inc.abs_path.c_str())); // create the initial parser state from resource ParserState pstate(strings.back(), contents, idx); + + // check existing import stack for possible recursion + for (size_t i = 0; i < import_stack.size() - 2; ++i) { + auto parent = import_stack[i]; + if (std::strcmp(parent->abs_path, import->abs_path) == 0) { + std::string stack("An @import loop has been found:"); + for (size_t n = 1; n < i + 2; ++n) { + stack += "\n " + std::string(import_stack[n]->imp_path) + + " imports " + std::string(import_stack[n+1]->imp_path); + } + // implement error throw directly until we + // decided how to handle full stack traces + ParserState state = prstate ? *prstate : pstate; + throw Exception::InvalidSyntax(state, stack, &import_stack); + // error(stack, prstate ? *prstate : pstate, import_stack); + } + } + // create a parser instance from the given c_str buffer Parser p(Parser::from_c_str(contents, *this, pstate)); // do not yet dispose these buffers @@ -338,13 +351,14 @@ // process the resolved entry else if (resolved.size() == 1) { + bool use_cache = c_importers.size() == 0; // use cache for the resource loading - if (sheets.count(resolved[0].abs_path)) return resolved[0]; + if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0]; // try to read the content of the resolved file entry // the memory buffer returned must be freed by us! if (char* contents = read_file(resolved[0].abs_path)) { // register the newly resolved file resource - register_resource(resolved[0], { contents, 0 }); + register_resource(resolved[0], { contents, 0 }, &pstate); // return resolved entry return resolved[0]; } @@ -365,10 +379,7 @@ if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) { protocol = std::string(imp_path.c_str(), proto - 3); - // std::cerr << "==================== " << protocol << "\n"; - if (protocol.compare("file") && true) { - - } + // if (protocol.compare("file") && true) { } } // add urls (protocol other than file) and urls without procotol to `urls` member @@ -426,14 +437,14 @@ // query data from the current include Sass_Import_Entry include = *it_includes; char* source = sass_import_take_source(include); - char* srcmap = sass_import_take_source(include); + char* srcmap = sass_import_take_srcmap(include); size_t line = sass_import_get_error_line(include); size_t column = sass_import_get_error_column(include); const char *abs_path = sass_import_get_abs_path(include); // handle error message passed back from custom importer // it may (or may not) override the line and column info if (const char* err_message = sass_import_get_error_message(include)) { - if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }); + if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate); if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); else error(err_message, ParserState(ctx_path, source, Position(line, column))); } @@ -447,7 +458,7 @@ // attach information to AST node imp->incs().push_back(include); // register the resource buffers - register_resource(include, { source, srcmap }); + register_resource(include, { source, srcmap }, &pstate); } // only a path was retuned // try to load it like normal @@ -494,9 +505,9 @@ // get the resulting buffer from stream OutputBuffer emitted = emitter.get_buffer(); // should we append a source map url? - if (!c_options->omit_source_map_url) { + if (!c_options.omit_source_map_url) { // generate an embeded source map - if (c_options->source_map_embed) { + if (c_options.source_map_embed) { emitted.buffer += linefeed; emitted.buffer += format_embedded_source_map(); } @@ -581,7 +592,7 @@ if (!source_c_str) return 0; // convert indented sass syntax - if(c_options->is_indented_syntax_src) { + if(c_options.is_indented_syntax_src) { // call sass2scss to convert the string char * converted = sass2scss(source_c_str, // preserve the structure as much as possible diff -Nru libsass-3.3.2/src/context.hpp libsass-3.3.4/src/context.hpp --- libsass-3.3.2/src/context.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/context.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -38,6 +38,7 @@ public: const std::string CWD; + struct Sass_Options& c_options; std::string entry_path; size_t head_imports; Memory_Manager mem; @@ -53,7 +54,6 @@ std::vector import_stack; struct Sass_Compiler* c_compiler; - struct Sass_Options* c_options; // absolute paths to includes std::vector included_files; @@ -86,17 +86,17 @@ const std::string source_map_root; // path for sourceRoot property (pass-through) virtual ~Context(); - Context(struct Sass_Context*); + Context(struct Sass_Context&); virtual Block* parse() = 0; virtual Block* compile(); virtual char* render(Block* root); virtual char* render_srcmap(); - void register_resource(const Include&, const Resource&); + void register_resource(const Include&, const Resource&, ParserState* = 0); std::vector find_includes(const Importer& import); Include load_import(const Importer&, ParserState pstate); - Sass_Output_Style output_style() { return c_options->output_style; }; + Sass_Output_Style output_style() { return c_options.output_style; }; std::vector get_included_files(bool skip = false, size_t headers = 0); private: @@ -119,7 +119,7 @@ class File_Context : public Context { public: - File_Context(struct Sass_File_Context* ctx) + File_Context(struct Sass_File_Context& ctx) : Context(ctx) { } virtual ~File_Context(); @@ -130,13 +130,13 @@ public: char* source_c_str; char* srcmap_c_str; - Data_Context(struct Sass_Data_Context* ctx) + Data_Context(struct Sass_Data_Context& ctx) : Context(ctx) { - source_c_str = ctx->source_string; - srcmap_c_str = ctx->srcmap_string; - ctx->source_string = 0; // passed away - ctx->srcmap_string = 0; // passed away + source_c_str = ctx.source_string; + srcmap_c_str = ctx.srcmap_string; + ctx.source_string = 0; // passed away + ctx.srcmap_string = 0; // passed away } virtual ~Data_Context(); virtual Block* parse(); diff -Nru libsass-3.3.2/src/cssize.cpp libsass-3.3.4/src/cssize.cpp --- libsass-3.3.2/src/cssize.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/cssize.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,9 +1,9 @@ +#include "sass.hpp" #include #include #include #include "cssize.hpp" -#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" @@ -100,6 +100,10 @@ // rr->tabs(r->block()->tabs()); p_stack.pop_back(); + if (!rr->block()) { + error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate()); + } + Block* props = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate()); Block* rules = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate()); for (size_t i = 0, L = rr->block()->length(); i < L; i++) @@ -513,15 +517,14 @@ Media_Query* Cssize::merge_media_query(Media_Query* mq1, Media_Query* mq2) { - To_String to_string(&ctx); std::string type; std::string mod; std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : ""); - std::string t1 = mq1->media_type() ? mq1->media_type()->perform(&to_string) : ""; + std::string t1 = mq1->media_type() ? mq1->media_type()->to_string(ctx.c_options) : ""; std::string m2 = std::string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : ""); - std::string t2 = mq2->media_type() ? mq2->media_type()->perform(&to_string) : ""; + std::string t2 = mq2->media_type() ? mq2->media_type()->to_string(ctx.c_options) : ""; if (t1.empty()) t1 = t2; diff -Nru libsass-3.3.2/src/cssize.hpp libsass-3.3.4/src/cssize.hpp --- libsass-3.3.2/src/cssize.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/cssize.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -22,9 +22,7 @@ public: Cssize(Context&, Backtrace*); - virtual ~Cssize() { } - - using Operation::operator(); + ~Cssize() { } Statement* operator()(Block*); Statement* operator()(Ruleset*); diff -Nru libsass-3.3.2/src/debugger.hpp libsass-3.3.4/src/debugger.hpp --- libsass-3.3.2/src/debugger.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/debugger.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -77,8 +77,10 @@ Selector_List* selector = dynamic_cast(node); std::cerr << ind << "Selector_List " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " <" << selector->hash() << ">"; std::cerr << " [@media:" << selector->media_block() << "]"; std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << std::endl; @@ -94,6 +96,7 @@ std::cerr << ind << "Parent_Selector " << selector; // if (selector->not_selector()) cerr << " [in_declaration]"; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " <" << selector->hash() << ">"; std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; // debug_ast(selector->selector(), ind + "->", env); @@ -101,9 +104,11 @@ Complex_Selector* selector = dynamic_cast(node); std::cerr << ind << "Complex_Selector " << selector << " (" << pstate_source_position(node) << ")" + << " <" << selector->hash() << ">" << " [weight:" << longToHex(selector->specificity()) << "]" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") + << (selector->has_parent_ref() ? " [has parent]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << (selector->has_line_break() ? " [line-break]": " -") << " -- "; @@ -129,9 +134,12 @@ Compound_Selector* selector = dynamic_cast(node); std::cerr << ind << "Compound_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " <" << selector->hash() << ">"; std::cerr << " [weight:" << longToHex(selector->specificity()) << "]"; std::cerr << " [@media:" << selector->media_block() << "]"; + std::cerr << (selector->extended() ? " [extended]": " -"); std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); std::cerr << (selector->has_line_break() ? " [line-break]": " -"); std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; @@ -146,35 +154,66 @@ Wrapped_Selector* selector = dynamic_cast(node); std::cerr << ind << "Wrapped_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + std::cerr << (selector->has_line_break() ? " [line-break]": " -"); + std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); + std::cerr << std::endl; debug_ast(selector->selector(), ind + " () ", env); } else if (dynamic_cast(node)) { Pseudo_Selector* selector = dynamic_cast(node); std::cerr << ind << "Pseudo_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + std::cerr << (selector->has_line_break() ? " [line-break]": " -"); + std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); + std::cerr << std::endl; debug_ast(selector->expression(), ind + " <= ", env); } else if (dynamic_cast(node)) { Attribute_Selector* selector = dynamic_cast(node); std::cerr << ind << "Attribute_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + std::cerr << (selector->has_line_break() ? " [line-break]": " -"); + std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); + std::cerr << std::endl; debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env); } else if (dynamic_cast(node)) { Selector_Qualifier* selector = dynamic_cast(node); std::cerr << ind << "Selector_Qualifier " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + std::cerr << (selector->has_line_break() ? " [line-break]": " -"); + std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); + std::cerr << std::endl; } else if (dynamic_cast(node)) { Type_Selector* selector = dynamic_cast(node); std::cerr << ind << "Type_Selector " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << - " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << " <" << selector->hash() << ">"; + std::cerr << " <<" << selector->ns_name() << ">>"; + std::cerr << (selector->is_optional() ? " [is_optional]": " -"); + std::cerr << (selector->has_parent_ref() ? " [has-parent]": " -"); + std::cerr << (selector->has_line_break() ? " [line-break]": " -"); + std::cerr << (selector->has_line_feed() ? " [line-feed]": " -"); + std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">"; + std::cerr << std::endl; } else if (dynamic_cast(node)) { Selector_Placeholder* selector = dynamic_cast(node); std::cerr << ind << "Selector_Placeholder [" << selector->ns_name() << "] " << selector + << " <" << selector->hash() << ">" << " [@media:" << selector->media_block() << "]" << (selector->is_optional() ? " [is_optional]": " -") << (selector->has_line_break() ? " [line-break]": " -") @@ -282,6 +321,7 @@ std::cerr << ind << "Debug " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; + debug_ast(block->value(), ind + " "); } else if (dynamic_cast(node)) { Comment* block = dynamic_cast(node); std::cerr << ind << "Comment " << block; @@ -317,15 +357,16 @@ Import_Stub* block = dynamic_cast(node); std::cerr << ind << "Import_Stub " << block; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [" << block->imp_path() << "] "; std::cerr << " " << block->tabs() << std::endl; } else if (dynamic_cast(node)) { Import* block = dynamic_cast(node); std::cerr << ind << "Import " << block; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " " << block->tabs() << std::endl; - debug_ast(block->media_queries(), ind + " @ "); // std::vector files_; - for (auto imp : block->urls()) debug_ast(imp, "@ ", env); + for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env); + debug_ast(block->media_queries(), ind + "@@ "); } else if (dynamic_cast(node)) { Assignment* block = dynamic_cast(node); std::cerr << ind << "Assignment " << block; @@ -414,11 +455,13 @@ else if (expression->type() == Textual::DIMENSION) std::cerr << " [DIMENSION]"; else if (expression->type() == Textual::HEX) std::cerr << " [HEX]"; std::cerr << expression << " [" << expression->value() << "]"; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; if (expression->is_delayed()) std::cerr << " [delayed]"; std::cerr << std::endl; } else if (dynamic_cast(node)) { Variable* expression = dynamic_cast(node); std::cerr << ind << "Variable " << expression; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->name() << "]" << std::endl; std::string name(expression->name()); @@ -426,6 +469,7 @@ } else if (dynamic_cast(node)) { Function_Call_Schema* expression = dynamic_cast(node); std::cerr << ind << "Function_Call_Schema " << expression; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << "" << std::endl; debug_ast(expression->name(), ind + "name: ", env); @@ -433,8 +477,12 @@ } else if (dynamic_cast(node)) { Function_Call* expression = dynamic_cast(node); std::cerr << ind << "Function_Call " << expression; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " [" << expression->name() << "]" << std::endl; + std::cerr << " [" << expression->name() << "]"; + if (expression->is_delayed()) std::cerr << " [delayed]"; + if (expression->is_interpolant()) std::cerr << " [interpolant]"; + std::cerr << std::endl; debug_ast(expression->arguments(), ind + " args: ", env); } else if (dynamic_cast(node)) { Arguments* expression = dynamic_cast(node); @@ -473,13 +521,19 @@ } else if (dynamic_cast(node)) { Unary_Expression* expression = dynamic_cast(node); std::cerr << ind << "Unary_Expression " << expression; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type() << "]" << std::endl; debug_ast(expression->operand(), ind + " operand: ", env); } else if (dynamic_cast(node)) { Binary_Expression* expression = dynamic_cast(node); std::cerr << ind << "Binary_Expression " << expression; + if (expression->is_interpolant()) std::cerr << " [is interpolant] "; + if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; + if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " [delayed: " << expression->is_delayed() << "] "; + std::cerr << " [ws_before: " << expression->op().ws_before << "] "; + std::cerr << " [ws_after: " << expression->op().ws_after << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [" << expression->type_name() << "]" << std::endl; debug_ast(expression->left(), ind + " left: ", env); @@ -487,6 +541,7 @@ } else if (dynamic_cast(node)) { Map* expression = dynamic_cast(node); std::cerr << ind << "Map " << expression; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " [Hashed]" << std::endl; for (auto i : expression->elements()) { @@ -498,7 +553,7 @@ std::cerr << ind << "List " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " << - (expression->separator() == SASS_COMMA ? "Comma " : "Space ") << + (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map" : "Space ") << " [delayed: " << expression->is_delayed() << "] " << " [interpolant: " << expression->is_interpolant() << "] " << " [arglist: " << expression->is_arglist() << "] " << @@ -514,16 +569,19 @@ Boolean* expression = dynamic_cast(node); std::cerr << ind << "Boolean " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << "]" << std::endl; } else if (dynamic_cast(node)) { Color* expression = dynamic_cast(node); std::cerr << ind << "Color " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl; } else if (dynamic_cast(node)) { Number* expression = dynamic_cast(node); std::cerr << ind << "Number " << expression; std::cerr << " (" << pstate_source_position(node) << ")"; + std::cerr << " [interpolant: " << expression->is_interpolant() << "] "; std::cerr << " [" << expression->value() << expression->unit() << "]" << " [hash: " << expression->hash() << "] " << std::endl; @@ -554,8 +612,10 @@ std::cerr << ind << "String_Schema " << expression; std::cerr << " " << expression->concrete_type(); if (expression->is_delayed()) std::cerr << " [delayed]"; - if (expression->has_interpolants()) std::cerr << " [has_interpolants]"; - if (expression->is_interpolant()) std::cerr << " [interpolant]"; + if (expression->is_interpolant()) std::cerr << " [is interpolant]"; + if (expression->has_interpolant()) std::cerr << " [has interpolant]"; + if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; + if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (dynamic_cast(node)) { @@ -660,10 +720,10 @@ debug_node(const_cast(node), ind); } -inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, std::string ind = "") +inline void debug_subset_map(Sass::ExtensionSubsetMap& map, std::string ind = "") { if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; - for(auto const &it : map->values()) { + for(auto const &it : map.values()) { debug_ast(it.first, ind + "first: "); debug_ast(it.second, ind + "second: "); } diff -Nru libsass-3.3.2/src/emitter.cpp libsass-3.3.4/src/emitter.cpp --- libsass-3.3.2/src/emitter.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/emitter.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "util.hpp" #include "context.hpp" #include "output.hpp" @@ -6,20 +7,20 @@ namespace Sass { - Emitter::Emitter(Context* ctx) + Emitter::Emitter(struct Sass_Output_Options& opt) : wbuf(), - ctx(ctx), + opt(opt), indentation(0), scheduled_space(0), scheduled_linefeed(0), scheduled_delimiter(false), + scheduled_mapping(0), in_comment(false), in_wrapped(false), in_media_block(false), in_declaration(false), in_space_array(false), - in_comma_array(false), - in_debug(false) + in_comma_array(false) { } // return buffer as string @@ -28,9 +29,9 @@ return wbuf.buffer; } - Sass_Output_Style Emitter::output_style(void) + Sass_Output_Style Emitter::output_style(void) const { - return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED; + return opt.output_style; } // PROXY METHODS FOR SOURCE MAPS @@ -44,9 +45,11 @@ void Emitter::set_filename(const std::string& str) { wbuf.smap.file = str; } - void Emitter::add_open_mapping(AST_Node* node) + void Emitter::schedule_mapping(const AST_Node* node) + { scheduled_mapping = node; } + void Emitter::add_open_mapping(const AST_Node* node) { wbuf.smap.add_open_mapping(node); } - void Emitter::add_close_mapping(AST_Node* node) + void Emitter::add_close_mapping(const AST_Node* node) { wbuf.smap.add_close_mapping(node); } ParserState Emitter::remap(const ParserState& pstate) { return wbuf.smap.remap(pstate); } @@ -54,9 +57,11 @@ // MAIN BUFFER MANIPULATION // add outstanding delimiter - void Emitter::finalize(void) + void Emitter::finalize(bool final) { scheduled_space = 0; + if (output_style() == SASS_STYLE_COMPRESSED) + if (final) scheduled_delimiter = false; if (scheduled_linefeed) scheduled_linefeed = 1; flush_schedules(); @@ -70,7 +75,7 @@ std::string linefeeds = ""; for (size_t i = 0; i < scheduled_linefeed; i++) - linefeeds += ctx ? ctx->linefeed : "\n"; + linefeeds += opt.linefeed; scheduled_space = 0; scheduled_linefeed = 0; append_string(linefeeds); @@ -103,10 +108,11 @@ // append some text or token to the buffer void Emitter::append_string(const std::string& text) { + // write space/lf flush_schedules(); - if (in_comment && output_style() == SASS_STYLE_COMPACT) { + if (in_comment && output_style() == COMPACT) { // unescape comment nodes std::string out = comment_to_string(text); // add to buffer @@ -133,10 +139,16 @@ // append some text or token to the buffer // this adds source-mappings for node start and end - void Emitter::append_token(const std::string& text, AST_Node* node) + void Emitter::append_token(const std::string& text, const AST_Node* node) { flush_schedules(); add_open_mapping(node); + // hotfix for browser issues + // this is pretty ugly indeed + if (scheduled_mapping) { + add_open_mapping(scheduled_mapping); + scheduled_mapping = 0; + } append_string(text); add_close_mapping(node); } @@ -145,27 +157,27 @@ void Emitter::append_indentation() { - if (output_style() == SASS_STYLE_COMPRESSED) return; - if (output_style() == SASS_STYLE_COMPACT) return; + if (output_style() == COMPRESSED) return; + if (output_style() == COMPACT) return; if (in_declaration && in_comma_array) return; if (scheduled_linefeed && indentation) scheduled_linefeed = 1; std::string indent = ""; for (size_t i = 0; i < indentation; i++) - indent += ctx ? ctx->indent : " "; + indent += opt.indent; append_string(indent); } void Emitter::append_delimiter() { scheduled_delimiter = true; - if (output_style() == SASS_STYLE_COMPACT) { + if (output_style() == COMPACT) { if (indentation == 0) { append_mandatory_linefeed(); } else { append_mandatory_space(); } - } else if (output_style() != SASS_STYLE_COMPRESSED) { + } else if (output_style() != COMPRESSED) { append_optional_linefeed(); } } @@ -191,7 +203,7 @@ void Emitter::append_optional_space() { - if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && buffer().size()) { + if ((output_style() != COMPRESSED) && buffer().size()) { char lst = buffer().at(buffer().length() - 1); if (!isspace(lst) || scheduled_delimiter) { append_mandatory_space(); @@ -201,17 +213,17 @@ void Emitter::append_special_linefeed() { - if (output_style() == SASS_STYLE_COMPACT) { + if (output_style() == COMPACT) { append_mandatory_linefeed(); for (size_t p = 0; p < indentation; p++) - append_string(ctx ? ctx->indent : " "); + append_string(opt.indent); } } void Emitter::append_optional_linefeed() { if (in_declaration && in_comma_array) return; - if (output_style() == SASS_STYLE_COMPACT) { + if (output_style() == COMPACT) { append_mandatory_space(); } else { append_mandatory_linefeed(); @@ -220,7 +232,7 @@ void Emitter::append_mandatory_linefeed() { - if (output_style() != SASS_STYLE_COMPRESSED) { + if (output_style() != COMPRESSED) { scheduled_linefeed = 1; scheduled_space = 0; // flush_schedules(); @@ -242,9 +254,9 @@ { -- indentation; scheduled_linefeed = 0; - if (output_style() == SASS_STYLE_COMPRESSED) + if (output_style() == COMPRESSED) scheduled_delimiter = false; - if (output_style() == SASS_STYLE_EXPANDED) { + if (output_style() == EXPANDED) { append_optional_linefeed(); append_indentation(); } else { @@ -254,7 +266,7 @@ if (node) add_close_mapping(node); append_optional_linefeed(); if (indentation != 0) return; - if (output_style() != SASS_STYLE_COMPRESSED) + if (output_style() != COMPRESSED) scheduled_linefeed = 2; } diff -Nru libsass-3.3.2/src/emitter.hpp libsass-3.3.4/src/emitter.hpp --- libsass-3.3.2/src/emitter.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/emitter.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -2,6 +2,7 @@ #define SASS_EMITTER_H #include +#include "sass.hpp" #include "sass/base.h" #include "source_map.hpp" #include "ast_fwd_decl.hpp" @@ -12,7 +13,7 @@ class Emitter { public: - Emitter(Context* ctx); + Emitter(struct Sass_Output_Options& opt); virtual ~Emitter() { } protected: @@ -24,17 +25,19 @@ // proxy methods for source maps void add_source_index(size_t idx); void set_filename(const std::string& str); - void add_open_mapping(AST_Node* node); - void add_close_mapping(AST_Node* node); + void add_open_mapping(const AST_Node* node); + void add_close_mapping(const AST_Node* node); + void schedule_mapping(const AST_Node* node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); public: - Context* ctx; + struct Sass_Output_Options& opt; size_t indentation; size_t scheduled_space; size_t scheduled_linefeed; bool scheduled_delimiter; + const AST_Node* scheduled_mapping; public: // output strings different in comments @@ -48,16 +51,14 @@ // nested lists need parentheses bool in_space_array; bool in_comma_array; - // list separators don't get compressed in @debug - bool in_debug; public: // return buffer as std::string std::string get_buffer(void); // flush scheduled space/linefeed - Sass_Output_Style output_style(void); + Sass_Output_Style output_style(void) const; // add outstanding linefeed - void finalize(void); + void finalize(bool final = true); // flush scheduled space/linefeed void flush_schedules(void); // prepend some text or token to the buffer @@ -69,7 +70,7 @@ void append_wspace(const std::string& text); // append some text or token to the buffer // this adds source-mappings for node start and end - void append_token(const std::string& text, AST_Node* node); + void append_token(const std::string& text, const AST_Node* node); public: // syntax sugar void append_indentation(); diff -Nru libsass-3.3.2/src/environment.cpp libsass-3.3.4/src/environment.cpp --- libsass-3.3.2/src/environment.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/environment.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,14 +1,24 @@ +#include "sass.hpp" #include "ast.hpp" #include "environment.hpp" namespace Sass { template - Environment::Environment() : local_frame_(std::map()), parent_(0) { } - template - Environment::Environment(Environment* env) : local_frame_(std::map()), parent_(env) { } - template - Environment::Environment(Environment& env) : local_frame_(std::map()), parent_(&env) { } + Environment::Environment(bool is_shadow) + : local_frame_(std::map()), + parent_(0), is_shadow_(false) + { } + template + Environment::Environment(Environment* env, bool is_shadow) + : local_frame_(std::map()), + parent_(env), is_shadow_(is_shadow) + { } + template + Environment::Environment(Environment& env, bool is_shadow) + : local_frame_(std::map()), + parent_(&env), is_shadow_(is_shadow) + { } // link parent to create a stack template @@ -118,12 +128,13 @@ template void Environment::set_lexical(const std::string& key, T val) { - auto cur = this; - while (cur->is_lexical()) { + auto cur = this; bool shadow = false; + while (cur->is_lexical() || shadow) { if (cur->has_local(key)) { cur->set_local(key, val); return; } + shadow = cur->is_shadow(); cur = cur->parent_; } set_local(key, val); diff -Nru libsass-3.3.2/src/environment.hpp libsass-3.3.4/src/environment.hpp --- libsass-3.3.2/src/environment.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/environment.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -15,12 +15,13 @@ // TODO: test with map std::map local_frame_; ADD_PROPERTY(Environment*, parent) + ADD_PROPERTY(bool, is_shadow) public: Memory_Manager mem; - Environment(); - Environment(Environment* env); - Environment(Environment& env); + Environment(bool is_shadow = false); + Environment(Environment* env, bool is_shadow = false); + Environment(Environment& env, bool is_shadow = false); // link parent to create a stack void link(Environment& env); @@ -87,6 +88,7 @@ #endif }; + } #endif diff -Nru libsass-3.3.2/src/error_handling.cpp libsass-3.3.4/src/error_handling.cpp --- libsass-3.3.2/src/error_handling.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/error_handling.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,7 +1,7 @@ +#include "sass.hpp" #include "ast.hpp" #include "prelexer.hpp" #include "backtrace.hpp" -#include "to_string.hpp" #include "error_handling.hpp" #include @@ -10,16 +10,12 @@ namespace Exception { - Base::Base(ParserState pstate, std::string msg) - : std::runtime_error(msg), - msg(msg), pstate(pstate) + Base::Base(ParserState pstate, std::string msg, std::vector* import_stack) + : std::runtime_error(msg), msg(msg), + prefix("Error"), pstate(pstate), + import_stack(import_stack) { } - const char* Base::what() const throw() - { - return msg.c_str(); - } - InvalidSass::InvalidSass(ParserState pstate, std::string msg) : Base(pstate, msg) { } @@ -29,9 +25,9 @@ : Base(selector->pstate()), parent(parent), selector(selector) { msg = "Invalid parent selector for \""; - msg += selector->to_string(false); + msg += selector->to_string(Sass_Inspect_Options()); msg += "\": \""; - msg += parent->to_string(false);; + msg += parent->to_string(Sass_Inspect_Options()); msg += "\""; } @@ -39,15 +35,96 @@ : Base(pstate), fn(fn), arg(arg), type(type), value(value) { msg = arg + ": \""; - msg += value->to_string(true, 5); + msg += value->to_string(Sass_Inspect_Options()); msg += "\" is not a " + type; msg += " for `" + fn + "'"; } - InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg) - : Base(pstate, msg) + InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector* import_stack) + : Base(pstate, msg, import_stack) { } + UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op) + : lhs(lhs), rhs(rhs), op(op) + { + msg = def_op_msg + ": \""; + msg += lhs->to_string({ NESTED, 5 }); + msg += " " + op + " "; + msg += rhs->to_string({ TO_SASS, 5 }); + msg += "\"."; + } + + InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op) + : UndefinedOperation(lhs, rhs, op) + { + msg = def_op_null_msg + ": \""; + msg += lhs->inspect(); + msg += " " + op + " "; + msg += rhs->inspect(); + msg += "\"."; + } + + ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs) + : lhs(lhs), rhs(rhs) + { + msg = "divided by 0"; + } + + DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org) + : Base(org.pstate()), dup(dup), org(org) + { + msg = "Duplicate key "; + dup.get_duplicate_key()->is_delayed(false); + msg += dup.get_duplicate_key()->inspect(); + msg += " in map ("; + msg += org.inspect(); + msg += ")."; + } + + TypeMismatch::TypeMismatch(const Expression& var, const std::string type) + : Base(var.pstate()), var(var), type(type) + { + msg = var.to_string(); + msg += " is not an "; + msg += type; + msg += "."; + } + + InvalidValue::InvalidValue(const Expression& val) + : Base(val.pstate()), val(val) + { + msg = val.to_string(); + msg += " isn't a valid CSS value."; + } + + IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs) + : lhs(lhs), rhs(rhs) + { + msg = "Incompatible units: '"; + msg += rhs.unit(); + msg += "' and '"; + msg += lhs.unit(); + msg += "'."; + } + + AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op) + : lhs(lhs), rhs(rhs), op(op) + { + msg = "Alpha channels must be equal: "; + msg += lhs->to_string({ NESTED, 5 }); + msg += " " + op + " "; + msg += rhs->to_string({ NESTED, 5 }); + msg += "."; + } + + + SassValueError::SassValueError(ParserState pstate, OperationError& err) + : Base(pstate, err.what()) + { + msg = err.what(); + prefix = err.errtype(); + } + } diff -Nru libsass-3.3.2/src/error_handling.hpp libsass-3.3.4/src/error_handling.hpp --- libsass-3.3.2/src/error_handling.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/error_handling.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -12,16 +12,21 @@ namespace Exception { - const std::string def_msg = "Invalid sass"; + const std::string def_msg = "Invalid sass detected"; + const std::string def_op_msg = "Undefined operation"; + const std::string def_op_null_msg = "Invalid null operation"; class Base : public std::runtime_error { protected: std::string msg; + std::string prefix; public: ParserState pstate; + std::vector* import_stack; public: - Base(ParserState pstate, std::string msg = def_msg); - virtual const char* what() const throw(); + Base(ParserState pstate, std::string msg = def_msg, std::vector* import_stack = 0); + virtual const char* errtype() const { return prefix.c_str(); } + virtual const char* what() const throw() { return msg.c_str(); } virtual ~Base() throw() {}; }; @@ -53,10 +58,106 @@ class InvalidSyntax : public Base { public: - InvalidSyntax(ParserState pstate, std::string msg); + InvalidSyntax(ParserState pstate, std::string msg, std::vector* import_stack = 0); virtual ~InvalidSyntax() throw() {}; }; + /* common virtual base class (has no pstate) */ + class OperationError : public std::runtime_error { + protected: + std::string msg; + public: + OperationError(std::string msg = def_op_msg) + : std::runtime_error(msg), msg(msg) + {}; + public: + virtual const char* errtype() const { return "Error"; } + virtual const char* what() const throw() { return msg.c_str(); } + virtual ~OperationError() throw() {}; + }; + + class ZeroDivisionError : public OperationError { + protected: + const Expression& lhs; + const Expression& rhs; + public: + ZeroDivisionError(const Expression& lhs, const Expression& rhs); + virtual const char* errtype() const { return "ZeroDivisionError"; } + virtual ~ZeroDivisionError() throw() {}; + }; + + class DuplicateKeyError : public Base { + protected: + const Map& dup; + const Expression& org; + public: + DuplicateKeyError(const Map& dup, const Expression& org); + virtual const char* errtype() const { return "Error"; } + virtual ~DuplicateKeyError() throw() {}; + }; + + class TypeMismatch : public Base { + protected: + const Expression& var; + const std::string type; + public: + TypeMismatch(const Expression& var, const std::string type); + virtual const char* errtype() const { return "Error"; } + virtual ~TypeMismatch() throw() {}; + }; + + class InvalidValue : public Base { + protected: + const Expression& val; + public: + InvalidValue(const Expression& val); + virtual const char* errtype() const { return "Error"; } + virtual ~InvalidValue() throw() {}; + }; + + class IncompatibleUnits : public OperationError { + protected: + const Number& lhs; + const Number& rhs; + public: + IncompatibleUnits(const Number& lhs, const Number& rhs); + virtual ~IncompatibleUnits() throw() {}; + }; + + class UndefinedOperation : public OperationError { + protected: + const Expression* lhs; + const Expression* rhs; + const std::string op; + public: + UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op); + // virtual const char* errtype() const { return "Error"; } + virtual ~UndefinedOperation() throw() {}; + }; + + class InvalidNullOperation : public UndefinedOperation { + public: + InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op); + virtual ~InvalidNullOperation() throw() {}; + }; + + class AlphaChannelsNotEqual : public OperationError { + protected: + const Expression* lhs; + const Expression* rhs; + const std::string op; + public: + AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op); + // virtual const char* errtype() const { return "Error"; } + virtual ~AlphaChannelsNotEqual() throw() {}; + }; + + class SassValueError : public Base { + public: + SassValueError(ParserState pstate, OperationError& err); + virtual ~SassValueError() throw() {}; + }; + } void warn(std::string msg, ParserState pstate); diff -Nru libsass-3.3.2/src/eval.cpp libsass-3.3.4/src/eval.cpp --- libsass-3.3.2/src/eval.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/eval.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -10,7 +11,6 @@ #include "ast.hpp" #include "bind.hpp" #include "util.hpp" -#include "to_string.hpp" #include "inspect.hpp" #include "environment.hpp" #include "position.hpp" @@ -41,8 +41,7 @@ Eval::Eval(Expand& exp) : exp(exp), - ctx(exp.ctx), - listize(exp.ctx) + ctx(exp.ctx) { } Eval::~Eval() { } @@ -160,11 +159,11 @@ std::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->pstate()); + throw Exception::TypeMismatch(*low, "integer"); } Expression* high = f->upper_bound()->perform(this); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->pstate()); + throw Exception::TypeMismatch(*high, "integer"); } Number* sass_start = static_cast(low); Number* sass_end = static_cast(high); @@ -178,10 +177,10 @@ double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment - Env* env = exp.environment(); - Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit()); - AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0; - env->set_local(variable, it); + Env env(environment(), true); + exp.env_stack.push_back(&env); + Number* it = SASS_MEMORY_NEW(env.mem, Number, low->pstate(), start, sass_end->unit()); + env.set_local(variable, it); Block* body = f->block(); Expression* val = 0; if (start < end) { @@ -190,7 +189,7 @@ i < end; ++i) { it->value(i); - env->set_local(variable, it); + env.set_local(variable, it); val = body->perform(this); if (val) break; } @@ -200,14 +199,12 @@ i > end; --i) { it->value(i); - env->set_local(variable, it); + env.set_local(variable, it); val = body->perform(this); if (val) break; } } - // restore original environment - if (!old_var) env->del_local(variable); - else env->set_local(variable, old_var); + exp.env_stack.pop_back(); return val; } @@ -217,12 +214,17 @@ { std::vector variables(e->variables()); Expression* expr = e->list()->perform(this); - Env* env = exp.environment(); - List* list = 0; + Env env(environment(), true); + exp.env_stack.push_back(&env); + Vectorized* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { map = static_cast(expr); } + else if (Selector_List* ls = dynamic_cast(expr)) { + Listize listize(ctx.mem); + list = dynamic_cast(ls->perform(&listize)); + } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA); *list << expr; @@ -230,12 +232,7 @@ else { list = static_cast(expr); } - // remember variables and then reset them - std::vector old_vars(variables.size()); - for (size_t i = 0, L = variables.size(); i < L; ++i) { - old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0; - env->set_local(variables[i], 0); - } + Block* body = e->block(); Expression* val = 0; @@ -247,10 +244,10 @@ List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE); *variable << key; *variable << value; - env->set_local(variables[0], variable); + env.set_local(variables[0], variable); } else { - env->set_local(variables[0], key); - env->set_local(variables[1], value); + env.set_local(variables[0], key); + env.set_local(variables[1], value); } val = body->perform(this); @@ -258,6 +255,9 @@ } } else { + if (list->length() == 1 && dynamic_cast(list)) { + list = dynamic_cast*>(list); + } for (size_t i = 0, L = list->length(); i < L; ++i) { Expression* e = (*list)[i]; // unwrap value if the expression is an argument @@ -266,21 +266,23 @@ if (List* scalars = dynamic_cast(e)) { if (variables.size() == 1) { Expression* var = scalars; - env->set_local(variables[0], var); + env.set_local(variables[0], var); } else { + // XXX: this is never hit via spec tests for (size_t j = 0, K = variables.size(); j < K; ++j) { Expression* res = j >= scalars->length() ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()) : (*scalars)[j]; - env->set_local(variables[j], res); + env.set_local(variables[j], res); } } } else { if (variables.size() > 0) { - env->set_local(variables[0], e); + env.set_local(variables[0], e); for (size_t j = 1, K = variables.size(); j < K; ++j) { + // XXX: this is never hit via spec tests Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()); - env->set_local(variables[j], res); + env.set_local(variables[j], res); } } } @@ -288,11 +290,7 @@ if (val) break; } } - // restore original environment - for (size_t j = 0, K = variables.size(); j < K; ++j) { - if(!old_vars[j]) env->del_local(variables[j]); - else env->set_local(variables[j], old_vars[j]); - } + exp.env_stack.pop_back(); return val; } @@ -300,10 +298,16 @@ { Expression* pred = w->predicate(); Block* body = w->block(); + Env env(environment(), true); + exp.env_stack.push_back(&env); while (*pred->perform(this)) { Expression* val = body->perform(this); - if (val) return val; + if (val) { + exp.env_stack.pop_back(); + return val; + } } + exp.env_stack.pop_back(); return 0; } @@ -314,8 +318,9 @@ Expression* Eval::operator()(Warning* w) { + Sass_Output_Style outstyle = ctx.c_options.output_style; + ctx.c_options.output_style = NESTED; Expression* message = w->message()->perform(this); - To_String to_string(&ctx); Env* env = exp.environment(); // try to use generic function @@ -331,24 +336,27 @@ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); + ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; } - std::string result(unquote(message->perform(&to_string))); + std::string result(unquote(message->to_sass())); Backtrace top(backtrace(), w->pstate(), ""); std::cerr << "WARNING: " << result; - std::cerr << top.to_string(true); + std::cerr << top.to_string(); std::cerr << std::endl << std::endl; + ctx.c_options.output_style = outstyle; return 0; } Expression* Eval::operator()(Error* e) { + Sass_Output_Style outstyle = ctx.c_options.output_style; + ctx.c_options.output_style = NESTED; Expression* message = e->message()->perform(this); - To_String to_string(&ctx); Env* env = exp.environment(); // try to use generic function @@ -364,21 +372,24 @@ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); + ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; } - std::string result(unquote(message->perform(&to_string))); + std::string result(unquote(message->to_sass())); + ctx.c_options.output_style = outstyle; error(result, e->pstate()); return 0; } Expression* Eval::operator()(Debug* d) { + Sass_Output_Style outstyle = ctx.c_options.output_style; + ctx.c_options.output_style = NESTED; Expression* message = d->value()->perform(this); - To_String to_string(&ctx, false, true); Env* env = exp.environment(); // try to use generic function @@ -394,6 +405,7 @@ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA); sass_list_set_value(c_args, 0, message->perform(&to_c)); union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler); + ctx.c_options.output_style = outstyle; sass_delete_value(c_args); sass_delete_value(c_val); return 0; @@ -401,10 +413,11 @@ } std::string cwd(ctx.cwd()); - std::string result(unquote(message->perform(&to_string))); + std::string result(unquote(message->to_sass())); std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd)); std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path)); + ctx.c_options.output_style = outstyle; std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result; std::cerr << std::endl; @@ -413,7 +426,29 @@ Expression* Eval::operator()(List* l) { + // special case for unevaluated map + if (l->separator() == SASS_HASH) { + Map* lm = SASS_MEMORY_NEW(ctx.mem, Map, + l->pstate(), + l->length() / 2); + for (size_t i = 0, L = l->length(); i < L; i += 2) + { + Expression* key = (*l)[i+0]->perform(this); + Expression* val = (*l)[i+1]->perform(this); + // make sure the color key never displays its real name + key->is_delayed(true); + *lm << std::make_pair(key, val); + } + if (lm->has_duplicate_key()) { + throw Exception::DuplicateKeyError(*lm, *l); + } + + lm->is_interpolant(l->is_interpolant()); + return lm->perform(this); + } + // check if we should expand it if (l->is_expanded()) return l; + // regular case for unevaluated lists List* ll = SASS_MEMORY_NEW(ctx.mem, List, l->pstate(), l->length(), @@ -422,6 +457,7 @@ for (size_t i = 0, L = l->length(); i < L; ++i) { *ll << (*l)[i]->perform(this); } + ll->is_interpolant(l->is_interpolant()); ll->is_expanded(true); return ll; } @@ -433,8 +469,7 @@ // make sure we're not starting with duplicate keys. // the duplicate key state will have been set in the parser phase. if (m->has_duplicate_key()) { - To_String to_string(&ctx); - error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate()); + throw Exception::DuplicateKeyError(*m, *m); } Map* mm = SASS_MEMORY_NEW(ctx.mem, Map, @@ -448,8 +483,7 @@ // check the evaluated keys aren't duplicates. if (mm->has_duplicate_key()) { - To_String to_string(&ctx); - error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", mm->pstate()); + throw Exception::DuplicateKeyError(*mm, *m); } mm->is_expanded(true); @@ -458,33 +492,75 @@ Expression* Eval::operator()(Binary_Expression* b) { + + String_Schema* ret_schema = 0; enum Sass_OP op_type = b->type(); + // don't eval delayed expressions (the '/' when used as a separator) - if (op_type == Sass_OP::DIV && b->is_delayed()) return b; - b->is_delayed(false); - // if one of the operands is a '/' then make sure it's evaluated - Expression* lhs = b->left()->perform(this); - lhs->is_delayed(false); - while (typeid(*lhs) == typeid(Binary_Expression)) { - Binary_Expression* lhs_ex = static_cast(lhs); - if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break; - lhs = Eval::operator()(lhs_ex); + if (op_type == Sass_OP::DIV && b->is_delayed()) { + b->right(b->right()->perform(this)); + b->left(b->left()->perform(this)); + return b; + } + + // only the last item will be used to eval the binary expression + if (String_Schema* s_l = dynamic_cast(b->left())) { + if (!s_l->has_interpolant() && (!s_l->is_right_interpolant())) { + ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_l->pstate()); + Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), + b->op(), s_l->last(), b->right()); + bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); + // bin_ex->is_interpolant(b->left()->is_interpolant()); + for (size_t i = 0; i < s_l->length() - 1; ++i) { + *ret_schema << s_l->at(i)->perform(this); + } + *ret_schema << bin_ex->perform(this); + return ret_schema->perform(this); + } + } + if (String_Schema* s_r = dynamic_cast(b->right())) { + if (!s_r->has_interpolant() && (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV)) { + ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_r->pstate()); + Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(), + b->op(), b->left(), s_r->first()); + bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed()); + // if (op_type == Sass_OP::SUB && b->is_right_interpolant()) bin_ex->is_interpolant(true); + *ret_schema << bin_ex->perform(this); + for (size_t i = 1; i < s_r->length(); ++i) { + *ret_schema << s_r->at(i)->perform(this); + } + return ret_schema->perform(this); + } } - switch (op_type) { - case Sass_OP::AND: - return *lhs ? b->right()->perform(this) : lhs; - break; - case Sass_OP::OR: - return *lhs ? lhs : b->right()->perform(this); - break; + // don't eval delayed expressions (the '/' when used as a separator) + if (op_type == Sass_OP::DIV && b->is_delayed()) { + b->right(b->right()->perform(this)); + b->left(b->left()->perform(this)); + return b; + } - default: - break; + // b->is_delayed(false); + Expression* lhs = b->left(); + Expression* rhs = b->right(); + + // bool delay_lhs = false; + // bool delay_rhs = false; + + if (String_Schema* schema = dynamic_cast(lhs)) { + if (schema->is_right_interpolant()) { + b->is_delayed(true); + // delay_lhs = true; + } } - // not a logical connective, so go ahead and eval the rhs - Expression* rhs = b->right()->perform(this); + if (String_Schema* schema = dynamic_cast(rhs)) { + if (schema->is_left_interpolant()) { + b->is_delayed(true); + // delay_rhs = true; + } + } + // maybe fully evaluate structure if (op_type == Sass_OP::EQ || op_type == Sass_OP::NEQ || @@ -493,6 +569,26 @@ op_type == Sass_OP::LT || op_type == Sass_OP::LTE) { + + if (String_Schema* schema = dynamic_cast(lhs)) { + if (schema->has_interpolants()) { + b->is_delayed(true); + } + } + if (String_Schema* schema = dynamic_cast(rhs)) { + if (schema->has_interpolants()) { + b->is_delayed(true); + } + } + lhs->is_expanded(false); + lhs->set_delayed(false); + lhs = lhs->perform(this); + lhs->is_expanded(false); + lhs->set_delayed(false); + lhs = lhs->perform(this); + rhs->is_expanded(false); + rhs->set_delayed(false); + rhs = rhs->perform(this); rhs->is_expanded(false); rhs->set_delayed(false); rhs = rhs->perform(this); @@ -503,6 +599,30 @@ // rhs = rhs->perform(this); } + // if one of the operands is a '/' then make sure it's evaluated + lhs = lhs->perform(this); + lhs->is_delayed(false); + while (typeid(*lhs) == typeid(Binary_Expression)) { + Binary_Expression* lhs_ex = static_cast(lhs); + if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break; + lhs = Eval::operator()(lhs_ex); + } + + switch (op_type) { + case Sass_OP::AND: + return *lhs ? b->right()->perform(this) : lhs; + break; + + case Sass_OP::OR: + return *lhs ? lhs : b->right()->perform(this); + break; + + default: + break; + } + // not a logical connective, so go ahead and eval the rhs + rhs = rhs->perform(this); + // upgrade string to number if possible (issue #948) if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) { if (String_Constant* str = dynamic_cast(rhs)) { @@ -515,18 +635,6 @@ } } - // see if it's a relational expression - switch(op_type) { - case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs)); - case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs)); - case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs) && !eq(lhs, rhs)); - case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs)); - case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs)); - case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs) || eq(lhs, rhs)); - - default: break; - } - Expression::Concrete_Type l_type = lhs->concrete_type(); Expression::Concrete_Type r_type = rhs->concrete_type(); @@ -534,23 +642,25 @@ // Is one of the operands an interpolant? String_Schema* s1 = dynamic_cast(b->left()); String_Schema* s2 = dynamic_cast(b->right()); + Binary_Expression* b1 = dynamic_cast(b->left()); + Binary_Expression* b2 = dynamic_cast(b->right()); - if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants())) { - std::string sep; - switch (op_type) { - case Sass_OP::SUB: sep = "-"; break; - case Sass_OP::DIV: sep = "/"; break; - case Sass_OP::ADD: sep = "+"; break; - case Sass_OP::MUL: sep = "*"; break; - default: break; - } + bool schema_op = false; + bool force_delay = (s2 && s2->is_left_interpolant()) || + (s1 && s1->is_right_interpolant()) || + (b1 && b1->is_right_interpolant()) || + (b2 && b2->is_left_interpolant()); + + if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay) + { // If possible upgrade LHS to a number - if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB) { + if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB || + op_type == Sass_OP::EQ) { if (String_Constant* str = dynamic_cast(lhs)) { std::string value(str->value()); const char* start = value.c_str(); - if (Prelexer::sequence < Prelexer::number >(start) != 0) { + if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) { lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value()); lhs->is_delayed(false); lhs = lhs->perform(this); } @@ -568,54 +678,118 @@ To_Value to_value(ctx, ctx.mem); Value* v_l = dynamic_cast(lhs->perform(&to_value)); Value* v_r = dynamic_cast(rhs->perform(&to_value)); - Expression::Concrete_Type l_type = lhs->concrete_type(); - Expression::Concrete_Type r_type = rhs->concrete_type(); + l_type = lhs->concrete_type(); + r_type = rhs->concrete_type(); - if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { - return SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(), - v_l->to_string() + " " + sep + " " + v_r->to_string()); + if (s2 && s2->has_interpolants() && s2->length()) { + Textual* front = dynamic_cast(s2->elements().front()); + if (front && !front->is_interpolant()) + { + // XXX: this is never hit via spec tests + schema_op = true; + rhs = front->perform(this); + } + } + + if (force_delay) { + std::string str(""); + str += v_l->to_string(ctx.c_options); + if (b->op().ws_before) str += " "; + str += b->separator(); + if (b->op().ws_after) str += " "; + str += v_r->to_string(ctx.c_options); + String_Constant* val = SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(), str); + val->is_interpolant(b->left()->has_interpolant()); + return val; } } + // see if it's a relational expression + try { + switch(op_type) { + case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs)); + case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs)); + case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs)); + case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gte")); + case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lt")); + case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs)); + default: break; + } + } + catch (Exception::OperationError& err) + { + // throw Exception::Base(b->pstate(), err.what()); + throw Exception::SassValueError(b->pstate(), err); + } + + l_type = lhs->concrete_type(); + r_type = rhs->concrete_type(); + // ToDo: throw error in op functions // ToDo: then catch and re-throw them - ParserState pstate(b->pstate()); - int precision = (int)ctx.c_options->precision; - bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED; - if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { - const Number* l_n = dynamic_cast(lhs); - const Number* r_n = dynamic_cast(rhs); - return op_numbers(ctx.mem, op_type, *l_n, *r_n, compressed, precision, &pstate); - } - if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { - const Number* l_n = dynamic_cast(lhs); - const Color* r_c = dynamic_cast(rhs); - return op_number_color(ctx.mem, op_type, *l_n, *r_c, compressed, precision, &pstate); - } - if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { - const Color* l_c = dynamic_cast(lhs); - const Number* r_n = dynamic_cast(rhs); - return op_color_number(ctx.mem, op_type, *l_c, *r_n, compressed, precision, &pstate); - } - if (l_type == Expression::COLOR && r_type == Expression::COLOR) { - const Color* l_c = dynamic_cast(lhs); - const Color* r_c = dynamic_cast(rhs); - return op_colors(ctx.mem, op_type, *l_c, *r_c, compressed, precision, &pstate); - } - - To_Value to_value(ctx, ctx.mem); - Value* v_l = dynamic_cast(lhs->perform(&to_value)); - Value* v_r = dynamic_cast(rhs->perform(&to_value)); - Value* ex = op_strings(ctx.mem, op_type, *v_l, *v_r, compressed, precision, &pstate); - if (String_Constant* str = dynamic_cast(ex)) + Expression* rv = 0; + try { + ParserState pstate(b->pstate()); + if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { + const Number* l_n = dynamic_cast(lhs); + const Number* r_n = dynamic_cast(rhs); + rv = op_numbers(ctx.mem, op_type, *l_n, *r_n, ctx.c_options, &pstate); + } + else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { + const Number* l_n = dynamic_cast(lhs); + const Color* r_c = dynamic_cast(rhs); + rv = op_number_color(ctx.mem, op_type, *l_n, *r_c, ctx.c_options, &pstate); + } + else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) { + const Color* l_c = dynamic_cast(lhs); + const Number* r_n = dynamic_cast(rhs); + rv = op_color_number(ctx.mem, op_type, *l_c, *r_n, ctx.c_options, &pstate); + } + else if (l_type == Expression::COLOR && r_type == Expression::COLOR) { + const Color* l_c = dynamic_cast(lhs); + const Color* r_c = dynamic_cast(rhs); + rv = op_colors(ctx.mem, op_type, *l_c, *r_c, ctx.c_options, &pstate); + } + else { + To_Value to_value(ctx, ctx.mem); + Value* v_l = dynamic_cast(lhs->perform(&to_value)); + Value* v_r = dynamic_cast(rhs->perform(&to_value)); + bool interpolant = b->is_right_interpolant() || + b->is_left_interpolant() || + b->is_interpolant(); + if (op_type == Sass_OP::SUB) interpolant = false; + // if (op_type == Sass_OP::DIV) interpolant = true; + Value* ex = op_strings(ctx.mem, b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress + if (String_Constant* str = dynamic_cast(ex)) + { + if (str->concrete_type() == Expression::STRING) + { + String_Constant* lstr = dynamic_cast(lhs); + String_Constant* rstr = dynamic_cast(rhs); + if (op_type != Sass_OP::SUB) { + if (String_Constant* org = lstr ? lstr : rstr) + { str->quote_mark(org->quote_mark()); } + } + } + } + ex->is_interpolant(b->is_interpolant()); + rv = ex; + } + } + catch (Exception::OperationError& err) { - if (str->concrete_type() != Expression::STRING) return ex; - String_Constant* lstr = dynamic_cast(lhs); - String_Constant* rstr = dynamic_cast(rhs); - if (String_Constant* org = lstr ? lstr : rstr) - { str->quote_mark(org->quote_mark()); } + // throw Exception::Base(b->pstate(), err.what()); + throw Exception::SassValueError(b->pstate(), err); } - return ex; + + if (rv) { + if (schema_op) { + // XXX: this is never hit via spec tests + (*s2)[0] = rv; + rv = s2->perform(this); + } + } + return rv; } @@ -635,16 +809,15 @@ return result; } else { - To_String to_string(&ctx); // 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)) { + if (operand->concrete_type() == Expression::NULL_VAL && dynamic_cast(u->operand())) { u->operand(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), "")); } else u->operand(operand); String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), - u->perform(&to_string)); + u->inspect()); return result; } // unreachable @@ -654,6 +827,7 @@ Expression* Eval::operator()(Function_Call* c) { if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) { + // XXX: this is never hit via spec tests std::ostringstream stm; stm << "Stack depth exceeded max of " << Constants::MaxCallStack; error(stm.str(), c->pstate(), backtrace()); @@ -673,13 +847,14 @@ c->pstate(), c->name(), args); - To_String to_string(&ctx); if (args->has_named_arguments()) { error("Function " + c->name() + " doesn't support keyword arguments", c->pstate()); } - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, - c->pstate(), - lit->perform(&to_string)); + String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, + c->pstate(), + lit->to_string(ctx.c_options)); + str->is_interpolant(c->is_interpolant()); + return str; } else { // call generic function full_name = "*[f]"; @@ -711,10 +886,9 @@ bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this); Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`"); exp.backtrace_stack.push_back(&here); - // if it's user-defined, eval the body - if (body) result = body->perform(this); - // if it's native, invoke the underlying CPP function - else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace()); + // eval the body if user-defined or special, invoke underlying CPP function if native + if (body && !Prelexer::re_special_fun(c->name().c_str())) { result = body->perform(this); } + else if (func) { result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace()); } if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate()); exp.backtrace_stack.pop_back(); } @@ -767,6 +941,7 @@ result->is_delayed(result->concrete_type() == Expression::STRING); if (!result->is_delayed()) result = result->perform(this); + result->is_interpolant(c->is_interpolant()); exp.env_stack.pop_back(); return result; } @@ -782,13 +957,11 @@ Expression* Eval::operator()(Variable* v) { - To_String to_string(&ctx); std::string name(v->name()); Expression* value = 0; Env* env = environment(); if (env->has(name)) value = static_cast((*env)[name]); else error("Undefined variable: \"" + v->name() + "\".", v->pstate()); - // std::cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << std::endl; if (typeid(*value) == typeid(Argument)) value = static_cast(value)->value(); // behave according to as ruby sass (add leading zero) @@ -800,7 +973,7 @@ if (auto str = dynamic_cast(value)) { value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str); } else if (auto str = dynamic_cast(value)) { - value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->perform(&to_string)); + value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->value()); } } else if (value->concrete_type() == Expression::LIST) { @@ -822,16 +995,21 @@ value = value->perform(this); // ->perform(&listize); } - // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::endl; - return value; + value->is_interpolant(v->is_interpolant()); + value->is_expanded(false); + return value->perform(this); } Expression* Eval::operator()(Textual* t) { using Prelexer::number; Expression* result = 0; - bool zero = !( t->value().substr(0, 1) == "." || - t->value().substr(0, 2) == "-." ); + size_t L = t->value().length(); + bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") || + (L > 1 && t->value().substr(0, 2) == "0.") || + (L > 1 && t->value().substr(0, 2) == "-.") || + (L > 2 && t->value().substr(0, 3) == "-0.") + ); const std::string& text = t->value(); size_t num_pos = text.find_first_not_of(" \n\r\t"); @@ -854,7 +1032,7 @@ t->pstate(), sass_atof(num.c_str()), "%", - zero); + true); break; case Textual::DIMENSION: result = SASS_MEMORY_NEW(ctx.mem, Number, @@ -878,7 +1056,7 @@ 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, true, + 1, // alpha channel t->value()); } else { @@ -887,11 +1065,12 @@ static_cast(strtol(std::string(2,hext[0]).c_str(), NULL, 16)), static_cast(strtol(std::string(2,hext[1]).c_str(), NULL, 16)), static_cast(strtol(std::string(2,hext[2]).c_str(), NULL, 16)), - 1, false, + 1, // alpha channel t->value()); } } break; } + result->is_interpolant(t->is_interpolant()); return result; } @@ -905,139 +1084,121 @@ return b; } - char is_quoted(std::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; - } - } + void Eval::interpolation(Context& ctx, std::string& res, Expression* ex, bool into_quotes, bool was_itpl) { - std::string Eval::interpolation(Expression* s, bool into_quotes) { - Env* env = environment(); - if (String_Quoted* str_quoted = dynamic_cast(s)) { - if (str_quoted->quote_mark()) { - if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) { - return evacuate_escapes(str_quoted->value()); - } else { - return string_escape(quote(str_quoted->value(), str_quoted->quote_mark())); - } - } else { - return evacuate_escapes(str_quoted->value()); + bool needs_closing_brace = false; +//debug_ast(ex); + if (Arguments* args = dynamic_cast(ex)) { + List* ll = SASS_MEMORY_NEW(ctx.mem, List, args->pstate(), 0, SASS_COMMA); + for(auto arg : *args) { + *ll << arg->value(); + } + ll->is_interpolant(args->is_interpolant()); + needs_closing_brace = true; + res += "("; + ex = ll; + } + if (Number* nr = dynamic_cast(ex)) { + if (!nr->is_valid_css_unit()) { + throw Exception::InvalidValue(*nr); + } + } + if (Argument* arg = dynamic_cast(ex)) { + ex = arg->value(); + } + if (String_Quoted* sq = dynamic_cast(ex)) { + if (was_itpl) { + bool was_interpolant = ex->is_interpolant(); + ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, sq->pstate(), sq->value()); + ex->is_interpolant(was_interpolant); } - } else if (String_Constant* str_constant = dynamic_cast(s)) { - if (into_quotes && !str_constant->is_interpolant()) return str_constant->value(); - return evacuate_escapes(str_constant->value()); - } else if (dynamic_cast(s)) { - To_String to_string(&ctx); - Expression* sel = s->perform(this); - return evacuate_quotes(sel ? sel->perform(&to_string) : ""); - - } else if (String_Schema* str_schema = dynamic_cast(s)) { - // To_String to_string(&ctx); - // return evacuate_quotes(str_schema->perform(&to_string)); - - std::string res = ""; - for(auto i : str_schema->elements()) - res += (interpolation(i)); - //ToDo: do this in one step - auto esc = evacuate_escapes(res); - auto unq = unquote(esc); - if (unq == esc) { - return string_to_output(res); + } + + if (dynamic_cast(ex)) { return; } + + // parent selector needs another go + if (dynamic_cast(ex)) { + // XXX: this is never hit via spec tests + ex = ex->perform(this); + } + + if (List* l = dynamic_cast(ex)) { + List* ll = SASS_MEMORY_NEW(ctx.mem, List, l->pstate(), 0, l->separator()); + // this fixes an issue with bourbon sample, not really sure why + // if (l->size() && dynamic_cast((*l)[0])) { res += ""; } + for(auto item : *l) { + item->is_interpolant(l->is_interpolant()); + std::string rl(""); interpolation(ctx, rl, item, into_quotes, l->is_interpolant()); + bool is_null = dynamic_cast(item) != 0; // rl != "" + if (!is_null) *ll << SASS_MEMORY_NEW(ctx.mem, String_Quoted, item->pstate(), rl); + } + res += (ll->to_string(ctx.c_options)); + ll->is_interpolant(l->is_interpolant()); + } + + // Value + // Textual + // Function_Call + // Selector_List + // String_Quoted + // String_Constant + // Parent_Selector + // Binary_Expression + else { + // ex = ex->perform(this); + if (into_quotes && ex->is_interpolant()) { + res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : ""); } else { - return evacuate_quotes(unq); + res += ex ? ex->to_string(ctx.c_options) : ""; } - } else if (List* list = dynamic_cast(s)) { - std::string acc = ""; // ToDo: different output styles - std::string sep = list->separator() == SASS_COMMA ? "," : " "; - if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " "; - bool initial = false; - for(auto item : list->elements()) { - if (item->concrete_type() != Expression::NULL_VAL) { - if (initial) acc += sep; - acc += interpolation(item); - initial = true; - } - } - return evacuate_quotes(acc); - } else if (Variable* var = dynamic_cast(s)) { - std::string name(var->name()); - if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate()); - Expression* value = static_cast((*env)[name]); - return evacuate_quotes(interpolation(value)); - } else if (dynamic_cast(s)) { - Expression* ex = s->perform(this); - // avoid recursive calls if same object gets returned - // since we will call interpolate again for the result - if (ex == s) { - To_String to_string(&ctx); - return evacuate_quotes(s->perform(&to_string)); - } - return evacuate_quotes(interpolation(ex)); - } else if (dynamic_cast(s)) { - Expression* ex = s->perform(this); - return evacuate_quotes(unquote(interpolation(ex))); - } else if (dynamic_cast(s)) { - Expression* ex = s->perform(this); - return evacuate_quotes(interpolation(ex)); - } else if (dynamic_cast(s)) { - To_String to_string(&ctx); - std::string dbg(s->perform(&to_string)); - error(dbg + " isn't a valid CSS value.", s->pstate()); - return dbg; - } else { - To_String to_string(&ctx); - return evacuate_quotes(s->perform(&to_string)); } + + if (needs_closing_brace) res += ")"; + } Expression* Eval::operator()(String_Schema* s) { - std::string acc; - bool into_quotes = false; size_t L = s->length(); + bool into_quotes = false; if (L > 1) { + if (!dynamic_cast((*s)[0]) && !dynamic_cast((*s)[L - 1])) { if (String_Constant* l = dynamic_cast((*s)[0])) { if (String_Constant* r = dynamic_cast((*s)[L - 1])) { if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true; if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true; } } - } - for (size_t i = 0; i < L; ++i) { - // really a very special fix, but this is the logic I got from - // analyzing the ruby sass behavior and it actually seems to work - // https://github.com/sass/libsass/issues/1333 - if (i == 0 && L > 1 && dynamic_cast((*s)[i])) { - Expression* ex = (*s)[i]->perform(this); - if (auto sq = dynamic_cast(ex)) { - if (sq->is_delayed() && ! s->has_interpolants()) { - acc += string_escape(quote(sq->value(), sq->quote_mark())); - } else { - acc += interpolation((*s)[i], into_quotes); - } - } else if (ex) { - acc += interpolation((*s)[i], into_quotes); - } - } else if ((*s)[i]) { - acc += interpolation((*s)[i], into_quotes); } } - String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), acc); - if (!str->quote_mark()) { - str->value(string_unescape(str->value())); - } else if (str->quote_mark()) { - str->quote_mark('*'); - } - str->is_delayed(true); + bool was_quoted = false; + bool was_interpolant = false; + std::string res(""); + for (size_t i = 0; i < L; ++i) { + bool is_quoted = dynamic_cast((*s)[i]) != NULL; + (*s)[i]->perform(this); + if (was_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } + else if (i > 0 && is_quoted && !(*s)[i]->is_interpolant() && !was_interpolant) { res += " "; } + Expression* ex = (*s)[i]->is_delayed() ? (*s)[i] : (*s)[i]->perform(this); + interpolation(ctx, res, ex, into_quotes, ex->is_interpolant()); + was_quoted = dynamic_cast((*s)[i]) != NULL; + was_interpolant = (*s)[i]->is_interpolant(); + + } + if (!s->is_interpolant()) { + if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(ctx.mem, Null, s->pstate()); + return SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res); + } + String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), res); + // if (s->is_interpolant()) str->quote_mark(0); + // String_Constant* str = SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res); + if (str->quote_mark()) str->quote_mark('*'); + else if (!is_in_comment) str->value(string_to_output(str->value())); + str->is_interpolant(s->is_interpolant()); return str; } + Expression* Eval::operator()(String_Constant* s) { if (!s->is_delayed() && name_to_color(s->value())) { @@ -1051,7 +1212,11 @@ Expression* Eval::operator()(String_Quoted* s) { - return s; + String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), ""); + str->value(s->value()); + str->quote_mark(s->quote_mark()); + str->is_interpolant(s->is_interpolant()); + return str; } Expression* Eval::operator()(Supports_Operator* c) @@ -1111,7 +1276,6 @@ Expression* Eval::operator()(Media_Query* q) { - To_String to_string(&ctx); String* t = q->media_type(); t = static_cast(t ? t->perform(this) : 0); Media_Query* qq = SASS_MEMORY_NEW(ctx.mem, Media_Query, @@ -1138,6 +1302,7 @@ Expression* value = e->value(); value = (value ? value->perform(this) : 0); if (value && dynamic_cast(value)) { + // XXX: this is never hit via spec tests value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, value->pstate(), dynamic_cast(value)->value()); @@ -1191,8 +1356,49 @@ { Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate()); for (size_t i = 0, L = a->length(); i < L; ++i) { - *aa << static_cast((*a)[i]->perform(this)); + Argument* arg = static_cast((*a)[i]->perform(this)); + if (!(arg->is_rest_argument() || arg->is_keyword_argument())) { + *aa << arg; + } + } + + if (a->has_rest_argument()) { + Expression* splat = static_cast( + a->get_rest_argument()->perform(this) + )->value()->perform(this); + + Sass_Separator separator = SASS_COMMA; + List* ls = dynamic_cast(splat); + Map* ms = dynamic_cast(splat); + + List* arglist = SASS_MEMORY_NEW(ctx.mem, List, + splat->pstate(), + 0, + ls ? ls->separator() : separator, + true); + + if (ls && ls->is_arglist()) { + for (auto as : *ls) *arglist << as; + } else if (ms) { + *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), ms, "", false, true); + } else if (ls) { + for (auto as : *ls) *arglist << as; + } else { + *arglist << splat; + } + if (arglist->length()) { + *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), arglist, "", true); + } } + + if (a->has_keyword_argument()) { + Expression* kwarg = static_cast( + a->get_keyword_argument()->perform(this) + )->value()->perform(this); + + *aa << SASS_MEMORY_NEW(ctx.mem, Argument, kwarg->pstate(), kwarg, "", false, true); + } + return aa; } @@ -1214,25 +1420,27 @@ return lhs && rhs && *lhs == *rhs; } - bool Eval::lt(Expression* lhs, Expression* rhs) + bool Eval::lt(Expression* lhs, Expression* rhs, std::string op) { Number* l = dynamic_cast(lhs); Number* r = dynamic_cast(rhs); - if (!l) error("may only compare numbers", lhs->pstate()); - if (!r) error("may only compare numbers", rhs->pstate()); + // use compare operator from ast node + if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op); // use compare operator from ast node return *l < *r; } - Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, bool compressed, int precision, ParserState* pstate) + Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate) { double lv = l.value(); double rv = r.value(); - if (op == Sass_OP::DIV && !rv) { - return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), "Infinity"); + if (op == Sass_OP::DIV && rv == 0) { + // XXX: this is never hit via spec tests + return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN"); } if (op == Sass_OP::MOD && !rv) { - error("division by zero", pstate ? *pstate : r.pstate()); + // XXX: this is never hit via spec tests + throw Exception::ZeroDivisionError(l, r); } Number tmp(r); @@ -1240,10 +1448,6 @@ tmp.normalize(l.find_convertible_unit(), strict); std::string l_unit(l.unit()); std::string r_unit(tmp.unit()); - if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() && - (op == Sass_OP::ADD || op == Sass_OP::SUB)) { - error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", pstate ? *pstate : r.pstate()); - } Number* v = SASS_MEMORY_NEW(mem, Number, l); v->pstate(pstate ? *pstate : l.pstate()); if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) { @@ -1269,16 +1473,20 @@ v->numerator_units().push_back(r.denominator_units()[i]); } } else { + Number rh(r); + v->value(ops[op](lv, rh.value() * r.convert_factor(l))); + // v->normalize(); + return v; + v->value(ops[op](lv, tmp.value())); } v->normalize(); return v; } - Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, bool compressed, int precision, ParserState* pstate) + Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, struct Sass_Inspect_Options opt, ParserState* pstate) { Color r(rh); - r.disp(""); double lv = l.value(); switch (op) { case Sass_OP::ADD: @@ -1293,15 +1501,15 @@ case Sass_OP::SUB: case Sass_OP::DIV: { std::string sep(op == Sass_OP::SUB ? "-" : "/"); - std::string color(r.to_string(compressed||!r.sixtuplet(), precision)); + std::string color(r.to_string(opt)); return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), - l.to_string(compressed, precision) + l.to_string(opt) + sep + color); } break; case Sass_OP::MOD: { - error("cannot divide a number by a color", pstate ? *pstate : r.pstate()); + throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op)); } break; default: break; // caller should ensure that we don't get here } @@ -1309,10 +1517,13 @@ return SASS_MEMORY_NEW(mem, Color, rh); } - Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, bool compressed, int precision, ParserState* pstate) + Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate) { double rv = r.value(); - if (op == Sass_OP::DIV && !rv) error("division by zero", pstate ? *pstate : r.pstate()); + if (op == Sass_OP::DIV && !rv) { + // comparison of Fixnum with Float failed? + throw Exception::ZeroDivisionError(l, r); + } return SASS_MEMORY_NEW(mem, Color, pstate ? *pstate : l.pstate(), ops[op](l.r(), rv), @@ -1321,13 +1532,14 @@ l.a()); } - Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, bool compressed, int precision, ParserState* pstate) + Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate) { if (l.a() != r.a()) { - error("alpha channels must be equal when combining colors", pstate ? *pstate : r.pstate()); + throw Exception::AlphaChannelsNotEqual(&l, &r, "+"); } if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) { - error("division by zero", pstate ? *pstate : r.pstate()); + // comparison of Fixnum with Float failed? + throw Exception::ZeroDivisionError(l, r); } return SASS_MEMORY_NEW(mem, Color, pstate ? *pstate : l.pstate(), @@ -1337,66 +1549,39 @@ l.a()); } - Value* Eval::op_strings(Memory_Manager& mem, enum Sass_OP op, Value& lhs, Value& rhs, bool compressed, int precision, ParserState* pstate) + Value* Eval::op_strings(Memory_Manager& mem, Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed) { Expression::Concrete_Type ltype = lhs.concrete_type(); Expression::Concrete_Type rtype = rhs.concrete_type(); + enum Sass_OP op = operand.operand; String_Quoted* lqstr = dynamic_cast(&lhs); String_Quoted* rqstr = dynamic_cast(&rhs); - std::string lstr(lqstr ? lqstr->value() : lhs.to_string(compressed, precision)); - std::string rstr(rqstr ? rqstr->value() : rhs.to_string(compressed, precision)); + std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt)); + std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt)); - bool l_str_quoted = ((Sass::String*)&lhs) && ((Sass::String*)&lhs)->sass_fix_1291(); - bool r_str_quoted = ((Sass::String*)&rhs) && ((Sass::String*)&rhs)->sass_fix_1291(); - bool l_str_color = ltype == Expression::STRING && name_to_color(lstr) && !l_str_quoted; - bool r_str_color = rtype == Expression::STRING && name_to_color(rstr) && !r_str_quoted; - - if (l_str_color && r_str_color) { - const Color* c_l = name_to_color(lstr); - const Color* c_r = name_to_color(rstr); - return op_colors(mem, op,*c_l, *c_r, compressed, precision); - } - else if (l_str_color && rtype == Expression::COLOR) { - const Color* c_l = name_to_color(lstr); - const Color* c_r = dynamic_cast(&rhs); - return op_colors(mem, op, *c_l, *c_r, compressed, precision); - } - else if (ltype == Expression::COLOR && r_str_color) { - const Color* c_l = dynamic_cast(&lhs); - const Color* c_r = name_to_color(rstr); - return op_colors(mem, op, *c_l, *c_r, compressed, precision); - } - else if (l_str_color && rtype == Expression::NUMBER) { - const Color* c_l = name_to_color(lstr); - const Number* n_r = dynamic_cast(&rhs); - return op_color_number(mem, op, *c_l, *n_r, compressed, precision); - } - else if (ltype == Expression::NUMBER && r_str_color) { - const Number* n_l = dynamic_cast(&lhs); - const Color* c_r = name_to_color(rstr); - return op_number_color(mem, op, *n_l, *c_r, compressed, precision); - } - if (op == Sass_OP::MUL) error("invalid operands for multiplication", lhs.pstate()); - if (op == Sass_OP::MOD) error("invalid operands for modulo", lhs.pstate()); + if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op)); + if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op)); + if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op)); + if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op)); std::string sep; switch (op) { case Sass_OP::SUB: sep = "-"; break; case Sass_OP::DIV: sep = "/"; break; + case Sass_OP::MUL: sep = "*"; break; + case Sass_OP::MOD: sep = "%"; break; + case Sass_OP::EQ: sep = "=="; break; + case Sass_OP::NEQ: sep = "!="; break; + case Sass_OP::LT: sep = "<"; break; + case Sass_OP::GT: sep = ">"; break; + case Sass_OP::LTE: sep = "<="; break; + case Sass_OP::GTE: sep = ">="; break; default: break; } - if (ltype == Expression::NULL_VAL) error("Invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs.pstate()); - if (rtype == Expression::NULL_VAL) error("Invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", rhs.pstate()); - - if (ltype == Expression::NUMBER && sep == "/" && rtype == Expression::STRING) - { - return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), - lhs.to_string() + sep + rhs.to_string()); - } - if ( (ltype == Expression::STRING || sep == "") && - (sep != "/" || !rqstr || !rqstr->quote_mark()) + if ( (sep == "") /* && + (sep != "/" || !rqstr || !rqstr->quote_mark()) */ ) { char quote_mark = 0; std::string unq(unquote(lstr + sep + rstr, "e_mark, true)); @@ -1405,7 +1590,19 @@ } return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr); } - return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + quote(rstr)); + + if (sep != "" && !delayed) { + if (operand.ws_before) sep = " " + sep; + if (operand.ws_after) sep = sep + " "; + } + + if (op == Sass_OP::SUB || op == Sass_OP::DIV) { + if (lqstr && lqstr->quote_mark()) lstr = quote(lstr); + if (rqstr && rqstr->quote_mark()) rstr = quote(rstr); + return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), lstr + sep + rstr); + } + + return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + (rstr)); } Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate) @@ -1496,6 +1693,7 @@ } + // XXX: this is never hit via spec tests Attribute_Selector* Eval::operator()(Attribute_Selector* s) { String* attr = s->value(); @@ -1507,9 +1705,8 @@ Selector_List* Eval::operator()(Selector_Schema* s) { - To_String to_string; // the parser will look for a brace to end the selector - std::string result_str(s->contents()->perform(this)->perform(&to_string)); + std::string result_str(s->contents()->perform(this)->to_string(ctx.c_options)); result_str = unquote(Util::rtrim(result_str)) + "{"; Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()); return operator()(p.parse_selector_list(exp.block_stack.back()->is_root())); diff -Nru libsass-3.3.2/src/eval.hpp libsass-3.3.4/src/eval.hpp --- libsass-3.3.2/src/eval.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/eval.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,15 +1,15 @@ #ifndef SASS_EVAL_H #define SASS_EVAL_H +#include "ast.hpp" #include "context.hpp" -#include "listize.hpp" #include "operation.hpp" +#include "environment.hpp" namespace Sass { class Expand; class Context; - class Listize; class Eval : public Operation_CRTP { @@ -19,17 +19,16 @@ public: Expand& exp; Context& ctx; - Listize listize; Eval(Expand& exp); - virtual ~Eval(); + ~Eval(); + + bool is_in_comment; Env* environment(); Context& context(); Selector_List* selector(); Backtrace* backtrace(); - using Operation::operator(); - // for evaluating function bodies Expression* operator()(Block*); Expression* operator()(Assignment*); @@ -87,16 +86,16 @@ // -- only need to define two comparisons, and the rest can be implemented in terms of them static bool eq(Expression*, Expression*); - static bool lt(Expression*, Expression*); + static bool lt(Expression*, Expression*, std::string op); // -- arithmetic on the combinations that matter - static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0); - static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0); - static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0); - static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0); - static Value* op_strings(Memory_Manager&, enum Sass_OP, Value&, Value&, bool compressed = false, int precision = 5, ParserState* pstate = 0); + static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); + static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); + static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); + static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, ParserState* pstate = 0); + static Value* op_strings(Memory_Manager&, Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, ParserState* pstate = 0, bool interpolant = false); private: - std::string interpolation(Expression* s, bool into_quotes = false); + void interpolation(Context& ctx, std::string& res, Expression* ex, bool into_quotes, bool was_itpl = false); }; diff -Nru libsass-3.3.2/src/expand.cpp libsass-3.3.4/src/expand.cpp --- libsass-3.3.2/src/expand.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/expand.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,14 +1,10 @@ -#ifdef _MSC_VER -#pragma warning(disable : 4503) -#endif - +#include "sass.hpp" #include #include #include "expand.hpp" #include "bind.hpp" #include "eval.hpp" -#include "to_string.hpp" #include "backtrace.hpp" #include "context.hpp" #include "parser.hpp" @@ -20,6 +16,7 @@ eval(Eval(*this)), env_stack(std::vector()), block_stack(std::vector()), + call_stack(std::vector()), property_stack(std::vector()), selector_stack(std::vector()), backtrace_stack(std::vector()), @@ -28,6 +25,7 @@ env_stack.push_back(0); env_stack.push_back(env); block_stack.push_back(0); + call_stack.push_back(0); // import_stack.push_back(0); property_stack.push_back(0); selector_stack.push_back(0); @@ -106,7 +104,7 @@ while (tail) { if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) { if (dynamic_cast(header) == NULL) continue; // skip all others - To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string)); + std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace()); } tail = tail->tail(); @@ -119,13 +117,33 @@ Selector_List* sel = dynamic_cast(ex); if (sel == 0) throw std::runtime_error("Expanded null selector"); + if (sel->length() == 0 || sel->has_parent_ref()) { + bool has_parent_selector = false; + for (size_t i = 0, L = selector_stack.size(); i < L && !has_parent_selector; i++) { + Selector_List* ll = selector_stack.at(i); + has_parent_selector = ll != 0 && ll->length() > 0; + } + if (!has_parent_selector) { + error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace()); + } + } + selector_stack.push_back(sel); + Env* env = 0; + if (block_stack.back()->is_root()) { + env = new Env(environment()); + env_stack.push_back(env); + } Block* blk = r->block()->perform(this)->block(); Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset, r->pstate(), sel, blk); selector_stack.pop_back(); + if (block_stack.back()->is_root()) { + env_stack.pop_back(); + delete env; + } rr->tabs(r->tabs()); return rr; @@ -177,12 +195,15 @@ Statement* Expand::operator()(Media_Block* m) { - To_String to_string(&ctx); Expression* mq = m->media_queries()->perform(&eval); - mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries(); + std::string str_mq(mq->to_string(ctx.c_options)); + char* str = sass_strdup(str_mq.c_str()); + ctx.strings.push_back(str); + Parser p(Parser::from_c_str(str, ctx, mq->pstate())); + mq = p.parse_media_queries(); Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block, m->pstate(), - static_cast(mq), + static_cast(mq->perform(&eval)), m->block()->perform(this)->block(), 0); mm->tabs(m->tabs()); @@ -318,6 +339,11 @@ Statement* Expand::operator()(Import_Stub* i) { + // get parent node from call stack + AST_Node* parent = call_stack.back(); + if (parent && dynamic_cast(parent) == NULL) { + error("Import directives may not be used within control directives or mixins.", i->pstate()); + } // we don't seem to need that actually afterall Sass_Import_Entry import = sass_make_import( i->imp_path().c_str(), @@ -355,12 +381,18 @@ Statement* Expand::operator()(Comment* c) { + eval.is_in_comment = true; + auto rv = SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast(c->text()->perform(&eval)), c->is_important()); + eval.is_in_comment = false; // TODO: eval the text, once we're parsing/storing it as a String_Schema - return SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast(c->text()->perform(&eval)), c->is_important()); + return rv; } Statement* Expand::operator()(If* i) { + Env env(environment(), true); + env_stack.push_back(&env); + call_stack.push_back(i); if (*i->predicate()->perform(&eval)) { append_block(i->block()); } @@ -368,6 +400,8 @@ Block* alt = i->alternative(); if (alt) append_block(alt); } + call_stack.pop_back(); + env_stack.pop_back(); return 0; } @@ -378,11 +412,11 @@ std::string variable(f->variable()); Expression* low = f->lower_bound()->perform(&eval); if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace()); + throw Exception::TypeMismatch(*low, "integer"); } Expression* high = f->upper_bound()->perform(&eval); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace()); + throw Exception::TypeMismatch(*high, "integer"); } Number* sass_start = static_cast(low); Number* sass_end = static_cast(high); @@ -396,10 +430,11 @@ double start = sass_start->value(); double end = sass_end->value(); // only create iterator once in this environment - Env* env = environment(); - Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit()); - AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0; - env->set_local(variable, it); + Env env(environment(), true); + env_stack.push_back(&env); + call_stack.push_back(f); + Number* it = SASS_MEMORY_NEW(env.mem, Number, low->pstate(), start, sass_end->unit()); + env.set_local(variable, it); Block* body = f->block(); if (start < end) { if (f->is_inclusive()) ++end; @@ -407,7 +442,7 @@ i < end; ++i) { it->value(i); - env->set_local(variable, it); + env.set_local(variable, it); append_block(body); } } else { @@ -416,13 +451,12 @@ i > end; --i) { it->value(i); - env->set_local(variable, it); + env.set_local(variable, it); append_block(body); } } - // restore original environment - if (!old_var) env->del_local(variable); - else env->set_local(variable, old_var); + call_stack.pop_back(); + env_stack.pop_back(); return 0; } @@ -432,11 +466,15 @@ { std::vector variables(e->variables()); Expression* expr = e->list()->perform(&eval); - List* list = 0; + Vectorized* list = 0; Map* map = 0; if (expr->concrete_type() == Expression::MAP) { map = static_cast(expr); } + else if (Selector_List* ls = dynamic_cast(expr)) { + Listize listize(ctx.mem); + list = dynamic_cast(ls->perform(&listize)); + } else if (expr->concrete_type() != Expression::LIST) { list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA); *list << expr; @@ -445,12 +483,9 @@ list = static_cast(expr); } // remember variables and then reset them - Env* env = environment(); - std::vector old_vars(variables.size()); - for (size_t i = 0, L = variables.size(); i < L; ++i) { - old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0; - env->set_local(variables[i], 0); - } + Env env(environment(), true); + env_stack.push_back(&env); + call_stack.push_back(e); Block* body = e->block(); if (map) { @@ -462,16 +497,19 @@ List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE); *variable << k; *variable << v; - env->set_local(variables[0], variable); + env.set_local(variables[0], variable); } else { - env->set_local(variables[0], k); - env->set_local(variables[1], v); + env.set_local(variables[0], k); + env.set_local(variables[1], v); } append_block(body); } } else { - bool arglist = list->is_arglist(); + // bool arglist = list->is_arglist(); + if (list->length() == 1 && dynamic_cast(list)) { + list = dynamic_cast*>(list); + } for (size_t i = 0, L = list->length(); i < L; ++i) { Expression* e = (*list)[i]; // unwrap value if the expression is an argument @@ -480,33 +518,30 @@ if (List* scalars = dynamic_cast(e)) { if (variables.size() == 1) { Expression* var = scalars; - if (arglist) var = (*scalars)[0]; - env->set_local(variables[0], var); + // if (arglist) var = (*scalars)[0]; + env.set_local(variables[0], var); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) { Expression* res = j >= scalars->length() ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()) : (*scalars)[j]->perform(&eval); - env->set_local(variables[j], res); + env.set_local(variables[j], res); } } } else { if (variables.size() > 0) { - env->set_local(variables[0], e); + env.set_local(variables[0], e); for (size_t j = 1, K = variables.size(); j < K; ++j) { Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate()); - env->set_local(variables[j], res); + env.set_local(variables[j], res); } } } append_block(body); } } - // restore original environment - for (size_t j = 0, K = variables.size(); j < K; ++j) { - if(!old_vars[j]) env->del_local(variables[j]); - else env->set_local(variables[j], old_vars[j]); - } + call_stack.pop_back(); + env_stack.pop_back(); return 0; } @@ -514,9 +549,14 @@ { Expression* pred = w->predicate(); Block* body = w->block(); + Env env(environment(), true); + env_stack.push_back(&env); + call_stack.push_back(w); while (*pred->perform(&eval)) { append_block(body); } + call_stack.pop_back(); + env_stack.pop_back(); return 0; } @@ -526,20 +566,16 @@ return 0; } - Statement* Expand::operator()(Extension* e) - { - To_String to_string(&ctx); - Selector_List* extender = dynamic_cast(selector()); - if (!extender) return 0; - selector_stack.push_back(0); - if (Selector_List* selector_list = dynamic_cast(e->selector())) { - for (Complex_Selector* complex_selector : selector_list->elements()) { + void Expand::expand_selector_list(Selector* s, Selector_List* extender) { + + if (Selector_List* sl = dynamic_cast(s)) { + for (Complex_Selector* complex_selector : sl->elements()) { Complex_Selector* tail = complex_selector; while (tail) { if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) { if (dynamic_cast(header) == NULL) continue; // skip all others - To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string)); + std::string sel_str(complex_selector->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace()); } tail = tail->tail(); @@ -547,16 +583,17 @@ } } - Selector_List* contextualized = dynamic_cast(e->selector()->perform(&eval)); - if (contextualized == NULL) return 0; + + Selector_List* contextualized = dynamic_cast(s->perform(&eval)); + if (contextualized == NULL) return; for (auto complex_sel : contextualized->elements()) { Complex_Selector* c = complex_sel; if (!c->head() || c->tail()) { - To_String to_string(&ctx); std::string sel_str(contextualized->perform(&to_string)); + std::string sel_str(contextualized->to_string(ctx.c_options)); error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace()); } Compound_Selector* placeholder = c->head(); - placeholder->is_optional(e->selector()->is_optional()); + placeholder->is_optional(s->is_optional()); for (size_t i = 0, L = extender->length(); i < L; ++i) { Complex_Selector* sel = (*extender)[i]; if (!(sel->head() && sel->head()->length() > 0 && @@ -579,8 +616,16 @@ } } - selector_stack.pop_back(); + } + Statement* Expand::operator()(Extension* e) + { + if (Selector_List* extender = dynamic_cast(selector())) { + selector_stack.push_back(0); + Selector* s = e->selector(); + expand_selector_list(s, extender); + selector_stack.pop_back(); + } return 0; } @@ -680,10 +725,12 @@ // process and add to last block on stack inline void Expand::append_block(Block* b) { + if (b->is_root()) call_stack.push_back(b); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* ith = (*b)[i]->perform(this); if (ith) *block_stack.back() << ith; } + if (b->is_root()) call_stack.pop_back(); } } diff -Nru libsass-3.3.2/src/expand.hpp libsass-3.3.4/src/expand.hpp --- libsass-3.3.2/src/expand.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/expand.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -30,6 +30,7 @@ // it's easier to work with vectors std::vector env_stack; std::vector block_stack; + std::vector call_stack; std::vector property_stack; std::vector selector_stack; std::vectorbacktrace_stack; @@ -37,11 +38,12 @@ Statement* fallback_impl(AST_Node* n); + private: + void expand_selector_list(Selector*, Selector_List* extender); + public: Expand(Context&, Env*, Backtrace*); - virtual ~Expand() { } - - using Operation::operator(); + ~Expand() { } Statement* operator()(Block*); Statement* operator()(Ruleset*); diff -Nru libsass-3.3.2/src/extend.cpp libsass-3.3.4/src/extend.cpp --- libsass-3.3.2/src/extend.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/extend.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,10 +1,6 @@ -#ifdef _MSC_VER -#pragma warning(disable : 4503) -#endif - +#include "sass.hpp" #include "extend.hpp" #include "context.hpp" -#include "to_string.hpp" #include "backtrace.hpp" #include "paths.hpp" #include "parser.hpp" @@ -85,23 +81,20 @@ std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) { - To_String to_string; for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) { if (i > 0) os << ", "; - os << compoundSelector[i]->perform(&to_string); + os << compoundSelector[i]->to_string(); } return os; } std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) { - To_String to_string; - os << simpleSelector.perform(&to_string); + os << simpleSelector.to_string(); return os; } // Print a string representation of a Compound_Selector static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) { - To_String to_string; if (message) { std::cerr << message; @@ -125,7 +118,6 @@ // 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) { std::cerr << message; @@ -144,7 +136,6 @@ std::ostream& operator<<(std::ostream& os, Complex_Selector& complexSelector) { - To_String to_string; os << "["; Complex_Selector* pIter = &complexSelector; @@ -164,7 +155,7 @@ first = false; if (pIter->head()) { - os << pIter->head()->perform(&to_string); + os << pIter->head()->to_string(); } else { os << "NULL_HEAD"; } @@ -179,7 +170,6 @@ // 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) { std::cerr << message; @@ -197,7 +187,6 @@ } static void printSelsNewSeqPairCollection(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) { - To_String to_string; if (message) { std::cerr << message; @@ -225,7 +214,6 @@ // 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) { std::cerr << message; @@ -1558,8 +1546,6 @@ Node extendedSelectors = Node::createCollection(); // extendedSelectors.got_line_feed = true; - To_String to_string; - SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec()); typedef std::vector > > GroupedByToAResult; @@ -1591,6 +1577,7 @@ for (size_t index = 0; index < pCompound->length(); index++) { Simple_Selector* pSimpleSelector = (*pCompound)[index]; (*pSels) << pSimpleSelector; + pCompound->extended(true); } } @@ -1732,18 +1719,32 @@ Compound_Selector* pHead = pIter->head(); if (pHead) { + for (Simple_Selector* pSimple : *pHead) { + if (Wrapped_Selector* ws = dynamic_cast(pSimple)) { + if (Selector_List* sl = dynamic_cast(ws->selector())) { + for (Complex_Selector* cs : sl->elements()) { + while (cs) { + if (complexSelectorHasExtension(cs, ctx, subset_map)) { + hasExtension = true; + break; + } + cs = cs->tail(); + } + } + } + } + } SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec()); for (ExtensionPair ext : entries) { // check if both selectors have the same media block parent if (ext.first->media_block() == pComplexSelector->media_block()) continue; - To_String to_string(&ctx); if (ext.second->media_block() == 0) continue; if (pComplexSelector->media_block() && ext.second->media_block()->media_queries() && pComplexSelector->media_block()->media_queries() ) { - std::string query_left(ext.second->media_block()->media_queries()->perform(&to_string)); - std::string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string)); + std::string query_left(ext.second->media_block()->media_queries()->to_string(ctx.c_options)); + std::string query_right(pComplexSelector->media_block()->media_queries()->to_string(ctx.c_options)); if (query_left == query_right) continue; } @@ -1754,7 +1755,7 @@ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd)); err << "You may not @extend an outer selector from within @media.\n"; err << "You may only @extend selectors within the same directive.\n"; - err << "From \"@extend " << ext.second->perform(&to_string) << "\""; + err << "From \"@extend " << ext.second->to_string(ctx.c_options) << "\""; err << " on line " << pstate.line+1 << " of " << rel_path << "\n"; error(err.str(), pComplexSelector->pstate()); } @@ -1764,29 +1765,6 @@ pIter = pIter->tail(); } - if (!hasExtension) { - /* ToDo: don't break stuff - std::stringstream err; - To_String to_string(&ctx); - std::string cwd(Sass::File::get_cwd()); - std::string sel1(pComplexSelector->perform(&to_string)); - Compound_Selector* pExtendSelector = 0; - for (auto i : subset_map.values()) { - if (i.first == pComplexSelector) { - pExtendSelector = i.second; - break; - } - } - if (!pExtendSelector || !pExtendSelector->is_optional()) { - std::string sel2(pExtendSelector ? pExtendSelector->perform(&to_string) : "[unknown]"); - err << "\"" << sel1 << "\" failed to @extend \"" << sel2 << "\"\n"; - err << "The selector \"" << sel2 << "\" was not found.\n"; - err << "Use \"@extend " << sel2 << " !optional\" if the extend should be able to fail."; - error(err.str(), pExtendSelector ? pExtendSelector->pstate() : pComplexSelector->pstate()); - } - */ - } - return hasExtension; } @@ -1928,8 +1906,6 @@ */ Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) { - To_String to_string(&ctx); - Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length()); extendedSomething = false; @@ -1967,6 +1943,21 @@ } } + for (Complex_Selector* cs : *pNewSelectors) { + while (cs) { + if (cs->head()) { + for (Simple_Selector* ss : *cs->head()) { + if (Wrapped_Selector* ws = dynamic_cast(ss)) { + if (Selector_List* sl = dynamic_cast(ws->selector())) { + bool extended = false; + ws->selector(extendSelectorList(sl, ctx, subset_map, false, extended)); + } + } + } + } + cs = cs->tail(); + } + } return pNewSelectors; } @@ -2006,9 +1997,8 @@ // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change. template static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subset_map) { - To_String to_string(&ctx); - DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast(pObject->selector())->perform(&to_string)) + DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast(pObject->selector())->to_string(ctx.c_options)) // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions // of doing so, so for now, let's just not extend things that won't be output later. @@ -2021,8 +2011,8 @@ Selector_List* pNewSelectorList = Extend::extendSelectorList(static_cast(pObject->selector()), ctx, subset_map, false, extendedSomething); if (extendedSomething && pNewSelectorList) { - DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->perform(&to_string)) - DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string)) + DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast(pObject->selector())->to_string(ctx.c_options)) + DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->to_string(ctx.c_options)) pNewSelectorList->remove_parent_selectors(); pObject->selector(pNewSelectorList); } else { @@ -2041,6 +2031,25 @@ for (size_t i = 0, L = b->length(); i < L; ++i) { (*b)[i]->perform(this); } + // do final check if everything was extended + // we set `extended` flag on extended selectors + if (b->is_root()) { + // debug_subset_map(subset_map); + for(auto const &it : subset_map.values()) { + Complex_Selector* sel = it.first ? it.first->first() : NULL; + Compound_Selector* ext = it.second ? it.second : NULL; + if (ext && (ext->extended() || ext->is_optional())) continue; + std::string str_sel(sel->to_string({ NESTED, 5 })); + std::string str_ext(ext->to_string({ NESTED, 5 })); + // debug_ast(sel, "sel: "); + // debug_ast(ext, "ext: "); + error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n" + "The selector \"" + str_ext + "\" was not found.\n" + "Use \"@extend " + str_ext + " !optional\" if the" + " extend should be able to fail.", ext->pstate()); + } + } + } void Extend::operator()(Ruleset* pRuleset) diff -Nru libsass-3.3.2/src/extend.hpp libsass-3.3.4/src/extend.hpp --- libsass-3.3.2/src/extend.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/extend.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -25,9 +25,7 @@ static Node subweave(Node& one, Node& two, Context& ctx); static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething); Extend(Context&, ExtensionSubsetMap&); - virtual ~Extend() { } - - using Operation::operator(); + ~Extend() { } void operator()(Block*); void operator()(Ruleset*); diff -Nru libsass-3.3.2/src/file.cpp libsass-3.3.4/src/file.cpp --- libsass-3.3.2/src/file.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/file.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,19 +1,15 @@ #ifdef _WIN32 -#ifdef __MINGW32__ -#ifndef off64_t -#define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */ -#endif -#endif -#include -#define getcwd _getcwd -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +# ifdef __MINGW32__ +# ifndef off64_t +# define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */ +# endif +# endif +# include +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #else -#include -#endif -#ifdef _MSC_VER -#define NOMINMAX +# include #endif - +#include "sass.hpp" #include #include #include @@ -27,15 +23,24 @@ #include "sass2scss.h" #ifdef _WIN32 -#include -#endif +# include -#ifndef FS_CASE_SENSITIVE -#ifdef _WIN32 -#define FS_CASE_SENSITIVE 0 -#else -#define FS_CASE_SENSITIVE 1 -#endif +# ifdef _MSC_VER +# include +inline static std::string wstring_to_string(const std::wstring& wstr) +{ + std::wstring_convert, wchar_t> wchar_converter; + return wchar_converter.to_bytes(wstr); +} +# else // mingw(/gcc) does not support C++11's codecvt yet. +inline static std::string wstring_to_string(const std::wstring &wstr) +{ + int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); + std::string strTo(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); + return strTo; +} +# endif #endif namespace Sass { @@ -46,9 +51,12 @@ std::string get_cwd() { const size_t wd_len = 1024; - char wd[wd_len]; - std::string cwd = getcwd(wd, wd_len); - #ifdef _WIN32 + #ifndef _WIN32 + char wd[wd_len]; + std::string cwd = getcwd(wd, wd_len); + #else + wchar_t wd[wd_len]; + std::string cwd = wstring_to_string(_wgetcwd(wd, wd_len)); //convert backslashes to forward slashes replace(cwd.begin(), cwd.end(), '\\', '/'); #endif @@ -184,8 +192,10 @@ 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); + size_t L = l.length(), pos = find_last_folder_separator(l, L - 2); + bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\'); + bool is_self = pos + 3 == L && (l[pos+1] == '.'); + if (!is_self && !is_slash) r = r.substr(3); l = l.substr(0, pos == std::string::npos ? pos : pos + 1); } diff -Nru libsass-3.3.2/src/functions.cpp libsass-3.3.4/src/functions.cpp --- libsass-3.3.2/src/functions.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/functions.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,10 +1,10 @@ +#include "sass.hpp" #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 "extend.hpp" #include "eval.hpp" @@ -14,6 +14,7 @@ #include "sass/base.h" #include "utf8.h" +#include #include #include #include @@ -155,7 +156,6 @@ template <> Selector_List* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { - To_String to_string(&ctx, false); Expression* exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; @@ -163,13 +163,15 @@ msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), pstate); } - std::string exp_src = exp->perform(&to_string) + "{"; + if (String_Constant* str =dynamic_cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options) + "{"; return Parser::parse_selector(exp_src.c_str(), ctx); } template <> Complex_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { - To_String to_string(&ctx, false); Expression* exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; @@ -177,21 +179,26 @@ msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'"; error(msg.str(), pstate); } - std::string exp_src = exp->perform(&to_string) + "{"; + if (String_Constant* str =dynamic_cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); return (sel_list->length() > 0) ? sel_list->first() : 0; } template <> Compound_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) { - To_String to_string(&ctx, false); Expression* exp = ARG(argname, Expression); if (exp->concrete_type() == Expression::NULL_VAL) { std::stringstream msg; msg << argname << ": null is not a string for `" << function_name(sig) << "'"; error(msg.str(), pstate); } - std::string exp_src = exp->perform(&to_string) + "{"; + if (String_Constant* str =dynamic_cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx); return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0; } @@ -211,9 +218,9 @@ return seed; } #else - static std::random_device rd; uint64_t GetSeed() { + std::random_device rd; return rd(); } #endif @@ -311,9 +318,9 @@ return SASS_MEMORY_NEW(ctx.mem, Color, pstate, - Sass::round(w1*color1->r() + w2*color2->r()), - Sass::round(w1*color1->g() + w2*color2->g()), - Sass::round(w1*color1->b() + w2*color2->b()), + Sass::round(w1*color1->r() + w2*color2->r(), ctx.c_options.precision), + Sass::round(w1*color1->g() + w2*color2->g(), ctx.c_options.precision), + Sass::round(w1*color1->b() + w2*color2->b(), ctx.c_options.precision), color1->a()*p + color2->a()*(1-p)); } @@ -510,8 +517,7 @@ // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$amount"]); if (!amount) { - To_String to_string(&ctx); - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "saturate(" + env["$color"]->perform(&to_string) + ")"); + return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")"); } ARGR("$amount", Number, 0, 100); @@ -571,8 +577,7 @@ // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { - To_String to_string(&ctx); - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "grayscale(" + amount->perform(&to_string) + ")"); + return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")"); } Color* rgb_color = ARG("$color", Color); @@ -608,8 +613,7 @@ // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { - To_String to_string(&ctx); - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->perform(&to_string) + ")"); + return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")"); } Color* rgb_color = ARG("$color", Color); @@ -636,8 +640,7 @@ // CSS3 filter function overload: pass literal through directly Number* amount = dynamic_cast(env["$color"]); if (amount) { - To_String to_string(&ctx); - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->perform(&to_string) + ")"); + return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")"); } return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->a()); @@ -854,10 +857,10 @@ std::stringstream ss; ss << '#' << std::setw(2) << std::setfill('0'); - ss << std::hex << std::setw(2) << static_cast(Sass::round(a)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(r)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(g)); - ss << std::hex << std::setw(2) << static_cast(Sass::round(b)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(a, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(r, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(g, ctx.c_options.precision)); + ss << std::hex << std::setw(2) << static_cast(Sass::round(b, ctx.c_options.precision)); std::string result(ss.str()); for (size_t i = 0, L = result.length(); i < L; ++i) { @@ -884,9 +887,11 @@ return (Expression*) arg; } else { - To_String to_string(&ctx, false, true); - std::string val(arg->perform(&to_string)); + Sass_Output_Style oldstyle = ctx.c_options.output_style; + ctx.c_options.output_style = SASS_STYLE_NESTED; + std::string val(arg->to_string(ctx.c_options)); val = dynamic_cast(arg) ? "null" : val; + ctx.c_options.output_style = oldstyle; deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate); return (Expression*) arg; @@ -896,9 +901,8 @@ Signature quote_sig = "quote($string)"; BUILT_IN(sass_quote) { - To_String to_string(&ctx); AST_Node* arg = env["$string"]; - std::string str(quote(arg->perform(&to_string), String_Constant::double_quote())); + std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); String_Quoted* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str); result->is_delayed(true); return result; @@ -1088,7 +1092,7 @@ Number* n = ARG("$number", Number); Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n); r->pstate(pstate); - r->value(Sass::round(r->value())); + r->value(Sass::round(r->value(), ctx.c_options.precision)); return r; } @@ -1128,10 +1132,13 @@ List* arglist = ARG("$numbers", List); Number* least = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Number* xi = dynamic_cast(arglist->value_at_index(i)); - if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate); + Expression* val = arglist->value_at_index(i); + Number* xi = dynamic_cast(val); + if (!xi) { + error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate); + } if (least) { - if (Eval::lt(xi, least)) least = xi; + if (*xi < *least) least = xi; } else least = xi; } return least; @@ -1143,10 +1150,13 @@ List* arglist = ARG("$numbers", List); Number* greatest = 0; for (size_t i = 0, L = arglist->length(); i < L; ++i) { - Number* xi = dynamic_cast(arglist->value_at_index(i)); - if (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate); + Expression* val = arglist->value_at_index(i); + Number* xi = dynamic_cast(val); + if (!xi) { + error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate); + } if (greatest) { - if (Eval::lt(greatest, xi)) greatest = xi; + if (*greatest < *xi) greatest = xi; } else greatest = xi; } return greatest; @@ -1231,7 +1241,7 @@ double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1); if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); // return (*sl)[static_cast(index)]; - Listize listize(ctx); + Listize listize(ctx.mem); return (*sl)[static_cast(index)]->perform(&listize); } List* l = dynamic_cast(env["$list"]); @@ -1326,7 +1336,7 @@ List* l = dynamic_cast(env["$list"]); Expression* v = ARG("$val", Expression); if (Selector_List* sl = dynamic_cast(env["$list"])) { - Listize listize(ctx); + Listize listize(ctx.mem); l = dynamic_cast(sl->perform(&listize)); } String_Constant* sep = ARG("$separator", String_Constant); @@ -1662,6 +1672,9 @@ // MISCELLANEOUS FUNCTIONS ////////////////////////// + // value.check_deprecated_interp if value.is_a?(Sass::Script::Value::String) + // unquoted_string(value.to_sass) + Signature inspect_sig = "inspect($value)"; BUILT_IN(inspect) { @@ -1673,23 +1686,22 @@ } else if (v->concrete_type() == Expression::STRING) { return v; } else { - bool parentheses = v->concrete_type() == Expression::MAP || - v->concrete_type() == Expression::LIST; + // ToDo: fix to_sass for nested parentheses Sass_Output_Style old_style; - old_style = ctx.c_options->output_style; - ctx.c_options->output_style = SASS_STYLE_NESTED; - To_String to_string(&ctx, false); - std::string inspect = v->perform(&to_string); - if (inspect.empty() && parentheses) inspect = "()"; - ctx.c_options->output_style = old_style; - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect); + old_style = ctx.c_options.output_style; + ctx.c_options.output_style = TO_SASS; + Emitter emitter(ctx.c_options); + Inspect i(emitter); + i.in_declaration = false; + v->perform(&i); + ctx.c_options.output_style = old_style; + return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, i.get_buffer()); } // return v; } Signature selector_nest_sig = "selector-nest($selectors...)"; BUILT_IN(selector_nest) { - To_String to_string(&ctx, false); List* arglist = ARG("$selectors", List); // Not enough parameters @@ -1706,7 +1718,10 @@ msg << "a list of strings, or a list of lists of strings for 'selector-nest'"; error(msg.str(), pstate); } - std::string exp_src = exp->perform(&to_string) + "{"; + if (String_Constant* str =dynamic_cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string(ctx.c_options) + "{"; Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } @@ -1731,14 +1746,13 @@ result->elements(exploded); } - Listize listize(ctx); + Listize listize(ctx.mem); return result->perform(&listize); } Signature selector_append_sig = "selector-append($selectors...)"; BUILT_IN(selector_append) { - To_String to_string; List* arglist = ARG("$selectors", List); // Not enough parameters @@ -1755,7 +1769,10 @@ msg << "a list of strings, or a list of lists of strings for 'selector-append'"; error(msg.str(), pstate); } - std::string exp_src = exp->perform(&to_string) + "{"; + if (String_Constant* str =dynamic_cast(exp)) { + str->quote_mark(0); + } + std::string exp_src = exp->to_string() + "{"; Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx); parsedSelectors.push_back(sel); } @@ -1790,9 +1807,9 @@ // Must be a simple sequence if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) { std::string msg("Can't append `"); - msg += childSeq->perform(&to_string); + msg += childSeq->to_string(); msg += "` to `"; - msg += parentSeqClone->perform(&to_string);; + msg += parentSeqClone->to_string(); msg += "`"; error(msg, pstate, backtrace); } @@ -1801,9 +1818,9 @@ Type_Selector* pType = dynamic_cast(base->head()->first()); if(pType && pType->name() == "*") { std::string msg("Can't append `"); - msg += childSeq->perform(&to_string); + msg += childSeq->to_string(); msg += "` to `"; - msg += parentSeqClone->perform(&to_string);; + msg += parentSeqClone->to_string(); msg += "`"; error(msg, pstate, backtrace); } @@ -1823,7 +1840,7 @@ result->elements(newElements); } - Listize listize(ctx); + Listize listize(ctx.mem); return result->perform(&listize); } @@ -1834,7 +1851,7 @@ Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize); Selector_List* result = selector1->unify_with(selector2, ctx); - Listize listize(ctx); + Listize listize(ctx.mem); return result->perform(&listize); } @@ -1843,12 +1860,11 @@ { Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize); - To_String to_string; List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { Simple_Selector* ss = (*sel)[i]; - std::string ss_string = ss->perform(&to_string) ; + std::string ss_string = ss->to_string() ; *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, ss->pstate(), ss_string); } @@ -1869,7 +1885,7 @@ bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething); - Listize listize(ctx); + Listize listize(ctx.mem); return result->perform(&listize); } @@ -1886,7 +1902,7 @@ bool extendedSomething; Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething); - Listize listize(ctx); + Listize listize(ctx.mem); return result->perform(&listize); } @@ -1895,14 +1911,13 @@ { Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize); - Listize listize(ctx); + Listize listize(ctx.mem); return sel->perform(&listize); } Signature is_superselector_sig = "is-superselector($super, $sub)"; BUILT_IN(is_superselector) { - To_String to_string(&ctx, false); Selector_List* sel_sup = ARGSEL("$super", Selector_List, p_contextualize); Selector_List* sel_sub = ARGSEL("$sub", Selector_List, p_contextualize); bool result = sel_sup->is_superselector_of(sel_sub); diff -Nru libsass-3.3.2/src/functions.hpp libsass-3.3.4/src/functions.hpp --- libsass-3.3.2/src/functions.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/functions.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,6 +1,7 @@ #ifndef SASS_FUNCTIONS_H #define SASS_FUNCTIONS_H +#include "listize.hpp" #include "position.hpp" #include "environment.hpp" #include "sass/functions.h" diff -Nru libsass-3.3.2/src/inspect.cpp libsass-3.3.4/src/inspect.cpp --- libsass-3.3.2/src/inspect.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/inspect.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -26,11 +27,11 @@ add_open_mapping(block); append_scope_opener(); } - if (output_style() == SASS_STYLE_NESTED) indentation += block->tabs(); + if (output_style() == NESTED) indentation += block->tabs(); for (size_t i = 0, L = block->length(); i < L; ++i) { (*block)[i]->perform(this); } - if (output_style() == SASS_STYLE_NESTED) indentation -= block->tabs(); + if (output_style() == NESTED) indentation -= block->tabs(); if (!block->is_root()) { append_scope_closer(); add_close_mapping(block); @@ -123,14 +124,15 @@ if (dec->value()->concrete_type() == Expression::NULL_VAL) return; bool was_decl = in_declaration; in_declaration = true; - if (output_style() == SASS_STYLE_NESTED) + if (output_style() == NESTED) indentation += dec->tabs(); append_indentation(); dec->property()->perform(this); append_colon_separator(); if (dec->value()->concrete_type() == Expression::SELECTOR) { - Listize listize(*ctx); + Memory_Manager mem; + Listize listize(mem); dec->value()->perform(&listize)->perform(this); } else { dec->value()->perform(this); @@ -141,7 +143,7 @@ append_string("!important"); } append_delimiter(); - if (output_style() == SASS_STYLE_NESTED) + if (output_style() == NESTED) indentation -= dec->tabs(); in_declaration = was_decl; } @@ -169,9 +171,11 @@ } import->urls().front()->perform(this); - if (import->media_queries()) { - append_mandatory_space(); - import->media_queries()->perform(this); + if (import->urls().size() == 1) { + if (import->media_queries()) { + append_mandatory_space(); + import->media_queries()->perform(this); + } } append_delimiter(); for (size_t i = 1, S = import->urls().size(); i < S; ++i) { @@ -184,9 +188,11 @@ } import->urls()[i]->perform(this); - if (import->media_queries()) { - append_mandatory_space(); - import->media_queries()->perform(this); + if (import->urls().size() - 1 == i) { + if (import->media_queries()) { + append_mandatory_space(); + import->media_queries()->perform(this); + } } append_delimiter(); } @@ -346,6 +352,10 @@ void Inspect::operator()(Map* map) { + if (output_style() == TO_SASS && map->empty()) { + append_string("()"); + return; + } if (map->empty()) return; if (map->is_invisible()) return; bool items_output = false; @@ -362,15 +372,25 @@ void Inspect::operator()(List* list) { + if (output_style() == TO_SASS && list->empty()) { + append_string("()"); + return; + } std::string sep(list->separator() == SASS_SPACE ? " " : ","); - if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && sep == ",") sep += " "; + if ((output_style() != COMPRESSED) && sep == ",") sep += " "; else if (in_media_block && sep != " ") sep += " "; // verified if (list->empty()) return; bool items_output = false; bool was_space_array = in_space_array; bool was_comma_array = in_comma_array; - if (!in_declaration && ( + // probably ruby sass eqivalent of element_needs_parens + if (output_style() == TO_SASS && list->length() == 1 && + (!dynamic_cast((*list)[0]) && + !dynamic_cast((*list)[0]))) { + append_string("("); + } + else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { @@ -381,9 +401,16 @@ else if (list->separator() == SASS_COMMA) in_comma_array = true; for (size_t i = 0, L = list->size(); i < L; ++i) { + if (list->separator() == SASS_HASH) + { sep[0] = i % 2 ? ':' : ','; } Expression* list_item = (*list)[i]; - if (list_item->is_invisible()) { - continue; + if (output_style() != TO_SASS) { + if (list_item->is_invisible()) { + // this fixes an issue with "" in a list + if (!dynamic_cast(list_item)) { + continue; + } + } } if (items_output) { append_string(sep); @@ -396,7 +423,13 @@ in_comma_array = was_comma_array; in_space_array = was_space_array; - if (!in_declaration && ( + // probably ruby sass eqivalent of element_needs_parens + if (output_style() == TO_SASS && list->length() == 1 && + (!dynamic_cast((*list)[0]) && + !dynamic_cast((*list)[0]))) { + append_string(",)"); + } + else if (!in_declaration && (list->separator() == SASS_HASH || (list->separator() == SASS_SPACE && in_space_array) || (list->separator() == SASS_COMMA && in_comma_array) )) { @@ -408,22 +441,41 @@ void Inspect::operator()(Binary_Expression* expr) { expr->left()->perform(this); + if ( in_media_block || + (output_style() == INSPECT) || ( + expr->op().ws_before + && (!expr->is_interpolant()) + && (!expr->is_delayed() || + expr->is_left_interpolant() || + expr->is_right_interpolant() + ) + + )) append_string(" "); switch (expr->type()) { - case Sass_OP::AND: append_string(" and "); break; - case Sass_OP::OR: append_string(" or "); break; - case Sass_OP::EQ: append_string(" == "); break; - case Sass_OP::NEQ: append_string(" != "); break; - case Sass_OP::GT: append_string(" > "); break; - case Sass_OP::GTE: append_string(" >= "); break; - case Sass_OP::LT: append_string(" < "); break; - case Sass_OP::LTE: append_string(" <= "); break; - case Sass_OP::ADD: append_string(" + "); break; - case Sass_OP::SUB: append_string(" - "); break; - case Sass_OP::MUL: append_string(" * "); break; - case Sass_OP::DIV: append_string(in_media_block ? " / " : "/"); break; - case Sass_OP::MOD: append_string(" % "); break; + case Sass_OP::AND: append_string("&&"); break; + case Sass_OP::OR: append_string("||"); break; + case Sass_OP::EQ: append_string("=="); break; + case Sass_OP::NEQ: append_string("!="); break; + case Sass_OP::GT: append_string(">"); break; + case Sass_OP::GTE: append_string(">="); break; + case Sass_OP::LT: append_string("<"); break; + case Sass_OP::LTE: append_string("<="); break; + case Sass_OP::ADD: append_string("+"); break; + case Sass_OP::SUB: append_string("-"); break; + case Sass_OP::MUL: append_string("*"); break; + case Sass_OP::DIV: append_string("/"); break; + case Sass_OP::MOD: append_string("%"); break; default: break; // shouldn't get here } + if ( in_media_block || + (output_style() == INSPECT) || ( + expr->op().ws_after + && (!expr->is_interpolant()) + && (!expr->is_delayed() + || expr->is_left_interpolant() + || expr->is_right_interpolant() + ) + )) append_string(" "); expr->right()->perform(this); } @@ -458,29 +510,187 @@ void Inspect::operator()(Number* n) { - // use values to_string facility - bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; - std::string res(n->to_string(compressed, (int)ctx->c_options->precision)); + + std::string res; + + // check if the fractional part of the value equals to zero + // neat trick from http://stackoverflow.com/a/1521682/1550314 + // double int_part; bool is_int = modf(value, &int_part) == 0.0; + + // this all cannot be done with one run only, since fixed + // output differs from normal output and regular output + // can contain scientific notation which we do not want! + + // first sample + std::stringstream ss; + ss.precision(12); + ss << n->value(); + + // check if we got scientific notation in result + if (ss.str().find_first_of("e") != std::string::npos) { + ss.clear(); ss.str(std::string()); + ss.precision(std::max(12, opt.precision)); + ss << std::fixed << n->value(); + } + + std::string tmp = ss.str(); + size_t pos_point = tmp.find_first_of(".,"); + size_t pos_fract = tmp.find_last_not_of("0"); + bool is_int = pos_point == pos_fract || + pos_point == std::string::npos; + + // reset stream for another run + ss.clear(); ss.str(std::string()); + + // take a shortcut for integers + if (is_int) + { + ss.precision(0); + ss << std::fixed << n->value(); + res = std::string(ss.str()); + } + // process floats + else + { + // do we have have too much precision? + if (pos_fract < opt.precision + pos_point) + { ss.precision((int)(pos_fract - pos_point)); } + else { ss.precision(opt.precision); } + // round value again + ss << std::fixed << n->value(); + res = std::string(ss.str()); + // maybe we truncated up to decimal point + size_t pos = res.find_last_not_of("0"); + // handle case where we have a "0" + if (pos == std::string::npos) { + res = "0.0"; + } else { + bool at_dec_point = res[pos] == '.' || + res[pos] == ','; + // don't leave a blank point + if (at_dec_point) ++ pos; + res.resize (pos + 1); + } + } + + // some final cosmetics + if (res == "0.0") res = "0"; + else if (res == "") res = "0"; + else if (res == "-0") res = "0"; + else if (res == "-0.0") res = "0"; + else if (opt.output_style == COMPRESSED) + { + // check if handling negative nr + size_t off = res[0] == '-' ? 1 : 0; + // remove leading zero from floating point in compressed mode + if (n->zero() && res[off] == '0' && res[off+1] == '.') res.erase(off, 1); + } + + // add unit now + res += n->unit(); + // output the final token append_token(res, n); } + // helper function for serializing colors + template + static double cap_channel(double c) { + if (c > range) return range; + else if (c < 0) return 0; + else return c; + } + void Inspect::operator()(Color* c) { - // use values to_string facility - bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; - std::string res(c->to_string(compressed, (int)ctx->c_options->precision)); // output the final token - append_token(res, c); + std::stringstream ss; + + // original color name + // maybe an unknown token + std::string name = c->disp(); + + // resolved color + std::string res_name = name; + + double r = Sass::round(cap_channel<0xff>(c->r()), opt.precision); + double g = Sass::round(cap_channel<0xff>(c->g()), opt.precision); + double b = Sass::round(cap_channel<0xff>(c->b()), opt.precision); + double a = cap_channel<1> (c->a()); + + // get color from given name (if one was given at all) + if (name != "" && name_to_color(name)) { + const Color* n = name_to_color(name); + r = Sass::round(cap_channel<0xff>(n->r()), opt.precision); + g = Sass::round(cap_channel<0xff>(n->g()), opt.precision); + b = Sass::round(cap_channel<0xff>(n->b()), opt.precision); + a = cap_channel<1> (n->a()); + } + // otherwise get the possible resolved color name + else { + double numval = r * 0x10000 + g * 0x100 + b; + if (color_to_name(numval)) + res_name = color_to_name(numval); + } + + std::stringstream hexlet; + bool compressed = opt.output_style == COMPRESSED; + hexlet << '#' << std::setw(1) << std::setfill('0'); + // create a short color hexlet if there is any need for it + if (compressed && is_color_doublet(r, g, b) && a == 1) { + hexlet << std::hex << std::setw(1) << (static_cast(r) >> 4); + hexlet << std::hex << std::setw(1) << (static_cast(g) >> 4); + hexlet << std::hex << std::setw(1) << (static_cast(b) >> 4); + } else { + hexlet << std::hex << std::setw(2) << static_cast(r); + hexlet << std::hex << std::setw(2) << static_cast(g); + hexlet << std::hex << std::setw(2) << static_cast(b); + } + + if (compressed && !c->is_delayed()) name = ""; + if (opt.output_style == INSPECT && a >= 1) { + append_token(hexlet.str(), c); + return; + } + + // retain the originally specified color definition if unchanged + if (name != "") { + ss << name; + } + else if (r == 0 && g == 0 && b == 0 && a == 0) { + ss << "transparent"; + } + else if (a >= 1) { + if (res_name != "") { + if (compressed && hexlet.str().size() < res_name.size()) { + ss << hexlet.str(); + } else { + ss << res_name; + } + } + else { + ss << hexlet.str(); + } + } + else { + ss << "rgba("; + ss << static_cast(r) << ","; + if (!compressed) ss << " "; + ss << static_cast(g) << ","; + if (!compressed) ss << " "; + ss << static_cast(b) << ","; + if (!compressed) ss << " "; + ss << a << ')'; + } + + append_token(ss.str(), c); + } void Inspect::operator()(Boolean* b) { - // use values to_string facility - bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED; - std::string res(b->to_string(compressed, (int)ctx->c_options->precision)); // output the final token - append_token(res, b); + append_token(b->value() ? "true" : "false", b); } void Inspect::operator()(String_Schema* ss) @@ -496,25 +706,28 @@ void Inspect::operator()(String_Constant* s) { - // get options from optional? context - int precision = ctx ? (int)ctx->c_options->precision : 5; - bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; - // use values to_string facility - std::string res(s->to_string(compressed, precision)); - // output the final token - append_token(res, s); + append_token(s->value(), s); } void Inspect::operator()(String_Quoted* s) { - // get options from optional? context - int precision = ctx ? (int)ctx->c_options->precision : 5; - bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false; - // use values to_string facility - std::string res(s->to_string(compressed, precision)); - // output the final token - append_token(res, s); + if (const char q = s->quote_mark()) { + append_token(quote(s->value(), q), s); + } else { + append_token(s->value(), s); + } } + + void Inspect::operator()(Custom_Error* e) + { + append_token(e->message(), e); + } + + void Inspect::operator()(Custom_Warning* w) + { + append_token(w->message(), w); + } + void Inspect::operator()(Supports_Operator* so) { @@ -611,11 +824,8 @@ void Inspect::operator()(Null* n) { - // use values to_string facility - bool compressed = output_style() == SASS_STYLE_COMPRESSED; - std::string res(n->to_string(compressed, (int)ctx->c_options->precision)); // output the final token - append_token(res, n); + append_token("null", n); } // parameters and arguments @@ -752,7 +962,7 @@ (*s)[i]->perform(this); } if (s->has_line_break()) { - if (output_style() != SASS_STYLE_COMPACT) { + if (output_style() != COMPACT) { append_optional_linefeed(); } } @@ -774,7 +984,7 @@ if (head && head->length() != 0) head->perform(this); bool is_empty = !head || head->length() == 0 || head->is_empty_reference(); bool is_tail = head && !head->is_empty_reference() && tail; - if (output_style() == SASS_STYLE_COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; + if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0; switch (comb) { case Complex_Selector::ANCESTOR_OF: @@ -810,7 +1020,7 @@ } if (tail) tail->perform(this); if (!tail && c->has_line_break()) { - if (output_style() == SASS_STYLE_COMPACT) { + if (output_style() == COMPACT) { append_mandatory_space(); } } @@ -818,10 +1028,23 @@ void Inspect::operator()(Selector_List* g) { - if (g->empty()) return; + + if (g->empty()) { + if (output_style() == TO_SASS) { + append_token("()", g); + } + return; + } + bool was_comma_array = in_comma_array; - if (!in_declaration && in_comma_array) { + // probably ruby sass eqivalent of element_needs_parens + if (output_style() == TO_SASS && g->length() == 1 && + (!dynamic_cast((*g)[0]) && + !dynamic_cast((*g)[0]))) { + append_string("("); + } + else if (!in_declaration && in_comma_array) { append_string("("); } @@ -830,7 +1053,10 @@ for (size_t i = 0, L = g->length(); i < L; ++i) { if (!in_wrapped && i == 0) append_indentation(); if ((*g)[i] == 0) continue; + schedule_mapping((*g)[i]->last()); + // add_open_mapping((*g)[i]->last()); (*g)[i]->perform(this); + // add_close_mapping((*g)[i]->last()); if (i < L - 1) { scheduled_space = 0; append_comma_separator(); @@ -838,7 +1064,13 @@ } in_comma_array = was_comma_array; - if (!in_declaration && in_comma_array) { + // probably ruby sass eqivalent of element_needs_parens + if (output_style() == TO_SASS && g->length() == 1 && + (!dynamic_cast((*g)[0]) && + !dynamic_cast((*g)[0]))) { + append_string(",)"); + } + else if (!in_declaration && in_comma_array) { append_string(")"); } diff -Nru libsass-3.3.2/src/inspect.hpp libsass-3.3.4/src/inspect.hpp --- libsass-3.3.2/src/inspect.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/inspect.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -64,6 +64,8 @@ virtual void operator()(String_Schema*); virtual void operator()(String_Constant*); virtual void operator()(String_Quoted*); + virtual void operator()(Custom_Error*); + virtual void operator()(Custom_Warning*); virtual void operator()(Supports_Operator*); virtual void operator()(Supports_Negation*); virtual void operator()(Supports_Declaration*); diff -Nru libsass-3.3.2/src/json.cpp libsass-3.3.4/src/json.cpp --- libsass-3.3.2/src/json.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/json.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -544,42 +544,50 @@ static void append_node(JsonNode *parent, JsonNode *child) { - child->parent = parent; - child->prev = parent->children.tail; - child->next = NULL; - - if (parent->children.tail != NULL) - parent->children.tail->next = child; - else - parent->children.head = child; - parent->children.tail = child; + if (child != NULL && parent != NULL) { + child->parent = parent; + child->prev = parent->children.tail; + child->next = NULL; + + if (parent->children.tail != NULL) + parent->children.tail->next = child; + else + parent->children.head = child; + parent->children.tail = child; + } } static void prepend_node(JsonNode *parent, JsonNode *child) { - child->parent = parent; - child->prev = NULL; - child->next = parent->children.head; - - if (parent->children.head != NULL) - parent->children.head->prev = child; - else - parent->children.tail = child; - parent->children.head = child; + if (child != NULL && parent != NULL) { + child->parent = parent; + child->prev = NULL; + child->next = parent->children.head; + + if (parent->children.head != NULL) + parent->children.head->prev = child; + else + parent->children.tail = child; + parent->children.head = child; + } } static void append_member(JsonNode *object, char *key, JsonNode *value) { - value->key = key; - append_node(object, value); + if (value != NULL && object != NULL) { + value->key = key; + append_node(object, value); + } } void json_append_element(JsonNode *array, JsonNode *element) { - assert(array->tag == JSON_ARRAY); - assert(element->parent == NULL); - - append_node(array, element); + if (array != NULL && element !=NULL) { + assert(array->tag == JSON_ARRAY); + assert(element->parent == NULL); + + append_node(array, element); + } } void json_prepend_element(JsonNode *array, JsonNode *element) @@ -592,40 +600,47 @@ void json_append_member(JsonNode *object, const char *key, JsonNode *value) { - assert(object->tag == JSON_OBJECT); - assert(value->parent == NULL); - - append_member(object, json_strdup(key), value); + if (object != NULL && key != NULL && value != NULL) { + assert(object->tag == JSON_OBJECT); + assert(value->parent == NULL); + + append_member(object, json_strdup(key), value); + } } void json_prepend_member(JsonNode *object, const char *key, JsonNode *value) { - assert(object->tag == JSON_OBJECT); - assert(value->parent == NULL); - - value->key = json_strdup(key); - prepend_node(object, value); + if (object != NULL && key != NULL && value != NULL) { + assert(object->tag == JSON_OBJECT); + assert(value->parent == NULL); + + value->key = json_strdup(key); + prepend_node(object, value); + } } void json_remove_from_parent(JsonNode *node) { - JsonNode *parent = node->parent; - - if (parent != NULL) { - if (node->prev != NULL) - node->prev->next = node->next; - else - parent->children.head = node->next; - if (node->next != NULL) - node->next->prev = node->prev; - else - parent->children.tail = node->prev; - - free(node->key); - - node->parent = NULL; - node->prev = node->next = NULL; - node->key = NULL; + if (node != NULL) { + JsonNode *parent = node->parent; + + if (parent != NULL) { + if (node->prev != NULL) + node->prev->next = node->next; + else + parent->children.head = node->next; + + if (node->next != NULL) + node->next->prev = node->prev; + else + parent->children.tail = node->prev; + + free(node->key); + + node->parent = NULL; + node->prev = node->next = NULL; + node->key = NULL; + } } } diff -Nru libsass-3.3.2/src/lexer.cpp libsass-3.3.4/src/lexer.cpp --- libsass-3.3.2/src/lexer.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/lexer.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -136,7 +137,7 @@ const char* any_char(const char* src) { return *src ? src + 1 : src; } // Match word boundary (zero-width lookahead). - const char* word_boundary(const char* src) { return is_character(*src) ? 0 : src; } + const char* word_boundary(const char* src) { return is_character(*src) || *src == '#' ? 0 : src; } // Match linefeed /(?:\n|\r\n?)/ const char* re_linebreak(const char* src) diff -Nru libsass-3.3.2/src/listize.cpp libsass-3.3.4/src/listize.cpp --- libsass-3.3.2/src/listize.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/listize.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,43 +1,43 @@ +#include "sass.hpp" #include #include #include #include "listize.hpp" -#include "to_string.hpp" #include "context.hpp" #include "backtrace.hpp" #include "error_handling.hpp" namespace Sass { - Listize::Listize(Context& ctx) - : ctx(ctx) + Listize::Listize(Memory_Manager& mem) + : mem(mem) { } Expression* Listize::operator()(Selector_List* sel) { - List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA); + List* l = SASS_MEMORY_NEW(mem, List, sel->pstate(), sel->length(), SASS_COMMA); for (size_t i = 0, L = sel->length(); i < L; ++i) { if (!(*sel)[i]) continue; *l << (*sel)[i]->perform(this); } - return l; + if (l->length()) return l; + return SASS_MEMORY_NEW(mem, Null, l->pstate()); } Expression* Listize::operator()(Compound_Selector* sel) { - To_String to_string; std::string str; for (size_t i = 0, L = sel->length(); i < L; ++i) { Expression* e = (*sel)[i]->perform(this); - if (e) str += e->perform(&to_string); + if (e) str += e->to_string(); } - return SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), str); + return SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), str); } Expression* Listize::operator()(Complex_Selector* sel) { - List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), 2); + List* l = SASS_MEMORY_NEW(mem, List, sel->pstate(), 2); Compound_Selector* head = sel->head(); if (head && !head->is_empty_reference()) @@ -46,22 +46,21 @@ if (hh) *l << hh; } - To_String to_string; std::string reference = ! sel->reference() ? "" - : sel->reference()->perform(&to_string); + : sel->reference()->to_string(); switch(sel->combinator()) { case Complex_Selector::PARENT_OF: - *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), ">"); + *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), ">"); break; case Complex_Selector::ADJACENT_TO: - *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "+"); + *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "+"); break; case Complex_Selector::REFERENCE: - *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "/" + reference + "/"); + *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "/" + reference + "/"); break; case Complex_Selector::PRECEDES: - *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "~"); + *l << SASS_MEMORY_NEW(mem, String_Quoted, sel->pstate(), "~"); break; case Complex_Selector::ANCESTOR_OF: break; diff -Nru libsass-3.3.2/src/listize.hpp libsass-3.3.4/src/listize.hpp --- libsass-3.3.2/src/listize.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/listize.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -16,15 +16,13 @@ class Listize : public Operation_CRTP { - Context& ctx; + Memory_Manager& mem; Expression* fallback_impl(AST_Node* n); public: - Listize(Context&); - virtual ~Listize() { } - - using Operation::operator(); + Listize(Memory_Manager&); + ~Listize() { } Expression* operator()(Selector_List*); Expression* operator()(Complex_Selector*); diff -Nru libsass-3.3.2/src/memory_manager.cpp libsass-3.3.4/src/memory_manager.cpp --- libsass-3.3.2/src/memory_manager.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/memory_manager.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "ast.hpp" #include "memory_manager.hpp" diff -Nru libsass-3.3.2/src/node.cpp libsass-3.3.4/src/node.cpp --- libsass-3.3.2/src/node.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/node.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,7 +1,7 @@ +#include "sass.hpp" #include #include "node.hpp" -#include "to_string.hpp" #include "context.hpp" #include "parser.hpp" @@ -153,8 +153,7 @@ } else if (node.isSelector()){ - To_String to_string; - os << node.selector()->head()->perform(&to_string); + os << node.selector()->head()->to_string(); } else if (node.isCollection()) { diff -Nru libsass-3.3.2/src/operation.hpp libsass-3.3.4/src/operation.hpp --- libsass-3.3.2/src/operation.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/operation.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -15,7 +15,7 @@ virtual T operator()(Ruleset* x) = 0; virtual T operator()(Propset* x) = 0; virtual T operator()(Bubble* x) = 0; - virtual T operator()(Supports_Block* x) = 0; + virtual T operator()(Supports_Block* x) = 0; virtual T operator()(Media_Block* x) = 0; virtual T operator()(At_Root_Block* x) = 0; virtual T operator()(At_Rule* x) = 0; @@ -88,84 +88,83 @@ template class Operation_CRTP : public Operation { public: - virtual T operator()(AST_Node* x) { return static_cast(this)->fallback(x); } - virtual ~Operation_CRTP() = 0; + D& impl() { return static_cast(*this); } + public: + T operator()(AST_Node* x) { return static_cast(this)->fallback(x); } // 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()(Bubble* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } - virtual T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } - virtual T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Keyframe_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()(Error* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Debug* 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); } + T operator()(Block* x) { return static_cast(this)->fallback(x); } + T operator()(Ruleset* x) { return static_cast(this)->fallback(x); } + T operator()(Propset* x) { return static_cast(this)->fallback(x); } + T operator()(Bubble* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Block* x) { return static_cast(this)->fallback(x); } + T operator()(Media_Block* x) { return static_cast(this)->fallback(x); } + T operator()(At_Root_Block* x) { return static_cast(this)->fallback(x); } + T operator()(At_Rule* x) { return static_cast(this)->fallback(x); } + T operator()(Keyframe_Rule* x) { return static_cast(this)->fallback(x); } + T operator()(Declaration* x) { return static_cast(this)->fallback(x); } + T operator()(Assignment* x) { return static_cast(this)->fallback(x); } + T operator()(Import* x) { return static_cast(this)->fallback(x); } + T operator()(Import_Stub* x) { return static_cast(this)->fallback(x); } + T operator()(Warning* x) { return static_cast(this)->fallback(x); } + T operator()(Error* x) { return static_cast(this)->fallback(x); } + T operator()(Debug* x) { return static_cast(this)->fallback(x); } + T operator()(Comment* x) { return static_cast(this)->fallback(x); } + T operator()(If* x) { return static_cast(this)->fallback(x); } + T operator()(For* x) { return static_cast(this)->fallback(x); } + T operator()(Each* x) { return static_cast(this)->fallback(x); } + T operator()(While* x) { return static_cast(this)->fallback(x); } + T operator()(Return* x) { return static_cast(this)->fallback(x); } + T operator()(Content* x) { return static_cast(this)->fallback(x); } + T operator()(Extension* x) { return static_cast(this)->fallback(x); } + T operator()(Definition* x) { return static_cast(this)->fallback(x); } + T operator()(Mixin_Call* x) { return static_cast(this)->fallback(x); } // expressions - 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()(Custom_Warning* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Custom_Error* 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()(String_Quoted* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Condition* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Operator* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Negation* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Declaration* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Supports_Interpolation* 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()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Null* x) { return static_cast(this)->fallback(x); } - virtual T operator()(Parent_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(List* x) { return static_cast(this)->fallback(x); } + T operator()(Map* x) { return static_cast(this)->fallback(x); } + T operator()(Binary_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(Unary_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(Function_Call* x) { return static_cast(this)->fallback(x); } + T operator()(Function_Call_Schema* x) { return static_cast(this)->fallback(x); } + T operator()(Custom_Warning* x) { return static_cast(this)->fallback(x); } + T operator()(Custom_Error* x) { return static_cast(this)->fallback(x); } + T operator()(Variable* x) { return static_cast(this)->fallback(x); } + T operator()(Textual* x) { return static_cast(this)->fallback(x); } + T operator()(Number* x) { return static_cast(this)->fallback(x); } + T operator()(Color* x) { return static_cast(this)->fallback(x); } + T operator()(Boolean* x) { return static_cast(this)->fallback(x); } + T operator()(String_Schema* x) { return static_cast(this)->fallback(x); } + T operator()(String_Constant* x) { return static_cast(this)->fallback(x); } + T operator()(String_Quoted* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Condition* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Operator* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Negation* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Declaration* x) { return static_cast(this)->fallback(x); } + T operator()(Supports_Interpolation* x) { return static_cast(this)->fallback(x); } + T operator()(Media_Query* x) { return static_cast(this)->fallback(x); } + T operator()(Media_Query_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(At_Root_Expression* x) { return static_cast(this)->fallback(x); } + T operator()(Null* x) { return static_cast(this)->fallback(x); } + T operator()(Parent_Selector* x) { return static_cast(this)->fallback(x); } // parameters and arguments - 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); } + T operator()(Parameter* x) { return static_cast(this)->fallback(x); } + T operator()(Parameters* x) { return static_cast(this)->fallback(x); } + T operator()(Argument* x) { return static_cast(this)->fallback(x); } + T operator()(Arguments* x) { return static_cast(this)->fallback(x); } // selectors - virtual T operator()(Selector_Schema* 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); } + T operator()(Selector_Schema* x) { return static_cast(this)->fallback(x); } + T operator()(Selector_Placeholder* x) { return static_cast(this)->fallback(x); } + T operator()(Type_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Selector_Qualifier* x) { return static_cast(this)->fallback(x); } + T operator()(Attribute_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Pseudo_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Wrapped_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Compound_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Complex_Selector* x) { return static_cast(this)->fallback(x); } + T operator()(Selector_List* x) { return static_cast(this)->fallback(x); } template T fallback(U x) { return T(); } }; - template - inline Operation_CRTP::~Operation_CRTP() { } } diff -Nru libsass-3.3.2/src/output.cpp libsass-3.3.4/src/output.cpp --- libsass-3.3.2/src/output.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/output.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,11 +1,11 @@ +#include "sass.hpp" #include "ast.hpp" #include "output.hpp" -#include "to_string.hpp" namespace Sass { - Output::Output(Context* ctx) - : Inspect(Emitter(ctx)), + Output::Output(Sass_Output_Options& opt) + : Inspect(Emitter(opt)), charset(""), top_nodes(0) {} @@ -20,16 +20,11 @@ void Output::operator()(Number* n) { // use values to_string facility - To_String to_string(ctx); - std::string res = n->perform(&to_string); + std::string res = n->to_string(opt); // check for a valid unit here // includes result for reporting - if (n->numerator_units().size() > 1 || - n->denominator_units().size() > 0 || - (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != std::string::npos) || - (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != std::string::npos) - ) { - error(res + " isn't a valid CSS value.", n->pstate()); + if (!n->is_valid_css_unit()) { + throw Exception::InvalidValue(*n); } // output the final token append_token(res, n); @@ -42,15 +37,14 @@ void Output::operator()(Map* m) { - To_String to_string(ctx); - std::string dbg(m->perform(&to_string)); + std::string dbg(m->to_string(opt)); error(dbg + " isn't a valid CSS value.", m->pstate()); } OutputBuffer Output::get_buffer(void) { - Emitter emitter(ctx); + Emitter emitter(opt); Inspect inspect(emitter); size_t size_nodes = top_nodes.size(); @@ -60,23 +54,25 @@ } // flush scheduled outputs - inspect.finalize(); + // maybe omit semicolon if possible + inspect.finalize(wbuf.buffer.size() == 0); // prepend buffer on top prepend_output(inspect.output()); // make sure we end with a linefeed - if (!ends_with(wbuf.buffer, ctx->linefeed)) { + if (!ends_with(wbuf.buffer, opt.linefeed)) { // if the output is not completely empty - if (!wbuf.buffer.empty()) append_string(ctx->linefeed); + if (!wbuf.buffer.empty()) append_string(opt.linefeed); } // search for unicode char for(const char& chr : wbuf.buffer) { // skip all ascii chars - if (chr >= 0) continue; + // static cast to unsigned to handle `char` being signed / unsigned + if (static_cast(chr) < 128) continue; // declare the charset - if (output_style() != SASS_STYLE_COMPRESSED) + if (output_style() != COMPRESSED) charset = "@charset \"UTF-8\";" - + ctx->linefeed; + + std::string(opt.linefeed); else charset = "\xEF\xBB\xBF"; // abort search break; @@ -91,11 +87,10 @@ void Output::operator()(Comment* c) { - To_String to_string(ctx); - std::string txt = c->text()->perform(&to_string); + std::string txt = c->text()->to_string(opt); // if (indentation && txt == "/**/") return; bool important = c->is_important(); - if (output_style() != SASS_STYLE_COMPRESSED || important) { + if (output_style() != COMPRESSED || important) { if (buffer().size() == 0) { top_nodes.push_back(c); } else { @@ -131,8 +126,8 @@ if (b->has_non_hoistable()) { decls = true; - if (output_style() == SASS_STYLE_NESTED) indentation += r->tabs(); - if (ctx && ctx->c_options->source_comments) { + if (output_style() == NESTED) indentation += r->tabs(); + if (opt.source_comments) { std::stringstream ss; append_indentation(); ss << "/* line " << r->pstate().line + 1 << ", " << r->pstate().path << " */"; @@ -171,7 +166,7 @@ stm->perform(this); } } - if (output_style() == SASS_STYLE_NESTED) indentation -= r->tabs(); + if (output_style() == NESTED) indentation -= r->tabs(); append_scope_closer(b); } @@ -238,7 +233,7 @@ return; } - if (output_style() == SASS_STYLE_NESTED) indentation += f->tabs(); + if (output_style() == NESTED) indentation += f->tabs(); append_indentation(); append_token("@supports", f); append_mandatory_space(); @@ -274,7 +269,7 @@ } } - if (output_style() == SASS_STYLE_NESTED) indentation -= f->tabs(); + if (output_style() == NESTED) indentation -= f->tabs(); append_scope_closer(); @@ -297,7 +292,7 @@ } return; } - if (output_style() == SASS_STYLE_NESTED) indentation += m->tabs(); + if (output_style() == NESTED) indentation += m->tabs(); append_indentation(); append_token("@media", m); append_mandatory_space(); @@ -311,7 +306,7 @@ if (i < L - 1) append_special_linefeed(); } - if (output_style() == SASS_STYLE_NESTED) indentation -= m->tabs(); + if (output_style() == NESTED) indentation -= m->tabs(); append_scope_closer(); } @@ -332,7 +327,8 @@ } if (v) { append_mandatory_space(); - v->perform(this); + // ruby sass bug? should use options? + append_token(v->to_string(/* opt */), v); } if (!b) { append_delimiter(); @@ -340,7 +336,8 @@ } if (b->is_invisible() || b->length() == 0) { - return append_string(" {}"); + append_optional_space(); + return append_string("{}"); } append_scope_opener(); @@ -380,7 +377,7 @@ void Output::operator()(String_Constant* s) { std::string value(s->value()); - if (s->can_compress_whitespace() && output_style() == SASS_STYLE_COMPRESSED) { + if (s->can_compress_whitespace() && output_style() == COMPRESSED) { value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); } if (!in_comment) { diff -Nru libsass-3.3.2/src/output.hpp libsass-3.3.4/src/output.hpp --- libsass-3.3.2/src/output.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/output.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -23,8 +23,7 @@ using Inspect::operator(); public: - // change to Emitter - Output(Context* ctx); + Output(Sass_Output_Options& opt); virtual ~Output(); protected: diff -Nru libsass-3.3.2/src/parser.cpp libsass-3.3.4/src/parser.cpp --- libsass-3.3.2/src/parser.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/parser.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,10 +1,10 @@ +#include "sass.hpp" #include #include #include #include "parser.hpp" #include "file.hpp" #include "inspect.hpp" -#include "to_string.hpp" #include "constants.hpp" #include "util.hpp" #include "prelexer.hpp" @@ -19,33 +19,33 @@ using namespace Constants; using namespace Prelexer; - Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate) + Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); - p.source = str; - p.position = p.source; - p.end = str + strlen(str); + p.source = source ? source : beg; + p.position = beg ? beg : p.source; + p.end = p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); return p; } - Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate) + Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); - p.source = beg; - p.position = p.source; - p.end = end; + p.source = source ? source : beg; + p.position = beg ? beg : p.source; + p.end = end ? end : p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); return p; } - Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate) + Selector_List* Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source) { - Parser p = Parser::from_c_str(src, ctx, pstate); + Parser p = Parser::from_c_str(beg, ctx, pstate, source); // ToDo: ruby sass errors on parent references // ToDo: remap the source-map entries somehow return p.parse_selector_list(false); @@ -57,12 +57,12 @@ && ! peek_css>(start); } - Parser Parser::from_token(Token t, Context& ctx, ParserState pstate) + Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source) { Parser p(ctx, pstate); - p.source = t.begin; - p.position = p.source; - p.end = t.end; + p.source = source ? source : t.begin; + p.position = t.begin ? t.begin : p.source; + p.end = t.end ? t.end : p.position + strlen(p.position); Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate); p.block_stack.push_back(root); root->is_root(true); @@ -177,11 +177,7 @@ Block* block = block_stack.back(); - while (lex< block_comment >()) { - bool is_important = lexed.begin[2] == '!'; - String* contents = parse_interpolated_chunk(lexed); - (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important); - } + parse_block_comments(); // throw away white-space // includes line comments @@ -203,14 +199,17 @@ else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); } // abort if we are in function context and have nothing parsed yet - else if (stack.back() == function_def) { - error("Functions can only contain variable declarations and control directives", pstate); + else if (stack.back() == Scope::Function) { + error("Functions can only contain variable declarations and control directives.", pstate); } // parse imports to process later else if (lex < kwd_import >(true)) { - if (stack.back() == mixin_def || stack.back() == function_def) { - error("Import directives may not be used within control directives or mixins.", pstate); + Scope parent = stack.empty() ? Scope::Rules : stack.back(); + if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) { + if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20 + error("Import directives may not be used within control directives or mixins.", pstate); + } } Import* imp = parse_import(); // if it is a url, we only add the statement @@ -270,7 +269,9 @@ if (peek< exactly<'{'> >()) { if (decl->is_indented()) ++ indentation; // parse a propset that rides on the declaration's property + stack.push_back(Scope::Properties); (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block()); + stack.pop_back(); if (decl->is_indented()) -- indentation; } } @@ -288,22 +289,17 @@ do { while (lex< block_comment >()); if (lex< quoted_string >()) { - if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp)) - { - // push single file import - // import_single_file(imp, lexed); - to_import.push_back(std::pair(std::string(lexed), 0)); - } + to_import.push_back(std::pair(std::string(lexed), 0)); } else if (lex< uri_prefix >()) { Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate); Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args); + if (lex< quoted_string >()) { Expression* the_url = parse_string(); *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url); } - else if (lex < uri_value >(false)) { // don't skip comments - String* the_url = parse_interpolated_chunk(lexed); + else if (String* the_url = parse_url_function_argument()) { *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url); } else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) { @@ -331,7 +327,7 @@ for(auto location : to_import) { if (location.second) { imp->urls().push_back(location.second); - } else { + } else if (!ctx.call_importers(unquote(location.first), path, pstate, imp)) { ctx.import_url(imp, location.first, path); } } @@ -341,6 +337,15 @@ Definition* Parser::parse_definition(Definition::Type which_type) { + Scope parent = stack.empty() ? Scope::Rules : stack.back(); + if (parent != Scope::Root && parent != Scope::Rules && parent != Scope::Function) { + if (which_type == Definition::FUNCTION) { + error("Functions may not be defined within control directives or other mixins.", pstate); + } else { + error("Mixins may not be defined within control directives or other mixins.", pstate); + } + + } std::string which_str(lexed); if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate); std::string name(Util::normalize_underscores(lexed)); @@ -348,8 +353,8 @@ { error("Invalid function name \"" + name + "\".", pstate); } ParserState source_position_of_def = pstate; Parameters* params = parse_parameters(); - if (which_type == Definition::MIXIN) stack.push_back(mixin_def); - else stack.push_back(function_def); + if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin); + else stack.push_back(Scope::Function); Block* body = parse_block(); stack.pop_back(); Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type); @@ -431,8 +436,11 @@ bool is_keyword = false; Expression* val = parse_space_list(); val->is_delayed(false); + List* l = dynamic_cast(val); if (lex_css< exactly< ellipsis > >()) { - if (val->concrete_type() == Expression::MAP) is_keyword = true; + if (val->concrete_type() == Expression::MAP || ( + (l != NULL && l->separator() == SASS_HASH) + )) is_keyword = true; else is_arglist = true; } arg = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword); @@ -474,7 +482,9 @@ if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root)); else ruleset->selector(parse_selector_schema(lookahead.found)); // then parse the inner block + stack.push_back(Scope::Rules); ruleset->block(parse_block()); + stack.pop_back(); // update for end position ruleset->update_pstate(pstate); // inherit is_root from parent block @@ -501,7 +511,7 @@ // process until end while (i < end_of_selector) { // try to parse mutliple interpolants - if (const char* p = find_first_in_interval< exactly >(i, end_of_selector)) { + if (const char* p = find_first_in_interval< exactly, block_comment >(i, end_of_selector)) { // accumulate the preceding segment if the position has advanced if (i < p) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // check if the interpolation only contains white-space (error out) @@ -514,6 +524,7 @@ Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list(); // set status on the list expression interpolant->is_interpolant(true); + // schema->has_interpolants(true); // add to the string schema (*schema) << interpolant; // advance position @@ -579,7 +590,6 @@ bool reloop = true; bool had_linefeed = false; Complex_Selector* sel = 0; - To_String to_string(&ctx); Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate); group->media_block(last_media_block); @@ -604,7 +614,7 @@ while (peek_css< exactly<','> >()) { lex< css_comments >(false); - // consume everything up and including the comma speparator + // consume everything up and including the comma separator reloop = lex< exactly<','> >() != 0; // remember line break (also between some commas) had_linefeed = had_linefeed || peek_newline(); @@ -676,14 +686,14 @@ sel->tail(parse_complex_selector(true)); if (sel->tail()) { // ToDo: move this logic below into tail setter - if (sel->tail()->has_reference()) sel->has_reference(true); + // if (sel->tail()->has_reference()) sel->has_reference(true); if (sel->tail()->has_placeholder()) sel->has_placeholder(true); } } // add a parent selector if we are not in a root // also skip adding parent ref if we only have refs - if (!sel->has_reference() && !in_at_root && !in_root) { + if (!sel->has_parent_ref() && !in_at_root && !in_root) { // create the objects to wrap parent selector reference Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); parent->media_block(last_media_block); @@ -708,7 +718,7 @@ // EO parse_complex_selector // parse one compound selector, which is basically - // a list of simple selectors (directly adjancent) + // a list of simple selectors (directly adjacent) // lex them exactly (without skipping white-space) Compound_Selector* Parser::parse_compound_selector() { @@ -725,7 +735,7 @@ // remove all block comments (don't skip white-space) lex< delimited_by< slash_star, star_slash, false > >(false); // parse functional - if (peek < re_pseudo_selector >()) + if (match < re_pseudo_selector >()) { (*seq) << parse_simple_selector(); } @@ -735,6 +745,18 @@ // this produces a linefeed!? seq->has_parent_reference(true); (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); + // parent selector only allowed at start + // upcoming Sass may allow also trailing + if (seq->length() > 1) { + ParserState state(pstate); + Simple_Selector* cur = (*seq)[seq->length()-1]; + Simple_Selector* prev = (*seq)[seq->length()-2]; + std::string sel(prev->to_string({ NESTED, 5 })); + std::string found(cur->to_string({ NESTED, 5 })); + if (lex < identifier >()) { found += std::string(lexed); } + error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n" + "\"" + found + "\" may only be used at the beginning of a compound selector.", state); + } } // parse type selector else if (lex< re_type_selector >(false)) @@ -811,7 +833,7 @@ } // a pseudo selector often starts with one or two colons - // it can contain more selectors inside parantheses + // it can contain more selectors inside parentheses Simple_Selector* Parser::parse_pseudo_selector() { if (lex< sequence< optional < pseudo_prefix >, @@ -901,7 +923,8 @@ Block* block = block_stack.back(); while (lex< block_comment >()) { bool is_important = lexed.begin[2] == '!'; - String* contents = parse_interpolated_chunk(lexed); + // flag on second param is to skip loosely over comments + String* contents = parse_interpolated_chunk(lexed, true); (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important); } } @@ -971,7 +994,7 @@ Expression* Parser::parse_map() { Expression* key = parse_list(); - Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1); + List* map = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_HASH); if (String_Quoted* str = dynamic_cast(key)) { if (!str->quote_mark() && !str->is_delayed()) { if (const Color* col = name_to_color(str->value())) { @@ -984,14 +1007,12 @@ } // it's not a map so return the lexed value as a list value - if (!peek< exactly<':'> >()) + if (!lex_css< exactly<':'> >()) { return key; } - lex< exactly<':'> >(); - Expression* value = parse_space_list(); - (*map) << std::make_pair(key, value); + (*map) << key << value; while (lex_css< exactly<','> >()) { @@ -1016,7 +1037,7 @@ Expression* value = parse_space_list(); - (*map) << std::make_pair(key, value); + (*map) << key << value; } ParserState ps = map->pstate(); @@ -1047,6 +1068,7 @@ exactly<'{'>, exactly<')'>, exactly<':'>, + end_of_file, exactly, default_flag, global_flag @@ -1072,6 +1094,7 @@ exactly<'{'>, exactly<')'>, exactly<':'>, + end_of_file, exactly, default_flag, global_flag @@ -1098,6 +1121,7 @@ exactly<')'>, exactly<','>, exactly<':'>, + end_of_file, exactly, default_flag, global_flag @@ -1115,6 +1139,7 @@ exactly<')'>, exactly<','>, exactly<':'>, + end_of_file, exactly, default_flag, global_flag @@ -1140,7 +1165,7 @@ // if it's a singleton, return it directly if (operands.size() == 0) return conj; // fold all operands into one binary expression - return fold_operands(conj, operands, Sass_OP::OR); + return fold_operands(conj, operands, { Sass_OP::OR }); } // EO parse_disjunction @@ -1156,7 +1181,7 @@ // if it's a singleton, return it directly if (operands.size() == 0) return rel; // fold all operands into one binary expression - return fold_operands(rel, operands, Sass_OP::AND); + return fold_operands(rel, operands, { Sass_OP::AND }); } // EO parse_conjunction @@ -1165,30 +1190,38 @@ { // parse the left hand side expression Expression* lhs = parse_expression(); + std::vector operands; + std::vector operators; // if it's a singleton, return it (don't wrap it) - if (!(peek< alternatives < + while (peek< alternatives < kwd_eq, kwd_neq, kwd_gte, kwd_gt, kwd_lte, kwd_lt - > >(position))) - { return lhs; } + > >(position)) + { + // is directly adjancent to expression? + bool left_ws = peek < css_comments >() != NULL; + // parse the operator + enum Sass_OP op + = lex() ? Sass_OP::EQ + : lex() ? Sass_OP::NEQ + : lex() ? Sass_OP::GTE + : lex() ? Sass_OP::LTE + : lex() ? Sass_OP::GT + : lex() ? Sass_OP::LT + // we checked the possibilities on top of fn + : Sass_OP::EQ; + // is directly adjacent to expression? + bool right_ws = peek < css_comments >() != NULL; + operators.push_back({ op, left_ws, right_ws }); + operands.push_back(parse_expression()); + left_ws = peek < css_comments >() != NULL; + } // parse the operator - enum Sass_OP op - = lex() ? Sass_OP::EQ - : lex() ? Sass_OP::NEQ - : lex() ? Sass_OP::GTE - : lex() ? Sass_OP::LTE - : lex() ? Sass_OP::GT - : lex() ? Sass_OP::LT - // we checked the possibilites on top of fn - : Sass_OP::EQ; - // parse the right hand side expression - Expression* rhs = parse_expression(); - // return binary expression with a left and a right hand side - return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs); + return fold_operands(lhs, operands, operators); } // parse_relation @@ -1199,20 +1232,36 @@ // parse addition and subtraction operations Expression* Parser::parse_expression() { + // parses multiple add and subtract operations + // NOTE: make sure that identifiers starting with + // NOTE: dashes do NOT count as subtract operation Expression* lhs = parse_operators(); // if it's a singleton, return it (don't wrap it) - if (!(peek< exactly<'+'> >(position) || + if (!(peek_css< exactly<'+'> >(position) || // condition is a bit misterious, but some combinations should not be counted as operations (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) || (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) || - peek< identifier >(position)) + peek< sequence < zero_plus < exactly <'-' > >, identifier > >(position)) { return lhs; } std::vector operands; - std::vector operators; - while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) { - operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB); + std::vector operators; + bool left_ws = peek < css_comments >() != NULL; + while ( + lex_css< exactly<'+'> >() || + + ( + ! peek_css< sequence < zero_plus < exactly <'-' > >, identifier > >(position) + && lex_css< sequence< negate< digit >, exactly<'-'> > >() + ) + + ) { + + + bool right_ws = peek < css_comments >() != NULL; + operators.push_back({ lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB, left_ws, right_ws }); operands.push_back(parse_operators()); + left_ws = peek < css_comments >() != NULL; } if (operands.size() == 0) return lhs; @@ -1223,25 +1272,21 @@ Expression* Parser::parse_operators() { Expression* factor = parse_factor(); - // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant - if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) { - String_Schema* ss = dynamic_cast(factor); - if (ss && ss->has_interpolants()) return factor; - } // if it's a singleton, return it (don't wrap it) - if (!peek_css< class_char< static_ops > >()) return factor; - // parse more factors and operators std::vector operands; // factors - std::vector operators; // ops + std::vector operators; // ops // lex operations to apply to lhs + const char* left_ws = peek < css_comments >(); while (lex_css< class_char< static_ops > >()) { + const char* right_ws = peek < css_comments >(); switch(*lexed.begin) { - case '*': operators.push_back(Sass_OP::MUL); break; - case '/': operators.push_back(Sass_OP::DIV); break; - case '%': operators.push_back(Sass_OP::MOD); break; + case '*': operators.push_back({ Sass_OP::MUL, left_ws != 0, right_ws != 0 }); break; + case '/': operators.push_back({ Sass_OP::DIV, left_ws != 0, right_ws != 0 }); break; + case '%': operators.push_back({ Sass_OP::MOD, left_ws != 0, right_ws != 0 }); break; default: throw std::runtime_error("unknown static op parsed"); break; } operands.push_back(parse_factor()); + left_ws = peek < css_comments >(); } // operands and operators to binary expression return fold_operands(factor, operands, operators); @@ -1260,18 +1305,20 @@ // lex the expected closing parenthesis if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate); // expression can be evaluated - 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); + // 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() == Sass_OP::DIV) lhs->is_delayed(false); + if (b && b->type() == Sass_OP::DIV) b->set_delayed(false); } return value; } + // string may be interpolated + // if (lex< quoted_string >()) { + // return parse_string(); + // } else if (peek< ie_property >()) { return parse_ie_property(); } @@ -1332,6 +1379,17 @@ if (lex< kwd_important >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); } + // parse `10%4px` into separated items and not a schema + if (lex< sequence < percentage, lookahead < number > > >()) + { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); } + + if (lex< sequence < number, lookahead< sequence < op, number > > > >()) + { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); } + + // string may be interpolated + if (lex< sequence < quoted_string, lookahead < exactly <'-'> > > >()) + { return parse_string(); } + if (const char* stop = peek< value_schema >()) { return parse_value_schema(stop); } @@ -1355,7 +1413,7 @@ if (lex< percentage >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); } - // match hex number first because 0x000 looks like a number followed by an indentifier + // match hex number first because 0x000 looks like a number followed by an identifier if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); } @@ -1363,7 +1421,8 @@ { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); } // also handle the 10em- foo special case - if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >()) + // alternatives < exactly < '.' >, .. > -- `1.5em-.75em` is split into a list, not a binary expression + if (lex< sequence< dimension, optional< sequence< exactly<'-'>, lookahead< alternatives < space > > > > > >()) { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); } if (lex< sequence< static_component, one_plus< strict_identifier > > >()) @@ -1379,7 +1438,7 @@ if (lex< sequence< exactly<'%'>, optional< percentage > > >()) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } - error("error reading values after " + lexed.to_string(), pstate); + css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); // unreachable statement return 0; @@ -1391,7 +1450,9 @@ { const char* i = chunk.begin; // see if there any interpolants - const char* p = find_first_in_interval< exactly >(i, chunk.end); + const char* p = constant ? find_first_in_interval< exactly >(i, chunk.end) : + find_first_in_interval< exactly, block_comment >(i, chunk.end); + if (!p) { String_Quoted* str_quoted = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(i, chunk.end)); if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*'); @@ -1400,8 +1461,10 @@ } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + schema->is_interpolant(true); while (i < chunk.end) { - p = find_first_in_interval< exactly >(i, chunk.end); + p = constant ? find_first_in_interval< exactly >(i, chunk.end) : + find_first_in_interval< exactly, block_comment >(i, chunk.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty @@ -1415,7 +1478,7 @@ const char* j = skip_over_scopes< exactly, exactly >(p + 2, chunk.end); // find the closing brace if (j) { --j; // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j; @@ -1467,14 +1530,14 @@ Token str(lexed); const char* i = str.begin; // see if there any interpolants - const char* p = find_first_in_interval< exactly >(str.begin, str.end); + const char* p = find_first_in_interval< exactly, block_comment >(str.begin, str.end); if (!p) { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end)); } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); while (i < str.end) { - p = find_first_in_interval< exactly >(i, str.end); + p = find_first_in_interval< exactly, block_comment >(i, str.end); if (p) { if (i < p) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty @@ -1485,7 +1548,7 @@ const char* j = skip_over_scopes< exactly, exactly >(p+2, str.end); // find the closing brace if (j) { // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j; @@ -1532,11 +1595,18 @@ } const char* e = 0; + const char* ee = end; + end = stop; size_t num_items = 0; + bool need_space = false; while (position < stop) { // parse space between tokens if (lex< spaces >() && num_items) { - (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); + need_space = true; + } + if (need_space) { + need_space = false; + // (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); } if ((e = peek< re_functional >()) && e < stop) { (*schema) << parse_function_call(); @@ -1547,26 +1617,37 @@ if (peek< exactly< rbrace > >()) { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); } + Expression* ex = 0; if (lex< re_static_expression >()) { - (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); } else { - (*schema) << parse_list(); + ex = parse_list(); } + ex->is_interpolant(true); + (*schema) << ex; // ToDo: no error check here? lex < exactly < rbrace > >(); } - // lex some string constants - else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) { + // lex some string constants or other valid token + // Note: [-+] chars are left over from i.e. `#{3}+3` + else if (lex< alternatives < exactly<'%'>, exactly < '-' >, exactly < '+' > > >()) { (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); - if (*position == '"' || *position == '\'') { - (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); - } } // lex a quoted string else if (lex< quoted_string >()) { - (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"'); - if (*position == '"' || *position == '\'' || alpha(position)) { - (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); + // need_space = true; + // if (schema->length()) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " "); + // else need_space = true; + (*schema) << parse_string(); + if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { + // need_space = true; + } + if (peek < exactly < '-' > >()) break; + } + else if (lex< sequence < identifier > >()) { + (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); + if ((*position == '"' || *position == '\'') || peek < alternatives < alpha > >()) { + // need_space = true; } } // lex (normalized) variable @@ -1598,10 +1679,15 @@ (*schema) << parse_factor(); } else { - return schema; + break; } ++num_items; } + if (position != stop) { + (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(position, stop)); + position = stop; + } + end = ee; return schema; } @@ -1612,14 +1698,14 @@ Token id(lexed); const char* i = id.begin; // see if there any interpolants - const char* p = find_first_in_interval< exactly >(id.begin, id.end); + const char* p = find_first_in_interval< exactly, block_comment >(id.begin, id.end); if (!p) { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end)); } String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); while (i < id.end) { - p = find_first_in_interval< exactly >(i, id.end); + p = find_first_in_interval< exactly, block_comment >(i, id.end); if (p) { if (i < p) { // accumulate the preceding segment if it's nonempty @@ -1635,10 +1721,10 @@ const char* j = skip_over_scopes< exactly, exactly >(p+2, id.end); // find the closing brace if (j) { // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; - schema->has_interpolants(true); + // schema->has_interpolants(true); i = j; } else { @@ -1682,13 +1768,43 @@ String* Parser::parse_url_function_string() { - const char* p = position; + std::string prefix(""); + if (lex< uri_prefix >()) { + prefix = std::string(lexed); + } + + String* url_string = parse_url_function_argument(); + + std::string suffix(""); + if (lex< real_uri_suffix >()) { + suffix = std::string(lexed); + } + + std::string uri(""); + if (url_string) { + uri = url_string->to_string({ NESTED, 5 }); + } + + if (String_Schema* schema = dynamic_cast(url_string)) { + String_Schema* res = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate); + (*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, prefix); + (*res) += schema; + (*res) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, suffix); + return res; + } else { + std::string res = prefix + uri + suffix; + return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res); + } + } - lex< uri_prefix >(); - std::string prefix = lexed; + String* Parser::parse_url_function_argument() + { + const char* p = position; - lex< real_uri_value >(false); - std::string uri = lexed; + std::string uri(""); + if (lex< real_uri_value >(false)) { + uri = lexed.to_string(); + } if (peek< exactly< hash_lbrace > >()) { const char* pp = position; @@ -1696,14 +1812,15 @@ while (peek< exactly< hash_lbrace > >(pp)) { pp = sequence< interpolant, real_uri_value >(pp); } - position = peek< real_uri_suffix >(pp); + position = pp; return parse_interpolated_chunk(Token(p, position)); - } else { - lex< real_uri_suffix >(); - std::string res = prefix + Util::rtrim(uri) + lexed.to_string(); + } + else if (uri != "") { + std::string res = Util::rtrim(uri); return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res); } + return 0; } Function_Call* Parser::parse_function_call() @@ -1727,7 +1844,14 @@ Content* Parser::parse_content_directive() { - if (stack.back() != mixin_def) { + bool missing_mixin_parent = true; + for (auto parent : stack) { + if (parent == Scope::Mixin) { + missing_mixin_parent = false; + break; + } + } + if (missing_mixin_parent) { error("@content may only be used within a mixin", pstate); } return SASS_MEMORY_NEW(ctx.mem, Content, pstate); @@ -1735,6 +1859,7 @@ If* Parser::parse_if_directive(bool else_if) { + stack.push_back(Scope::Control); ParserState if_source_position = pstate; Expression* predicate = parse_list(); predicate->is_delayed(false); @@ -1750,11 +1875,13 @@ else if (lex_css< kwd_else_directive >()) { alternative = parse_block(); } + stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative); } For* Parser::parse_for_directive() { + stack.push_back(Scope::Control); ParserState for_source_position = pstate; lex_variable(); std::string var(Util::normalize_underscores(lexed)); @@ -1768,6 +1895,7 @@ Expression* upper_bound = parse_expression(); upper_bound->is_delayed(false); Block* body = parse_block(); + stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive); } @@ -1778,7 +1906,7 @@ if (!peek< exactly <'$'> >()) { css_error("Invalid CSS", " after ", ": expected \"$\", was "); } - // we expect a simple identfier as the call name + // we expect a simple identifier as the call name if (!lex< sequence < exactly <'$'>, identifier > >()) { lex< exactly <'$'> >(); // move pstate and position up css_error("Invalid CSS", " after ", ": expected identifier, was "); @@ -1789,7 +1917,7 @@ // helper to parse identifier Token Parser::lex_identifier() { - // we expect a simple identfier as the call name + // we expect a simple identifier as the call name if (!lex< identifier >()) { // ToDo: pstate wrong? css_error("Invalid CSS", " after ", ": expected identifier, was "); } @@ -1799,6 +1927,7 @@ Each* Parser::parse_each_directive() { + stack.push_back(Scope::Control); ParserState each_source_position = pstate; std::vector vars; lex_variable(); @@ -1817,12 +1946,14 @@ } } Block* body = parse_block(); + stack.pop_back(); return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body); } // called after parsing `kwd_while_directive` While* Parser::parse_while_directive() { + stack.push_back(Scope::Control); // create the initial while call object While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0); // parse mandatory predicate @@ -1832,12 +1963,15 @@ // parse mandatory block call->block(parse_block()); // return ast node + stack.pop_back(); + // return ast node return call; } // EO parse_while_directive Media_Block* Parser::parse_media_block() { + stack.push_back(Scope::Media); Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0); media_block->media_queries(parse_media_queries()); @@ -1845,7 +1979,7 @@ last_media_block = media_block; media_block->block(parse_css_block()); last_media_block = prev_media_block; - + stack.pop_back(); return media_block; } @@ -1898,10 +2032,10 @@ } feature = parse_expression(); Expression* expression = 0; - if (lex< exactly<':'> >()) { + if (lex_css< exactly<':'> >()) { expression = parse_list(); } - if (!lex< exactly<')'> >()) { + if (!lex_css< exactly<')'> >()) { error("unclosed parenthesis in media query expression", pstate); } return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression); @@ -1970,7 +2104,7 @@ } // TODO: This needs some major work. Although feature conditions - // look like declarations their semantics differ siginificantly + // look like declarations their semantics differ significantly Supports_Condition* Parser::parse_supports_declaration() { Supports_Condition* cond = 0; @@ -2082,16 +2216,37 @@ Warning* Parser::parse_warning() { + if (stack.back() != Scope::Root && + stack.back() != Scope::Function && + stack.back() != Scope::Mixin && + stack.back() != Scope::Control && + stack.back() != Scope::Rules) { + error("Illegal nesting: Only properties may be nested beneath properties.", pstate); + } return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list()); } Error* Parser::parse_error() { + if (stack.back() != Scope::Root && + stack.back() != Scope::Function && + stack.back() != Scope::Mixin && + stack.back() != Scope::Control && + stack.back() != Scope::Rules) { + error("Illegal nesting: Only properties may be nested beneath properties.", pstate); + } return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list()); } Debug* Parser::parse_debug() { + if (stack.back() != Scope::Root && + stack.back() != Scope::Function && + stack.back() != Scope::Mixin && + stack.back() != Scope::Control && + stack.back() != Scope::Rules) { + error("Illegal nesting: Only properties may be nested beneath properties.", pstate); + } return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list()); } @@ -2113,57 +2268,69 @@ rv.error = p; if (const char* q = peek < - one_plus < - alternatives < - // consume whitespace and comments - spaces, block_comment, line_comment, - // match `/deep/` selector (pass-trough) - // there is no functionality for it yet - schema_reference_combinator, - // match selector ops /[*&%,()\[\]]/ - class_char < selector_lookahead_ops >, - // match selector combinators /[>+~]/ - class_char < selector_combinator_ops >, - // match attribute compare operators - alternatives < - exact_match, class_match, dash_match, - prefix_match, suffix_match, substring_match + alternatives < + // partial BEM selector + sequence < + ampersand, + one_plus < + exactly < '-' > >, - // main selector match - sequence < - // allow namespace prefix - optional < namespace_schema >, - // modifiers prefixes + word_boundary + >, + // main selector matching + one_plus < + alternatives < + // consume whitespace and comments + spaces, block_comment, line_comment, + // match `/deep/` selector (pass-trough) + // there is no functionality for it yet + schema_reference_combinator, + // match selector ops /[*&%,()\[\]]/ + class_char < selector_lookahead_ops >, + // match selector combinators /[>+~]/ + class_char < selector_combinator_ops >, + // match attribute compare operators alternatives < - sequence < - exactly <'#'>, - // not for interpolation - negate < exactly <'{'> > - >, - // class match - exactly <'.'>, - // single or double colon - optional < pseudo_prefix > + exact_match, class_match, dash_match, + prefix_match, suffix_match, substring_match >, - // accept hypens in token - one_plus < sequence < - // can start with hyphens - zero_plus < exactly<'-'> >, - // now the main token + // main selector match + sequence < + // allow namespace prefix + optional < namespace_schema >, + // modifiers prefixes alternatives < - kwd_optional, - exactly <'*'>, - quoted_string, - interpolant, - identifier, - percentage, - dimension, - variable, - alnum - > - > >, - // can also end with hyphens - zero_plus < exactly<'-'> > + sequence < + exactly <'#'>, + // not for interpolation + negate < exactly <'{'> > + >, + // class match + exactly <'.'>, + // single or double colon + optional < pseudo_prefix > + >, + // accept hyphens in token + one_plus < sequence < + // can start with hyphens + zero_plus < exactly<'-'> >, + // now the main token + alternatives < + kwd_optional, + exactly <'*'>, + quoted_string, + interpolant, + identifier, + variable, + percentage, + binomial, + dimension, + alnum + > + > >, + // can also end with hyphens + zero_plus < exactly<'-'> > + > > > > @@ -2232,19 +2399,27 @@ non_greedy < alternatives < // consume whitespace - block_comment, spaces, + block_comment, // spaces, // main tokens - interpolant, + sequence < + interpolant, + optional < + quoted_string + > + >, identifier, variable, // issue #442 sequence < parenthese_scope, - interpolant + interpolant, + optional < + quoted_string + > > >, sequence < - optional_spaces, + // optional_spaces, alternatives < exactly<'{'>, exactly<'}'>, @@ -2267,7 +2442,7 @@ // ToDo: remove rv.position = q; // check expected opening bracket - // only after successfull matching + // only after successful matching if (peek < exactly<'{'> >(q)) rv.found = q; else if (peek < exactly<';'> >(q)) rv.found = q; else if (peek < exactly<'}'> >(q)) rv.found = q; @@ -2346,15 +2521,15 @@ } - Expression* Parser::fold_operands(Expression* base, std::vector& operands, enum Sass_OP op) + Expression* Parser::fold_operands(Expression* base, std::vector& operands, Operand op) { for (size_t i = 0, S = operands.size(); i < S; ++i) { base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]); Binary_Expression* b = static_cast(base); - if (op == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { + if (op.operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } - else { + else if (b && b->op().operand != Sass_OP::DIV) { b->left()->is_delayed(false); b->right()->is_delayed(false); } @@ -2362,18 +2537,62 @@ return base; } - Expression* Parser::fold_operands(Expression* base, std::vector& operands, std::vector& ops) + Expression* Parser::fold_operands(Expression* base, std::vector& operands, std::vector& ops, size_t i) { - for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); + + if (String_Schema* schema = dynamic_cast(base)) { + // return schema; + if (schema->has_interpolants()) { + if (i + 1 < operands.size() && ( + (ops[0].operand == Sass_OP::EQ) + || (ops[0].operand == Sass_OP::ADD) + || (ops[0].operand == Sass_OP::DIV) + || (ops[0].operand == Sass_OP::MUL) + || (ops[0].operand == Sass_OP::NEQ) + || (ops[0].operand == Sass_OP::LT) + || (ops[0].operand == Sass_OP::GT) + || (ops[0].operand == Sass_OP::LTE) + || (ops[0].operand == Sass_OP::GTE) + )) { + Expression* rhs = fold_operands(operands[0], operands, ops, 1); + rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[0], schema, rhs); + rhs->set_delayed(false); + rhs->is_delayed(true); + return rhs; + } + // return schema; + } + } + + for (size_t S = operands.size(); i < S; ++i) { + if (String_Schema* schema = dynamic_cast(operands[i])) { + if (schema->has_interpolants()) { + if (i + 1 < S) { + Expression* rhs = fold_operands(operands[i+1], operands, ops, i + 2); + rhs = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], schema, rhs); + base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, rhs); + rhs->is_delayed(true); + base->is_delayed(true); + return base; + } + base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); + if (ops[i].operand != Sass_OP::DIV) base->is_delayed(true); + return base; + } else { + base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); + } + } else { + base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]); + } Binary_Expression* b = static_cast(base); - if (ops[i] == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { + if (b && ops[i].operand == Sass_OP::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); } - else { + else if (b) { b->left()->is_delayed(false); b->right()->is_delayed(false); } + } return base; } @@ -2387,22 +2606,37 @@ void Parser::css_error(const std::string& msg, const std::string& prefix, const std::string& middle) { int max_len = 18; + const char* end = this->end; + while (*end != 0) ++ end; const char* pos = peek < optional_spaces >(); - const char* last_pos(pos - 1); + const char* last_pos(pos); + if (last_pos > source) { + utf8::prior(last_pos, source); + } // backup position to last significant char - while ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos; + while (last_pos > source && last_pos < end) { + if (!Prelexer::is_space(*last_pos)) break; + utf8::prior(last_pos, source); + } bool ellipsis_left = false; - const char* pos_left(last_pos + 1); - const char* end_left(last_pos + 1); + const char* pos_left(last_pos); + const char* end_left(last_pos); + + utf8::next(pos_left, end); + utf8::next(end_left, end); while (pos_left > source) { - if (end_left - pos_left >= max_len) { - ellipsis_left = true; + if (utf8::distance(pos_left, end_left) >= max_len) { + utf8::prior(pos_left, source); + ellipsis_left = *(pos_left) != '\n' && + *(pos_left) != '\r'; + utf8::next(pos_left, end); break; } - const char* prev = pos_left - 1; + const char* prev = pos_left; + utf8::prior(prev, source); if (*prev == '\r') break; if (*prev == '\n') break; pos_left = prev; @@ -2414,21 +2648,24 @@ bool ellipsis_right = false; const char* end_right(pos); const char* pos_right(pos); - while (end_right <= end) { - if (end_right - pos_right > max_len) { - ellipsis_right = true; + while (end_right < end) { + if (utf8::distance(pos_right, end_right) > max_len) { + ellipsis_left = *(pos_right) != '\n' && + *(pos_right) != '\r'; break; } if (*end_right == '\r') break; if (*end_right == '\n') break; - ++ end_right; + utf8::next(end_right, end); } - if (end_right > end) end_right = end; + // if (*end_right == 0) end_right ++; std::string left(pos_left, end_left); std::string right(pos_right, end_right); - if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15); - if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis; + size_t left_subpos = left.size() > 15 ? left.size() - 15 : 0; + size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0; + if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos); + if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis; // now pass new message to the more generic error function error(msg + prefix + quote(left) + middle + quote(right), pstate); } diff -Nru libsass-3.3.2/src/parser.hpp libsass-3.3.4/src/parser.hpp --- libsass-3.3.2/src/parser.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/parser.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -23,11 +23,11 @@ class Parser : public ParserState { public: - enum Syntactic_Context { nothing, mixin_def, function_def }; + enum Scope { Root, Mixin, Function, Media, Control, Properties, Rules }; Context& ctx; std::vector block_stack; - std::vector stack; + std::vector stack; Media_Block* last_media_block; const char* source; const char* position; @@ -44,14 +44,14 @@ Parser(Context& ctx, const ParserState& pstate) : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0), source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0) - { in_at_root = false; stack.push_back(nothing); } + { in_at_root = false; stack.push_back(Scope::Root); } // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]")); - static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]")); - static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]")); - static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]")); + static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); + static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); + static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0); // special static parsers to convert strings into certain selectors - static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]")); + static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0); #ifdef __clang__ @@ -99,6 +99,15 @@ // peek will only skip over space, tabs and line comment // return the position where the lexer match will occur template + const char* match(const char* start = 0) + { + // match the given prelexer + return mx(position); + } + + // peek will only skip over space, tabs and line comment + // return the position where the lexer match will occur + template const char* peek(const char* start = 0) { @@ -107,7 +116,10 @@ const char* it_before_token = sneak < mx >(start); // match the given prelexer - return mx(it_before_token); + const char* match = mx(it_before_token); + + // check if match is in valid range + return match <= end ? match : 0; } @@ -133,6 +145,9 @@ // now call matcher to get position after token const char* it_after_token = mx(it_before_token); + // check if match is in valid range + if (it_after_token > end) return 0; + // maybe we want to update the parser state anyway? if (force == false) { // assertion that we got a valid match @@ -253,6 +268,7 @@ Function_Call* parse_function_call(); Function_Call_Schema* parse_function_call_schema(); String* parse_url_function_string(); + String* parse_url_function_argument(); String* parse_interpolated_chunk(Token, bool constant = false); String* parse_string(); String_Constant* parse_static_expression(); @@ -298,8 +314,8 @@ Lookahead lookahead_for_selector(const char* start = 0); Lookahead lookahead_for_include(const char* start = 0); - Expression* fold_operands(Expression* base, std::vector& operands, Sass_OP op); - Expression* fold_operands(Expression* base, std::vector& operands, std::vector& ops); + Expression* fold_operands(Expression* base, std::vector& operands, Operand op); + Expression* fold_operands(Expression* base, std::vector& operands, std::vector& ops, size_t i = 0); void throw_syntax_error(std::string message, size_t ln = 0); void throw_read_error(std::string message, size_t ln = 0); diff -Nru libsass-3.3.2/src/plugins.cpp libsass-3.3.4/src/plugins.cpp --- libsass-3.3.2/src/plugins.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/plugins.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -7,6 +7,7 @@ #include #endif +#include "sass.hpp" #include #include "output.hpp" #include "plugins.hpp" diff -Nru libsass-3.3.2/src/position.cpp libsass-3.3.4/src/position.cpp --- libsass-3.3.2/src/position.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/position.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "position.hpp" namespace Sass { diff -Nru libsass-3.3.2/src/prelexer.cpp libsass-3.3.4/src/prelexer.cpp --- libsass-3.3.2/src/prelexer.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/prelexer.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -153,8 +154,8 @@ >(src); } - // Match CSS unit identifier. - const char* unit_identifier(const char* src) + // Match a single CSS unit + const char* one_unit(const char* src) { return sequence < optional < exactly <'-'> >, @@ -169,6 +170,34 @@ >(src); } + // Match numerator/denominator CSS units + const char* multiple_units(const char* src) + { + return + sequence < + one_unit, + zero_plus < + sequence < + exactly <'*'>, + one_unit + > + > + >(src); + } + + // Match complex CSS unit identifiers + const char* unit_identifier(const char* src) + { + return sequence < + multiple_units, + optional < + sequence < + exactly <'/'>, + multiple_units + > > + >(src); + } + const char* identifier_alnums(const char* src) { return one_plus< identifier_alnum >(src); @@ -194,7 +223,12 @@ sequence < zero_plus < alternatives < - identifier, + sequence < + optional < + exactly <'$'> + >, + identifier + >, exactly <'-'> > >, @@ -202,9 +236,13 @@ zero_plus < alternatives < digits, - identifier, + sequence < + optional < + exactly <'$'> + >, + identifier + >, quoted_string, - exactly<'+'>, exactly<'-'> > > @@ -278,18 +316,54 @@ >(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, quoted_string > >, - interpolant, - zero_plus< alternatives< identifier, percentage, dimension, hex, number, quoted_string, exactly<'%'> > > > >(src); + const char* sass_value(const char* src) { + return alternatives < + quoted_string, + identifier, + percentage, + hex, + dimension, + number + >(src); } - /* not used anymore - remove? - const char* filename(const char* src) { - return one_plus< alternatives< identifier, number, exactly<'.'> > >(src); + // this is basically `one_plus < sass_value >` + // takes care to not parse invalid combinations + const char* value_combinations(const char* src) { + // `2px-2px` is invalid combo + bool was_number = false; + const char* pos = src; + while (src) { + if ((pos = alternatives < quoted_string, identifier, percentage, hex >(src))) { + was_number = false; + src = pos; + } else if (!was_number && !exactly<'+'>(src) && (pos = alternatives < dimension, number >(src))) { + was_number = true; + src = pos; + } else { + break; + } + } + return src; + } + + // must be at least one interpolant + // can be surrounded by sass values + // make sure to never parse (dim)(dim) + // since this wrongly consumes `2px-1px` + // `2px1px` is valid number (unit `px1px`) + const char* value_schema(const char* src) + { + return sequence < + one_plus < + sequence < + optional < value_combinations >, + interpolant, + optional < value_combinations > + > + > + >(src); } - */ // Match CSS '@' keywords. const char* at_keyword(const char* src) { @@ -539,6 +613,9 @@ } // Match CSS numeric constants. + const char* op(const char* src) { + return class_char(src); + } const char* sign(const char* src) { return class_char(src); } @@ -603,23 +680,19 @@ // Match CSS uri specifiers. const char* uri_prefix(const char* src) { - return exactly(src); - } - const char* uri_value(const char* src) - { - return - sequence < - negate < - exactly < '$' > + return sequence < + exactly < + url_kwd >, zero_plus < - alternatives < - alnum, - interpolant, - exactly <'/'>, - class_char < uri_chars > + sequence < + exactly <'-'>, + one_plus < + alpha + > > - > + >, + exactly <'('> >(src); } @@ -1107,34 +1180,39 @@ return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src); } - template - const char* padded_token(const char* src) - { - size_t got = 0; - const char* pos = src; - while (got < size) { - if (!mx(pos)) break; - ++ pos; ++ got; - } - while (got < size) { - if (!pad(pos)) break; - ++ pos; ++ got; - } - return got ? pos : 0; - } - - template - const char* minmax_range(const char* src) - { - size_t got = 0; - const char* pos = src; - while (got < max) { - if (!mx(pos)) break; - ++ pos; ++ got; - } - if (got < min) return 0; - if (got > max) return 0; - return pos; + // lexer special_fn: these functions cannot be overloaded + // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i) + const char* re_special_fun(const char* src) { + return sequence < + optional < + sequence < + exactly <'-'>, + one_plus < + alternatives < + alpha, + exactly <'+'>, + exactly <'-'> + > + > + > + >, + alternatives < + exactly < calc_fn_kwd >, + exactly < expression_kwd >, + sequence < + sequence < + exactly < progid_kwd >, + exactly <':'> + >, + zero_plus < + alternatives < + char_range <'a', 'z'>, + exactly <'.'> + > + > + > + > + >(src); } } diff -Nru libsass-3.3.2/src/prelexer.hpp libsass-3.3.4/src/prelexer.hpp --- libsass-3.3.2/src/prelexer.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/prelexer.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -200,6 +200,8 @@ const char* strict_identifier_alpha(const char* src); const char* strict_identifier_alnum(const char* src); // Match a CSS unit identifier. + const char* one_unit(const char* src); + const char* multiple_units(const char* src); const char* unit_identifier(const char* src); // const char* strict_identifier_alnums(const char* src); // Match reference selector. @@ -210,6 +212,7 @@ // Match interpolant schemas const char* identifier_schema(const char* src); const char* value_schema(const char* src); + const char* sass_value(const char* src); // const char* filename(const char* src); // const char* filename_schema(const char* src); // const char* url_schema(const char* src); @@ -252,6 +255,7 @@ const char* re_nothing(const char* src); const char* re_type_selector2(const char* src); + const char* re_special_fun(const char* src); const char* kwd_warn(const char* src); const char* kwd_err(const char* src); @@ -282,6 +286,7 @@ // Match placeholder selectors. const char* placeholder(const char* src); // Match CSS numeric constants. + const char* op(const char* src); const char* sign(const char* src); const char* unsigned_number(const char* src); const char* number(const char* src); @@ -296,7 +301,6 @@ // const char* rgb_prefix(const char* src); // Match CSS uri specifiers. const char* uri_prefix(const char* src); - const char* uri_value(const char* src); // Match CSS "!important" keyword. const char* kwd_important(const char* src); // Match CSS "!optional" keyword. @@ -381,6 +385,18 @@ } return 0; } + template + const char* find_first_in_interval(const char* beg, const char* end) { + bool esc = false; + while ((beg < end) && *beg) { + if (esc) esc = false; + else if (*beg == '\\') esc = true; + else if (const char* pos = skip(beg)) beg = pos; + else if (mx(beg)) return beg; + ++beg; + } + return 0; + } template unsigned int count_interval(const char* beg, const char* end) { unsigned int counter = 0; @@ -405,10 +421,42 @@ } template - const char* padded_token(const char* src); + const char* padded_token(const char* src) + { + size_t got = 0; + const char* pos = src; + while (got < size) { + if (!mx(pos)) break; + ++ pos; ++ got; + } + while (got < size) { + if (!pad(pos)) break; + ++ pos; ++ got; + } + return got ? pos : 0; + } template - const char* minmax_range(const char* src); + const char* minmax_range(const char* src) + { + size_t got = 0; + const char* pos = src; + while (got < max) { + if (!mx(pos)) break; + ++ pos; ++ got; + } + if (got < min) return 0; + if (got > max) return 0; + return pos; + } + + template + const char* char_range(const char* src) + { + if (*src < min) return 0; + if (*src > max) return 0; + return src + 1; + } } } diff -Nru libsass-3.3.2/src/remove_placeholders.cpp libsass-3.3.4/src/remove_placeholders.cpp --- libsass-3.3.2/src/remove_placeholders.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/remove_placeholders.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,7 +1,7 @@ +#include "sass.hpp" #include "remove_placeholders.hpp" #include "context.hpp" #include "inspect.hpp" -#include "to_string.hpp" #include namespace Sass { @@ -16,21 +16,47 @@ } } + Selector_List* Remove_Placeholders::remove_placeholders(Selector_List* sl) + { + Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate()); + + for (size_t i = 0, L = sl->length(); i < L; ++i) { + if (!(*sl)[i]->contains_placeholder()) { + *new_sl << (*sl)[i]; + } + } + + return new_sl; + + } + + void Remove_Placeholders::operator()(Ruleset* r) { // Create a new selector group without placeholders Selector_List* sl = static_cast(r->selector()); if (sl) { - Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate()); - - for (size_t i = 0, L = sl->length(); i < L; ++i) { - if (!(*sl)[i]->contains_placeholder()) { - *new_sl << (*sl)[i]; + // Set the new placeholder selector list + r->selector(remove_placeholders(sl)); + // Remove placeholders in wrapped selectors + for (Complex_Selector* cs : *sl) { + while (cs) { + if (cs->head()) { + for (Simple_Selector* ss : *cs->head()) { + if (Wrapped_Selector* ws = dynamic_cast(ss)) { + if (Selector_List* sl = dynamic_cast(ws->selector())) { + Selector_List* clean = remove_placeholders(sl); + // also clean superflous parent selectors + // probably not really the correct place + clean->remove_parent_selectors(); + ws->selector(clean); + } + } } + } + cs = cs->tail(); } - - // Set the new placeholder selector list - r->selector(new_sl); + } } // Iterate into child blocks diff -Nru libsass-3.3.2/src/remove_placeholders.hpp libsass-3.3.4/src/remove_placeholders.hpp --- libsass-3.3.2/src/remove_placeholders.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/remove_placeholders.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -17,11 +17,12 @@ void fallback_impl(AST_Node* n) {} + private: + Selector_List* remove_placeholders(Selector_List*); + public: Remove_Placeholders(Context&); - virtual ~Remove_Placeholders() { } - - using Operation::operator(); + ~Remove_Placeholders() { } void operator()(Block*); void operator()(Ruleset*); diff -Nru libsass-3.3.2/src/sass_context.cpp libsass-3.3.4/src/sass_context.cpp --- libsass-3.3.2/src/sass_context.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_context.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -10,6 +11,7 @@ #include "util.hpp" #include "context.hpp" #include "sass_context.hpp" +#include "sass_functions.hpp" #include "ast_fwd_decl.hpp" #include "error_handling.hpp" @@ -41,11 +43,10 @@ catch (Exception::Base& e) { std::stringstream msg_stream; std::string cwd(Sass::File::get_cwd()); - std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); - std::string msg_prefix("Error: "); + std::string msg_prefix(e.errtype()); bool got_newline = false; - msg_stream << msg_prefix; + msg_stream << msg_prefix << ": "; const char* msg = e.what(); while(msg && *msg) { if (*msg == '\r') { @@ -53,15 +54,26 @@ } else if (*msg == '\n') { got_newline = true; } else if (got_newline) { - msg_stream << std::string(msg_prefix.size(), ' '); + msg_stream << std::string(msg_prefix.size() + 2, ' '); got_newline = false; } msg_stream << *msg; ++ msg; } if (!got_newline) msg_stream << "\n"; - msg_stream << std::string(msg_prefix.size(), ' '); - msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n"; + if (e.import_stack) { + for (size_t i = 1; i < e.import_stack->size() - 1; ++i) { + std::string path((*e.import_stack)[i]->imp_path); + std::string rel_path(Sass::File::abs2rel(path, cwd, cwd)); + msg_stream << std::string(msg_prefix.size() + 2, ' '); + msg_stream << (i == 1 ? " on line " : " from line "); + msg_stream << e.pstate.line+1 << " of " << rel_path << "\n"; + } + } else { + std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); + msg_stream << std::string(msg_prefix.size() + 2, ' '); + msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n"; + } // now create the code trace (ToDo: maybe have util functions?) if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) { @@ -111,6 +123,7 @@ msg_stream << "Unable to allocate memory: " << ba.what() << std::endl; json_append_member(json_err, "status", json_mknumber(2)); json_append_member(json_err, "message", json_mkstring(ba.what())); + json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(ba.what()); @@ -122,9 +135,10 @@ catch (std::exception& e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject(); - msg_stream << "Error: " << e.what() << std::endl; + msg_stream << "Internal Error: " << e.what() << std::endl; json_append_member(json_err, "status", json_mknumber(3)); json_append_member(json_err, "message", json_mkstring(e.what())); + json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e.what()); @@ -136,9 +150,10 @@ catch (std::string& e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject(); - msg_stream << "Error: " << e << std::endl; + msg_stream << "Internal Error: " << e << std::endl; json_append_member(json_err, "status", json_mknumber(4)); json_append_member(json_err, "message", json_mkstring(e.c_str())); + json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e.c_str()); @@ -150,9 +165,10 @@ catch (const char* e) { std::stringstream msg_stream; JsonNode* json_err = json_mkobject(); - msg_stream << "Error: " << e << std::endl; + msg_stream << "Internal Error: " << e << std::endl; json_append_member(json_err, "status", json_mknumber(4)); json_append_member(json_err, "message", json_mkstring(e)); + json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str())); c_ctx->error_json = json_stringify(json_err, " ");; c_ctx->error_message = sass_strdup(msg_stream.str().c_str()); c_ctx->error_text = sass_strdup(e); @@ -204,7 +220,10 @@ size_t imp_size = 0; while (imp) { imp_size ++; imp = imp->next; } // create char* array to hold all paths plus null terminator const char** plugin_paths = (const char**) calloc(imp_size + 1, sizeof(char*)); - if (plugin_paths == 0) throw(std::bad_alloc()); + if (plugin_paths == 0) { + free(include_paths); //free include_paths before throw + throw(std::bad_alloc()); + } // reset iterator imp = c_ctx->plugin_paths; // copy over the paths @@ -398,14 +417,14 @@ struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) { if (data_ctx == 0) return 0; - Context* cpp_ctx = new Data_Context(data_ctx); + Context* cpp_ctx = new Data_Context(*data_ctx); return sass_prepare_context(data_ctx, cpp_ctx); } struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) { if (file_ctx == 0) return 0; - Context* cpp_ctx = new File_Context(file_ctx); + Context* cpp_ctx = new File_Context(*file_ctx); return sass_prepare_context(file_ctx, cpp_ctx); } @@ -416,10 +435,11 @@ return data_ctx->error_status; try { if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); } - if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } + // empty source string is a valid case, even if not really usefull (different than with file context) + // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } } catch (...) { return handle_errors(data_ctx) | 1; } - Context* cpp_ctx = new Data_Context(data_ctx); + Context* cpp_ctx = new Data_Context(*data_ctx); return sass_compile_context(data_ctx, cpp_ctx); } @@ -433,7 +453,7 @@ if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); } } catch (...) { return handle_errors(file_ctx) | 1; } - Context* cpp_ctx = new File_Context(file_ctx); + Context* cpp_ctx = new File_Context(*file_ctx); return sass_compile_context(file_ctx, cpp_ctx); } diff -Nru libsass-3.3.2/src/sass_context.hpp libsass-3.3.4/src/sass_context.hpp --- libsass-3.3.2/src/sass_context.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_context.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -2,36 +2,12 @@ #define SASS_SASS_CONTEXT_H #include "sass.h" +#include "sass.hpp" #include "context.hpp" #include "ast_fwd_decl.hpp" -// Input behaviours -enum Sass_Input_Style { - SASS_CONTEXT_NULL, - SASS_CONTEXT_FILE, - SASS_CONTEXT_DATA, - SASS_CONTEXT_FOLDER -}; - -// simple linked list -struct string_list { - string_list* next; - char* string; -}; - // sass config options structure -struct Sass_Options { - - // Precision for fractional numbers - int precision; - - // Output style for the generated css code - // A value from above SASS_STYLE_* constants - enum Sass_Output_Style output_style; - - // Emit comments in the generated CSS indicating - // the corresponding source line. - bool source_comments; +struct Sass_Options : Sass_Output_Options { // embed sourceMappingUrl as data uri bool source_map_embed; @@ -59,11 +35,6 @@ // information in source-maps etc. char* output_path; - // String to be used for indentation - const char* indent; - // String to be used to for line feeds - const char* linefeed; - // Colon-separated list of paths // Semicolon-separated on Windows // Maybe use array interface instead? diff -Nru libsass-3.3.2/src/sass.cpp libsass-3.3.4/src/sass.cpp --- libsass-3.3.2/src/sass.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include diff -Nru libsass-3.3.2/src/sass_functions.cpp libsass-3.3.4/src/sass_functions.cpp --- libsass-3.3.2/src/sass_functions.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_functions.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include "util.hpp" #include "context.hpp" diff -Nru libsass-3.3.2/src/sass.hpp libsass-3.3.4/src/sass.hpp --- libsass-3.3.2/src/sass.hpp 1970-01-01 00:00:00.000000000 +0000 +++ libsass-3.3.4/src/sass.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -0,0 +1,129 @@ +// must be the first include in all compile units +#ifndef SASS_SASS_H +#define SASS_SASS_H + +// undefine extensions macro to tell sys includes +// that we do not want any macros to be exported +// mainly fixes an issue on SmartOS (SEC macro) +#undef __EXTENSIONS__ + +#ifdef _MSC_VER +#pragma warning(disable : 4005) +#endif + +// aplies to MSVC and MinGW +#ifdef _WIN32 +// we do not want the ERROR macro +# define NOGDI +// we do not want the min/max macro +# define NOMINMAX +// we do not want the IN/OUT macro +# define _NO_W32_PSEUDO_MODIFIERS +#endif + + +// should we be case insensitive +// when dealing with files or paths +#ifndef FS_CASE_SENSITIVE +# ifdef _WIN32 +# define FS_CASE_SENSITIVE 0 +# else +# define FS_CASE_SENSITIVE 1 +# endif +#endif + +// path separation char +#ifndef PATH_SEP +# ifdef _WIN32 +# define PATH_SEP ';' +# else +# define PATH_SEP ':' +# endif +#endif + + +// include C-API header +#include "sass/base.h" + +// output behaviours +namespace Sass { + + // create some C++ aliases for the most used options + const static Sass_Output_Style NESTED = SASS_STYLE_NESTED; + const static Sass_Output_Style COMPACT = SASS_STYLE_COMPACT; + const static Sass_Output_Style EXPANDED = SASS_STYLE_EXPANDED; + const static Sass_Output_Style COMPRESSED = SASS_STYLE_COMPRESSED; + // only used internal to trigger ruby inspect behavior + const static Sass_Output_Style INSPECT = SASS_STYLE_INSPECT; + const static Sass_Output_Style TO_SASS = SASS_STYLE_TO_SASS; + +}; + +// input behaviours +enum Sass_Input_Style { + SASS_CONTEXT_NULL, + SASS_CONTEXT_FILE, + SASS_CONTEXT_DATA, + SASS_CONTEXT_FOLDER +}; + +// simple linked list +struct string_list { + string_list* next; + char* string; +}; + +// sass config options structure +struct Sass_Inspect_Options { + + // Output style for the generated css code + // A value from above SASS_STYLE_* constants + enum Sass_Output_Style output_style; + + // Precision for fractional numbers + int precision; + + // initialization list (constructor with defaults) + Sass_Inspect_Options(Sass_Output_Style style = Sass::NESTED, + int precision = 5) + : output_style(style), precision(precision) + { } + +}; + +// sass config options structure +struct Sass_Output_Options : Sass_Inspect_Options { + + // String to be used for indentation + const char* indent; + // String to be used to for line feeds + const char* linefeed; + + // Emit comments in the generated CSS indicating + // the corresponding source line. + bool source_comments; + + // initialization list (constructor with defaults) + Sass_Output_Options(struct Sass_Inspect_Options opt, + const char* indent = " ", + const char* linefeed = "\n", + bool source_comments = false) + : Sass_Inspect_Options(opt), + indent(indent), linefeed(linefeed), + source_comments(source_comments) + { } + + // initialization list (constructor with defaults) + Sass_Output_Options(Sass_Output_Style style = Sass::NESTED, + int precision = 5, + const char* indent = " ", + const char* linefeed = "\n", + bool source_comments = false) + : Sass_Inspect_Options(style, precision), + indent(indent), linefeed(linefeed), + source_comments(source_comments) + { } + +}; + +#endif diff -Nru libsass-3.3.2/src/sass_interface.cpp libsass-3.3.4/src/sass_interface.cpp --- libsass-3.3.2/src/sass_interface.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_interface.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -69,9 +70,8 @@ else { output_path = c_ctx->output_path; } - Data_Context cpp_ctx( - (Sass_Data_Context*) 0 - ); + struct Sass_Data_Context opt; + Data_Context cpp_ctx(opt); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; while ((this_func_data) && (*this_func_data)) { @@ -145,9 +145,8 @@ else { output_path = c_ctx->output_path; } - File_Context cpp_ctx( - (Sass_File_Context*) 0 - ); + struct Sass_File_Context opt; + File_Context cpp_ctx(opt); if (c_ctx->c_functions) { Sass_Function_List this_func_data = c_ctx->c_functions; while ((this_func_data) && (*this_func_data)) { diff -Nru libsass-3.3.2/src/sass_util.cpp libsass-3.3.4/src/sass_util.cpp --- libsass-3.3.2/src/sass_util.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_util.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,5 +1,5 @@ +#include "sass.hpp" #include "node.hpp" -#include "to_string.hpp" namespace Sass { @@ -38,7 +38,6 @@ end */ Node paths(const Node& arrs, Context& ctx) { - To_String to_string(&ctx); Node loopStart = Node::createCollection(); loopStart.collection()->push_back(Node::createCollection()); diff -Nru libsass-3.3.2/src/sass_util.hpp libsass-3.3.4/src/sass_util.hpp --- libsass-3.3.2/src/sass_util.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_util.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -221,28 +221,28 @@ std::map order; - std::map > grouped; + std::map > grouped; for (typename std::vector::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) { EnumType& e = *enumIter; KeyType key = keyFunc(e); - if (grouped.find(key) == grouped.end()) { + if (grouped.find(key.hash()) == grouped.end()) { order.insert(std::make_pair((unsigned int)order.size(), key)); std::vector newCollection; newCollection.push_back(e); - grouped.insert(std::make_pair(key, newCollection)); + grouped.insert(std::make_pair(key.hash(), newCollection)); } else { - std::vector& collection = grouped.at(key); + std::vector& collection = grouped.at(key.hash()); collection.push_back(e); } } for (unsigned int index = 0; index < order.size(); index++) { KeyType& key = order.at(index); - std::vector& values = grouped.at(key); + std::vector& values = grouped.at(key.hash()); std::pair > grouping = std::make_pair(key, values); diff -Nru libsass-3.3.2/src/sass_values.cpp libsass-3.3.4/src/sass_values.cpp --- libsass-3.3.2/src/sass_values.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/sass_values.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include "util.hpp" @@ -276,7 +277,8 @@ { Memory_Manager mem; Value* val = sass_value_to_ast_node(mem, v); - std::string str(val->to_string(compressed, precision)); + Sass_Inspect_Options options(compressed ? COMPRESSED : NESTED, precision); + std::string str(val->to_string(options)); return sass_make_qstring(str.c_str()); } @@ -290,42 +292,43 @@ Value* lhs = sass_value_to_ast_node(mem, a); Value* rhs = sass_value_to_ast_node(mem, b); + struct Sass_Inspect_Options options(NESTED, 5); // see if it's a relational expression switch(op) { case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs)); case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs)); - case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs) && !Eval::eq(lhs, rhs)); - case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs)); - case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs)); - case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs) || Eval::eq(lhs, rhs)); + case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs, "gt") && !Eval::eq(lhs, rhs)); + case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs, "gte")); + case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs, "lt")); + case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs, "lte") || Eval::eq(lhs, rhs)); default: break; } if (sass_value_is_number(a) && sass_value_is_number(b)) { const Number* l_n = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); - rv = Eval::op_numbers(mem, op, *l_n, *r_n); + rv = Eval::op_numbers(mem, op, *l_n, *r_n, options); } else if (sass_value_is_number(a) && sass_value_is_color(a)) { const Number* l_n = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); - rv = Eval::op_number_color(mem, op, *l_n, *r_c); + rv = Eval::op_number_color(mem, op, *l_n, *r_c, options); } else if (sass_value_is_color(a) && sass_value_is_number(b)) { const Color* l_c = dynamic_cast(lhs); const Number* r_n = dynamic_cast(rhs); - rv = Eval::op_color_number(mem, op, *l_c, *r_n); + rv = Eval::op_color_number(mem, op, *l_c, *r_n, options); } else if (sass_value_is_color(a) && sass_value_is_color(b)) { const Color* l_c = dynamic_cast(lhs); const Color* r_c = dynamic_cast(rhs); - rv = Eval::op_colors(mem, op, *l_c, *r_c); + rv = Eval::op_colors(mem, op, *l_c, *r_c, options); } else /* convert other stuff to string and apply operation */ { Value* l_v = dynamic_cast(lhs); Value* r_v = dynamic_cast(rhs); - rv = Eval::op_strings(mem, op, *l_v, *r_v); + rv = Eval::op_strings(mem, op, *l_v, *r_v, options); } // ToDo: maybe we should should return null value? diff -Nru libsass-3.3.2/src/source_map.cpp libsass-3.3.4/src/source_map.cpp --- libsass-3.3.2/src/source_map.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/source_map.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -16,7 +17,7 @@ std::string SourceMap::render_srcmap(Context &ctx) { - const bool include_sources = ctx.c_options->source_map_contents; + const bool include_sources = ctx.c_options.source_map_contents; const std::vector links = ctx.srcmap_links; const std::vector& sources(ctx.resources); @@ -159,12 +160,12 @@ current_position += offset; } - void SourceMap::add_open_mapping(AST_Node* node) + void SourceMap::add_open_mapping(const AST_Node* node) { mappings.push_back(Mapping(node->pstate(), current_position)); } - void SourceMap::add_close_mapping(AST_Node* node) + void SourceMap::add_close_mapping(const AST_Node* node) { mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position)); } diff -Nru libsass-3.3.2/src/source_map.hpp libsass-3.3.4/src/source_map.hpp --- libsass-3.3.2/src/source_map.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/source_map.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -28,8 +28,8 @@ void prepend(const Offset& offset); void append(const OutputBuffer& out); void prepend(const OutputBuffer& out); - void add_open_mapping(AST_Node* node); - void add_close_mapping(AST_Node* node); + void add_open_mapping(const AST_Node* node); + void add_close_mapping(const AST_Node* node); std::string render_srcmap(Context &ctx); ParserState remap(const ParserState& pstate); diff -Nru libsass-3.3.2/src/subset_map.hpp libsass-3.3.4/src/subset_map.hpp --- libsass-3.3.2/src/subset_map.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/subset_map.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -94,7 +94,6 @@ { 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)); } } diff -Nru libsass-3.3.2/src/to_c.cpp libsass-3.3.4/src/to_c.cpp --- libsass-3.3.2/src/to_c.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_c.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "to_c.hpp" #include "ast.hpp" diff -Nru libsass-3.3.2/src/to_c.hpp libsass-3.3.4/src/to_c.hpp --- libsass-3.3.2/src/to_c.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_c.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -8,15 +8,13 @@ namespace Sass { class To_C : public Operation_CRTP { - // import all the class-specific methods and override as desired - using Operation::operator(); // override this to define a catch-all union Sass_Value* fallback_impl(AST_Node* n); public: To_C() { } - virtual ~To_C() { } + ~To_C() { } union Sass_Value* operator()(Boolean*); union Sass_Value* operator()(Number*); diff -Nru libsass-3.3.2/src/to_string.cpp libsass-3.3.4/src/to_string.cpp --- libsass-3.3.2/src/to_string.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_string.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -#include -#include -#include -#include - -#include "ast.hpp" -#include "inspect.hpp" -#include "context.hpp" -#include "to_string.hpp" - -namespace Sass { - - To_String::To_String(Context* ctx, bool in_declaration, bool in_debug) - : ctx(ctx), in_declaration(in_declaration), in_debug(in_debug) { } - To_String::~To_String() { } - - inline std::string To_String::fallback_impl(AST_Node* n) - { - Emitter emitter(ctx); - Inspect i(emitter); - i.in_declaration = in_declaration; - i.in_debug = in_debug; - if (n) n->perform(&i); - return i.get_buffer(); - } - - inline std::string To_String::operator()(String_Schema* s) - { - std::string acc(""); - for (size_t i = 0, L = s->length(); i < L; ++i) { - acc += s->elements()[i]->perform(this); - } - return acc; - } - - inline std::string To_String::operator()(String_Quoted* s) - { - return s->value(); - } - - inline std::string To_String::operator()(String_Constant* s) - { - return s->value(); - } - - inline std::string To_String::operator()(Null* n) - { return in_debug ? "null" : ""; } -} diff -Nru libsass-3.3.2/src/to_string.hpp libsass-3.3.4/src/to_string.hpp --- libsass-3.3.2/src/to_string.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_string.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -#ifndef SASS_TO_STRING_H -#define SASS_TO_STRING_H - -#include - -#include "operation.hpp" - -namespace Sass { - - class 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 - std::string fallback_impl(AST_Node* n); - - Context* ctx; - bool in_declaration; - bool in_debug; - - public: - - To_String(Context* ctx = 0, bool in_declaration = true, bool in_debug = false); - virtual ~To_String(); - - std::string operator()(Null* n); - std::string operator()(String_Schema*); - std::string operator()(String_Quoted*); - std::string operator()(String_Constant*); - - template - std::string fallback(U n) { return fallback_impl(n); } - }; -} - -#endif diff -Nru libsass-3.3.2/src/to_value.cpp libsass-3.3.4/src/to_value.cpp --- libsass-3.3.2/src/to_value.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_value.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,6 +1,6 @@ +#include "sass.hpp" #include "ast.hpp" #include "to_value.hpp" -#include "to_string.hpp" namespace Sass { @@ -91,19 +91,17 @@ // Selector_List is converted to a string Value* To_Value::operator()(Selector_List* s) { - To_String to_string(&ctx); return SASS_MEMORY_NEW(mem, String_Quoted, s->pstate(), - s->perform(&to_string)); + s->to_string(ctx.c_options)); } // Binary_Expression is converted to a string Value* To_Value::operator()(Binary_Expression* s) { - To_String to_string(&ctx); return SASS_MEMORY_NEW(mem, String_Quoted, s->pstate(), - s->perform(&to_string)); + s->to_string(ctx.c_options)); } }; diff -Nru libsass-3.3.2/src/to_value.hpp libsass-3.3.4/src/to_value.hpp --- libsass-3.3.2/src/to_value.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/to_value.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -21,7 +21,7 @@ To_Value(Context& ctx, Memory_Manager& mem) : ctx(ctx), mem(mem) { } - virtual ~To_Value() { } + ~To_Value() { } using Operation::operator(); Value* operator()(Argument*); diff -Nru libsass-3.3.2/src/units.cpp libsass-3.3.4/src/units.cpp --- libsass-3.3.2/src/units.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/units.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,10 +1,7 @@ +#include "sass.hpp" #include #include "units.hpp" -#ifdef IN -#undef IN -#endif - namespace Sass { /* the conversion matrix can be readed the following way */ @@ -52,88 +49,128 @@ /* dppx */ { 1/96.0, 2.54/96, 1 } }; - SassUnitType get_unit_type(SassUnit unit) + UnitClass get_unit_type(UnitType unit) + { + switch (unit & 0xFF00) + { + case UnitClass::LENGTH: return UnitClass::LENGTH; break; + case UnitClass::ANGLE: return UnitClass::ANGLE; break; + case UnitClass::TIME: return UnitClass::TIME; break; + case UnitClass::FREQUENCY: return UnitClass::FREQUENCY; break; + case UnitClass::RESOLUTION: return UnitClass::RESOLUTION; break; + default: return UnitClass::INCOMMENSURABLE; break; + } + }; + + std::string get_unit_class(UnitType unit) { switch (unit & 0xFF00) { - case SassUnitType::LENGTH: return SassUnitType::LENGTH; break; - case SassUnitType::ANGLE: return SassUnitType::ANGLE; break; - case SassUnitType::TIME: return SassUnitType::TIME; break; - case SassUnitType::FREQUENCY: return SassUnitType::FREQUENCY; break; - case SassUnitType::RESOLUTION: return SassUnitType::RESOLUTION; break; - default: return SassUnitType::INCOMMENSURABLE; break; + case UnitClass::LENGTH: return "LENGTH"; break; + case UnitClass::ANGLE: return "ANGLE"; break; + case UnitClass::TIME: return "TIME"; break; + case UnitClass::FREQUENCY: return "FREQUENCY"; break; + case UnitClass::RESOLUTION: return "RESOLUTION"; break; + default: return "INCOMMENSURABLE"; break; } }; - SassUnit string_to_unit(const std::string& s) + UnitType string_to_unit(const std::string& s) { // size units - if (s == "px") return SassUnit::PX; - else if (s == "pt") return SassUnit::PT; - else if (s == "pc") return SassUnit::PC; - else if (s == "mm") return SassUnit::MM; - else if (s == "cm") return SassUnit::CM; - else if (s == "in") return SassUnit::IN; + if (s == "px") return UnitType::PX; + else if (s == "pt") return UnitType::PT; + else if (s == "pc") return UnitType::PC; + else if (s == "mm") return UnitType::MM; + else if (s == "cm") return UnitType::CM; + else if (s == "in") return UnitType::IN; // angle units - else if (s == "deg") return SassUnit::DEG; - else if (s == "grad") return SassUnit::GRAD; - else if (s == "rad") return SassUnit::RAD; - else if (s == "turn") return SassUnit::TURN; + else if (s == "deg") return UnitType::DEG; + else if (s == "grad") return UnitType::GRAD; + else if (s == "rad") return UnitType::RAD; + else if (s == "turn") return UnitType::TURN; // time units - else if (s == "s") return SassUnit::SEC; - else if (s == "ms") return SassUnit::MSEC; + else if (s == "s") return UnitType::SEC; + else if (s == "ms") return UnitType::MSEC; // frequency units - else if (s == "Hz") return SassUnit::HERTZ; - else if (s == "kHz") return SassUnit::KHERTZ; + else if (s == "Hz") return UnitType::HERTZ; + else if (s == "kHz") return UnitType::KHERTZ; // resolutions units - else if (s == "dpi") return SassUnit::DPI; - else if (s == "dpcm") return SassUnit::DPCM; - else if (s == "dppx") return SassUnit::DPPX; + else if (s == "dpi") return UnitType::DPI; + else if (s == "dpcm") return UnitType::DPCM; + else if (s == "dppx") return UnitType::DPPX; // for unknown units - else return SassUnit::UNKNOWN; + else return UnitType::UNKNOWN; } - const char* unit_to_string(SassUnit unit) + const char* unit_to_string(UnitType unit) { switch (unit) { // size units - case SassUnit::PX: return "px"; break; - case SassUnit::PT: return "pt"; break; - case SassUnit::PC: return "pc"; break; - case SassUnit::MM: return "mm"; break; - case SassUnit::CM: return "cm"; break; - case SassUnit::IN: return "in"; break; + case UnitType::PX: return "px"; break; + case UnitType::PT: return "pt"; break; + case UnitType::PC: return "pc"; break; + case UnitType::MM: return "mm"; break; + case UnitType::CM: return "cm"; break; + case UnitType::IN: return "in"; break; // angle units - case SassUnit::DEG: return "deg"; break; - case SassUnit::GRAD: return "grad"; break; - case SassUnit::RAD: return "rad"; break; - case SassUnit::TURN: return "turn"; break; + case UnitType::DEG: return "deg"; break; + case UnitType::GRAD: return "grad"; break; + case UnitType::RAD: return "rad"; break; + case UnitType::TURN: return "turn"; break; // time units - case SassUnit::SEC: return "s"; break; - case SassUnit::MSEC: return "ms"; break; + case UnitType::SEC: return "s"; break; + case UnitType::MSEC: return "ms"; break; // frequency units - case SassUnit::HERTZ: return "Hz"; break; - case SassUnit::KHERTZ: return "kHz"; break; + case UnitType::HERTZ: return "Hz"; break; + case UnitType::KHERTZ: return "kHz"; break; // resolutions units - case SassUnit::DPI: return "dpi"; break; - case SassUnit::DPCM: return "dpcm"; break; - case SassUnit::DPPX: return "dppx"; break; + case UnitType::DPI: return "dpi"; break; + case UnitType::DPCM: return "dpcm"; break; + case UnitType::DPPX: return "dppx"; break; // for unknown units default: return ""; break; } } + std::string unit_to_class(const std::string& s) + { + if (s == "px") return "LENGTH"; + else if (s == "pt") return "LENGTH"; + else if (s == "pc") return "LENGTH"; + else if (s == "mm") return "LENGTH"; + else if (s == "cm") return "LENGTH"; + else if (s == "in") return "LENGTH"; + // angle units + else if (s == "deg") return "ANGLE"; + else if (s == "grad") return "ANGLE"; + else if (s == "rad") return "ANGLE"; + else if (s == "turn") return "ANGLE"; + // time units + else if (s == "s") return "TIME"; + else if (s == "ms") return "TIME"; + // frequency units + else if (s == "Hz") return "FREQUENCY"; + else if (s == "kHz") return "FREQUENCY"; + // resolutions units + else if (s == "dpi") return "RESOLUTION"; + else if (s == "dpcm") return "RESOLUTION"; + else if (s == "dppx") return "RESOLUTION"; + // for unknown units + return "CUSTOM:" + s; + } + // throws incompatibleUnits exceptions double conversion_factor(const std::string& s1, const std::string& s2, bool strict) { // assert for same units if (s1 == s2) return 1; // get unit enum from string - SassUnit u1 = string_to_unit(s1); - SassUnit u2 = string_to_unit(s2); + UnitType u1 = string_to_unit(s1); + UnitType u2 = string_to_unit(s2); // query unit group types - SassUnitType t1 = get_unit_type(u1); - SassUnitType t2 = get_unit_type(u2); + UnitClass t1 = get_unit_type(u1); + UnitClass t2 = get_unit_type(u2); // get absolute offset // used for array acces size_t i1 = u1 - t1; @@ -144,17 +181,17 @@ // only process known units if (u1 != UNKNOWN && u2 != UNKNOWN) { switch (t1) { - case SassUnitType::LENGTH: return size_conversion_factors[i1][i2]; break; - case SassUnitType::ANGLE: return angle_conversion_factors[i1][i2]; break; - case SassUnitType::TIME: return time_conversion_factors[i1][i2]; break; - case SassUnitType::FREQUENCY: return frequency_conversion_factors[i1][i2]; break; - case SassUnitType::RESOLUTION: return resolution_conversion_factors[i1][i2]; break; + case UnitClass::LENGTH: return size_conversion_factors[i1][i2]; break; + case UnitClass::ANGLE: return angle_conversion_factors[i1][i2]; break; + case UnitClass::TIME: return time_conversion_factors[i1][i2]; break; + case UnitClass::FREQUENCY: return frequency_conversion_factors[i1][i2]; break; + case UnitClass::RESOLUTION: return resolution_conversion_factors[i1][i2]; break; // ToDo: should we throw error here? - case SassUnitType::INCOMMENSURABLE: return 0; break; + case UnitClass::INCOMMENSURABLE: return 0; break; } } // fallback - return 1; + return 0; } } diff -Nru libsass-3.3.2/src/units.hpp libsass-3.3.4/src/units.hpp --- libsass-3.3.2/src/units.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/units.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -7,9 +7,9 @@ namespace Sass { - const double PI = acos(-1); + const double PI = std::acos(-1); - enum SassUnitType { + enum UnitClass { LENGTH = 0x000, ANGLE = 0x100, TIME = 0x200, @@ -18,10 +18,10 @@ INCOMMENSURABLE = 0x500 }; - enum SassUnit { + enum UnitType { // size units - IN = SassUnitType::LENGTH, + IN = UnitClass::LENGTH, CM, PC, MM, @@ -58,9 +58,11 @@ extern const double frequency_conversion_factors[2][2]; extern const double resolution_conversion_factors[3][3]; - enum SassUnit string_to_unit(const std::string&); - const char* unit_to_string(SassUnit unit); - enum SassUnitType get_unit_type(SassUnit unit); + enum Sass::UnitType string_to_unit(const std::string&); + const char* unit_to_string(Sass::UnitType unit); + enum Sass::UnitClass get_unit_type(Sass::UnitType unit); + std::string get_unit_class(Sass::UnitType unit); + std::string unit_to_class(const std::string&); // throws incompatibleUnits exceptions double conversion_factor(const std::string&, const std::string&, bool = true); @@ -68,7 +70,7 @@ { public: const char* msg; - incompatibleUnits(SassUnit a, SassUnit b) + incompatibleUnits(Sass::UnitType a, Sass::UnitType b) : exception() { std::stringstream ss; diff -Nru libsass-3.3.2/src/utf8_string.cpp libsass-3.3.4/src/utf8_string.cpp --- libsass-3.3.2/src/utf8_string.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/utf8_string.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include #include #include @@ -68,6 +69,8 @@ } } + #ifdef _WIN32 + // utf16 functions using std::wstring; @@ -93,5 +96,7 @@ return utf16; } + #endif + } } diff -Nru libsass-3.3.2/src/util.cpp libsass-3.3.4/src/util.cpp --- libsass-3.3.2/src/util.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/util.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "sass.h" #include "ast.hpp" #include "util.hpp" @@ -16,21 +17,15 @@ exit(EXIT_FAILURE); \ } while (0) - double round(double val) + double round(double val, size_t precision) { + // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 + if (fmod(val, 1) - 0.5 > - std::pow(0.1, precision + 1)) return std::ceil(val); + else if (fmod(val, 1) - 0.5 > std::pow(0.1, precision)) return std::floor(val); // work around some compiler issue // cygwin has it not defined in std using namespace std; - - // This was later repatched in 3.4.20 - // which is as yet unreleased. - // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93 - if (fmod(val, 1) - 0.5 > -0.00001) return std::ceil(val); return ::round(val); - - // Use this version once sass-spec is at 3.4.20 - // if (fmod(val, 1) - 0.5 > -0.00001) return ::round(val); - // return value > 0 ? std::ceil(val) : std::floor(val); } /* Sadly, sass_strdup is not portable. */ @@ -104,94 +99,6 @@ return *array = arr; } - std::string string_eval_escapes(const std::string& s) - { - - std::string out(""); - bool esc = false; - for (size_t i = 0, L = s.length(); i < L; ++i) { - if(s[i] == '\\' && esc == false) { - esc = true; - - // escape length - size_t len = 1; - - // parse as many sequence chars as possible - // ToDo: Check if ruby aborts after possible max - while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len; - - // hex string? - if (len > 1) { - - // convert the extracted hex string to code point value - // ToDo: Maybe we could do this without creating a substring - uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16); - - if (cp == 0) cp = 0xFFFD; - - // assert invalid code points - if (cp >= 1) { - - // use a very simple approach to convert via utf8 lib - // maybe there is a more elegant way; maybe we shoud - // convert the whole output from string to a stream!? - // allocate memory for utf8 char and convert to utf8 - unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u); - for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]); - - // skip some more chars? - i += len - 1; esc = false; - if (cp == 10) out += ' '; - - } - - } - - } - else { - out += s[i]; - esc = false; - } - } - return out; - - } - - // double escape every escape sequences - // escape unescaped quotes and backslashes - std::string string_escape(const std::string& str) - { - std::string out(""); - for (auto i : str) { - // escape some characters - if (i == '"') out += '\\'; - if (i == '\'') out += '\\'; - if (i == '\\') out += '\\'; - out += i; - } - return out; - } - - // unescape every escape sequence - // only removes unescaped backslashes - std::string string_unescape(const std::string& str) - { - std::string out(""); - bool esc = false; - for (auto i : str) { - if (esc || i != '\\') { - esc = false; - out += i; - } else { - esc = true; - } - } - // open escape sequence at end - // maybe it should thow an error - if (esc) { out += '\\'; } - return out; - } - // read css string (handle multiline DELIM) std::string read_css_string(const std::string& str) { @@ -215,28 +122,6 @@ return out; } - // evacuate unescaped quoted - // leave everything else untouched - std::string evacuate_quotes(const std::string& str) - { - std::string out(""); - bool esc = false; - for (auto i : str) { - if (!esc) { - // ignore next character - if (i == '\\') esc = true; - // evacuate unescaped quotes - else if (i == '"') out += '\\'; - else if (i == '\'') out += '\\'; - } - // get escaped char now - else { esc = false; } - // remove nothing - out += i; - } - return out; - } - // double escape all escape sequences // keep unescaped quotes and backslashes std::string evacuate_escapes(const std::string& str) @@ -321,38 +206,6 @@ else return text; } - std::string normalize_wspace(const std::string& str) - { - bool ws = false; - bool esc = false; - std::string text = ""; - for(const char& i : str) { - if (!esc && i == '\\') { - esc = true; - ws = false; - text += i; - } else if (esc) { - esc = false; - ws = false; - text += i; - } else if ( - i == ' ' || - i == '\r' || - i == '\n' || - i == ' ' - ) { - // only add one space - if (!ws) text += ' '; - ws = true; - } else { - ws = false; - text += i; - } - } - if (esc) text += '\\'; - return text; - } - // find best quote_mark by detecting if the string contains any single // or double quotes. When a single quote is found, we not we want a double // quote as quote_mark. Otherwise we check if the string cotains any double @@ -462,7 +315,7 @@ } - std::string quote(const std::string& s, char q, bool keep_linefeed_whitespace) + std::string quote(const std::string& s, char q) { // autodetect with fallback to given quote @@ -488,13 +341,27 @@ int cp = utf8::next(it, end); + // in case of \r, check if the next in sequence + // is \n and then advance the iterator. + if (cp == '\r' && it < end && utf8::peek_next(it, end) == '\n') { + cp = utf8::next(it, end); + } + if (cp == '\n') { quoted.push_back('\\'); quoted.push_back('a'); // we hope we can remove this flag once we figure out // why ruby sass has these different output behaviors - if (keep_linefeed_whitespace) + // gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ") + using namespace Prelexer; + if (alternatives < + Prelexer::char_range<'a', 'f'>, + Prelexer::char_range<'A', 'F'>, + Prelexer::char_range<'0', '9'>, + space + >(it) != NULL) { quoted.push_back(' '); + } } else if (cp < 127) { quoted.push_back((char) cp); } else { @@ -615,7 +482,7 @@ } } else if (Comment* c = dynamic_cast(stm)) { // keep for uncompressed - if (style != SASS_STYLE_COMPRESSED) { + if (style != COMPRESSED) { hasDeclarations = true; } // output style compressed @@ -720,7 +587,7 @@ else if (typeid(*stm) == typeid(Comment)) { Comment* c = (Comment*) stm; // keep for uncompressed - if (style != SASS_STYLE_COMPRESSED) { + if (style != COMPRESSED) { return true; } // output style compressed diff -Nru libsass-3.3.2/src/util.hpp libsass-3.3.4/src/util.hpp --- libsass-3.3.2/src/util.hpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/util.hpp 2016-03-17 14:51:35.000000000 +0000 @@ -4,6 +4,7 @@ #include #include #include +#include "sass.hpp" #include "sass/base.h" #include "ast_fwd_decl.hpp" @@ -11,23 +12,18 @@ namespace Sass { - double round(double val); + double round(double val, size_t precision = 0); char* sass_strdup(const char* str); double sass_atof(const char* str); const char* safe_str(const char *, const char* = ""); void free_string_array(char **); char **copy_strings(const std::vector&, char ***, int = 0); - std::string string_escape(const std::string& str); - std::string string_unescape(const std::string& str); - std::string string_eval_escapes(const std::string& str); std::string read_css_string(const std::string& str); - std::string evacuate_quotes(const std::string& str); std::string evacuate_escapes(const std::string& str); std::string string_to_output(const std::string& str); std::string comment_to_string(const std::string& text); - std::string normalize_wspace(const std::string& str); - std::string quote(const std::string&, char q = 0, bool keep_linefeed_whitespace = false); + std::string quote(const std::string&, char q = 0); std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false); char detect_best_quotemark(const char* s, char qm = '"'); @@ -47,13 +43,13 @@ std::string vecJoin(const std::vector& vec, const std::string& sep); bool containsAnyPrintableStatements(Block* b); - bool isPrintable(Ruleset* r, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(Supports_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(Media_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(Block* b, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(String_Constant* s, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(String_Quoted* s, Sass_Output_Style style = SASS_STYLE_NESTED); - bool isPrintable(Declaration* d, Sass_Output_Style style = SASS_STYLE_NESTED); + bool isPrintable(Ruleset* r, Sass_Output_Style style = NESTED); + bool isPrintable(Supports_Block* r, Sass_Output_Style style = NESTED); + bool isPrintable(Media_Block* r, Sass_Output_Style style = NESTED); + bool isPrintable(Block* b, Sass_Output_Style style = NESTED); + bool isPrintable(String_Constant* s, Sass_Output_Style style = NESTED); + bool isPrintable(String_Quoted* s, Sass_Output_Style style = NESTED); + bool isPrintable(Declaration* d, Sass_Output_Style style = NESTED); bool isAscii(const char chr); } diff -Nru libsass-3.3.2/src/values.cpp libsass-3.3.4/src/values.cpp --- libsass-3.3.2/src/values.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/src/values.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,3 +1,4 @@ +#include "sass.hpp" #include "sass.h" #include "values.hpp" diff -Nru libsass-3.3.2/test/test_node.cpp libsass-3.3.4/test/test_node.cpp --- libsass-3.3.2/test/test_node.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/test/test_node.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -2,7 +2,6 @@ #include #include "node.hpp" -#include "to_string.hpp" #include "parser.hpp" @@ -13,9 +12,6 @@ Context ctx = Context::Data(); - To_String to_string; - - const char* const ROUNDTRIP_TESTS[] = { NULL, "~", @@ -47,7 +43,7 @@ pOrigSelector = createComplexSelector(toTest); } - std::string expected(pOrigSelector ? pOrigSelector->perform(&to_string) : "NULL"); + std::string expected(pOrigSelector ? pOrigSelector->to_string() : "NULL"); // Roundtrip the selector into a node and back @@ -63,7 +59,7 @@ // Show the result - std::string result(pNewSelector ? pNewSelector->perform(&to_string) : "NULL"); + std::string result(pNewSelector ? pNewSelector->to_string() : "NULL"); cout << "SELECTOR: " << expected << endl; cout << "NEW SELECTOR: " << result << endl; diff -Nru libsass-3.3.2/test/test_selector_difference.cpp libsass-3.3.4/test/test_selector_difference.cpp --- libsass-3.3.2/test/test_selector_difference.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/test/test_selector_difference.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,21 +1,19 @@ #include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" -#include "../to_string.hpp" #include #include using namespace Sass; Context ctx = Context::Data(); -To_String to_string; Compound_Selector* selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } void diff(std::string s, std::string t) { - std::cout << s << " - " << t << " = " << selector(s + ";")->minus(selector(t + ";"), ctx)->perform(&to_string) << std::endl; + std::cout << s << " - " << t << " = " << selector(s + ";")->minus(selector(t + ";"), ctx)->to_string() << std::endl; } int main() diff -Nru libsass-3.3.2/test/test_specificity.cpp libsass-3.3.4/test/test_specificity.cpp --- libsass-3.3.2/test/test_specificity.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/test/test_specificity.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,14 +1,12 @@ #include "../ast.hpp" #include "../context.hpp" #include "../parser.hpp" -#include "../to_string.hpp" #include #include using namespace Sass; Context ctx = Context::Data(); -To_String to_string; Selector* selector(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_selector_list(); } diff -Nru libsass-3.3.2/test/test_superselector.cpp libsass-3.3.4/test/test_superselector.cpp --- libsass-3.3.2/test/test_superselector.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/test/test_superselector.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,13 +1,11 @@ #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(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } diff -Nru libsass-3.3.2/test/test_unification.cpp libsass-3.3.4/test/test_unification.cpp --- libsass-3.3.2/test/test_unification.cpp 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/test/test_unification.cpp 2016-03-17 14:51:35.000000000 +0000 @@ -1,13 +1,11 @@ #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(std::string src) { return Parser::from_c_str(src.c_str(), ctx, "", Position()).parse_compound_selector(); } @@ -15,7 +13,7 @@ void unify(std::string lhs, std::string rhs) { Compound_Selector* unified = selector(lhs + ";")->unify_with(selector(rhs + ";"), ctx); - std::cout << lhs << " UNIFIED WITH " << rhs << " =\t" << (unified ? unified->perform(&to_string) : "NOTHING") << std::endl; + std::cout << lhs << " UNIFIED WITH " << rhs << " =\t" << (unified ? unified->to_string() : "NOTHING") << std::endl; } int main() diff -Nru libsass-3.3.2/win/libsass.targets libsass-3.3.4/win/libsass.targets --- libsass-3.3.2/win/libsass.targets 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/win/libsass.targets 2016-03-17 14:51:35.000000000 +0000 @@ -1,5 +1,16 @@ - + + + + + + + + + + + + @@ -7,6 +18,7 @@ + @@ -36,11 +48,14 @@ + + + + - @@ -51,8 +66,7 @@ - - + @@ -91,7 +105,6 @@ - diff -Nru libsass-3.3.2/win/libsass.vcxproj libsass-3.3.4/win/libsass.vcxproj --- libsass-3.3.2/win/libsass.vcxproj 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/win/libsass.vcxproj 2016-03-17 14:51:35.000000000 +0000 @@ -4,16 +4,17 @@ [NA] ..\src ..\src + ..\include - + - %(PreprocessorDefinitions);LIBSASS_VERSION="$(LIBSASS_VERSION)" + %(PreprocessorDefinitions);LIBSASS_VERSION="$(LIBSASS_VERSION)"; @@ -54,6 +55,7 @@ DynamicLibrary + ADD_EXPORTS;$(PreprocessorDefinitions); StaticLibrary @@ -125,7 +127,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console @@ -138,7 +140,7 @@ Level3 Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console @@ -153,7 +155,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console @@ -170,7 +172,7 @@ MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;_LIB;$(PreprocessorDefinitions); Console @@ -179,7 +181,7 @@ true - + diff -Nru libsass-3.3.2/win/libsass.vcxproj.filters libsass-3.3.4/win/libsass.vcxproj.filters --- libsass-3.3.2/win/libsass.vcxproj.filters 2015-11-11 08:08:38.000000000 +0000 +++ libsass-3.3.4/win/libsass.vcxproj.filters 2016-03-17 14:51:35.000000000 +0000 @@ -1,303 +1,345 @@  - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - + {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {bb9c270d-e9f5-49bf-afda-771a1a4bb5b7} + h;hh;hpp;hxx;hm;in;inl;inc;xsd + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + + Include Headers + + - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files - - - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers + + + Headers + + + Headers - Header Files + Headers + + + Headers - Header Files + Headers - Header Files + Headers - Header Files - - - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers - Header Files + Headers + + + Headers + + + Headers - Source Files + Sources - Source Files + Sources + Sources + + Source Files - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files - - - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources - Source Files + Sources