hlibsass (empty) → 0.1.0.0
raw patch · 114 files changed
+29434/−0 lines, 114 filesdep +basedep +hlibsassdep +hspecbuild-type:Customsetup-changed
Dependencies added: base, hlibsass, hspec
Files
- Binding/Libsass.hs +19/−0
- Binding/Libsass/Context.hs +358/−0
- Binding/Libsass/Functions.hs +156/−0
- Binding/Libsass/Types.hs +127/−0
- Binding/Libsass/Values.hs +234/−0
- Binding/Libsass/Wrappers.hs +34/−0
- LICENSE +20/−0
- Setup.hs +55/−0
- hlibsass.cabal +57/−0
- libsass/LICENSE +25/−0
- libsass/Makefile +242/−0
- libsass/ast.cpp +591/−0
- libsass/ast.hpp +2168/−0
- libsass/ast_def_macros.hpp +21/−0
- libsass/ast_factory.hpp +92/−0
- libsass/ast_fwd_decl.hpp +85/−0
- libsass/b64/cencode.h +32/−0
- libsass/b64/encode.h +77/−0
- libsass/backtrace.hpp +77/−0
- libsass/base64vlq.cpp +43/−0
- libsass/base64vlq.hpp +31/−0
- libsass/bind.cpp +186/−0
- libsass/bind.hpp +18/−0
- libsass/cencode.c +102/−0
- libsass/color_names.hpp +327/−0
- libsass/constants.cpp +166/−0
- libsass/constants.hpp +170/−0
- libsass/context.cpp +560/−0
- libsass/context.hpp +157/−0
- libsass/contextualize.cpp +141/−0
- libsass/contextualize.hpp +46/−0
- libsass/contextualize_eval.cpp +93/−0
- libsass/contextualize_eval.hpp +44/−0
- libsass/cssize.cpp +549/−0
- libsass/cssize.hpp +82/−0
- libsass/debug.hpp +39/−0
- libsass/debugger.hpp +391/−0
- libsass/emitter.cpp +255/−0
- libsass/emitter.hpp +83/−0
- libsass/environment.hpp +167/−0
- libsass/error_handling.cpp +25/−0
- libsass/error_handling.hpp +29/−0
- libsass/eval.cpp +1288/−0
- libsass/eval.hpp +88/−0
- libsass/expand.cpp +538/−0
- libsass/expand.hpp +78/−0
- libsass/extend.cpp +2010/−0
- libsass/extend.hpp +45/−0
- libsass/file.cpp +349/−0
- libsass/file.hpp +60/−0
- libsass/functions.cpp +1567/−0
- libsass/functions.hpp +184/−0
- libsass/inspect.cpp +866/−0
- libsass/inspect.hpp +98/−0
- libsass/json.cpp +1411/−0
- libsass/json.hpp +117/−0
- libsass/kwd_arg_macros.hpp +28/−0
- libsass/lexer.cpp +133/−0
- libsass/lexer.hpp +239/−0
- libsass/listize.cpp +83/−0
- libsass/listize.hpp +41/−0
- libsass/mapping.hpp +18/−0
- libsass/memory_manager.hpp +57/−0
- libsass/node.cpp +251/−0
- libsass/node.hpp +121/−0
- libsass/operation.hpp +166/−0
- libsass/output.cpp +400/−0
- libsass/output.hpp +56/−0
- libsass/parser.cpp +2228/−0
- libsass/parser.hpp +280/−0
- libsass/paths.hpp +74/−0
- libsass/plugins.cpp +168/−0
- libsass/plugins.hpp +58/−0
- libsass/position.cpp +163/−0
- libsass/position.hpp +120/−0
- libsass/posix/getopt.c +562/−0
- libsass/posix/getopt.h +95/−0
- libsass/prelexer.cpp +724/−0
- libsass/prelexer.hpp +360/−0
- libsass/remove_placeholders.cpp +59/−0
- libsass/remove_placeholders.hpp +43/−0
- libsass/sass.cpp +43/−0
- libsass/sass.h +68/−0
- libsass/sass2scss.cpp +835/−0
- libsass/sass2scss.h +115/−0
- libsass/sass_context.cpp +894/−0
- libsass/sass_context.h +149/−0
- libsass/sass_functions.cpp +173/−0
- libsass/sass_functions.h +108/−0
- libsass/sass_interface.cpp +287/−0
- libsass/sass_interface.h +102/−0
- libsass/sass_util.cpp +132/−0
- libsass/sass_util.hpp +260/−0
- libsass/sass_values.cpp +339/−0
- libsass/sass_values.h +124/−0
- libsass/sass_version.h +8/−0
- libsass/source_map.cpp +184/−0
- libsass/source_map.hpp +65/−0
- libsass/subset_map.hpp +147/−0
- libsass/to_c.cpp +61/−0
- libsass/to_c.hpp +44/−0
- libsass/to_string.cpp +34/−0
- libsass/to_string.hpp +34/−0
- libsass/units.cpp +55/−0
- libsass/units.hpp +15/−0
- libsass/utf8.h +34/−0
- libsass/utf8/checked.h +327/−0
- libsass/utf8/core.h +329/−0
- libsass/utf8/unchecked.h +228/−0
- libsass/utf8_string.cpp +97/−0
- libsass/utf8_string.hpp +37/−0
- libsass/util.cpp +626/−0
- libsass/util.hpp +48/−0
- tests/Main.hs +32/−0
+ Binding/Libsass.hs view
@@ -0,0 +1,19 @@+-- |+-- This is the main module of the library - it reexports rest of the library, so+-- you should only import this one.++{-# LANGUAGE ForeignFunctionInterface #-}+module Binding.Libsass+ (+ libsass_version+ , module Libsass+ ) where++import Binding.Libsass.Context as Libsass+import Binding.Libsass.Functions as Libsass+import Binding.Libsass.Types as Libsass+import Binding.Libsass.Wrappers as Libsass+import Binding.Libsass.Values as Libsass+import Foreign.C.String++foreign import ccall unsafe "sass.h" libsass_version :: CString
+ Binding/Libsass/Context.hs view
@@ -0,0 +1,358 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Binding.Libsass.Context where++import Binding.Libsass.Types+import Foreign+import Foreign.C.String+import Foreign.C.Types++foreign import ccall unsafe "sass_context.h" sass_make_options+ :: IO (Ptr SassOptions)++foreign import ccall unsafe "sass_context.h" sass_make_file_context+ :: CString+ -> IO (Ptr SassFileContext)++-- | Creates and initializes a data context, ie. context that parses string+-- instead of a file.+--+-- WARNING! The string that this function takes is released by the libsass+-- during cleanup. You must not deallocate it.+foreign import ccall unsafe "sass_context.h" sass_make_data_context+ :: CString+ -> IO (Ptr SassDataContext)++foreign import ccall safe "sass_context.h" sass_compile_file_context+ :: Ptr SassFileContext+ -> IO CInt++foreign import ccall safe "sass_context.h" sass_compile_data_context+ :: Ptr SassDataContext+ -> IO CInt++foreign import ccall unsafe "sass_context.h" sass_make_file_compiler+ :: Ptr SassFileContext+ -> IO (Ptr SassCompiler)++foreign import ccall unsafe "sass_context.h" sass_make_data_compiler+ :: Ptr SassDataContext+ -> IO (Ptr SassCompiler)++foreign import ccall safe "sass_context.h" sass_compiler_parse+ :: Ptr SassCompiler+ -> IO CInt++foreign import ccall safe "sass_context.h" sass_compiler_execute+ :: Ptr SassCompiler+ -> IO CInt++foreign import ccall unsafe "sass_context.h" sass_delete_compiler+ :: Ptr SassCompiler+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_delete_file_context+ :: Ptr SassFileContext+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_delete_data_context+ :: Ptr SassDataContext+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_file_context_get_context+ :: Ptr SassFileContext+ -> Ptr SassContext++foreign import ccall unsafe "sass_context.h" sass_data_context_get_context+ :: Ptr SassDataContext+ -> Ptr SassContext++foreign import ccall unsafe "sass_context.h" sass_context_get_options+ :: Ptr SassContext+ -> Ptr SassOptions++foreign import ccall unsafe "sass_context.h" sass_file_context_get_options+ :: Ptr SassFileContext+ -> Ptr SassOptions++foreign import ccall unsafe "sass_context.h" sass_data_context_get_options+ :: Ptr SassDataContext+ -> Ptr SassOptions++foreign import ccall unsafe "sass_context.h" sass_file_context_set_options+ :: Ptr SassFileContext+ -> Ptr SassOptions+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_data_context_set_options+ :: Ptr SassDataContext+ -> Ptr SassOptions+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_get_precision+ :: Ptr SassOptions+ -> IO CInt++foreign import ccall unsafe "sass_context.h" sass_option_get_output_style+ :: Ptr SassOptions+ -> IO CInt -- ^ Returns 'SassOutputStyle'++foreign import ccall unsafe "sass_context.h" sass_option_get_source_comments+ :: Ptr SassOptions+ -> IO Bool++foreign import ccall unsafe "sass_context.h" sass_option_get_source_map_embed+ :: Ptr SassOptions+ -> IO Bool++foreign import ccall unsafe "sass_context.h" sass_option_get_source_map_contents+ :: Ptr SassOptions+ -> IO Bool++foreign import ccall unsafe "sass_context.h" sass_option_get_omit_source_map_url+ :: Ptr SassOptions+ -> IO Bool++foreign import ccall unsafe "sass_context.h" sass_option_get_is_indented_syntax_src+ :: Ptr SassOptions+ -> IO Bool++foreign import ccall unsafe "sass_context.h" sass_option_get_indent+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_linefeed+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_input_path+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_output_path+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_plugin_path+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_include_path+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_source_map_file+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_source_map_root+ :: Ptr SassOptions+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_option_get_c_headers+ :: Ptr SassOptions+ -> IO SassImporterList++foreign import ccall unsafe "sass_context.h" sass_option_get_c_importers+ :: Ptr SassOptions+ -> IO SassImporterList++foreign import ccall unsafe "sass_context.h" sass_option_get_c_functions+ :: Ptr SassOptions+ -> IO SassFunctionList++foreign import ccall unsafe "sass_context.h" sass_option_set_precision+ :: Ptr SassOptions+ -> CInt+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_output_style+ :: Ptr SassOptions+ -> CInt -- ^ 'SassOutputStyle'+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_source_comments+ :: Ptr SassOptions+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_source_map_embed+ :: Ptr SassOptions+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_source_map_contents+ :: Ptr SassOptions+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_omit_source_map_url+ :: Ptr SassOptions+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_is_indented_syntax_src+ :: Ptr SassOptions+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_indent+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_linefeed+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_input_path+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_output_path+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_plugin_path+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_include_path+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_source_map_file+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_source_map_root+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_c_headers+ :: Ptr SassOptions+ -> SassImporterList+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_c_importers+ :: Ptr SassOptions+ -> SassImporterList+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_set_c_functions+ :: Ptr SassOptions+ -> SassFunctionList+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_context_get_output_string+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_status+ :: Ptr SassContext+ -> IO CInt++foreign import ccall unsafe "sass_context.h" sass_context_get_error_json+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_text+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_message+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_file+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_src+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_error_line+ :: Ptr SassContext+ -> IO CSize++foreign import ccall unsafe "sass_context.h" sass_context_get_error_column+ :: Ptr SassContext+ -> IO CSize++foreign import ccall unsafe "sass_context.h" sass_context_get_source_map_string+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_get_included_files+ :: Ptr SassContext+ -> IO (Ptr CString)++foreign import ccall unsafe "sass_context.h" sass_context_get_included_files_size+ :: Ptr SassContext+ -> IO CSize++foreign import ccall unsafe "sass_context.h" sass_context_take_error_json+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_error_text+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_error_message+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_error_file+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_output_string+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_source_map_string+ :: Ptr SassContext+ -> IO CString++foreign import ccall unsafe "sass_context.h" sass_context_take_included_files+ :: Ptr SassContext+ -> IO (Ptr CString)++foreign import ccall unsafe "sass_context.h" sass_compiler_get_state+ :: Ptr SassCompiler+ -> IO CInt -- ^ Retruns 'SassCompilerState'++foreign import ccall unsafe "sass_context.h" sass_compiler_get_context+ :: Ptr SassCompiler+ -> IO (Ptr SassContext)++foreign import ccall unsafe "sass_context.h" sass_compiler_get_import_stack_size+ :: Ptr SassCompiler+ -> IO CSize++foreign import ccall unsafe "sass_context.h" sass_compiler_get_last_import+ :: Ptr SassCompiler+ -> IO SassImportEntry++foreign import ccall unsafe "sass_context.h" sass_compiler_get_import_entry+ :: Ptr SassCompiler+ -> CSize+ -> IO SassImportEntry++foreign import ccall unsafe "sass_context.h" sass_option_push_plugin_path+ :: Ptr SassOptions+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_context.h" sass_option_push_include_path+ :: Ptr SassOptions+ -> CString+ -> IO ()
+ Binding/Libsass/Functions.hs view
@@ -0,0 +1,156 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Binding.Libsass.Functions where++import Binding.Libsass.Types+import Foreign+import Foreign.C++foreign import ccall unsafe "sass_function.h" sass_make_importer_list+ :: CSize+ -> IO SassImporterList++-- Libsass lacks implementation of these functions.++-- foreign import ccall unsafe "sass_function.h" sass_importer_get_list_entry+-- :: SassImporterList+-- -> CSize+-- -> IO SassImporterEntry++-- foreign import ccall unsafe "sass_function.h" sass_importer_set_list_entry+-- :: SassImporterList+-- -> CSize+-- -> SassImporterEntry+-- -> IO ()++foreign import ccall unsafe "sass_function.h" sass_make_importer+ :: SassImporterFn+ -> CDouble+ -> Ptr ()+ -> IO SassImporterEntry++foreign import ccall unsafe "sass_function.h" sass_importer_get_function+ :: SassImporterEntry+ -> IO SassImporterFn++foreign import ccall unsafe "sass_function.h" sass_importer_get_priority+ :: SassImporterEntry+ -> IO CDouble+foreign import ccall unsafe "sass_function.h" sass_importer_get_cookie+ :: SassImporterEntry+ -> IO (Ptr ())++foreign import ccall unsafe "sass_function.h" sass_delete_importer+ :: SassImporterEntry+ -> IO ()++foreign import ccall unsafe "sass_function.h" sass_make_import_list+ :: CSize+ -> IO SassImportList++foreign import ccall unsafe "sass_function.h" sass_make_import_entry+ :: CString+ -> CString+ -> CString+ -> IO SassImportEntry++foreign import ccall unsafe "sass_function.h" sass_make_import+ :: CString+ -> CString+ -> CString+ -> CString+ -> IO SassImportEntry++foreign import ccall unsafe "sass_function.h" sass_import_set_error+ :: SassImportEntry+ -> CString+ -> CSize+ -> CSize+ -> IO SassImportEntry++foreign import ccall unsafe "sass_function.h" sass_import_set_list_entry+ :: SassImportList+ -> CSize+ -> SassImportEntry+ -> IO ()++foreign import ccall unsafe "sass_function.h" sass_import_get_list_entry+ :: SassImportList+ -> CSize+ -> IO SassImportEntry++foreign import ccall unsafe "sass_function.h" sass_import_get_path+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_get_base+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_get_source+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_get_srcmap+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_take_source+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_take_srcmap+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_import_get_error_line+ :: SassImportEntry+ -> IO CSize++foreign import ccall unsafe "sass_function.h" sass_import_get_error_column+ :: SassImportEntry+ -> IO CSize++foreign import ccall unsafe "sass_function.h" sass_import_get_error_message+ :: SassImportEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_delete_import_list+ :: SassImportList+ -> IO ()++foreign import ccall unsafe "sass_function.h" sass_delete_import+ :: SassImportEntry+ -> IO ()++foreign import ccall unsafe "sass_function.h" sass_make_function_list+ :: CSize+ -> IO SassFunctionList++foreign import ccall unsafe "sass_function.h" sass_make_function+ :: CString+ -> SassFunctionFn+ -> Ptr ()+ -> IO SassFunctionEntry++foreign import ccall unsafe "sass_function.h" sass_function_get_list_entry+ :: SassFunctionList+ -> CSize+ -> IO SassFunctionEntry++foreign import ccall unsafe "sass_function.h" sass_function_set_list_entry+ :: SassFunctionList+ -> CSize+ -> SassFunctionEntry+ -> IO ()++foreign import ccall unsafe "sass_function.h" sass_function_get_signature+ :: SassFunctionEntry+ -> IO CString++foreign import ccall unsafe "sass_function.h" sass_function_get_function+ :: SassFunctionEntry+ -> IO SassFunctionFn++foreign import ccall unsafe "sass_function.h" sass_function_get_cookie+ :: SassFunctionEntry+ -> IO (Ptr ())
+ Binding/Libsass/Types.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE EmptyDataDecls #-}+module Binding.Libsass.Types where++import Foreign+import Foreign.C.String++-- From sass.h++-- | Defines output style of compiled CSS.+data SassOutputStyle = SassStyleNested+ | SassStyleExpanded+ | SassStyleCompact+ | SassStyleCompressed+ deriving (Eq, Show)++instance Enum SassOutputStyle where+ fromEnum SassStyleNested = 0+ fromEnum SassStyleExpanded = 1+ fromEnum SassStyleCompact = 2+ fromEnum SassStyleCompressed = 3++ toEnum 0 = SassStyleNested+ toEnum 1 = SassStyleExpanded+ toEnum 2 = SassStyleCompact+ toEnum 3 = SassStyleCompressed+ toEnum u = error ("SassOutputStyle.toEnum: Cannot match " ++ show u)++-- From sass_context.h++data SassCompiler+data SassOptions+data SassContext+data SassFileContext+data SassDataContext++data SassCompilerState = SassCompilerCreated+ | SassCompilerParsed+ | SassCompilerExecuted+ deriving (Show, Eq)++instance Enum SassCompilerState where+ fromEnum SassCompilerCreated = 0+ fromEnum SassCompilerParsed = 1+ fromEnum SassCompilerExecuted = 2++ toEnum 0 = SassCompilerCreated+ toEnum 1 = SassCompilerParsed+ toEnum 2 = SassCompilerExecuted+ toEnum u = error ("SassCompilerState.toEnum: Cannot match " ++ show u)++-- From sass_functions.h++data SassImport+data SassImporter+data SassFunction++type SassImportEntry = Ptr SassImport+type SassImportList = Ptr (Ptr SassImport)+type SassImporterEntry = Ptr SassImporter+type SassImporterList = Ptr (Ptr SassImporter)++type SassImporterFnType = + CString+ -> SassImporterEntry+ -> Ptr SassCompiler+ -> IO SassImportList+type SassImporterFn = FunPtr SassImporterFnType++type SassFunctionEntry = Ptr SassFunction+type SassFunctionList = Ptr (Ptr SassFunction)++type SassFunctionFnType =+ Ptr SassValue+ -> SassFunctionEntry+ -> Ptr SassOptions+ -> IO (Ptr SassValue)+type SassFunctionFn = FunPtr SassFunctionFnType++-- From sass_values.h++data SassValue++data SassTag = SassBoolean+ | SassNumber+ | SassColor+ | SassString+ | SassList+ | SassMap+ | SassNull+ | SassError+ | SassWarning+ deriving (Eq, Show)++instance Enum SassTag where+ fromEnum SassBoolean = 0+ fromEnum SassNumber = 1+ fromEnum SassColor = 2+ fromEnum SassString = 3+ fromEnum SassList = 4+ fromEnum SassMap = 5+ fromEnum SassNull = 6+ fromEnum SassError = 7+ fromEnum SassWarning = 8++ toEnum 0 = SassBoolean+ toEnum 1 = SassNumber+ toEnum 2 = SassColor+ toEnum 3 = SassString+ toEnum 4 = SassList+ toEnum 5 = SassMap+ toEnum 6 = SassNull+ toEnum 7 = SassError+ toEnum 8 = SassWarning+ toEnum u = error ("SassTag.toEnum: Cannot match " ++ show u)++-- ^ Separator used in Sass lists.+data SassSeparator = SassSeparatorComma+ | SassSeparatorSpace+ deriving (Eq, Show)++instance Enum SassSeparator where+ fromEnum SassSeparatorComma = 0+ fromEnum SassSeparatorSpace = 1++ toEnum 0 = SassSeparatorComma+ toEnum 1 = SassSeparatorSpace+ toEnum u = error ("SassSeparator.toEnum: Cannot match " ++ show u)
+ Binding/Libsass/Values.hs view
@@ -0,0 +1,234 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Binding.Libsass.Values where++import Binding.Libsass.Types+import Foreign+import Foreign.C++foreign import ccall unsafe "sass_values.h" sass_value_get_tag+ :: Ptr SassValue+ -> IO CInt -- ^ Returns 'SassTag'++foreign import ccall unsafe "sass_values.h" sass_value_is_null+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_number+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_string+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_boolean+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_color+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_list+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_map+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_error+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_value_is_warning+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_number_get_value+ :: Ptr SassValue+ -> IO CDouble++foreign import ccall unsafe "sass_values.h" sass_number_set_value+ :: Ptr SassValue+ -> CDouble+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_number_get_unit+ :: Ptr SassValue+ -> IO CString++foreign import ccall unsafe "sass_values.h" sass_number_set_unit+ :: Ptr SassValue+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_string_get_value+ :: Ptr SassValue+ -> IO CString++foreign import ccall unsafe "sass_values.h" sass_string_set_value+ :: Ptr SassValue+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_boolean_get_value+ :: Ptr SassValue+ -> IO Bool++foreign import ccall unsafe "sass_values.h" sass_boolean_set_value+ :: Ptr SassValue+ -> Bool+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_color_get_r+ :: Ptr SassValue+ -> IO CDouble++foreign import ccall unsafe "sass_values.h" sass_color_set_r+ :: Ptr SassValue+ -> CDouble+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_color_get_g+ :: Ptr SassValue+ -> IO CDouble++foreign import ccall unsafe "sass_values.h" sass_color_set_g+ :: Ptr SassValue+ -> CDouble+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_color_get_b+ :: Ptr SassValue+ -> IO CDouble++foreign import ccall unsafe "sass_values.h" sass_color_set_b+ :: Ptr SassValue+ -> CDouble+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_color_get_a+ :: Ptr SassValue+ -> IO CDouble++foreign import ccall unsafe "sass_values.h" sass_color_set_a+ :: Ptr SassValue+ -> CDouble+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_list_get_length+ :: Ptr SassValue+ -> IO CSize++foreign import ccall unsafe "sass_values.h" sass_list_get_separator+ :: Ptr SassValue+ -> IO CInt -- ^ Retrurns 'SassSeparator'++foreign import ccall unsafe "sass_values.h" sass_list_set_separator+ :: Ptr SassValue+ -> CInt -- ^ 'SassSeparator'+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_list_get_value+ :: Ptr SassValue+ -> CSize+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_list_set_value+ :: Ptr SassValue+ -> CSize+ -> Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_map_get_length+ :: Ptr SassValue+ -> IO CSize++foreign import ccall unsafe "sass_values.h" sass_map_get_key+ :: Ptr SassValue+ -> CSize+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_map_set_key+ :: Ptr SassValue+ -> CSize+ -> Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_map_get_value+ :: Ptr SassValue+ -> CSize+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_map_set_value+ :: Ptr SassValue+ -> CSize+ -> Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_error_get_message+ :: Ptr SassValue+ -> IO CString++foreign import ccall unsafe "sass_values.h" sass_error_set_message+ :: Ptr SassValue+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_warning_get_message+ :: Ptr SassValue+ -> IO CString++foreign import ccall unsafe "sass_values.h" sass_warning_set_message+ :: Ptr SassValue+ -> CString+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_make_null+ :: IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_boolean+ :: Bool+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_string+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_number+ :: CDouble+ -> CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_color+ :: CDouble+ -> CDouble+ -> CDouble+ -> CDouble+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_list+ :: CSize+ -> CInt -- ^ 'SassSeparator'+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_map+ :: CSize+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_error+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_make_warning+ :: CString+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass_values.h" sass_delete_value+ :: Ptr SassValue+ -> IO ()++foreign import ccall unsafe "sass_values.h" sass_clone_value+ :: Ptr SassValue+ -> IO (Ptr SassValue)
+ Binding/Libsass/Wrappers.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE ForeignFunctionInterface #-}+module Binding.Libsass.Wrappers where++import Binding.Libsass.Types+import Foreign++foreign import ccall safe "wrapper" mkSassImporterFn+ :: SassImporterFnType+ -> IO SassImporterFn++foreign import ccall safe "wrapper" mkSassFunctionFn+ :: SassFunctionFnType+ -> IO SassFunctionFn++foreign import ccall "sass_context.h &sass_delete_compiler"+ p_sass_delete_compiler :: FunPtr (Ptr SassCompiler -> IO ())++foreign import ccall "sass_context.h &sass_delete_file_context"+ p_sass_delete_file_context :: FunPtr (Ptr SassFileContext -> IO ())++foreign import ccall "sass_context.h &sass_delete_data_context"+ p_sass_delete_data_context :: FunPtr (Ptr SassDataContext -> IO ())++foreign import ccall "sass_function.h &sass_delete_import_list"+ p_sass_delete_import_list :: FunPtr (SassImportList -> IO ())++foreign import ccall "sass_function.h &sass_delete_import"+ p_sass_delete_import :: FunPtr (SassImportEntry -> IO ())++foreign import ccall "sass_function.h &sass_delete_importer"+ p_sass_delete_importer :: FunPtr (SassImporterEntry -> IO ())++foreign import ccall "sass_values.h &sass_delete_value"+ p_sass_delete_value :: FunPtr (Ptr SassValue -> IO ())
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 Jakub Fijałkowski++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,55 @@+import Data.Maybe (fromJust)+import Distribution.PackageDescription+import Distribution.Simple+import Distribution.Simple.LocalBuildInfo (InstallDirs (..),+ LocalBuildInfo (..),+ absoluteInstallDirs,+ localPkgDescr)+import Distribution.Simple.Setup+import Distribution.Simple.Utils (rawSystemExit)+import System.Directory (getCurrentDirectory)++main = defaultMainWithHooks simpleUserHooks+ {+ preConf = \a f -> makeLibsass a f >> preConf simpleUserHooks a f+ , copyHook = copyLibsass+ , confHook = \a f -> confHook simpleUserHooks a f >>= updateExtraLibDirs+ , postClean = cleanLibsass+ }++makeLibsass :: Args -> ConfigFlags -> IO ()+makeLibsass _ flags =+ rawSystemExit (fromFlag $ configVerbosity flags) "env"+ ["make", "--directory=libsass"]++updateExtraLibDirs :: LocalBuildInfo -> IO LocalBuildInfo+updateExtraLibDirs localBuildInfo = do+ let packageDescription = localPkgDescr localBuildInfo+ lib = fromJust $ library packageDescription+ libBuild = libBuildInfo lib+ dir <- getCurrentDirectory+ return localBuildInfo {+ localPkgDescr = packageDescription {+ library = Just $ lib {+ libBuildInfo = libBuild {+ extraLibDirs = (dir ++ "/libsass/lib") :+ extraLibDirs libBuild+ }+ }+ }+ }++copyLibsass :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags+ -> IO ()+copyLibsass pkg_descr lbi _ flags = do+ let libPref = libdir . absoluteInstallDirs pkg_descr lbi+ . fromFlag . copyDest+ $ flags+ rawSystemExit (fromFlag $ copyVerbosity flags) "cp"+ ["libsass/lib/libsass.a", libPref]+++cleanLibsass :: Args -> CleanFlags -> PackageDescription -> () -> IO ()+cleanLibsass _ flags _ _ =+ rawSystemExit (fromFlag $ cleanVerbosity flags) "env"+ ["make", "--directory=libsass", "clean"]
+ hlibsass.cabal view
@@ -0,0 +1,57 @@+name: hlibsass+version: 0.1.0.0+license: MIT+license-file: LICENSE+author: Jakub Fijałkowski <fiolek94@gmail.com>+maintainer: fiolek94@gmail.com+category: Foreign+build-type: Custom+cabal-version: >= 1.10+stability: experimental+synopsis: Low-level bindings to libsass+description:+ This package provides (very) low level bindings to libsass. It uses libsass+ compiled to static library, so if you use this in your project, you have to+ link your project with C++ runtime(which libsass does automatically - links+ with libstd++).+ For documentation visit <https://github.com/sass/libsass>.++extra-source-files:+ libsass/*.cpp+ libsass/*.c+ libsass/*.h+ libsass/*.hpp+ libsass/b64/*.h+ libsass/posix/*.h+ libsass/posix/*.c+ libsass/utf8/*.h+ libsass/Makefile+ libsass/LICENSE++source-repository head+ type: git+ location: git://github.com/jakubfijalkowski/hlibsass.git++library+ exposed-modules:+ Binding.Libsass+ , Binding.Libsass.Types+ , Binding.Libsass.Wrappers+ , Binding.Libsass.Context+ , Binding.Libsass.Functions+ , Binding.Libsass.Values+ build-depends: base >= 4.7 && < 5+ hs-source-dirs: .+ default-language: Haskell2010+ default-extensions: ForeignFunctionInterface+ extra-libraries: sass+ ghc-options: -Wall+ extra-libraries: stdc++, sass++test-suite tests+ hs-source-dirs: tests+ main-is: Main.hs+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ build-depends: base >= 4.7 && < 5, hspec, hlibsass+ default-language: Haskell2010
+ libsass/LICENSE view
@@ -0,0 +1,25 @@++Copyright (C) 2012 by Hampton Catlin++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to do+so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+++The following files in the spec were taken from the original Ruby Sass project which+is copyright Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein and under+the same license.
+ libsass/Makefile view
@@ -0,0 +1,242 @@+CC ?= gcc+CXX ?= g+++RM ?= rm -f+CP ?= cp -a+MKDIR ?= mkdir+WINDRES ?= windres+CFLAGS ?= -Wall -O2+CXXFLAGS ?= -Wall -O2+LDFLAGS ?= -Wall -O2 -Wl,--no-undefined+CAT ?= $(if $(filter $(OS),Windows_NT),type,cat)++ifneq (,$(findstring /cygdrive/,$(PATH)))+ UNAME := Cygwin+else+ ifneq (,$(findstring WINDOWS,$(PATH)))+ UNAME := Windows+ else+ ifneq (,$(findstring mingw32,$(MAKE)))+ UNAME := MinGW+ else+ ifneq (,$(findstring MINGW32,$(shell uname -s)))+ UNAME = MinGW+ else+ UNAME := $(shell uname -s)+ endif+ endif+ endif+endif++ifeq "$(LIBSASS_VERSION)" ""+ ifneq "$(wildcard ./.git/ )" ""+ LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags)+ endif+endif++ifeq "$(LIBSASS_VERSION)" ""+ ifneq ("$(wildcard VERSION)","")+ LIBSASS_VERSION ?= $(shell $(CAT) VERSION)+ endif+endif++ifneq "$(LIBSASS_VERSION)" ""+ CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""+ CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\""+endif++# enable mandatory flag+ifeq (MinGW,$(UNAME))+ CXXFLAGS += -std=gnu++0x+ LDFLAGS += -std=gnu++0x+else+ CXXFLAGS += -std=c++0x+ LDFLAGS += -std=c++0x+endif++ifneq "$(SASS_LIBSASS_PATH)" ""+ CFLAGS += -I $(SASS_LIBSASS_PATH)+ CXXFLAGS += -I $(SASS_LIBSASS_PATH)+endif++ifneq "$(EXTRA_CFLAGS)" ""+ CFLAGS += $(EXTRA_CFLAGS)+endif+ifneq "$(EXTRA_CXXFLAGS)" ""+ CXXFLAGS += $(EXTRA_CXXFLAGS)+endif+ifneq "$(EXTRA_LDFLAGS)" ""+ LDFLAGS += $(EXTRA_LDFLAGS)+endif++LDLIBS = -lstdc++ -lm+ifeq ($(UNAME),Darwin)+ CFLAGS += -stdlib=libc+++ CXXFLAGS += -stdlib=libc+++ LDFLAGS += -stdlib=libc+++endif++ifneq (MinGW,$(UNAME))+ LDFLAGS += -ldl+ LDLIBS += -ldl+endif++ifneq ($(BUILD),shared)+ BUILD = static+endif++ifeq (,$(PREFIX))+ ifeq (,$(TRAVIS_BUILD_DIR))+ PREFIX = /usr/local+ else+ PREFIX = $(TRAVIS_BUILD_DIR)+ endif+endif++SASS_SASSC_PATH ?= sassc+SASS_SPEC_PATH ?= sass-spec+SASS_SPEC_SPEC_DIR ?= spec+SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc+RUBY_BIN = ruby++ifeq (MinGW,$(UNAME))+ SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe+endif+ifeq (Windows,$(UNAME))+ SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc.exe+endif++SOURCES = \+ ast.cpp \+ base64vlq.cpp \+ bind.cpp \+ constants.cpp \+ context.cpp \+ contextualize.cpp \+ contextualize_eval.cpp \+ cssize.cpp \+ listize.cpp \+ error_handling.cpp \+ eval.cpp \+ expand.cpp \+ extend.cpp \+ file.cpp \+ functions.cpp \+ inspect.cpp \+ lexer.cpp \+ node.cpp \+ json.cpp \+ emitter.cpp \+ output.cpp \+ parser.cpp \+ plugins.cpp \+ position.cpp \+ prelexer.cpp \+ remove_placeholders.cpp \+ sass.cpp \+ sass_util.cpp \+ sass_values.cpp \+ sass_context.cpp \+ sass_functions.cpp \+ sass_interface.cpp \+ sass2scss.cpp \+ source_map.cpp \+ to_c.cpp \+ to_string.cpp \+ units.cpp \+ utf8_string.cpp \+ util.cpp++CSOURCES = cencode.c++RESOURCES =++LIBRARIES = lib/libsass.so++ifeq (MinGW,$(UNAME))+ ifeq (shared,$(BUILD))+ CFLAGS += -D ADD_EXPORTS+ CXXFLAGS += -D ADD_EXPORTS+ LIBRARIES += lib/libsass.dll+ RESOURCES += res/resource.rc+ endif+else+ CFLAGS += -fPIC+ CXXFLAGS += -fPIC+ LDFLAGS += -fPIC+endif++OBJECTS = $(SOURCES:.cpp=.o)+COBJECTS = $(CSOURCES:.c=.o)+RCOBJECTS = $(RESOURCES:.rc=.o)++DEBUG_LVL ?= NONE++all: $(BUILD)++debug: $(BUILD)++debug-static: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS))+debug-static: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS))+debug-static: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS))+debug-static: static++debug-shared: LDFLAGS := -g $(filter-out -O2,$(LDFLAGS))+debug-shared: CFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CFLAGS))+debug-shared: CXXFLAGS := -g -DDEBUG -DDEBUG_LVL="$(DEBUG_LVL)" $(filter-out -O2,$(CXXFLAGS))+debug-shared: shared++static: lib/libsass.a+shared: $(LIBRARIES)++lib:+ $(MKDIR) lib++lib/libsass.a: lib $(COBJECTS) $(OBJECTS)+ $(AR) rcvs $@ $(COBJECTS) $(OBJECTS)++lib/libsass.so: lib $(COBJECTS) $(OBJECTS)+ $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(LDLIBS)++lib/libsass.dll: lib $(COBJECTS) $(OBJECTS) $(RCOBJECTS)+ $(CXX) -shared $(LDFLAGS) -o $@ $(COBJECTS) $(OBJECTS) $(RCOBJECTS) $(LDLIBS) -s -Wl,--subsystem,windows,--out-implib,lib/libsass.a++%.o: %.c+ $(CC) $(CFLAGS) -c -o $@ $<++%.o: %.rc+ $(WINDRES) -i $< -o $@++%.o: %.cpp+ $(CXX) $(CXXFLAGS) -c -o $@ $<++%: %.o static+ $(CXX) $(CXXFLAGS) -o $@ $+ $(LDFLAGS) $(LDLIBS)++install: install-$(BUILD)++install-static: lib/libsass.a+ install -pm0755 $< $(DESTDIR)$(PREFIX)/$<++install-shared: lib/libsass.so+ install -pm0755 $< $(DESTDIR)$(PREFIX)/$<++$(SASSC_BIN): $(BUILD)+ cd $(SASS_SASSC_PATH) && $(MAKE)++sassc: $(SASSC_BIN)+ $(SASSC_BIN) -v++test: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)++test_build: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s --ignore-todo $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR)++test_issues: $(SASSC_BIN)+ $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) $(LOG_FLAGS) $(SASS_SPEC_PATH)/spec/issues++clean:+ $(RM) $(RCOBJECTS) $(COBJECTS) $(OBJECTS) $(LIBRARIES) lib/*.a lib/*.so lib/*.dll lib/*.la+++.PHONY: all debug debug-static debug-shared static shared install install-static install-shared sassc clean
+ libsass/ast.cpp view
@@ -0,0 +1,591 @@+#include "ast.hpp"+#include "context.hpp"+#include "to_string.hpp"+#include <set>+#include <algorithm>+#include <iostream>++namespace Sass {+ using namespace std;++ bool Compound_Selector::operator<(const Compound_Selector& rhs) const+ {+ To_String to_string;+ // ugly+ return const_cast<Compound_Selector*>(this)->perform(&to_string) <+ const_cast<Compound_Selector&>(rhs).perform(&to_string);+ }++ bool Complex_Selector::operator<(const Complex_Selector& rhs) const+ {+ To_String to_string;+ return const_cast<Complex_Selector*>(this)->perform(&to_string) <+ const_cast<Complex_Selector&>(rhs).perform(&to_string);+ }++ bool Complex_Selector::operator==(const Complex_Selector& rhs) const {+ // TODO: We have to access the tail directly using tail_ since ADD_PROPERTY doesn't provide a const version.++ const Complex_Selector* pOne = this;+ const Complex_Selector* pTwo = &rhs;++ // Consume any empty references at the beginning of the Complex_Selector+ if (pOne->combinator() == Complex_Selector::ANCESTOR_OF && pOne->head()->is_empty_reference()) {+ pOne = pOne->tail_;+ }+ if (pTwo->combinator() == Complex_Selector::ANCESTOR_OF && pTwo->head()->is_empty_reference()) {+ pTwo = pTwo->tail_;+ }++ while (pOne && pTwo) {+ if (pOne->combinator() != pTwo->combinator()) {+ return false;+ }++ if (*(pOne->head()) != *(pTwo->head())) {+ return false;+ }++ pOne = pOne->tail_;+ pTwo = pTwo->tail_;+ }++ return pOne == NULL && pTwo == NULL;+ }++ Compound_Selector* Compound_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ Compound_Selector* unified = rhs;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if (!unified) break;+ else unified = (*this)[i]->unify_with(unified, ctx);+ }+ return unified;+ }++ bool Simple_Selector::operator==(const Simple_Selector& rhs) const+ {+ // Compare the string representations for equality.++ // Cast away const here. To_String should take a const object, but it doesn't.+ Simple_Selector* pLHS = const_cast<Simple_Selector*>(this);+ Simple_Selector* pRHS = const_cast<Simple_Selector*>(&rhs);++ To_String to_string;+ return pLHS->perform(&to_string) == pRHS->perform(&to_string);+ }++ bool Simple_Selector::operator<(const Simple_Selector& rhs) const {+ // Use the string representation for ordering.++ // Cast away const here. To_String should take a const object, but it doesn't.+ Simple_Selector* pLHS = const_cast<Simple_Selector*>(this);+ Simple_Selector* pRHS = const_cast<Simple_Selector*>(&rhs);++ To_String to_string;+ return pLHS->perform(&to_string) < pRHS->perform(&to_string);+ }++ Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ To_String to_string(&ctx);+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ { if (perform(&to_string) == (*rhs)[i]->perform(&to_string)) return rhs; }++ // check for pseudo elements because they need to come last+ size_t i, L;+ bool found = false;+ if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector))+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector)) && (*rhs)[L-1]->is_pseudo_element())+ { found = true; break; }+ }+ }+ else+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if (typeid(*(*rhs)[i]) == typeid(Pseudo_Selector) || typeid(*(*rhs)[i]) == typeid(Wrapped_Selector))+ { found = true; break; }+ }+ }+ if (!found)+ {+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(*rhs);+ (*cpy) << this;+ return cpy;+ }+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->pstate());+ for (size_t j = 0; j < i; ++j)+ { (*cpy) << (*rhs)[j]; }+ (*cpy) << this;+ for (size_t j = i; j < L; ++j)+ { (*cpy) << (*rhs)[j]; }+ return cpy;+ }++ Compound_Selector* Type_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ // TODO: handle namespaces++ // if the rhs is empty, just return a copy of this+ if (rhs->length() == 0) {+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->pstate());+ (*cpy) << this;+ return cpy;+ }++ // if this is a universal selector and rhs is not empty, just return the rhs+ if (name() == "*")+ { return new (ctx.mem) Compound_Selector(*rhs); }+++ Simple_Selector* rhs_0 = (*rhs)[0];+ // otherwise, this is a tag name+ if (typeid(*rhs_0) == typeid(Type_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ if (static_cast<Type_Selector*>(rhs_0)->name() == "*")+ {+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->pstate());+ (*cpy) << this;+ for (size_t i = 1, L = rhs->length(); i < L; ++i)+ { (*cpy) << (*rhs)[i]; }+ return cpy;+ }+ // if rhs is another tag name and it matches this, return rhs+ else if (static_cast<Type_Selector*>(rhs_0)->name() == name())+ { return new (ctx.mem) Compound_Selector(*rhs); }+ // else the tag names don't match; return nil+ else+ { return 0; }+ }+ // else it's a tag name and a bunch of qualifiers -- just append them+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(rhs->pstate());+ (*cpy) << this;+ (*cpy) += rhs;+ return cpy;+ }++ Compound_Selector* Selector_Qualifier::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ if (name()[0] == '#')+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ Simple_Selector* rhs_i = (*rhs)[i];+ if (typeid(*rhs_i) == typeid(Selector_Qualifier) &&+ static_cast<Selector_Qualifier*>(rhs_i)->name()[0] == '#' &&+ static_cast<Selector_Qualifier*>(rhs_i)->name() != name())+ return 0;+ }+ }+ rhs->has_line_break(has_line_break());+ return Simple_Selector::unify_with(rhs, ctx);+ }++ Compound_Selector* Pseudo_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ if (is_pseudo_element())+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ Simple_Selector* rhs_i = (*rhs)[i];+ if (typeid(*rhs_i) == typeid(Pseudo_Selector) &&+ static_cast<Pseudo_Selector*>(rhs_i)->is_pseudo_element() &&+ static_cast<Pseudo_Selector*>(rhs_i)->name() != name())+ { return 0; }+ }+ }+ return Simple_Selector::unify_with(rhs, ctx);+ }++ bool Compound_Selector::is_superselector_of(Compound_Selector* rhs)+ {+ To_String to_string;++ Simple_Selector* lbase = base();+ Simple_Selector* rbase = rhs->base();++ // Check if pseudo-elements are the same between the selectors++ set<string> lpsuedoset, rpsuedoset;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if ((*this)[i]->is_pseudo_element()) {+ string pseudo((*this)[i]->perform(&to_string));+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ lpsuedoset.insert(pseudo);+ }+ }+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((*rhs)[i]->is_pseudo_element()) {+ string pseudo((*rhs)[i]->perform(&to_string));+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ rpsuedoset.insert(pseudo);+ }+ }+ if (lpsuedoset != rpsuedoset) {+ return false;+ }++ // Check the Simple_Selectors++ set<string> lset, rset;++ if (!lbase) // no lbase; just see if the left-hand qualifiers are a subset of the right-hand selector+ {+ for (size_t i = 0, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->perform(&to_string)); }+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ { rset.insert((*rhs)[i]->perform(&to_string)); }+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());+ }+ else { // there's an lbase+ for (size_t i = 1, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->perform(&to_string)); }+ if (rbase)+ {+ if (lbase->perform(&to_string) != rbase->perform(&to_string)) // if there's an rbase, make sure they match+ { return false; }+ else // the bases do match, so compare qualifiers+ {+ for (size_t i = 1, L = rhs->length(); i < L; ++i)+ { rset.insert((*rhs)[i]->perform(&to_string)); }+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());+ }+ }+ }+ // catch-all+ return false;+ }++ bool Compound_Selector::operator==(const Compound_Selector& rhs) const {+ To_String to_string;++ // Check if pseudo-elements are the same between the selectors++ set<string> lpsuedoset, rpsuedoset;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if ((*this)[i]->is_pseudo_element()) {+ string pseudo((*this)[i]->perform(&to_string));+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ lpsuedoset.insert(pseudo);+ }+ }+ for (size_t i = 0, L = rhs.length(); i < L; ++i)+ {+ if (rhs[i]->is_pseudo_element()) {+ string pseudo(rhs[i]->perform(&to_string));+ pseudo = pseudo.substr(pseudo.find_first_not_of(":")); // strip off colons to ensure :after matches ::after since ruby sass is forgiving+ rpsuedoset.insert(pseudo);+ }+ }+ if (lpsuedoset != rpsuedoset) {+ return false;+ }++ // Check the base++ const Simple_Selector* const lbase = base();+ const Simple_Selector* const rbase = rhs.base();++ if ((lbase && !rbase) ||+ (!lbase && rbase) ||+ ((lbase && rbase) && (*lbase != *rbase))) {+ return false;+ }+++ // Check the rest of the SimpleSelectors+ // Use string representations. We can't create a set of Simple_Selector pointers because std::set == std::set is going to call ==+ // on the pointers to determine equality. I don't know of a way to pass in a comparison object. The one you can specify as part of+ // the template type is used for ordering, but not equality. We also can't just put in non-pointer Simple_Selectors because the+ // class is intended to be subclassed, and we'd get splicing.++ set<string> lset, rset;++ for (size_t i = 0, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->perform(&to_string)); }+ for (size_t i = 0, L = rhs.length(); i < L; ++i)+ { rset.insert(rhs[i]->perform(&to_string)); }++ return lset == rset;+ }++ bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const {+ return *pLeft < *pRight;+ }++ bool Complex_Selector::is_superselector_of(Compound_Selector* rhs)+ {+ if (length() != 1)+ { return false; }+ return base()->is_superselector_of(rhs);+ }++ bool Complex_Selector::is_superselector_of(Complex_Selector* rhs)+ {+ Complex_Selector* lhs = this;+ To_String to_string;+ // check for selectors with leading or trailing combinators+ if (!lhs->head() || !rhs->head())+ { return false; }+ Complex_Selector* l_innermost = lhs->innermost();+ if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !l_innermost->tail())+ { return false; }+ Complex_Selector* r_innermost = rhs->innermost();+ if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF && !r_innermost->tail())+ { return false; }+ // more complex (i.e., longer) selectors are always more specific+ size_t l_len = lhs->length(), r_len = rhs->length();+ if (l_len > r_len)+ { return false; }++ if (l_len == 1)+ { return lhs->head()->is_superselector_of(rhs->base()); }++ bool found = false;+ Complex_Selector* marker = rhs;+ for (size_t i = 0, L = rhs->length(); i < L; ++i) {+ if (i == L-1)+ { return false; }+ if (lhs->head()->is_superselector_of(marker->head()))+ { found = true; break; }+ marker = marker->tail();+ }+ if (!found)+ { return false; }++ /*+ Hmm, I hope I have the logic right:++ if lhs has a combinator:+ if !(marker has a combinator) return false+ if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false+ return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)+ else if marker has a combinator:+ if !(marker.combinator == ">") return false+ return lhs.tail.is_superselector_of(marker.tail)+ else+ return lhs.tail.is_superselector_of(marker.tail)+ */+ if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() == Complex_Selector::ANCESTOR_OF)+ { return false; }+ if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() != Complex_Selector::PARENT_OF)+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ else+ {+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ // catch-all+ return false;+ }++ size_t Complex_Selector::length()+ {+ // TODO: make this iterative+ if (!tail()) return 1;+ return 1 + tail()->length();+ }++ Compound_Selector* Complex_Selector::base()+ {+ if (!tail()) return head();+ else return tail()->base();+ }++ Complex_Selector* Complex_Selector::context(Context& ctx)+ {+ if (!tail()) return 0;+ if (!head()) return tail()->context(ctx);+ Complex_Selector* cpy = new (ctx.mem) Complex_Selector(pstate(), combinator(), head(), tail()->context(ctx));+ cpy->media_block(media_block());+ cpy->last_block(last_block());+ return cpy;+ }++ Complex_Selector* Complex_Selector::innermost()+ {+ if (!tail()) return this;+ else return tail()->innermost();+ }++ Complex_Selector::Combinator Complex_Selector::clear_innermost()+ {+ Combinator c;+ if (!tail() || tail()->length() == 1)+ { c = combinator(); combinator(ANCESTOR_OF); tail(0); }+ else+ { c = tail()->clear_innermost(); }+ return c;+ }++ void Complex_Selector::set_innermost(Complex_Selector* val, Combinator c)+ {+ if (!tail())+ { tail(val); combinator(c); }+ else+ { tail()->set_innermost(val, c); }+ }++ Complex_Selector* Complex_Selector::clone(Context& ctx) const+ {+ Complex_Selector* cpy = new (ctx.mem) Complex_Selector(*this);+ if (tail()) cpy->tail(tail()->clone(ctx));+ return cpy;+ }++ Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const+ {+ Complex_Selector* cpy = new (ctx.mem) Complex_Selector(*this);++ if (head()) {+ cpy->head(head()->clone(ctx));+ }++ if (tail()) {+ cpy->tail(tail()->cloneFully(ctx));+ }++ return cpy;+ }++ Compound_Selector* Compound_Selector::clone(Context& ctx) const+ {+ Compound_Selector* cpy = new (ctx.mem) Compound_Selector(*this);+ return cpy;+ }++++ /* not used anymore - remove?+ Selector_Placeholder* Selector::find_placeholder()+ {+ return 0;+ }*/++ void Selector_List::adjust_after_pushing(Complex_Selector* c)+ {+ if (c->has_reference()) has_reference(true);+ if (c->has_placeholder()) has_placeholder(true);++#ifdef DEBUG+ To_String to_string;+ this->mCachedSelector(this->perform(&to_string));+#endif+ }++ /* not used anymore - remove?+ Selector_Placeholder* Selector_List::find_placeholder()+ {+ if (has_placeholder()) {+ for (size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder();+ }+ }+ return 0;+ }*/++ /* not used anymore - remove?+ Selector_Placeholder* Complex_Selector::find_placeholder()+ {+ if (has_placeholder()) {+ if (head() && head()->has_placeholder()) return head()->find_placeholder();+ else if (tail() && tail()->has_placeholder()) return tail()->find_placeholder();+ }+ return 0;+ }*/++ /* not used anymore - remove?+ Selector_Placeholder* Compound_Selector::find_placeholder()+ {+ if (has_placeholder()) {+ for (size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->has_placeholder()) return (*this)[i]->find_placeholder();+ }+ // return this;+ }+ return 0;+ }*/++ /* not used anymore - remove?+ Selector_Placeholder* Selector_Placeholder::find_placeholder()+ {+ return this;+ }*/++ vector<string> Compound_Selector::to_str_vec()+ {+ To_String to_string;+ vector<string> result;+ result.reserve(length());+ for (size_t i = 0, L = length(); i < L; ++i)+ { result.push_back((*this)[i]->perform(&to_string)); }+ return result;+ }++ Compound_Selector* Compound_Selector::minus(Compound_Selector* rhs, Context& ctx)+ {+ To_String to_string(&ctx);+ Compound_Selector* result = new (ctx.mem) Compound_Selector(pstate());++ // not very efficient because it needs to preserve order+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ bool found = false;+ string thisSelector((*this)[i]->perform(&to_string));+ for (size_t j = 0, M = rhs->length(); j < M; ++j)+ {+ if (thisSelector == (*rhs)[j]->perform(&to_string))+ {+ found = true;+ break;+ }+ }+ if (!found) (*result) << (*this)[i];+ }++ return result;+ }++ void Compound_Selector::mergeSources(SourcesSet& sources, Context& ctx)+ {+ for (SourcesSet::iterator iterator = sources.begin(), endIterator = sources.end(); iterator != endIterator; ++iterator) {+ this->sources_.insert((*iterator)->clone(ctx));+ }+ }++ /* not used anymore - remove?+ vector<Compound_Selector*> Complex_Selector::to_vector()+ {+ vector<Compound_Selector*> result;+ Compound_Selector* h = head();+ Complex_Selector* t = tail();+ if (h) result.push_back(h);+ while (t)+ {+ h = t->head();+ t = t->tail();+ if (h) result.push_back(h);+ }+ return result;+ }*/++}+
+ libsass/ast.hpp view
@@ -0,0 +1,2168 @@+#ifndef SASS_AST_H+#define SASS_AST_H++#include <set>+#include <deque>+#include <vector>+#include <string>+#include <sstream>+#include <iostream>+#include <typeinfo>+#include <algorithm>+#include <unordered_map>++#ifdef __clang__++/*+ * There are some overloads used here that trigger the clang overload+ * hiding warning. Specifically:+ *+ * Type type() which hides string type() from Expression+ *+ * and+ *+ * Block* block() which hides virtual Block* block() from Statement+ *+ */++#pragma clang diagnostic push+#pragma clang diagnostic ignored "-Woverloaded-virtual"++#endif++#include "util.hpp"+#include "units.hpp"+#include "context.hpp"+#include "position.hpp"+#include "constants.hpp"+#include "operation.hpp"+#include "position.hpp"+#include "inspect.hpp"+#include "source_map.hpp"+#include "environment.hpp"+#include "error_handling.hpp"+#include "ast_def_macros.hpp"+#include "ast_fwd_decl.hpp"+#include "to_string.hpp"+#include "source_map.hpp"++#include "sass.h"+#include "sass_values.h"+#include "sass_functions.h"++namespace Sass {+ using namespace std;++ //////////////////////////////////////////////////////////+ // Abstract base class for all abstract syntax tree nodes.+ //////////////////////////////////////////////////////////+ class AST_Node {+ ADD_PROPERTY(ParserState, pstate);+ public:+ AST_Node(ParserState pstate)+ : pstate_(pstate)+ { }+ virtual ~AST_Node() = 0;+ // virtual Block* block() { return 0; }+ public:+ Offset off() { return pstate(); };+ Position pos() { return pstate(); };+ ATTACH_OPERATIONS();+ };+ inline AST_Node::~AST_Node() { }+++ //////////////////////////////////////////////////////////////////////+ // Abstract base class for expressions. This side of the AST hierarchy+ // represents elements in value contexts, which exist primarily to be+ // evaluated and returned.+ //////////////////////////////////////////////////////////////////////+ class Expression : public AST_Node {+ public:+ enum Concrete_Type {+ NONE,+ BOOLEAN,+ NUMBER,+ COLOR,+ STRING,+ LIST,+ MAP,+ SELECTOR,+ NULL_VAL,+ NUM_TYPES+ };+ private:+ // expressions in some contexts shouldn't be evaluated+ ADD_PROPERTY(bool, is_delayed);+ ADD_PROPERTY(bool, is_expanded);+ ADD_PROPERTY(bool, is_interpolant);+ ADD_PROPERTY(Concrete_Type, concrete_type);+ public:+ Expression(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : AST_Node(pstate),+ is_delayed_(d),+ is_expanded_(d),+ is_interpolant_(i),+ concrete_type_(ct)+ { }+ virtual operator bool() { return true; }+ virtual ~Expression() { };+ virtual string type() { return ""; /* TODO: raise an error? */ }+ virtual bool is_invisible() { return false; }+ static string type_name() { return ""; }+ virtual bool is_false() { return false; }+ virtual bool operator==( Expression& rhs) const { return false; }+ virtual size_t hash() { return 0; }+ };+}+++/////////////////////////////////////////////////////////////////////////////+// Hash method specializations for unordered_map to work with Sass::Expression+/////////////////////////////////////////////////////////////////////////////++namespace std {+ template<>+ struct hash<Sass::Expression*>+ {+ size_t operator()(Sass::Expression* s) const+ {+ return s->hash();+ }+ };+ template<>+ struct equal_to<Sass::Expression*>+ {+ bool operator()( Sass::Expression* lhs, Sass::Expression* rhs) const+ {+ return *lhs == *rhs;+ }+ };+}++namespace Sass {+ using namespace std;++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like vectors. Uses the+ // "Template Method" design pattern to allow subclasses to adjust their flags+ // when certain objects are pushed.+ /////////////////////////////////////////////////////////////////////////////+ template <typename T>+ class Vectorized {+ vector<T> elements_;+ protected:+ size_t hash_;+ void reset_hash() { hash_ = 0; }+ virtual void adjust_after_pushing(T element) { }+ public:+ Vectorized(size_t s = 0) : elements_(vector<T>())+ { elements_.reserve(s); }+ virtual ~Vectorized() = 0;+ size_t length() const { return elements_.size(); }+ bool empty() const { return elements_.empty(); }+ T last() { return elements_.back(); }+ T& operator[](size_t i) { return elements_[i]; }+ const T& operator[](size_t i) const { return elements_[i]; }+ Vectorized& operator<<(T element)+ {+ reset_hash();+ elements_.push_back(element);+ adjust_after_pushing(element);+ return *this;+ }+ Vectorized& operator+=(Vectorized* v)+ {+ for (size_t i = 0, L = v->length(); i < L; ++i) *this << (*v)[i];+ return *this;+ }+ Vectorized& unshift(T element)+ {+ elements_.insert(elements_.begin(), element);+ return *this;+ }+ vector<T>& elements() { return elements_; }+ const vector<T>& elements() const { return elements_; }+ vector<T>& elements(vector<T>& e) { elements_ = e; return elements_; }+ };+ template <typename T>+ inline Vectorized<T>::~Vectorized() { }++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like a hash table. Uses an+ // extra <vector> internally to maintain insertion order for interation.+ /////////////////////////////////////////////////////////////////////////////+ class Hashed {+ private:+ unordered_map<Expression*, Expression*> elements_;+ vector<Expression*> list_;+ protected:+ size_t hash_;+ Expression* duplicate_key_;+ void reset_hash() { hash_ = 0; }+ void reset_duplicate_key() { duplicate_key_ = 0; }+ virtual void adjust_after_pushing(std::pair<Expression*, Expression*> p) { }+ public:+ Hashed(size_t s = 0) : elements_(unordered_map<Expression*, Expression*>(s)), list_(vector<Expression*>())+ { elements_.reserve(s); list_.reserve(s); reset_duplicate_key(); }+ virtual ~Hashed();+ size_t length() const { return list_.size(); }+ bool empty() const { return list_.empty(); }+ bool has(Expression* k) const { return elements_.count(k) == 1; }+ Expression* at(Expression* k) const { return elements_.at(k); }+ bool has_duplicate_key() const { return duplicate_key_ != 0; }+ Expression* get_duplicate_key() const { return duplicate_key_; }+ Hashed& operator<<(pair<Expression*, Expression*> p)+ {+ reset_hash();++ if (!has(p.first)) list_.push_back(p.first);+ else if (!duplicate_key_) duplicate_key_ = p.first;++ elements_[p.first] = p.second;++ adjust_after_pushing(p);+ return *this;+ }+ Hashed& operator+=(Hashed* h)+ {+ if (length() == 0) {+ this->elements_ = h->elements_;+ this->list_ = h->list_;+ return *this;+ }++ for (auto key : h->keys()) {+ *this << make_pair(key, h->at(key));+ }++ reset_duplicate_key();+ return *this;+ }+ const unordered_map<Expression*, Expression*>& pairs() const { return elements_; }+ const vector<Expression*>& keys() const { return list_; }+ };+ inline Hashed::~Hashed() { }+++ /////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements. This side of the AST hierarchy+ // represents elements in expansion contexts, which exist primarily to be+ // rewritten and macro-expanded.+ /////////////////////////////////////////////////////////////////////////+ class Statement : public AST_Node {+ public:+ enum Statement_Type {+ NONE,+ RULESET,+ MEDIA,+ DIRECTIVE,+ FEATURE,+ ATROOT,+ BUBBLE,+ KEYFRAMERULE+ };+ private:+ ADD_PROPERTY(Block*, block);+ ADD_PROPERTY(Statement_Type, statement_type);+ ADD_PROPERTY(size_t, tabs);+ ADD_PROPERTY(bool, group_end);+ public:+ Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0)+ : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)+ { }+ virtual ~Statement() = 0;+ // needed for rearranging nested rulesets during CSS emission+ virtual bool is_hoistable() { return false; }+ virtual bool is_invisible() { return false; }+ virtual bool bubbles() { return false; }+ virtual Block* block() { return 0; }+ };+ inline Statement::~Statement() { }++ ////////////////////////+ // Blocks of statements.+ ////////////////////////+ class Block : public Statement, public Vectorized<Statement*> {+ ADD_PROPERTY(bool, is_root);+ // needed for properly formatted CSS emission+ ADD_PROPERTY(bool, has_hoistable);+ ADD_PROPERTY(bool, has_non_hoistable);+ protected:+ void adjust_after_pushing(Statement* s)+ {+ if (s->is_hoistable()) has_hoistable_ = true;+ else has_non_hoistable_ = true;+ };+ public:+ Block(ParserState pstate, size_t s = 0, bool r = false)+ : Statement(pstate),+ Vectorized<Statement*>(s),+ is_root_(r), has_hoistable_(false), has_non_hoistable_(false)+ { }+ Block* block() { return this; }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements that contain blocks of statements.+ ////////////////////////////////////////////////////////////////////////+ class Has_Block : public Statement {+ ADD_PROPERTY(Block*, block);+ public:+ Has_Block(ParserState pstate, Block* b)+ : Statement(pstate), block_(b)+ { }+ virtual ~Has_Block() = 0;+ };+ inline Has_Block::~Has_Block() { }++ /////////////////////////////////////////////////////////////////////////////+ // Rulesets (i.e., sets of styles headed by a selector and containing a block+ // of style declarations.+ /////////////////////////////////////////////////////////////////////////////+ class Ruleset : public Has_Block {+ ADD_PROPERTY(Selector*, selector);+ public:+ Ruleset(ParserState pstate, Selector* s, Block* b)+ : Has_Block(pstate, b), selector_(s)+ { statement_type(RULESET); }+ bool is_invisible();+ // nested rulesets need to be hoisted out of their enclosing blocks+ bool is_hoistable() { return true; }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////+ // Nested declaration sets (i.e., namespaced properties).+ /////////////////////////////////////////////////////////+ class Propset : public Has_Block {+ ADD_PROPERTY(String*, property_fragment);+ public:+ Propset(ParserState pstate, String* pf, Block* b = 0)+ : Has_Block(pstate, b), property_fragment_(pf)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////+ // Bubble.+ /////////////////+ class Bubble : public Statement {+ ADD_PROPERTY(Statement*, node);+ ADD_PROPERTY(bool, group_end);+ public:+ Bubble(ParserState pstate, Statement* n, Statement* g = 0, size_t t = 0)+ : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)+ { }+ bool bubbles() { return true; }+ ATTACH_OPERATIONS();+ };++ /////////////////+ // Media queries.+ /////////////////+ class Media_Block : public Has_Block {+ ADD_PROPERTY(List*, media_queries);+ ADD_PROPERTY(Selector*, selector);+ public:+ Media_Block(ParserState pstate, List* mqs, Block* b)+ : Has_Block(pstate, b), media_queries_(mqs), selector_(0)+ { statement_type(MEDIA); }+ Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s)+ : Has_Block(pstate, b), media_queries_(mqs), selector_(s)+ { statement_type(MEDIA); }+ bool bubbles() { return true; }+ bool is_hoistable() { return true; }+ bool is_invisible() {+ bool is_invisible = true;+ for (size_t i = 0, L = block()->length(); i < L && is_invisible; i++)+ is_invisible &= (*block())[i]->is_invisible();+ return is_invisible;+ }+ ATTACH_OPERATIONS();+ };++ ///////////////////+ // Feature queries.+ ///////////////////+ class Feature_Block : public Has_Block {+ ADD_PROPERTY(Feature_Query*, feature_queries);+ ADD_PROPERTY(Selector*, selector);+ public:+ Feature_Block(ParserState pstate, Feature_Query* fqs, Block* b)+ : Has_Block(pstate, b), feature_queries_(fqs), selector_(0)+ { statement_type(FEATURE); }+ bool is_hoistable() { return true; }+ bool bubbles() { return true; }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////////////////////////+ // At-rules -- arbitrary directives beginning with "@" that may have an+ // optional statement block.+ ///////////////////////////////////////////////////////////////////////+ class At_Rule : public Has_Block {+ ADD_PROPERTY(string, keyword);+ ADD_PROPERTY(Selector*, selector);+ ADD_PROPERTY(Expression*, value);+ public:+ At_Rule(ParserState pstate, string kwd, Selector* sel = 0, Block* b = 0)+ : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(0) // set value manually if needed+ { statement_type(DIRECTIVE); }+ bool bubbles() { return is_keyframes() || is_media(); }+ bool is_media() {+ return keyword_.compare("@-webkit-media") == 0 ||+ keyword_.compare("@-moz-media") == 0 ||+ keyword_.compare("@-o-media") == 0 ||+ keyword_.compare("@media") == 0;+ }+ bool is_keyframes() {+ return keyword_.compare("@-webkit-keyframes") == 0 ||+ keyword_.compare("@-moz-keyframes") == 0 ||+ keyword_.compare("@-o-keyframes") == 0 ||+ keyword_.compare("@keyframes") == 0;+ }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////////////////////////+ // Keyframe-rules -- the child blocks of "@keyframes" nodes.+ ///////////////////////////////////////////////////////////////////////+ class Keyframe_Rule : public Has_Block {+ ADD_PROPERTY(Selector*, selector);+ public:+ Keyframe_Rule(ParserState pstate, Block* b)+ : Has_Block(pstate, b), selector_(0)+ { statement_type(KEYFRAMERULE); }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////+ // Declarations -- style rules consisting of a property name and values.+ ////////////////////////////////////////////////////////////////////////+ class Declaration : public Statement {+ ADD_PROPERTY(String*, property);+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(bool, is_important);+ public:+ Declaration(ParserState pstate,+ String* prop, Expression* val, bool i = false)+ : Statement(pstate), property_(prop), value_(val), is_important_(i)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////+ // Assignments -- variable and value.+ /////////////////////////////////////+ class Assignment : public Statement {+ ADD_PROPERTY(string, variable);+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(bool, is_default);+ ADD_PROPERTY(bool, is_global);+ public:+ Assignment(ParserState pstate,+ string var, Expression* val,+ bool is_default = false,+ bool is_global = false)+ : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////////+ // Import directives. CSS and Sass import lists can be intermingled, so it's+ // necessary to store a list of each in an Import node.+ ////////////////////////////////////////////////////////////////////////////+ class Import : public Statement {+ vector<string> files_;+ vector<Expression*> urls_;+ public:+ Import(ParserState pstate)+ : Statement(pstate),+ files_(vector<string>()), urls_(vector<Expression*>())+ { }+ vector<string>& files() { return files_; }+ vector<Expression*>& urls() { return urls_; }+ ATTACH_OPERATIONS();+ };++ class Import_Stub : public Statement {+ ADD_PROPERTY(string, file_name);+ public:+ Import_Stub(ParserState pstate, string f)+ : Statement(pstate), file_name_(f)+ { }+ ATTACH_OPERATIONS();+ };++ //////////////////////////////+ // The Sass `@warn` directive.+ //////////////////////////////+ class Warning : public Statement {+ ADD_PROPERTY(Expression*, message);+ public:+ Warning(ParserState pstate, Expression* msg)+ : Statement(pstate), message_(msg)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////+ // The Sass `@error` directive.+ ///////////////////////////////+ class Error : public Statement {+ ADD_PROPERTY(Expression*, message);+ public:+ Error(ParserState pstate, Expression* msg)+ : Statement(pstate), message_(msg)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////+ // The Sass `@debug` directive.+ ///////////////////////////////+ class Debug : public Statement {+ ADD_PROPERTY(Expression*, value);+ public:+ Debug(ParserState pstate, Expression* val)+ : Statement(pstate), value_(val)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////+ // CSS comments. These may be interpolated.+ ///////////////////////////////////////////+ class Comment : public Statement {+ ADD_PROPERTY(String*, text);+ ADD_PROPERTY(bool, is_important);+ public:+ Comment(ParserState pstate, String* txt, bool is_important)+ : Statement(pstate), text_(txt), is_important_(is_important)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////+ // The Sass `@if` control directive.+ ////////////////////////////////////+ class If : public Statement {+ ADD_PROPERTY(Expression*, predicate);+ ADD_PROPERTY(Block*, consequent);+ ADD_PROPERTY(Block*, alternative);+ public:+ If(ParserState pstate, Expression* pred, Block* con, Block* alt = 0)+ : Statement(pstate), predicate_(pred), consequent_(con), alternative_(alt)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////+ // The Sass `@for` control directive.+ /////////////////////////////////////+ class For : public Has_Block {+ ADD_PROPERTY(string, variable);+ ADD_PROPERTY(Expression*, lower_bound);+ ADD_PROPERTY(Expression*, upper_bound);+ ADD_PROPERTY(bool, is_inclusive);+ public:+ For(ParserState pstate,+ string var, Expression* lo, Expression* hi, Block* b, bool inc)+ : Has_Block(pstate, b),+ variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)+ { }+ ATTACH_OPERATIONS();+ };++ //////////////////////////////////////+ // The Sass `@each` control directive.+ //////////////////////////////////////+ class Each : public Has_Block {+ ADD_PROPERTY(vector<string>, variables);+ ADD_PROPERTY(Expression*, list);+ public:+ Each(ParserState pstate, vector<string> vars, Expression* lst, Block* b)+ : Has_Block(pstate, b), variables_(vars), list_(lst)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////+ // The Sass `@while` control directive.+ ///////////////////////////////////////+ class While : public Has_Block {+ ADD_PROPERTY(Expression*, predicate);+ public:+ While(ParserState pstate, Expression* pred, Block* b)+ : Has_Block(pstate, b), predicate_(pred)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////////+ // The @return directive for use inside SassScript functions.+ /////////////////////////////////////////////////////////////+ class Return : public Statement {+ ADD_PROPERTY(Expression*, value);+ public:+ Return(ParserState pstate, Expression* val)+ : Statement(pstate), value_(val)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////+ // The Sass `@extend` directive.+ ////////////////////////////////+ class Extension : public Statement {+ ADD_PROPERTY(Selector*, selector);+ public:+ Extension(ParserState pstate, Selector* s)+ : Statement(pstate), selector_(s)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////////////////////////+ // Definitions for both mixins and functions. The two cases are distinguished+ // by a type tag.+ /////////////////////////////////////////////////////////////////////////////+ struct Backtrace;+ typedef Environment<AST_Node*> Env;+ typedef const char* Signature;+ typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);+ typedef const char* Signature;+ class Definition : public Has_Block {+ public:+ enum Type { MIXIN, FUNCTION };+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(Parameters*, parameters);+ ADD_PROPERTY(Env*, environment);+ ADD_PROPERTY(Type, type);+ ADD_PROPERTY(Native_Function, native_function);+ ADD_PROPERTY(Sass_Function_Entry, c_function);+ ADD_PROPERTY(void*, cookie);+ ADD_PROPERTY(Context*, ctx);+ ADD_PROPERTY(bool, is_overload_stub);+ ADD_PROPERTY(Signature, signature);+ public:+ Definition(ParserState pstate,+ string n,+ Parameters* params,+ Block* b,+ Context* ctx,+ Type t)+ : Has_Block(pstate, b),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(t),+ native_function_(0),+ c_function_(0),+ cookie_(0),+ ctx_(ctx),+ is_overload_stub_(false),+ signature_(0)+ { }+ Definition(ParserState pstate,+ Signature sig,+ string n,+ Parameters* params,+ Native_Function func_ptr,+ Context* ctx,+ bool overload_stub = false)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(func_ptr),+ c_function_(0),+ cookie_(0),+ ctx_(ctx),+ is_overload_stub_(overload_stub),+ signature_(sig)+ { }+ Definition(ParserState pstate,+ Signature sig,+ string n,+ Parameters* params,+ Sass_Function_Entry c_func,+ Context* ctx,+ bool whatever,+ bool whatever2)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(0),+ c_function_(c_func),+ cookie_(sass_function_get_cookie(c_func)),+ ctx_(ctx),+ is_overload_stub_(false),+ signature_(sig)+ { }+ ATTACH_OPERATIONS();+ };++ //////////////////////////////////////+ // Mixin calls (i.e., `@include ...`).+ //////////////////////////////////////+ class Mixin_Call : public Has_Block {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(Arguments*, arguments);+ public:+ Mixin_Call(ParserState pstate, string n, Arguments* args, Block* b = 0)+ : Has_Block(pstate, b), name_(n), arguments_(args)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////+ // The @content directive for mixin content blocks.+ ///////////////////////////////////////////////////+ class Content : public Statement {+ public:+ Content(ParserState pstate) : Statement(pstate) { }+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////////////////////////+ // Lists of values, both comma- and space-separated (distinguished by a+ // type-tag.) Also used to represent variable-length argument lists.+ ///////////////////////////////////////////////////////////////////////+ class List : public Expression, public Vectorized<Expression*> {+ void adjust_after_pushing(Expression* e) { is_expanded(false); }+ public:+ enum Separator { SPACE, COMMA };+ private:+ ADD_PROPERTY(Separator, separator);+ ADD_PROPERTY(bool, is_arglist);+ public:+ List(ParserState pstate,+ size_t size = 0, Separator sep = SPACE, bool argl = false)+ : Expression(pstate),+ Vectorized<Expression*>(size),+ separator_(sep), is_arglist_(argl)+ { concrete_type(LIST); }+ string type() { return is_arglist_ ? "arglist" : "list"; }+ static string type_name() { return "list"; }+ bool is_invisible() { return !length(); }+ Expression* value_at_index(size_t i);++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ List& l = dynamic_cast<List&>(rhs);+ if (!(l && length() == l.length() && separator() == l.separator())) return false;+ for (size_t i = 0, L = l.length(); i < L; ++i)+ if (!(*(elements()[i]) == *(l[i]))) return false;+ return true;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }++ }++ virtual size_t hash()+ {+ if (hash_ > 0) return hash_;++ hash_ = std::hash<string>()(separator() == COMMA ? "comma" : "space");++ for (size_t i = 0, L = length(); i < L; ++i)+ hash_ ^= (elements()[i])->hash();++ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////////////////////////+ // Key value paris.+ ///////////////////////////////////////////////////////////////////////+ class Map : public Expression, public Hashed {+ void adjust_after_pushing(std::pair<Expression*, Expression*> p) { is_expanded(false); }+ public:+ Map(ParserState pstate,+ size_t size = 0)+ : Expression(pstate),+ Hashed(size)+ { concrete_type(MAP); }+ string type() { return "map"; }+ static string type_name() { return "map"; }+ bool is_invisible() { return !length(); }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Map& m = dynamic_cast<Map&>(rhs);+ if (!(m && length() == m.length())) return false;+ for (auto key : keys())+ if (!(*at(key) == *m.at(key))) return false;+ return true;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ > 0) return hash_;++ for (auto key : keys())+ hash_ ^= key->hash() ^ at(key)->hash();++ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ //////////////////////////////////////////////////////////////////////////+ // Binary expressions. Represents logical, relational, and arithmetic+ // operations. Templatized to avoid large switch statements and repetitive+ // subclassing.+ //////////////////////////////////////////////////////////////////////////+ class Binary_Expression : public Expression {+ public:+ enum Type {+ AND, OR, // logical connectives+ EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations+ ADD, SUB, MUL, DIV, MOD, // arithmetic functions+ NUM_OPS // so we know how big to make the op table+ };+ private:+ ADD_PROPERTY(Type, type);+ ADD_PROPERTY(Expression*, left);+ ADD_PROPERTY(Expression*, right);+ public:+ Binary_Expression(ParserState pstate,+ Type t, Expression* lhs, Expression* rhs)+ : Expression(pstate), type_(t), left_(lhs), right_(rhs)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////////+ // Arithmetic negation (logical negation is just an ordinary function call).+ ////////////////////////////////////////////////////////////////////////////+ class Unary_Expression : public Expression {+ public:+ enum Type { PLUS, MINUS, NOT };+ private:+ ADD_PROPERTY(Type, type);+ ADD_PROPERTY(Expression*, operand);+ public:+ Unary_Expression(ParserState pstate, Type t, Expression* o)+ : Expression(pstate), type_(t), operand_(o)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////+ // Individual argument objects for mixin and function calls.+ ////////////////////////////////////////////////////////////+ class Argument : public Expression {+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(bool, is_rest_argument);+ ADD_PROPERTY(bool, is_keyword_argument);+ size_t hash_;+ public:+ Argument(ParserState pstate, Expression* val, string n = "", bool rest = false, bool keyword = false)+ : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)+ {+ if (!name_.empty() && is_rest_argument_) {+ error("variable-length argument may not be passed by name", pstate);+ }+ }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Argument& m = dynamic_cast<Argument&>(rhs);+ if (!(m && name() == m.name())) return false;+ return *value() == *value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ > 0) return hash_;++ hash_ = std::hash<string>()(name()) ^ value()->hash();++ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////+ // Argument lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all ordinal arguments precede all+ // named arguments).+ ////////////////////////////////////////////////////////////////////////+ class Arguments : public Expression, public Vectorized<Argument*> {+ ADD_PROPERTY(bool, has_named_arguments);+ ADD_PROPERTY(bool, has_rest_argument);+ ADD_PROPERTY(bool, has_keyword_argument);+ protected:+ void adjust_after_pushing(Argument* a)+ {+ if (!a->name().empty()) {+ if (has_rest_argument_ || has_keyword_argument_) {+ error("named arguments must precede variable-length argument", a->pstate());+ }+ has_named_arguments_ = true;+ }+ else if (a->is_rest_argument()) {+ if (has_rest_argument_) {+ error("functions and mixins may only be called with one variable-length argument", a->pstate());+ }+ if (has_keyword_argument_) {+ error("only keyword arguments may follow variable arguments", a->pstate());+ }+ has_rest_argument_ = true;+ }+ else if (a->is_keyword_argument()) {+ if (has_keyword_argument_) {+ error("functions and mixins may only be called with one keyword argument", a->pstate());+ }+ has_keyword_argument_ = true;+ }+ else {+ if (has_rest_argument_) {+ error("ordinal arguments must precede variable-length arguments", a->pstate());+ }+ if (has_named_arguments_) {+ error("ordinal arguments must precede named arguments", a->pstate());+ }+ }+ }+ public:+ Arguments(ParserState pstate)+ : Expression(pstate),+ Vectorized<Argument*>(),+ has_named_arguments_(false),+ has_rest_argument_(false),+ has_keyword_argument_(false)+ { }+ ATTACH_OPERATIONS();+ };++ //////////////////+ // Function calls.+ //////////////////+ class Function_Call : public Expression {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(Arguments*, arguments);+ ADD_PROPERTY(void*, cookie);+ size_t hash_;+ public:+ Function_Call(ParserState pstate, string n, Arguments* args, void* cookie)+ : Expression(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0)+ { concrete_type(STRING); }+ Function_Call(ParserState pstate, string n, Arguments* args)+ : Expression(pstate), name_(n), arguments_(args), cookie_(0), hash_(0)+ { concrete_type(STRING); }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Function_Call& m = dynamic_cast<Function_Call&>(rhs);+ if (!(m && name() == m.name())) return false;+ if (!(m && arguments()->length() == m.arguments()->length())) return false;+ for (size_t i =0, L = arguments()->length(); i < L; ++i)+ if (!((*arguments())[i] == (*m.arguments())[i])) return false;+ return true;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ > 0) return hash_;++ hash_ = std::hash<string>()(name());+ for (auto argument : arguments()->elements())+ hash_ ^= argument->hash();++ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ /////////////////////////+ // Function call schemas.+ /////////////////////////+ class Function_Call_Schema : public Expression {+ ADD_PROPERTY(String*, name);+ ADD_PROPERTY(Arguments*, arguments);+ public:+ Function_Call_Schema(ParserState pstate, String* n, Arguments* args)+ : Expression(pstate), name_(n), arguments_(args)+ { concrete_type(STRING); }+ ATTACH_OPERATIONS();+ };++ ///////////////////////+ // Variable references.+ ///////////////////////+ class Variable : public Expression {+ ADD_PROPERTY(string, name);+ public:+ Variable(ParserState pstate, string n)+ : Expression(pstate), name_(n)+ { }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Variable& e = dynamic_cast<Variable&>(rhs);+ return e && name() == e.name();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ return std::hash<string>()(name());+ }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////////+ // Textual (i.e., unevaluated) numeric data. Variants are distinguished with+ // a type tag.+ ////////////////////////////////////////////////////////////////////////////+ class Textual : public Expression {+ public:+ enum Type { NUMBER, PERCENTAGE, DIMENSION, HEX };+ private:+ ADD_PROPERTY(Type, type);+ ADD_PROPERTY(string, value);+ size_t hash_;+ public:+ Textual(ParserState pstate, Type t, string val)+ : Expression(pstate, true), type_(t), value_(val),+ hash_(0)+ { }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Textual& e = dynamic_cast<Textual&>(rhs);+ return e && value() == e.value() && type() == e.type();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) hash_ = std::hash<string>()(value_) ^ std::hash<int>()(type_);+ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////+ // Numbers, percentages, dimensions, and colors.+ ////////////////////////////////////////////////+ class Number : public Expression {+ ADD_PROPERTY(double, value);+ ADD_PROPERTY(bool, zero);+ vector<string> numerator_units_;+ vector<string> denominator_units_;+ size_t hash_;+ public:+ Number(ParserState pstate, double val, string u = "", bool zero = true)+ : Expression(pstate),+ value_(val),+ zero_(zero),+ numerator_units_(vector<string>()),+ denominator_units_(vector<string>()),+ hash_(0)+ {+ if (!u.empty()) numerator_units_.push_back(u);+ concrete_type(NUMBER);+ }+ bool zero() { return zero_; }+ vector<string>& numerator_units() { return numerator_units_; }+ vector<string>& denominator_units() { return denominator_units_; }+ string type() { return "number"; }+ static string type_name() { return "number"; }+ string unit() const+ {+ stringstream u;+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ if (i) u << '*';+ u << numerator_units_[i];+ }+ if (!denominator_units_.empty()) u << '/';+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ if (i) u << '*';+ u << denominator_units_[i];+ }+ return u.str();+ }+ bool is_unitless()+ { return numerator_units_.empty() && denominator_units_.empty(); }+ void normalize(string to = "")+ {+ // (multiple passes because I'm too tired to think up something clever)+ // Find a unit to convert everything to, if one isn't provided.+ if (to.empty()) {+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ string u(numerator_units_[i]);+ if (string_to_unit(u) == INCOMMENSURABLE) {+ continue;+ }+ else {+ to = u;+ break;+ }+ }+ }+ if (to.empty()) {+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ string u(denominator_units_[i]);+ if (string_to_unit(u) == INCOMMENSURABLE) {+ continue;+ }+ else {+ to = u;+ break;+ }+ }+ }+ // Now loop through again and do all the conversions.+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ string from(numerator_units_[i]);+ if (string_to_unit(from) == INCOMMENSURABLE) continue;+ value_ *= conversion_factor(from, to);+ numerator_units_[i] = to;+ }+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ string from(denominator_units_[i]);+ if (string_to_unit(from) == INCOMMENSURABLE) continue;+ value_ /= conversion_factor(from, to);+ denominator_units_[i] = to;+ }+ // Now divide out identical units in the numerator and denominator.+ vector<string> ncopy;+ ncopy.reserve(numerator_units_.size());+ for (vector<string>::iterator n = numerator_units_.begin();+ n != numerator_units_.end();+ ++n) {+ vector<string>::iterator d = find(denominator_units_.begin(),+ denominator_units_.end(),+ *n);+ if (d != denominator_units_.end()) {+ denominator_units_.erase(d);+ }+ else {+ ncopy.push_back(*n);+ }+ }+ numerator_units_ = ncopy;+ // Sort the units to make them pretty and, well, normal.+ sort(numerator_units_.begin(), numerator_units_.end());+ sort(denominator_units_.begin(), denominator_units_.end());+ }+ // useful for making one number compatible with another+ string find_convertible_unit() const+ {+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ string u(numerator_units_[i]);+ if (string_to_unit(u) != INCOMMENSURABLE) return u;+ }+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ string u(denominator_units_[i]);+ if (string_to_unit(u) != INCOMMENSURABLE) return u;+ }+ return string();+ }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Number& e(dynamic_cast<Number&>(rhs));+ if (!e) return false;+ e.normalize(find_convertible_unit());+ return unit() == e.unit() && value() == e.value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) hash_ = std::hash<double>()(value_);+ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ //////////+ // Colors.+ //////////+ class Color : public Expression {+ ADD_PROPERTY(double, r);+ ADD_PROPERTY(double, g);+ ADD_PROPERTY(double, b);+ ADD_PROPERTY(double, a);+ ADD_PROPERTY(bool, sixtuplet);+ ADD_PROPERTY(string, disp);+ size_t hash_;+ public:+ Color(ParserState pstate, double r, double g, double b, double a = 1, bool sixtuplet = true, const string disp = "")+ : Expression(pstate), r_(r), g_(g), b_(b), a_(a), sixtuplet_(sixtuplet), disp_(disp),+ hash_(0)+ { concrete_type(COLOR); }+ string type() { return "color"; }+ static string type_name() { return "color"; }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Color& c = (dynamic_cast<Color&>(rhs));+ return c && r() == c.r() && g() == c.g() && b() == c.b() && a() == c.a();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) hash_ = std::hash<double>()(r_) ^ std::hash<double>()(g_) ^ std::hash<double>()(b_) ^ std::hash<double>()(a_);+ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ////////////+ // Booleans.+ ////////////+ class Boolean : public Expression {+ ADD_PROPERTY(bool, value);+ size_t hash_;+ public:+ Boolean(ParserState pstate, bool val)+ : Expression(pstate), value_(val),+ hash_(0)+ { concrete_type(BOOLEAN); }+ virtual operator bool() { return value_; }+ string type() { return "bool"; }+ static string type_name() { return "bool"; }+ virtual bool is_false() { return !value_; }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ Boolean& e = dynamic_cast<Boolean&>(rhs);+ return e && value() == e.value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) hash_ = std::hash<bool>()(value_);+ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for Sass string values. Includes interpolated and+ // "flat" strings.+ ////////////////////////////////////////////////////////////////////////+ class String : public Expression {+ ADD_PROPERTY(bool, sass_fix_1291);+ public:+ String(ParserState pstate, bool delayed = false, bool sass_fix_1291 = false)+ : Expression(pstate, delayed), sass_fix_1291_(sass_fix_1291)+ { concrete_type(STRING); }+ static string type_name() { return "string"; }+ virtual ~String() = 0;+ ATTACH_OPERATIONS();+ };+ inline String::~String() { };++ ///////////////////////////////////////////////////////////////////////+ // Interpolated strings. Meant to be reduced to flat strings during the+ // evaluation phase.+ ///////////////////////////////////////////////////////////////////////+ class String_Schema : public String, public Vectorized<Expression*> {+ ADD_PROPERTY(bool, has_interpolants);+ size_t hash_;+ public:+ String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)+ : String(pstate), Vectorized<Expression*>(size), has_interpolants_(has_interpolants), hash_(0)+ { }+ string type() { return "string"; }+ static string type_name() { return "string"; }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ String_Schema& e = dynamic_cast<String_Schema&>(rhs);+ if (!(e && length() == e.length())) return false;+ for (size_t i = 0, L = length(); i < L; ++i)+ if (!((*this)[i] == e[i])) return false;+ return true;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ > 0) return hash_;++ for (auto string : elements())+ hash_ ^= string->hash();++ return hash_;+ }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////+ // Flat strings -- the lowest level of raw textual data.+ ////////////////////////////////////////////////////////+ class String_Constant : public String {+ ADD_PROPERTY(char, quote_mark);+ ADD_PROPERTY(string, value);+ protected:+ size_t hash_;+ public:+ String_Constant(ParserState pstate, string val)+ : String(pstate), quote_mark_(0), value_(read_css_string(val)), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg)+ : String(pstate), quote_mark_(0), value_(read_css_string(string(beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg, const char* end)+ : String(pstate), quote_mark_(0), value_(read_css_string(string(beg, end-beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const Token& tok)+ : String(pstate), quote_mark_(0), value_(read_css_string(string(tok.begin, tok.end))), hash_(0)+ { }+ string type() { return "string"; }+ static string type_name() { return "string"; }++ virtual bool operator==(Expression& rhs) const+ {+ try+ {+ String_Constant& e = dynamic_cast<String_Constant&>(rhs);+ return e && value_ == e.value_;+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) hash_ = std::hash<string>()(value_);+ return hash_;+ }++ // static char auto_quote() { return '*'; }+ static char double_quote() { return '"'; }+ static char single_quote() { return '\''; }++ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////+ // Possibly quoted string (unquote on instantiation)+ ////////////////////////////////////////////////////////+ class String_Quoted : public String_Constant {+ public:+ String_Quoted(ParserState pstate, string val)+ : String_Constant(pstate, val)+ {+ value_ = unquote(value_, "e_mark_);+ }+ ATTACH_OPERATIONS();+ };++ /////////////////+ // Media queries.+ /////////////////+ class Media_Query : public Expression,+ public Vectorized<Media_Query_Expression*> {+ ADD_PROPERTY(String*, media_type);+ ADD_PROPERTY(bool, is_negated);+ ADD_PROPERTY(bool, is_restricted);+ public:+ Media_Query(ParserState pstate,+ String* t = 0, size_t s = 0, bool n = false, bool r = false)+ : Expression(pstate), Vectorized<Media_Query_Expression*>(s),+ media_type_(t), is_negated_(n), is_restricted_(r)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////+ // Media expressions (for use inside media queries).+ ////////////////////////////////////////////////////+ class Media_Query_Expression : public Expression {+ ADD_PROPERTY(Expression*, feature);+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(bool, is_interpolated);+ public:+ Media_Query_Expression(ParserState pstate,+ Expression* f, Expression* v, bool i = false)+ : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)+ { }+ ATTACH_OPERATIONS();+ };++ ///////////////////+ // Feature queries.+ ///////////////////+ class Feature_Query : public Expression, public Vectorized<Feature_Query_Condition*> {+ public:+ Feature_Query(ParserState pstate, size_t s = 0)+ : Expression(pstate), Vectorized<Feature_Query_Condition*>(s)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////+ // Feature expressions (for use inside feature queries).+ ////////////////////////////////////////////////////////+ class Feature_Query_Condition : public Expression, public Vectorized<Feature_Query_Condition*> {+ public:+ enum Operand { NONE, AND, OR, NOT };+ private:+ ADD_PROPERTY(String*, feature);+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(Operand, operand);+ ADD_PROPERTY(bool, is_root);+ public:+ Feature_Query_Condition(ParserState pstate, size_t s = 0, String* f = 0,+ Expression* v = 0, Operand o = NONE, bool r = false)+ : Expression(pstate), Vectorized<Feature_Query_Condition*>(s),+ feature_(f), value_(v), operand_(o), is_root_(r)+ { }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////+ // At root expressions (for use inside @at-root).+ /////////////////////////////////////////////////+ class At_Root_Expression : public Expression {+ private:+ ADD_PROPERTY(String*, feature);+ ADD_PROPERTY(Expression*, value);+ ADD_PROPERTY(bool, is_interpolated);+ public:+ At_Root_Expression(ParserState pstate, String* f = 0, Expression* v = 0, bool i = false)+ : Expression(pstate), feature_(f), value_(v), is_interpolated_(i)+ { }+ bool exclude(string str)+ {+ To_String to_string;+ bool with = feature() && unquote(feature()->perform(&to_string)).compare("with") == 0;+ List* l = static_cast<List*>(value());+ string v;++ if (with)+ {+ if (!l || l->length() == 0) return str.compare("rule") != 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->perform(&to_string));+ if (v.compare("all") == 0 || v == str) return false;+ }+ return true;+ }+ else+ {+ if (!l || !l->length()) return str.compare("rule") == 0;+ for (size_t i = 0, L = l->length(); i < L; ++i)+ {+ v = unquote((*l)[i]->perform(&to_string));+ if (v.compare("all") == 0 || v == str) return true;+ }+ return false;+ }+ }+ ATTACH_OPERATIONS();+ };++ ///////////+ // At-root.+ ///////////+ class At_Root_Block : public Has_Block {+ ADD_PROPERTY(At_Root_Expression*, expression);+ public:+ At_Root_Block(ParserState pstate, Block* b = 0, At_Root_Expression* e = 0)+ : Has_Block(pstate, b), expression_(e)+ { statement_type(ATROOT); }+ bool is_hoistable() { return true; }+ bool bubbles() { return true; }+ bool exclude_node(Statement* s) {+ if (s->statement_type() == Statement::DIRECTIVE)+ {+ return expression()->exclude(static_cast<At_Rule*>(s)->keyword().erase(0, 1));+ }+ if (s->statement_type() == Statement::MEDIA)+ {+ return expression()->exclude("media");+ }+ if (s->statement_type() == Statement::RULESET)+ {+ return expression()->exclude("rule");+ }+ if (s->statement_type() == Statement::FEATURE)+ {+ return expression()->exclude("supports");+ }+ if (static_cast<At_Rule*>(s)->is_keyframes())+ {+ return expression()->exclude("keyframes");+ }+ return false;+ }+ ATTACH_OPERATIONS();+ };++ //////////////////+ // The null value.+ //////////////////+ class Null : public Expression {+ public:+ Null(ParserState pstate) : Expression(pstate) { concrete_type(NULL_VAL); }+ string type() { return "null"; }+ static string type_name() { return "null"; }+ bool is_invisible() { return true; }+ operator bool() { return false; }+ bool is_false() { return true; }++ virtual bool operator==(Expression& rhs) const+ {+ return rhs.concrete_type() == NULL_VAL;+ }++ virtual size_t hash()+ {+ return 0;+ }++ ATTACH_OPERATIONS();+ };++ /////////////////////////////////+ // Thunks for delayed evaluation.+ /////////////////////////////////+ class Thunk : public Expression {+ ADD_PROPERTY(Expression*, expression);+ ADD_PROPERTY(Env*, environment);+ public:+ Thunk(ParserState pstate, Expression* exp, Env* env = 0)+ : Expression(pstate), expression_(exp), environment_(env)+ { }+ };++ /////////////////////////////////////////////////////////+ // Individual parameter objects for mixins and functions.+ /////////////////////////////////////////////////////////+ class Parameter : public AST_Node {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(Expression*, default_value);+ ADD_PROPERTY(bool, is_rest_parameter);+ public:+ Parameter(ParserState pstate,+ string n, Expression* def = 0, bool rest = false)+ : AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)+ {+ if (default_value_ && is_rest_parameter_) {+ error("variable-length parameter may not have a default value", pstate);+ }+ }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////////////////////+ // Parameter lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all optional parameters follow all+ // required parameters).+ /////////////////////////////////////////////////////////////////////////+ class Parameters : public AST_Node, public Vectorized<Parameter*> {+ ADD_PROPERTY(bool, has_optional_parameters);+ ADD_PROPERTY(bool, has_rest_parameter);+ protected:+ void adjust_after_pushing(Parameter* p)+ {+ if (p->default_value()) {+ if (has_rest_parameter_) {+ error("optional parameters may not be combined with variable-length parameters", p->pstate());+ }+ has_optional_parameters_ = true;+ }+ else if (p->is_rest_parameter()) {+ if (has_rest_parameter_) {+ error("functions and mixins cannot have more than one variable-length parameter", p->pstate());+ }+ has_rest_parameter_ = true;+ }+ else {+ if (has_rest_parameter_) {+ error("required parameters must precede variable-length parameters", p->pstate());+ }+ if (has_optional_parameters_) {+ error("required parameters must precede optional parameters", p->pstate());+ }+ }+ }+ public:+ Parameters(ParserState pstate)+ : AST_Node(pstate),+ Vectorized<Parameter*>(),+ has_optional_parameters_(false),+ has_rest_parameter_(false)+ { }+ ATTACH_OPERATIONS();+ };++ //////////////////////////////////////////////////////////////////////////////////////////+ // Additional method on Lists to retrieve values directly or from an encompassed Argument.+ //////////////////////////////////////////////////////////////////////////////////////////+ inline Expression* List::value_at_index(size_t i) { return is_arglist_ ? ((Argument*)(*this)[i])->value() : (*this)[i]; }++ ////////////+ // The Parent Selector Expression.+ ////////////+ class Parent_Selector : public Expression {+ ADD_PROPERTY(Selector*, selector);+ public:+ Parent_Selector(ParserState pstate, Selector* r = 0)+ : Expression(pstate), selector_(r)+ { concrete_type(SELECTOR); }+ virtual Selector* selector() { return selector_; }+ string type() { return "selector"; }+ static string type_name() { return "selector"; }++ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////+ // Abstract base class for CSS selectors.+ /////////////////////////////////////////+ class Selector : public AST_Node {+ ADD_PROPERTY(bool, has_reference);+ ADD_PROPERTY(bool, has_placeholder);+ // line break before list separator+ ADD_PROPERTY(bool, has_line_feed);+ // line break after list separator+ ADD_PROPERTY(bool, has_line_break);+ // maybe we have optional flag+ ADD_PROPERTY(bool, is_optional);+ // parent block pointers+ ADD_PROPERTY(Block*, last_block);+ ADD_PROPERTY(Media_Block*, media_block);+ public:+ Selector(ParserState pstate, bool r = false, bool h = false)+ : AST_Node(pstate),+ has_reference_(r),+ has_placeholder_(h),+ has_line_feed_(false),+ has_line_break_(false),+ is_optional_(false),+ media_block_(0)+ { }+ virtual ~Selector() = 0;+ // virtual Selector_Placeholder* find_placeholder();+ virtual unsigned long specificity() {+ return Constants::Specificity_Universal;+ };+ };+ inline Selector::~Selector() { }++ /////////////////////////////////////////////////////////////////////////+ // Interpolated selectors -- the interpolated String will be expanded and+ // re-parsed into a normal selector class.+ /////////////////////////////////////////////////////////////////////////+ class Selector_Schema : public Selector {+ ADD_PROPERTY(String*, contents);+ public:+ Selector_Schema(ParserState pstate, String* c)+ : Selector(pstate), contents_(c)+ { }+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////+ // Abstract base class for simple selectors.+ ////////////////////////////////////////////+ class Simple_Selector : public Selector {+ public:+ Simple_Selector(ParserState pstate)+ : Selector(pstate)+ { }+ virtual ~Simple_Selector() = 0;+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ virtual bool is_pseudo_element() { return false; }+ virtual bool is_pseudo_class() { return false; }++ bool operator==(const Simple_Selector& rhs) const;+ inline bool operator!=(const Simple_Selector& rhs) const { return !(*this == rhs); }++ bool operator<(const Simple_Selector& rhs) const;+ };+ inline Simple_Selector::~Simple_Selector() { }++ /////////////////////////////////////+ // Parent references (i.e., the "&").+ /////////////////////////////////////+ class Selector_Reference : public Simple_Selector {+ ADD_PROPERTY(Selector*, selector);+ public:+ Selector_Reference(ParserState pstate, Selector* r = 0)+ : Simple_Selector(pstate), selector_(r)+ { has_reference(true); }+ virtual unsigned long specificity()+ {+ if (!selector()) return 0;+ return selector()->specificity();+ }+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////////////////////+ // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.+ /////////////////////////////////////////////////////////////////////////+ class Selector_Placeholder : public Simple_Selector {+ ADD_PROPERTY(string, name);+ public:+ Selector_Placeholder(ParserState pstate, string n)+ : Simple_Selector(pstate), name_(n)+ { has_placeholder(true); }+ // virtual Selector_Placeholder* find_placeholder();+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////////////////////////+ // Type selectors (and the universal selector) -- e.g., div, span, *.+ /////////////////////////////////////////////////////////////////////+ class Type_Selector : public Simple_Selector {+ ADD_PROPERTY(string, name);+ public:+ Type_Selector(ParserState pstate, string n)+ : Simple_Selector(pstate), name_(n)+ { }+ virtual unsigned long specificity()+ {+ // ToDo: What is the specificity of the star selector?+ if (name() == "*") return Constants::Specificity_Universal;+ else return Constants::Specificity_Type;+ }+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////+ // Selector qualifiers -- i.e., classes and ids.+ ////////////////////////////////////////////////+ class Selector_Qualifier : public Simple_Selector {+ ADD_PROPERTY(string, name);+ public:+ Selector_Qualifier(ParserState pstate, string n)+ : Simple_Selector(pstate), name_(n)+ { }+ virtual unsigned long specificity()+ {+ if (name()[0] == '#') return Constants::Specificity_ID;+ if (name()[0] == '.') return Constants::Specificity_Class;+ else return Constants::Specificity_Type;+ }+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ ATTACH_OPERATIONS();+ };++ ///////////////////////////////////////////////////+ // Attribute selectors -- e.g., [src*=".jpg"], etc.+ ///////////////////////////////////////////////////+ class Attribute_Selector : public Simple_Selector {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(string, matcher);+ ADD_PROPERTY(String*, value); // might be interpolated+ public:+ Attribute_Selector(ParserState pstate, string n, string m, String* v)+ : Simple_Selector(pstate), name_(n), matcher_(m), value_(v)+ { }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_Attr;+ }+ ATTACH_OPERATIONS();+ };++ //////////////////////////////////////////////////////////////////+ // Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.+ //////////////////////////////////////////////////////////////////+ /* '::' starts a pseudo-element, ':' a pseudo-class */+ /* Except :first-line, :first-letter, :before and :after */+ /* Note that pseudo-elements are restricted to one per selector */+ /* and occur only in the last simple_selector_sequence. */+ inline bool is_pseudo_class_element(const string& name)+ {+ return name == ":before" ||+ name == ":after" ||+ name == ":first-line" ||+ name == ":first-letter";+ }++ class Pseudo_Selector : public Simple_Selector {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(String*, expression);+ public:+ Pseudo_Selector(ParserState pstate, string n, String* expr = 0)+ : Simple_Selector(pstate), name_(n), expression_(expr)+ { }++ // A pseudo-class always consists of a "colon" (:) followed by the name+ // of the pseudo-class and optionally by a value between parentheses.+ virtual bool is_pseudo_class()+ {+ return (name_[0] == ':' && name_[1] != ':')+ && ! is_pseudo_class_element(name_);+ }++ // A pseudo-element is made of two colons (::) followed by the name.+ // The `::` notation is introduced by the current document in order to+ // establish a discrimination between pseudo-classes and pseudo-elements.+ // For compatibility with existing style sheets, user agents must also+ // accept the previous one-colon notation for pseudo-elements introduced+ // in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and+ // :after). This compatibility is not allowed for the new pseudo-elements+ // introduced in this specification.+ virtual bool is_pseudo_element()+ {+ return (name_[0] == ':' && name_[1] == ':')+ || is_pseudo_class_element(name_);+ }+ virtual unsigned long specificity()+ {+ if (is_pseudo_element())+ return Constants::Specificity_Type;+ return Constants::Specificity_Pseudo;+ }+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ ATTACH_OPERATIONS();+ };++ /////////////////////////////////////////////////+ // Wrapped selector -- pseudo selector that takes a list of selectors as argument(s) e.g., :not(:first-of-type), :-moz-any(ol p.blah, ul, menu, dir)+ /////////////////////////////////////////////////+ class Wrapped_Selector : public Simple_Selector {+ ADD_PROPERTY(string, name);+ ADD_PROPERTY(Selector*, selector);+ public:+ Wrapped_Selector(ParserState pstate, string n, Selector* sel)+ : Simple_Selector(pstate), name_(n), selector_(sel)+ { }+ // Selectors inside the negation pseudo-class are counted like any+ // other, but the negation itself does not count as a pseudo-class.+ virtual unsigned long specificity()+ {+ return selector_ ? selector_->specificity() : 0;+ }+ ATTACH_OPERATIONS();+ };++ struct Complex_Selector_Pointer_Compare {+ bool operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const;+ };++ ////////////////////////////////////////////////////////////////////////////+ // Simple selector sequences. Maintains flags indicating whether it contains+ // any parent references or placeholders, to simplify expansion.+ ////////////////////////////////////////////////////////////////////////////+ typedef set<Complex_Selector*, Complex_Selector_Pointer_Compare> SourcesSet;+ class Compound_Selector : public Selector, public Vectorized<Simple_Selector*> {+ private:+ SourcesSet sources_;+ protected:+ void adjust_after_pushing(Simple_Selector* s)+ {+ if (s->has_reference()) has_reference(true);+ if (s->has_placeholder()) has_placeholder(true);+ }+ public:+ Compound_Selector(ParserState pstate, size_t s = 0)+ : Selector(pstate),+ Vectorized<Simple_Selector*>(s)+ { }++ Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx);+ // virtual Selector_Placeholder* find_placeholder();+ Simple_Selector* base()+ {+ // Implement non-const in terms of const. Safe to const_cast since this method is non-const+ return const_cast<Simple_Selector*>(static_cast<const Compound_Selector*>(this)->base());+ }+ const Simple_Selector* base() const {+ if (length() > 0 && typeid(*(*this)[0]) == typeid(Type_Selector))+ return (*this)[0];+ return 0;+ }+ bool is_superselector_of(Compound_Selector* rhs);+ virtual unsigned long specificity()+ {+ int sum = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ { sum += (*this)[i]->specificity(); }+ return sum;+ }+ bool is_empty_reference()+ {+ return length() == 1 &&+ typeid(*(*this)[0]) == typeid(Selector_Reference) &&+ !static_cast<Selector_Reference*>((*this)[0])->selector();+ }+ vector<string> to_str_vec(); // sometimes need to convert to a flat "by-value" data structure++ bool operator<(const Compound_Selector& rhs) const;++ bool operator==(const Compound_Selector& rhs) const;+ inline bool operator!=(const Compound_Selector& rhs) const { return !(*this == rhs); }++ SourcesSet& sources() { return sources_; }+ void clearSources() { sources_.clear(); }+ void mergeSources(SourcesSet& sources, Context& ctx);++ Compound_Selector* clone(Context&) const; // does not clone the Simple_Selector*s++ Compound_Selector* minus(Compound_Selector* rhs, Context& ctx);+ ATTACH_OPERATIONS();+ };++ ////////////////////////////////////////////////////////////////////////////+ // General selectors -- i.e., simple sequences combined with one of the four+ // CSS selector combinators (">", "+", "~", and whitespace). Essentially a+ // linked list.+ ////////////////////////////////////////////////////////////////////////////+ class Complex_Selector : public Selector {+ public:+ enum Combinator { ANCESTOR_OF, PARENT_OF, PRECEDES, ADJACENT_TO };+ private:+ ADD_PROPERTY(Combinator, combinator);+ ADD_PROPERTY(Compound_Selector*, head);+ ADD_PROPERTY(Complex_Selector*, tail);+ public:+ Complex_Selector(ParserState pstate,+ Combinator c,+ Compound_Selector* h,+ Complex_Selector* t)+ : Selector(pstate), combinator_(c), head_(h), tail_(t)+ {+ if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true);+ if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true);+ }+ Compound_Selector* base();+ Complex_Selector* context(Context&);+ Complex_Selector* innermost();+ size_t length();+ bool is_superselector_of(Compound_Selector*);+ bool is_superselector_of(Complex_Selector*);+ // virtual Selector_Placeholder* find_placeholder();+ Combinator clear_innermost();+ void set_innermost(Complex_Selector*, Combinator);+ virtual unsigned long specificity() const+ {+ int sum = 0;+ if (head()) sum += head()->specificity();+ if (tail()) sum += tail()->specificity();+ return sum;+ }+ bool operator<(const Complex_Selector& rhs) const;+ bool operator==(const Complex_Selector& rhs) const;+ inline bool operator!=(const Complex_Selector& rhs) const { return !(*this == rhs); }+ SourcesSet sources()+ {+ //s = Set.new+ //seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}+ //s++ SourcesSet srcs;++ Compound_Selector* pHead = head();+ Complex_Selector* pTail = tail();++ if (pHead) {+ SourcesSet& headSources = pHead->sources();+ srcs.insert(headSources.begin(), headSources.end());+ }++ if (pTail) {+ SourcesSet tailSources = pTail->sources();+ srcs.insert(tailSources.begin(), tailSources.end());+ }++ return srcs;+ }+ void addSources(SourcesSet& sources, Context& ctx) {+ // members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}+ Complex_Selector* pIter = this;+ while (pIter) {+ Compound_Selector* pHead = pIter->head();++ if (pHead) {+ pHead->mergeSources(sources, ctx);+ }++ pIter = pIter->tail();+ }+ }+ void clearSources() {+ Complex_Selector* pIter = this;+ while (pIter) {+ Compound_Selector* pHead = pIter->head();++ if (pHead) {+ pHead->clearSources();+ }++ pIter = pIter->tail();+ }+ }+ Complex_Selector* clone(Context&) const; // does not clone Compound_Selector*s+ Complex_Selector* cloneFully(Context&) const; // clones Compound_Selector*s+ // vector<Compound_Selector*> to_vector();+ ATTACH_OPERATIONS();+ };++ typedef deque<Complex_Selector*> ComplexSelectorDeque;++ ///////////////////////////////////+ // Comma-separated selector groups.+ ///////////////////////////////////+ class Selector_List : public Selector, public Vectorized<Complex_Selector*> {+#ifdef DEBUG+ ADD_PROPERTY(string, mCachedSelector);+#endif+ ADD_PROPERTY(vector<string>, wspace);+ protected:+ void adjust_after_pushing(Complex_Selector* c);+ public:+ Selector_List(ParserState pstate, size_t s = 0)+ : Selector(pstate), Vectorized<Complex_Selector*>(s), wspace_(0)+ { }+ // virtual Selector_Placeholder* find_placeholder();+ virtual unsigned long specificity()+ {+ unsigned long sum = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ { sum += (*this)[i]->specificity(); }+ return sum;+ }+ // vector<Complex_Selector*> members() { return elements_; }+ ATTACH_OPERATIONS();+ };++ inline bool Ruleset::is_invisible() {+ bool is_invisible = true;+ Selector_List* sl = static_cast<Selector_List*>(selector());+ for (size_t i = 0, L = sl->length(); i < L && is_invisible; ++i)+ is_invisible &= (*sl)[i]->has_placeholder();+ return is_invisible;+ }+++ template<typename SelectorType>+ bool selectors_equal(const SelectorType& one, const SelectorType& two, bool simpleSelectorOrderDependent) {+ // Test for equality among selectors while differentiating between checks that demand the underlying Simple_Selector+ // ordering to be the same or not. This works because operator< (which doesn't make a whole lot of sense for selectors, but+ // is required for proper stl collection ordering) is implemented using string comparision. This gives stable sorting+ // behavior, and can be used to determine if the selectors would have exactly idential output. operator== matches the+ // ruby sass implementations for eql, which sometimes perform order independent comparisions (like set comparisons of the+ // members of a SimpleSequence (Compound_Selector)).+ //+ // Due to the reliance on operator== and operater< behavior, this templated method is currently only intended for+ // use with Compound_Selector and Complex_Selector objects.+ if (simpleSelectorOrderDependent) {+ return !(one < two) && !(two < one);+ } else {+ return one == two;+ }+ }++}++#ifdef __clang__++#pragma clang diagnostic pop++#endif++#endif
+ libsass/ast_def_macros.hpp view
@@ -0,0 +1,21 @@+#ifndef SASS_AST_DEF_MACROS_H+#define SASS_AST_DEF_MACROS_H++#define ATTACH_OPERATIONS()\+virtual void perform(Operation<void>* op) { (*op)(this); }\+virtual AST_Node* perform(Operation<AST_Node*>* op) { return (*op)(this); }\+virtual Statement* perform(Operation<Statement*>* op) { return (*op)(this); }\+virtual Expression* perform(Operation<Expression*>* op) { return (*op)(this); }\+virtual Selector* perform(Operation<Selector*>* op) { return (*op)(this); }\+virtual string perform(Operation<string>* op) { return (*op)(this); }\+virtual Sass_Value* perform(Operation<Sass_Value*>* op) { return (*op)(this); }++#define ADD_PROPERTY(type, name)\+protected:\+ type name##_;\+public:\+ type name() const { return name##_; }\+ type name(type name##__) { return name##_ = name##__; }\+private:++#endif
+ libsass/ast_factory.hpp view
@@ -0,0 +1,92 @@+#ifndef SASS_AST_FACTORY_H+#define SASS_AST_FACTORY_H++#include <vector>++#include "ast.hpp"++namespace Sass {+ using namespace std;++ class AST_Factory {+ vector<AST_Node*> nodes;+ public:+ // statements+ Block* new_Block(string p, size_t l, size_t s = 0, bool r = false);+ Ruleset* new_Ruleset(string p, size_t l, Selector* s, Block* b);+ Propset* new_Propset(string p, size_t l, String* pf, Block* b);+ Feature_Query* new_Feature_Query(string p, size_t l, Feature_Query* f, Block* b);+ Media_Query* new_Media_Query(string p, size_t l, List* q, Block* b);+ At_Root_Block* new_At_Root_Block(string p, size_t l, Selector* sel, Block* b);+ At_Rule* new_At_Rule(string p, size_t l, string kwd, Selector* sel, Block* b);+ Keyframe_Rule* new_Keyframe_Rule(string p, size_t l, Block* b);+ Declaration* new_Declaration(string p, size_t l, String* prop, List* vals);+ Assignment* new_Assignment(string p, size_t l, string var, Expression* val, bool guarded = false);+ Import<Function_Call*>* new_CSS_Import(string p, size_t l, Function_Call* loc);+ Import<String*>* new_SASS_Import(string p, size_t l, String* loc);+ Warning* new_Warning(string p, size_t l, Expression* msg);+ Error* new_Error(string p, size_t l, Expression* msg);+ Debug* new_Debug(string p, size_t l, Expression* val);+ Comment* new_Comment(string p, size_t l, String* txt);+ If* new_If(string p, size_t l, Expression* pred, Block* con, Block* alt = 0);+ For* new_For(string p, size_t l, string var, Expression* lo, Expression* hi, Block* b, bool inc);+ Each* new_Each(string p, size_t l, vector<string> vars, Expression* lst, Block* b);+ While* new_While(string p, size_t l, Expression* pred, Block* b);+ Extension* new_Extension(string p, size_t l, Selector* s);+ Definition<MIXIN>* new_Mixin_Definition(string p, size_t l, string n, Parameters* params, Block* b);+ Definition<FUNCTION>* new_Function_Definition(string p, size_t l, string n, Parameters* params, Block* b);+ Mixin_Call* new_Mixin_Call(string p, size_t l, string n, Arguments* args, Block* b = 0);+ // expressions+ List* new_List(string p, size_t l, size_t size = 0, List::Separator sep = List::space, bool argl = false);+ Map* new_Map(string p, size_t l, size_t size = 0);+ Binary_Expression<AND>* new_And(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<OR>* new_Or(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<EQ>* new_Eq(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<NEQ>* new_Neq(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<GT>* new_Gt(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<GTE>* new_Gte(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<LT>* new_Lt(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<LTE>* new_Lte(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<ADD>* new_Add(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<SUB>* new_Sub(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<MUL>* new_Mul(string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<DIV>* new_Div(string p, size_t l, Expression* lhs, Expression* rhs);+ Negation* new_Negation(string p, size_t l, Expression* o);+ Function_Call* new_Function_Call(string p, size_t l, String* n, Arguments* args);+ Variable* new_Variable(string p, size_t l, string n);+ Textual<NUMBER>* new_Textual_Number(string p, size_t l, string val);+ Textual<PERCENTAGE>* new_Textual_Percentage(string p, size_t l, string val);+ Textual<DIMENSION>* new_Textual_Dimension(string p, size_t l, string val);+ Textual<HEX>* new_Textual_Hex(string p, size_t l, string val);+ Number* new_Number(string p, size_t l, double val);+ Percentage* new_Percentage(string p, size_t l, double val);+ Dimension* new_Dimension(string p, size_t l, double val, string unit);+ Color* new_Color(string p, size_t l, double r, double g, double b, double a = 1, string disp = "");+ Boolean* new_Boolean(string p, size_t l, bool val);+ String_Schema* new_String_Schema(string p, size_t l, size_t size = 0);+ String_Constant* new_String_Constant(string p, size_t l, string val);+ String_Constant* new_String_Constant(string p, size_t l, const char* beg);+ String_Constant* new_String_Constant(string p, size_t l, const char* beg, const char* end);+ Feature_Query_Condition* new_Feature_Query_Condition(string p, size_t l, String* f, Expression* v);+ Media_Expression* new_Media_Expression(string p, size_t l, String* f, Expression* v);+ Parent_Selector* new_Parent_Selector(string p, size_t l, Selector* s);+ // parameters and arguments+ Parameter* new_Parameter(string p, size_t l, string n, Expression* def = 0, bool rest = false);+ Parameters* new_Parameters(string p, size_t l);+ Argument* new_Argument(string p, size_t l, Expression* val, string n = "", bool rest = false);+ Arguments* new_Arguments(string p, size_t l);+ // selectors+ Selector_Schema* new_Selector_Schema(string p, size_t l, String* c);+ Attribute_Selector* new_Attribute_Selector(string p, size_t l, string n, string m, String* v);+ Simple_Selector* new_Simple_Selector(string p, size_t l, string c);+ Reference_Selector* new_Reference_Selector(string p, size_t l);+ Placeholder_Selector* new_Placeholder_Selector(string p, size_t l, string n);+ Pseudo_Selector* new_Pseudo_Selector(string p, size_t l, string n, Expression* expr = 0);+ Wrapped_Selector* new_Wrapped_Selector(string p, size_t l, string n, Simple_Base* sel);+ Compound_Selector* new_Compound_Selector(string p, size_t l, size_t s = 0);+ Complex_Selector* new_Complex_Selector(string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel);+ Selector_List* new_Selector_List(string p, size_t l, size_t s = 0);+ };+}++#endif
+ libsass/ast_fwd_decl.hpp view
@@ -0,0 +1,85 @@+#ifndef SASS_AST_FWD_DECL_H+#define SASS_AST_FWD_DECL_H++/////////////////////////////////////////////+// Forward declarations for the AST visitors.+/////////////////////////////////////////////+namespace Sass {++ enum Output_Style { NESTED, EXPANDED, COMPACT, COMPRESSED, FORMATTED };++ class AST_Node;+ // statements+ class Statement;+ class Block;+ class Ruleset;+ class Propset;+ class Bubble;+ class Media_Block;+ class Feature_Block;+ class At_Rule;+ class Keyframe_Rule;+ class At_Root_Block;+ class Declaration;+ class Assignment;+ class Import;+ class Import_Stub;+ class Warning;+ class Error;+ class Debug;+ class Comment;+ class If;+ class For;+ class Each;+ class While;+ class Return;+ class Content;+ class Extension;+ class Definition;+ class Mixin_Call;+ // expressions+ class Expression;+ class List;+ class Map;+ class Binary_Expression;+ class Unary_Expression;+ class Function_Call;+ class Function_Call_Schema;+ class Variable;+ class Textual;+ class Number;+ class Color;+ class Boolean;+ class String_Schema;+ class String;+ class String_Constant;+ class String_Quoted;+ class Media_Query;+ class Media_Query_Expression;+ class Feature_Query;+ class Feature_Query_Condition;+ class At_Root_Expression;+ class Null;+ class Parent_Selector;+ // parameters and arguments+ class Parameter;+ class Parameters;+ class Argument;+ class Arguments;+ // selectors+ class Selector;+ class Selector_Schema;+ class Selector_Reference;+ class Selector_Placeholder;+ class Type_Selector;+ class Selector_Qualifier;+ class Attribute_Selector;+ class Pseudo_Selector;+ class Wrapped_Selector;+ class Compound_Selector;+ class Complex_Selector;+ class Selector_List;++}++#endif
+ libsass/b64/cencode.h view
@@ -0,0 +1,32 @@+/*+cencode.h - c header for a base64 encoding algorithm++This is part of the libb64 project, and has been placed in the public domain.+For details, see http://sourceforge.net/projects/libb64+*/++#ifndef BASE64_CENCODE_H+#define BASE64_CENCODE_H++typedef enum+{+ step_A, step_B, step_C+} base64_encodestep;++typedef struct+{+ base64_encodestep step;+ char result;+ int stepcount;+} base64_encodestate;++void base64_init_encodestate(base64_encodestate* state_in);++char base64_encode_value(char value_in);++int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);++int base64_encode_blockend(char* code_out, base64_encodestate* state_in);++#endif /* BASE64_CENCODE_H */+
+ libsass/b64/encode.h view
@@ -0,0 +1,77 @@+// :mode=c++:+/*+encode.h - c++ wrapper for a base64 encoding algorithm++This is part of the libb64 project, and has been placed in the public domain.+For details, see http://sourceforge.net/projects/libb64+*/+#ifndef BASE64_ENCODE_H+#define BASE64_ENCODE_H++#include <iostream>++namespace base64+{+ extern "C"+ {+ #include "cencode.h"+ }++ struct encoder+ {+ base64_encodestate _state;+ int _buffersize;++ encoder(int buffersize_in = BUFFERSIZE)+ : _buffersize(buffersize_in)+ {}++ int encode(char value_in)+ {+ return base64_encode_value(value_in);+ }++ int encode(const char* code_in, const int length_in, char* plaintext_out)+ {+ return base64_encode_block(code_in, length_in, plaintext_out, &_state);+ }++ int encode_end(char* plaintext_out)+ {+ return base64_encode_blockend(plaintext_out, &_state);+ }++ void encode(std::istream& istream_in, std::ostream& ostream_in)+ {+ base64_init_encodestate(&_state);+ //+ const int N = _buffersize;+ char* plaintext = new char[N];+ char* code = new char[2*N];+ int plainlength;+ int codelength;++ do+ {+ istream_in.read(plaintext, N);+ plainlength = static_cast<int>(istream_in.gcount());+ //+ codelength = encode(plaintext, plainlength, code);+ ostream_in.write(code, codelength);+ }+ while (istream_in.good() && plainlength > 0);++ codelength = encode_end(code);+ ostream_in.write(code, codelength);+ //+ base64_init_encodestate(&_state);++ delete [] code;+ delete [] plaintext;+ }+ };++} // namespace base64++#endif // BASE64_ENCODE_H+
+ libsass/backtrace.hpp view
@@ -0,0 +1,77 @@+#ifndef SASS_BACKTRACE_H+#define SASS_BACKTRACE_H++#include <sstream>++#include "file.hpp"+#include "position.hpp"++namespace Sass {++ using namespace std;++ struct Backtrace {++ Backtrace* parent;+ ParserState pstate;+ string caller;++ Backtrace(Backtrace* prn, ParserState pstate, string c)+ : parent(prn),+ pstate(pstate),+ caller(c)+ { }++ string to_string(bool warning = false)+ {+ size_t i = -1;+ stringstream ss;+ string cwd(Sass::File::get_cwd());+ Backtrace* this_point = this;++ if (!warning) ss << endl << "Backtrace:";+ // the first tracepoint (which is parent-less) is an empty placeholder+ while (this_point->parent) {++ // make path relative to the current directory+ string rel_path(Sass::File::resolve_relative_path(this_point->pstate.path, cwd, cwd));++ if (warning) {+ ss << endl+ << "\t"+ << (++i == 0 ? "on" : "from")+ << " line "+ << this_point->pstate.line+ << " of "+ << rel_path;+ } else {+ ss << endl+ << "\t"+ << rel_path+ << ":"+ << this_point->pstate.line+ << this_point->parent->caller;+ }++ this_point = this_point->parent;+ }++ return ss.str();+ }++ size_t depth()+ {+ size_t d = 0;+ Backtrace* p = parent;+ while (p) {+ ++d;+ p = p->parent;+ }+ return d-1;+ }++ };++}++#endif
+ libsass/base64vlq.cpp view
@@ -0,0 +1,43 @@+#include "base64vlq.hpp"++namespace Sass {++ string Base64VLQ::encode(const int number) const+ {+ string encoded = "";++ int vlq = to_vlq_signed(number);++ do {+ int digit = vlq & VLQ_BASE_MASK;+ vlq >>= VLQ_BASE_SHIFT;+ if (vlq > 0) {+ digit |= VLQ_CONTINUATION_BIT;+ }+ encoded += base64_encode(digit);+ } while (vlq > 0);++ return encoded;+ }++ char Base64VLQ::base64_encode(const int number) const+ {+ int index = number;+ if (index < 0) index = 0;+ if (index > 63) index = 63;+ return CHARACTERS[index];+ }++ int Base64VLQ::to_vlq_signed(const int number) const+ {+ return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0;+ }++ const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";++ const int Base64VLQ::VLQ_BASE_SHIFT = 5;+ const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT;+ const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1;+ const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE;++}
+ libsass/base64vlq.hpp view
@@ -0,0 +1,31 @@+#ifndef SASS_BASE64VLQ_H+#define SASS_BASE64VLQ_H++#include <string>++namespace Sass {+ using std::string;++ class Base64VLQ {++ public:++ string encode(const int number) const;++ private:++ char base64_encode(const int number) const;++ int to_vlq_signed(const int number) const;++ static const char* CHARACTERS;++ static const int VLQ_BASE_SHIFT;+ static const int VLQ_BASE;+ static const int VLQ_BASE_MASK;+ static const int VLQ_CONTINUATION_BIT;+ };++}++#endif
+ libsass/bind.cpp view
@@ -0,0 +1,186 @@+#include "bind.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "eval.hpp"+#include <map>+#include <iostream>+#include <sstream>+#include "to_string.hpp"++namespace Sass {+ using namespace std;++ void bind(string callee, Parameters* ps, Arguments* as, Context& ctx, Env* env, Eval* eval)+ {+ map<string, Parameter*> param_map;++ // Set up a map to ensure named arguments refer to actual parameters. Also+ // eval each default value left-to-right, wrt env, populating env as we go.+ for (size_t i = 0, L = ps->length(); i < L; ++i) {+ Parameter* p = (*ps)[i];+ param_map[p->name()] = p;+ // if (p->default_value()) {+ // env->local_frame()[p->name()] = p->default_value()->perform(eval->with(env));+ // }+ }++ // plug in all args; if we have leftover params, deal with it later+ size_t ip = 0, LP = ps->length();+ size_t ia = 0, LA = as->length();+ while (ia < LA) {+ if (ip >= LP) {+ stringstream msg;+ msg << callee << " only takes " << LP << " arguments; "+ << "given " << LA;+ error(msg.str(), as->pstate());+ }+ Parameter* p = (*ps)[ip];+ Argument* a = (*as)[ia];++ // If the current parameter is the rest parameter, process and break the loop+ if (p->is_rest_parameter()) {+ if (a->is_rest_argument()) {+ // rest param and rest arg -- just add one to the other+ if (env->has_local(p->name())) {+ *static_cast<List*>(env->local_frame()[p->name()])+ += static_cast<List*>(a->value());+ }+ else {+ env->local_frame()[p->name()] = a->value();+ }+ } else {++ // copy all remaining arguments into the rest parameter, preserving names+ List* arglist = new (ctx.mem) List(p->pstate(),+ 0,+ List::COMMA,+ true);+ env->local_frame()[p->name()] = arglist;+ while (ia < LA) {+ a = (*as)[ia];+ (*arglist) << new (ctx.mem) Argument(a->pstate(),+ a->value(),+ a->name(),+ false);+ ++ia;+ }+ }+ ++ip;+ break;+ }++ // If the current argument is the rest argument, extract a value for processing+ else if (a->is_rest_argument()) {+ // normal param and rest arg+ List* arglist = static_cast<List*>(a->value());+ // empty rest arg - treat all args as default values+ if (!arglist->length()) {+ break;+ }+ // otherwise move one of the rest args into the param, converting to argument if necessary+ if (arglist->is_arglist()) {+ a = static_cast<Argument*>((*arglist)[0]);+ } else {+ Expression* a_to_convert = (*arglist)[0];+ a = new (ctx.mem) Argument(a_to_convert->pstate(), a_to_convert, "", false);+ }+ arglist->elements().erase(arglist->elements().begin());+ if (!arglist->length() || (!arglist->is_arglist() && ip + 1 == LP)) {+ ++ia;+ }+ } else if (a->is_keyword_argument()) {+ Map* argmap = static_cast<Map*>(a->value());++ for (auto key : argmap->keys()) {+ string name = "$" + unquote(static_cast<String_Constant*>(key)->value());++ if (!param_map.count(name)) {+ stringstream msg;+ msg << callee << " has no parameter named " << name;+ error(msg.str(), a->pstate());+ }+ env->local_frame()[name] = argmap->at(key);+ }+ ++ia;+ continue;+ } else {+ ++ia;+ }++ if (a->name().empty()) {+ if (env->has_local(p->name())) {+ stringstream msg;+ msg << "parameter " << p->name()+ << " provided more than once in call to " << callee;+ error(msg.str(), a->pstate());+ }+ // ordinal arg -- bind it to the next param+ env->local_frame()[p->name()] = a->value();+ ++ip;+ }+ else {+ // named arg -- bind it to the appropriately named param+ if (!param_map.count(a->name())) {+ stringstream msg;+ msg << callee << " has no parameter named " << a->name();+ error(msg.str(), a->pstate());+ }+ if (param_map[a->name()]->is_rest_parameter()) {+ stringstream msg;+ msg << "argument " << a->name() << " of " << callee+ << "cannot be used as named argument";+ error(msg.str(), a->pstate());+ }+ if (env->has_local(a->name())) {+ stringstream msg;+ msg << "parameter " << p->name()+ << "provided more than once in call to " << callee;+ error(msg.str(), a->pstate());+ }+ env->local_frame()[a->name()] = a->value();+ }+ }++ // If we make it here, we're out of args but may have leftover params.+ // That's only okay if they have default values, or were already bound by+ // named arguments, or if it's a single rest-param.+ for (size_t i = ip; i < LP; ++i) {+ To_String to_string(&ctx);+ Parameter* leftover = (*ps)[i];+ // cerr << "env for default params:" << endl;+ // env->print();+ // cerr << "********" << endl;+ if (!env->has_local(leftover->name())) {+ if (leftover->is_rest_parameter()) {+ env->local_frame()[leftover->name()] = new (ctx.mem) List(leftover->pstate(),+ 0,+ List::COMMA,+ true);+ }+ else if (leftover->default_value()) {+ // make sure to eval the default value in the env that we've been populating+ Env* old_env = eval->env;+ Backtrace* old_bt = eval->backtrace;+ Contextualize* old_context = eval->contextualize;+ Expression* dv = leftover->default_value()->perform(eval->with(env, eval->backtrace));+ eval->env = old_env;+ eval->backtrace = old_bt;+ eval->contextualize = old_context;+ // dv->perform(&to_string);+ env->local_frame()[leftover->name()] = dv;+ }+ else {+ // param is unbound and has no default value -- error+ stringstream msg;+ msg << "required parameter " << leftover->name()+ << " is missing in call to " << callee;+ error(msg.str(), as->pstate());+ }+ }+ }++ return;+ }+++}
+ libsass/bind.hpp view
@@ -0,0 +1,18 @@+#ifndef SASS_BIND_H+#define SASS_BIND_H++#include <string>+#include "environment.hpp"++namespace Sass {+ class AST_Node;+ class Parameters;+ class Arguments;+ class Context;+ class Eval;+ typedef Environment<AST_Node*> Env;++ void bind(string caller, Parameters*, Arguments*, Context&, Env*, Eval*);+}++#endif
+ libsass/cencode.c view
@@ -0,0 +1,102 @@+/*+cencoder.c - c source to a base64 encoding algorithm implementation++This is part of the libb64 project, and has been placed in the public domain.+For details, see http://sourceforge.net/projects/libb64+*/++#include "b64/cencode.h"++void base64_init_encodestate(base64_encodestate* state_in)+{+ state_in->step = step_A;+ state_in->result = 0;+ state_in->stepcount = 0;+}++char base64_encode_value(char value_in)+{+ static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";+ if (value_in > 63) return '=';+ return encoding[(int)value_in];+}++int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)+{+ const char* plainchar = plaintext_in;+ const char* const plaintextend = plaintext_in + length_in;+ char* codechar = code_out;+ char result;+ char fragment;++ result = state_in->result;++ switch (state_in->step)+ {+ while (1)+ {+ case step_A:+ if (plainchar == plaintextend)+ {+ state_in->result = result;+ state_in->step = step_A;+ return codechar - code_out;+ }+ fragment = *plainchar++;+ result = (fragment & 0x0fc) >> 2;+ *codechar++ = base64_encode_value(result);+ result = (fragment & 0x003) << 4;+ case step_B:+ if (plainchar == plaintextend)+ {+ state_in->result = result;+ state_in->step = step_B;+ return codechar - code_out;+ }+ fragment = *plainchar++;+ result |= (fragment & 0x0f0) >> 4;+ *codechar++ = base64_encode_value(result);+ result = (fragment & 0x00f) << 2;+ case step_C:+ if (plainchar == plaintextend)+ {+ state_in->result = result;+ state_in->step = step_C;+ return codechar - code_out;+ }+ fragment = *plainchar++;+ result |= (fragment & 0x0c0) >> 6;+ *codechar++ = base64_encode_value(result);+ result = (fragment & 0x03f) >> 0;+ *codechar++ = base64_encode_value(result);++ ++(state_in->stepcount);+ }+ }+ /* control should not reach here */+ return codechar - code_out;+}++int base64_encode_blockend(char* code_out, base64_encodestate* state_in)+{+ char* codechar = code_out;++ switch (state_in->step)+ {+ case step_B:+ *codechar++ = base64_encode_value(state_in->result);+ *codechar++ = '=';+ *codechar++ = '=';+ break;+ case step_C:+ *codechar++ = base64_encode_value(state_in->result);+ *codechar++ = '=';+ break;+ case step_A:+ break;+ }+ *codechar++ = '\n';++ return codechar - code_out;+}+
+ libsass/color_names.hpp view
@@ -0,0 +1,327 @@+#ifndef SASS_COLOR_NAMES_H+#define SASS_COLOR_NAMES_H++namespace Sass {++ /*+ * Color names are processed in order. For color names with alternate aliases+ * (i.e. gray & grey), the canonical name should be listed last, so it takes+ * precedence.+ */+ const char* color_names[] =+ {+ "aliceblue",+ "antiquewhite",+ "aqua",+ "aquamarine",+ "azure",+ "beige",+ "bisque",+ "black",+ "blanchedalmond",+ "blue",+ "blueviolet",+ "brown",+ "burlywood",+ "cadetblue",+ "chartreuse",+ "chocolate",+ "coral",+ "cornflowerblue",+ "cornsilk",+ "crimson",+ "cyan",+ "darkblue",+ "darkcyan",+ "darkgoldenrod",+ "darkgrey",+ "darkgray",+ "darkgreen",+ "darkkhaki",+ "darkmagenta",+ "darkolivegreen",+ "darkorange",+ "darkorchid",+ "darkred",+ "darksalmon",+ "darkseagreen",+ "darkslateblue",+ "darkslategrey",+ "darkslategray",+ "darkturquoise",+ "darkviolet",+ "deeppink",+ "deepskyblue",+ "dimgrey",+ "dimgray",+ "dodgerblue",+ "firebrick",+ "floralwhite",+ "forestgreen",+ "fuchsia",+ "gainsboro",+ "ghostwhite",+ "gold",+ "goldenrod",+ "grey",+ "gray",+ "green",+ "greenyellow",+ "honeydew",+ "hotpink",+ "indianred",+ "indigo",+ "ivory",+ "khaki",+ "lavender",+ "lavenderblush",+ "lawngreen",+ "lemonchiffon",+ "lightblue",+ "lightcoral",+ "lightcyan",+ "lightgoldenrodyellow",+ "lightgrey",+ "lightgray",+ "lightgreen",+ "lightpink",+ "lightsalmon",+ "lightseagreen",+ "lightskyblue",+ "lightslategrey",+ "lightslategray",+ "lightsteelblue",+ "lightyellow",+ "lime",+ "limegreen",+ "linen",+ "magenta",+ "maroon",+ "mediumaquamarine",+ "mediumblue",+ "mediumorchid",+ "mediumpurple",+ "mediumseagreen",+ "mediumslateblue",+ "mediumspringgreen",+ "mediumturquoise",+ "mediumvioletred",+ "midnightblue",+ "mintcream",+ "mistyrose",+ "moccasin",+ "navajowhite",+ "navy",+ "oldlace",+ "olive",+ "olivedrab",+ "orange",+ "orangered",+ "orchid",+ "palegoldenrod",+ "palegreen",+ "paleturquoise",+ "palevioletred",+ "papayawhip",+ "peachpuff",+ "peru",+ "pink",+ "plum",+ "powderblue",+ "purple",+ "red",+ "rosybrown",+ "royalblue",+ "saddlebrown",+ "salmon",+ "sandybrown",+ "seagreen",+ "seashell",+ "sienna",+ "silver",+ "skyblue",+ "slateblue",+ "slategrey",+ "slategray",+ "snow",+ "springgreen",+ "steelblue",+ "tan",+ "teal",+ "thistle",+ "tomato",+ "turquoise",+ "violet",+ "wheat",+ "white",+ "whitesmoke",+ "yellow",+ "yellowgreen",+ // rebeccapurple+ "rebeccapurple",+ // transparent+ "transparent",+ // sentinel value+ 0+ };++ const double color_values[] =+ {+ 0xf0, 0xf8, 0xff, 1,+ 0xfa, 0xeb, 0xd7, 1,+ 0x00, 0xff, 0xff, 1,+ 0x7f, 0xff, 0xd4, 1,+ 0xf0, 0xff, 0xff, 1,+ 0xf5, 0xf5, 0xdc, 1,+ 0xff, 0xe4, 0xc4, 1,+ 0x00, 0x00, 0x00, 1,+ 0xff, 0xeb, 0xcd, 1,+ 0x00, 0x00, 0xff, 1,+ 0x8a, 0x2b, 0xe2, 1,+ 0xa5, 0x2a, 0x2a, 1,+ 0xde, 0xb8, 0x87, 1,+ 0x5f, 0x9e, 0xa0, 1,+ 0x7f, 0xff, 0x00, 1,+ 0xd2, 0x69, 0x1e, 1,+ 0xff, 0x7f, 0x50, 1,+ 0x64, 0x95, 0xed, 1,+ 0xff, 0xf8, 0xdc, 1,+ 0xdc, 0x14, 0x3c, 1,+ 0x00, 0xff, 0xff, 1,+ 0x00, 0x00, 0x8b, 1,+ 0x00, 0x8b, 0x8b, 1,+ 0xb8, 0x86, 0x0b, 1,+ 0xa9, 0xa9, 0xa9, 1,+ 0xa9, 0xa9, 0xa9, 1,+ 0x00, 0x64, 0x00, 1,+ 0xbd, 0xb7, 0x6b, 1,+ 0x8b, 0x00, 0x8b, 1,+ 0x55, 0x6b, 0x2f, 1,+ 0xff, 0x8c, 0x00, 1,+ 0x99, 0x32, 0xcc, 1,+ 0x8b, 0x00, 0x00, 1,+ 0xe9, 0x96, 0x7a, 1,+ 0x8f, 0xbc, 0x8f, 1,+ 0x48, 0x3d, 0x8b, 1,+ 0x2f, 0x4f, 0x4f, 1,+ 0x2f, 0x4f, 0x4f, 1,+ 0x00, 0xce, 0xd1, 1,+ 0x94, 0x00, 0xd3, 1,+ 0xff, 0x14, 0x93, 1,+ 0x00, 0xbf, 0xff, 1,+ 0x69, 0x69, 0x69, 1,+ 0x69, 0x69, 0x69, 1,+ 0x1e, 0x90, 0xff, 1,+ 0xb2, 0x22, 0x22, 1,+ 0xff, 0xfa, 0xf0, 1,+ 0x22, 0x8b, 0x22, 1,+ 0xff, 0x00, 0xff, 1,+ 0xdc, 0xdc, 0xdc, 1,+ 0xf8, 0xf8, 0xff, 1,+ 0xff, 0xd7, 0x00, 1,+ 0xda, 0xa5, 0x20, 1,+ 0x80, 0x80, 0x80, 1,+ 0x80, 0x80, 0x80, 1,+ 0x00, 0x80, 0x00, 1,+ 0xad, 0xff, 0x2f, 1,+ 0xf0, 0xff, 0xf0, 1,+ 0xff, 0x69, 0xb4, 1,+ 0xcd, 0x5c, 0x5c, 1,+ 0x4b, 0x00, 0x82, 1,+ 0xff, 0xff, 0xf0, 1,+ 0xf0, 0xe6, 0x8c, 1,+ 0xe6, 0xe6, 0xfa, 1,+ 0xff, 0xf0, 0xf5, 1,+ 0x7c, 0xfc, 0x00, 1,+ 0xff, 0xfa, 0xcd, 1,+ 0xad, 0xd8, 0xe6, 1,+ 0xf0, 0x80, 0x80, 1,+ 0xe0, 0xff, 0xff, 1,+ 0xfa, 0xfa, 0xd2, 1,+ 0xd3, 0xd3, 0xd3, 1,+ 0xd3, 0xd3, 0xd3, 1,+ 0x90, 0xee, 0x90, 1,+ 0xff, 0xb6, 0xc1, 1,+ 0xff, 0xa0, 0x7a, 1,+ 0x20, 0xb2, 0xaa, 1,+ 0x87, 0xce, 0xfa, 1,+ 0x77, 0x88, 0x99, 1,+ 0x77, 0x88, 0x99, 1,+ 0xb0, 0xc4, 0xde, 1,+ 0xff, 0xff, 0xe0, 1,+ 0x00, 0xff, 0x00, 1,+ 0x32, 0xcd, 0x32, 1,+ 0xfa, 0xf0, 0xe6, 1,+ 0xff, 0x00, 0xff, 1,+ 0x80, 0x00, 0x00, 1,+ 0x66, 0xcd, 0xaa, 1,+ 0x00, 0x00, 0xcd, 1,+ 0xba, 0x55, 0xd3, 1,+ 0x93, 0x70, 0xdb, 1,+ 0x3c, 0xb3, 0x71, 1,+ 0x7b, 0x68, 0xee, 1,+ 0x00, 0xfa, 0x9a, 1,+ 0x48, 0xd1, 0xcc, 1,+ 0xc7, 0x15, 0x85, 1,+ 0x19, 0x19, 0x70, 1,+ 0xf5, 0xff, 0xfa, 1,+ 0xff, 0xe4, 0xe1, 1,+ 0xff, 0xe4, 0xb5, 1,+ 0xff, 0xde, 0xad, 1,+ 0x00, 0x00, 0x80, 1,+ 0xfd, 0xf5, 0xe6, 1,+ 0x80, 0x80, 0x00, 1,+ 0x6b, 0x8e, 0x23, 1,+ 0xff, 0xa5, 0x00, 1,+ 0xff, 0x45, 0x00, 1,+ 0xda, 0x70, 0xd6, 1,+ 0xee, 0xe8, 0xaa, 1,+ 0x98, 0xfb, 0x98, 1,+ 0xaf, 0xee, 0xee, 1,+ 0xdb, 0x70, 0x93, 1,+ 0xff, 0xef, 0xd5, 1,+ 0xff, 0xda, 0xb9, 1,+ 0xcd, 0x85, 0x3f, 1,+ 0xff, 0xc0, 0xcb, 1,+ 0xdd, 0xa0, 0xdd, 1,+ 0xb0, 0xe0, 0xe6, 1,+ 0x80, 0x00, 0x80, 1,+ 0xff, 0x00, 0x00, 1,+ 0xbc, 0x8f, 0x8f, 1,+ 0x41, 0x69, 0xe1, 1,+ 0x8b, 0x45, 0x13, 1,+ 0xfa, 0x80, 0x72, 1,+ 0xf4, 0xa4, 0x60, 1,+ 0x2e, 0x8b, 0x57, 1,+ 0xff, 0xf5, 0xee, 1,+ 0xa0, 0x52, 0x2d, 1,+ 0xc0, 0xc0, 0xc0, 1,+ 0x87, 0xce, 0xeb, 1,+ 0x6a, 0x5a, 0xcd, 1,+ 0x70, 0x80, 0x90, 1,+ 0x70, 0x80, 0x90, 1,+ 0xff, 0xfa, 0xfa, 1,+ 0x00, 0xff, 0x7f, 1,+ 0x46, 0x82, 0xb4, 1,+ 0xd2, 0xb4, 0x8c, 1,+ 0x00, 0x80, 0x80, 1,+ 0xd8, 0xbf, 0xd8, 1,+ 0xff, 0x63, 0x47, 1,+ 0x40, 0xe0, 0xd0, 1,+ 0xee, 0x82, 0xee, 1,+ 0xf5, 0xde, 0xb3, 1,+ 0xff, 0xff, 0xff, 1,+ 0xf5, 0xf5, 0xf5, 1,+ 0xff, 0xff, 0x00, 1,+ 0x9a, 0xcd, 0x32, 1,+ // rebeccapurple+ 0x66, 0x33, 0x99, 1,+ // transparent+ 0x00, 0x00, 0x00, 0,+ // sentinel value+ 0xfff+ };++}++#endif
+ libsass/constants.cpp view
@@ -0,0 +1,166 @@+#include "constants.hpp"++namespace Sass {+ namespace Constants {++ // https://github.com/sass/libsass/issues/592+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity+ // https://github.com/sass/sass/issues/1495#issuecomment-61189114+ extern const unsigned long Specificity_Star = 0;+ extern const unsigned long Specificity_Universal = 1 << 0;+ extern const unsigned long Specificity_Type = 1 << 8;+ extern const unsigned long Specificity_Class = 1 << 16;+ extern const unsigned long Specificity_Attr = 1 << 16;+ extern const unsigned long Specificity_Pseudo = 1 << 16;+ extern const unsigned long Specificity_ID = 1 << 24;++ // sass keywords+ extern const char at_root_kwd[] = "@at-root";+ extern const char import_kwd[] = "@import";+ extern const char mixin_kwd[] = "@mixin";+ extern const char function_kwd[] = "@function";+ extern const char return_kwd[] = "@return";+ extern const char include_kwd[] = "@include";+ extern const char content_kwd[] = "@content";+ extern const char extend_kwd[] = "@extend";+ extern const char if_kwd[] = "@if";+ extern const char else_kwd[] = "@else";+ extern const char if_after_else_kwd[] = "if";+ extern const char for_kwd[] = "@for";+ extern const char from_kwd[] = "from";+ extern const char to_kwd[] = "to";+ extern const char through_kwd[] = "through";+ extern const char each_kwd[] = "@each";+ extern const char in_kwd[] = "in";+ extern const char while_kwd[] = "@while";+ extern const char warn_kwd[] = "@warn";+ extern const char error_kwd[] = "@error";+ extern const char debug_kwd[] = "@debug";+ extern const char default_kwd[] = "default";+ extern const char global_kwd[] = "global";+ extern const char null_kwd[] = "null";+ extern const char optional_kwd[] = "optional";+ extern const char with_kwd[] = "with";+ extern const char without_kwd[] = "without";+ extern const char all_kwd[] = "all";+ extern const char rule_kwd[] = "rule";++ // css standard units+ extern const char em_kwd[] = "em";+ extern const char ex_kwd[] = "ex";+ extern const char px_kwd[] = "px";+ extern const char cm_kwd[] = "cm";+ extern const char mm_kwd[] = "mm";+ extern const char pt_kwd[] = "pt";+ extern const char pc_kwd[] = "pc";+ extern const char deg_kwd[] = "deg";+ extern const char rad_kwd[] = "rad";+ extern const char grad_kwd[] = "grad";+ extern const char turn_kwd[] = "turn";+ extern const char ms_kwd[] = "ms";+ extern const char s_kwd[] = "s";+ extern const char Hz_kwd[] = "Hz";+ extern const char kHz_kwd[] = "kHz";++ // vendor prefixes+ extern const char vendor_opera_kwd[] = "-o-";+ extern const char vendor_webkit_kwd[] = "-webkit-";+ extern const char vendor_mozilla_kwd[] = "-moz-";+ extern const char vendor_ms_kwd[] = "-ms-";+ extern const char vendor_khtml_kwd[] = "-khtml-";++ // css functions and keywords+ extern const char charset_kwd[] = "@charset";+ extern const char media_kwd[] = "@media";+ extern const char supports_kwd[] = "@supports";+ extern const char keyframes_kwd[] = "keyframes";+ extern const char only_kwd[] = "only";+ extern const char rgb_kwd[] = "rgb(";+ extern const char url_kwd[] = "url(";+ extern const char important_kwd[] = "important";+ extern const char pseudo_not_kwd[] = ":not(";+ extern const char even_kwd[] = "even";+ extern const char odd_kwd[] = "odd";+ extern const char progid_kwd[] = "progid";+ extern const char expression_kwd[] = "expression";+ extern const char calc_kwd[] = "calc(";+ extern const char moz_calc_kwd[] = "-moz-calc(";+ extern const char webkit_calc_kwd[] = "-webkit-calc(";+ extern const char ms_calc_kwd[] = "-ms-calc(";++ // css selector keywords+ extern const char sel_deep_kwd[] = "/deep/";++ // css attribute-matching operators+ extern const char tilde_equal[] = "~=";+ extern const char pipe_equal[] = "|=";+ extern const char caret_equal[] = "^=";+ extern const char dollar_equal[] = "$=";+ extern const char star_equal[] = "*=";++ // relational & logical operators and constants+ extern const char and_kwd[] = "and";+ extern const char or_kwd[] = "or";+ extern const char not_kwd[] = "not";+ extern const char gt[] = ">";+ extern const char gte[] = ">=";+ extern const char lt[] = "<";+ extern const char lte[] = "<=";+ extern const char eq[] = "==";+ extern const char neq[] = "!=";+ extern const char true_kwd[] = "true";+ extern const char false_kwd[] = "false";++ // miscellaneous punctuation and delimiters+ extern const char percent_str[] = "%";+ extern const char empty_str[] = "";+ extern const char slash_slash[] = "//";+ extern const char slash_star[] = "/*";+ extern const char star_slash[] = "*/";+ extern const char hash_lbrace[] = "#{";+ extern const char rbrace[] = "}";+ extern const char rparen[] = ")";+ extern const char sign_chars[] = "-+";+ extern const char hyphen[] = "-";+ extern const char ellipsis[] = "...";+ // extern const char url_space_chars[] = " \t\r\n\f";+ extern const char escape_chars[] = " -~"; // need to include unicode spaces too+ // type names+ extern const char numeric_name[] = "numeric value";+ extern const char number_name[] = "number";+ extern const char percentage_name[] = "percentage";+ extern const char dimension_name[] = "numeric dimension";+ extern const char string_name[] = "string";+ extern const char bool_name[] = "bool";+ extern const char color_name[] = "color";+ extern const char list_name[] = "list";+ extern const char map_name[] = "map";+ extern const char arglist_name[] = "arglist";++ // constants for uri parsing (RFC 3986 Appendix A.)+ extern const char uri_chars[] = ":;/?!$%&#@[]{}'\"*+-.,_=";++ // some specific constant character classes+ // they must be static to be useable by lexer+ extern const char static_ops[] = "*/%";++ // byte order marks+ // (taken from http://en.wikipedia.org/wiki/Byte_order_mark)+ extern const unsigned char utf_8_bom[] = { 0xEF, 0xBB, 0xBF };+ extern const unsigned char utf_16_bom_be[] = { 0xFE, 0xFF };+ extern const unsigned char utf_16_bom_le[] = { 0xFF, 0xFE };+ extern const unsigned char utf_32_bom_be[] = { 0x00, 0x00, 0xFE, 0xFF };+ extern const unsigned char utf_32_bom_le[] = { 0xFF, 0xFE, 0x00, 0x00 };+ extern const unsigned char utf_7_bom_1[] = { 0x2B, 0x2F, 0x76, 0x38 };+ extern const unsigned char utf_7_bom_2[] = { 0x2B, 0x2F, 0x76, 0x39 };+ extern const unsigned char utf_7_bom_3[] = { 0x2B, 0x2F, 0x76, 0x2B };+ extern const unsigned char utf_7_bom_4[] = { 0x2B, 0x2F, 0x76, 0x2F };+ extern const unsigned char utf_7_bom_5[] = { 0x2B, 0x2F, 0x76, 0x38, 0x2D };+ extern const unsigned char utf_1_bom[] = { 0xF7, 0x64, 0x4C };+ extern const unsigned char utf_ebcdic_bom[] = { 0xDD, 0x73, 0x66, 0x73 };+ extern const unsigned char scsu_bom[] = { 0x0E, 0xFE, 0xFF };+ extern const unsigned char bocu_1_bom[] = { 0xFB, 0xEE, 0x28 };+ extern const unsigned char gb_18030_bom[] = { 0x84, 0x31, 0x95, 0x33 };++ }+}
+ libsass/constants.hpp view
@@ -0,0 +1,170 @@+#ifndef SASS_CONSTANTS_H+#define SASS_CONSTANTS_H++namespace Sass {+ namespace Constants {++ // https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity+ // The following list of selectors is by increasing specificity:+ extern const unsigned long Specificity_Star;+ extern const unsigned long Specificity_Universal;+ extern const unsigned long Specificity_Type;+ extern const unsigned long Specificity_Class;+ extern const unsigned long Specificity_Attr;+ extern const unsigned long Specificity_Pseudo;+ extern const unsigned long Specificity_ID;++ // sass keywords+ extern const char at_root_kwd[];+ extern const char import_kwd[];+ extern const char mixin_kwd[];+ extern const char function_kwd[];+ extern const char return_kwd[];+ extern const char include_kwd[];+ extern const char content_kwd[];+ extern const char extend_kwd[];+ extern const char if_kwd[];+ extern const char else_kwd[];+ extern const char if_after_else_kwd[];+ extern const char for_kwd[];+ extern const char from_kwd[];+ extern const char to_kwd[];+ extern const char through_kwd[];+ extern const char each_kwd[];+ extern const char in_kwd[];+ extern const char while_kwd[];+ extern const char warn_kwd[];+ extern const char error_kwd[];+ extern const char debug_kwd[];+ extern const char default_kwd[];+ extern const char global_kwd[];+ extern const char null_kwd[];+ extern const char optional_kwd[];+ extern const char with_kwd[];+ extern const char without_kwd[];+ extern const char all_kwd[];+ extern const char rule_kwd[];++ // css standard units+ extern const char em_kwd[];+ extern const char ex_kwd[];+ extern const char px_kwd[];+ extern const char cm_kwd[];+ extern const char mm_kwd[];+ extern const char pt_kwd[];+ extern const char pc_kwd[];+ extern const char deg_kwd[];+ extern const char rad_kwd[];+ extern const char grad_kwd[];+ extern const char turn_kwd[];+ extern const char ms_kwd[];+ extern const char s_kwd[];+ extern const char Hz_kwd[];+ extern const char kHz_kwd[];++ // vendor prefixes+ extern const char vendor_opera_kwd[];+ extern const char vendor_webkit_kwd[];+ extern const char vendor_mozilla_kwd[];+ extern const char vendor_ms_kwd[];+ extern const char vendor_khtml_kwd[];++ // css functions and keywords+ extern const char charset_kwd[];+ extern const char media_kwd[];+ extern const char supports_kwd[];+ extern const char keyframes_kwd[];+ extern const char only_kwd[];+ extern const char rgb_kwd[];+ extern const char url_kwd[];+ extern const char image_url_kwd[];+ extern const char important_kwd[];+ extern const char pseudo_not_kwd[];+ extern const char even_kwd[];+ extern const char odd_kwd[];+ extern const char progid_kwd[];+ extern const char expression_kwd[];+ extern const char calc_kwd[];+ extern const char moz_calc_kwd[];+ extern const char webkit_calc_kwd[];+ extern const char ms_calc_kwd[];++ // css selector keywords+ extern const char sel_deep_kwd[];++ // css attribute-matching operators+ extern const char tilde_equal[];+ extern const char pipe_equal[];+ extern const char caret_equal[];+ extern const char dollar_equal[];+ extern const char star_equal[];++ // relational & logical operators and constants+ extern const char and_kwd[];+ extern const char or_kwd[];+ extern const char not_kwd[];+ extern const char gt[];+ extern const char gte[];+ extern const char lt[];+ extern const char lte[];+ extern const char eq[];+ extern const char neq[];+ extern const char true_kwd[];+ extern const char false_kwd[];++ // miscellaneous punctuation and delimiters+ extern const char percent_str[];+ extern const char empty_str[];+ extern const char slash_slash[];+ extern const char slash_star[];+ extern const char star_slash[];+ extern const char hash_lbrace[];+ extern const char rbrace[];+ extern const char rparen[];+ extern const char sign_chars[];+ extern const char hyphen[];+ extern const char ellipsis[];+ // extern const char url_space_chars[];+ extern const char escape_chars[];++ // type names+ extern const char numeric_name[];+ extern const char number_name[];+ extern const char percentage_name[];+ extern const char dimension_name[];+ extern const char string_name[];+ extern const char bool_name[];+ extern const char color_name[];+ extern const char list_name[];+ extern const char map_name[];+ extern const char arglist_name[];++ // constants for uri parsing (RFC 3986 Appendix A.)+ extern const char uri_chars[];++ // some specific constant character classes+ // they must be static to be useable by lexer+ extern const char static_ops[];++ // byte order marks+ // (taken from http://en.wikipedia.org/wiki/Byte_order_mark)+ extern const unsigned char utf_8_bom[];+ extern const unsigned char utf_16_bom_be[];+ extern const unsigned char utf_16_bom_le[];+ extern const unsigned char utf_32_bom_be[];+ extern const unsigned char utf_32_bom_le[];+ extern const unsigned char utf_7_bom_1[];+ extern const unsigned char utf_7_bom_2[];+ extern const unsigned char utf_7_bom_3[];+ extern const unsigned char utf_7_bom_4[];+ extern const unsigned char utf_7_bom_5[];+ extern const unsigned char utf_1_bom[];+ extern const unsigned char utf_ebcdic_bom[];+ extern const unsigned char scsu_bom[];+ extern const unsigned char bocu_1_bom[];+ extern const unsigned char gb_18030_bom[];++ }+}++#endif
+ libsass/context.cpp view
@@ -0,0 +1,560 @@+#ifdef _WIN32+#define PATH_SEP ';'+#else+#define PATH_SEP ':'+#endif++#include "ast.hpp"+#include "util.hpp"+#include "sass.h"+#include "context.hpp"+#include "plugins.hpp"+#include "constants.hpp"+#include "parser.hpp"+#include "file.hpp"+#include "inspect.hpp"+#include "output.hpp"+#include "expand.hpp"+#include "eval.hpp"+#include "contextualize.hpp"+#include "contextualize_eval.hpp"+#include "cssize.hpp"+#include "listize.hpp"+#include "extend.hpp"+#include "remove_placeholders.hpp"+#include "color_names.hpp"+#include "functions.hpp"+#include "backtrace.hpp"+#include "sass2scss.h"+#include "prelexer.hpp"+#include "emitter.hpp"++#include <string>+#include <cstdlib>+#include <cstring>+#include <iomanip>+#include <sstream>+#include <iostream>++namespace Sass {+ using namespace Constants;+ using namespace File;+ using namespace Sass;+ using std::cerr;+ using std::endl;++ Sass_Queued::Sass_Queued(const string& load_path, const string& abs_path, const char* source)+ {+ this->load_path = load_path;+ this->abs_path = abs_path;+ this->source = source;+ }++ inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)+ { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }++ Context::Context(Context::Data initializers)+ : // Output(this),+ head_imports(0),+ mem(Memory_Manager<AST_Node>()),+ c_options (initializers.c_options()),+ c_compiler (initializers.c_compiler()),+ source_c_str (initializers.source_c_str()),+ sources (vector<const char*>()),+ plugin_paths (initializers.plugin_paths()),+ include_paths (initializers.include_paths()),+ queue (vector<Sass_Queued>()),+ style_sheets (map<string, Block*>()),+ emitter (this),+ c_headers (vector<Sass_Importer_Entry>()),+ c_importers (vector<Sass_Importer_Entry>()),+ c_functions (vector<Sass_Function_Entry>()),+ indent (initializers.indent()),+ linefeed (initializers.linefeed()),+ input_path (make_canonical_path(initializers.input_path())),+ output_path (make_canonical_path(initializers.output_path())),+ source_comments (initializers.source_comments()),+ output_style (initializers.output_style()),+ source_map_file (make_canonical_path(initializers.source_map_file())),+ source_map_root (initializers.source_map_root()), // pass-through+ source_map_embed (initializers.source_map_embed()),+ source_map_contents (initializers.source_map_contents()),+ omit_source_map_url (initializers.omit_source_map_url()),+ is_indented_syntax_src (initializers.is_indented_syntax_src()),+ names_to_colors (map<string, Color*>()),+ colors_to_names (map<int, string>()),+ precision (initializers.precision()),+ plugins(),+ subset_map (Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> >())+ {++ cwd = get_cwd();++ // enforce some safe defaults+ // used to create relative file links+ if (input_path == "") input_path = "stdin";+ if (output_path == "") output_path = "stdout";++ include_paths.push_back(cwd);+ collect_include_paths(initializers.include_paths_c_str());+ // collect_include_paths(initializers.include_paths_array());+ collect_plugin_paths(initializers.plugin_paths_c_str());+ // collect_plugin_paths(initializers.plugin_paths_array());++ setup_color_map();++ for (size_t i = 0, S = plugin_paths.size(); i < S; ++i) {+ plugins.load_plugins(plugin_paths[i]);+ }++ for(auto fn : plugins.get_functions()) {+ c_functions.push_back(fn);+ }+ for(auto fn : plugins.get_headers()) {+ c_headers.push_back(fn);+ }+ for(auto fn : plugins.get_importers()) {+ c_importers.push_back(fn);+ }++ sort (c_headers.begin(), c_headers.end(), sort_importers);+ sort (c_importers.begin(), c_importers.end(), sort_importers);+ string entry_point = initializers.entry_point();+ if (!entry_point.empty()) {+ string result(add_file(entry_point));+ if (result.empty()) {+ throw "File to read not found or unreadable: " + entry_point;+ }+ }++ emitter.set_filename(resolve_relative_path(output_path, source_map_file, cwd));++ }++ void Context::add_c_function(Sass_Function_Entry function)+ {+ c_functions.push_back(function);+ }+ void Context::add_c_header(Sass_Importer_Entry header)+ {+ c_headers.push_back(header);+ // need to sort the array afterwards (no big deal)+ sort (c_headers.begin(), c_headers.end(), sort_importers);+ }+ void Context::add_c_importer(Sass_Importer_Entry importer)+ {+ c_importers.push_back(importer);+ // need to sort the array afterwards (no big deal)+ sort (c_importers.begin(), c_importers.end(), sort_importers);+ }++ Context::~Context()+ {+ // everything that gets put into sources will be freed by us+ for (size_t n = 0; n < import_stack.size(); ++n) sass_delete_import(import_stack[n]);+ // sources are allocated by strdup or malloc (overtaken from C code)+ for (size_t i = 0; i < sources.size(); ++i) free((void*)sources[i]);+ // clear inner structures (vectors)+ sources.clear(); import_stack.clear();+ }++ void Context::setup_color_map()+ {+ size_t i = 0;+ while (color_names[i]) {+ string name(color_names[i]);+ Color* value = new (mem) Color(ParserState("[COLOR TABLE]"),+ color_values[i*4],+ color_values[i*4+1],+ color_values[i*4+2],+ color_values[i*4+3]);+ names_to_colors[name] = value;+ // only map fully opaque colors+ if (color_values[i*4+3] >= 1) {+ int numval = static_cast<int>(color_values[i*4])*0x10000;+ numval += static_cast<int>(color_values[i*4+1])*0x100;+ numval += static_cast<int>(color_values[i*4+2]);+ colors_to_names[numval] = name;+ }+ ++i;+ }+ }++ void Context::collect_include_paths(const char* paths_str)+ {++ if (paths_str) {+ const char* beg = paths_str;+ const char* end = Prelexer::find_first<PATH_SEP>(beg);++ while (end) {+ string path(beg, end - beg);+ if (!path.empty()) {+ if (*path.rbegin() != '/') path += '/';+ include_paths.push_back(path);+ }+ beg = end + 1;+ end = Prelexer::find_first<PATH_SEP>(beg);+ }++ string path(beg);+ if (!path.empty()) {+ if (*path.rbegin() != '/') path += '/';+ include_paths.push_back(path);+ }+ }+ }++ void Context::collect_include_paths(const char** paths_array)+ {+ if (paths_array) {+ for (size_t i = 0; paths_array[i]; i++) {+ collect_include_paths(paths_array[i]);+ }+ }+ }++ void Context::collect_plugin_paths(const char* paths_str)+ {++ if (paths_str) {+ const char* beg = paths_str;+ const char* end = Prelexer::find_first<PATH_SEP>(beg);++ while (end) {+ string path(beg, end - beg);+ if (!path.empty()) {+ if (*path.rbegin() != '/') path += '/';+ plugin_paths.push_back(path);+ }+ beg = end + 1;+ end = Prelexer::find_first<PATH_SEP>(beg);+ }++ string path(beg);+ if (!path.empty()) {+ if (*path.rbegin() != '/') path += '/';+ plugin_paths.push_back(path);+ }+ }+ }++ void Context::collect_plugin_paths(const char** paths_array)+ {+ if (paths_array) {+ for (size_t i = 0; paths_array[i]; i++) {+ collect_plugin_paths(paths_array[i]);+ }+ }+ }+ void Context::add_source(string load_path, string abs_path, const char* contents)+ {+ sources.push_back(contents);+ included_files.push_back(abs_path);+ queue.push_back(Sass_Queued(load_path, abs_path, contents));+ emitter.add_source_index(sources.size() - 1);+ include_links.push_back(resolve_relative_path(abs_path, source_map_file, cwd));+ }++ // Add a new import file to the context+ string Context::add_file(const string& file)+ {+ using namespace File;+ string path(make_canonical_path(file));+ string resolved(find_file(path, include_paths));+ if (resolved == "") return resolved;+ if (char* contents = read_file(resolved)) {+ add_source(path, resolved, contents);+ style_sheets[path] = 0;+ return path;+ }+ return string("");+ }++ // Add a new import file to the context+ // This has some previous directory context+ string Context::add_file(const string& base, const string& file)+ {+ using namespace File;+ string path(make_canonical_path(file));+ string base_file(join_paths(base, path));+ string resolved(resolve_file(base_file));+ if (style_sheets.count(base_file)) return base_file;+ if (char* contents = read_file(resolved)) {+ add_source(base_file, resolved, contents);+ style_sheets[base_file] = 0;+ return base_file;+ }+ // now go the regular code path+ return add_file(path);+ }++ void register_function(Context&, Signature sig, Native_Function f, Env* env);+ void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env);+ void register_overload_stub(Context&, string name, Env* env);+ void register_built_in_functions(Context&, Env* env);+ void register_c_functions(Context&, Env* env, Sass_Function_List);+ void register_c_function(Context&, Env* env, Sass_Function_Entry);++ char* Context::compile_block(Block* root)+ {+ if (!root) return 0;+ root->perform(&emitter);+ emitter.finalize();+ OutputBuffer emitted = emitter.get_buffer();+ string output = emitted.buffer;+ if (source_map_file != "" && !omit_source_map_url) {+ output += linefeed + format_source_mapping_url(source_map_file);+ }+ return sass_strdup(output.c_str());+ }++ Block* Context::parse_file()+ {+ Block* root = 0;+ for (size_t i = 0; i < queue.size(); ++i) {+ Sass_Import_Entry import = sass_make_import(+ queue[i].load_path.c_str(),+ queue[i].abs_path.c_str(),+ 0, 0+ );+ import_stack.push_back(import);+ Parser p(Parser::from_c_str(queue[i].source, *this, ParserState(queue[i].abs_path, queue[i].source, i)));+ Block* ast = p.parse();+ sass_delete_import(import_stack.back());+ import_stack.pop_back();+ if (i == 0) root = ast;+ style_sheets[queue[i].load_path] = ast;+ }+ if (root == 0) return 0;+ Env tge;+ Backtrace backtrace(0, ParserState("", 0), "");+ register_built_in_functions(*this, &tge);+ for (size_t i = 0, S = c_functions.size(); i < S; ++i) {+ register_c_function(*this, &tge, c_functions[i]);+ }+ Contextualize contextualize(*this, &tge, &backtrace);+ Listize listize(*this);+ Eval eval(*this, &contextualize, &listize, &tge, &backtrace);+ Contextualize_Eval contextualize_eval(*this, &eval, &tge, &backtrace);+ Expand expand(*this, &eval, &contextualize_eval, &tge, &backtrace);+ Cssize cssize(*this, &tge, &backtrace);+ root = root->perform(&expand)->block();+ root = root->perform(&cssize)->block();+ if (!subset_map.empty()) {+ Extend extend(*this, subset_map);+ root->perform(&extend);+ }++ Remove_Placeholders remove_placeholders(*this);+ root->perform(&remove_placeholders);++ return root;+ }++ Block* Context::parse_string()+ {+ if (!source_c_str) return 0;+ queue.clear();+ if(is_indented_syntax_src) {+ char * contents = sass2scss(source_c_str, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);+ add_source(input_path, input_path, contents);+ delete [] source_c_str;+ return parse_file();+ }+ add_source(input_path, input_path, source_c_str);+ return parse_file();+ }++ char* Context::compile_file()+ {+ // returns NULL if something fails+ return compile_block(parse_file());+ }++ char* Context::compile_string()+ {+ // returns NULL if something fails+ return compile_block(parse_string());+ }++ string Context::format_source_mapping_url(const string& file)+ {+ string url = resolve_relative_path(file, output_path, cwd);+ if (source_map_embed) {+ string map = emitter.generate_source_map(*this);+ istringstream is( map );+ ostringstream buffer;+ base64::encoder E;+ E.encode(is, buffer);+ url = "data:application/json;base64," + buffer.str();+ url.erase(url.size() - 1);+ }+ return "/*# sourceMappingURL=" + url + " */";+ }++ char* Context::generate_source_map()+ {+ if (source_map_file == "") return 0;+ char* result = 0;+ string map = emitter.generate_source_map(*this);+ result = sass_strdup(map.c_str());+ return result;+ }+++ std::vector<std::string> Context::get_included_files(size_t skip)+ {+ vector<string> includes = included_files;+ if (includes.size() == 0) return includes;+ std::sort( includes.begin() + skip, includes.end() );+ includes.erase( includes.begin(), includes.begin() + skip );+ includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );+ // the skip solution seems more robust, as we may have real files named stdin+ // includes.erase( std::remove( includes.begin(), includes.end(), "stdin" ), includes.end() );+ return includes;+ }++ string Context::get_cwd()+ {+ return Sass::File::get_cwd();+ }++ void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)+ {+ Definition* def = make_native_function(sig, f, ctx);+ def->environment(env);+ (*env)[def->name() + "[f]"] = def;+ }++ void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)+ {+ Definition* def = make_native_function(sig, f, ctx);+ stringstream ss;+ ss << def->name() << "[f]" << arity;+ def->environment(env);+ (*env)[ss.str()] = def;+ }++ void register_overload_stub(Context& ctx, string name, Env* env)+ {+ Definition* stub = new (ctx.mem) Definition(ParserState("[built-in function]"),+ 0,+ name,+ 0,+ 0,+ &ctx,+ true);+ (*env)[name + "[f]"] = stub;+ }+++ void register_built_in_functions(Context& ctx, Env* env)+ {+ using namespace Functions;+ // RGB Functions+ register_function(ctx, rgb_sig, rgb, env);+ register_overload_stub(ctx, "rgba", env);+ register_function(ctx, rgba_4_sig, rgba_4, 4, env);+ register_function(ctx, rgba_2_sig, rgba_2, 2, env);+ register_function(ctx, red_sig, red, env);+ register_function(ctx, green_sig, green, env);+ register_function(ctx, blue_sig, blue, env);+ register_function(ctx, mix_sig, mix, env);+ // HSL Functions+ register_function(ctx, hsl_sig, hsl, env);+ register_function(ctx, hsla_sig, hsla, env);+ register_function(ctx, hue_sig, hue, env);+ register_function(ctx, saturation_sig, saturation, env);+ register_function(ctx, lightness_sig, lightness, env);+ register_function(ctx, adjust_hue_sig, adjust_hue, env);+ register_function(ctx, lighten_sig, lighten, env);+ register_function(ctx, darken_sig, darken, env);+ register_function(ctx, saturate_sig, saturate, env);+ register_function(ctx, desaturate_sig, desaturate, env);+ register_function(ctx, grayscale_sig, grayscale, env);+ register_function(ctx, complement_sig, complement, env);+ register_function(ctx, invert_sig, invert, env);+ // Opacity Functions+ register_function(ctx, alpha_sig, alpha, env);+ register_function(ctx, opacity_sig, alpha, env);+ register_function(ctx, opacify_sig, opacify, env);+ register_function(ctx, fade_in_sig, opacify, env);+ register_function(ctx, transparentize_sig, transparentize, env);+ register_function(ctx, fade_out_sig, transparentize, env);+ // Other Color Functions+ register_function(ctx, adjust_color_sig, adjust_color, env);+ register_function(ctx, scale_color_sig, scale_color, env);+ register_function(ctx, change_color_sig, change_color, env);+ register_function(ctx, ie_hex_str_sig, ie_hex_str, env);+ // String Functions+ register_function(ctx, unquote_sig, sass_unquote, env);+ register_function(ctx, quote_sig, sass_quote, env);+ register_function(ctx, str_length_sig, str_length, env);+ register_function(ctx, str_insert_sig, str_insert, env);+ register_function(ctx, str_index_sig, str_index, env);+ register_function(ctx, str_slice_sig, str_slice, env);+ register_function(ctx, to_upper_case_sig, to_upper_case, env);+ register_function(ctx, to_lower_case_sig, to_lower_case, env);+ // Number Functions+ register_function(ctx, percentage_sig, percentage, env);+ register_function(ctx, round_sig, round, env);+ register_function(ctx, ceil_sig, ceil, env);+ register_function(ctx, floor_sig, floor, env);+ register_function(ctx, abs_sig, abs, env);+ register_function(ctx, min_sig, min, env);+ register_function(ctx, max_sig, max, env);+ register_function(ctx, random_sig, random, env);+ // List Functions+ register_function(ctx, length_sig, length, env);+ register_function(ctx, nth_sig, nth, env);+ register_function(ctx, set_nth_sig, set_nth, env);+ register_function(ctx, index_sig, index, env);+ register_function(ctx, join_sig, join, env);+ register_function(ctx, append_sig, append, env);+ register_function(ctx, compact_sig, compact, env);+ register_function(ctx, zip_sig, zip, env);+ register_function(ctx, list_separator_sig, list_separator, env);+ // Map Functions+ register_function(ctx, map_get_sig, map_get, env);+ register_function(ctx, map_merge_sig, map_merge, env);+ register_function(ctx, map_remove_sig, map_remove, env);+ register_function(ctx, map_keys_sig, map_keys, env);+ register_function(ctx, map_values_sig, map_values, env);+ register_function(ctx, map_has_key_sig, map_has_key, env);+ register_function(ctx, keywords_sig, keywords, env);+ // Introspection Functions+ register_function(ctx, type_of_sig, type_of, env);+ register_function(ctx, unit_sig, unit, env);+ register_function(ctx, unitless_sig, unitless, env);+ register_function(ctx, comparable_sig, comparable, env);+ register_function(ctx, variable_exists_sig, variable_exists, env);+ register_function(ctx, global_variable_exists_sig, global_variable_exists, env);+ register_function(ctx, function_exists_sig, function_exists, env);+ register_function(ctx, mixin_exists_sig, mixin_exists, env);+ register_function(ctx, feature_exists_sig, feature_exists, env);+ register_function(ctx, call_sig, call, env);+ // Boolean Functions+ register_function(ctx, not_sig, sass_not, env);+ register_function(ctx, if_sig, sass_if, env);+ // Misc Functions+ register_function(ctx, inspect_sig, inspect, env);+ register_function(ctx, unique_id_sig, unique_id, env);+ }++ void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)+ {+ while (descrs && *descrs) {+ register_c_function(ctx, env, *descrs);+ ++descrs;+ }+ }+ void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)+ {+ Definition* def = make_c_function(descr, ctx);+ def->environment(env);+ (*env)[def->name() + "[f]"] = def;+ }+++}
+ libsass/context.hpp view
@@ -0,0 +1,157 @@+#ifndef SASS_CONTEXT_H+#define SASS_CONTEXT_H++#include <string>+#include <vector>+#include <map>++#define BUFFERSIZE 255+#include "b64/encode.h"++#include "ast_fwd_decl.hpp"+#include "kwd_arg_macros.hpp"+#include "memory_manager.hpp"+#include "environment.hpp"+#include "source_map.hpp"+#include "subset_map.hpp"+#include "output.hpp"+#include "plugins.hpp"+#include "sass_functions.h"++struct Sass_Function;++namespace Sass {+ using namespace std;+ struct Sass_Queued {+ string abs_path;+ string load_path;+ const char* source;+ public:+ Sass_Queued(const string& load_path, const string& abs_path, const char* source);+ };++ class Context {+ public:+ size_t head_imports;+ Memory_Manager<AST_Node> mem;++ struct Sass_Options* c_options;+ struct Sass_Compiler* c_compiler;+ const char* source_c_str;++ // c-strs containing Sass file contents+ // we will overtake ownership of memory+ vector<const char*> sources;+ // absolute paths to includes+ vector<string> included_files;+ // relative links to includes+ vector<string> include_links;+ // vectors above have same size++ vector<string> plugin_paths; // relative paths to load plugins+ vector<string> include_paths; // lookup paths for includes+ vector<Sass_Queued> queue; // queue of files to be parsed+ map<string, Block*> style_sheets; // map of paths to ASTs+ // SourceMap source_map;+ Output emitter;++ vector<Sass_Importer_Entry> c_headers;+ vector<Sass_Importer_Entry> c_importers;+ vector<Sass_Function_Entry> c_functions;++ void add_c_header(Sass_Importer_Entry header);+ void add_c_importer(Sass_Importer_Entry importer);+ void add_c_function(Sass_Function_Entry function);++ string indent; // String to be used for indentation+ string linefeed; // String to be used for line feeds+ string input_path; // for relative paths in src-map+ string output_path; // for relative paths to the output+ bool source_comments; // for inline debug comments in css output+ Output_Style output_style; // output style for the generated css code+ string source_map_file; // path to source map file (enables feature)+ string source_map_root; // path for sourceRoot property (pass-through)+ bool source_map_embed; // embed in sourceMappingUrl (as data-url)+ bool source_map_contents; // insert included contents into source map+ bool omit_source_map_url; // disable source map comment in css output+ bool is_indented_syntax_src; // treat source string as sass++ // overload import calls+ vector<Sass_Import_Entry> import_stack;++ map<string, Color*> names_to_colors;+ map<int, string> colors_to_names;++ size_t precision; // precision for outputting fractional numbers++ KWD_ARG_SET(Data) {+ KWD_ARG(Data, struct Sass_Options*, c_options);+ KWD_ARG(Data, struct Sass_Compiler*, c_compiler);+ KWD_ARG(Data, const char*, source_c_str);+ KWD_ARG(Data, string, entry_point);+ KWD_ARG(Data, string, input_path);+ KWD_ARG(Data, string, output_path);+ KWD_ARG(Data, string, indent);+ KWD_ARG(Data, string, linefeed);+ KWD_ARG(Data, const char*, include_paths_c_str);+ KWD_ARG(Data, const char*, plugin_paths_c_str);+ // KWD_ARG(Data, const char**, include_paths_array);+ // KWD_ARG(Data, const char**, plugin_paths_array);+ KWD_ARG(Data, vector<string>, include_paths);+ KWD_ARG(Data, vector<string>, plugin_paths);+ KWD_ARG(Data, bool, source_comments);+ KWD_ARG(Data, Output_Style, output_style);+ KWD_ARG(Data, string, source_map_file);+ KWD_ARG(Data, string, source_map_root);+ KWD_ARG(Data, bool, omit_source_map_url);+ KWD_ARG(Data, bool, is_indented_syntax_src);+ KWD_ARG(Data, size_t, precision);+ KWD_ARG(Data, bool, source_map_embed);+ KWD_ARG(Data, bool, source_map_contents);+ };++ Context(Data);+ ~Context();+ static string get_cwd();+ void setup_color_map();++ Block* parse_file();+ Block* parse_string();+ void add_source(string, string, const char*);++ string add_file(const string& file);+ string add_file(const string& base, const string& file);+++ // allow to optionally overwrite the input path+ // default argument for input_path is string("stdin")+ // usefull to influence the source-map generating etc.+ char* compile_file();+ char* compile_string();+ char* compile_block(Block* root);+ char* generate_source_map();++ vector<string> get_included_files(size_t skip = 0);++ private:+ void collect_plugin_paths(const char* paths_str);+ void collect_plugin_paths(const char** paths_array);+ void collect_include_paths(const char* paths_str);+ void collect_include_paths(const char** paths_array);+ string format_source_mapping_url(const string& file);++ string cwd;+ Plugins plugins;++ // void register_built_in_functions(Env* env);+ // void register_function(Signature sig, Native_Function f, Env* env);+ // void register_function(Signature sig, Native_Function f, size_t arity, Env* env);+ // void register_overload_stub(string name, Env* env);++ public:+ Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> > subset_map;+ };++}++#endif
+ libsass/contextualize.cpp view
@@ -0,0 +1,141 @@+#include "contextualize.hpp"+#include "ast.hpp"+#include "eval.hpp"+#include "backtrace.hpp"+#include "to_string.hpp"+#include "parser.hpp"++namespace Sass {++ Contextualize::Contextualize(Context& ctx, Env* env, Backtrace* bt, Selector* placeholder, Selector* extender)+ : ctx(ctx), env(env), backtrace(bt), parent(0), placeholder(placeholder), extender(extender)+ { }++ Contextualize::~Contextualize() { }++ Selector* Contextualize::fallback_impl(AST_Node* n)+ { return parent; }++ Contextualize* Contextualize::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex)+ {+ parent = s;+ env = e;+ backtrace = bt;+ placeholder = p;+ extender = ex;+ return this;+ }++ Selector* Contextualize::operator()(Selector_List* s)+ {+ Selector_List* p = static_cast<Selector_List*>(parent);+ Selector_List* ss = 0;+ if (p) {+ ss = new (ctx.mem) Selector_List(s->pstate(), p->length() * s->length());+ for (size_t i = 0, L = p->length(); i < L; ++i) {+ for (size_t j = 0, L = s->length(); j < L; ++j) {+ parent = (*p)[i];+ Complex_Selector* comb = static_cast<Complex_Selector*>((*s)[j]->perform(this));+ if (parent->has_line_feed()) comb->has_line_feed(true);+ if (comb) *ss << comb;+ }+ }+ }+ else {+ ss = new (ctx.mem) Selector_List(s->pstate(), s->length());+ for (size_t j = 0, L = s->length(); j < L; ++j) {+ Complex_Selector* comb = static_cast<Complex_Selector*>((*s)[j]->perform(this));+ if (comb) *ss << comb;+ }+ }+ return ss->length() ? ss : 0;+ }++ Selector* Contextualize::operator()(Complex_Selector* s)+ {+ To_String to_string(&ctx);+ Complex_Selector* ss = new (ctx.mem) Complex_Selector(*s);+ // ss->last_block(s->last_block());+ // ss->media_block(s->media_block());+ Compound_Selector* new_head = 0;+ Complex_Selector* new_tail = 0;+ if (ss->head()) {+ new_head = static_cast<Compound_Selector*>(s->head()->perform(this));+ ss->head(new_head);+ }+ if (ss->tail()) {+ new_tail = static_cast<Complex_Selector*>(s->tail()->perform(this));+ // new_tail->last_block(s->last_block());+ // new_tail->media_block(s->media_block());+ ss->tail(new_tail);+ }+ if ((new_head && new_head->has_placeholder()) || (new_tail && new_tail->has_placeholder())) {+ ss->has_placeholder(true);+ }+ else {+ ss->has_placeholder(false);+ }+ if (!ss->head() && ss->combinator() == Complex_Selector::ANCESTOR_OF) {+ return ss->tail();+ }+ else {+ return ss;+ }+ }++ Selector* Contextualize::operator()(Compound_Selector* s)+ {+ To_String to_string(&ctx);+ if (placeholder && extender && s->perform(&to_string) == placeholder->perform(&to_string)) {+ return extender;+ }+ Compound_Selector* ss = new (ctx.mem) Compound_Selector(s->pstate(), s->length());+ ss->last_block(s->last_block());+ ss->media_block(s->media_block());+ ss->has_line_break(s->has_line_break());+ for (size_t i = 0, L = s->length(); i < L; ++i) {+ Simple_Selector* simp = static_cast<Simple_Selector*>((*s)[i]->perform(this));+ if (simp) *ss << simp;+ }+ return ss->length() ? ss : 0;+ }++ Selector* Contextualize::operator()(Wrapped_Selector* s)+ {+ Selector* old_parent = parent;+ parent = 0;+ Wrapped_Selector* neg = new (ctx.mem) Wrapped_Selector(s->pstate(),+ s->name(),+ s->selector()->perform(this));+ parent = old_parent;+ return neg;+ }++ Selector* Contextualize::operator()(Pseudo_Selector* s)+ { return s; }++ Selector* Contextualize::operator()(Selector_Qualifier* s)+ { return s; }++ Selector* Contextualize::operator()(Type_Selector* s)+ { return s; }++ Selector* Contextualize::operator()(Selector_Placeholder* p)+ {+ To_String to_string(&ctx);+ if (placeholder && extender && p->perform(&to_string) == placeholder->perform(&to_string)) {+ return extender;+ }+ else {+ return p;+ }+ }++ Selector* Contextualize::operator()(Selector_Reference* s)+ {+ if (!parent) return 0;+ Selector_Reference* ss = new (ctx.mem) Selector_Reference(*s);+ ss->selector(parent);+ return ss;+ }+}
+ libsass/contextualize.hpp view
@@ -0,0 +1,46 @@+#ifndef SASS_CONTEXTUALIZE_H+#define SASS_CONTEXTUALIZE_H++#include "context.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {+ struct Backtrace;++ typedef Environment<AST_Node*> Env;++ class Contextualize : public Operation_CRTP<Selector*, Contextualize> {+++ public:+ Context& ctx;+ Env* env;+ Backtrace* backtrace;+ Selector* parent;+ Selector* placeholder;+ Selector* extender;++ Selector* fallback_impl(AST_Node* n);+ Contextualize(Context&, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0);+ virtual ~Contextualize();+ Contextualize* with(Selector*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0);+ using Operation<Selector*>::operator();++ Selector* operator()(Selector_List*);+ Selector* operator()(Complex_Selector*);+ Selector* operator()(Compound_Selector*);+ Selector* operator()(Wrapped_Selector*);+ Selector* operator()(Pseudo_Selector*);+ Selector* operator()(Selector_Qualifier*);+ Selector* operator()(Type_Selector*);+ Selector* operator()(Selector_Placeholder*);+ Selector* operator()(Selector_Reference*);++ template <typename U>+ Selector* fallback(U x) { return fallback_impl(x); }+ };+}++#endif
+ libsass/contextualize_eval.cpp view
@@ -0,0 +1,93 @@+#include "contextualize_eval.hpp"+#include "ast.hpp"+#include "eval.hpp"+#include "backtrace.hpp"+#include "to_string.hpp"+#include "parser.hpp"++namespace Sass {++ Contextualize_Eval::Contextualize_Eval(Context& ctx, Eval* eval, Env* env, Backtrace* bt)+ : Contextualize(ctx, env, bt), eval(eval)+ { }++ Contextualize_Eval::~Contextualize_Eval() { }++ Selector* Contextualize_Eval::fallback_impl(AST_Node* n)+ {+ return Contextualize::fallback_impl(n);+ }++ Contextualize_Eval* Contextualize_Eval::with(Selector* s, Env* e, Backtrace* bt, Selector* p, Selector* ex)+ {+ Contextualize::with(s, e, bt, p, ex);+ eval = eval->with(s, e, bt, p, ex);+ return this;+ }++ Selector* Contextualize_Eval::operator()(Selector_Schema* s)+ {+ To_String to_string;+ string result_str(s->contents()->perform(eval)->perform(&to_string));+ result_str += '{'; // the parser looks for a brace to end the selector+ Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->pstate()).parse_selector_group();+ return result_sel->perform(this);+ }++ Selector* Contextualize_Eval::operator()(Selector_List* s)+ {+ return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Complex_Selector* s)+ {+ return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Compound_Selector* s)+ {+ return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Wrapped_Selector* s)+ {+ return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Pseudo_Selector* s)+ {+ return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Attribute_Selector* s)+ {+ // the value might be interpolated; evaluate it+ String* v = s->value();+ if (v && eval) {+ Eval* eval_with = eval->with(env, backtrace);+ v = static_cast<String*>(v->perform(eval_with));+ }+ To_String toString;+ Attribute_Selector* ss = new (ctx.mem) Attribute_Selector(*s);+ ss->value(v);+ return ss;+ }++ Selector* Contextualize_Eval::operator()(Selector_Qualifier* s)+ { return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Type_Selector* s)+ { return Contextualize::operator ()(s);+ }++ Selector* Contextualize_Eval::operator()(Selector_Placeholder* p)+ {+ return Contextualize::operator ()(p);+ }++ Selector* Contextualize_Eval::operator()(Selector_Reference* s)+ {+ return Contextualize::operator ()(s);+ }+}
+ libsass/contextualize_eval.hpp view
@@ -0,0 +1,44 @@+#ifndef SASS_CONTEXTUALIZE_EVAL_H+#define SASS_CONTEXTUALIZE_EVAL_H++#include "eval.hpp"+#include "context.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {+ struct Backtrace;++ typedef Environment<AST_Node*> Env;++ class Contextualize_Eval : public Contextualize {++ Eval* eval;++ Selector* fallback_impl(AST_Node* n);++ public:+ Contextualize_Eval(Context&, Eval*, Env*, Backtrace*);+ virtual ~Contextualize_Eval();+ Contextualize_Eval* with(Selector*, Env*, Backtrace*, Selector* placeholder = 0, Selector* extender = 0);+ using Operation<Selector*>::operator();++ Selector* operator()(Selector_Schema*);+ Selector* operator()(Selector_List*);+ Selector* operator()(Complex_Selector*);+ Selector* operator()(Compound_Selector*);+ Selector* operator()(Wrapped_Selector*);+ Selector* operator()(Pseudo_Selector*);+ Selector* operator()(Attribute_Selector*);+ Selector* operator()(Selector_Qualifier*);+ Selector* operator()(Type_Selector*);+ Selector* operator()(Selector_Placeholder*);+ Selector* operator()(Selector_Reference*);++ template <typename U>+ Selector* fallback(U x) { return fallback_impl(x); }+ };+}++#endif
+ libsass/cssize.cpp view
@@ -0,0 +1,549 @@+#include <iostream>+#include <typeinfo>++#include "cssize.hpp"+#include "to_string.hpp"+#include "context.hpp"+#include "backtrace.hpp"++namespace Sass {++ Cssize::Cssize(Context& ctx, Env* env, Backtrace* bt)+ : ctx(ctx),+ env(env),+ block_stack(vector<Block*>()),+ p_stack(vector<Statement*>()),+ backtrace(bt)+ { }++ Statement* Cssize::parent()+ {+ return p_stack.size() ? p_stack.back() : block_stack.front();+ }++ Statement* Cssize::operator()(Block* b)+ {+ Env new_env;+ new_env.link(*env);+ env = &new_env;+ Block* bb = new (ctx.mem) Block(b->pstate(), b->length(), b->is_root());+ // bb->tabs(b->tabs());+ block_stack.push_back(bb);+ append_block(b);+ block_stack.pop_back();+ env = env->parent();+ return bb;+ }++ Statement* Cssize::operator()(At_Rule* r)+ {+ if (!r->block() || !r->block()->length()) return r;++ if (parent()->statement_type() == Statement::RULESET)+ {+ return (r->is_keyframes()) ? new (ctx.mem) Bubble(r->pstate(), r) : bubble(r);+ }++ p_stack.push_back(r);+ At_Rule* rr = new (ctx.mem) At_Rule(r->pstate(),+ r->keyword(),+ r->selector(),+ r->block() ? r->block()->perform(this)->block() : 0);+ if (r->value()) rr->value(r->value());+ p_stack.pop_back();++ bool directive_exists = false;+ size_t L = rr->block() ? rr->block()->length() : 0;+ for (size_t i = 0; i < L && !directive_exists; ++i) {+ Statement* s = (*r->block())[i];+ if (s->statement_type() != Statement::BUBBLE) directive_exists = true;+ else {+ s = static_cast<Bubble*>(s)->node();+ if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;+ else directive_exists = (static_cast<At_Rule*>(s)->keyword() == rr->keyword());+ }++ }++ Block* result = new (ctx.mem) Block(rr->pstate());+ if (!(directive_exists || rr->is_keyframes()))+ {+ At_Rule* empty_node = static_cast<At_Rule*>(rr);+ empty_node->block(new (ctx.mem) Block(rr->block() ? rr->block()->pstate() : rr->pstate()));+ *result << empty_node;+ }++ Statement* ss = debubble(rr->block() ? rr->block() : new (ctx.mem) Block(rr->pstate()), rr);+ for (size_t i = 0, L = ss->block()->length(); i < L; ++i) {+ *result << (*ss->block())[i];+ }++ return result;+ }++ Statement* Cssize::operator()(Keyframe_Rule* r)+ {+ if (!r->block() || !r->block()->length()) return r;++ Keyframe_Rule* rr = new (ctx.mem) Keyframe_Rule(r->pstate(),+ r->block()->perform(this)->block());+ if (r->selector()) rr->selector(r->selector());++ return debubble(rr->block(), rr)->block();+ }++ Statement* Cssize::operator()(Ruleset* r)+ {+ p_stack.push_back(r);+ Ruleset* rr = new (ctx.mem) Ruleset(r->pstate(),+ r->selector(),+ r->block()->perform(this)->block());+ // rr->tabs(r->block()->tabs());+ p_stack.pop_back();++ Block* props = new (ctx.mem) Block(rr->block()->pstate());+ Block* rules = new (ctx.mem) Block(rr->block()->pstate());+ for (size_t i = 0, L = rr->block()->length(); i < L; i++)+ {+ Statement* s = (*rr->block())[i];+ if (bubblable(s)) *rules << s;+ if (!bubblable(s)) *props << s;+ }++ if (props->length())+ {+ Block* bb = new (ctx.mem) Block(rr->block()->pstate());+ *bb += props;+ rr->block(bb);++ for (size_t i = 0, L = rules->length(); i < L; i++)+ {+ (*rules)[i]->tabs((*rules)[i]->tabs() + 1);+ }++ rules->unshift(rr);+ }++ rules = debubble(rules)->block();++ if (!(!rules->length() ||+ !bubblable(rules->last()) ||+ parent()->statement_type() == Statement::RULESET))+ {+ rules->last()->group_end(true);+ }++ return rules;+ }++ Statement* Cssize::operator()(Media_Block* m)+ {+ if (parent()->statement_type() == Statement::RULESET)+ { return bubble(m); }++ if (parent()->statement_type() == Statement::MEDIA)+ { return new (ctx.mem) Bubble(m->pstate(), m); }++ p_stack.push_back(m);++ Media_Block* mm = new (ctx.mem) Media_Block(m->pstate(),+ m->media_queries(),+ m->block()->perform(this)->block());+ mm->tabs(m->tabs());++ p_stack.pop_back();++ return debubble(mm->block(), mm)->block();+ }++ Statement* Cssize::operator()(Feature_Block* m)+ {+ if (!m->block()->length())+ { return m; }++ if (parent()->statement_type() == Statement::RULESET)+ { return bubble(m); }++ p_stack.push_back(m);++ Feature_Block* mm = new (ctx.mem) Feature_Block(m->pstate(),+ m->feature_queries(),+ m->block()->perform(this)->block());+ mm->tabs(m->tabs());++ p_stack.pop_back();++ return debubble(mm->block(), mm)->block();+ }++ Statement* Cssize::operator()(At_Root_Block* m)+ {+ bool tmp = false;+ for (size_t i = 0, L = p_stack.size(); i < L; ++i) {+ Statement* s = p_stack[i];+ tmp |= m->exclude_node(s);+ }++ if (!tmp)+ {+ Block* bb = m->block()->perform(this)->block();+ for (size_t i = 0, L = bb->length(); i < L; ++i) {+ // (bb->elements())[i]->tabs(m->tabs());+ if (bubblable((*bb)[i])) (*bb)[i]->tabs((*bb)[i]->tabs() + m->tabs());+ }+ if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end());+ return bb;+ }++ if (m->exclude_node(parent()))+ {+ return new (ctx.mem) Bubble(m->pstate(), m);+ }++ return bubble(m);+ }++ Statement* Cssize::bubble(At_Rule* m)+ {+ Block* bb = new (ctx.mem) Block(this->parent()->pstate());+ Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+ new_rule->block(bb);+ new_rule->tabs(this->parent()->tabs());++ size_t L = m->block() ? m->block()->length() : 0;+ for (size_t i = 0; i < L; ++i) {+ *new_rule->block() << (*m->block())[i];+ }++ Block* wrapper_block = new (ctx.mem) Block(m->block() ? m->block()->pstate() : m->pstate());+ *wrapper_block << new_rule;+ At_Rule* mm = new (ctx.mem) At_Rule(m->pstate(),+ m->keyword(),+ m->selector(),+ wrapper_block);+ if (m->value()) mm->value(m->value());++ Bubble* bubble = new (ctx.mem) Bubble(mm->pstate(), mm);+ return bubble;+ }++ Statement* Cssize::bubble(At_Root_Block* m)+ {+ Block* bb = new (ctx.mem) Block(this->parent()->pstate());+ Has_Block* new_rule = static_cast<Has_Block*>(shallow_copy(this->parent()));+ new_rule->block(bb);+ new_rule->tabs(this->parent()->tabs());++ for (size_t i = 0, L = m->block()->length(); i < L; ++i) {+ *new_rule->block() << (*m->block())[i];+ }++ Block* wrapper_block = new (ctx.mem) Block(m->block()->pstate());+ *wrapper_block << new_rule;+ At_Root_Block* mm = new (ctx.mem) At_Root_Block(m->pstate(),+ wrapper_block,+ m->expression());++ Bubble* bubble = new (ctx.mem) Bubble(mm->pstate(), mm);+ return bubble;+ }++ Statement* Cssize::bubble(Feature_Block* m)+ {+ Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));++ Block* bb = new (ctx.mem) Block(parent->block()->pstate());+ Ruleset* new_rule = new (ctx.mem) Ruleset(parent->pstate(),+ parent->selector(),+ bb);+ new_rule->tabs(parent->tabs());++ for (size_t i = 0, L = m->block()->length(); i < L; ++i) {+ *new_rule->block() << (*m->block())[i];+ }++ Block* wrapper_block = new (ctx.mem) Block(m->block()->pstate());+ *wrapper_block << new_rule;+ Feature_Block* mm = new (ctx.mem) Feature_Block(m->pstate(),+ m->feature_queries(),+ wrapper_block);++ mm->tabs(m->tabs());++ Bubble* bubble = new (ctx.mem) Bubble(mm->pstate(), mm);+ return bubble;+ }++ Statement* Cssize::bubble(Media_Block* m)+ {+ Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));++ Block* bb = new (ctx.mem) Block(parent->block()->pstate());+ Ruleset* new_rule = new (ctx.mem) Ruleset(parent->pstate(),+ parent->selector(),+ bb);+ new_rule->tabs(parent->tabs());++ for (size_t i = 0, L = m->block()->length(); i < L; ++i) {+ *new_rule->block() << (*m->block())[i];+ }++ Block* wrapper_block = new (ctx.mem) Block(m->block()->pstate());+ *wrapper_block << new_rule;+ Media_Block* mm = new (ctx.mem) Media_Block(m->pstate(),+ m->media_queries(),+ wrapper_block,+ m->selector());++ mm->tabs(m->tabs());++ Bubble* bubble = new (ctx.mem) Bubble(mm->pstate(), mm);++ return bubble;+ }++ bool Cssize::bubblable(Statement* s)+ {+ return s->statement_type() == Statement::RULESET || s->bubbles();+ }++ Statement* Cssize::flatten(Statement* s)+ {+ Block* bb = s->block();+ Block* result = new (ctx.mem) Block(bb->pstate(), 0, bb->is_root());+ for (size_t i = 0, L = bb->length(); i < L; ++i) {+ Statement* ss = (*bb)[i];+ if (ss->block()) {+ ss = flatten(ss);+ for (size_t j = 0, K = ss->block()->length(); j < K; ++j) {+ *result << (*ss->block())[j];+ }+ }+ else {+ *result << ss;+ }+ }+ return result;+ }++ vector<pair<bool, Block*>> Cssize::slice_by_bubble(Statement* b)+ {+ vector<pair<bool, Block*>> results;+ for (size_t i = 0, L = b->block()->length(); i < L; ++i) {+ Statement* value = (*b->block())[i];+ bool key = value->statement_type() == Statement::BUBBLE;++ if (!results.empty() && results.back().first == key)+ {+ Block* wrapper_block = results.back().second;+ *wrapper_block << value;+ }+ else+ {+ Block* wrapper_block = new (ctx.mem) Block(value->pstate());+ *wrapper_block << value;+ results.push_back(make_pair(key, wrapper_block));+ }+ }+ return results;+ }++ Statement* Cssize::shallow_copy(Statement* s)+ {+ switch (s->statement_type())+ {+ case Statement::RULESET:+ return new (ctx.mem) Ruleset(*static_cast<Ruleset*>(s));+ case Statement::MEDIA:+ return new (ctx.mem) Media_Block(*static_cast<Media_Block*>(s));+ case Statement::BUBBLE:+ return new (ctx.mem) Bubble(*static_cast<Bubble*>(s));+ case Statement::DIRECTIVE:+ return new (ctx.mem) At_Rule(*static_cast<At_Rule*>(s));+ case Statement::FEATURE:+ return new (ctx.mem) Feature_Block(*static_cast<Feature_Block*>(s));+ case Statement::ATROOT:+ return new (ctx.mem) At_Root_Block(*static_cast<At_Root_Block*>(s));+ case Statement::KEYFRAMERULE:+ return new (ctx.mem) Keyframe_Rule(*static_cast<Keyframe_Rule*>(s));+ case Statement::NONE:+ default:+ error("unknown internal error; please contact the LibSass maintainers", s->pstate(), backtrace);+ String_Constant* msg = new (ctx.mem) String_Constant(ParserState("[WARN]"), string("`CSSize` can't clone ") + typeid(*s).name());+ return new (ctx.mem) Warning(ParserState("[WARN]"), msg);+ }+ }++ Statement* Cssize::debubble(Block* children, Statement* parent)+ {+ Has_Block* previous_parent = 0;+ vector<pair<bool, Block*>> baz = slice_by_bubble(children);+ Block* result = new (ctx.mem) Block(children->pstate());++ for (size_t i = 0, L = baz.size(); i < L; ++i) {+ bool is_bubble = baz[i].first;+ Block* slice = baz[i].second;++ if (!is_bubble) {+ if (!parent) {+ *result << slice;+ }+ else if (previous_parent) {+ *previous_parent->block() += slice;+ }+ else {+ previous_parent = static_cast<Has_Block*>(shallow_copy(parent));+ previous_parent->tabs(parent->tabs());++ Has_Block* new_parent = static_cast<Has_Block*>(shallow_copy(parent));+ new_parent->block(slice);+ new_parent->tabs(parent->tabs());++ *result << new_parent;+ }+ continue;+ }++ Block* wrapper_block = new (ctx.mem) Block(children->block()->pstate(),+ children->block()->length(),+ children->block()->is_root());++ for (size_t j = 0, K = slice->length(); j < K; ++j)+ {+ Statement* ss = 0;+ Bubble* b = static_cast<Bubble*>((*slice)[j]);++ if (!parent ||+ parent->statement_type() != Statement::MEDIA ||+ b->node()->statement_type() != Statement::MEDIA ||+ static_cast<Media_Block*>(b->node())->media_queries() == static_cast<Media_Block*>(parent)->media_queries())+ {+ ss = b->node();+ }+ else+ {+ List* mq = merge_media_queries(static_cast<Media_Block*>(b->node()), static_cast<Media_Block*>(parent));+ static_cast<Media_Block*>(b->node())->media_queries(mq);+ ss = b->node();+ }++ if (!ss) continue;++ ss->tabs(ss->tabs() + b->tabs());+ ss->group_end(b->group_end());++ if (!ss) continue;++ Block* bb = new (ctx.mem) Block(children->block()->pstate(),+ children->block()->length(),+ children->block()->is_root());+ *bb << ss->perform(this);+ Statement* wrapper = flatten(bb);+ *wrapper_block << wrapper;++ if (wrapper->block()->length()) {+ previous_parent = 0;+ }+ }++ if (wrapper_block) {+ *result << flatten(wrapper_block);+ }+ }++ return flatten(result);+ }++ Statement* Cssize::fallback_impl(AST_Node* n)+ {+ return static_cast<Statement*>(n);+ }++ void Cssize::append_block(Block* b)+ {+ Block* current_block = block_stack.back();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* ith = (*b)[i]->perform(this);+ if (ith && ith->block()) {+ for (size_t j = 0, K = ith->block()->length(); j < K; ++j) {+ *current_block << (*ith->block())[j];+ }+ }+ else if (ith) {+ *current_block << ith;+ }+ }+ }++ List* Cssize::merge_media_queries(Media_Block* m1, Media_Block* m2)+ {+ List* qq = new (ctx.mem) List(m1->media_queries()->pstate(),+ m1->media_queries()->length(),+ List::COMMA);++ for (size_t i = 0, L = m1->media_queries()->length(); i < L; i++) {+ for (size_t j = 0, K = m2->media_queries()->length(); j < K; j++) {+ Media_Query* mq1 = static_cast<Media_Query*>((*m1->media_queries())[i]);+ Media_Query* mq2 = static_cast<Media_Query*>((*m2->media_queries())[j]);+ Media_Query* mq = merge_media_query(mq1, mq2);++ if (mq) *qq << mq;+ }+ }++ return qq;+ }+++ Media_Query* Cssize::merge_media_query(Media_Query* mq1, Media_Query* mq2)+ {+ To_String to_string(&ctx);++ string type;+ string mod;++ string m1 = string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");+ string t1 = mq1->media_type() ? mq1->media_type()->perform(&to_string) : "";+ string m2 = string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");+ string t2 = mq2->media_type() ? mq2->media_type()->perform(&to_string) : "";+++ if (t1.empty()) t1 = t2;+ if (t2.empty()) t2 = t1;++ if ((m1 == "not") ^ (m2 == "not")) {+ if (t1 == t2) {+ return 0;+ }+ type = m1 == "not" ? t2 : t1;+ mod = m1 == "not" ? m2 : m1;+ }+ else if (m1 == "not" && m2 == "not") {+ if (t1 != t2) {+ return 0;+ }+ type = t1;+ mod = "not";+ }+ else if (t1 != t2) {+ return 0;+ } else {+ type = t1;+ mod = m1.empty() ? m2 : m1;+ }++ Media_Query* mm = new (ctx.mem) Media_Query(+ mq1->pstate(), 0,+ mq1->length() + mq2->length(), mod == "not", mod == "only"+ );++ if (!type.empty()) {+ mm->media_type(new (ctx.mem) String_Constant(mq1->pstate(), type));+ }++ *mm += mq2;+ *mm += mq1;+ return mm;+ }+}
+ libsass/cssize.hpp view
@@ -0,0 +1,82 @@+#ifndef SASS_CSSIZE_H+#define SASS_CSSIZE_H++#include <vector>+#include <iostream>++#include "ast.hpp"+#include "context.hpp"+#include "operation.hpp"+#include "environment.hpp"++namespace Sass {+ using namespace std;++ typedef Environment<AST_Node*> Env;+ struct Backtrace;++ class Cssize : public Operation_CRTP<Statement*, Cssize> {++ Context& ctx;+ Env* env;+ vector<Block*> block_stack;+ vector<Statement*> p_stack;+ Backtrace* backtrace;++ Statement* fallback_impl(AST_Node* n);++ public:+ Cssize(Context&, Env*, Backtrace*);+ virtual ~Cssize() { }++ using Operation<Statement*>::operator();++ Statement* operator()(Block*);+ Statement* operator()(Ruleset*);+ // Statement* operator()(Propset*);+ // Statement* operator()(Bubble*);+ Statement* operator()(Media_Block*);+ Statement* operator()(Feature_Block*);+ Statement* operator()(At_Root_Block*);+ Statement* operator()(At_Rule*);+ Statement* operator()(Keyframe_Rule*);+ // Statement* operator()(Declaration*);+ // Statement* operator()(Assignment*);+ // Statement* operator()(Import*);+ // Statement* operator()(Import_Stub*);+ // Statement* operator()(Warning*);+ // Statement* operator()(Error*);+ // Statement* operator()(Comment*);+ // Statement* operator()(If*);+ // Statement* operator()(For*);+ // Statement* operator()(Each*);+ // Statement* operator()(While*);+ // Statement* operator()(Return*);+ // Statement* operator()(Extension*);+ // Statement* operator()(Definition*);+ // Statement* operator()(Mixin_Call*);+ // Statement* operator()(Content*);++ Statement* parent();+ vector<pair<bool, Block*>> slice_by_bubble(Statement*);+ Statement* bubble(At_Rule*);+ Statement* bubble(At_Root_Block*);+ Statement* bubble(Media_Block*);+ Statement* bubble(Feature_Block*);+ Statement* shallow_copy(Statement*);+ Statement* debubble(Block* children, Statement* parent = 0);+ Statement* flatten(Statement*);+ bool bubblable(Statement*);++ List* merge_media_queries(Media_Block*, Media_Block*);+ Media_Query* merge_media_query(Media_Query*, Media_Query*);++ template <typename U>+ Statement* fallback(U x) { return fallback_impl(x); }++ void append_block(Block*);+ };++}++#endif
+ libsass/debug.hpp view
@@ -0,0 +1,39 @@+#ifndef SASS_DEBUG_H+#define SASS_DEBUG_H++#include <stdint.h>++enum dbg_lvl_t : uint32_t {+ NONE = 0,+ TRIM = 1,+ CHUNKS = 2,+ SUBWEAVE = 4,+ WEAVE = 8,+ EXTEND_COMPOUND = 16,+ EXTEND_COMPLEX = 32,+ LCS = 64,+ EXTEND_OBJECT = 128,+ ALL = UINT32_MAX+};++#ifdef DEBUG++#ifndef DEBUG_LVL+const uint32_t debug_lvl = UINT32_MAX;+#else+const uint32_t debug_lvl = (DEBUG_LVL);+#endif // DEBUG_LVL++#define DEBUG_PRINT(lvl, x) if((lvl) & debug_lvl) { std::cerr << x; }+#define DEBUG_PRINTLN(lvl, x) if((lvl) & debug_lvl) { std::cerr << x << std::endl; }+#define DEBUG_EXEC(lvl, x) if((lvl) & debug_lvl) { x; }++#else // DEBUG++#define DEBUG_PRINT(lvl, x)+#define DEBUG_PRINTLN(lvl, x)+#define DEBUG_EXEC(lvl, x)++#endif // DEBUG++#endif // SASS_DEBUG
+ libsass/debugger.hpp view
@@ -0,0 +1,391 @@+#ifndef SASS_DEBUGGER_H+#define SASS_DEBUGGER_H++#include <string>+#include <sstream>+#include "ast_fwd_decl.hpp"++using namespace std;+using namespace Sass;++inline string str_replace(std::string str, const std::string& oldStr, const std::string& newStr)+{+ size_t pos = 0;+ while((pos = str.find(oldStr, pos)) != std::string::npos)+ {+ str.replace(pos, oldStr.length(), newStr);+ pos += newStr.length();+ }+ return str;+}++inline string prettyprint(const string& str) {+ string clean = str_replace(str, "\n", "\\n");+ clean = str_replace(clean, " ", "\\t");+ clean = str_replace(clean, "\r", "\\r");+ return clean;+}++inline string longToHex(long long t) {+ std::stringstream is;+ is << std::hex << t;+ return is.str();+}++inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)+{+ if (node == 0) return;+ if (ind == "") cerr << "####################################################################\n";+ if (dynamic_cast<Bubble*>(node)) {+ Bubble* bubble = dynamic_cast<Bubble*>(node);+ cerr << ind << "Bubble " << bubble << " " << bubble->tabs() << endl;+ } else if (dynamic_cast<At_Root_Block*>(node)) {+ At_Root_Block* root_block = dynamic_cast<At_Root_Block*>(node);+ cerr << ind << "At_Root_Block " << root_block << " " << root_block->tabs() << endl;+ if (root_block->block()) for(auto i : root_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Selector_List*>(node)) {+ Selector_List* selector = dynamic_cast<Selector_List*>(node);++ cerr << ind << "Selector_List " << selector+ << " [block:" << selector->last_block() << "]"+ << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "")+ << " [@media:" << selector->media_block() << "]"+ << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << endl;++ for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }++// } else if (dynamic_cast<Expression*>(node)) {+// Expression* expression = dynamic_cast<Expression*>(node);+// cerr << ind << "Expression " << expression << " " << expression->concrete_type() << endl;++ } else if (dynamic_cast<Parent_Selector*>(node)) {+ Parent_Selector* selector = dynamic_cast<Parent_Selector*>(node);+ cerr << ind << "Parent_Selector " << selector;+ cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl;+ debug_ast(selector->selector(), ind + "->", env);++ } else if (dynamic_cast<Complex_Selector*>(node)) {+ Complex_Selector* selector = dynamic_cast<Complex_Selector*>(node);+ cerr << ind << "Complex_Selector " << selector+ << " [block:" << selector->last_block() << "]"+ << " [weight:" << longToHex(selector->specificity()) << "]"+ << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "")+ << " [@media:" << selector->media_block() << "]"+ << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -") << " -> ";+ switch (selector->combinator()) {+ case Complex_Selector::PARENT_OF: cerr << "{>}"; break;+ case Complex_Selector::PRECEDES: cerr << "{~}"; break;+ case Complex_Selector::ADJACENT_TO: cerr << "{+}"; break;+ case Complex_Selector::ANCESTOR_OF: cerr << "{ }"; break;+ }+ cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl;+ debug_ast(selector->head(), ind + " ", env);+ debug_ast(selector->tail(), ind + "-", env);+ } else if (dynamic_cast<Compound_Selector*>(node)) {+ Compound_Selector* selector = dynamic_cast<Compound_Selector*>(node);+ cerr << ind << "Compound_Selector " << selector;+ cerr << " [block:" << selector->last_block() << "]";+ cerr << " [weight:" << longToHex(selector->specificity()) << "]";+ // cerr << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "");+ cerr << " [@media:" << selector->media_block() << "]";+ cerr << (selector->is_optional() ? " [is_optional]": " -");+ cerr << (selector->has_line_break() ? " [line-break]": " -");+ cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl;+ for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Propset*>(node)) {+ Propset* selector = dynamic_cast<Propset*>(node);+ cerr << ind << "Propset " << selector << " " << selector->tabs() << endl;+ if (selector->block()) for(auto i : selector->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Wrapped_Selector*>(node)) {+ Wrapped_Selector* selector = dynamic_cast<Wrapped_Selector*>(node);+ cerr << ind << "Wrapped_Selector " << selector << " <<" << selector->name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl;+ debug_ast(selector->selector(), ind + " () ", env);+ } else if (dynamic_cast<Pseudo_Selector*>(node)) {+ Pseudo_Selector* selector = dynamic_cast<Pseudo_Selector*>(node);+ cerr << ind << "Pseudo_Selector " << selector << " <<" << selector->name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl;+ debug_ast(selector->expression(), ind + " <= ", env);+ } else if (dynamic_cast<Attribute_Selector*>(node)) {+ Attribute_Selector* selector = dynamic_cast<Attribute_Selector*>(node);+ cerr << ind << "Attribute_Selector " << selector << " <<" << selector->name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl;+ debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);+ } else if (dynamic_cast<Selector_Qualifier*>(node)) {+ Selector_Qualifier* selector = dynamic_cast<Selector_Qualifier*>(node);+ cerr << ind << "Selector_Qualifier " << selector << " <<" << selector->name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl;+ } else if (dynamic_cast<Type_Selector*>(node)) {+ Type_Selector* selector = dynamic_cast<Type_Selector*>(node);+ cerr << ind << "Type_Selector " << selector << " <<" << selector->name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") <<+ " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << endl;+ } else if (dynamic_cast<Selector_Placeholder*>(node)) {++ Selector_Placeholder* selector = dynamic_cast<Selector_Placeholder*>(node);+ cerr << ind << "Selector_Placeholder [" << selector->name() << "] " << selector+ << " [block:" << selector->last_block() << "]"+ << " [@media:" << selector->media_block() << "]"+ << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << endl;++ } else if (dynamic_cast<Selector_Reference*>(node)) {+ Selector_Reference* selector = dynamic_cast<Selector_Reference*>(node);+ cerr << ind << "Selector_Reference " << selector << " @ref " << selector->selector() << endl;+ } else if (dynamic_cast<Simple_Selector*>(node)) {+ Simple_Selector* selector = dynamic_cast<Simple_Selector*>(node);+ cerr << ind << "Simple_Selector " << selector << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << endl;++ } else if (dynamic_cast<Selector_Schema*>(node)) {+ Selector_Schema* selector = dynamic_cast<Selector_Schema*>(node);+ cerr << ind << "Selector_Schema " << selector+ << " [block:" << selector->last_block() << "]"+ << (selector->last_block() && selector->last_block()->is_root() ? " [root]" : "")+ << " [@media:" << selector->media_block() << "]"+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << endl;++ debug_ast(selector->contents(), ind + " ");+ // for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }++ } else if (dynamic_cast<Selector*>(node)) {+ Selector* selector = dynamic_cast<Selector*>(node);+ cerr << ind << "Selector " << selector+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << endl;++ } else if (dynamic_cast<Media_Query_Expression*>(node)) {+ Media_Query_Expression* block = dynamic_cast<Media_Query_Expression*>(node);+ cerr << ind << "Media_Query_Expression " << block+ << (block->is_interpolated() ? " [is_interpolated]": " -")+ << endl;+ debug_ast(block->feature(), ind + " f) ");+ debug_ast(block->value(), ind + " v) ");++ } else if (dynamic_cast<Media_Query*>(node)) {+ Media_Query* block = dynamic_cast<Media_Query*>(node);+ cerr << ind << "Media_Query " << block+ << (block->is_negated() ? " [is_negated]": " -")+ << (block->is_restricted() ? " [is_restricted]": " -")+ << endl;+ debug_ast(block->media_type(), ind + " ");+ for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }++ } else if (dynamic_cast<Media_Block*>(node)) {+ Media_Block* block = dynamic_cast<Media_Block*>(node);+ cerr << ind << "Media_Block " << block << " " << block->tabs() << endl;+ debug_ast(block->media_queries(), ind + " =@ ");+ debug_ast(block->selector(), ind + " -@ ");+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Feature_Block*>(node)) {+ Feature_Block* block = dynamic_cast<Feature_Block*>(node);+ cerr << ind << "Feature_Block " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Block*>(node)) {+ Block* root_block = dynamic_cast<Block*>(node);+ cerr << ind << "Block " << root_block << " " << root_block->tabs() << endl;+ if (root_block->block()) for(auto i : root_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Warning*>(node)) {+ Warning* block = dynamic_cast<Warning*>(node);+ cerr << ind << "Warning " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Error*>(node)) {+ Error* block = dynamic_cast<Error*>(node);+ cerr << ind << "Error " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Debug*>(node)) {+ Debug* block = dynamic_cast<Debug*>(node);+ cerr << ind << "Debug " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Comment*>(node)) {+ Comment* block = dynamic_cast<Comment*>(node);+ cerr << ind << "Comment " << block << " " << block->tabs() <<+ " <" << prettyprint(block->pstate().token.ws_before()) << ">" << endl;+ debug_ast(block->text(), ind + "// ", env);+ } else if (dynamic_cast<If*>(node)) {+ If* block = dynamic_cast<If*>(node);+ cerr << ind << "If " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Return*>(node)) {+ Return* block = dynamic_cast<Return*>(node);+ cerr << ind << "Return " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Extension*>(node)) {+ Extension* block = dynamic_cast<Extension*>(node);+ cerr << ind << "Extension " << block << " " << block->tabs() << endl;+ debug_ast(block->selector(), ind + "-> ", env);+ } else if (dynamic_cast<Content*>(node)) {+ Content* block = dynamic_cast<Content*>(node);+ cerr << ind << "Content " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Import_Stub*>(node)) {+ Import_Stub* block = dynamic_cast<Import_Stub*>(node);+ cerr << ind << "Import_Stub " << block << " " << block->tabs() << endl;+ } else if (dynamic_cast<Import*>(node)) {+ Import* block = dynamic_cast<Import*>(node);+ cerr << ind << "Import " << block << " " << block->tabs() << endl;+ // vector<string> files_;+ for (auto imp : block->urls()) debug_ast(imp, "@ ", env);+ } else if (dynamic_cast<Assignment*>(node)) {+ Assignment* block = dynamic_cast<Assignment*>(node);+ cerr << ind << "Assignment " << block << " <<" << block->variable() << ">> " << block->tabs() << endl;+ debug_ast(block->value(), ind + "=", env);+ } else if (dynamic_cast<Declaration*>(node)) {+ Declaration* block = dynamic_cast<Declaration*>(node);+ cerr << ind << "Declaration " << block << " " << block->tabs() << endl;+ debug_ast(block->property(), ind + " prop: ", env);+ debug_ast(block->value(), ind + " value: ", env);+ } else if (dynamic_cast<Keyframe_Rule*>(node)) {+ Keyframe_Rule* has_block = dynamic_cast<Keyframe_Rule*>(node);+ cerr << ind << "Keyframe_Rule " << has_block << " " << has_block->tabs() << endl;+ if (has_block->selector()) debug_ast(has_block->selector(), ind + "@");+ if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<At_Rule*>(node)) {+ At_Rule* block = dynamic_cast<At_Rule*>(node);+ cerr << ind << "At_Rule " << block << " [" << block->keyword() << "] " << block->tabs() << endl;+ debug_ast(block->value(), ind + "+", env);+ debug_ast(block->selector(), ind + "~", env);+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Each*>(node)) {+ Each* block = dynamic_cast<Each*>(node);+ cerr << ind << "Each " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<For*>(node)) {+ For* block = dynamic_cast<For*>(node);+ cerr << ind << "For " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<While*>(node)) {+ While* block = dynamic_cast<While*>(node);+ cerr << ind << "While " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Definition*>(node)) {+ Definition* block = dynamic_cast<Definition*>(node);+ cerr << ind << "Definition " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Mixin_Call*>(node)) {+ Mixin_Call* block = dynamic_cast<Mixin_Call*>(node);+ cerr << ind << "Mixin_Call " << block << " " << block->tabs() << endl;+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Ruleset*>(node)) {+ Ruleset* ruleset = dynamic_cast<Ruleset*>(node);+ cerr << ind << "Ruleset " << ruleset << " " << ruleset->tabs() << endl;+ debug_ast(ruleset->selector(), ind + " ");+ if (ruleset->block()) for(auto i : ruleset->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Block*>(node)) {+ Block* block = dynamic_cast<Block*>(node);+ cerr << ind << "Block " << block << " " << block->tabs() << endl;+ for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Textual*>(node)) {+ Textual* expression = dynamic_cast<Textual*>(node);+ cerr << ind << "Textual ";+ if (expression->type() == Textual::NUMBER) cerr << " [NUMBER]";+ else if (expression->type() == Textual::PERCENTAGE) cerr << " [PERCENTAGE]";+ else if (expression->type() == Textual::DIMENSION) cerr << " [DIMENSION]";+ else if (expression->type() == Textual::HEX) cerr << " [HEX]";+ cerr << expression << " [" << expression->value() << "]" << endl;+ } else if (dynamic_cast<Variable*>(node)) {+ Variable* expression = dynamic_cast<Variable*>(node);+ cerr << ind << "Variable " << expression << " [" << expression->name() << "]" << endl;+ string name(expression->name());+ if (env && env->has(name)) debug_ast(static_cast<Expression*>((*env)[name]), ind + " -> ", env);+ } else if (dynamic_cast<Function_Call_Schema*>(node)) {+ Function_Call_Schema* expression = dynamic_cast<Function_Call_Schema*>(node);+ cerr << ind << "Function_Call_Schema " << expression << "]" << endl;+ debug_ast(expression->name(), ind + "name: ", env);+ debug_ast(expression->arguments(), ind + " args: ", env);+ } else if (dynamic_cast<Function_Call*>(node)) {+ Function_Call* expression = dynamic_cast<Function_Call*>(node);+ cerr << ind << "Function_Call " << expression << " [" << expression->name() << "]" << endl;+ debug_ast(expression->arguments(), ind + " args: ", env);+ } else if (dynamic_cast<Arguments*>(node)) {+ Arguments* expression = dynamic_cast<Arguments*>(node);+ cerr << ind << "Arguments " << expression << "]" << endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Argument*>(node)) {+ Argument* expression = dynamic_cast<Argument*>(node);+ cerr << ind << "Argument " << expression << " [" << expression->value() << "]" << endl;+ debug_ast(expression->value(), ind + " value: ", env);+ } else if (dynamic_cast<Unary_Expression*>(node)) {+ Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);+ cerr << ind << "Unary_Expression " << expression << " [" << expression->type() << "]" << endl;+ debug_ast(expression->operand(), ind + " operand: ", env);+ } else if (dynamic_cast<Binary_Expression*>(node)) {+ Binary_Expression* expression = dynamic_cast<Binary_Expression*>(node);+ cerr << ind << "Binary_Expression " << expression << " [" << expression->type() << "]" << endl;+ debug_ast(expression->left(), ind + " left: ", env);+ debug_ast(expression->right(), ind + " right: ", env);+ } else if (dynamic_cast<Map*>(node)) {+ Map* expression = dynamic_cast<Map*>(node);+ cerr << ind << "Map " << expression << " [Hashed]" << endl;+ } else if (dynamic_cast<List*>(node)) {+ List* expression = dynamic_cast<List*>(node);+ cerr << ind << "List " << expression << " (" << expression->length() << ") " <<+ (expression->separator() == Sass::List::Separator::COMMA ? "Comma " : "Space ") <<+ " [delayed: " << expression->is_delayed() << "] " <<+ " [interpolant: " << expression->is_interpolant() << "] " <<+ endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Content*>(node)) {+ Content* expression = dynamic_cast<Content*>(node);+ cerr << ind << "Content " << expression << " [Statement]" << endl;+ } else if (dynamic_cast<Boolean*>(node)) {+ Boolean* expression = dynamic_cast<Boolean*>(node);+ cerr << ind << "Boolean " << expression << " [" << expression->value() << "]" << endl;+ } else if (dynamic_cast<Color*>(node)) {+ Color* expression = dynamic_cast<Color*>(node);+ cerr << ind << "Color " << expression << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << endl;+ } else if (dynamic_cast<Number*>(node)) {+ Number* expression = dynamic_cast<Number*>(node);+ cerr << ind << "Number " << expression << " [" << expression->value() << expression->unit() << "]" << endl;+ } else if (dynamic_cast<String_Quoted*>(node)) {+ String_Quoted* expression = dynamic_cast<String_Quoted*>(node);+ cerr << ind << "String_Quoted : " << expression << " [" << prettyprint(expression->value()) << "]" <<+ (expression->is_delayed() ? " {delayed}" : "") <<+ (expression->sass_fix_1291() ? " {sass_fix_1291}" : "") <<+ (expression->quote_mark() != 0 ? " {qm:" + string(1, expression->quote_mark()) + "}" : "") <<+ " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;+ } else if (dynamic_cast<String_Constant*>(node)) {+ String_Constant* expression = dynamic_cast<String_Constant*>(node);+ cerr << ind << "String_Constant : " << expression << " [" << prettyprint(expression->value()) << "]" <<+ (expression->is_delayed() ? " {delayed}" : "") <<+ (expression->sass_fix_1291() ? " {sass_fix_1291}" : "") <<+ " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;+ } else if (dynamic_cast<String_Schema*>(node)) {+ String_Schema* expression = dynamic_cast<String_Schema*>(node);+ cerr << ind << "String_Schema " << expression << " " << expression->concrete_type() <<+ (expression->has_interpolants() ? " {has_interpolants}" : "") <<+ endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<String*>(node)) {+ String* expression = dynamic_cast<String*>(node);+ cerr << ind << "String " << expression << expression->concrete_type() <<+ " " << (expression->sass_fix_1291() ? "{sass_fix_1291}" : "") <<+ endl;+ } else if (dynamic_cast<Expression*>(node)) {+ Expression* expression = dynamic_cast<Expression*>(node);+ cerr << ind << "Expression " << expression;+ switch (expression->concrete_type()) {+ case Expression::Concrete_Type::NONE: cerr << " [NONE]"; break;+ case Expression::Concrete_Type::BOOLEAN: cerr << " [BOOLEAN]"; break;+ case Expression::Concrete_Type::NUMBER: cerr << " [NUMBER]"; break;+ case Expression::Concrete_Type::COLOR: cerr << " [COLOR]"; break;+ case Expression::Concrete_Type::STRING: cerr << " [STRING]"; break;+ case Expression::Concrete_Type::LIST: cerr << " [LIST]"; break;+ case Expression::Concrete_Type::MAP: cerr << " [MAP]"; break;+ case Expression::Concrete_Type::SELECTOR: cerr << " [SELECTOR]"; break;+ case Expression::Concrete_Type::NULL_VAL: cerr << " [NULL_VAL]"; break;+ case Expression::Concrete_Type::NUM_TYPES: cerr << " [NUM_TYPES]"; break;+ }+ cerr << endl;+ } else if (dynamic_cast<Has_Block*>(node)) {+ Has_Block* has_block = dynamic_cast<Has_Block*>(node);+ cerr << ind << "Has_Block " << has_block << " " << has_block->tabs() << endl;+ if (has_block->block()) for(auto i : has_block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Statement*>(node)) {+ Statement* statement = dynamic_cast<Statement*>(node);+ cerr << ind << "Statement " << statement << " " << statement->tabs() << endl;+ }++ if (ind == "") cerr << "####################################################################\n";+}++#endif // SASS_DEBUGGER
+ libsass/emitter.cpp view
@@ -0,0 +1,255 @@+#include "util.hpp"+#include "context.hpp"+#include "output.hpp"+#include "emitter.hpp"+#include "utf8_string.hpp"++namespace Sass {+ using namespace std;++ Emitter::Emitter(Context* ctx)+ : wbuf(),+ ctx(ctx),+ indentation(0),+ scheduled_space(0),+ scheduled_linefeed(0),+ scheduled_delimiter(false),+ in_comment(false),+ in_wrapped(false),+ in_media_block(false),+ in_declaration(false),+ in_declaration_list(false)+ { }++ // return buffer as string+ string Emitter::get_buffer(void)+ {+ return wbuf.buffer;+ }++ Output_Style Emitter::output_style(void)+ {+ return ctx ? ctx->output_style : COMPRESSED;+ }++ // PROXY METHODS FOR SOURCE MAPS++ void Emitter::add_source_index(size_t idx)+ { wbuf.smap.source_index.push_back(idx); }++ string Emitter::generate_source_map(Context &ctx)+ { return wbuf.smap.generate_source_map(ctx); }++ void Emitter::set_filename(const string& str)+ { wbuf.smap.file = str; }++ void Emitter::add_open_mapping(AST_Node* node)+ { wbuf.smap.add_open_mapping(node); }+ void Emitter::add_close_mapping(AST_Node* node)+ { wbuf.smap.add_close_mapping(node); }+ ParserState Emitter::remap(const ParserState& pstate)+ { return wbuf.smap.remap(pstate); }++ // MAIN BUFFER MANIPULATION++ // add outstanding delimiter+ void Emitter::finalize(void)+ {+ scheduled_space = 0;+ if (scheduled_linefeed)+ scheduled_linefeed = 1;+ flush_schedules();+ }++ // flush scheduled space/linefeed+ void Emitter::flush_schedules(void)+ {+ // check the schedule+ if (scheduled_linefeed) {+ string linefeeds = "";++ for (size_t i = 0; i < scheduled_linefeed; i++)+ linefeeds += ctx ? ctx->linefeed : "\n";+ scheduled_space = 0;+ scheduled_linefeed = 0;+ append_string(linefeeds);++ } else if (scheduled_space) {+ string spaces(scheduled_space, ' ');+ scheduled_space = 0;+ append_string(spaces);+ }+ if (scheduled_delimiter) {+ scheduled_delimiter = false;+ append_string(";");+ }+ }++ // prepend some text or token to the buffer+ void Emitter::prepend_output(const OutputBuffer& output)+ {+ wbuf.smap.prepend(output);+ wbuf.buffer = output.buffer + wbuf.buffer;+ }++ // prepend some text or token to the buffer+ void Emitter::prepend_string(const string& text)+ {+ wbuf.smap.prepend(Offset(text));+ wbuf.buffer = text + wbuf.buffer;+ }++ // append some text or token to the buffer+ void Emitter::append_string(const string& text)+ {+ // write space/lf+ flush_schedules();++ if (in_comment && output_style() == COMPACT) {+ // unescape comment nodes+ string out = comment_to_string(text);+ // add to buffer+ wbuf.buffer += out;+ // account for data in source-maps+ wbuf.smap.append(Offset(out));+ } else {+ // add to buffer+ wbuf.buffer += text;+ // account for data in source-maps+ wbuf.smap.append(Offset(text));+ }+ }++ // append some white-space only text+ void Emitter::append_wspace(const string& text)+ {+ if (text.empty()) return;+ if (peek_linefeed(text.c_str())) {+ scheduled_space = 0;+ append_mandatory_linefeed();+ }+ }++ // append some text or token to the buffer+ // this adds source-mappings for node start and end+ void Emitter::append_token(const string& text, AST_Node* node)+ {+ flush_schedules();+ add_open_mapping(node);+ append_string(text);+ add_close_mapping(node);+ }++ // HELPER METHODS++ void Emitter::append_indentation()+ {+ if (output_style() == COMPRESSED) return;+ if (output_style() == COMPACT) return;+ if (scheduled_linefeed && indentation)+ scheduled_linefeed = 1;+ string indent = "";+ for (size_t i = 0; i < indentation; i++)+ indent += ctx ? ctx->indent : " ";+ append_string(indent);+ }++ void Emitter::append_delimiter()+ {+ scheduled_delimiter = true;+ if (output_style() == COMPACT) {+ if (indentation == 0) {+ append_mandatory_linefeed();+ } else {+ append_mandatory_space();+ }+ } else if (output_style() != COMPRESSED) {+ append_optional_linefeed();+ }+ }++ void Emitter::append_comma_separator()+ {+ scheduled_space = 0;+ append_string(",");+ append_optional_space();+ }++ void Emitter::append_colon_separator()+ {+ scheduled_space = 0;+ append_string(":");+ append_optional_space();+ }++ void Emitter::append_mandatory_space()+ {+ scheduled_space = 1;+ }++ void Emitter::append_optional_space()+ {+ if (output_style() != COMPRESSED && buffer().size()) {+ char lst = buffer().at(buffer().length() - 1);+ if (!isspace(lst)) append_mandatory_space();+ }+ }++ void Emitter::append_special_linefeed()+ {+ if (output_style() == COMPACT) {+ append_mandatory_linefeed();+ for (size_t p = 0; p < indentation; p++)+ append_string(ctx ? ctx->indent : " ");+ }+ }++ void Emitter::append_optional_linefeed()+ {+ if (output_style() == COMPACT) {+ append_mandatory_space();+ } else {+ append_mandatory_linefeed();+ }+ }++ void Emitter::append_mandatory_linefeed()+ {+ if (output_style() != COMPRESSED) {+ scheduled_linefeed = 1;+ scheduled_space = 0;+ // flush_schedules();+ }+ }++ void Emitter::append_scope_opener(AST_Node* node)+ {+ append_optional_space();+ flush_schedules();+ if (node) add_open_mapping(node);+ append_string("{");+ append_optional_linefeed();+ // append_optional_space();+ ++ indentation;+ }+ void Emitter::append_scope_closer(AST_Node* node)+ {+ -- indentation;+ scheduled_linefeed = 0;+ if (output_style() == COMPRESSED)+ scheduled_delimiter = false;+ if (output_style() == EXPANDED) {+ append_optional_linefeed();+ append_indentation();+ } else {+ append_optional_space();+ }+ append_string("}");+ if (node) add_close_mapping(node);+ append_optional_linefeed();+ if (indentation != 0) return;+ if (output_style() != COMPRESSED)+ scheduled_linefeed = 2;+ }++}
+ libsass/emitter.hpp view
@@ -0,0 +1,83 @@+#ifndef SASS_EMITTER_H+#define SASS_EMITTER_H++#include <string>+#include "source_map.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {+ class Context;+ using namespace std;++ class Emitter {++ public:+ Emitter(Context* ctx);+ virtual ~Emitter() { };++ protected:+ OutputBuffer wbuf;+ public:+ const string buffer(void) { return wbuf.buffer; }+ const SourceMap smap(void) { return wbuf.smap; }+ const OutputBuffer output(void) { return wbuf; }+ // proxy methods for source maps+ void add_source_index(size_t idx);+ void set_filename(const string& str);+ void add_open_mapping(AST_Node* node);+ void add_close_mapping(AST_Node* node);+ string generate_source_map(Context &ctx);+ ParserState remap(const ParserState& pstate);++ public:+ Context* ctx;+ size_t indentation;+ size_t scheduled_space;+ size_t scheduled_linefeed;+ bool scheduled_delimiter;++ public:+ bool in_comment;+ bool in_wrapped;+ bool in_media_block;+ bool in_declaration;+ bool in_declaration_list;++ public:+ // return buffer as string+ string get_buffer(void);+ // flush scheduled space/linefeed+ Output_Style output_style(void);+ // add outstanding linefeed+ void finalize(void);+ // flush scheduled space/linefeed+ void flush_schedules(void);+ // prepend some text or token to the buffer+ void prepend_string(const string& text);+ void prepend_output(const OutputBuffer& out);+ // append some text or token to the buffer+ void append_string(const string& text);+ // append some white-space only text+ void append_wspace(const string& text);+ // append some text or token to the buffer+ // this adds source-mappings for node start and end+ void append_token(const string& text, AST_Node* node);++ public: // syntax sugar+ void append_indentation();+ void append_optional_space(void);+ void append_mandatory_space(void);+ void append_special_linefeed(void);+ void append_optional_linefeed(void);+ void append_mandatory_linefeed(void);+ void append_scope_opener(AST_Node* node = 0);+ void append_scope_closer(AST_Node* node = 0);+ void append_comma_separator(void);+ void append_colon_separator(void);+ void append_delimiter(void);++ };++}++#endif
+ libsass/environment.hpp view
@@ -0,0 +1,167 @@+#ifndef SASS_ENVIRONMENT_H+#define SASS_ENVIRONMENT_H++#include <map>+#include <string>+#include <iostream>++#include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp"+#include "memory_manager.hpp"++namespace Sass {+ using std::string;+ using std::map;+ using std::cerr;+ using std::endl;++ template <typename T>+ class Environment {+ // TODO: test with unordered_map+ map<string, T> local_frame_;+ ADD_PROPERTY(Environment*, parent);++ public:+ Memory_Manager<AST_Node> mem;+ Environment() : local_frame_(map<string, T>()), parent_(0) { }++ // link parent to create a stack+ void link(Environment& env) { parent_ = &env; }+ void link(Environment* env) { parent_ = env; }++ // this is used to find the global frame+ // which is the second last on the stack+ bool is_lexical() const+ {+ return !! parent_ && parent_->parent_;+ }++ // only match the real root scope+ // there is still a parent around+ // not sure what it is actually use for+ // I guess we store functions etc. there+ bool is_root_scope() const+ {+ return parent_ && ! parent_->parent_;+ }++ // scope operates on the current frame++ map<string, T>& local_frame() {+ return local_frame_;+ }++ bool has_local(const string& key) const+ { return local_frame_.count(key); }++ T& get_local(const string& key)+ { return local_frame_[key]; }++ void set_local(const string& key, T val)+ { local_frame_[key] = val; }++ void del_local(const string& key)+ { local_frame_.erase(key); }+++ // global operates on the global frame+ // which is the second last on the stack++ Environment* global_env()+ {+ Environment* cur = this;+ while (cur->is_lexical()) {+ cur = cur->parent_;+ }+ return cur;+ }++ bool has_global(const string& key)+ { return global_env()->has(key); }++ T& get_global(const string& key)+ { return (*global_env())[key]; }++ void set_global(const string& key, T val)+ { global_env()->local_frame_[key] = val; }++ void del_global(const string& key)+ { global_env()->local_frame_.erase(key); }++ // see if we have a lexical variable+ // move down the stack but stop before we+ // reach the global frame (is not included)+ bool has_lexical(const string& key) const+ {+ auto cur = this;+ while (cur->is_lexical()) {+ if (cur->has_local(key)) return true;+ cur = cur->parent_;+ }+ return false;+ }++ // see if we have a lexical we could update+ // either update already existing lexical value+ // or if flag is set, we create one if no lexical found+ void set_lexical(const string& key, T val, bool global = false)+ {+ auto cur = this;+ while (cur->is_lexical()) {+ if (cur->has_local(key)) {+ cur->set_local(key, val);+ return;+ }+ cur = cur->parent_;+ }+ if (global) {+ set_global(key, val);+ } else {+ set_local(key, val);+ }+ }++ // look on the full stack for key+ // include all scopes available+ bool has(const string& key) const+ {+ auto cur = this;+ while (cur) {+ if (cur->has_local(key)) {+ return true;+ }+ cur = cur->parent_;+ }+ return false;+ }++ // use array access for getter and setter functions+ T& operator[](const string& key)+ {+ auto cur = this;+ while (cur) {+ if (cur->has_local(key)) {+ return cur->get_local(key);+ }+ cur = cur->parent_;+ }+ return get_local(key);+ }++ #ifdef DEBUG+ void print()+ {+ for (typename map<string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) {+ cerr << i->first << endl;+ }+ if (parent_) {+ cerr << "---" << endl;+ parent_->print();+ }+ }+ #endif++ };+}++#endif
+ libsass/error_handling.cpp view
@@ -0,0 +1,25 @@+#include "prelexer.hpp"+#include "backtrace.hpp"+#include "error_handling.hpp"++namespace Sass {++ Sass_Error::Sass_Error(Type type, ParserState pstate, string message)+ : type(type), pstate(pstate), message(message)+ { }++ void error(string msg, ParserState pstate)+ {+ throw Sass_Error(Sass_Error::syntax, pstate, msg);+ }++ void error(string msg, ParserState pstate, Backtrace* bt)+ {++ Backtrace top(bt, pstate, "");+ msg += top.to_string();++ throw Sass_Error(Sass_Error::syntax, pstate, msg);+ }++}
+ libsass/error_handling.hpp view
@@ -0,0 +1,29 @@+#ifndef SASS_ERROR_HANDLING_H+#define SASS_ERROR_HANDLING_H++#include <string>++#include "position.hpp"++namespace Sass {+ using namespace std;++ struct Backtrace;++ struct Sass_Error {+ enum Type { read, write, syntax, evaluation };++ Type type;+ ParserState pstate;+ string message;++ Sass_Error(Type type, ParserState pstate, string message);++ };++ void error(string msg, ParserState pstate);+ void error(string msg, ParserState pstate, Backtrace* bt);++}++#endif
+ libsass/eval.cpp view
@@ -0,0 +1,1288 @@+#include "file.hpp"+#include "eval.hpp"+#include "ast.hpp"+#include "bind.hpp"+#include "util.hpp"+#include "to_string.hpp"+#include "inspect.hpp"+#include "to_c.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "prelexer.hpp"+#include "parser.hpp"++#include <cstdlib>+#include <cmath>+#include <iostream>+#include <iomanip>+#include <typeinfo>++namespace Sass {+ using namespace std;++ inline double add(double x, double y) { return x + y; }+ inline double sub(double x, double y) { return x - y; }+ inline double mul(double x, double y) { return x * y; }+ inline double div(double x, double y) { return x / y; } // x/0 checked by caller+ typedef double (*bop)(double, double);+ bop ops[Binary_Expression::NUM_OPS] = {+ 0, 0, // and, or+ 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte+ add, sub, mul, div, fmod+ };++ Eval::Eval(Context& ctx, Contextualize* contextualize, Listize* listize, Env* env, Backtrace* bt)+ : ctx(ctx), contextualize(contextualize), listize(listize), env(env), backtrace(bt) { }+ Eval::~Eval() { }++ Eval* Eval::with(Env* e, Backtrace* bt) // for setting the env before eval'ing an expression+ {+ contextualize = contextualize->with(0, e, bt);+ env = e;+ backtrace = bt;+ return this;+ }++ Eval* Eval::with(Selector* c, Env* e, Backtrace* bt, Selector* p, Selector* ex) // for setting the env before eval'ing an expression+ {+ contextualize = contextualize->with(c, e, bt, p, ex);+ env = e;+ backtrace = bt;+ return this;+ }++ Expression* Eval::operator()(Block* b)+ {+ Expression* val = 0;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ val = (*b)[i]->perform(this);+ if (val) return val;+ }+ return val;+ }++ Expression* Eval::operator()(Assignment* a)+ {+ string var(a->variable());+ if (a->is_global()) {+ env->set_global(var, a->value()->perform(this));+ }+ else if (a->is_default()) {+ if (env->has_lexical(var)) return 0;+ if (env->has_global(var)) {+ Expression* e = static_cast<Expression*>(env->get_global(var));+ if (e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(this));+ }+ } else {+ env->set_global(var, a->value()->perform(this));+ }+ }+ else {+ env->set_lexical(var, a->value()->perform(this));+ }+ return 0;+ }++ Expression* Eval::operator()(If* i)+ {+ if (*i->predicate()->perform(this)) {+ return i->consequent()->perform(this);+ }+ else {+ Block* alt = i->alternative();+ if (alt) return alt->perform(this);+ }+ return 0;+ }++ // For does not create a new env scope+ // But iteration vars are reset afterwards+ Expression* Eval::operator()(For* f)+ {+ string variable(f->variable());+ Expression* low = f->lower_bound()->perform(this);+ if (low->concrete_type() != Expression::NUMBER) {+ error("lower bound of `@for` directive must be numeric", low->pstate());+ }+ Expression* high = f->upper_bound()->perform(this);+ if (high->concrete_type() != Expression::NUMBER) {+ error("upper bound of `@for` directive must be numeric", high->pstate());+ }+ Number* sass_start = static_cast<Number*>(low);+ Number* sass_end = static_cast<Number*>(high);+ // check if units are valid for sequence+ if (sass_start->unit() != sass_end->unit()) {+ stringstream msg; msg << "Incompatible units: '"+ << sass_start->unit() << "' and '"+ << sass_end->unit() << "'.";+ error(msg.str(), low->pstate(), backtrace);+ }+ double start = sass_start->value();+ double end = sass_end->value();+ // only create iterator once in this environment+ Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit());+ AST_Node* old_var = env->get_local(variable);+ env->set_local(variable, it);+ Block* body = f->block();+ Expression* val = 0;+ if (start < end) {+ if (f->is_inclusive()) ++end;+ for (double i = start;+ i < end;+ ++i) {+ it->value(i);+ env->set_local(variable, it);+ val = body->perform(this);+ if (val) break;+ }+ } else {+ if (f->is_inclusive()) --end;+ for (double i = start;+ i > end;+ --i) {+ it->value(i);+ env->set_local(variable, it);+ val = body->perform(this);+ if (val) break;+ }+ }+ // restore original environment+ if (!old_var) env->del_local(variable);+ else env->set_local(variable, old_var);+ return val;+ }++ // Eval does not create a new env scope+ // But iteration vars are reset afterwards+ Expression* Eval::operator()(Each* e)+ {+ vector<string> variables(e->variables());+ Expression* expr = e->list()->perform(this);+ List* list = 0;+ Map* map = 0;+ if (expr->concrete_type() == Expression::MAP) {+ map = static_cast<Map*>(expr);+ }+ else if (expr->concrete_type() != Expression::LIST) {+ list = new (ctx.mem) List(expr->pstate(), 1, List::COMMA);+ *list << expr;+ }+ else {+ list = static_cast<List*>(expr);+ }+ // remember variables and then reset them+ vector<AST_Node*> old_vars(variables.size());+ for (size_t i = 0, L = variables.size(); i < L; ++i) {+ old_vars[i] = env->get_local(variables[i]);+ env->set_local(variables[i], 0);+ }+ Block* body = e->block();+ Expression* val = 0;++ if (map) {+ for (auto key : map->keys()) {+ Expression* value = map->at(key);++ if (variables.size() == 1) {+ List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE);+ *variable << key;+ *variable << value;+ env->set_local(variables[0], variable);+ } else {+ env->set_local(variables[0], key);+ env->set_local(variables[1], value);+ }++ val = body->perform(this);+ if (val) break;+ }+ }+ else {+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ List* variable = 0;+ if ((*list)[i]->concrete_type() != Expression::LIST || variables.size() == 1) {+ variable = new (ctx.mem) List((*list)[i]->pstate(), 1, List::COMMA);+ *variable << (*list)[i];+ }+ else {+ variable = static_cast<List*>((*list)[i]);+ }+ for (size_t j = 0, K = variables.size(); j < K; ++j) {+ if (j < variable->length()) {+ env->set_local(variables[j], (*variable)[j]);+ }+ else {+ env->set_local(variables[j], new (ctx.mem) Null(expr->pstate()));+ }+ val = body->perform(this);+ if (val) break;+ }+ 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]);+ }+ return val;+ }++ Expression* Eval::operator()(While* w)+ {+ Expression* pred = w->predicate();+ Block* body = w->block();+ while (*pred->perform(this)) {+ Expression* val = body->perform(this);+ if (val) return val;+ }+ return 0;+ }++ Expression* Eval::operator()(Return* r)+ {+ return r->value()->perform(this);+ }++ Expression* Eval::operator()(Warning* w)+ {+ Expression* message = w->message()->perform(this);+ To_String to_string(&ctx);++ // try to use generic function+ if (env->has("@warn[f]")) {++ Definition* def = static_cast<Definition*>((*env)["@warn[f]"]);+ // Block* body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ string result(unquote(message->perform(&to_string)));+ Backtrace top(backtrace, w->pstate(), "");+ cerr << "WARNING: " << result;+ cerr << top.to_string(true);+ cerr << endl << endl;+ return 0;+ }++ Expression* Eval::operator()(Error* e)+ {+ Expression* message = e->message()->perform(this);+ To_String to_string(&ctx);++ // try to use generic function+ if (env->has("@error[f]")) {++ Definition* def = static_cast<Definition*>((*env)["@error[f]"]);+ // Block* body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ string result(unquote(message->perform(&to_string)));+ error(result, e->pstate());+ return 0;+ }++ Expression* Eval::operator()(Debug* d)+ {+ Expression* message = d->value()->perform(this);+ To_String to_string(&ctx);++ // try to use generic function+ if (env->has("@debug[f]")) {++ Definition* def = static_cast<Definition*>((*env)["@debug[f]"]);+ // Block* body = def->block();+ // Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();+ Sass_Function_Fn c_func = sass_function_get_function(c_function);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);+ sass_list_set_value(c_args, 0, message->perform(&to_c));+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ string cwd(ctx.get_cwd());+ string result(unquote(message->perform(&to_string)));+ string rel_path(Sass::File::resolve_relative_path(d->pstate().path, cwd, cwd));+ cerr << rel_path << ":" << d->pstate().line << ":" << " DEBUG: " << result;+ cerr << endl;+ return 0;+ }++ Expression* Eval::operator()(List* l)+ {+ if (l->is_expanded()) return l;+ List* ll = new (ctx.mem) List(l->pstate(),+ l->length(),+ l->separator(),+ l->is_arglist());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ *ll << (*l)[i]->perform(this);+ }+ ll->is_expanded(true);+ return ll;+ }++ Expression* Eval::operator()(Map* m)+ {+ if (m->is_expanded()) return m;+ Map* mm = new (ctx.mem) Map(m->pstate(),+ m->length());+ for (auto key : m->keys()) {+ *mm << std::make_pair(key->perform(this), m->at(key)->perform(this));+ }+ mm->is_expanded(true);+ return mm;+ }++ // -- only need to define two comparisons, and the rest can be implemented in terms of them+ bool eq(Expression*, Expression*, Context&, Eval*);+ bool lt(Expression*, Expression*, Context&);+ // -- arithmetic on the combinations that matter+ Expression* op_numbers(Context&, Binary_Expression*, Expression*, Expression*);+ Expression* op_number_color(Context&, Binary_Expression::Type, Expression*, Expression*);+ Expression* op_color_number(Context&, Binary_Expression::Type, Expression*, Expression*);+ Expression* op_colors(Context&, Binary_Expression::Type, Expression*, Expression*);+ Expression* op_strings(Context&, Binary_Expression::Type, Expression*, Expression*);++ Expression* Eval::operator()(Binary_Expression* b)+ {+ Binary_Expression::Type op_type = b->type();+ // don't eval delayed expressions (the '/' when used as a separator)+ if (op_type == Binary_Expression::DIV && b->is_delayed()) return b;+ // 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)) lhs = lhs->perform(this);++ switch (op_type) {+ case Binary_Expression::AND:+ return *lhs ? b->right()->perform(this) : lhs;+ break;++ case Binary_Expression::OR:+ return *lhs ? lhs : b->right()->perform(this);+ break;++ default:+ break;+ }+ // not a logical connective, so go ahead and eval the rhs+ Expression* rhs = b->right()->perform(this);+ rhs->is_delayed(false);+ while (typeid(*rhs) == typeid(Binary_Expression)) rhs = rhs->perform(this);++ // see if it's a relational expression+ switch(op_type) {+ case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->pstate(), eq(lhs, rhs, ctx));+ case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->pstate(), !eq(lhs, rhs, ctx));+ case Binary_Expression::GT: return new (ctx.mem) Boolean(b->pstate(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx));+ case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->pstate(), !lt(lhs, rhs, ctx));+ case Binary_Expression::LT: return new (ctx.mem) Boolean(b->pstate(), lt(lhs, rhs, ctx));+ case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->pstate(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx));++ default: break;+ }++ Expression::Concrete_Type l_type = lhs->concrete_type();+ Expression::Concrete_Type r_type = rhs->concrete_type();++ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {+ return op_numbers(ctx, b, lhs, rhs);+ }+ if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {+ return op_number_color(ctx, op_type, lhs, rhs);+ }+ if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {+ return op_color_number(ctx, op_type, lhs, rhs);+ }+ if (l_type == Expression::COLOR && r_type == Expression::COLOR) {+ return op_colors(ctx, op_type, lhs, rhs);+ }++ Expression* ex = op_strings(ctx, op_type, lhs, rhs);+ if (String_Constant* str = (String_Constant*) ex)+ {+ if (str->concrete_type() != Expression::STRING) return ex;+ String_Constant* lstr = dynamic_cast<String_Constant*>(lhs);+ String_Constant* rstr = dynamic_cast<String_Constant*>(rhs);+ if (String_Constant* org = lstr ? lstr : rstr)+ { str->quote_mark(org->quote_mark()); }+ }+ return ex;++ }++ Expression* Eval::operator()(Unary_Expression* u)+ {+ Expression* operand = u->operand()->perform(this);+ if (u->type() == Unary_Expression::NOT) {+ Boolean* result = new (ctx.mem) Boolean(u->pstate(), (bool)*operand);+ result->value(!result->value());+ return result;+ }+ else if (operand->concrete_type() == Expression::NUMBER) {+ Number* result = new (ctx.mem) Number(*static_cast<Number*>(operand));+ result->value(u->type() == Unary_Expression::MINUS+ ? -result->value()+ : result->value());+ 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)) {+ u->operand(new (ctx.mem) String_Constant(u->pstate(), ""));+ }+ else u->operand(operand);+ String_Constant* result = new (ctx.mem) String_Constant(u->pstate(),+ u->perform(&to_string));+ return result;+ }+ // unreachable+ return u;+ }++ Expression* Eval::operator()(Function_Call* c)+ {+ string name(Util::normalize_underscores(c->name()));+ string full_name(name + "[f]");+ Arguments* args = c->arguments();+ if (full_name != "if[f]") {+ args = static_cast<Arguments*>(args->perform(this));+ }++ // try to use generic function+ if (!env->has(full_name)) {+ if (env->has("*[f]")) {+ full_name = "*[f]";+ }+ }++ // if it doesn't exist, just pass it through as a literal+ if (!env->has(full_name)) {+ Function_Call* lit = new (ctx.mem) Function_Call(c->pstate(),+ c->name(),+ args);+ To_String to_string(&ctx);+ return new (ctx.mem) String_Constant(c->pstate(),+ lit->perform(&to_string));+ }++ Expression* result = c;+ Definition* def = static_cast<Definition*>((*env)[full_name]);+ Block* body = def->block();+ Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();++ if (full_name != "if[f]") {+ for (size_t i = 0, L = args->length(); i < L; ++i) {+ (*args)[i]->value((*args)[i]->value()->perform(this));+ }+ }++ Parameters* params = def->parameters();+ Env new_env;+ new_env.link(def->environment());+ // bind("function " + c->name(), params, args, ctx, &new_env, this);+ // Env* old_env = env;+ // env = &new_env;++ // Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`");+ // backtrace = &here;++ // if it's user-defined, eval the body+ if (body) {++ bind("function " + c->name(), params, args, ctx, &new_env, this);+ Env* old_env = env;+ env = &new_env;++ Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`");+ backtrace = &here;++ result = body->perform(this);+ if (!result) {+ error(string("function ") + c->name() + " did not return a value", c->pstate());+ }+ backtrace = here.parent;+ env = old_env;+ }+ // if it's native, invoke the underlying CPP function+ else if (func) {++ bind("function " + c->name(), params, args, ctx, &new_env, this);+ Env* old_env = env;+ env = &new_env;++ Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`");+ backtrace = &here;++ result = func(*env, *old_env, ctx, def->signature(), c->pstate(), backtrace);++ backtrace = here.parent;+ env = old_env;+ }+ // else if it's a user-defined c function+ else if (c_function) {++ Sass_Function_Fn c_func = sass_function_get_function(c_function);+ if (full_name == "*[f]") {+ String_Constant *str = new (ctx.mem) String_Constant(c->pstate(), c->name());+ Arguments* new_args = new (ctx.mem) Arguments(c->pstate());+ *new_args << new (ctx.mem) Argument(c->pstate(), str);+ *new_args += args;+ args = new_args;+ }++ // populates env with default values for params+ bind("function " + c->name(), params, args, ctx, &new_env, this);+ Env* old_env = env;+ env = &new_env;++ Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`");+ backtrace = &here;++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(env->local_frame().size(), SASS_COMMA);+ for(size_t i = 0; i < params[0].length(); i++) {+ string key = params[0][i]->name();+ AST_Node* node = env->local_frame().at(key);+ Expression* arg = static_cast<Expression*>(node);+ sass_list_set_value(c_args, i, arg->perform(&to_c));+ }+ Sass_Value* c_val = c_func(c_args, c_function, ctx.c_options);+ if (sass_value_get_tag(c_val) == SASS_ERROR) {+ error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace);+ } else if (sass_value_get_tag(c_val) == SASS_WARNING) {+ error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace);+ }+ result = cval_to_astnode(c_val, ctx, backtrace, c->pstate());++ backtrace = here.parent;+ sass_delete_value(c_args);+ if (c_val != c_args)+ sass_delete_value(c_val);+ env = old_env;+ }+ // else it's an overloaded native function; resolve it+ else if (def->is_overload_stub()) {+ size_t arity = args->length();+ stringstream ss;+ ss << full_name << arity;+ string resolved_name(ss.str());+ if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->pstate());+ Definition* resolved_def = static_cast<Definition*>((*env)[resolved_name]);+ params = resolved_def->parameters();+ Env newer_env;+ newer_env.link(resolved_def->environment());+ bind("function " + c->name(), params, args, ctx, &newer_env, this);+ Env* old_env = env;+ env = &newer_env;++ Backtrace here(backtrace, c->pstate(), ", in function `" + c->name() + "`");+ backtrace = &here;++ result = resolved_def->native_function()(*env, *old_env, ctx, resolved_def->signature(), c->pstate(), backtrace);++ backtrace = here.parent;+ env = old_env;+ }++ // backtrace = here.parent;+ // env = old_env;+++ // link back to function definition+ // only do this for custom functions+ if (result->pstate().file == string::npos)+ result->pstate(c->pstate());++ do {+ result->is_delayed(result->concrete_type() == Expression::STRING);+ result = result->perform(this);+ } while (result->concrete_type() == Expression::NONE);+ return result;+ }++ Expression* Eval::operator()(Function_Call_Schema* s)+ {+ Expression* evaluated_name = s->name()->perform(this);+ Expression* evaluated_args = s->arguments()->perform(this);+ String_Schema* ss = new (ctx.mem) String_Schema(s->pstate(), 2);+ (*ss) << evaluated_name << evaluated_args;+ return ss->perform(this);+ }++ Expression* Eval::operator()(Variable* v)+ {+ To_String to_string(&ctx);+ string name(v->name());+ Expression* value = 0;+ if (env->has(name)) value = static_cast<Expression*>((*env)[name]);+ else error("Undefined variable: \"" + v->name() + "\".", v->pstate());+ // cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << endl;+ if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();++ // behave according to as ruby sass (add leading zero)+ if (value->concrete_type() == Expression::NUMBER) {+ value = new (ctx.mem) Number(*static_cast<Number*>(value));+ static_cast<Number*>(value)->zero(true);+ }+ else if (value->concrete_type() == Expression::STRING) {+ if (auto str = dynamic_cast<String_Quoted*>(value)) {+ value = new (ctx.mem) String_Quoted(*str);+ } else if (auto str = dynamic_cast<String_Constant*>(value)) {+ value = new (ctx.mem) String_Constant(*str);+ }+ }+ else if (value->concrete_type() == Expression::LIST) {+ value = new (ctx.mem) List(*static_cast<List*>(value));+ }+ else if (value->concrete_type() == Expression::MAP) {+ value = new (ctx.mem) Map(*static_cast<Map*>(value));+ }+ else if (value->concrete_type() == Expression::BOOLEAN) {+ value = new (ctx.mem) Boolean(*static_cast<Boolean*>(value));+ }+ else if (value->concrete_type() == Expression::COLOR) {+ value = new (ctx.mem) Color(*static_cast<Color*>(value));+ }+ else if (value->concrete_type() == Expression::NULL_VAL) {+ value = new (ctx.mem) Null(value->pstate());+ }++ // cerr << "\ttype is now: " << typeid(*value).name() << endl << endl;+ return value;+ }++ Expression* Eval::operator()(Textual* t)+ {+ using Prelexer::number;+ Expression* result = 0;+ bool zero = !( t->value().substr(0, 1) == "." ||+ t->value().substr(0, 2) == "-." );++ const string& text = t->value();+ size_t num_pos = text.find_first_not_of(" \n\r\t");+ if (num_pos == string::npos) num_pos = text.length();+ size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);+ if (unit_pos == string::npos) unit_pos = text.length();+ const string& num = text.substr(num_pos, unit_pos - num_pos);++ switch (t->type())+ {+ case Textual::NUMBER:+ result = new (ctx.mem) Number(t->pstate(),+ sass_atof(num.c_str()),+ "",+ zero);+ break;+ case Textual::PERCENTAGE:+ result = new (ctx.mem) Number(t->pstate(),+ sass_atof(num.c_str()),+ "%",+ zero);+ break;+ case Textual::DIMENSION:+ result = new (ctx.mem) Number(t->pstate(),+ sass_atof(num.c_str()),+ Token(number(text.c_str())),+ zero);+ break;+ case Textual::HEX: {+ string hext(t->value().substr(1)); // chop off the '#'+ if (hext.length() == 6) {+ string r(hext.substr(0,2));+ string g(hext.substr(2,2));+ string b(hext.substr(4,2));+ result = new (ctx.mem) Color(t->pstate(),+ static_cast<double>(strtol(r.c_str(), NULL, 16)),+ static_cast<double>(strtol(g.c_str(), NULL, 16)),+ static_cast<double>(strtol(b.c_str(), NULL, 16)),+ 1, true,+ t->value());+ }+ else {+ result = new (ctx.mem) Color(t->pstate(),+ static_cast<double>(strtol(string(2,hext[0]).c_str(), NULL, 16)),+ static_cast<double>(strtol(string(2,hext[1]).c_str(), NULL, 16)),+ static_cast<double>(strtol(string(2,hext[2]).c_str(), NULL, 16)),+ 1, false,+ t->value());+ }+ } break;+ }+ return result;+ }++ Expression* Eval::operator()(Number* n)+ {+ // behave according to as ruby sass (add leading zero)+ return new (ctx.mem) Number(n->pstate(),+ n->value(),+ n->unit(),+ true);+ }++ Expression* Eval::operator()(Boolean* b)+ {+ return b;+ }++ char is_quoted(string str)+ {+ size_t len = str.length();+ if (len < 2) return 0;+ if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {+ return str[0];+ }+ else {+ return 0;+ }+ }++ string Eval::interpolation(Expression* s) {+ if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {+ if (str_quoted->quote_mark()) {+ return string_escape(str_quoted->value());+ } else {+ return evacuate_escapes(str_quoted->value());+ }+ } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {+ return evacuate_escapes(str_constant->value());+ } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {+ 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);+ } else {+ return evacuate_quotes(unq);+ }+ } else if (List* list = dynamic_cast<List*>(s)) {+ string acc = ""; // ToDo: different output styles+ string sep = list->separator() == List::Separator::COMMA ? "," : " ";+ if (ctx.output_style != COMPRESSED && sep == ",") sep += " ";+ bool initial = false;+ for(auto item : list->elements()) {+ if (initial) acc += sep;+ acc += interpolation(item);+ initial = true;+ }+ return evacuate_quotes(acc);+ } else if (Variable* var = dynamic_cast<Variable*>(s)) {+ string name(var->name());+ if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate());+ Expression* value = static_cast<Expression*>((*env)[name]);+ return evacuate_quotes(interpolation(value));+ } else if (Binary_Expression* var = dynamic_cast<Binary_Expression*>(s)) {+ Expression* ex = var->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else if (Function_Call* var = dynamic_cast<Function_Call*>(s)) {+ Expression* ex = var->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else if (Parent_Selector* var = dynamic_cast<Parent_Selector*>(s)) {+ Expression* ex = var->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else if (Unary_Expression* var = dynamic_cast<Unary_Expression*>(s)) {+ Expression* ex = var->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else if (Selector* var = dynamic_cast<Selector*>(s)) {+ Expression* ex = var->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else {+ To_String to_string(&ctx);+ // to_string.in_decl_list = true;+ return evacuate_quotes(s->perform(&to_string));+ }+ }++ Expression* Eval::operator()(String_Schema* s)+ {+ string acc;+ for (size_t i = 0, L = s->length(); i < L; ++i) {+ acc += interpolation((*s)[i]);+ }+ String_Quoted* str = 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('*');+ }+ return str;+ }++ Expression* Eval::operator()(String_Constant* s)+ {+ if (!s->quote_mark() && !s->is_delayed() && ctx.names_to_colors.count(s->value())) {+ Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]);+ c->pstate(s->pstate());+ c->disp(s->value());+ return c;+ }+ return s;+ }++ Expression* Eval::operator()(Feature_Query* q)+ {+ Feature_Query* qq = new (ctx.mem) Feature_Query(q->pstate(),+ q->length());+ for (size_t i = 0, L = q->length(); i < L; ++i) {+ *qq << static_cast<Feature_Query_Condition*>((*q)[i]->perform(this));+ }+ return qq;+ }++ Expression* Eval::operator()(Feature_Query_Condition* c)+ {+ String* feature = c->feature();+ Expression* value = c->value();+ value = (value ? value->perform(this) : 0);+ Feature_Query_Condition* cc = new (ctx.mem) Feature_Query_Condition(c->pstate(),+ c->length(),+ feature,+ value,+ c->operand(),+ c->is_root());+ for (size_t i = 0, L = c->length(); i < L; ++i) {+ *cc << static_cast<Feature_Query_Condition*>((*c)[i]->perform(this));+ }+ return cc;+ }++ Expression* Eval::operator()(At_Root_Expression* e)+ {+ Expression* feature = e->feature();+ feature = (feature ? feature->perform(this) : 0);+ Expression* value = e->value();+ value = (value ? value->perform(this) : 0);+ Expression* ee = new (ctx.mem) At_Root_Expression(e->pstate(),+ static_cast<String*>(feature),+ value,+ e->is_interpolated());+ return ee;+ }++ Expression* Eval::operator()(Media_Query* q)+ {+ To_String to_string(&ctx);+ String* t = q->media_type();+ t = static_cast<String*>(t ? t->perform(this) : 0);+ Media_Query* qq = new (ctx.mem) Media_Query(q->pstate(),+ t,+ q->length(),+ q->is_negated(),+ q->is_restricted());+ for (size_t i = 0, L = q->length(); i < L; ++i) {+ *qq << static_cast<Media_Query_Expression*>((*q)[i]->perform(this));+ }+ return qq;+ }++ Expression* Eval::operator()(Media_Query_Expression* e)+ {+ Expression* feature = e->feature();+ feature = (feature ? feature->perform(this) : 0);+ Expression* value = e->value();+ value = (value ? value->perform(this) : 0);+ return new (ctx.mem) Media_Query_Expression(e->pstate(),+ feature,+ value,+ e->is_interpolated());+ }++ Expression* Eval::operator()(Null* n)+ {+ return n;+ }++ Expression* Eval::operator()(Argument* a)+ {+ Expression* val = a->value();+ val->is_delayed(false);+ val = val->perform(this);+ val->is_delayed(false);++ bool is_rest_argument = a->is_rest_argument();+ bool is_keyword_argument = a->is_keyword_argument();++ if (a->is_rest_argument()) {+ if (val->concrete_type() == Expression::MAP) {+ is_rest_argument = false;+ is_keyword_argument = true;+ }+ else+ if(val->concrete_type() != Expression::LIST) {+ List* wrapper = new (ctx.mem) List(val->pstate(),+ 0,+ List::COMMA,+ true);+ *wrapper << val;+ val = wrapper;+ }+ }+ return new (ctx.mem) Argument(a->pstate(),+ val,+ a->name(),+ is_rest_argument,+ is_keyword_argument);+ }++ Expression* Eval::operator()(Arguments* a)+ {+ Arguments* aa = new (ctx.mem) Arguments(a->pstate());+ for (size_t i = 0, L = a->length(); i < L; ++i) {+ *aa << static_cast<Argument*>((*a)[i]->perform(this));+ }+ return aa;+ }++ Expression* Eval::operator()(Comment* c)+ {+ return 0;+ }++ Expression* Eval::operator()(Parent_Selector* p)+ {+ Selector* s = p->perform(contextualize);+ // access to parent selector may return 0+ Selector_List* l = static_cast<Selector_List*>(s);+ if (!s) { l = new (ctx.mem) Selector_List(p->pstate()); }+ return l->perform(listize);+ }++ inline Expression* Eval::fallback_impl(AST_Node* n)+ {+ return static_cast<Expression*>(n);+ }++ // All the binary helpers.++ bool eq(Expression* lhs, Expression* rhs, Context& ctx)+ {+ Expression::Concrete_Type ltype = lhs->concrete_type();+ Expression::Concrete_Type rtype = rhs->concrete_type();+ if (ltype != rtype) return false;+ switch (ltype) {++ case Expression::BOOLEAN: {+ return static_cast<Boolean*>(lhs)->value() ==+ static_cast<Boolean*>(rhs)->value();+ } break;++ case Expression::NUMBER: {+ Number* l = static_cast<Number*>(lhs);+ Number* r = static_cast<Number*>(rhs);+ Number tmp_r(*r);+ tmp_r.normalize(l->find_convertible_unit());+ return l->unit() == tmp_r.unit() && l->value() == tmp_r.value()+ ? true+ : false;+ } break;++ case Expression::COLOR: {+ Color* l = static_cast<Color*>(lhs);+ Color* r = static_cast<Color*>(rhs);+ return l->r() == r->r() &&+ l->g() == r->g() &&+ l->b() == r->b() &&+ l->a() == r->a();+ } break;++ case Expression::STRING: {+ return unquote(static_cast<String_Constant*>(lhs)->value()) ==+ unquote(static_cast<String_Constant*>(rhs)->value());+ } break;++ case Expression::LIST: {+ List* l = static_cast<List*>(lhs);+ List* r = static_cast<List*>(rhs);+ if (l->length() != r->length()) return false;+ if (l->separator() != r->separator()) return false;+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ if (!eq((*l)[i], (*r)[i], ctx)) return false;+ }+ return true;+ } break;++ case Expression::MAP: {+ Map* l = static_cast<Map*>(lhs);+ Map* r = static_cast<Map*>(rhs);+ if (l->length() != r->length()) return false;+ for (auto key : l->keys())+ if (!eq(l->at(key), r->at(key), ctx)) return false;+ return true;+ } break;+ case Expression::NULL_VAL: {+ return true;+ } break;++ default: break;+ }+ return false;+ }++ bool lt(Expression* lhs, Expression* rhs, Context& ctx)+ {+ if (lhs->concrete_type() != Expression::NUMBER ||+ rhs->concrete_type() != Expression::NUMBER)+ error("may only compare numbers", lhs->pstate());+ Number* l = static_cast<Number*>(lhs);+ Number* r = static_cast<Number*>(rhs);+ Number tmp_r(*r);+ tmp_r.normalize(l->find_convertible_unit());+ string l_unit(l->unit());+ string r_unit(tmp_r.unit());+ if (!l_unit.empty() && !r_unit.empty() && l->unit() != tmp_r.unit()) {+ error("cannot compare numbers with incompatible units", l->pstate());+ }+ return l->value() < tmp_r.value();+ }++ Expression* op_numbers(Context& ctx, Binary_Expression* b, Expression* lhs, Expression* rhs)+ {+ Number* l = static_cast<Number*>(lhs);+ Number* r = static_cast<Number*>(rhs);+ double lv = l->value();+ double rv = r->value();+ Binary_Expression::Type op = b->type();+ if (op == Binary_Expression::DIV && !rv) {+ return new (ctx.mem) String_Constant(l->pstate(), "Infinity");+ }+ if (op == Binary_Expression::MOD && !rv) {+ error("division by zero", r->pstate());+ }++ Number tmp(*r);+ tmp.normalize(l->find_convertible_unit());+ string l_unit(l->unit());+ string r_unit(tmp.unit());+ if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&+ (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) {+ error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", l->pstate());+ }+ Number* v = new (ctx.mem) Number(*l);+ v->pstate(b->pstate());+ if (l_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB || op == Binary_Expression::MOD)) {+ v->numerator_units() = r->numerator_units();+ v->denominator_units() = r->denominator_units();+ }++ v->value(ops[op](lv, tmp.value()));+ if (op == Binary_Expression::MUL) {+ for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {+ v->numerator_units().push_back(r->numerator_units()[i]);+ }+ for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) {+ v->denominator_units().push_back(r->denominator_units()[i]);+ }+ }+ else if (op == Binary_Expression::DIV) {+ for (size_t i = 0, S = r->numerator_units().size(); i < S; ++i) {+ v->denominator_units().push_back(r->numerator_units()[i]);+ }+ for (size_t i = 0, S = r->denominator_units().size(); i < S; ++i) {+ v->numerator_units().push_back(r->denominator_units()[i]);+ }+ }+ v->normalize();+ return v;+ }++ Expression* op_number_color(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)+ {+ Number* l = static_cast<Number*>(lhs);+ Color* r = static_cast<Color*>(rhs);+ // TODO: currently SASS converts colors to standard form when adding to strings;+ // when https://github.com/nex3/sass/issues/363 is added this can be removed to+ // preserve the original value+ r->disp("");+ double lv = l->value();+ switch (op) {+ case Binary_Expression::ADD:+ case Binary_Expression::MUL: {+ return new (ctx.mem) Color(l->pstate(),+ ops[op](lv, r->r()),+ ops[op](lv, r->g()),+ ops[op](lv, r->b()),+ r->a());+ } break;+ case Binary_Expression::SUB:+ case Binary_Expression::DIV: {+ string sep(op == Binary_Expression::SUB ? "-" : "/");+ To_String to_string(&ctx);+ string color(r->sixtuplet() && (ctx.output_style != COMPRESSED) ?+ r->perform(&to_string) :+ Util::normalize_sixtuplet(r->perform(&to_string)));+ return new (ctx.mem) String_Constant(l->pstate(),+ l->perform(&to_string)+ + sep+ + color);+ } break;+ case Binary_Expression::MOD: {+ error("cannot divide a number by a color", r->pstate());+ } break;+ default: break; // caller should ensure that we don't get here+ }+ // unreachable+ return l;+ }++ Expression* op_color_number(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)+ {+ Color* l = static_cast<Color*>(lhs);+ Number* r = static_cast<Number*>(rhs);+ double rv = r->value();+ if (op == Binary_Expression::DIV && !rv) error("division by zero", r->pstate());+ return new (ctx.mem) Color(l->pstate(),+ ops[op](l->r(), rv),+ ops[op](l->g(), rv),+ ops[op](l->b(), rv),+ l->a());+ }++ Expression* op_colors(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs)+ {+ Color* l = static_cast<Color*>(lhs);+ Color* r = static_cast<Color*>(rhs);+ if (l->a() != r->a()) {+ error("alpha channels must be equal when combining colors", r->pstate());+ }+ if ((op == Binary_Expression::DIV || op == Binary_Expression::MOD) &&+ (!r->r() || !r->g() ||!r->b())) {+ error("division by zero", r->pstate());+ }+ return new (ctx.mem) Color(l->pstate(),+ ops[op](l->r(), r->r()),+ ops[op](l->g(), r->g()),+ ops[op](l->b(), r->b()),+ l->a());+ }++ Expression* op_strings(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression*rhs)+ {+ To_String to_string(&ctx);+ Expression::Concrete_Type ltype = lhs->concrete_type();+ Expression::Concrete_Type rtype = rhs->concrete_type();++ string lstr(lhs->perform(&to_string));+ string rstr(rhs->perform(&to_string));++ 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 && ctx.names_to_colors.count(lstr) && !l_str_quoted;+ bool r_str_color = rtype == Expression::STRING && ctx.names_to_colors.count(rstr) && !r_str_quoted;++ if (l_str_color && r_str_color) {+ return op_colors(ctx, op, ctx.names_to_colors[lstr], ctx.names_to_colors[rstr]);+ }+ else if (l_str_color && rtype == Expression::COLOR) {+ return op_colors(ctx, op, ctx.names_to_colors[lstr], rhs);+ }+ else if (l_str_color && rtype == Expression::NUMBER) {+ return op_color_number(ctx, op, ctx.names_to_colors[lstr], rhs);+ }+ else if (ltype == Expression::COLOR && r_str_color) {+ return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]);+ }+ else if (ltype == Expression::NUMBER && r_str_color) {+ return op_number_color(ctx, op, lhs, ctx.names_to_colors[rstr]);+ }+ if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->pstate());+ if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->pstate());+ string sep;+ switch (op) {+ case Binary_Expression::SUB: sep = "-"; break;+ case Binary_Expression::DIV: 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\".", lhs->pstate());+ string result((lstr) + sep + (rstr));+ String_Quoted* str = new (ctx.mem) String_Quoted(lhs->pstate(), result);+ str->quote_mark(0);+ return str;+ }++ Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate)+ {+ using std::strlen;+ using std::strcpy;+ Expression* e = 0;+ switch (sass_value_get_tag(v)) {+ case SASS_BOOLEAN: {+ e = new (ctx.mem) Boolean(pstate, !!sass_boolean_get_value(v));+ } break;+ case SASS_NUMBER: {+ e = new (ctx.mem) Number(pstate, sass_number_get_value(v), sass_number_get_unit(v));+ } break;+ case SASS_COLOR: {+ e = new (ctx.mem) Color(pstate, sass_color_get_r(v), sass_color_get_g(v), sass_color_get_b(v), sass_color_get_a(v));+ } break;+ case SASS_STRING: {+ e = new (ctx.mem) String_Constant(pstate, sass_string_get_value(v));+ } break;+ case SASS_LIST: {+ List* l = new (ctx.mem) List(pstate, sass_list_get_length(v), sass_list_get_separator(v) == SASS_COMMA ? List::COMMA : List::SPACE);+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+ *l << cval_to_astnode(sass_list_get_value(v, i), ctx, backtrace, pstate);+ }+ e = l;+ } break;+ case SASS_MAP: {+ Map* m = new (ctx.mem) Map(pstate);+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+ *m << std::make_pair(+ cval_to_astnode(sass_map_get_key(v, i), ctx, backtrace, pstate),+ cval_to_astnode(sass_map_get_value(v, i), ctx, backtrace, pstate));+ }+ e = m;+ } break;+ case SASS_NULL: {+ e = new (ctx.mem) Null(pstate);+ } break;+ case SASS_ERROR: {+ error("Error in C function: " + string(sass_error_get_message(v)), pstate, backtrace);+ } break;+ case SASS_WARNING: {+ error("Warning in C function: " + string(sass_warning_get_message(v)), pstate, backtrace);+ } break;+ }+ return e;+ }++}
+ libsass/eval.hpp view
@@ -0,0 +1,88 @@+#ifndef SASS_EVAL_H+#define SASS_EVAL_H++#include <iostream>++#include "context.hpp"+#include "position.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "contextualize.hpp"+#include "listize.hpp"+#include "sass_values.h"++namespace Sass {+ using namespace std;++ typedef Environment<AST_Node*> Env;+ struct Backtrace;+ class Contextualize;+ class Listize;++ class Eval : public Operation_CRTP<Expression*, Eval> {++ Context& ctx;++ Expression* fallback_impl(AST_Node* n);++ public:+ Contextualize* contextualize;+ Listize* listize;+ Env* env;+ Backtrace* backtrace;+ Eval(Context&, Contextualize*, Listize*, Env*, Backtrace*);+ virtual ~Eval();+ Eval* with(Env* e, Backtrace* bt); // for setting the env before eval'ing an expression+ Eval* with(Selector* c, Env* e, Backtrace* bt, Selector* placeholder = 0, Selector* extender = 0); // for setting the env before eval'ing an expression+ using Operation<Expression*>::operator();++ // for evaluating function bodies+ Expression* operator()(Block*);+ Expression* operator()(Assignment*);+ Expression* operator()(If*);+ Expression* operator()(For*);+ Expression* operator()(Each*);+ Expression* operator()(While*);+ Expression* operator()(Return*);+ Expression* operator()(Warning*);+ Expression* operator()(Error*);+ Expression* operator()(Debug*);++ Expression* operator()(List*);+ Expression* operator()(Map*);+ Expression* operator()(Binary_Expression*);+ Expression* operator()(Unary_Expression*);+ Expression* operator()(Function_Call*);+ Expression* operator()(Function_Call_Schema*);+ Expression* operator()(Variable*);+ Expression* operator()(Textual*);+ Expression* operator()(Number*);+ Expression* operator()(Boolean*);+ Expression* operator()(String_Schema*);+ Expression* operator()(String_Constant*);+ Expression* operator()(Media_Query*);+ Expression* operator()(Media_Query_Expression*);+ Expression* operator()(At_Root_Expression*);+ Expression* operator()(Feature_Query*);+ Expression* operator()(Feature_Query_Condition*);+ Expression* operator()(Null*);+ Expression* operator()(Argument*);+ Expression* operator()(Arguments*);+ Expression* operator()(Comment*);+ Expression* operator()(Parent_Selector* p);++ template <typename U>+ Expression* fallback(U x) { return fallback_impl(x); }++ private:+ string interpolation(Expression* s);++ };++ Expression* cval_to_astnode(Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));++ bool eq(Expression*, Expression*, Context&);+ bool lt(Expression*, Expression*, Context&);+}++#endif
+ libsass/expand.cpp view
@@ -0,0 +1,538 @@+#include <iostream>+#include <typeinfo>++#include "expand.hpp"+#include "bind.hpp"+#include "eval.hpp"+#include "contextualize_eval.hpp"+#include "to_string.hpp"+#include "backtrace.hpp"+#include "context.hpp"+#include "parser.hpp"++namespace Sass {++ Expand::Expand(Context& ctx, Eval* eval, Contextualize_Eval* contextualize_eval, Env* env, Backtrace* bt)+ : ctx(ctx),+ eval(eval),+ contextualize_eval(contextualize_eval),+ env(env),+ block_stack(vector<Block*>()),+ property_stack(vector<String*>()),+ selector_stack(vector<Selector*>()),+ at_root_selector_stack(vector<Selector*>()),+ in_at_root(false),+ in_keyframes(false),+ backtrace(bt)+ { selector_stack.push_back(0); }++ Statement* Expand::operator()(Block* b)+ {+ Env new_env;+ new_env.link(*env);+ env = &new_env;+ Block* bb = new (ctx.mem) Block(b->pstate(), b->length(), b->is_root());+ block_stack.push_back(bb);+ append_block(b);+ block_stack.pop_back();+ env = env->parent();+ return bb;+ }++ Statement* Expand::operator()(Ruleset* r)+ {+ bool old_in_at_root = in_at_root;+ in_at_root = false;++ if (in_keyframes) {+ To_String to_string;+ Keyframe_Rule* k = new (ctx.mem) Keyframe_Rule(r->pstate(), r->block()->perform(this)->block());+ if (r->selector()) k->selector(r->selector()->perform(contextualize_eval->with(0, env, backtrace)));+ in_at_root = old_in_at_root;+ old_in_at_root = false;+ return k;+ }++ Contextualize_Eval* contextual = contextualize_eval->with(selector_stack.back(), env, backtrace);+ if (old_in_at_root && !r->selector()->has_reference())+ contextual = contextualize_eval->with(at_root_selector_stack.back(), env, backtrace);++ Selector* sel_ctx = r->selector()->perform(contextual);+ if (sel_ctx == 0) throw "Cannot expand null selector";++ Emitter emitter(&ctx);+ Inspect isp(emitter);+ sel_ctx->perform(&isp);+ string str = isp.get_buffer();+ str += ";";++ Parser p(ctx, r->pstate());+ p.block_stack.push_back(r->selector() ? r->selector()->last_block() : 0);+ p.last_media_block = r->selector() ? r->selector()->media_block() : 0;+ p.source = str.c_str();+ p.position = str.c_str();+ p.end = str.c_str() + strlen(str.c_str());+ Selector_List* sel_lst = p.parse_selector_group();+ // sel_lst->pstate(isp.remap(sel_lst->pstate()));++ for(size_t i = 0; i < sel_lst->length(); i++) {++ Complex_Selector* pIter = (*sel_lst)[i];+ while (pIter) {+ Compound_Selector* pHead = pIter->head();+ // pIter->pstate(isp.remap(pIter->pstate()));+ if (pHead) {+ // pHead->pstate(isp.remap(pHead->pstate()));+ // (*pHead)[0]->pstate(isp.remap((*pHead)[0]->pstate()));+ }+ pIter = pIter->tail();+ }+ }+ sel_ctx = sel_lst;++ selector_stack.push_back(sel_ctx);+ Block* blk = r->block()->perform(this)->block();+ Ruleset* rr = new (ctx.mem) Ruleset(r->pstate(),+ sel_ctx,+ blk);+ rr->tabs(r->tabs());+ selector_stack.pop_back();+ in_at_root = old_in_at_root;+ old_in_at_root = false;+ return rr;+ }++ Statement* Expand::operator()(Propset* p)+ {+ property_stack.push_back(p->property_fragment());+ Block* expanded_block = p->block()->perform(this)->block();++ Block* current_block = block_stack.back();+ for (size_t i = 0, L = expanded_block->length(); i < L; ++i) {+ Statement* stm = (*expanded_block)[i];+ if (typeid(*stm) == typeid(Declaration)) {+ Declaration* dec = static_cast<Declaration*>(stm);+ String_Schema* combined_prop = new (ctx.mem) String_Schema(p->pstate());+ if (!property_stack.empty()) {+ *combined_prop << property_stack.back()+ << new (ctx.mem) String_Constant(p->pstate(), "-")+ << dec->property(); // TODO: eval the prop into a string constant+ }+ else {+ *combined_prop << dec->property();+ }+ dec->property(combined_prop);+ *current_block << dec;+ }+ else if (typeid(*stm) == typeid(Comment)) {+ // drop comments in propsets+ }+ else {+ error("contents of namespaced properties must result in style declarations only", stm->pstate(), backtrace);+ }+ }++ property_stack.pop_back();++ return 0;+ }++ Statement* Expand::operator()(Feature_Block* f)+ {+ Expression* feature_queries = f->feature_queries()->perform(eval->with(env, backtrace));+ Feature_Block* ff = new (ctx.mem) Feature_Block(f->pstate(),+ static_cast<Feature_Query*>(feature_queries),+ f->block()->perform(this)->block());+ ff->selector(selector_stack.back());+ return ff;+ }++ Statement* Expand::operator()(Media_Block* m)+ {+ To_String to_string(&ctx);+ Expression* mq = m->media_queries()->perform(eval->with(env, backtrace));+ mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries();+ Media_Block* mm = new (ctx.mem) Media_Block(m->pstate(),+ static_cast<List*>(mq),+ m->block()->perform(this)->block(),+ selector_stack.back());+ mm->tabs(m->tabs());+ return mm;+ }++ Statement* Expand::operator()(At_Root_Block* a)+ {+ in_at_root = true;+ at_root_selector_stack.push_back(0);+ Block* ab = a->block();+ Expression* ae = a->expression();+ if (ae) ae = ae->perform(eval->with(env, backtrace));+ else ae = new (ctx.mem) At_Root_Expression(a->pstate());+ Block* bb = ab ? ab->perform(this)->block() : 0;+ At_Root_Block* aa = new (ctx.mem) At_Root_Block(a->pstate(),+ bb,+ static_cast<At_Root_Expression*>(ae));+ at_root_selector_stack.pop_back();+ in_at_root = false;+ return aa;+ }++ Statement* Expand::operator()(At_Rule* a)+ {+ bool old_in_keyframes = in_keyframes;+ in_keyframes = a->is_keyframes();+ Block* ab = a->block();+ Selector* as = a->selector();+ Expression* av = a->value();+ if (as) as = as->perform(contextualize_eval->with(0, env, backtrace));+ else if (av) av = av->perform(eval->with(env, backtrace));+ Block* bb = ab ? ab->perform(this)->block() : 0;+ At_Rule* aa = new (ctx.mem) At_Rule(a->pstate(),+ a->keyword(),+ as,+ bb);+ if (av) aa->value(av);+ in_keyframes = old_in_keyframes;+ return aa;+ }++ Statement* Expand::operator()(Declaration* d)+ {+ String* old_p = d->property();+ String* new_p = static_cast<String*>(old_p->perform(eval->with(env, backtrace)));+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();+ Expression* value = d->value()->perform(eval->with(p, env, backtrace));+ if (value->is_invisible() && !d->is_important()) return 0;+ Declaration* decl = new (ctx.mem) Declaration(d->pstate(),+ new_p,+ value,+ d->is_important());+ decl->tabs(d->tabs());+ return decl;+ }++ Statement* Expand::operator()(Assignment* a)+ {+ string var(a->variable());+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();+ if (a->is_global()) {+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));+ }+ else if (a->is_default()) {+ if (env->has_lexical(var)) return 0;+ if (env->has_global(var)) {+ Expression* e = static_cast<Expression*>(env->get_global(var));+ if (e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));+ }+ } else {+ env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));+ }+ }+ else {+ env->set_lexical(var, a->value()->perform(eval->with(p, env, backtrace)));+ }+ return 0;+ }++ Statement* Expand::operator()(Import* imp)+ {+ Import* result = new (ctx.mem) Import(imp->pstate());+ for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {+ result->urls().push_back(imp->urls()[i]->perform(eval->with(env, backtrace)));+ }+ return result;+ }++ Statement* Expand::operator()(Import_Stub* i)+ {+ append_block(ctx.style_sheets[i->file_name()]);+ return 0;+ }++ Statement* Expand::operator()(Warning* w)+ {+ // eval handles this too, because warnings may occur in functions+ w->perform(eval->with(env, backtrace));+ return 0;+ }++ Statement* Expand::operator()(Error* e)+ {+ // eval handles this too, because errors may occur in functions+ e->perform(eval->with(env, backtrace));+ return 0;+ }++ Statement* Expand::operator()(Debug* d)+ {+ // eval handles this too, because warnings may occur in functions+ d->perform(eval->with(env, backtrace));+ return 0;+ }++ Statement* Expand::operator()(Comment* c)+ {+ // TODO: eval the text, once we're parsing/storing it as a String_Schema+ return new (ctx.mem) Comment(c->pstate(), static_cast<String*>(c->text()->perform(eval->with(env, backtrace))), c->is_important());+ }++ Statement* Expand::operator()(If* i)+ {+ if (*i->predicate()->perform(eval->with(env, backtrace))) {+ append_block(i->consequent());+ }+ else {+ Block* alt = i->alternative();+ if (alt) append_block(alt);+ }+ return 0;+ }++ // For does not create a new env scope+ // But iteration vars are reset afterwards+ Statement* Expand::operator()(For* f)+ {+ string variable(f->variable());+ Expression* low = f->lower_bound()->perform(eval->with(env, backtrace));+ if (low->concrete_type() != Expression::NUMBER) {+ error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace);+ }+ Expression* high = f->upper_bound()->perform(eval->with(env, backtrace));+ if (high->concrete_type() != Expression::NUMBER) {+ error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace);+ }+ Number* sass_start = static_cast<Number*>(low);+ Number* sass_end = static_cast<Number*>(high);+ // check if units are valid for sequence+ if (sass_start->unit() != sass_end->unit()) {+ stringstream msg; msg << "Incompatible units: '"+ << sass_start->unit() << "' and '"+ << sass_end->unit() << "'.";+ error(msg.str(), low->pstate(), backtrace);+ }+ double start = sass_start->value();+ double end = sass_end->value();+ // only create iterator once in this environment+ Number* it = new (env->mem) Number(low->pstate(), start, sass_end->unit());+ AST_Node* old_var = env->get_local(variable);+ env->set_local(variable, it);+ Block* body = f->block();+ if (start < end) {+ if (f->is_inclusive()) ++end;+ for (double i = start;+ i < end;+ ++i) {+ it->value(i);+ env->set_local(variable, it);+ append_block(body);+ }+ } else {+ if (f->is_inclusive()) --end;+ for (double i = start;+ i > end;+ --i) {+ it->value(i);+ env->set_local(variable, it);+ append_block(body);+ }+ }+ // restore original environment+ if (!old_var) env->del_local(variable);+ else env->set_local(variable, old_var);+ return 0;+ }++ // Eval does not create a new env scope+ // But iteration vars are reset afterwards+ Statement* Expand::operator()(Each* e)+ {+ vector<string> variables(e->variables());+ Expression* expr = e->list()->perform(eval->with(env, backtrace));+ List* list = 0;+ Map* map = 0;+ if (expr->concrete_type() == Expression::MAP) {+ map = static_cast<Map*>(expr);+ }+ else if (expr->concrete_type() != Expression::LIST) {+ list = new (ctx.mem) List(expr->pstate(), 1, List::COMMA);+ *list << expr;+ }+ else {+ list = static_cast<List*>(expr);+ }+ // remember variables and then reset them+ vector<AST_Node*> old_vars(variables.size());+ for (size_t i = 0, L = variables.size(); i < L; ++i) {+ old_vars[i] = env->get_local(variables[i]);+ env->set_local(variables[i], 0);+ }+ Block* body = e->block();++ if (map) {+ for (auto key : map->keys()) {+ Expression* k = key->perform(eval->with(env, backtrace));+ Expression* v = map->at(key)->perform(eval->with(env, backtrace));++ if (variables.size() == 1) {+ List* variable = new (ctx.mem) List(map->pstate(), 2, List::SPACE);+ *variable << k;+ *variable << v;+ env->set_local(variables[0], variable);+ } else {+ env->set_local(variables[0], k);+ env->set_local(variables[1], v);+ }+ append_block(body);+ }+ }+ else {+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ List* variable = 0;+ if ((*list)[i]->concrete_type() != Expression::LIST || variables.size() == 1) {+ variable = new (ctx.mem) List((*list)[i]->pstate(), 1, List::COMMA);+ *variable << (*list)[i];+ }+ else {+ variable = static_cast<List*>((*list)[i]);+ }+ for (size_t j = 0, K = variables.size(); j < K; ++j) {+ if (j < variable->length()) {+ env->set_local(variables[j], (*variable)[j]->perform(eval->with(env, backtrace)));+ }+ else {+ env->set_local(variables[j], new (ctx.mem) Null(expr->pstate()));+ }+ }+ 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]);+ }+ return 0;+ }++ Statement* Expand::operator()(While* w)+ {+ Expression* pred = w->predicate();+ Block* body = w->block();+ while (*pred->perform(eval->with(env, backtrace))) {+ append_block(body);+ }+ return 0;+ }++ Statement* Expand::operator()(Return* r)+ {+ error("@return may only be used within a function", r->pstate(), backtrace);+ return 0;+ }++ Statement* Expand::operator()(Extension* e)+ {+ To_String to_string(&ctx);+ Selector_List* extender = static_cast<Selector_List*>(selector_stack.back());+ if (!extender) return 0;+ Contextualize_Eval* eval = contextualize_eval->with(0, env, backtrace);+ Selector_List* selector_list = static_cast<Selector_List*>(e->selector());+ Selector_List* contextualized = static_cast<Selector_List*>(selector_list->perform(eval));+ // ToDo: remove once feature proves stable!+ // if (contextualized->length() != 1) {+ // error("selector groups may not be extended", extendee->pstate(), backtrace);+ // }+ for (auto complex_sel : contextualized->elements()) {+ Complex_Selector* c = complex_sel;+ if (!c->head() || c->tail()) {+ error("nested selectors may not be extended", c->pstate(), backtrace);+ }+ Compound_Selector* compound_sel = c->head();+ compound_sel->is_optional(selector_list->is_optional());+ // // need to convert the compound selector into a by-value data structure+ // vector<string> target_vec;+ // for (size_t i = 0, L = compound_sel->length(); i < L; ++i)+ // { target_vec.push_back((*compound_sel)[i]->perform(&to_string)); }+ for (size_t i = 0, L = extender->length(); i < L; ++i) {+ // let's test this out+ // cerr << "REGISTERING EXTENSION REQUEST: " << (*extender)[i]->perform(&to_string) << " <- " << compound_sel->perform(&to_string) << endl;+ ctx.subset_map.put(compound_sel->to_str_vec(), make_pair((*extender)[i], compound_sel));+ }+ }+ return 0;+ }++ Statement* Expand::operator()(Definition* d)+ {+ Definition* dd = new (ctx.mem) Definition(*d);+ env->local_frame()[d->name() ++ (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;+ // set the static link so we can have lexical scoping+ dd->environment(env);+ return 0;+ }++ Statement* Expand::operator()(Mixin_Call* c)+ {+ string full_name(c->name() + "[m]");+ if (!env->has(full_name)) {+ error("no mixin named " + c->name(), c->pstate(), backtrace);+ }+ Definition* def = static_cast<Definition*>((*env)[full_name]);+ Block* body = def->block();+ Parameters* params = def->parameters();+ Selector* p = selector_stack.size() <= 1 ? 0 : selector_stack.back();++ Arguments* args = static_cast<Arguments*>(c->arguments()+ ->perform(eval->with(p, env, backtrace)));+ Backtrace here(backtrace, c->pstate(), ", in mixin `" + c->name() + "`");+ backtrace = &here;+ Env new_env;+ new_env.link(def->environment());+ if (c->block()) {+ // represent mixin content blocks as thunks/closures+ Definition* thunk = new (ctx.mem) Definition(c->pstate(),+ "@content",+ new (ctx.mem) Parameters(c->pstate()),+ c->block(),+ &ctx,+ Definition::MIXIN);+ thunk->environment(env);+ new_env.local_frame()["@content[m]"] = thunk;+ }+ bind("mixin " + c->name(), params, args, ctx, &new_env, eval);+ Env* old_env = env;+ env = &new_env;+ append_block(body);+ env = old_env;+ backtrace = here.parent;+ return 0;+ }++ Statement* Expand::operator()(Content* c)+ {+ // convert @content directives into mixin calls to the underlying thunk+ if (!env->has("@content[m]")) return 0;+ Mixin_Call* call = new (ctx.mem) Mixin_Call(c->pstate(),+ "@content",+ new (ctx.mem) Arguments(c->pstate()));+ return call->perform(this);+ }++ inline Statement* Expand::fallback_impl(AST_Node* n)+ {+ error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace);+ String_Constant* msg = new (ctx.mem) String_Constant(ParserState("[WARN]"), string("`Expand` doesn't handle ") + typeid(*n).name());+ return new (ctx.mem) Warning(ParserState("[WARN]"), msg);+ }++ inline void Expand::append_block(Block* b)+ {+ Block* current_block = block_stack.back();+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* ith = (*b)[i]->perform(this);+ if (ith) *current_block << ith;+ }+ }+}
+ libsass/expand.hpp view
@@ -0,0 +1,78 @@+#ifndef SASS_EXPAND_H+#define SASS_EXPAND_H++#include <map>+#include <vector>+#include <iostream>++#include "ast.hpp"+#include "eval.hpp"+#include "operation.hpp"+#include "environment.hpp"+#include "contextualize.hpp"++namespace Sass {+ using namespace std;++ class Context;+ class Eval;+ class Contextualize_Eval;+ typedef Environment<AST_Node*> Env;+ struct Backtrace;++ class Expand : public Operation_CRTP<Statement*, Expand> {++ Context& ctx;+ Eval* eval;+ Contextualize_Eval* contextualize_eval;+ Env* env;+ vector<Block*> block_stack;+ vector<String*> property_stack;+ vector<Selector*> selector_stack;+ vector<Selector*> at_root_selector_stack;+ bool in_at_root;+ bool in_keyframes;+ Backtrace* backtrace;++ Statement* fallback_impl(AST_Node* n);++ public:+ Expand(Context&, Eval*, Contextualize_Eval*, Env*, Backtrace*);+ virtual ~Expand() { }++ using Operation<Statement*>::operator();++ Statement* operator()(Block*);+ Statement* operator()(Ruleset*);+ Statement* operator()(Propset*);+ Statement* operator()(Media_Block*);+ Statement* operator()(Feature_Block*);+ Statement* operator()(At_Root_Block*);+ Statement* operator()(At_Rule*);+ Statement* operator()(Declaration*);+ Statement* operator()(Assignment*);+ Statement* operator()(Import*);+ Statement* operator()(Import_Stub*);+ Statement* operator()(Warning*);+ Statement* operator()(Error*);+ Statement* operator()(Debug*);+ Statement* operator()(Comment*);+ Statement* operator()(If*);+ Statement* operator()(For*);+ Statement* operator()(Each*);+ Statement* operator()(While*);+ Statement* operator()(Return*);+ Statement* operator()(Extension*);+ Statement* operator()(Definition*);+ Statement* operator()(Mixin_Call*);+ Statement* operator()(Content*);++ template <typename U>+ Statement* fallback(U x) { return fallback_impl(x); }++ void append_block(Block*);+ };++}++#endif
+ libsass/extend.cpp view
@@ -0,0 +1,2010 @@+#include "extend.hpp"+#include "context.hpp"+#include "contextualize.hpp"+#include "to_string.hpp"+#include "backtrace.hpp"+#include "paths.hpp"+#include "parser.hpp"+#include "node.hpp"+#include "sass_util.hpp"+#include "debug.hpp"+#include <iostream>+#include <deque>++/*+ NOTES:++ - The print* functions print to cerr. This allows our testing frameworks (like sass-spec) to ignore the output, which+ is very helpful when debugging. The format of the output is mainly to wrap things in square brackets to match what+ ruby already outputs (to make comparisons easier).++ - For the direct porting effort, we're trying to port method-for-method until we get all the tests passing.+ Where applicable, I've tried to include the ruby code above the function for reference until all our tests pass.+ The ruby code isn't always directly portable, so I've tried to include any modified ruby code that was actually+ used for the porting.++ - DO NOT try to optimize yet. We get a tremendous benefit out of comparing the output of each stage of the extend to the ruby+ output at the same stage. This makes it much easier to determine where problems are. Try to keep as close to+ the ruby code as you can until we have all the sass-spec tests passing. Then, we should optimize. However, if you see+ something that could probably be optimized, let's not forget it. Add a // TODO: or // IMPROVEMENT: comment.++ - Coding conventions in this file (these may need to be changed before merging back into master)+ - Very basic hungarian notation:+ p prefix for pointers (pSelector)+ no prefix for value types and references (selector)+ - Use STL iterators where possible+ - prefer verbose naming over terse naming+ - use typedefs for STL container types for make maintenance easier++ - You may see a lot of comments that say "// TODO: is this the correct combinator?". See the comment referring to combinators+ in extendCompoundSelector for a more extensive explanation of my confusion. I think our divergence in data model from ruby+ sass causes this to be necessary.+++ GLOBAL TODOS:++ - wrap the contents of the print functions in DEBUG preprocesser conditionals so they will be optimized away in non-debug mode.++ - consider making the extend* functions member functions to avoid passing around ctx and subsetMap map around. This has the+ drawback that the implementation details of the operator are then exposed to the outside world, which is not ideal and+ can cause additional compile time dependencies.++ - mark the helper methods in this file static to given them compilation unit linkage.++ - implement parent directive matching++ - fix compilation warnings for unused Extend members if we really don't need those references anymore.+ */+++namespace Sass {+++ typedef pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+ typedef vector<ExtensionPair> SubsetMapEntries;++++#ifdef DEBUG++ // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp+ ostream& operator<<(ostream& os, const Complex_Selector::Combinator combinator) {+ switch (combinator) {+ case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;+ case Complex_Selector::PARENT_OF: os << "\">\""; break;+ case Complex_Selector::PRECEDES: os << "\"~\""; break;+ case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;+ }++ return os;+ }+++ ostream& operator<<(ostream& os, Compound_Selector& compoundSelector) {+ To_String to_string;+ os << compoundSelector.perform(&to_string);+ return os;+ }+++ // Print a string representation of a Compound_Selector+ static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ cerr << message;+ }++ if (pCompoundSelector) {+ cerr << *pCompoundSelector;+ } else {+ cerr << "NULL";+ }++ if (newline) {+ cerr << endl;+ }+ }+++ ostream& operator<<(ostream& os, Complex_Selector& complexSelector) {+ To_String to_string;++ os << "[";+ Complex_Selector* pIter = &complexSelector;+ bool first = true;+ while (pIter) {+ if (pIter->combinator() != Complex_Selector::ANCESTOR_OF) {+ if (!first) {+ os << ", ";+ }+ first = false;+ os << pIter->combinator();+ }++ if (!first) {+ os << ", ";+ }+ first = false;++ if (pIter->head()) {+ os << pIter->head()->perform(&to_string);+ } else {+ os << "NULL_HEAD";+ }++ pIter = pIter->tail();+ }+ os << "]";++ return os;+ }+++ // Print a string representation of a Complex_Selector+ static void printComplexSelector(Complex_Selector* pComplexSelector, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ cerr << message;+ }++ if (pComplexSelector) {+ cerr << *pComplexSelector;+ } else {+ cerr << "NULL";+ }++ if (newline) {+ cerr << endl;+ }+ }+++ // Print a string representation of a SourcesSet+ static void printSourcesSet(SourcesSet& sources, Context& ctx, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ cerr << message;+ }++ // Convert to a deque of strings so we can sort since order doesn't matter in a set. This should cut down on+ // the differences we see when debug printing.+ typedef deque<string> SourceStrings;+ SourceStrings sourceStrings;+ for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {+ Complex_Selector* pSource = *iterator;+ stringstream sstream;+ sstream << complexSelectorToNode(pSource, ctx);+ sourceStrings.push_back(sstream.str());+ }++ // Sort to get consistent output+ std::sort(sourceStrings.begin(), sourceStrings.end());++ cerr << "SourcesSet[";+ for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {+ string source = *iterator;+ if (iterator != sourceStrings.begin()) {+ cerr << ", ";+ }+ cerr << source;+ }+ cerr << "]";++ if (newline) {+ cerr << endl;+ }+ }+++ ostream& operator<<(ostream& os, SubsetMapEntries& entries) {+ os << "SUBSET_MAP_ENTRIES[";++ for (SubsetMapEntries::iterator iterator = entries.begin(), endIterator = entries.end(); iterator != endIterator; ++iterator) {+ Complex_Selector* pExtComplexSelector = iterator->first; // The selector up to where the @extend is (ie, the thing to merge)+ Compound_Selector* pExtCompoundSelector = iterator->second; // The stuff after the @extend++ if (iterator != entries.begin()) {+ os << ", ";+ }++ os << "(";++ if (pExtComplexSelector) {+ cerr << *pExtComplexSelector;+ } else {+ cerr << "NULL";+ }++ os << " -> ";++ if (pExtCompoundSelector) {+ cerr << *pExtCompoundSelector;+ } else {+ cerr << "NULL";+ }++ os << ")";++ }++ os << "]";++ return os;+ }+#endif++ static bool parentSuperselector(Complex_Selector* pOne, Complex_Selector* pTwo, Context& ctx) {+ // TODO: figure out a better way to create a Complex_Selector from scratch+ // TODO: There's got to be a better way. This got ugly quick...+ Position noPosition(-1, -1, -1);+ Type_Selector fakeParent(ParserState("[FAKE]"), "temp");+ Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);+ fakeHead.elements().push_back(&fakeParent);+ Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);++ pOne->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);+ pTwo->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);++ bool isSuperselector = pOne->is_superselector_of(pTwo);++ pOne->clear_innermost();+ pTwo->clear_innermost();++ return isSuperselector;+ }++ void nodeToComplexSelectorDeque(const Node& node, ComplexSelectorDeque& out, Context& ctx) {+ for (NodeDeque::iterator iter = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {+ Node& child = *iter;+ out.push_back(nodeToComplexSelector(child, ctx));+ }+ }++ Node complexSelectorDequeToNode(const ComplexSelectorDeque& deque, Context& ctx) {+ Node result = Node::createCollection();++ for (ComplexSelectorDeque::const_iterator iter = deque.begin(), iterEnd = deque.end(); iter != iterEnd; iter++) {+ Complex_Selector* pChild = *iter;+ result.collection()->push_back(complexSelectorToNode(pChild, ctx));+ }++ return result;+ }++ class LcsCollectionComparator {+ public:+ LcsCollectionComparator(Context& ctx) : mCtx(ctx) {}++ Context& mCtx;++ bool operator()(Complex_Selector* pOne, Complex_Selector* pTwo, Complex_Selector*& pOut) const {+ /*+ This code is based on the following block from ruby sass' subweave+ do |s1, s2|+ next s1 if s1 == s2+ next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)+ next s2 if parent_superselector?(s1, s2)+ next s1 if parent_superselector?(s2, s1)+ end+ */++ if (selectors_equal(*pOne, *pTwo, true /*simpleSelectorOrderDependent*/)) {+ pOut = pOne;+ return true;+ }++ if (pOne->combinator() != Complex_Selector::ANCESTOR_OF || pTwo->combinator() != Complex_Selector::ANCESTOR_OF) {+ return false;+ }++ if (parentSuperselector(pOne, pTwo, mCtx)) {+ pOut = pTwo;+ return true;+ }++ if (parentSuperselector(pTwo, pOne, mCtx)) {+ pOut = pOne;+ return true;+ }++ return false;+ }+ };+++ /*+ This is the equivalent of ruby's Sass::Util.lcs_backtrace.++ # Computes a single longest common subsequence for arrays x and y.+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS+ */+ void lcs_backtrace(const LCSTable& c, ComplexSelectorDeque& x, ComplexSelectorDeque& y, int i, int j, const LcsCollectionComparator& comparator, ComplexSelectorDeque& out) {+ //DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output++ if (i == 0 || j == 0) {+ DEBUG_PRINTLN(LCS, "RETURNING EMPTY")+ return;+ }+++ Complex_Selector* pCompareOut = NULL;+ if (comparator(x[i], y[j], pCompareOut)) {+ DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")+ lcs_backtrace(c, x, y, i - 1, j - 1, comparator, out);+ out.push_back(pCompareOut);+ return;+ }++ if (c[i][j - 1] > c[i - 1][j]) {+ DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")+ lcs_backtrace(c, x, y, i, j - 1, comparator, out);+ return;+ }++ DEBUG_PRINTLN(LCS, "FINAL RETURN")+ lcs_backtrace(c, x, y, i - 1, j, comparator, out);+ return;+ }++ /*+ This is the equivalent of ruby's Sass::Util.lcs_table.++ # Calculates the memoization table for the Least Common Subsequence algorithm.+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS+ */+ void lcs_table(const ComplexSelectorDeque& x, const ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, LCSTable& out) {+ //DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output++ LCSTable c(x.size(), vector<int>(y.size()));++ // These shouldn't be necessary since the vector will be initialized to 0 already.+ // x.size.times {|i| c[i][0] = 0}+ // y.size.times {|j| c[0][j] = 0}++ for (size_t i = 1; i < x.size(); i++) {+ for (size_t j = 1; j < y.size(); j++) {+ Complex_Selector* pCompareOut = NULL;++ if (comparator(x[i], y[j], pCompareOut)) {+ c[i][j] = c[i - 1][j - 1] + 1;+ } else {+ c[i][j] = max(c[i][j - 1], c[i - 1][j]);+ }+ }+ }++ out = c;+ }++ /*+ This is the equivalent of ruby's Sass::Util.lcs.++ # Computes a single longest common subsequence for `x` and `y`.+ # If there are more than one longest common subsequences,+ # the one returned is that which starts first in `x`.++ # @param x [NodeCollection]+ # @param y [NodeCollection]+ # @comparator An equality check between elements of `x` and `y`.+ # @return [NodeCollection] The LCS++ http://en.wikipedia.org/wiki/Longest_common_subsequence_problem+ */+ void lcs(ComplexSelectorDeque& x, ComplexSelectorDeque& y, const LcsCollectionComparator& comparator, Context& ctx, ComplexSelectorDeque& out) {+ //DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)+ // TODO: make printComplexSelectorDeque and use DEBUG_EXEC AND DEBUG_PRINTLN HERE to get equivalent output++ x.push_front(NULL);+ y.push_front(NULL);++ LCSTable table;+ lcs_table(x, y, comparator, table);++ return lcs_backtrace(table, x, y, static_cast<int>(x.size()) - 1, static_cast<int>(y.size()) - 1, comparator, out);+ }+++ /*+ This is the equivalent of ruby's Sequence.trim.++ The following is the modified version of the ruby code that was more portable to C++. You+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.++ # Avoid truly horrific quadratic behavior. TODO: I think there+ # may be a way to get perfect trimming without going quadratic.+ return seqses if seqses.size > 100++ # Keep the results in a separate array so we can be sure we aren't+ # comparing against an already-trimmed selector. This ensures that two+ # identical selectors don't mutually trim one another.+ result = seqses.dup++ # This is n^2 on the sequences, but only comparing between+ # separate sequences should limit the quadratic behavior.+ seqses.each_with_index do |seqs1, i|+ tempResult = []++ for seq1 in seqs1 do+ max_spec = 0+ for seq in _sources(seq1) do+ max_spec = [max_spec, seq.specificity].max+ end+++ isMoreSpecificOuter = false+ for seqs2 in result do+ if seqs1.equal?(seqs2) then+ next+ end++ # Second Law of Extend: the specificity of a generated selector+ # should never be less than the specificity of the extending+ # selector.+ #+ # See https://github.com/nex3/sass/issues/324.+ isMoreSpecificInner = false+ for seq2 in seqs2 do+ isMoreSpecificInner = _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)+ if isMoreSpecificInner then+ break+ end+ end++ if isMoreSpecificInner then+ isMoreSpecificOuter = true+ break+ end+ end++ if !isMoreSpecificOuter then+ tempResult.push(seq1)+ end+ end++ result[i] = tempResult++ end++ result+ */+ /*+ - IMPROVEMENT: We could probably work directly in the output trimmed deque.+ */+ static Node trim(Node& seqses, Context& ctx) {+ // See the comments in the above ruby code before embarking on understanding this function.++ // Avoid poor performance in extreme cases.+ if (seqses.collection()->size() > 100) {+ return seqses;+ }+++ DEBUG_PRINTLN(TRIM, "TRIM: " << seqses)+++ Node result = Node::createCollection();+ result.plus(seqses);++ DEBUG_PRINTLN(TRIM, "RESULT INITIAL: " << result)++ // Normally we use the standard STL iterators, but in this case, we need to access the result collection by index since we're+ // iterating the input collection, computing a value, and then setting the result in the output collection. We have to keep track+ // of the index manually.+ int toTrimIndex = 0;++ for (NodeDeque::iterator seqsesIter = seqses.collection()->begin(), seqsesIterEnd = seqses.collection()->end(); seqsesIter != seqsesIterEnd; ++seqsesIter) {+ Node& seqs1 = *seqsesIter;++ DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1 << " " << toTrimIndex)++ Node tempResult = Node::createCollection();++ for (NodeDeque::iterator seqs1Iter = seqs1.collection()->begin(), seqs1EndIter = seqs1.collection()->end(); seqs1Iter != seqs1EndIter; ++seqs1Iter) {+ Node& seq1 = *seqs1Iter;++ Complex_Selector* pSeq1 = nodeToComplexSelector(seq1, ctx);++ // Compute the maximum specificity. This requires looking at the "sources" of the sequence. See SimpleSequence.sources in the ruby code+ // for a good description of sources.+ //+ // TODO: I'm pretty sure there's a bug in the sources code. It was implemented for sass-spec's 182_test_nested_extend_loop test.+ // While the test passes, I compared the state of each trim call to verify correctness. The last trim call had incorrect sources. We+ // had an extra source that the ruby version did not have. Without a failing test case, this is going to be extra hard to find. My+ // best guess at this point is that we're cloning an object somewhere and maintaining the sources when we shouldn't be. This is purely+ // a guess though.+ unsigned long maxSpecificity = 0;+ SourcesSet sources = pSeq1->sources();++ DEBUG_PRINTLN(TRIM, "TRIMASDF SEQ1: " << seq1)+ DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIMASDF SOURCES: "))++ for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {+ const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;+ maxSpecificity = max(maxSpecificity, pCurrentSelector->specificity());+ }++ DEBUG_PRINTLN(TRIM, "MAX SPECIFICITY: " << maxSpecificity)++ bool isMoreSpecificOuter = false;++ int resultIndex = 0;++ for (NodeDeque::iterator resultIter = result.collection()->begin(), resultIterEnd = result.collection()->end(); resultIter != resultIterEnd; ++resultIter) {+ Node& seqs2 = *resultIter;++ DEBUG_PRINTLN(TRIM, "SEQS1: " << seqs1)+ DEBUG_PRINTLN(TRIM, "SEQS2: " << seqs2)++ // Do not compare the same sequence to itself. The ruby call we're trying to+ // emulate is: seqs1.equal?(seqs2). equal? is an object comparison, not an equivalency comparision.+ // Since we have the same pointers in seqes and results, we can do a pointer comparision. seqs1 is+ // derived from seqses and seqs2 is derived from result.+ if (seqs1.collection() == seqs2.collection()) {+ DEBUG_PRINTLN(TRIM, "CONTINUE")+ continue;+ }++ bool isMoreSpecificInner = false;++ for (NodeDeque::iterator seqs2Iter = seqs2.collection()->begin(), seqs2IterEnd = seqs2.collection()->end(); seqs2Iter != seqs2IterEnd; ++seqs2Iter) {+ Node& seq2 = *seqs2Iter;++ Complex_Selector* pSeq2 = nodeToComplexSelector(seq2, ctx);++ DEBUG_PRINTLN(TRIM, "SEQ2 SPEC: " << pSeq2->specificity())+ DEBUG_PRINTLN(TRIM, "IS SPEC: " << pSeq2->specificity() << " >= " << maxSpecificity << " " << (pSeq2->specificity() >= maxSpecificity ? "true" : "false"))+ DEBUG_PRINTLN(TRIM, "IS SUPER: " << (pSeq2->is_superselector_of(pSeq1) ? "true" : "false"))++ isMoreSpecificInner = pSeq2->specificity() >= maxSpecificity && pSeq2->is_superselector_of(pSeq1);++ if (isMoreSpecificInner) {+ DEBUG_PRINTLN(TRIM, "FOUND MORE SPECIFIC")+ break;+ }+ }++ // If we found something more specific, we're done. Let the outer loop know and stop iterating.+ if (isMoreSpecificInner) {+ isMoreSpecificOuter = true;+ break;+ }++ resultIndex++;+ }++ if (!isMoreSpecificOuter) {+ DEBUG_PRINTLN(TRIM, "PUSHING: " << seq1)+ tempResult.collection()->push_back(seq1);+ }++ }++ DEBUG_PRINTLN(TRIM, "RESULT BEFORE ASSIGN: " << result)+ DEBUG_PRINTLN(TRIM, "TEMP RESULT: " << toTrimIndex << " " << tempResult)+ (*result.collection())[toTrimIndex] = tempResult;++ toTrimIndex++;++ DEBUG_PRINTLN(TRIM, "RESULT: " << result)+ }++ return result;+ }++++ static bool parentSuperselector(const Node& one, const Node& two, Context& ctx) {+ // TODO: figure out a better way to create a Complex_Selector from scratch+ // TODO: There's got to be a better way. This got ugly quick...+ Position noPosition(-1, -1, -1);+ Type_Selector fakeParent(ParserState("[FAKE]"), "temp");+ Compound_Selector fakeHead(ParserState("[FAKE]"), 1 /*size*/);+ fakeHead.elements().push_back(&fakeParent);+ Complex_Selector fakeParentContainer(ParserState("[FAKE]"), Complex_Selector::ANCESTOR_OF, &fakeHead /*head*/, NULL /*tail*/);++ Complex_Selector* pOneWithFakeParent = nodeToComplexSelector(one, ctx);+ pOneWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);+ Complex_Selector* pTwoWithFakeParent = nodeToComplexSelector(two, ctx);+ pTwoWithFakeParent->set_innermost(&fakeParentContainer, Complex_Selector::ANCESTOR_OF);++ return pOneWithFakeParent->is_superselector_of(pTwoWithFakeParent);+ }+++ class ParentSuperselectorChunker {+ public:+ ParentSuperselectorChunker(Node& lcs, Context& ctx) : mLcs(lcs), mCtx(ctx) {}+ Node& mLcs;+ Context& mCtx;++ bool operator()(const Node& seq) const {+ // {|s| parent_superselector?(s.first, lcs.first)}+ return parentSuperselector(seq.collection()->front(), mLcs.collection()->front(), mCtx);+ }+ };++ class SubweaveEmptyChunker {+ public:+ bool operator()(const Node& seq) const {+ // {|s| s.empty?}++ return seq.collection()->empty();+ }+ };++ /*+ # Takes initial subsequences of `seq1` and `seq2` and returns all+ # orderings of those subsequences. The initial subsequences are determined+ # by a block.+ #+ # Destructively removes the initial subsequences of `seq1` and `seq2`.+ #+ # For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`+ # denoting the boundary of the initial subsequence), this would return+ # `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and+ # `(3 4 5)`.+ #+ # @param seq1 [Array]+ # @param seq2 [Array]+ # @yield [a] Used to determine when to cut off the initial subsequences.+ # Called repeatedly for each sequence until it returns true.+ # @yieldparam a [Array] A final subsequence of one input sequence after+ # cutting off some initial subsequence.+ # @yieldreturn [Boolean] Whether or not to cut off the initial subsequence+ # here.+ # @return [Array<Array>] All possible orderings of the initial subsequences.+ def chunks(seq1, seq2)+ chunk1 = []+ chunk1 << seq1.shift until yield seq1+ chunk2 = []+ chunk2 << seq2.shift until yield seq2+ return [] if chunk1.empty? && chunk2.empty?+ return [chunk2] if chunk1.empty?+ return [chunk1] if chunk2.empty?+ [chunk1 + chunk2, chunk2 + chunk1]+ end+ */+ template<typename ChunkerType>+ static Node chunks(Node& seq1, Node& seq2, const ChunkerType& chunker) {+ Node chunk1 = Node::createCollection();+ while (!chunker(seq1)) {+ chunk1.collection()->push_back(seq1.collection()->front());+ seq1.collection()->pop_front();+ }++ Node chunk2 = Node::createCollection();+ while (!chunker(seq2)) {+ chunk2.collection()->push_back(seq2.collection()->front());+ seq2.collection()->pop_front();+ }++ if (chunk1.collection()->empty() && chunk2.collection()->empty()) {+ DEBUG_PRINTLN(CHUNKS, "RETURNING BOTH EMPTY")+ return Node::createCollection();+ }++ if (chunk1.collection()->empty()) {+ Node chunk2Wrapper = Node::createCollection();+ chunk2Wrapper.collection()->push_back(chunk2);+ DEBUG_PRINTLN(CHUNKS, "RETURNING ONE EMPTY")+ return chunk2Wrapper;+ }++ if (chunk2.collection()->empty()) {+ Node chunk1Wrapper = Node::createCollection();+ chunk1Wrapper.collection()->push_back(chunk1);+ DEBUG_PRINTLN(CHUNKS, "RETURNING TWO EMPTY")+ return chunk1Wrapper;+ }++ Node perms = Node::createCollection();++ Node firstPermutation = Node::createCollection();+ firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());+ firstPermutation.collection()->insert(firstPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());+ perms.collection()->push_back(firstPermutation);++ Node secondPermutation = Node::createCollection();+ secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk2.collection()->begin(), chunk2.collection()->end());+ secondPermutation.collection()->insert(secondPermutation.collection()->end(), chunk1.collection()->begin(), chunk1.collection()->end());+ perms.collection()->push_back(secondPermutation);++ DEBUG_PRINTLN(CHUNKS, "RETURNING PERM")++ return perms;+ }+++ static Node groupSelectors(Node& seq, Context& ctx) {+ Node newSeq = Node::createCollection();++ Node tail = Node::createCollection();+ tail.plus(seq);++ while (!tail.collection()->empty()) {+ Node head = Node::createCollection();++ do {+ head.collection()->push_back(tail.collection()->front());+ tail.collection()->pop_front();+ } while (!tail.collection()->empty() && (head.collection()->back().isCombinator() || tail.collection()->front().isCombinator()));++ newSeq.collection()->push_back(head);+ }++ return newSeq;+ }+++ static void getAndRemoveInitialOps(Node& seq, Node& ops) {+ NodeDeque& seqCollection = *(seq.collection());+ NodeDeque& opsCollection = *(ops.collection());++ while (seqCollection.size() > 0 && seqCollection.front().isCombinator()) {+ opsCollection.push_back(seqCollection.front());+ seqCollection.pop_front();+ }+ }+++ static void getAndRemoveFinalOps(Node& seq, Node& ops) {+ NodeDeque& seqCollection = *(seq.collection());+ NodeDeque& opsCollection = *(ops.collection());++ while (seqCollection.size() > 0 && seqCollection.back().isCombinator()) {+ opsCollection.push_back(seqCollection.back()); // Purposefully reversed to match ruby code+ seqCollection.pop_back();+ }+ }+++ /*+ def merge_initial_ops(seq1, seq2)+ ops1, ops2 = [], []+ ops1 << seq1.shift while seq1.first.is_a?(String)+ ops2 << seq2.shift while seq2.first.is_a?(String)++ newline = false+ newline ||= !!ops1.shift if ops1.first == "\n"+ newline ||= !!ops2.shift if ops2.first == "\n"++ # If neither sequence is a subsequence of the other, they cannot be+ # merged successfully+ lcs = Sass::Util.lcs(ops1, ops2)+ return unless lcs == ops1 || lcs == ops2+ return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)+ end+ */+ static Node mergeInitialOps(Node& seq1, Node& seq2, Context& ctx) {+ Node ops1 = Node::createCollection();+ Node ops2 = Node::createCollection();++ getAndRemoveInitialOps(seq1, ops1);+ getAndRemoveInitialOps(seq2, ops2);++ // TODO: Do we have this information available to us?+ // newline = false+ // newline ||= !!ops1.shift if ops1.first == "\n"+ // newline ||= !!ops2.shift if ops2.first == "\n"++ // If neither sequence is a subsequence of the other, they cannot be merged successfully+ DefaultLcsComparator lcsDefaultComparator;+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);++ if (!(opsLcs == ops1 || opsLcs == ops2)) {+ return Node::createNil();+ }++ // TODO: more newline logic+ // return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)++ return (ops1.collection()->size() > ops2.collection()->size() ? ops1 : ops2);+ }+++ /*+ def merge_final_ops(seq1, seq2, res = [])+++ # This code looks complicated, but it's actually just a bunch of special+ # cases for interactions between different combinators.+ op1, op2 = ops1.first, ops2.first+ if op1 && op2+ sel1 = seq1.pop+ sel2 = seq2.pop+ if op1 == '~' && op2 == '~'+ if sel1.superselector?(sel2)+ res.unshift sel2, '~'+ elsif sel2.superselector?(sel1)+ res.unshift sel1, '~'+ else+ merged = sel1.unify(sel2.members, sel2.subject?)+ res.unshift [+ [sel1, '~', sel2, '~'],+ [sel2, '~', sel1, '~'],+ ([merged, '~'] if merged)+ ].compact+ end+ elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')+ if op1 == '~'+ tilde_sel, plus_sel = sel1, sel2+ else+ tilde_sel, plus_sel = sel2, sel1+ end++ if tilde_sel.superselector?(plus_sel)+ res.unshift plus_sel, '+'+ else+ merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)+ res.unshift [+ [tilde_sel, '~', plus_sel, '+'],+ ([merged, '+'] if merged)+ ].compact+ end+ elsif op1 == '>' && %w[~ +].include?(op2)+ res.unshift sel2, op2+ seq1.push sel1, op1+ elsif op2 == '>' && %w[~ +].include?(op1)+ res.unshift sel1, op1+ seq2.push sel2, op2+ elsif op1 == op2+ return unless merged = sel1.unify(sel2.members, sel2.subject?)+ res.unshift merged, op1+ else+ # Unknown selector combinators can't be unified+ return+ end+ return merge_final_ops(seq1, seq2, res)+ elsif op1+ seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)+ res.unshift seq1.pop, op1+ return merge_final_ops(seq1, seq2, res)+ else # op2+ seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)+ res.unshift seq2.pop, op2+ return merge_final_ops(seq1, seq2, res)+ end+ end+ */+ static Node mergeFinalOps(Node& seq1, Node& seq2, Context& ctx, Node& res) {++ Node ops1 = Node::createCollection();+ Node ops2 = Node::createCollection();++ getAndRemoveFinalOps(seq1, ops1);+ getAndRemoveFinalOps(seq2, ops2);++ // TODO: do we have newlines to remove?+ // ops1.reject! {|o| o == "\n"}+ // ops2.reject! {|o| o == "\n"}++ if (ops1.collection()->empty() && ops2.collection()->empty()) {+ return res;+ }++ if (ops1.collection()->size() > 1 || ops2.collection()->size() > 1) {+ DefaultLcsComparator lcsDefaultComparator;+ Node opsLcs = lcs(ops1, ops2, lcsDefaultComparator, ctx);++ // If there are multiple operators, something hacky's going on. If one is a supersequence of the other, use that, otherwise give up.++ if (!(opsLcs == ops1 || opsLcs == ops2)) {+ return Node::createNil();+ }++ if (ops1.collection()->size() > ops2.collection()->size()) {+ res.collection()->insert(res.collection()->begin(), ops1.collection()->rbegin(), ops1.collection()->rend());+ } else {+ res.collection()->insert(res.collection()->begin(), ops2.collection()->rbegin(), ops2.collection()->rend());+ }++ return res;+ }++ if (!ops1.collection()->empty() && !ops2.collection()->empty()) {++ Node op1 = ops1.collection()->front();+ Node op2 = ops2.collection()->front();++ Node sel1 = seq1.collection()->back();+ seq1.collection()->pop_back();++ Node sel2 = seq2.collection()->back();+ seq2.collection()->pop_back();++ if (op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::PRECEDES) {++ if (sel1.selector()->is_superselector_of(sel2.selector())) {++ res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);+ res.collection()->push_front(sel2);++ } else if (sel2.selector()->is_superselector_of(sel1.selector())) {++ res.collection()->push_front(op1 /*PRECEDES - could have been op2 as well*/);+ res.collection()->push_front(sel1);++ } else {++ DEBUG_PRINTLN(ALL, "sel1: " << sel1)+ DEBUG_PRINTLN(ALL, "sel2: " << sel2)++ Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)+ Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+ pMergedWrapper->head(pMerged);++ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))++ Node newRes = Node::createCollection();++ Node firstPerm = Node::createCollection();+ firstPerm.collection()->push_back(sel1);+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ firstPerm.collection()->push_back(sel2);+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ newRes.collection()->push_back(firstPerm);++ Node secondPerm = Node::createCollection();+ secondPerm.collection()->push_back(sel2);+ secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ secondPerm.collection()->push_back(sel1);+ secondPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ newRes.collection()->push_back(secondPerm);++ if (pMerged) {+ Node mergedPerm = Node::createCollection();+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ newRes.collection()->push_back(mergedPerm);+ }++ res.collection()->push_front(newRes);++ DEBUG_PRINTLN(ALL, "RESULT: " << res)++ }++ } else if (((op1.combinator() == Complex_Selector::PRECEDES && op2.combinator() == Complex_Selector::ADJACENT_TO)) || ((op1.combinator() == Complex_Selector::ADJACENT_TO && op2.combinator() == Complex_Selector::PRECEDES))) {++ Node tildeSel = sel1;+ Node tildeOp = op1;+ Node plusSel = sel2;+ Node plusOp = op2;+ if (op1.combinator() != Complex_Selector::PRECEDES) {+ tildeSel = sel2;+ tildeOp = op2;+ plusSel = sel1;+ plusOp = op1;+ }++ if (tildeSel.selector()->is_superselector_of(plusSel.selector())) {++ res.collection()->push_front(plusOp);+ res.collection()->push_front(plusSel);++ } else {++ DEBUG_PRINTLN(ALL, "PLUS SEL: " << plusSel)+ DEBUG_PRINTLN(ALL, "TILDE SEL: " << tildeSel)++ Complex_Selector* pMergedWrapper = plusSel.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ // TODO: does subject matter? Ruby: merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)+ Compound_Selector* pMerged = plusSel.selector()->head()->unify_with(tildeSel.selector()->head(), ctx);+ pMergedWrapper->head(pMerged);++ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))++ Node newRes = Node::createCollection();++ Node firstPerm = Node::createCollection();+ firstPerm.collection()->push_back(tildeSel);+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::PRECEDES));+ firstPerm.collection()->push_back(plusSel);+ firstPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));+ newRes.collection()->push_back(firstPerm);++ if (pMerged) {+ Node mergedPerm = Node::createCollection();+ mergedPerm.collection()->push_back(Node::createSelector(pMergedWrapper, ctx));+ mergedPerm.collection()->push_back(Node::createCombinator(Complex_Selector::ADJACENT_TO));+ newRes.collection()->push_back(mergedPerm);+ }++ res.collection()->push_front(newRes);++ DEBUG_PRINTLN(ALL, "RESULT: " << res)++ }+ } else if (op1.combinator() == Complex_Selector::PARENT_OF && (op2.combinator() == Complex_Selector::PRECEDES || op2.combinator() == Complex_Selector::ADJACENT_TO)) {++ res.collection()->push_front(op2);+ res.collection()->push_front(sel2);++ seq2.collection()->push_back(sel1);+ seq2.collection()->push_back(op1);++ } else if (op2.combinator() == Complex_Selector::PARENT_OF && (op1.combinator() == Complex_Selector::PRECEDES || op1.combinator() == Complex_Selector::ADJACENT_TO)) {++ res.collection()->push_front(op1);+ res.collection()->push_front(sel1);++ seq2.collection()->push_back(sel2);+ seq2.collection()->push_back(op2);++ } else if (op1.combinator() == op2.combinator()) {++ DEBUG_PRINTLN(ALL, "sel1: " << sel1)+ DEBUG_PRINTLN(ALL, "sel2: " << sel2)++ Complex_Selector* pMergedWrapper = sel1.selector()->clone(ctx); // Clone the Complex_Selector to get back to something we can transform to a node once we replace the head with the unification result+ // TODO: does subject matter? Ruby: return unless merged = sel1.unify(sel2.members, sel2.subject?)+ Compound_Selector* pMerged = sel1.selector()->head()->unify_with(sel2.selector()->head(), ctx);+ pMergedWrapper->head(pMerged);++ DEBUG_EXEC(ALL, printCompoundSelector(pMerged, "MERGED: "))++ if (!pMerged) {+ return Node::createNil();+ }++ res.collection()->push_front(op1);+ res.collection()->push_front(Node::createSelector(pMergedWrapper, ctx));++ DEBUG_PRINTLN(ALL, "RESULT: " << res)++ } else {+ return Node::createNil();+ }++ return mergeFinalOps(seq1, seq2, ctx, res);++ } else if (!ops1.collection()->empty()) {++ Node op1 = ops1.collection()->front();++ if (op1.combinator() == Complex_Selector::PARENT_OF && !seq2.collection()->empty() && seq2.collection()->back().selector()->is_superselector_of(seq1.collection()->back().selector())) {+ seq2.collection()->pop_back();+ }++ // TODO: consider unshift(NodeCollection, Node)+ res.collection()->push_front(op1);+ res.collection()->push_front(seq1.collection()->back());+ seq1.collection()->pop_back();++ return mergeFinalOps(seq1, seq2, ctx, res);++ } else { // !ops2.collection()->empty()++ Node op2 = ops2.collection()->front();++ if (op2.combinator() == Complex_Selector::PARENT_OF && !seq1.collection()->empty() && seq1.collection()->back().selector()->is_superselector_of(seq2.collection()->back().selector())) {+ seq1.collection()->pop_back();+ }++ res.collection()->push_front(op2);+ res.collection()->push_front(seq2.collection()->back());+ seq2.collection()->pop_back();++ return mergeFinalOps(seq1, seq2, ctx, res);++ }++ }+++ /*+ This is the equivalent of ruby's Sequence.subweave.++ Here is the original subweave code for reference during porting.++ def subweave(seq1, seq2)+ return [seq2] if seq1.empty?+ return [seq1] if seq2.empty?++ seq1, seq2 = seq1.dup, seq2.dup+ return unless init = merge_initial_ops(seq1, seq2)+ return unless fin = merge_final_ops(seq1, seq2)+ seq1 = group_selectors(seq1)+ seq2 = group_selectors(seq2)+ lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|+ next s1 if s1 == s2+ next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)+ next s2 if parent_superselector?(s1, s2)+ next s1 if parent_superselector?(s2, s1)+ end++ diff = [[init]]+ until lcs.empty?+ diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]+ seq1.shift+ seq2.shift+ end+ diff << chunks(seq1, seq2) {|s| s.empty?}+ diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}+ diff.reject! {|c| c.empty?}++ result = Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}++ result+ end+ */+ static Node subweave(Node& one, Node& two, Context& ctx) {+ // Check for the simple cases+ if (one.collection()->size() == 0) {+ Node out = Node::createCollection();+ out.collection()->push_back(two);+ return out;+ }+ if (two.collection()->size() == 0) {+ Node out = Node::createCollection();+ out.collection()->push_back(one);+ return out;+ }++++ Node seq1 = Node::createCollection();+ seq1.plus(one);+ Node seq2 = Node::createCollection();+ seq2.plus(two);++ DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE ONE: " << seq1)+ DEBUG_PRINTLN(SUBWEAVE, "SUBWEAVE TWO: " << seq2)++ Node init = mergeInitialOps(seq1, seq2, ctx);+ if (init.isNil()) {+ return Node::createNil();+ }++ DEBUG_PRINTLN(SUBWEAVE, "INIT: " << init)++ Node res = Node::createCollection();+ Node fin = mergeFinalOps(seq1, seq2, ctx, res);+ if (fin.isNil()) {+ return Node::createNil();+ }++ DEBUG_PRINTLN(SUBWEAVE, "FIN: " << fin)+++ // Moving this line up since fin isn't modified between now and when it happened before+ // fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}++ for (NodeDeque::iterator finIter = fin.collection()->begin(), finEndIter = fin.collection()->end();+ finIter != finEndIter; ++finIter) {++ Node& childNode = *finIter;++ if (!childNode.isCollection()) {+ Node wrapper = Node::createCollection();+ wrapper.collection()->push_back(childNode);+ childNode = wrapper;+ }++ }++ DEBUG_PRINTLN(SUBWEAVE, "FIN MAPPED: " << fin)++++ Node groupSeq1 = groupSelectors(seq1, ctx);+ DEBUG_PRINTLN(SUBWEAVE, "SEQ1: " << groupSeq1)++ Node groupSeq2 = groupSelectors(seq2, ctx);+ DEBUG_PRINTLN(SUBWEAVE, "SEQ2: " << groupSeq2)+++ ComplexSelectorDeque groupSeq1Converted;+ nodeToComplexSelectorDeque(groupSeq1, groupSeq1Converted, ctx);++ ComplexSelectorDeque groupSeq2Converted;+ nodeToComplexSelectorDeque(groupSeq2, groupSeq2Converted, ctx);++ ComplexSelectorDeque out;+ LcsCollectionComparator collectionComparator(ctx);+ lcs(groupSeq2Converted, groupSeq1Converted, collectionComparator, ctx, out);+ Node seqLcs = complexSelectorDequeToNode(out, ctx);++ DEBUG_PRINTLN(SUBWEAVE, "SEQLCS: " << seqLcs)+++ Node initWrapper = Node::createCollection();+ initWrapper.collection()->push_back(init);+ Node diff = Node::createCollection();+ diff.collection()->push_back(initWrapper);++ DEBUG_PRINTLN(SUBWEAVE, "DIFF INIT: " << diff)+++ while (!seqLcs.collection()->empty()) {+ ParentSuperselectorChunker superselectorChunker(seqLcs, ctx);+ Node chunksResult = chunks(groupSeq1, groupSeq2, superselectorChunker);+ diff.collection()->push_back(chunksResult);++ Node lcsWrapper = Node::createCollection();+ lcsWrapper.collection()->push_back(seqLcs.collection()->front());+ seqLcs.collection()->pop_front();+ diff.collection()->push_back(lcsWrapper);++ groupSeq1.collection()->pop_front();+ groupSeq2.collection()->pop_front();+ }++ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST LCS: " << diff)+++ DEBUG_PRINTLN(SUBWEAVE, "CHUNKS: ONE=" << groupSeq1 << " TWO=" << groupSeq2)+++ SubweaveEmptyChunker emptyChunker;+ Node chunksResult = chunks(groupSeq1, groupSeq2, emptyChunker);+ diff.collection()->push_back(chunksResult);+++ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST CHUNKS: " << diff)+++ diff.collection()->insert(diff.collection()->end(), fin.collection()->begin(), fin.collection()->end());++ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST FIN MAPPED: " << diff)++ // JMA - filter out the empty nodes (use a new collection, since iterator erase() invalidates the old collection)+ Node diffFiltered = Node::createCollection();+ for (NodeDeque::iterator diffIter = diff.collection()->begin(), diffEndIter = diff.collection()->end();+ diffIter != diffEndIter; ++diffIter) {+ Node& node = *diffIter;+ if (node.collection() && !node.collection()->empty()) {+ diffFiltered.collection()->push_back(node);+ }+ }+ diff = diffFiltered;++ DEBUG_PRINTLN(SUBWEAVE, "DIFF POST REJECT: " << diff)+++ Node pathsResult = paths(diff, ctx);++ DEBUG_PRINTLN(SUBWEAVE, "PATHS: " << pathsResult)+++ // We're flattening in place+ for (NodeDeque::iterator pathsIter = pathsResult.collection()->begin(), pathsEndIter = pathsResult.collection()->end();+ pathsIter != pathsEndIter; ++pathsIter) {++ Node& child = *pathsIter;+ child = flatten(child, ctx);+ }++ DEBUG_PRINTLN(SUBWEAVE, "FLATTENED: " << pathsResult)+++ /*+ TODO: implement+ rejected = mapped.reject {|p| path_has_two_subjects?(p)}+ $stderr.puts "REJECTED: #{rejected}"+ */+++ return pathsResult;++ }+ /*+ // disabled to avoid clang warning [-Wunused-function]+ static Node subweaveNaive(const Node& one, const Node& two, Context& ctx) {+ Node out = Node::createCollection();++ // Check for the simple cases+ if (one.isNil()) {+ out.collection()->push_back(two.clone(ctx));+ } else if (two.isNil()) {+ out.collection()->push_back(one.clone(ctx));+ } else {+ // Do the naive implementation. pOne = A B and pTwo = C D ...yields... A B C D and C D A B+ // See https://gist.github.com/nex3/7609394 for details.++ Node firstPerm = one.clone(ctx);+ Node twoCloned = two.clone(ctx);+ firstPerm.plus(twoCloned);+ out.collection()->push_back(firstPerm);++ Node secondPerm = two.clone(ctx);+ Node oneCloned = one.clone(ctx);+ secondPerm.plus(oneCloned );+ out.collection()->push_back(secondPerm);+ }++ return out;+ }+ */+++ /*+ This is the equivalent of ruby's Sequence.weave.++ The following is the modified version of the ruby code that was more portable to C++. You+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.++ def weave(path)+ # This function works by moving through the selector path left-to-right,+ # building all possible prefixes simultaneously. These prefixes are+ # `befores`, while the remaining parenthesized suffixes is `afters`.+ befores = [[]]+ afters = path.dup++ until afters.empty?+ current = afters.shift.dup+ last_current = [current.pop]++ tempResult = []++ for before in befores do+ sub = subweave(before, current)+ if sub.nil?+ next+ end++ for seqs in sub do+ tempResult.push(seqs + last_current)+ end+ end++ befores = tempResult++ end++ return befores+ end+ */+ /*+ def weave(path)+ befores = [[]]+ afters = path.dup++ until afters.empty?+ current = afters.shift.dup++ last_current = [current.pop]+++ tempResult = []++ for before in befores do+ sub = subweave(before, current)++ if sub.nil?+ next []+ end+++ for seqs in sub do+ toPush = seqs + last_current++ tempResult.push(seqs + last_current)+ end++ end++ befores = tempResult++ end++ return befores+ end+ */+ static Node weave(Node& path, Context& ctx) {++ DEBUG_PRINTLN(WEAVE, "WEAVE: " << path)++ Node befores = Node::createCollection();+ befores.collection()->push_back(Node::createCollection());++ Node afters = Node::createCollection();+ afters.plus(path);++ while (!afters.collection()->empty()) {+ Node current = afters.collection()->front().clone(ctx);+ afters.collection()->pop_front();+ DEBUG_PRINTLN(WEAVE, "CURRENT: " << current)++ Node last_current = Node::createCollection();+ last_current.collection()->push_back(current.collection()->back());+ current.collection()->pop_back();+ DEBUG_PRINTLN(WEAVE, "CURRENT POST POP: " << current)+ DEBUG_PRINTLN(WEAVE, "LAST CURRENT: " << last_current)++ Node tempResult = Node::createCollection();++ for (NodeDeque::iterator beforesIter = befores.collection()->begin(), beforesEndIter = befores.collection()->end(); beforesIter != beforesEndIter; beforesIter++) {+ Node& before = *beforesIter;++ Node sub = subweave(before, current, ctx);++ DEBUG_PRINTLN(WEAVE, "SUB: " << sub)++ if (sub.isNil()) {+ return Node::createCollection();+ }++ for (NodeDeque::iterator subIter = sub.collection()->begin(), subEndIter = sub.collection()->end(); subIter != subEndIter; subIter++) {+ Node& seqs = *subIter;++ Node toPush = Node::createCollection();+ toPush.plus(seqs);+ toPush.plus(last_current);++ tempResult.collection()->push_back(toPush);++ }+ }++ befores = tempResult;++ }++ return befores;+ }++++ // This forward declaration is needed since extendComplexSelector calls extendCompoundSelector, which may recursively+ // call extendComplexSelector again.+ static Node extendComplexSelector(+ Complex_Selector* pComplexSelector,+ Context& ctx,+ ExtensionSubsetMap& subsetMap,+ set<Compound_Selector> seen);++++ /*+ This is the equivalent of ruby's SimpleSequence.do_extend.++ // TODO: I think I have some modified ruby code to put here. Check.+ */+ /*+ ISSUES:+ - Previous TODO: Do we need to group the results by extender?+ - What does subject do in?: next unless unified = seq.members.last.unify(self_without_sel, subject?)+ - IMPROVEMENT: The search for uniqueness at the end is not ideal since it's has to loop over everything...+ - IMPROVEMENT: Check if the final search for uniqueness is doing anything that extendComplexSelector isn't already doing...+ */+ template<typename KeyType>+ class GroupByToAFunctor {+ public:+ KeyType operator()(ExtensionPair& extPair) const {+ Complex_Selector* pSelector = extPair.first;+ return *pSelector;+ }+ };+ static Node extendCompoundSelector(+ Compound_Selector* pSelector,+ Context& ctx,+ ExtensionSubsetMap& subsetMap,+ set<Compound_Selector> seen) {++ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))++ Node extendedSelectors = Node::createCollection();++ To_String to_string;++ SubsetMapEntries entries = subsetMap.get_v(pSelector->to_str_vec());+++ typedef vector<pair<Complex_Selector, vector<ExtensionPair> > > GroupedByToAResult;++ GroupByToAFunctor<Complex_Selector> extPairKeyFunctor;+ GroupedByToAResult arr;+ group_by_to_a(entries, extPairKeyFunctor, arr);+++ typedef pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+ typedef vector<SelsNewSeqPair> SelsNewSeqPairCollection;+++ SelsNewSeqPairCollection holder;+++ for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {+ pair<Complex_Selector, vector<ExtensionPair> >& groupedPair = *groupedIter;++ Complex_Selector& seq = groupedPair.first;+ vector<ExtensionPair>& group = groupedPair.second;++// DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: "))+++ Compound_Selector* pSels = new (ctx.mem) Compound_Selector(pSelector->pstate());+ for (vector<ExtensionPair>::iterator groupIter = group.begin(), groupIterEnd = group.end(); groupIter != groupIterEnd; groupIter++) {+ ExtensionPair& pair = *groupIter;+ Compound_Selector* pCompound = pair.second;+ for (size_t index = 0; index < pCompound->length(); index++) {+ Simple_Selector* pSimpleSelector = (*pCompound)[index];+ (*pSels) << pSimpleSelector;+ }+ }++++// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSels, "SELS: "))+++ Complex_Selector* pExtComplexSelector = &seq; // The selector up to where the @extend is (ie, the thing to merge)+ Compound_Selector* pExtCompoundSelector = pSels; // All the simple selectors to be replaced from the current compound selector from all extensions++++ // TODO: This can return a Compound_Selector with no elements. Should that just be returning NULL?+ Compound_Selector* pSelectorWithoutExtendSelectors = pSelector->minus(pExtCompoundSelector, ctx);+++// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "MEMBERS: "))+// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "SELF_WO_SEL: "))+++ Compound_Selector* pInnermostCompoundSelector = pExtComplexSelector->base();+ Compound_Selector* pUnifiedSelector = NULL;++ if (!pInnermostCompoundSelector) {+ pInnermostCompoundSelector = new (ctx.mem) Compound_Selector(pSelector->pstate());+ }++ pUnifiedSelector = pInnermostCompoundSelector->unify_with(pSelectorWithoutExtendSelectors, ctx);++// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pInnermostCompoundSelector, "LHS: "))+// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelectorWithoutExtendSelectors, "RHS: "))+// DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pUnifiedSelector, "UNIFIED: "))++ if (!pUnifiedSelector || pUnifiedSelector->length() == 0) {+ continue;+ }++++ // TODO: implement the parent directive match (if necessary based on test failures)+ // next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?+++++ // TODO: This seems a little fishy to me. See if it causes any problems. From the ruby, we should be able to just+ // get rid of the last Compound_Selector and replace it with this one. I think the reason this code is more+ // complex is that Complex_Selector contains a combinator, but in ruby combinators have already been filtered+ // out and aren't operated on.+ Complex_Selector* pNewSelector = pExtComplexSelector->cloneFully(ctx);+ Complex_Selector* pNewInnerMost = new (ctx.mem) Complex_Selector(pSelector->pstate(), Complex_Selector::ANCESTOR_OF, pUnifiedSelector, NULL);+ Complex_Selector::Combinator combinator = pNewSelector->clear_innermost();+ pNewSelector->set_innermost(pNewInnerMost, combinator);++#ifdef DEBUG+ SourcesSet debugSet;+ debugSet = pNewSelector->sources();+ if (debugSet.size() > 0) {+ throw "The new selector should start with no sources. Something needs to be cloned to fix this.";+ }+ debugSet = pExtComplexSelector->sources();+ if (debugSet.size() > 0) {+ throw "The extension selector from our subset map should not have sources. These will bleed to the new selector. Something needs to be cloned to fix this.";+ }+#endif+++ if (pSelector && pSelector->has_line_feed()) pNewSelector->has_line_feed(true);++ // Set the sources on our new Complex_Selector to the sources of this simple sequence plus the thing we're extending.+ DEBUG_PRINTLN(EXTEND_COMPOUND, "SOURCES SETTING ON NEW SEQ: " << complexSelectorToNode(pNewSelector, ctx))++ DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet oldSet = pNewSelector->sources(); printSourcesSet(oldSet, ctx, "SOURCES NEW SEQ BEGIN: "))++ SourcesSet newSourcesSet = pSelector->sources();+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES THIS EXTEND: "))++ newSourcesSet.insert(pExtComplexSelector);+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(newSourcesSet, ctx, "SOURCES WITH NEW SOURCE: "))++ pNewSelector->addSources(newSourcesSet, ctx);++ DEBUG_EXEC(EXTEND_COMPOUND, SourcesSet newSet = pNewSelector->sources(); printSourcesSet(newSet, ctx, "SOURCES ON NEW SELECTOR AFTER ADD: "))+ DEBUG_EXEC(EXTEND_COMPOUND, printSourcesSet(pSelector->sources(), ctx, "SOURCES THIS EXTEND WHICH SHOULD BE SAME STILL: "))++++ holder.push_back(make_pair(pSels, pNewSelector));+ }+++ for (SelsNewSeqPairCollection::iterator holderIter = holder.begin(), holderIterEnd = holder.end(); holderIter != holderIterEnd; holderIter++) {+ SelsNewSeqPair& pair = *holderIter;++ Compound_Selector* pSels = pair.first;+ Complex_Selector* pNewSelector = pair.second;+++ if (seen.find(*pSels) != seen.end()) {+ continue;+ }+++ set<Compound_Selector> recurseSeen(seen);+ recurseSeen.insert(*pSels);+++ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))++ Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subsetMap, recurseSeen);++ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND RETURN: " << recurseExtendedSelectors)++ for (NodeDeque::iterator iterator = recurseExtendedSelectors.collection()->begin(), endIterator = recurseExtendedSelectors.collection()->end();+ iterator != endIterator; ++iterator) {+ Node& newSelector = *iterator;++// DEBUG_PRINTLN(EXTEND_COMPOUND, "EXTENDED AT THIS POINT: " << extendedSelectors)+// DEBUG_PRINTLN(EXTEND_COMPOUND, "SELECTOR EXISTS ALREADY: " << newSelector << " " << extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/));++ if (!extendedSelectors.contains(newSelector, false /*simpleSelectorOrderDependent*/)) {+// DEBUG_PRINTLN(EXTEND_COMPOUND, "ADDING NEW SELECTOR")+ extendedSelectors.collection()->push_back(newSelector);+ }+ }+ }++ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND END: "))++ return extendedSelectors;+ }+++ static bool complexSelectorHasExtension(+ Complex_Selector* pComplexSelector,+ Context& ctx,+ ExtensionSubsetMap& subsetMap) {++ bool hasExtension = false;++ Complex_Selector* pIter = pComplexSelector;++ while (!hasExtension && pIter) {+ Compound_Selector* pHead = pIter->head();++ if (pHead) {+ SubsetMapEntries entries = subsetMap.get_v(pHead->to_str_vec());+ 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()+ ) {+ string query_left(ext.second->media_block()->media_queries()->perform(&to_string));+ string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string));+ if (query_left == query_right) continue;+ }++ // fail if one goes across media block boundaries+ stringstream err;+ string cwd(Sass::File::get_cwd());+ ParserState pstate(ext.second->pstate());+ string rel_path(Sass::File::resolve_relative_path(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 << " on line " << pstate.line+1 << " of " << rel_path << "\n";+ error(err.str(), pComplexSelector->pstate());+ }+ if (entries.size() > 0) hasExtension = true;+ }++ pIter = pIter->tail();+ }++ if (!hasExtension) {+ /* ToDo: don't break stuff+ stringstream err;+ To_String to_string(&ctx);+ string cwd(Sass::File::get_cwd());+ string sel1(pComplexSelector->perform(&to_string));+ Compound_Selector* pExtendSelector = 0;+ for (auto i : subsetMap.values()) {+ if (i.first == pComplexSelector) {+ pExtendSelector = i.second;+ break;+ }+ }+ if (!pExtendSelector || !pExtendSelector->is_optional()) {+ 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;+ }+++ /*+ This is the equivalent of ruby's Sequence.do_extend.++ // TODO: I think I have some modified ruby code to put here. Check.+ */+ /*+ ISSUES:+ - check to automatically include combinators doesn't transfer over to libsass' data model where+ the combinator and compound selector are one unit+ next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)+ */+ static Node extendComplexSelector(+ Complex_Selector* pComplexSelector,+ Context& ctx,+ ExtensionSubsetMap& subsetMap,+ set<Compound_Selector> seen) {++ pComplexSelector->tail()->has_line_feed(pComplexSelector->has_line_feed());++ Node complexSelector = complexSelectorToNode(pComplexSelector, ctx);+ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX: " << complexSelector)++ Node extendedNotExpanded = Node::createCollection();++ for (NodeDeque::iterator complexSelIter = complexSelector.collection()->begin(), complexSelIterEnd = complexSelector.collection()->end(); complexSelIter != complexSelIterEnd; ++complexSelIter) {+ Node& sseqOrOp = *complexSelIter;++ DEBUG_PRINTLN(EXTEND_COMPLEX, "LOOP: " << sseqOrOp)++ // If it's not a selector (meaning it's a combinator), just include it automatically+ if (!sseqOrOp.isSelector()) {+ // Wrap our Combinator in two collections to match ruby. This is essentially making a collection Node+ // with one collection child. The collection child represents a Complex_Selector that is only a combinator.+ Node outer = Node::createCollection();+ Node inner = Node::createCollection();+ outer.collection()->push_back(inner);+ inner.collection()->push_back(sseqOrOp);+ extendedNotExpanded.collection()->push_back(outer);+ continue;+ }++ Compound_Selector* pCompoundSelector = sseqOrOp.selector()->head();++ Node extended = extendCompoundSelector(pCompoundSelector, ctx, subsetMap, seen);++ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED: " << extended)+++ // Prepend the Compound_Selector based on the choices logic; choices seems to be extend but with an ruby Array instead of a Sequence+ // due to the member mapping: choices = extended.map {|seq| seq.members}+ Complex_Selector* pJustCurrentCompoundSelector = sseqOrOp.selector();++ bool isSuperselector = false;+ for (NodeDeque::iterator iterator = extended.collection()->begin(), endIterator = extended.collection()->end();+ iterator != endIterator; ++iterator) {+ Node& childNode = *iterator;+ Complex_Selector* pExtensionSelector = nodeToComplexSelector(childNode, ctx);+ if (pExtensionSelector->is_superselector_of(pJustCurrentCompoundSelector)) {+ isSuperselector = true;+ break;+ }+ }++ if (!isSuperselector) {+ extended.collection()->push_front(complexSelectorToNode(pJustCurrentCompoundSelector, ctx));+ }++ DEBUG_PRINTLN(EXTEND_COMPLEX, "CHOICES UNSHIFTED: " << extended)++ // Aggregate our current extensions+ extendedNotExpanded.collection()->push_back(extended);+ }+++ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTENDED NOT EXPANDED: " << extendedNotExpanded)++++ // Ruby Equivalent: paths+ Node paths = Sass::paths(extendedNotExpanded, ctx);++ DEBUG_PRINTLN(EXTEND_COMPLEX, "PATHS: " << paths)++++ // Ruby Equivalent: weave+ Node weaves = Node::createCollection();++ for (NodeDeque::iterator pathsIter = paths.collection()->begin(), pathsEndIter = paths.collection()->end(); pathsIter != pathsEndIter; ++pathsIter) {+ Node& path = *pathsIter;+ Node weaved = weave(path, ctx);+ weaves.collection()->push_back(weaved);+ }++ DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)++++ // Ruby Equivalent: trim+ Node trimmed = trim(weaves, ctx);++ DEBUG_PRINTLN(EXTEND_COMPLEX, "TRIMMED: " << trimmed)+++ // Ruby Equivalent: flatten+ Node extendedSelectors = flatten(trimmed, ctx, 1);++ DEBUG_PRINTLN(EXTEND_COMPLEX, ">>>>> EXTENDED: " << extendedSelectors)+++ DEBUG_PRINTLN(EXTEND_COMPLEX, "EXTEND COMPLEX END: " << complexSelector)+++ return extendedSelectors;+ }++++ /*+ This is the equivalent of ruby's CommaSequence.do_extend.+ */+ static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subsetMap, bool& extendedSomething) {++ To_String to_string(&ctx);++ Selector_List* pNewSelectors = new (ctx.mem) Selector_List(pSelectorList->pstate(), pSelectorList->length());++ extendedSomething = false;++ for (size_t index = 0, length = pSelectorList->length(); index < length; index++) {+ Complex_Selector* pSelector = (*pSelectorList)[index];++ // ruby sass seems to keep a list of things that have extensions and then only extend those. We don't currently do that.+ // Since it's not that expensive to check if an extension exists in the subset map and since it can be relatively expensive to+ // run through the extend code (which does a data model transformation), check if there is anything to extend before doing+ // the extend. We might be able to optimize extendComplexSelector, but this approach keeps us closer to ruby sass (which helps+ // when debugging).+ if (!complexSelectorHasExtension(pSelector, ctx, subsetMap)) {+ *pNewSelectors << pSelector;+ continue;+ }++ extendedSomething = true;++ set<Compound_Selector> seen;+ Node extendedSelectors = extendComplexSelector(pSelector, ctx, subsetMap, seen);++ if (!pSelector->has_placeholder()) {+ if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {+ *pNewSelectors << pSelector;+ }+ }++ for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) {+ Node& childNode = *iterator;+ *pNewSelectors << nodeToComplexSelector(childNode, ctx);+ }+ }++ return pNewSelectors;++ }+++ bool shouldExtendBlock(Block* b) {++ // If a block is empty, there's no reason to extend it since any rules placed on this block+ // won't have any output. The main benefit of this is for structures like:+ //+ // .a {+ // .b {+ // x: y;+ // }+ // }+ //+ // We end up visiting two rulesets (one with the selector .a and the other with the selector .a .b).+ // In this case, we don't want to try to pull rules onto .a since they won't get output anyway since+ // there are no child statements. However .a .b should have extensions applied.++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];++ if (typeid(*stm) == typeid(Ruleset)) {+ // Do nothing. This doesn't count as a statement that causes extension since we'll iterate over this rule set in a future visit and try to extend it.+ }+ else {+ return true;+ }+ }++ return false;++ }+++ // Extend a ruleset by extending the selectors and updating them on the ruleset. The block's rules don't need to change.+ template <typename ObjectType>+ static void extendObjectWithSelectorAndBlock(ObjectType* pObject, Context& ctx, ExtensionSubsetMap& subsetMap) {+ To_String to_string(&ctx);++ DEBUG_PRINTLN(EXTEND_OBJECT, "FOUND SELECTOR: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))++ // Ruby sass seems to filter nodes that don't have any content well before we get here. I'm not sure the repercussions+ // of doing so, so for now, let's just not extend things that won't be output later.+ if (!shouldExtendBlock(pObject->block())) {+ DEBUG_PRINTLN(EXTEND_OBJECT, "RETURNING WITHOUT EXTEND ATTEMPT")+ return;+ }++ bool extendedSomething = false;+ Selector_List* pNewSelectorList = extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subsetMap, extendedSomething);++ if (extendedSomething && pNewSelectorList) {+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND ORIGINAL SELECTORS: " << static_cast<Selector_List*>(pObject->selector())->perform(&to_string))+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND SETTING NEW SELECTORS: " << pNewSelectorList->perform(&to_string))++ // re-parse in order to restructure expanded placeholder nodes correctly.+ //+ // TODO: I don't know if this is needed, but it was in the original C++ implementation, so I kept it. Try running the tests without re-parsing.+ // this probably messes up source-maps+ pObject->selector(+ Parser::from_c_str(+ (pNewSelectorList->perform(&to_string) + ";").c_str(),+ ctx,+ pNewSelectorList->pstate()+ ).parse_selector_group()+ );+ } else {+ DEBUG_PRINTLN(EXTEND_OBJECT, "EXTEND DID NOT TRY TO EXTEND ANYTHING")+ }+ }++++ Extend::Extend(Context& ctx, ExtensionSubsetMap& ssm)+ : ctx(ctx), subset_map(ssm)+ { }++ void Extend::operator()(Block* b)+ {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ (*b)[i]->perform(this);+ }+ }++ void Extend::operator()(Ruleset* pRuleset)+ {+ extendObjectWithSelectorAndBlock(pRuleset, ctx, subset_map);++ pRuleset->block()->perform(this);+ }++ void Extend::operator()(Feature_Block* pFeatureBlock)+ {+ if (pFeatureBlock->selector()) {+ extendObjectWithSelectorAndBlock(pFeatureBlock, ctx, subset_map);+ }++ pFeatureBlock->block()->perform(this);+ }++ void Extend::operator()(Media_Block* pMediaBlock)+ {+ if (pMediaBlock->selector()) {+ extendObjectWithSelectorAndBlock(pMediaBlock, ctx, subset_map);+ }++ pMediaBlock->block()->perform(this);+ }++ void Extend::operator()(At_Rule* a)+ {+ if (a->block()) a->block()->perform(this);+ }+}
+ libsass/extend.hpp view
@@ -0,0 +1,45 @@+#ifndef SASS_EXTEND_H+#define SASS_EXTEND_H++#include <map>+#include <set>+#include <vector>+#include <iostream>++#include "ast.hpp"+#include "operation.hpp"+#include "subset_map.hpp"++namespace Sass {+ using namespace std;++ class Context;++ typedef Subset_Map<string, pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;++ class Extend : public Operation_CRTP<void, Extend> {++ Context& ctx;+ ExtensionSubsetMap& subset_map;++ void fallback_impl(AST_Node* n) { };++ public:+ Extend(Context&, ExtensionSubsetMap&);+ virtual ~Extend() { }++ using Operation<void>::operator();++ void operator()(Block*);+ void operator()(Ruleset*);+ void operator()(Feature_Block*);+ void operator()(Media_Block*);+ void operator()(At_Rule*);++ template <typename U>+ void fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/file.cpp view
@@ -0,0 +1,349 @@+#ifdef _WIN32+#include <direct.h>+#define getcwd _getcwd+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)+#else+#include <unistd.h>+#endif++#include <iostream>+#include <fstream>+#include <cctype>+#include <vector>+#include <algorithm>+#include <sys/stat.h>+#include "file.hpp"+#include "context.hpp"+#include "utf8_string.hpp"+#include "sass2scss.h"++#ifdef _WIN32+#include <windows.h>+#endif++#ifndef FS_CASE_SENSITIVE+#ifdef _WIN32+#define FS_CASE_SENSITIVE 0+#else+#define FS_CASE_SENSITIVE 1+#endif+#endif++namespace Sass {+ namespace File {+ using namespace std;++ // return the current directory+ // always with forward slashes+ string get_cwd()+ {+ const size_t wd_len = 1024;+ char wd[wd_len];+ string cwd = getcwd(wd, wd_len);+ #ifdef _WIN32+ //convert backslashes to forward slashes+ replace(cwd.begin(), cwd.end(), '\\', '/');+ #endif+ if (cwd[cwd.length() - 1] != '/') cwd += '/';+ return cwd;+ }++ // test if path exists and is a file+ bool file_exists(const string& path)+ {+ #ifdef _WIN32+ wstring wpath = UTF_8::convert_to_utf16(path);+ DWORD dwAttrib = GetFileAttributesW(wpath.c_str());+ return (dwAttrib != INVALID_FILE_ATTRIBUTES &&+ (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)));+ #else+ struct stat st_buf;+ return (stat (path.c_str(), &st_buf) == 0) &&+ (!S_ISDIR (st_buf.st_mode));+ #endif+ }++ // return if given path is absolute+ // works with *nix and windows paths+ bool is_absolute_path(const string& path)+ {+ #ifdef _WIN32+ if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;+ #endif+ return path[0] == '/';+ }++ // helper function to find the last directory seperator+ inline size_t find_last_folder_separator(const string& path, size_t limit = string::npos)+ {+ size_t pos = string::npos;+ size_t pos_p = path.find_last_of('/', limit);+ #ifdef _WIN32+ size_t pos_w = path.find_last_of('\\', limit);+ #else+ size_t pos_w = string::npos;+ #endif+ if (pos_p != string::npos && pos_w != string::npos) {+ pos = max(pos_p, pos_w);+ }+ else if (pos_p != string::npos) {+ pos = pos_p;+ }+ else {+ pos = pos_w;+ }+ return pos;+ }++ // return only the directory part of path+ string dir_name(const string& path)+ {+ size_t pos = find_last_folder_separator(path);+ if (pos == string::npos) return "";+ else return path.substr(0, pos+1);+ }++ // return only the filename part of path+ string base_name(const string& path)+ {+ size_t pos = find_last_folder_separator(path);+ if (pos == string::npos) return path;+ else return path.substr(pos+1);+ }++ // do a locigal clean up of the path+ // no physical check on the filesystem+ string make_canonical_path (string path)+ {++ // declarations+ size_t pos;++ #ifdef _WIN32+ //convert backslashes to forward slashes+ replace(path.begin(), path.end(), '\\', '/');+ #endif++ pos = 0; // remove all self references inside the path string+ while((pos = path.find("/./", pos)) != string::npos) path.erase(pos, 2);++ pos = 0; // remove all leading and trailing self references+ while(path.length() > 1 && path.substr(0, 2) == "./") path.erase(0, 2);+ while((pos = path.length()) > 1 && path.substr(pos - 2) == "/.") path.erase(pos - 2);++ pos = 0; // collapse multiple delimiters into a single one+ while((pos = path.find("//", pos)) != string::npos) path.erase(pos, 1);++ return path;++ }++ // join two path segments cleanly together+ // but only if right side is not absolute yet+ string join_paths(string l, string r)+ {++ #ifdef _WIN32+ // convert Windows backslashes to URL forward slashes+ replace(l.begin(), l.end(), '\\', '/');+ replace(r.begin(), r.end(), '\\', '/');+ #endif++ if (l.empty()) return r;+ if (r.empty()) return l;++ if (is_absolute_path(r)) return r;+ if (l[l.length()-1] != '/') l += '/';++ while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {+ r = r.substr(3);+ size_t pos = find_last_folder_separator(l, l.length() - 2);+ l = l.substr(0, pos == string::npos ? pos : pos + 1);+ }++ return l + r;+ }++ // create an absolute path by resolving relative paths with cwd+ string make_absolute_path(const string& path, const string& cwd)+ {+ return make_canonical_path((is_absolute_path(path) ? path : join_paths(cwd, path)));+ }++ // create a path that is relative to the given base directory+ // path and base will first be resolved against cwd to make them absolute+ string resolve_relative_path(const string& uri, const string& base, const string& cwd)+ {++ string absolute_uri = make_absolute_path(uri, cwd);+ string absolute_base = make_absolute_path(base, cwd);++ #ifdef _WIN32+ // absolute link must have a drive letter, and we know that we+ // can only create relative links if both are on the same drive+ if (absolute_base[0] != absolute_uri[0]) return absolute_uri;+ #endif++ string stripped_uri = "";+ string stripped_base = "";++ size_t index = 0;+ size_t minSize = min(absolute_uri.size(), absolute_base.size());+ for (size_t i = 0; i < minSize; ++i) {+ #ifdef FS_CASE_SENSITIVE+ if (absolute_uri[i] != absolute_base[i]) break;+ #else+ // compare the charactes in a case insensitive manner+ // windows fs is only case insensitive in ascii ranges+ if (tolower(absolute_uri[i]) != tolower(absolute_base[i])) break;+ #endif+ if (absolute_uri[i] == '/') index = i + 1;+ }+ for (size_t i = index; i < absolute_uri.size(); ++i) {+ stripped_uri += absolute_uri[i];+ }+ for (size_t i = index; i < absolute_base.size(); ++i) {+ stripped_base += absolute_base[i];+ }++ size_t left = 0;+ size_t directories = 0;+ for (size_t right = 0; right < stripped_base.size(); ++right) {+ if (stripped_base[right] == '/') {+ if (stripped_base.substr(left, 2) != "..") {+ ++directories;+ }+ else if (directories > 1) {+ --directories;+ }+ else {+ directories = 0;+ }+ left = right + 1;+ }+ }++ string result = "";+ for (size_t i = 0; i < directories; ++i) {+ result += "../";+ }+ result += stripped_uri;++ return result;+ }++ // Resolution order for ambiguous imports:+ // (1) filename as given+ // (2) underscore + given+ // (3) underscore + given + extension+ // (4) given + extension+ string resolve_file(const string& filename)+ {+ // supported extensions+ const vector<string> exts = {+ ".scss", ".sass", ".css"+ };+ // split the filename+ string base(dir_name(filename));+ string name(base_name(filename));+ // create full path (maybe relative)+ string path(join_paths(base, name));+ if (file_exists(path)) return path;+ // next test variation with underscore+ path = join_paths(base, "_" + name);+ if (file_exists(path)) return path;+ // next test exts plus underscore+ for(auto ext : exts) {+ path = join_paths(base, "_" + name + ext);+ if (file_exists(path)) return path;+ }+ // next test plain name with exts+ for(auto ext : exts) {+ path = join_paths(base, name + ext);+ if (file_exists(path)) return path;+ }+ // nothing found+ return string("");+ }++ // helper function to resolve a filename+ string find_file(const string& file, const vector<string> paths)+ {+ // search in every include path for a match+ for (size_t i = 0, S = paths.size(); i < S; ++i)+ {+ string path(join_paths(paths[i], file));+ string resolved(resolve_file(path));+ if (resolved != "") return resolved;+ }+ // nothing found+ return string("");+ }++ // inc paths can be directly passed from C code+ string find_file(const string& file, const char* paths[])+ {+ if (paths == 0) return string("");+ vector<string> includes(0);+ // includes.push_back(".");+ const char** it = paths;+ while (it && *it) {+ includes.push_back(*it);+ ++it;+ }+ return find_file(file, includes);+ }++ // try to load the given filename+ // returned memory must be freed+ // will auto convert .sass files+ char* read_file(const string& path)+ {+ #ifdef _WIN32+ BYTE* pBuffer;+ DWORD dwBytes;+ // windows unicode filepaths are encoded in utf16+ wstring wpath = UTF_8::convert_to_utf16(path);+ HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);+ if (hFile == INVALID_HANDLE_VALUE) return 0;+ DWORD dwFileLength = GetFileSize(hFile, NULL);+ if (dwFileLength == INVALID_FILE_SIZE) return 0;+ // allocate an extra byte for the null char+ pBuffer = (BYTE*)malloc((dwFileLength+1)*sizeof(BYTE));+ ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);+ pBuffer[dwFileLength] = '\0';+ CloseHandle(hFile);+ // just convert from unsigned char*+ char* contents = (char*) pBuffer;+ #else+ struct stat st;+ if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;+ ifstream file(path.c_str(), ios::in | ios::binary | ios::ate);+ char* contents = 0;+ if (file.is_open()) {+ size_t size = file.tellg();+ // allocate an extra byte for the null char+ contents = (char*) malloc((size+1)*sizeof(char));+ file.seekg(0, ios::beg);+ file.read(contents, size);+ contents[size] = '\0';+ file.close();+ }+ #endif+ string extension;+ if (path.length() > 5) {+ extension = path.substr(path.length() - 5, 5);+ }+ for(size_t i=0; i<extension.size();++i)+ extension[i] = tolower(extension[i]);+ if (extension == ".sass" && contents != 0) {+ char * converted = sass2scss(contents, SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);+ free(contents); // free the indented contents+ return converted; // should be freed by caller+ } else {+ return contents;+ }+ }++ }+}
+ libsass/file.hpp view
@@ -0,0 +1,60 @@+#ifndef SASS_FILE_H+#define SASS_FILE_H++#include <string>+#include <vector>++namespace Sass {+ using namespace std;+ class Context;+ namespace File {++ // return the current directory+ // always with forward slashes+ string get_cwd();++ // test if path exists and is a file+ bool file_exists(const string& file);++ // return if given path is absolute+ // works with *nix and windows paths+ bool is_absolute_path(const string& path);++ // return only the directory part of path+ string dir_name(const string& path);++ // return only the filename part of path+ string base_name(const string&);++ // do a locigal clean up of the path+ // no physical check on the filesystem+ string make_canonical_path (string path);++ // join two path segments cleanly together+ // but only if right side is not absolute yet+ string join_paths(string root, string name);++ // create an absolute path by resolving relative paths with cwd+ string make_absolute_path(const string& path, const string& cwd = ".");++ // create a path that is relative to the given base directory+ // path and base will first be resolved against cwd to make them absolute+ string resolve_relative_path(const string& path, const string& base, const string& cwd = ".");++ // try to find/resolve the filename+ string resolve_file(const string& file);++ // helper function to resolve a filename+ string find_file(const string& file, const vector<string> paths);+ // inc paths can be directly passed from C code+ string find_file(const string& file, const char** paths);++ // try to load the given filename+ // returned memory must be freed+ // will auto convert .sass files+ char* read_file(const string& file);++ }+}++#endif
+ libsass/functions.cpp view
@@ -0,0 +1,1567 @@+#include "functions.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "parser.hpp"+#include "constants.hpp"+#include "to_string.hpp"+#include "inspect.hpp"+#include "eval.hpp"+#include "util.hpp"+#include "utf8_string.hpp"+#include "utf8.h"++#include <atomic>+#include <cstdlib>+#include <cmath>+#include <cctype>+#include <sstream>+#include <string>+#include <iomanip>+#include <iostream>+#include <random>+#include <set>++#ifdef __MINGW32__+#include "windows.h"+#include "wincrypt.h"+#endif++#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, backtrace)+#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, pstate, lo, hi, backtrace)+#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx)++namespace Sass {+ using std::stringstream;+ using std::endl;++ Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)+ {+ Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));+ sig_parser.lex<Prelexer::identifier>();+ string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters* params = sig_parser.parse_parameters();+ return new (ctx.mem) Definition(ParserState("[built-in function]"),+ sig,+ name,+ params,+ func,+ &ctx,+ false);+ }++ Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+ {+ const char* sig = sass_function_get_signature(c_func);+ Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[c function]"));+ // allow to overload generic callback plus @warn, @error and @debug with custom functions+ sig_parser.lex < alternatives < identifier, exactly <'*'>,+ exactly < Constants::warn_kwd >,+ exactly < Constants::error_kwd >,+ exactly < Constants::debug_kwd >+ > >();+ string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters* params = sig_parser.parse_parameters();+ return new (ctx.mem) Definition(ParserState("[c function]"),+ sig,+ name,+ params,+ c_func,+ &ctx,+ false, true);+ }++ namespace Functions {++ template <typename T>+ T* get_arg(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ T* val = dynamic_cast<T*>(env[argname]);+ if (!val) {+ string msg("argument `");+ msg += argname;+ msg += "` of `";+ msg += sig;+ msg += "` must be a ";+ msg += T::type_name();+ error(msg, pstate, backtrace);+ }+ return val;+ }++ Map* get_arg_m(const string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ Map* val = dynamic_cast<Map*>(env[argname]);+ if (val) return val;++ List* lval = dynamic_cast<List*>(env[argname]);+ if (lval && lval->length() == 0) return new (ctx.mem) Map(pstate, 0);++ // fallback on get_arg for error handling+ val = get_arg<Map>(argname, env, sig, pstate, backtrace);+ return val;+ }++ Number* get_arg_r(const string& argname, Env& env, Signature sig, ParserState pstate, double lo, double hi, Backtrace* backtrace)+ {+ // Minimal error handling -- the expectation is that built-ins will be written correctly!+ Number* val = get_arg<Number>(argname, env, sig, pstate, backtrace);+ double v = val->value();+ if (!(lo <= v && v <= hi)) {+ stringstream msg;+ msg << "argument `" << argname << "` of `" << sig << "` must be between ";+ msg << lo << " and " << hi;+ error(msg.str(), pstate, backtrace);+ }+ return val;+ }++#ifdef __MINGW32__+ uint64_t GetSeed()+ {+ HCRYPTPROV hp = 0;+ BYTE rb[8];+ CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);+ CryptGenRandom(hp, sizeof(rb), rb);+ CryptReleaseContext(hp, 0);++ uint64_t seed;+ memcpy(&seed, &rb[0], sizeof(seed));++ return seed;+ }+#else+ static random_device rd;+ uint64_t GetSeed()+ {+ return rd();+ }+#endif++ // note: the performance of many implementations of+ // random_device degrades sharply once the entropy pool+ // is exhausted. For practical use, random_device is+ // generally only used to seed a PRNG such as mt19937.+ static mt19937 rand(static_cast<unsigned int>(GetSeed()));++ // features+ static set<string> features {+ "global-variable-shadowing",+ "at-error",+ "units-level-3"+ };++ ////////////////+ // RGB FUNCTIONS+ ////////////////++ inline double color_num(Number* n) {+ if (n->unit() == "%") {+ return std::min(std::max(n->value(), 0.0), 1.0) * 255;+ } else {+ return std::min(std::max(n->value(), 0.0), 255.0);+ }+ }++ Signature rgb_sig = "rgb($red, $green, $blue)";+ BUILT_IN(rgb)+ {+ return new (ctx.mem) Color(pstate,+ color_num(ARGR("$red", Number, 0, 255)),+ color_num(ARGR("$green", Number, 0, 255)),+ color_num(ARGR("$blue", Number, 0, 255)));+ }++ Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";+ BUILT_IN(rgba_4)+ {+ return new (ctx.mem) Color(pstate,+ color_num(ARGR("$red", Number, 0, 255)),+ color_num(ARGR("$green", Number, 0, 255)),+ color_num(ARGR("$blue", Number, 0, 255)),+ ARGR("$alpha", Number, 0, 1)->value());+ }++ Signature rgba_2_sig = "rgba($color, $alpha)";+ BUILT_IN(rgba_2)+ {+ Color* c_arg = ARG("$color", Color);+ Color* new_c = new (ctx.mem) Color(*c_arg);+ new_c->a(ARGR("$alpha", Number, 0, 1)->value());+ new_c->disp("");+ return new_c;+ }++ Signature red_sig = "red($color)";+ BUILT_IN(red)+ { return new (ctx.mem) Number(pstate, ARG("$color", Color)->r()); }++ Signature green_sig = "green($color)";+ BUILT_IN(green)+ { return new (ctx.mem) Number(pstate, ARG("$color", Color)->g()); }++ Signature blue_sig = "blue($color)";+ BUILT_IN(blue)+ { return new (ctx.mem) Number(pstate, ARG("$color", Color)->b()); }++ Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)";+ BUILT_IN(mix)+ {+ Color* color1 = ARG("$color-1", Color);+ Color* color2 = ARG("$color-2", Color);+ Number* weight = ARGR("$weight", Number, 0, 100);++ double p = weight->value()/100;+ double w = 2*p - 1;+ double a = color1->a() - color2->a();++ double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;+ double w2 = 1 - w1;++ return new (ctx.mem) Color(pstate,+ std::floor(w1*color1->r() + w2*color2->r()),+ std::floor(w1*color1->g() + w2*color2->g()),+ std::floor(w1*color1->b() + w2*color2->b()),+ color1->a()*p + color2->a()*(1-p));+ }++ ////////////////+ // HSL FUNCTIONS+ ////////////////++ // RGB to HSL helper function+ struct HSL { double h; double s; double l; };+ HSL rgb_to_hsl(double r, double g, double b)+ {++ // Algorithm from http://en.wikipedia.org/wiki/wHSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV+ r /= 255.0; g /= 255.0; b /= 255.0;++ double max = std::max(r, std::max(g, b));+ double min = std::min(r, std::min(g, b));+ double del = max - min;++ double h = 0, s = 0, l = (max + min) / 2.0;++ if (max == min) {+ h = s = 0; // achromatic+ }+ else {+ if (l < 0.5) s = del / (2.0 * l);+ else s = del / (2.0 - 2.0 * l);++ if (r == max) h = 60 * (g - b) / del;+ else if (g == max) h = 60 * (b - r) / del + 120;+ else if (b == max) h = 60 * (r - g) / del + 240;+ }++ HSL hsl_struct;+ hsl_struct.h = h;+ hsl_struct.s = s * 100;+ hsl_struct.l = l * 100;++ return hsl_struct;+ }++ // hue to RGB helper function+ double h_to_rgb(double m1, double m2, double h) {+ if (h < 0) h += 1;+ if (h > 1) h -= 1;+ if (h*6.0 < 1) return m1 + (m2 - m1)*h*6;+ if (h*2.0 < 1) return m2;+ if (h*3.0 < 2) return m1 + (m2 - m1) * (2.0/3.0 - h)*6;+ return m1;+ }++ Color* hsla_impl(double h, double s, double l, double a, Context& ctx, ParserState pstate)+ {+ h /= 360.0;+ s /= 100.0;+ l /= 100.0;++ // Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.+ double m2;+ if (l <= 0.5) m2 = l*(s+1.0);+ else m2 = (l+s)-(l*s);+ double m1 = (l*2)-m2;+ // round the results -- consider moving this into the Color constructor+ double r = (h_to_rgb(m1, m2, h+1.0/3.0) * 255.0);+ double g = (h_to_rgb(m1, m2, h) * 255.0);+ double b = (h_to_rgb(m1, m2, h-1.0/3.0) * 255.0);++ return new (ctx.mem) Color(pstate, r, g, b, a);+ }++ Signature hsl_sig = "hsl($hue, $saturation, $lightness)";+ BUILT_IN(hsl)+ {+ return hsla_impl(ARG("$hue", Number)->value(),+ ARGR("$saturation", Number, 0, 100)->value(),+ ARGR("$lightness", Number, 0, 100)->value(),+ 1.0,+ ctx,+ pstate);+ }++ Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";+ BUILT_IN(hsla)+ {+ return hsla_impl(ARG("$hue", Number)->value(),+ ARGR("$saturation", Number, 0, 100)->value(),+ ARGR("$lightness", Number, 0, 100)->value(),+ ARGR("$alpha", Number, 0, 1)->value(),+ ctx,+ pstate);+ }++ Signature hue_sig = "hue($color)";+ BUILT_IN(hue)+ {+ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return new (ctx.mem) Number(pstate, hsl_color.h, "deg");+ }++ Signature saturation_sig = "saturation($color)";+ BUILT_IN(saturation)+ {+ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return new (ctx.mem) Number(pstate, hsl_color.s, "%");+ }++ Signature lightness_sig = "lightness($color)";+ BUILT_IN(lightness)+ {+ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return new (ctx.mem) Number(pstate, hsl_color.l, "%");+ }++ Signature adjust_hue_sig = "adjust-hue($color, $degrees)";+ BUILT_IN(adjust_hue)+ {+ Color* rgb_color = ARG("$color", Color);+ Number* degrees = ARG("$degrees", Number);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return hsla_impl(hsl_color.h + degrees->value(),+ hsl_color.s,+ hsl_color.l,+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature lighten_sig = "lighten($color, $amount)";+ BUILT_IN(lighten)+ {+ Color* rgb_color = ARG("$color", Color);+ Number* amount = ARGR("$amount", Number, 0, 100);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ //Check lightness is not negative before lighten it+ double hslcolorL = hsl_color.l;+ if (hslcolorL < 0) {+ hslcolorL = 0;+ }++ return hsla_impl(hsl_color.h,+ hsl_color.s,+ hslcolorL + amount->value(),+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature darken_sig = "darken($color, $amount)";+ BUILT_IN(darken)+ {+ Color* rgb_color = ARG("$color", Color);+ Number* amount = ARGR("$amount", Number, 0, 100);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());++ //Check lightness if not over 100, before darken it+ double hslcolorL = hsl_color.l;+ if (hslcolorL > 100) {+ hslcolorL = 100;+ }++ return hsla_impl(hsl_color.h,+ hsl_color.s,+ hslcolorL - amount->value(),+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature saturate_sig = "saturate($color, $amount: false)";+ BUILT_IN(saturate)+ {+ // CSS3 filter function overload: pass literal through directly+ Number* amount = dynamic_cast<Number*>(env["$amount"]);+ if (!amount) {+ To_String to_string(&ctx);+ return new (ctx.mem) String_Constant(pstate, "saturate(" + env["$color"]->perform(&to_string) + ")");+ }++ ARGR("$amount", Number, 0, 100);+ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());++ double hslcolorS = hsl_color.s + amount->value();++ // Saturation cannot be below 0 or above 100+ if (hslcolorS < 0) {+ hslcolorS = 0;+ }+ if (hslcolorS > 100) {+ hslcolorS = 100;+ }++ return hsla_impl(hsl_color.h,+ hslcolorS,+ hsl_color.l,+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature desaturate_sig = "desaturate($color, $amount)";+ BUILT_IN(desaturate)+ {+ Color* rgb_color = ARG("$color", Color);+ Number* amount = ARGR("$amount", Number, 0, 100);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());++ double hslcolorS = hsl_color.s - amount->value();++ // Saturation cannot be below 0 or above 100+ if (hslcolorS <= 0) {+ hslcolorS = 0;+ }+ if (hslcolorS > 100) {+ hslcolorS = 100;+ }++ return hsla_impl(hsl_color.h,+ hslcolorS,+ hsl_color.l,+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature grayscale_sig = "grayscale($color)";+ BUILT_IN(grayscale)+ {+ // CSS3 filter function overload: pass literal through directly+ Number* amount = dynamic_cast<Number*>(env["$color"]);+ if (amount) {+ To_String to_string(&ctx);+ return new (ctx.mem) String_Constant(pstate, "grayscale(" + amount->perform(&to_string) + ")");+ }++ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return hsla_impl(hsl_color.h,+ 0.0,+ hsl_color.l,+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature complement_sig = "complement($color)";+ BUILT_IN(complement)+ {+ Color* rgb_color = ARG("$color", Color);+ HSL hsl_color = rgb_to_hsl(rgb_color->r(),+ rgb_color->g(),+ rgb_color->b());+ return hsla_impl(hsl_color.h - 180.0,+ hsl_color.s,+ hsl_color.l,+ rgb_color->a(),+ ctx,+ pstate);+ }++ Signature invert_sig = "invert($color)";+ BUILT_IN(invert)+ {+ // CSS3 filter function overload: pass literal through directly+ Number* amount = dynamic_cast<Number*>(env["$color"]);+ if (amount) {+ To_String to_string(&ctx);+ return new (ctx.mem) String_Constant(pstate, "invert(" + amount->perform(&to_string) + ")");+ }++ Color* rgb_color = ARG("$color", Color);+ return new (ctx.mem) Color(pstate,+ 255 - rgb_color->r(),+ 255 - rgb_color->g(),+ 255 - rgb_color->b(),+ rgb_color->a());+ }++ ////////////////////+ // OPACITY FUNCTIONS+ ////////////////////+ Signature alpha_sig = "alpha($color)";+ Signature opacity_sig = "opacity($color)";+ BUILT_IN(alpha)+ {+ String_Constant* ie_kwd = dynamic_cast<String_Constant*>(env["$color"]);+ if (ie_kwd) {+ return new (ctx.mem) String_Constant(pstate, "alpha(" + ie_kwd->value() + ")");+ }++ // CSS3 filter function overload: pass literal through directly+ Number* amount = dynamic_cast<Number*>(env["$color"]);+ if (amount) {+ To_String to_string(&ctx);+ return new (ctx.mem) String_Constant(pstate, "opacity(" + amount->perform(&to_string) + ")");+ }++ return new (ctx.mem) Number(pstate, ARG("$color", Color)->a());+ }++ Signature opacify_sig = "opacify($color, $amount)";+ Signature fade_in_sig = "fade-in($color, $amount)";+ BUILT_IN(opacify)+ {+ Color* color = ARG("$color", Color);+ double amount = ARGR("$amount", Number, 0, 1)->value();+ double alpha = std::min(color->a() + amount, 1.0);+ return new (ctx.mem) Color(pstate,+ color->r(),+ color->g(),+ color->b(),+ alpha);+ }++ Signature transparentize_sig = "transparentize($color, $amount)";+ Signature fade_out_sig = "fade-out($color, $amount)";+ BUILT_IN(transparentize)+ {+ Color* color = ARG("$color", Color);+ double amount = ARGR("$amount", Number, 0, 1)->value();+ double alpha = std::max(color->a() - amount, 0.0);+ return new (ctx.mem) Color(pstate,+ color->r(),+ color->g(),+ color->b(),+ alpha);+ }++ ////////////////////////+ // OTHER COLOR FUNCTIONS+ ////////////////////////++ Signature adjust_color_sig = "adjust-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(adjust_color)+ {+ Color* color = ARG("$color", Color);+ Number* r = dynamic_cast<Number*>(env["$red"]);+ Number* g = dynamic_cast<Number*>(env["$green"]);+ Number* b = dynamic_cast<Number*>(env["$blue"]);+ Number* h = dynamic_cast<Number*>(env["$hue"]);+ Number* s = dynamic_cast<Number*>(env["$saturation"]);+ Number* l = dynamic_cast<Number*>(env["$lightness"]);+ Number* a = dynamic_cast<Number*>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("cannot specify both RGB and HSL values for `adjust-color`", pstate);+ }+ if (rgb) {+ return new (ctx.mem) Color(pstate,+ color->r() + (r ? r->value() : 0),+ color->g() + (g ? g->value() : 0),+ color->b() + (b ? b->value() : 0),+ color->a() + (a ? a->value() : 0));+ }+ if (hsl) {+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());+ return hsla_impl(hsl_struct.h + (h ? h->value() : 0),+ hsl_struct.s + (s ? s->value() : 0),+ hsl_struct.l + (l ? l->value() : 0),+ color->a() + (a ? a->value() : 0),+ ctx,+ pstate);+ }+ if (a) {+ return new (ctx.mem) Color(pstate,+ color->r(),+ color->g(),+ color->b(),+ color->a() + (a ? a->value() : 0));+ }+ error("not enough arguments for `adjust-color`", pstate);+ // unreachable+ return color;+ }++ Signature scale_color_sig = "scale-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(scale_color)+ {+ Color* color = ARG("$color", Color);+ Number* r = dynamic_cast<Number*>(env["$red"]);+ Number* g = dynamic_cast<Number*>(env["$green"]);+ Number* b = dynamic_cast<Number*>(env["$blue"]);+ Number* h = dynamic_cast<Number*>(env["$hue"]);+ Number* s = dynamic_cast<Number*>(env["$saturation"]);+ Number* l = dynamic_cast<Number*>(env["$lightness"]);+ Number* a = dynamic_cast<Number*>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("cannot specify both RGB and HSL values for `scale-color`", pstate);+ }+ if (rgb) {+ double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double gscale = (g ? ARGR("$green", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ return new (ctx.mem) Color(pstate,+ color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()),+ color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()),+ color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()),+ color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));+ }+ if (hsl) {+ double hscale = (h ? ARGR("$hue", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double sscale = (s ? ARGR("$saturation", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double lscale = (l ? ARGR("$lightness", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());+ hsl_struct.h += hscale * (hscale > 0.0 ? 360.0 - hsl_struct.h : hsl_struct.h);+ hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s);+ hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l);+ double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a());+ return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);+ }+ if (a) {+ double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0;+ return new (ctx.mem) Color(pstate,+ color->r(),+ color->g(),+ color->b(),+ color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));+ }+ error("not enough arguments for `scale-color`", pstate);+ // unreachable+ return color;+ }++ Signature change_color_sig = "change-color($color, $red: false, $green: false, $blue: false, $hue: false, $saturation: false, $lightness: false, $alpha: false)";+ BUILT_IN(change_color)+ {+ Color* color = ARG("$color", Color);+ Number* r = dynamic_cast<Number*>(env["$red"]);+ Number* g = dynamic_cast<Number*>(env["$green"]);+ Number* b = dynamic_cast<Number*>(env["$blue"]);+ Number* h = dynamic_cast<Number*>(env["$hue"]);+ Number* s = dynamic_cast<Number*>(env["$saturation"]);+ Number* l = dynamic_cast<Number*>(env["$lightness"]);+ Number* a = dynamic_cast<Number*>(env["$alpha"]);++ bool rgb = r || g || b;+ bool hsl = h || s || l;++ if (rgb && hsl) {+ error("cannot specify both RGB and HSL values for `change-color`", pstate);+ }+ if (rgb) {+ return new (ctx.mem) Color(pstate,+ r ? ARGR("$red", Number, 0, 255)->value() : color->r(),+ g ? ARGR("$green", Number, 0, 255)->value() : color->g(),+ b ? ARGR("$blue", Number, 0, 255)->value() : color->b(),+ a ? ARGR("$alpha", Number, 0, 255)->value() : color->a());+ }+ if (hsl) {+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());+ if (h) hsl_struct.h = static_cast<double>(((static_cast<int>(h->value()) % 360) + 360) % 360) / 360.0;+ if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value();+ if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value();+ double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();+ return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, pstate);+ }+ if (a) {+ double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a();+ return new (ctx.mem) Color(pstate,+ color->r(),+ color->g(),+ color->b(),+ alpha);+ }+ error("not enough arguments for `change-color`", pstate);+ // unreachable+ return color;+ }++ template <size_t range>+ static double cap_channel(double c) {+ if (c > range) return range;+ else if (c < 0) return 0;+ else return c;+ }++ Signature ie_hex_str_sig = "ie-hex-str($color)";+ BUILT_IN(ie_hex_str)+ {+ Color* c = ARG("$color", Color);+ double r = cap_channel<0xff>(c->r());+ double g = cap_channel<0xff>(c->g());+ double b = cap_channel<0xff>(c->b());+ double a = cap_channel<1> (c->a()) * 255;++ stringstream ss;+ ss << '#' << std::setw(2) << std::setfill('0');+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(a+0.5));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(r+0.5));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(g+0.5));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(std::floor(b+0.5));++ string result(ss.str());+ for (size_t i = 0, L = result.length(); i < L; ++i) {+ result[i] = std::toupper(result[i]);+ }+ return new (ctx.mem) String_Constant(pstate, result);+ }++ ///////////////////+ // STRING FUNCTIONS+ ///////////////////++ Signature unquote_sig = "unquote($string)";+ BUILT_IN(sass_unquote)+ {+ To_String to_string(&ctx);+ AST_Node* arg = env["$string"];+ if (String_Quoted* string_quoted = dynamic_cast<String_Quoted*>(arg)) {+ String_Constant* result = new (ctx.mem) String_Constant(pstate, string_quoted->value());+ // remember if the string was quoted (color tokens)+ result->sass_fix_1291(string_quoted->quote_mark() != 0);+ return result;+ }+ return new (ctx.mem) String_Constant(pstate, string(arg->perform(&to_string)));+ }++ Signature quote_sig = "quote($string)";+ BUILT_IN(sass_quote)+ {+ To_String to_string(&ctx);+ AST_Node* arg = env["$string"];+ string str(quote(arg->perform(&to_string), String_Constant::double_quote()));+ String_Constant* result = new (ctx.mem) String_Constant(pstate, str);+ result->is_delayed(true);+ return result;+ }+++ Signature str_length_sig = "str-length($string)";+ BUILT_IN(str_length)+ {+ size_t len = string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ len = UTF_8::code_point_count(s->value(), 0, s->value().size());++ }+ catch (utf8::invalid_code_point) {+ string msg("utf8::invalid_code_point");+ error(msg, pstate, backtrace);+ }+ catch (utf8::not_enough_room) {+ string msg("utf8::not_enough_room");+ error(msg, pstate, backtrace);+ }+ catch (utf8::invalid_utf8) {+ string msg("utf8::invalid_utf8");+ error(msg, pstate, backtrace);+ }+ catch (...) { throw; }+ // return something even if we had an error (-1)+ return new (ctx.mem) Number(pstate, len);+ }++ Signature str_insert_sig = "str-insert($string, $insert, $index)";+ BUILT_IN(str_insert)+ {+ string str;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ str = s->value();+ str = unquote(str);+ String_Constant* i = ARG("$insert", String_Constant);+ string ins = i->value();+ ins = unquote(ins);+ Number* ind = ARG("$index", Number);+ double index = ind->value();+ size_t len = UTF_8::code_point_count(str, 0, str.size());++ if (index > 0 && index <= len) {+ // positive and within string length+ str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index) - 1), ins);+ }+ else if (index > len) {+ // positive and past string length+ str += ins;+ }+ else if (index == 0) {+ str = ins + str;+ }+ else if (std::abs(index) <= len) {+ // negative and within string length+ index += len + 1;+ str.insert(UTF_8::offset_at_position(str, static_cast<size_t>(index)), ins);+ }+ else {+ // negative and past string length+ str = ins + str;+ }++ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if (ss->quote_mark()) str = quote(str);+ }+ }+ catch (utf8::invalid_code_point) {+ string msg("utf8::invalid_code_point");+ error(msg, pstate, backtrace);+ }+ catch (utf8::not_enough_room) {+ string msg("utf8::not_enough_room");+ error(msg, pstate, backtrace);+ }+ catch (utf8::invalid_utf8) {+ string msg("utf8::invalid_utf8");+ error(msg, pstate, backtrace);+ }+ catch (...) { throw; }+ return new (ctx.mem) String_Constant(pstate, str);+ }++ Signature str_index_sig = "str-index($string, $substring)";+ BUILT_IN(str_index)+ {+ size_t index = string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ String_Constant* t = ARG("$substring", String_Constant);+ string str = s->value();+ str = unquote(str);+ string substr = t->value();+ substr = unquote(substr);++ size_t c_index = str.find(substr);+ if(c_index == string::npos) {+ return new (ctx.mem) Null(pstate);+ }+ index = UTF_8::code_point_count(str, 0, c_index) + 1;+ }+ catch (utf8::invalid_code_point) {+ string msg("utf8::invalid_code_point");+ error(msg, pstate, backtrace);+ }+ catch (utf8::not_enough_room) {+ string msg("utf8::not_enough_room");+ error(msg, pstate, backtrace);+ }+ catch (utf8::invalid_utf8) {+ string msg("utf8::invalid_utf8");+ error(msg, pstate, backtrace);+ }+ catch (...) { throw; }+ // return something even if we had an error (-1)+ return new (ctx.mem) Number(pstate, index);+ }++ Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";+ BUILT_IN(str_slice)+ {+ string newstr;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ Number* n = ARG("$start-at", Number);+ Number* m = ARG("$end-at", Number);++ string str = unquote(s->value());++ // normalize into 0-based indices+ size_t start = UTF_8::offset_at_position(str, UTF_8::normalize_index(static_cast<int>(n->value()), UTF_8::code_point_count(str)));+ size_t end = UTF_8::offset_at_position(str, UTF_8::normalize_index(static_cast<int>(m->value()), UTF_8::code_point_count(str)));++ // `str-slice` should always return an empty string when $end-at == 0+ // `normalize_index` normalizes 1 -> 0 so we need to check the original value+ if(m->value() == 0) {+ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if(!ss->quote_mark()) return new (ctx.mem) Null(pstate);+ } else {+ return new (ctx.mem) Null(pstate);+ }+ newstr = "";+ } else if(start == end && m->value() != 0) {+ newstr = str.substr(start, 1);+ } else if(end > start) {+ newstr = str.substr(start, end - start + UTF_8::code_point_size_at_offset(str, end));+ }+ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if(ss->quote_mark()) newstr = quote(newstr);+ }+ }+ catch (utf8::invalid_code_point) {+ string msg("utf8::invalid_code_point");+ error(msg, pstate, backtrace);+ }+ catch (utf8::not_enough_room) {+ string msg("utf8::not_enough_room");+ error(msg, pstate, backtrace);+ }+ catch (utf8::invalid_utf8) {+ string msg("utf8::invalid_utf8");+ error(msg, pstate, backtrace);+ }+ catch (...) { throw; }+ return new (ctx.mem) String_Quoted(pstate, newstr);+ }++ Signature to_upper_case_sig = "to-upper-case($string)";+ BUILT_IN(to_upper_case)+ {+ String_Constant* s = ARG("$string", String_Constant);+ string str = s->value();++ for (size_t i = 0, L = str.length(); i < L; ++i) {+ if (Sass::Util::isAscii(str[i])) {+ str[i] = std::toupper(str[i]);+ }+ }++ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ str = ss->quote_mark() ? quote(str) : str;+ }+ return new (ctx.mem) String_Constant(pstate, str);+ }++ Signature to_lower_case_sig = "to-lower-case($string)";+ BUILT_IN(to_lower_case)+ {+ String_Constant* s = ARG("$string", String_Constant);+ string str = s->value();++ for (size_t i = 0, L = str.length(); i < L; ++i) {+ if (Sass::Util::isAscii(str[i])) {+ str[i] = std::tolower(str[i]);+ }+ }++ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ str = ss->quote_mark() ? quote(str, '"') : str;+ }+ return new (ctx.mem) String_Constant(pstate, str);+ }++ ///////////////////+ // NUMBER FUNCTIONS+ ///////////////////++ Signature percentage_sig = "percentage($number)";+ BUILT_IN(percentage)+ {+ Number* n = ARG("$number", Number);+ if (!n->is_unitless()) error("argument $number of `" + string(sig) + "` must be unitless", pstate);+ return new (ctx.mem) Number(pstate, n->value() * 100, "%");+ }++ Signature round_sig = "round($number)";+ BUILT_IN(round)+ {+ Number* n = ARG("$number", Number);+ Number* r = new (ctx.mem) Number(*n);+ r->pstate(pstate);+ r->value(std::floor(r->value() + 0.5));+ return r;+ }++ Signature ceil_sig = "ceil($number)";+ BUILT_IN(ceil)+ {+ Number* n = ARG("$number", Number);+ Number* r = new (ctx.mem) Number(*n);+ r->pstate(pstate);+ r->value(std::ceil(r->value()));+ return r;+ }++ Signature floor_sig = "floor($number)";+ BUILT_IN(floor)+ {+ Number* n = ARG("$number", Number);+ Number* r = new (ctx.mem) Number(*n);+ r->pstate(pstate);+ r->value(std::floor(r->value()));+ return r;+ }++ Signature abs_sig = "abs($number)";+ BUILT_IN(abs)+ {+ Number* n = ARG("$number", Number);+ Number* r = new (ctx.mem) Number(*n);+ r->pstate(pstate);+ r->value(std::abs(r->value()));+ return r;+ }++ Signature min_sig = "min($numbers...)";+ BUILT_IN(min)+ {+ List* arglist = ARG("$numbers", List);+ Number* least = 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));+ if (least) {+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);+ if (lt(xi, least, ctx)) least = xi;+ } else least = xi;+ }+ return least;+ }++ Signature max_sig = "max($numbers...)";+ BUILT_IN(max)+ {+ List* arglist = ARG("$numbers", List);+ Number* greatest = 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));+ if (greatest) {+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);+ if (lt(greatest, xi, ctx)) greatest = xi;+ } else greatest = xi;+ }+ return greatest;+ }++ Signature random_sig = "random($limit:false)";+ BUILT_IN(random)+ {+ Number* l = dynamic_cast<Number*>(env["$limit"]);+ if (l && trunc(l->value()) != l->value()) error("argument $limit of `" + string(sig) + "` must be an integer", pstate);+ if (l) {+ uniform_real_distribution<> distributor(1, l->value() + 1);+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ return new (ctx.mem) Number(pstate, (double)distributed);+ }+ else {+ uniform_real_distribution<> distributor(0, 1);+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ return new (ctx.mem) Number(pstate, trunc(distributed));+ }+ }++ /////////////////+ // LIST FUNCTIONS+ /////////////////++ Signature length_sig = "length($list)";+ BUILT_IN(length)+ {+ Expression* v = ARG("$list", Expression);+ if (v->concrete_type() == Expression::MAP) {+ Map* map = dynamic_cast<Map*>(env["$list"]);+ return new (ctx.mem) Number(pstate,+ map ? map->length() : 1);+ }++ List* list = dynamic_cast<List*>(env["$list"]);+ return new (ctx.mem) Number(pstate,+ list ? list->length() : 1);+ }++ Signature nth_sig = "nth($list, $n)";+ BUILT_IN(nth)+ {+ Map* m = dynamic_cast<Map*>(env["$list"]);+ List* l = dynamic_cast<List*>(env["$list"]);+ Number* n = ARG("$n", Number);+ if (n->value() == 0) error("argument `$n` of `" + string(sig) + "` must be non-zero", pstate);+ // if the argument isn't a list, then wrap it in a singleton list+ if (!m && !l) {+ l = new (ctx.mem) List(pstate, 1);+ *l << ARG("$list", Expression);+ }+ size_t len = m ? m->length() : l->length();+ bool empty = m ? m->empty() : l->empty();+ if (empty) error("argument `$list` of `" + string(sig) + "` must not be empty", pstate);+ double index = std::floor(n->value() < 0 ? len + n->value() : n->value() - 1);+ if (index < 0 || index > len - 1) error("index out of bounds for `" + string(sig) + "`", pstate);++ if (m) {+ l = new (ctx.mem) List(pstate, 1);+ *l << m->keys()[static_cast<unsigned int>(index)];+ *l << m->at(m->keys()[static_cast<unsigned int>(index)]);+ return l;+ }+ else {+ return l->value_at_index(static_cast<int>(index));+ }+ }++ Signature set_nth_sig = "set-nth($list, $n, $value)";+ BUILT_IN(set_nth)+ {+ List* l = dynamic_cast<List*>(env["$list"]);+ Number* n = ARG("$n", Number);+ Expression* v = ARG("$value", Expression);+ if (!l) {+ l = new (ctx.mem) List(pstate, 1);+ *l << ARG("$list", Expression);+ }+ if (l->empty()) error("argument `$list` of `" + string(sig) + "` must not be empty", pstate);+ double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);+ if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + string(sig) + "`", pstate);+ List* result = new (ctx.mem) List(pstate, l->length(), l->separator());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ *result << ((i == index) ? v : (*l)[i]);+ }+ return result;+ }++ Signature index_sig = "index($list, $value)";+ BUILT_IN(index)+ {+ List* l = dynamic_cast<List*>(env["$list"]);+ Expression* v = ARG("$value", Expression);+ if (!l) {+ l = new (ctx.mem) List(pstate, 1);+ *l << ARG("$list", Expression);+ }+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ if (eq(l->value_at_index(i), v, ctx)) return new (ctx.mem) Number(pstate, i+1);+ }+ return new (ctx.mem) Null(pstate);+ }++ Signature join_sig = "join($list1, $list2, $separator: auto)";+ BUILT_IN(join)+ {+ List* l1 = dynamic_cast<List*>(env["$list1"]);+ List* l2 = dynamic_cast<List*>(env["$list2"]);+ String_Constant* sep = ARG("$separator", String_Constant);+ List::Separator sep_val = (l1 ? l1->separator() : List::SPACE);+ if (!l1) {+ l1 = new (ctx.mem) List(pstate, 1);+ *l1 << ARG("$list1", Expression);+ sep_val = (l2 ? l2->separator() : List::SPACE);+ }+ if (!l2) {+ l2 = new (ctx.mem) List(pstate, 1);+ *l2 << ARG("$list2", Expression);+ }+ size_t len = l1->length() + l2->length();+ string sep_str = unquote(sep->value());+ if (sep_str == "space") sep_val = List::SPACE;+ else if (sep_str == "comma") sep_val = List::COMMA;+ else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ List* result = new (ctx.mem) List(pstate, len, sep_val);+ *result += l1;+ *result += l2;+ return result;+ }++ Signature append_sig = "append($list, $val, $separator: auto)";+ BUILT_IN(append)+ {+ List* l = dynamic_cast<List*>(env["$list"]);+ Expression* v = ARG("$val", Expression);+ String_Constant* sep = ARG("$separator", String_Constant);+ if (!l) {+ l = new (ctx.mem) List(pstate, 1);+ *l << ARG("$list", Expression);+ }+ List* result = new (ctx.mem) List(pstate, l->length() + 1, l->separator());+ string sep_str(unquote(sep->value()));+ if (sep_str == "space") result->separator(List::SPACE);+ else if (sep_str == "comma") result->separator(List::COMMA);+ else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ *result += l;+ *result << v;+ return result;+ }++ Signature zip_sig = "zip($lists...)";+ BUILT_IN(zip)+ {+ List* arglist = new (ctx.mem) List(*ARG("$lists", List));+ size_t shortest = 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ List* ith = dynamic_cast<List*>(arglist->value_at_index(i));+ if (!ith) {+ ith = new (ctx.mem) List(pstate, 1);+ *ith << arglist->value_at_index(i);+ if (arglist->is_arglist()) {+ ((Argument*)(*arglist)[i])->value(ith);+ } else {+ (*arglist)[i] = ith;+ }+ }+ shortest = (i ? std::min(shortest, ith->length()) : ith->length());+ }+ List* zippers = new (ctx.mem) List(pstate, shortest, List::COMMA);+ size_t L = arglist->length();+ for (size_t i = 0; i < shortest; ++i) {+ List* zipper = new (ctx.mem) List(pstate, L);+ for (size_t j = 0; j < L; ++j) {+ *zipper << (*static_cast<List*>(arglist->value_at_index(j)))[i];+ }+ *zippers << zipper;+ }+ return zippers;+ }++ Signature compact_sig = "compact($values...)";+ BUILT_IN(compact)+ {+ error("`compact` has been removed from libsass because it's not part of the Sass spec", pstate);+ return 0; // suppress warning, error will exit anyway+ }++ Signature list_separator_sig = "list_separator($list)";+ BUILT_IN(list_separator)+ {+ List* l = dynamic_cast<List*>(env["$list"]);+ if (!l) {+ l = new (ctx.mem) List(pstate, 1);+ *l << ARG("$list", Expression);+ }+ return new (ctx.mem) String_Constant(pstate,+ l->separator() == List::COMMA ? "comma" : "space");+ }++ /////////////////+ // MAP FUNCTIONS+ /////////////////++ Signature map_get_sig = "map-get($map, $key)";+ BUILT_IN(map_get)+ {+ Map* m = ARGM("$map", Map, ctx);+ Expression* v = ARG("$key", Expression);+ try {+ return m->at(v);+ } catch (const std::out_of_range&) {+ return new (ctx.mem) Null(pstate);+ }+ catch (...) { throw; }+ }++ Signature map_has_key_sig = "map-has-key($map, $key)";+ BUILT_IN(map_has_key)+ {+ Map* m = ARGM("$map", Map, ctx);+ Expression* v = ARG("$key", Expression);+ return new (ctx.mem) Boolean(pstate, m->has(v));+ }++ Signature map_keys_sig = "map-keys($map)";+ BUILT_IN(map_keys)+ {+ Map* m = ARGM("$map", Map, ctx);+ List* result = new (ctx.mem) List(pstate, m->length(), List::COMMA);+ for ( auto key : m->keys()) {+ *result << key;+ }+ return result;+ }++ Signature map_values_sig = "map-values($map)";+ BUILT_IN(map_values)+ {+ Map* m = ARGM("$map", Map, ctx);+ List* result = new (ctx.mem) List(pstate, m->length(), List::COMMA);+ for ( auto key : m->keys()) {+ *result << m->at(key);+ }+ return result;+ }++ Signature map_merge_sig = "map-merge($map1, $map2)";+ BUILT_IN(map_merge)+ {+ Map* m1 = ARGM("$map1", Map, ctx);+ Map* m2 = ARGM("$map2", Map, ctx);++ size_t len = m1->length() + m2->length();+ Map* result = new (ctx.mem) Map(pstate, len);+ *result += m1;+ *result += m2;+ return result;+ }++ Signature map_remove_sig = "map-remove($map, $keys...)";+ BUILT_IN(map_remove)+ {+ bool remove;+ Map* m = ARGM("$map", Map, ctx);+ List* arglist = ARG("$keys", List);+ Map* result = new (ctx.mem) Map(pstate, 1);+ for (auto key : m->keys()) {+ remove = false;+ for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {+ remove = eq(key, arglist->value_at_index(j), ctx);+ }+ if (!remove) *result << make_pair(key, m->at(key));+ }+ return result;+ }++ Signature keywords_sig = "keywords($args)";+ BUILT_IN(keywords)+ {+ List* arglist = new (ctx.mem) List(*ARG("$args", List));+ Map* result = new (ctx.mem) Map(pstate, 1);+ // The parser ensures the ordering of arguments so we can assert this+ // isn't keyword argument list the first argument isn't a keyword argument+ if (!(arglist->empty() || ((Argument*)(*arglist)[0])->is_keyword_argument())) return result;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ string name = string(((Argument*)(*arglist)[i])->name());+ string sanitized_name = string(name, 1);+ *result << make_pair(new (ctx.mem) String_Constant(pstate, sanitized_name),+ ((Argument*)(*arglist)[i])->value());+ }+ return result;+ }++ //////////////////////////+ // INTROSPECTION FUNCTIONS+ //////////////////////////++ Signature type_of_sig = "type-of($value)";+ BUILT_IN(type_of)+ {+ Expression* v = ARG("$value", Expression);+ if (v->concrete_type() == Expression::STRING) {+ To_String to_string(&ctx);+ string str(v->perform(&to_string));+ if (ctx.names_to_colors.count(str)) {+ return new (ctx.mem) String_Constant(pstate, "color");+ }+ }+ return new (ctx.mem) String_Constant(pstate, ARG("$value", Expression)->type());+ }++ Signature unit_sig = "unit($number)";+ BUILT_IN(unit)+ { return new (ctx.mem) String_Quoted(pstate, quote(ARG("$number", Number)->unit(), '"')); }++ Signature unitless_sig = "unitless($number)";+ BUILT_IN(unitless)+ { return new (ctx.mem) Boolean(pstate, ARG("$number", Number)->is_unitless()); }++ Signature comparable_sig = "comparable($number-1, $number-2)";+ BUILT_IN(comparable)+ {+ Number* n1 = ARG("$number-1", Number);+ Number* n2 = ARG("$number-2", Number);+ if (n1->is_unitless() || n2->is_unitless()) {+ return new (ctx.mem) Boolean(pstate, true);+ }+ Number tmp_n2(*n2);+ tmp_n2.normalize(n1->find_convertible_unit());+ return new (ctx.mem) Boolean(pstate, n1->unit() == tmp_n2.unit());+ }++ Signature variable_exists_sig = "variable-exists($name)";+ BUILT_IN(variable_exists)+ {+ string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has("$"+s)) {+ return new (ctx.mem) Boolean(pstate, true);+ }+ else {+ return new (ctx.mem) Boolean(pstate, false);+ }+ }++ Signature global_variable_exists_sig = "global-variable-exists($name)";+ BUILT_IN(global_variable_exists)+ {+ string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global("$"+s)) {+ return new (ctx.mem) Boolean(pstate, true);+ }+ else {+ return new (ctx.mem) Boolean(pstate, false);+ }+ }++ Signature function_exists_sig = "function-exists($name)";+ BUILT_IN(function_exists)+ {+ string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global(s+"[f]")) {+ return new (ctx.mem) Boolean(pstate, true);+ }+ else {+ return new (ctx.mem) Boolean(pstate, false);+ }+ }++ Signature mixin_exists_sig = "mixin-exists($name)";+ BUILT_IN(mixin_exists)+ {+ string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global(s+"[m]")) {+ return new (ctx.mem) Boolean(pstate, true);+ }+ else {+ return new (ctx.mem) Boolean(pstate, false);+ }+ }++ Signature feature_exists_sig = "feature-exists($name)";+ BUILT_IN(feature_exists)+ {+ string s = unquote(ARG("$name", String_Constant)->value());++ if(features.find(s) == features.end()) {+ return new (ctx.mem) Boolean(pstate, false);+ }+ else {+ return new (ctx.mem) Boolean(pstate, true);+ }+ }++ Signature call_sig = "call($name, $args...)";+ BUILT_IN(call)+ {+ string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));+ List* arglist = new (ctx.mem) List(*ARG("$args", List));++ Arguments* args = new (ctx.mem) Arguments(pstate);+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression* expr = arglist->value_at_index(i);+ if (arglist->is_arglist()) {+ Argument* arg = static_cast<Argument*>(expr);+ *args << new (ctx.mem) Argument(pstate,+ expr,+ "",+ arg->is_rest_argument(),+ arg->is_keyword_argument());+ } else {+ *args << new (ctx.mem) Argument(pstate, expr);+ }+ }+ Function_Call* func = new (ctx.mem) Function_Call(pstate, name, args);+ Contextualize contextualize(ctx, &d_env, backtrace);+ Listize listize(ctx);+ Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);+ return func->perform(&eval);++ }++ ////////////////////+ // BOOLEAN FUNCTIONS+ ////////////////////++ Signature not_sig = "not($value)";+ BUILT_IN(sass_not)+ { return new (ctx.mem) Boolean(pstate, ARG("$value", Expression)->is_false()); }++ Signature if_sig = "if($condition, $if-true, $if-false)";+ // BUILT_IN(sass_if)+ // { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }+ BUILT_IN(sass_if)+ {+ Contextualize contextualize(ctx, &d_env, backtrace);+ Listize listize(ctx);+ Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);+ bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false();+ if (is_true) {+ return ARG("$if-true", Expression)->perform(&eval);+ }+ else {+ return ARG("$if-false", Expression)->perform(&eval);+ }+ }++ ////////////////+ // URL FUNCTIONS+ ////////////////++ Signature image_url_sig = "image-url($path, $only-path: false, $cache-buster: false)";+ BUILT_IN(image_url)+ {+ error("`image_url` has been removed from libsass because it's not part of the Sass spec", pstate);+ return 0; // suppress warning, error will exit anyway+ }++ //////////////////////////+ // MISCELLANEOUS FUNCTIONS+ //////////////////////////++ Signature inspect_sig = "inspect($value)";+ BUILT_IN(inspect)+ {+ Expression* v = ARG("$value", Expression);+ if (v->concrete_type() == Expression::NULL_VAL) {+ return new (ctx.mem) String_Constant(pstate, "null");+ } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) {+ return new (ctx.mem) String_Constant(pstate, "false");+ } else if (v->concrete_type() == Expression::STRING) {+ return v;+ } else {+ Output_Style old_style;+ old_style = ctx.output_style;+ ctx.output_style = NESTED;+ To_String to_string(&ctx);+ string inspect = v->perform(&to_string);+ ctx.output_style = old_style;+ return new (ctx.mem) String_Constant(pstate, inspect);+++ }+ // return v;+ }++ Signature unique_id_sig = "unique-id()";+ BUILT_IN(unique_id)+ {+ std::stringstream ss;+ uniform_real_distribution<> distributor(0, 4294967296); // 16^8+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ ss << "u" << setfill('0') << setw(8) << std::hex << distributed;+ return new (ctx.mem) String_Constant(pstate, ss.str());+ }++ }+}
+ libsass/functions.hpp view
@@ -0,0 +1,184 @@+#ifndef SASS_FUNCTIONS_H+#define SASS_FUNCTIONS_H++#include <string>++#include "position.hpp"+#include "environment.hpp"+#include "sass_functions.h"++#define BUILT_IN(name) Expression*\+name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace)++namespace Sass {+ class Context;+ struct Backtrace;+ class AST_Node;+ class Expression;+ class Definition;+ typedef Environment<AST_Node*> Env;+ typedef const char* Signature;+ typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);++ Definition* make_native_function(Signature, Native_Function, Context& ctx);+ Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);++ namespace Functions {++ extern Signature rgb_sig;+ extern Signature rgba_4_sig;+ extern Signature rgba_2_sig;+ extern Signature red_sig;+ extern Signature green_sig;+ extern Signature blue_sig;+ extern Signature mix_sig;+ extern Signature hsl_sig;+ extern Signature hsla_sig;+ extern Signature hue_sig;+ extern Signature saturation_sig;+ extern Signature lightness_sig;+ extern Signature adjust_hue_sig;+ extern Signature lighten_sig;+ extern Signature darken_sig;+ extern Signature saturate_sig;+ extern Signature desaturate_sig;+ extern Signature grayscale_sig;+ extern Signature complement_sig;+ extern Signature invert_sig;+ extern Signature alpha_sig;+ extern Signature opacity_sig;+ extern Signature opacify_sig;+ extern Signature fade_in_sig;+ extern Signature transparentize_sig;+ extern Signature fade_out_sig;+ extern Signature adjust_color_sig;+ extern Signature scale_color_sig;+ extern Signature change_color_sig;+ extern Signature ie_hex_str_sig;+ extern Signature unquote_sig;+ extern Signature quote_sig;+ extern Signature str_length_sig;+ extern Signature str_insert_sig;+ extern Signature str_index_sig;+ extern Signature str_slice_sig;+ extern Signature to_upper_case_sig;+ extern Signature to_lower_case_sig;+ extern Signature percentage_sig;+ extern Signature round_sig;+ extern Signature ceil_sig;+ extern Signature floor_sig;+ extern Signature abs_sig;+ extern Signature min_sig;+ extern Signature max_sig;+ extern Signature inspect_sig;+ extern Signature random_sig;+ extern Signature length_sig;+ extern Signature nth_sig;+ extern Signature index_sig;+ extern Signature join_sig;+ extern Signature append_sig;+ extern Signature zip_sig;+ extern Signature compact_sig;+ extern Signature list_separator_sig;+ extern Signature type_of_sig;+ extern Signature unit_sig;+ extern Signature unitless_sig;+ extern Signature comparable_sig;+ extern Signature variable_exists_sig;+ extern Signature global_variable_exists_sig;+ extern Signature function_exists_sig;+ extern Signature mixin_exists_sig;+ extern Signature feature_exists_sig;+ extern Signature call_sig;+ extern Signature not_sig;+ extern Signature if_sig;+ extern Signature image_url_sig;+ extern Signature map_get_sig;+ extern Signature map_merge_sig;+ extern Signature map_remove_sig;+ extern Signature map_keys_sig;+ extern Signature map_values_sig;+ extern Signature map_has_key_sig;+ extern Signature keywords_sig;+ extern Signature set_nth_sig;+ extern Signature unique_id_sig;++ BUILT_IN(rgb);+ BUILT_IN(rgba_4);+ BUILT_IN(rgba_2);+ BUILT_IN(red);+ BUILT_IN(green);+ BUILT_IN(blue);+ BUILT_IN(mix);+ BUILT_IN(hsl);+ BUILT_IN(hsla);+ BUILT_IN(hue);+ BUILT_IN(saturation);+ BUILT_IN(lightness);+ BUILT_IN(adjust_hue);+ BUILT_IN(lighten);+ BUILT_IN(darken);+ BUILT_IN(saturate);+ BUILT_IN(desaturate);+ BUILT_IN(grayscale);+ BUILT_IN(complement);+ BUILT_IN(invert);+ BUILT_IN(alpha);+ BUILT_IN(opacify);+ BUILT_IN(transparentize);+ BUILT_IN(adjust_color);+ BUILT_IN(scale_color);+ BUILT_IN(change_color);+ BUILT_IN(ie_hex_str);+ BUILT_IN(sass_unquote);+ BUILT_IN(sass_quote);+ BUILT_IN(str_length);+ BUILT_IN(str_insert);+ BUILT_IN(str_index);+ BUILT_IN(str_slice);+ BUILT_IN(to_upper_case);+ BUILT_IN(to_lower_case);+ BUILT_IN(percentage);+ BUILT_IN(round);+ BUILT_IN(ceil);+ BUILT_IN(floor);+ BUILT_IN(abs);+ BUILT_IN(min);+ BUILT_IN(max);+ BUILT_IN(inspect);+ BUILT_IN(random);+ BUILT_IN(length);+ BUILT_IN(nth);+ BUILT_IN(index);+ BUILT_IN(join);+ BUILT_IN(append);+ BUILT_IN(zip);+ BUILT_IN(compact);+ BUILT_IN(list_separator);+ BUILT_IN(type_of);+ BUILT_IN(unit);+ BUILT_IN(unitless);+ BUILT_IN(comparable);+ BUILT_IN(variable_exists);+ BUILT_IN(global_variable_exists);+ BUILT_IN(function_exists);+ BUILT_IN(mixin_exists);+ BUILT_IN(feature_exists);+ BUILT_IN(call);+ BUILT_IN(sass_not);+ BUILT_IN(sass_if);+ BUILT_IN(image_url);+ BUILT_IN(map_get);+ BUILT_IN(map_merge);+ BUILT_IN(map_remove);+ BUILT_IN(map_keys);+ BUILT_IN(map_values);+ BUILT_IN(map_has_key);+ BUILT_IN(keywords);+ BUILT_IN(set_nth);+ BUILT_IN(unique_id);++ }+}++#endif
+ libsass/inspect.cpp view
@@ -0,0 +1,866 @@+#include "inspect.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "utf8/checked.h"+#include <cmath>+#include <string>+#include <iostream>+#include <iomanip>+#include <stdint.h>+#include <stdint.h>++namespace Sass {+ using namespace std;++ Inspect::Inspect(Emitter emi)+ : Emitter(emi)+ { }+ Inspect::~Inspect() { }++ // statements+ void Inspect::operator()(Block* block)+ {+ if (!block->is_root()) {+ add_open_mapping(block);+ append_scope_opener();+ }+ if (output_style() == NESTED) indentation += block->tabs();+ for (size_t i = 0, L = block->length(); i < L; ++i) {+ (*block)[i]->perform(this);+ }+ if (output_style() == NESTED) indentation -= block->tabs();+ if (!block->is_root()) {+ append_scope_closer();+ add_close_mapping(block);+ }++ }++ void Inspect::operator()(Ruleset* ruleset)+ {+ ruleset->selector()->perform(this);+ ruleset->block()->perform(this);+ }++ void Inspect::operator()(Keyframe_Rule* rule)+ {+ if (rule->selector()) rule->selector()->perform(this);+ if (rule->block()) rule->block()->perform(this);+ }++ void Inspect::operator()(Propset* propset)+ {+ propset->property_fragment()->perform(this);+ append_colon_separator();+ propset->block()->perform(this);+ }++ void Inspect::operator()(Bubble* bubble)+ {+ append_indentation();+ append_token("::BUBBLE", bubble);+ append_scope_opener();+ bubble->node()->perform(this);+ append_scope_closer();+ }++ void Inspect::operator()(Media_Block* media_block)+ {+ append_indentation();+ append_token("@media", media_block);+ append_mandatory_space();+ in_media_block = true;+ media_block->media_queries()->perform(this);+ in_media_block = false;+ media_block->block()->perform(this);+ }++ void Inspect::operator()(Feature_Block* feature_block)+ {+ append_indentation();+ append_token("@supports", feature_block);+ append_mandatory_space();+ feature_block->feature_queries()->perform(this);+ feature_block->block()->perform(this);+ }++ void Inspect::operator()(At_Root_Block* at_root_block)+ {+ append_indentation();+ append_token("@at-root ", at_root_block);+ append_mandatory_space();+ if(at_root_block->expression()) at_root_block->expression()->perform(this);+ at_root_block->block()->perform(this);+ }++ void Inspect::operator()(At_Rule* at_rule)+ {+ append_indentation();+ append_token(at_rule->keyword(), at_rule);+ if (at_rule->selector()) {+ append_mandatory_space();+ in_wrapped = true;+ at_rule->selector()->perform(this);+ in_wrapped = false;+ }+ if (at_rule->block()) {+ at_rule->block()->perform(this);+ }+ else {+ append_delimiter();+ }+ }++ void Inspect::operator()(Declaration* dec)+ {+ if (dec->value()->concrete_type() == Expression::NULL_VAL) return;+ in_declaration = true;+ if (output_style() == NESTED)+ indentation += dec->tabs();+ append_indentation();+ dec->property()->perform(this);+ append_colon_separator();+ dec->value()->perform(this);+ if (dec->is_important()) {+ append_optional_space();+ append_string("!important");+ }+ append_delimiter();+ if (output_style() == NESTED)+ indentation -= dec->tabs();+ in_declaration = false;+ }++ void Inspect::operator()(Assignment* assn)+ {+ append_token(assn->variable(), assn);+ append_colon_separator();+ assn->value()->perform(this);+ if (assn->is_default()) {+ append_optional_space();+ append_string("!default");+ }+ append_delimiter();+ }++ void Inspect::operator()(Import* import)+ {+ if (!import->urls().empty()) {+ append_token("@import", import);+ append_mandatory_space();++ if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls().front())) {+ strq->is_delayed(false);+ }++ import->urls().front()->perform(this);+ append_delimiter();+ for (size_t i = 1, S = import->urls().size(); i < S; ++i) {+ append_mandatory_linefeed();+ append_token("@import", import);+ append_mandatory_space();++ if (String_Quoted* strq = dynamic_cast<String_Quoted*>(import->urls()[i])) {+ strq->is_delayed(false);+ }++ import->urls()[i]->perform(this);+ append_delimiter();+ }+ }+ }++ void Inspect::operator()(Import_Stub* import)+ {+ append_indentation();+ append_token("@import", import);+ append_mandatory_space();+ append_string(import->file_name());+ append_delimiter();+ }++ void Inspect::operator()(Warning* warning)+ {+ append_indentation();+ append_token("@warn", warning);+ append_mandatory_space();+ warning->message()->perform(this);+ append_delimiter();+ }++ void Inspect::operator()(Error* error)+ {+ append_indentation();+ append_token("@error", error);+ append_mandatory_space();+ error->message()->perform(this);+ append_delimiter();+ }++ void Inspect::operator()(Debug* debug)+ {+ append_indentation();+ append_token("@debug", debug);+ append_mandatory_space();+ debug->value()->perform(this);+ append_delimiter();+ }++ void Inspect::operator()(Comment* comment)+ {+ in_comment = true;+ comment->text()->perform(this);+ in_comment = false;+ }++ void Inspect::operator()(If* cond)+ {+ append_indentation();+ append_token("@if", cond);+ append_mandatory_space();+ cond->predicate()->perform(this);+ cond->consequent()->perform(this);+ if (cond->alternative()) {+ append_optional_linefeed();+ append_indentation();+ append_string("else");+ cond->alternative()->perform(this);+ }+ }++ void Inspect::operator()(For* loop)+ {+ append_indentation();+ append_token("@for", loop);+ append_mandatory_space();+ append_string(loop->variable());+ append_string(" from ");+ loop->lower_bound()->perform(this);+ append_string(loop->is_inclusive() ? " through " : " to ");+ loop->upper_bound()->perform(this);+ loop->block()->perform(this);+ }++ void Inspect::operator()(Each* loop)+ {+ append_indentation();+ append_token("@each", loop);+ append_mandatory_space();+ append_string(loop->variables()[0]);+ for (size_t i = 1, L = loop->variables().size(); i < L; ++i) {+ append_comma_separator();+ append_string(loop->variables()[i]);+ }+ append_string(" in ");+ loop->list()->perform(this);+ loop->block()->perform(this);+ }++ void Inspect::operator()(While* loop)+ {+ append_indentation();+ append_token("@while", loop);+ append_mandatory_space();+ loop->predicate()->perform(this);+ loop->block()->perform(this);+ }++ void Inspect::operator()(Return* ret)+ {+ append_indentation();+ append_token("@return", ret);+ append_mandatory_space();+ ret->value()->perform(this);+ append_delimiter();+ }++ void Inspect::operator()(Extension* extend)+ {+ append_indentation();+ append_token("@extend", extend);+ append_mandatory_space();+ extend->selector()->perform(this);+ append_delimiter();+ }++ void Inspect::operator()(Definition* def)+ {+ append_indentation();+ if (def->type() == Definition::MIXIN) {+ append_token("@mixin", def);+ append_mandatory_space();+ } else {+ append_token("@function", def);+ append_mandatory_space();+ }+ append_string(def->name());+ def->parameters()->perform(this);+ def->block()->perform(this);+ }++ void Inspect::operator()(Mixin_Call* call)+ {+ append_indentation();+ append_token("@include", call);+ append_mandatory_space();+ append_string(call->name());+ if (call->arguments()) {+ call->arguments()->perform(this);+ }+ if (call->block()) {+ append_optional_space();+ call->block()->perform(this);+ }+ if (!call->block()) append_delimiter();+ }++ void Inspect::operator()(Content* content)+ {+ append_indentation();+ append_token("@content", content);+ append_delimiter();+ }++ void Inspect::operator()(Map* map)+ {+ if (map->empty()) return;+ if (map->is_invisible()) return;+ bool items_output = false;+ append_string("(");+ for (auto key : map->keys()) {+ if (key->is_invisible()) continue;+ if (map->at(key)->is_invisible()) continue;+ if (items_output) append_comma_separator();+ key->perform(this);+ append_colon_separator();+ map->at(key)->perform(this);+ items_output = true;+ }+ append_string(")");+ }++ void Inspect::operator()(List* list)+ {+ string sep(list->separator() == List::SPACE ? " " : ",");+ if (output_style() != COMPRESSED && sep == ",") sep += " ";+ else if (in_media_block && sep != " ") sep += " "; // verified+ if (list->empty()) return;+ bool items_output = false;+ in_declaration_list = in_declaration;+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ Expression* list_item = (*list)[i];+ if (list_item->is_invisible()) {+ continue;+ }+ if (items_output) {+ append_string(sep);+ }+ if (items_output && sep != " ")+ append_optional_space();+ list_item->perform(this);+ items_output = true;+ }+ in_declaration_list = false;+ }++ void Inspect::operator()(Binary_Expression* expr)+ {+ expr->left()->perform(this);+ switch (expr->type()) {+ case Binary_Expression::AND: append_string(" and "); break;+ case Binary_Expression::OR: append_string(" or "); break;+ case Binary_Expression::EQ: append_string(" == "); break;+ case Binary_Expression::NEQ: append_string(" != "); break;+ case Binary_Expression::GT: append_string(" > "); break;+ case Binary_Expression::GTE: append_string(" >= "); break;+ case Binary_Expression::LT: append_string(" < "); break;+ case Binary_Expression::LTE: append_string(" <= "); break;+ case Binary_Expression::ADD: append_string(" + "); break;+ case Binary_Expression::SUB: append_string(" - "); break;+ case Binary_Expression::MUL: append_string(" * "); break;+ case Binary_Expression::DIV: append_string("/"); break;+ case Binary_Expression::MOD: append_string(" % "); break;+ default: break; // shouldn't get here+ }+ expr->right()->perform(this);+ }++ void Inspect::operator()(Unary_Expression* expr)+ {+ if (expr->type() == Unary_Expression::PLUS) append_string("+");+ else append_string("-");+ expr->operand()->perform(this);+ }++ void Inspect::operator()(Function_Call* call)+ {+ append_token(call->name(), call);+ call->arguments()->perform(this);+ }++ void Inspect::operator()(Function_Call_Schema* call)+ {+ call->name()->perform(this);+ call->arguments()->perform(this);+ }++ void Inspect::operator()(Variable* var)+ {+ append_token(var->name(), var);+ }++ void Inspect::operator()(Textual* txt)+ {+ append_token(txt->value(), txt);+ }++ void Inspect::operator()(Number* n)+ {+ stringstream ss;+ ss.precision(ctx ? ctx->precision : 5);+ ss << fixed << n->value();+ string d(ss.str());+ // store if the value did not equal zero+ // if after applying precsision, the value gets+ // truncated to zero, sass emits 0.0 instead of 0+ bool nonzero = n->value() != 0;+ for (size_t i = d.length()-1; d[i] == '0'; --i) {+ d.resize(d.length()-1);+ }+ if (d[d.length()-1] == '.') d.resize(d.length()-1);+ if (n->numerator_units().size() > 1 ||+ n->denominator_units().size() > 0 ||+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != string::npos) ||+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != string::npos)+ ) {+ error(d + n->unit() + " isn't a valid CSS value.", n->pstate());+ }+ if (!n->zero() && !in_declaration_list) {+ if (d.substr(0, 3) == "-0.") d.erase(1, 1);+ if (d.substr(0, 2) == "0.") d.erase(0, 1);+ }+ // remove the leading minus+ if (d == "-0") d.erase(0, 1);+ // use fractional output if we had+ // a value before it got truncated+ if (d == "0" && nonzero) d = "0.0";+ // append number and unit+ append_token(d + n->unit(), n);+ }++ // helper function for serializing colors+ template <size_t range>+ static double cap_channel(double c) {+ if (c > range) return range;+ else if (c < 0) return 0;+ else return c;+ }++ void Inspect::operator()(Color* c)+ {+ stringstream ss;++ // check if we prefer short hex colors+ bool want_short = output_style() == COMPRESSED;++ // original color name+ // maybe an unknown token+ string name = c->disp();++ // resolved color+ string res_name = name;++ double r = round(cap_channel<0xff>(c->r()));+ double g = round(cap_channel<0xff>(c->g()));+ double b = round(cap_channel<0xff>(c->b()));+ double a = cap_channel<1> (c->a());++ // get color from given name (if one was given at all)+ if (name != "" && ctx && ctx->names_to_colors.count(name)) {+ Color* n = ctx->names_to_colors[name];+ r = round(cap_channel<0xff>(n->r()));+ g = round(cap_channel<0xff>(n->g()));+ b = round(cap_channel<0xff>(n->b()));+ a = cap_channel<1> (n->a());+ }+ // otherwise get the possible resolved color name+ else {+ int numval = static_cast<int>(r) * 0x10000 + static_cast<int>(g) * 0x100 + static_cast<int>(b);+ if (ctx && ctx->colors_to_names.count(numval))+ res_name = ctx->colors_to_names[numval];+ }++ stringstream hexlet;+ hexlet << '#' << setw(1) << setfill('0');+ // create a short color hexlet if there is any need for it+ if (want_short && is_color_doublet(r, g, b) && a == 1) {+ hexlet << hex << setw(1) << (static_cast<unsigned long>(r) >> 4);+ hexlet << hex << setw(1) << (static_cast<unsigned long>(g) >> 4);+ hexlet << hex << setw(1) << (static_cast<unsigned long>(b) >> 4);+ } else {+ hexlet << hex << setw(2) << static_cast<unsigned long>(r);+ hexlet << hex << setw(2) << static_cast<unsigned long>(g);+ hexlet << hex << setw(2) << static_cast<unsigned long>(b);+ }++ // 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 (want_short && hexlet.str().size() < res_name.size()) {+ ss << hexlet.str();+ } else {+ ss << res_name;+ }+ }+ else {+ ss << hexlet.str();+ }+ }+ else {+ ss << "rgba(";+ ss << static_cast<unsigned long>(r) << ",";+ if (output_style() != COMPRESSED) ss << " ";+ ss << static_cast<unsigned long>(g) << ",";+ if (output_style() != COMPRESSED) ss << " ";+ ss << static_cast<unsigned long>(b) << ",";+ if (output_style() != COMPRESSED) ss << " ";+ ss << a << ')';+ }+ append_token(ss.str(), c);+ }++ void Inspect::operator()(Boolean* b)+ {+ append_token(b->value() ? "true" : "false", b);+ }++ void Inspect::operator()(String_Schema* ss)+ {+ // Evaluation should turn these into String_Constants, so this method is+ // only for inspection purposes.+ for (size_t i = 0, L = ss->length(); i < L; ++i) {+ if ((*ss)[i]->is_interpolant()) append_string("#{");+ (*ss)[i]->perform(this);+ if ((*ss)[i]->is_interpolant()) append_string("}");+ }+ }++ void Inspect::operator()(String_Constant* s)+ {+ if (String_Quoted* quoted = dynamic_cast<String_Quoted*>(s)) {+ return Inspect::operator()(quoted);+ }+ append_token(s->value(), s);+ }++ void Inspect::operator()(String_Quoted* s)+ {+ if (s->quote_mark()) {+ append_token(quote(s->value(), s->quote_mark()), s);+ } else {+ append_token(s->value(), s);+ }+ }++ void Inspect::operator()(Feature_Query* fq)+ {+ size_t i = 0;+ (*fq)[i++]->perform(this);+ for (size_t L = fq->length(); i < L; ++i) {+ (*fq)[i]->perform(this);+ }+ }++ void Inspect::operator()(Feature_Query_Condition* fqc)+ {+ if (fqc->operand() == Feature_Query_Condition::AND) {+ append_mandatory_space();+ append_token("and", fqc);+ append_mandatory_space();+ } else if (fqc->operand() == Feature_Query_Condition::OR) {+ append_mandatory_space();+ append_token("or", fqc);+ append_mandatory_space();+ } else if (fqc->operand() == Feature_Query_Condition::NOT) {+ append_mandatory_space();+ append_token("not", fqc);+ append_mandatory_space();+ }++ if (!fqc->is_root()) append_string("(");++ if (!fqc->length()) {+ fqc->feature()->perform(this);+ append_string(": "); // verified+ fqc->value()->perform(this);+ }+ for (size_t i = 0, L = fqc->length(); i < L; ++i)+ (*fqc)[i]->perform(this);++ if (!fqc->is_root()) append_string(")");+ }++ void Inspect::operator()(Media_Query* mq)+ {+ size_t i = 0;+ if (mq->media_type()) {+ if (mq->is_negated()) append_string("not ");+ else if (mq->is_restricted()) append_string("only ");+ mq->media_type()->perform(this);+ }+ else {+ (*mq)[i++]->perform(this);+ }+ for (size_t L = mq->length(); i < L; ++i) {+ append_string(" and ");+ (*mq)[i]->perform(this);+ }+ }++ void Inspect::operator()(Media_Query_Expression* mqe)+ {+ if (mqe->is_interpolated()) {+ mqe->feature()->perform(this);+ }+ else {+ append_string("(");+ mqe->feature()->perform(this);+ if (mqe->value()) {+ append_string(": "); // verified+ mqe->value()->perform(this);+ }+ append_string(")");+ }+ }++ void Inspect::operator()(At_Root_Expression* ae)+ {+ if (ae->is_interpolated()) {+ ae->feature()->perform(this);+ }+ else {+ append_string("(");+ ae->feature()->perform(this);+ if (ae->value()) {+ append_colon_separator();+ ae->value()->perform(this);+ }+ append_string(")");+ }+ }++ void Inspect::operator()(Null* n)+ {+ append_token("null", n);+ }++ void Inspect::operator()(Parent_Selector* p)+ {+ if (p->selector()) {+ p->selector()->perform(this);+ append_delimiter();+ }+ else {+ append_string("&");+ }+ }++ // parameters and arguments+ void Inspect::operator()(Parameter* p)+ {+ append_token(p->name(), p);+ if (p->default_value()) {+ append_colon_separator();+ p->default_value()->perform(this);+ }+ else if (p->is_rest_parameter()) {+ append_string("...");+ }+ }++ void Inspect::operator()(Parameters* p)+ {+ append_string("(");+ if (!p->empty()) {+ (*p)[0]->perform(this);+ for (size_t i = 1, L = p->length(); i < L; ++i) {+ append_comma_separator();+ (*p)[i]->perform(this);+ }+ }+ append_string(")");+ }++ void Inspect::operator()(Argument* a)+ {+ if (!a->name().empty()) {+ append_token(a->name(), a);+ append_colon_separator();+ }+ // Special case: argument nulls can be ignored+ if (a->value()->concrete_type() == Expression::NULL_VAL) {+ return;+ }+ if (a->value()->concrete_type() == Expression::STRING) {+ String_Constant* s = static_cast<String_Constant*>(a->value());+ s->perform(this);+ } else a->value()->perform(this);+ if (a->is_rest_argument()) {+ append_string("...");+ }+ }++ void Inspect::operator()(Arguments* a)+ {+ append_string("(");+ if (!a->empty()) {+ (*a)[0]->perform(this);+ for (size_t i = 1, L = a->length(); i < L; ++i) {+ append_string(", "); // verified+ // Sass Bug? append_comma_separator();+ (*a)[i]->perform(this);+ }+ }+ append_string(")");+ }++ void Inspect::operator()(Selector_Schema* s)+ {+ s->contents()->perform(this);+ }++ void Inspect::operator()(Selector_Reference* ref)+ {+ if (ref->selector()) ref->selector()->perform(this);+ else append_string("&");+ }++ void Inspect::operator()(Selector_Placeholder* s)+ {+ append_token(s->name(), s);+ if (s->has_line_break()) append_optional_linefeed();+ if (s->has_line_break()) append_indentation();++ }++ void Inspect::operator()(Type_Selector* s)+ {+ append_token(s->name(), s);+ }++ void Inspect::operator()(Selector_Qualifier* s)+ {+ append_token(s->name(), s);+ if (s->has_line_break()) append_optional_linefeed();+ if (s->has_line_break()) append_indentation();+ }++ void Inspect::operator()(Attribute_Selector* s)+ {+ append_string("[");+ add_open_mapping(s);+ append_token(s->name(), s);+ if (!s->matcher().empty()) {+ append_string(s->matcher());+ if (s->value()) {+ s->value()->perform(this);+ }+ }+ add_close_mapping(s);+ append_string("]");+ }++ void Inspect::operator()(Pseudo_Selector* s)+ {+ append_token(s->name(), s);+ if (s->expression()) {+ s->expression()->perform(this);+ append_string(")");+ }+ }++ void Inspect::operator()(Wrapped_Selector* s)+ {+ bool was = in_wrapped;+ in_wrapped = true;+ append_token(s->name(), s);+ s->selector()->perform(this);+ append_string(")");+ in_wrapped = was;+ }++ void Inspect::operator()(Compound_Selector* s)+ {+ for (size_t i = 0, L = s->length(); i < L; ++i) {+ (*s)[i]->perform(this);+ }+ if (s->has_line_break()) {+ append_optional_linefeed();+ }+ }++ void Inspect::operator()(Complex_Selector* c)+ {+ Compound_Selector* head = c->head();+ Complex_Selector* tail = c->tail();+ Complex_Selector::Combinator comb = c->combinator();+ if (head && !head->is_empty_reference()) head->perform(this);+ bool is_empty = head && head->is_empty_reference();+ bool is_tail = head && !head->is_empty_reference() && tail;+ if (output_style() == COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;++ switch (comb) {+ case Complex_Selector::ANCESTOR_OF:+ if (is_tail) append_mandatory_space();+ break;+ case Complex_Selector::PARENT_OF:+ append_optional_space();+ append_string(">");+ append_optional_space();+ break;+ case Complex_Selector::ADJACENT_TO:+ append_optional_space();+ append_string("+");+ append_optional_space();+ break;+ case Complex_Selector::PRECEDES:+ if (is_empty) append_optional_space();+ else append_mandatory_space();+ append_string("~");+ if (tail) append_mandatory_space();+ else append_optional_space();+ break;+ }+ if (tail && comb != Complex_Selector::ANCESTOR_OF) {+ if (c->has_line_break()) append_optional_linefeed();+ }+ if (tail) tail->perform(this);+ }++ void Inspect::operator()(Selector_List* g)+ {+ if (g->empty()) return;+ for (size_t i = 0, L = g->length(); i < L; ++i) {+ if (!in_wrapped && i == 0) append_indentation();+ (*g)[i]->perform(this);+ if (i < L - 1) {+ append_comma_separator();+ if ((*g)[i]->has_line_feed()) {+ append_optional_linefeed();+ append_indentation();+ }+ }+ }+ }++ void Inspect::fallback_impl(AST_Node* n)+ {+ }++}
+ libsass/inspect.hpp view
@@ -0,0 +1,98 @@+#ifndef SASS_INSPECT_H+#define SASS_INSPECT_H++#include <string>++#include "position.hpp"+#include "operation.hpp"+#include "emitter.hpp"++namespace Sass {+ class Context;+ using namespace std;++ class Inspect : public Operation_CRTP<void, Inspect>, public Emitter {+ protected:+ // import all the class-specific methods and override as desired+ using Operation_CRTP<void, Inspect>::operator();++ void fallback_impl(AST_Node* n);++ public:++ Inspect(Emitter emi);+ virtual ~Inspect();++ // statements+ virtual void operator()(Block*);+ virtual void operator()(Ruleset*);+ virtual void operator()(Propset*);+ virtual void operator()(Bubble*);+ virtual void operator()(Feature_Block*);+ virtual void operator()(Media_Block*);+ virtual void operator()(At_Root_Block*);+ virtual void operator()(At_Rule*);+ virtual void operator()(Keyframe_Rule*);+ virtual void operator()(Declaration*);+ virtual void operator()(Assignment*);+ virtual void operator()(Import*);+ virtual void operator()(Import_Stub*);+ virtual void operator()(Warning*);+ virtual void operator()(Error*);+ virtual void operator()(Debug*);+ virtual void operator()(Comment*);+ virtual void operator()(If*);+ virtual void operator()(For*);+ virtual void operator()(Each*);+ virtual void operator()(While*);+ virtual void operator()(Return*);+ virtual void operator()(Extension*);+ virtual void operator()(Definition*);+ virtual void operator()(Mixin_Call*);+ virtual void operator()(Content*);+ // expressions+ virtual void operator()(Map*);+ virtual void operator()(List*);+ virtual void operator()(Binary_Expression*);+ virtual void operator()(Unary_Expression*);+ virtual void operator()(Function_Call*);+ virtual void operator()(Function_Call_Schema*);+ virtual void operator()(Variable*);+ virtual void operator()(Textual*);+ virtual void operator()(Number*);+ virtual void operator()(Color*);+ virtual void operator()(Boolean*);+ virtual void operator()(String_Schema*);+ virtual void operator()(String_Constant*);+ virtual void operator()(String_Quoted*);+ virtual void operator()(Feature_Query*);+ virtual void operator()(Feature_Query_Condition*);+ virtual void operator()(Media_Query*);+ virtual void operator()(Media_Query_Expression*);+ virtual void operator()(At_Root_Expression*);+ virtual void operator()(Null*);+ virtual void operator()(Parent_Selector* p);+ // parameters and arguments+ virtual void operator()(Parameter*);+ virtual void operator()(Parameters*);+ virtual void operator()(Argument*);+ virtual void operator()(Arguments*);+ // selectors+ virtual void operator()(Selector_Schema*);+ virtual void operator()(Selector_Reference*);+ virtual void operator()(Selector_Placeholder*);+ virtual void operator()(Type_Selector*);+ virtual void operator()(Selector_Qualifier*);+ virtual void operator()(Attribute_Selector*);+ virtual void operator()(Pseudo_Selector*);+ virtual void operator()(Wrapped_Selector*);+ virtual void operator()(Compound_Selector*);+ virtual void operator()(Complex_Selector*);+ virtual void operator()(Selector_List*);++ // template <typename U>+ // void fallback(U x) { fallback_impl(reinterpret_cast<AST_Node*>(x)); }+ };++}+#endif
+ libsass/json.cpp view
@@ -0,0 +1,1411 @@+/*+ Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)+ All rights reserved.++ Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:++ The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+*/++#include "json.hpp"++#include <assert.h>+#include <stdint.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>++#ifdef _MSC_VER++#include <stdarg.h>+#define snprintf c99_snprintf++inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)+{+ int count = -1;++ if (size != 0)+ count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);+ if (count == -1)+ count = _vscprintf(format, ap);++ return count;+}++inline int c99_snprintf(char* str, size_t size, const char* format, ...)+{+ int count;+ va_list ap;++ va_start(ap, format);+ count = c99_vsnprintf(str, size, format, ap);+ va_end(ap);++ return count;+}+#endif // _MSC_VER++#define out_of_memory() do { \+ fprintf(stderr, "Out of memory.\n"); \+ exit(EXIT_FAILURE); \+ } while (0)++/* Sadly, strdup is not portable. */+static char *json_strdup(const char *str)+{+ char *ret = (char*) malloc(strlen(str) + 1);+ if (ret == NULL)+ out_of_memory();+ strcpy(ret, str);+ return ret;+}++/* String buffer */++typedef struct+{+ char *cur;+ char *end;+ char *start;+} SB;++static void sb_init(SB *sb)+{+ sb->start = (char*) malloc(17);+ if (sb->start == NULL)+ out_of_memory();+ sb->cur = sb->start;+ sb->end = sb->start + 16;+}++/* sb and need may be evaluated multiple times. */+#define sb_need(sb, need) do { \+ if ((sb)->end - (sb)->cur < (need)) \+ sb_grow(sb, need); \+ } while (0)++static void sb_grow(SB *sb, int need)+{+ size_t length = sb->cur - sb->start;+ size_t alloc = sb->end - sb->start;+ + do {+ alloc *= 2;+ } while (alloc < length + need);+ + sb->start = (char*) realloc(sb->start, alloc + 1);+ if (sb->start == NULL)+ out_of_memory();+ sb->cur = sb->start + length;+ sb->end = sb->start + alloc;+}++static void sb_put(SB *sb, const char *bytes, int count)+{+ sb_need(sb, count);+ memcpy(sb->cur, bytes, count);+ sb->cur += count;+}++#define sb_putc(sb, c) do { \+ if ((sb)->cur >= (sb)->end) \+ sb_grow(sb, 1); \+ *(sb)->cur++ = (c); \+ } while (0)++static void sb_puts(SB *sb, const char *str)+{+ sb_put(sb, str, strlen(str));+}++static char *sb_finish(SB *sb)+{+ *sb->cur = 0;+ assert(sb->start <= sb->cur && strlen(sb->start) == (size_t)(sb->cur - sb->start));+ return sb->start;+}++static void sb_free(SB *sb)+{+ free(sb->start);+}++/*+ * Unicode helper functions+ *+ * These are taken from the ccan/charset module and customized a bit.+ * Putting them here means the compiler can (choose to) inline them,+ * and it keeps ccan/json from having a dependency.+ */++/*+ * Type for Unicode codepoints.+ * We need our own because wchar_t might be 16 bits.+ */+typedef uint32_t uchar_t;++/*+ * Validate a single UTF-8 character starting at @s.+ * The string must be null-terminated.+ *+ * If it's valid, return its length (1 thru 4).+ * If it's invalid or clipped, return 0.+ *+ * This function implements the syntax given in RFC3629, which is+ * the same as that given in The Unicode Standard, Version 6.0.+ *+ * It has the following properties:+ *+ * * All codepoints U+0000..U+10FFFF may be encoded,+ * except for U+D800..U+DFFF, which are reserved+ * for UTF-16 surrogate pair encoding.+ * * UTF-8 byte sequences longer than 4 bytes are not permitted,+ * as they exceed the range of Unicode.+ * * The sixty-six Unicode "non-characters" are permitted+ * (namely, U+FDD0..U+FDEF, U+xxFFFE, and U+xxFFFF).+ */+static int utf8_validate_cz(const char *s)+{+ unsigned char c = *s++;+ + if (c <= 0x7F) { /* 00..7F */+ return 1;+ } else if (c <= 0xC1) { /* 80..C1 */+ /* Disallow overlong 2-byte sequence. */+ return 0;+ } else if (c <= 0xDF) { /* C2..DF */+ /* Make sure subsequent byte is in the range 0x80..0xBF. */+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ + return 2;+ } else if (c <= 0xEF) { /* E0..EF */+ /* Disallow overlong 3-byte sequence. */+ if (c == 0xE0 && (unsigned char)*s < 0xA0)+ return 0;+ + /* Disallow U+D800..U+DFFF. */+ if (c == 0xED && (unsigned char)*s > 0x9F)+ return 0;+ + /* Make sure subsequent bytes are in the range 0x80..0xBF. */+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ + return 3;+ } else if (c <= 0xF4) { /* F0..F4 */+ /* Disallow overlong 4-byte sequence. */+ if (c == 0xF0 && (unsigned char)*s < 0x90)+ return 0;+ + /* Disallow codepoints beyond U+10FFFF. */+ if (c == 0xF4 && (unsigned char)*s > 0x8F)+ return 0;+ + /* Make sure subsequent bytes are in the range 0x80..0xBF. */+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ if (((unsigned char)*s++ & 0xC0) != 0x80)+ return 0;+ + return 4;+ } else { /* F5..FF */+ return 0;+ }+}++/* Validate a null-terminated UTF-8 string. */+static bool utf8_validate(const char *s)+{+ int len;+ + for (; *s != 0; s += len) {+ len = utf8_validate_cz(s);+ if (len == 0)+ return false;+ }+ + return true;+}++/*+ * Read a single UTF-8 character starting at @s,+ * returning the length, in bytes, of the character read.+ *+ * This function assumes input is valid UTF-8,+ * and that there are enough characters in front of @s.+ */+static int utf8_read_char(const char *s, uchar_t *out)+{+ const unsigned char *c = (const unsigned char*) s;+ + assert(utf8_validate_cz(s));++ if (c[0] <= 0x7F) {+ /* 00..7F */+ *out = c[0];+ return 1;+ } else if (c[0] <= 0xDF) {+ /* C2..DF (unless input is invalid) */+ *out = ((uchar_t)c[0] & 0x1F) << 6 |+ ((uchar_t)c[1] & 0x3F);+ return 2;+ } else if (c[0] <= 0xEF) {+ /* E0..EF */+ *out = ((uchar_t)c[0] & 0xF) << 12 |+ ((uchar_t)c[1] & 0x3F) << 6 |+ ((uchar_t)c[2] & 0x3F);+ return 3;+ } else {+ /* F0..F4 (unless input is invalid) */+ *out = ((uchar_t)c[0] & 0x7) << 18 |+ ((uchar_t)c[1] & 0x3F) << 12 |+ ((uchar_t)c[2] & 0x3F) << 6 |+ ((uchar_t)c[3] & 0x3F);+ return 4;+ }+}++/*+ * Write a single UTF-8 character to @s,+ * returning the length, in bytes, of the character written.+ *+ * @unicode must be U+0000..U+10FFFF, but not U+D800..U+DFFF.+ *+ * This function will write up to 4 bytes to @out.+ */+static int utf8_write_char(uchar_t unicode, char *out)+{+ unsigned char *o = (unsigned char*) out;+ + assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF));++ if (unicode <= 0x7F) {+ /* U+0000..U+007F */+ *o++ = unicode;+ return 1;+ } else if (unicode <= 0x7FF) {+ /* U+0080..U+07FF */+ *o++ = 0xC0 | unicode >> 6;+ *o++ = 0x80 | (unicode & 0x3F);+ return 2;+ } else if (unicode <= 0xFFFF) {+ /* U+0800..U+FFFF */+ *o++ = 0xE0 | unicode >> 12;+ *o++ = 0x80 | (unicode >> 6 & 0x3F);+ *o++ = 0x80 | (unicode & 0x3F);+ return 3;+ } else {+ /* U+10000..U+10FFFF */+ *o++ = 0xF0 | unicode >> 18;+ *o++ = 0x80 | (unicode >> 12 & 0x3F);+ *o++ = 0x80 | (unicode >> 6 & 0x3F);+ *o++ = 0x80 | (unicode & 0x3F);+ return 4;+ }+}++/*+ * Compute the Unicode codepoint of a UTF-16 surrogate pair.+ *+ * @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF.+ * If they aren't, this function returns false.+ */+static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uchar_t *unicode)+{+ if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {+ *unicode = 0x10000 + ((((uchar_t)uc & 0x3FF) << 10) | (lc & 0x3FF));+ return true;+ } else {+ return false;+ }+}++/*+ * Construct a UTF-16 surrogate pair given a Unicode codepoint.+ *+ * @unicode must be U+10000..U+10FFFF.+ */+static void to_surrogate_pair(uchar_t unicode, uint16_t *uc, uint16_t *lc)+{+ uchar_t n;+ + assert(unicode >= 0x10000 && unicode <= 0x10FFFF);+ + n = unicode - 0x10000;+ *uc = ((n >> 10) & 0x3FF) | 0xD800;+ *lc = (n & 0x3FF) | 0xDC00;+}++#define is_space(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || (c) == ' ')+#define is_digit(c) ((c) >= '0' && (c) <= '9')++static bool parse_value (const char **sp, JsonNode **out);+static bool parse_string (const char **sp, char **out);+static bool parse_number (const char **sp, double *out);+static bool parse_array (const char **sp, JsonNode **out);+static bool parse_object (const char **sp, JsonNode **out);+static bool parse_hex16 (const char **sp, uint16_t *out);++static bool expect_literal (const char **sp, const char *str);+static void skip_space (const char **sp);++static void emit_value (SB *out, const JsonNode *node);+static void emit_value_indented (SB *out, const JsonNode *node, const char *space, int indent_level);+static void emit_string (SB *out, const char *str);+static void emit_number (SB *out, double num);+static void emit_array (SB *out, const JsonNode *array);+static void emit_array_indented (SB *out, const JsonNode *array, const char *space, int indent_level);+static void emit_object (SB *out, const JsonNode *object);+static void emit_object_indented (SB *out, const JsonNode *object, const char *space, int indent_level);++static int write_hex16(char *out, uint16_t val);++static JsonNode *mknode(JsonTag tag);+static void append_node(JsonNode *parent, JsonNode *child);+static void prepend_node(JsonNode *parent, JsonNode *child);+static void append_member(JsonNode *object, char *key, JsonNode *value);++/* Assertion-friendly validity checks */+static bool tag_is_valid(unsigned int tag);+static bool number_is_valid(const char *num);++JsonNode *json_decode(const char *json)+{+ const char *s = json;+ JsonNode *ret;+ + skip_space(&s);+ if (!parse_value(&s, &ret))+ return NULL;+ + skip_space(&s);+ if (*s != 0) {+ json_delete(ret);+ return NULL;+ }+ + return ret;+}++char *json_encode(const JsonNode *node)+{+ return json_stringify(node, NULL);+}++char *json_encode_string(const char *str)+{+ SB sb;+ sb_init(&sb);+ + emit_string(&sb, str);+ + return sb_finish(&sb);+}++char *json_stringify(const JsonNode *node, const char *space)+{+ SB sb;+ sb_init(&sb);+ + if (space != NULL)+ emit_value_indented(&sb, node, space, 0);+ else+ emit_value(&sb, node);+ + return sb_finish(&sb);+}++void json_delete(JsonNode *node)+{+ if (node != NULL) {+ json_remove_from_parent(node);+ + switch (node->tag) {+ case JSON_STRING:+ free(node->string_);+ break;+ case JSON_ARRAY:+ case JSON_OBJECT:+ {+ JsonNode *child, *next;+ for (child = node->children.head; child != NULL; child = next) {+ next = child->next;+ json_delete(child);+ }+ break;+ }+ default:;+ }+ + free(node);+ }+}++bool json_validate(const char *json)+{+ const char *s = json;+ + skip_space(&s);+ if (!parse_value(&s, NULL))+ return false;+ + skip_space(&s);+ if (*s != 0)+ return false;+ + return true;+}++JsonNode *json_find_element(JsonNode *array, int index)+{+ JsonNode *element;+ int i = 0;+ + if (array == NULL || array->tag != JSON_ARRAY)+ return NULL;+ + json_foreach(element, array) {+ if (i == index)+ return element;+ i++;+ }+ + return NULL;+}++JsonNode *json_find_member(JsonNode *object, const char *name)+{+ JsonNode *member;+ + if (object == NULL || object->tag != JSON_OBJECT)+ return NULL;+ + json_foreach(member, object)+ if (strcmp(member->key, name) == 0)+ return member;+ + return NULL;+}++JsonNode *json_first_child(const JsonNode *node)+{+ if (node != NULL && (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT))+ return node->children.head;+ return NULL;+}++static JsonNode *mknode(JsonTag tag)+{+ JsonNode *ret = (JsonNode*) calloc(1, sizeof(JsonNode));+ if (ret == NULL)+ out_of_memory();+ ret->tag = tag;+ return ret;+}++JsonNode *json_mknull(void)+{+ return mknode(JSON_NULL);+}++JsonNode *json_mkbool(bool b)+{+ JsonNode *ret = mknode(JSON_BOOL);+ ret->bool_ = b;+ return ret;+}++static JsonNode *mkstring(char *s)+{+ JsonNode *ret = mknode(JSON_STRING);+ ret->string_ = s;+ return ret;+}++JsonNode *json_mkstring(const char *s)+{+ return mkstring(json_strdup(s));+}++JsonNode *json_mknumber(double n)+{+ JsonNode *node = mknode(JSON_NUMBER);+ node->number_ = n;+ return node;+}++JsonNode *json_mkarray(void)+{+ return mknode(JSON_ARRAY);+}++JsonNode *json_mkobject(void)+{+ return mknode(JSON_OBJECT);+}++static void append_node(JsonNode *parent, JsonNode *child)+{+ 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;+}++static void append_member(JsonNode *object, char *key, JsonNode *value)+{+ 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);+}++void json_prepend_element(JsonNode *array, JsonNode *element)+{+ assert(array->tag == JSON_ARRAY);+ assert(element->parent == NULL);+ + prepend_node(array, element);+}++void json_append_member(JsonNode *object, const char *key, JsonNode *value)+{+ 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);+}++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;+ }+}++static bool parse_value(const char **sp, JsonNode **out)+{+ const char *s = *sp;+ + switch (*s) {+ case 'n':+ if (expect_literal(&s, "null")) {+ if (out)+ *out = json_mknull();+ *sp = s;+ return true;+ }+ return false;+ + case 'f':+ if (expect_literal(&s, "false")) {+ if (out)+ *out = json_mkbool(false);+ *sp = s;+ return true;+ }+ return false;+ + case 't':+ if (expect_literal(&s, "true")) {+ if (out)+ *out = json_mkbool(true);+ *sp = s;+ return true;+ }+ return false;+ + case '"': {+ char *str;+ if (parse_string(&s, out ? &str : NULL)) {+ if (out)+ *out = mkstring(str);+ *sp = s;+ return true;+ }+ return false;+ }+ + case '[':+ if (parse_array(&s, out)) {+ *sp = s;+ return true;+ }+ return false;+ + case '{':+ if (parse_object(&s, out)) {+ *sp = s;+ return true;+ }+ return false;+ + default: {+ double num;+ if (parse_number(&s, out ? &num : NULL)) {+ if (out)+ *out = json_mknumber(num);+ *sp = s;+ return true;+ }+ return false;+ }+ }+}++static bool parse_array(const char **sp, JsonNode **out)+{+ const char *s = *sp;+ JsonNode *ret = out ? json_mkarray() : NULL;+ JsonNode *element;+ + if (*s++ != '[')+ goto failure;+ skip_space(&s);+ + if (*s == ']') {+ s++;+ goto success;+ }+ + for (;;) {+ if (!parse_value(&s, out ? &element : NULL))+ goto failure;+ skip_space(&s);+ + if (out)+ json_append_element(ret, element);+ + if (*s == ']') {+ s++;+ goto success;+ }+ + if (*s++ != ',')+ goto failure;+ skip_space(&s);+ }+ +success:+ *sp = s;+ if (out)+ *out = ret;+ return true;++failure:+ json_delete(ret);+ return false;+}++static bool parse_object(const char **sp, JsonNode **out)+{+ const char *s = *sp;+ JsonNode *ret = out ? json_mkobject() : NULL;+ char *key;+ JsonNode *value;+ + if (*s++ != '{')+ goto failure;+ skip_space(&s);+ + if (*s == '}') {+ s++;+ goto success;+ }+ + for (;;) {+ if (!parse_string(&s, out ? &key : NULL))+ goto failure;+ skip_space(&s);+ + if (*s++ != ':')+ goto failure_free_key;+ skip_space(&s);+ + if (!parse_value(&s, out ? &value : NULL))+ goto failure_free_key;+ skip_space(&s);+ + if (out)+ append_member(ret, key, value);+ + if (*s == '}') {+ s++;+ goto success;+ }+ + if (*s++ != ',')+ goto failure;+ skip_space(&s);+ }+ +success:+ *sp = s;+ if (out)+ *out = ret;+ return true;++failure_free_key:+ if (out)+ free(key);+failure:+ json_delete(ret);+ return false;+}++bool parse_string(const char **sp, char **out)+{+ const char *s = *sp;+ SB sb;+ char throwaway_buffer[4];+ /* enough space for a UTF-8 character */+ char *b;+ + if (*s++ != '"')+ return false;+ + if (out) {+ sb_init(&sb);+ sb_need(&sb, 4);+ b = sb.cur;+ } else {+ b = throwaway_buffer;+ }+ + while (*s != '"') {+ unsigned char c = *s++;+ + /* Parse next character, and write it to b. */+ if (c == '\\') {+ c = *s++;+ switch (c) {+ case '"':+ case '\\':+ case '/':+ *b++ = c;+ break;+ case 'b':+ *b++ = '\b';+ break;+ case 'f':+ *b++ = '\f';+ break;+ case 'n':+ *b++ = '\n';+ break;+ case 'r':+ *b++ = '\r';+ break;+ case 't':+ *b++ = '\t';+ break;+ case 'u':+ {+ uint16_t uc, lc;+ uchar_t unicode;+ + if (!parse_hex16(&s, &uc))+ goto failed;+ + if (uc >= 0xD800 && uc <= 0xDFFF) {+ /* Handle UTF-16 surrogate pair. */+ if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc))+ goto failed; /* Incomplete surrogate pair. */+ if (!from_surrogate_pair(uc, lc, &unicode))+ goto failed; /* Invalid surrogate pair. */+ } else if (uc == 0) {+ /* Disallow "\u0000". */+ goto failed;+ } else {+ unicode = uc;+ }+ + b += utf8_write_char(unicode, b);+ break;+ }+ default:+ /* Invalid escape */+ goto failed;+ }+ } else if (c <= 0x1F) {+ /* Control characters are not allowed in string literals. */+ goto failed;+ } else {+ /* Validate and echo a UTF-8 character. */+ int len;+ + s--;+ len = utf8_validate_cz(s);+ if (len == 0)+ goto failed; /* Invalid UTF-8 character. */+ + while (len--)+ *b++ = *s++;+ }+ + /*+ * Update sb to know about the new bytes,+ * and set up b to write another character.+ */+ if (out) {+ sb.cur = b;+ sb_need(&sb, 4);+ b = sb.cur;+ } else {+ b = throwaway_buffer;+ }+ }+ s++;+ + if (out)+ *out = sb_finish(&sb);+ *sp = s;+ return true;++failed:+ if (out)+ sb_free(&sb);+ return false;+}++/*+ * The JSON spec says that a number shall follow this precise pattern+ * (spaces and quotes added for readability):+ * '-'? (0 | [1-9][0-9]*) ('.' [0-9]+)? ([Ee] [+-]? [0-9]+)?+ *+ * However, some JSON parsers are more liberal. For instance, PHP accepts+ * '.5' and '1.'. JSON.parse accepts '+3'.+ *+ * This function takes the strict approach.+ */+bool parse_number(const char **sp, double *out)+{+ const char *s = *sp;++ /* '-'? */+ if (*s == '-')+ s++;++ /* (0 | [1-9][0-9]*) */+ if (*s == '0') {+ s++;+ } else {+ if (!is_digit(*s))+ return false;+ do {+ s++;+ } while (is_digit(*s));+ }++ /* ('.' [0-9]+)? */+ if (*s == '.') {+ s++;+ if (!is_digit(*s))+ return false;+ do {+ s++;+ } while (is_digit(*s));+ }++ /* ([Ee] [+-]? [0-9]+)? */+ if (*s == 'E' || *s == 'e') {+ s++;+ if (*s == '+' || *s == '-')+ s++;+ if (!is_digit(*s))+ return false;+ do {+ s++;+ } while (is_digit(*s));+ }++ if (out)+ *out = strtod(*sp, NULL);++ *sp = s;+ return true;+}++static void skip_space(const char **sp)+{+ const char *s = *sp;+ while (is_space(*s))+ s++;+ *sp = s;+}++static void emit_value(SB *out, const JsonNode *node)+{+ assert(tag_is_valid(node->tag));+ switch (node->tag) {+ case JSON_NULL:+ sb_puts(out, "null");+ break;+ case JSON_BOOL:+ sb_puts(out, node->bool_ ? "true" : "false");+ break;+ case JSON_STRING:+ emit_string(out, node->string_);+ break;+ case JSON_NUMBER:+ emit_number(out, node->number_);+ break;+ case JSON_ARRAY:+ emit_array(out, node);+ break;+ case JSON_OBJECT:+ emit_object(out, node);+ break;+ default:+ assert(false);+ }+}++void emit_value_indented(SB *out, const JsonNode *node, const char *space, int indent_level)+{+ assert(tag_is_valid(node->tag));+ switch (node->tag) {+ case JSON_NULL:+ sb_puts(out, "null");+ break;+ case JSON_BOOL:+ sb_puts(out, node->bool_ ? "true" : "false");+ break;+ case JSON_STRING:+ emit_string(out, node->string_);+ break;+ case JSON_NUMBER:+ emit_number(out, node->number_);+ break;+ case JSON_ARRAY:+ emit_array_indented(out, node, space, indent_level);+ break;+ case JSON_OBJECT:+ emit_object_indented(out, node, space, indent_level);+ break;+ default:+ assert(false);+ }+}++static void emit_array(SB *out, const JsonNode *array)+{+ const JsonNode *element;+ + sb_putc(out, '[');+ json_foreach(element, array) {+ emit_value(out, element);+ if (element->next != NULL)+ sb_putc(out, ',');+ }+ sb_putc(out, ']');+}++static void emit_array_indented(SB *out, const JsonNode *array, const char *space, int indent_level)+{+ const JsonNode *element = array->children.head;+ int i;+ + if (element == NULL) {+ sb_puts(out, "[]");+ return;+ }+ + sb_puts(out, "[\n");+ while (element != NULL) {+ for (i = 0; i < indent_level + 1; i++)+ sb_puts(out, space);+ emit_value_indented(out, element, space, indent_level + 1);+ + element = element->next;+ sb_puts(out, element != NULL ? ",\n" : "\n");+ }+ for (i = 0; i < indent_level; i++)+ sb_puts(out, space);+ sb_putc(out, ']');+}++static void emit_object(SB *out, const JsonNode *object)+{+ const JsonNode *member;+ + sb_putc(out, '{');+ json_foreach(member, object) {+ emit_string(out, member->key);+ sb_putc(out, ':');+ emit_value(out, member);+ if (member->next != NULL)+ sb_putc(out, ',');+ }+ sb_putc(out, '}');+}++static void emit_object_indented(SB *out, const JsonNode *object, const char *space, int indent_level)+{+ const JsonNode *member = object->children.head;+ int i;+ + if (member == NULL) {+ sb_puts(out, "{}");+ return;+ }+ + sb_puts(out, "{\n");+ while (member != NULL) {+ for (i = 0; i < indent_level + 1; i++)+ sb_puts(out, space);+ emit_string(out, member->key);+ sb_puts(out, ": ");+ emit_value_indented(out, member, space, indent_level + 1);+ + member = member->next;+ sb_puts(out, member != NULL ? ",\n" : "\n");+ }+ for (i = 0; i < indent_level; i++)+ sb_puts(out, space);+ sb_putc(out, '}');+}++void emit_string(SB *out, const char *str)+{+ bool escape_unicode = false;+ const char *s = str;+ char *b;+ + assert(utf8_validate(str));+ + /*+ * 14 bytes is enough space to write up to two+ * \uXXXX escapes and two quotation marks.+ */+ sb_need(out, 14);+ b = out->cur;+ + *b++ = '"';+ while (*s != 0) {+ unsigned char c = *s++;+ + /* Encode the next character, and write it to b. */+ switch (c) {+ case '"':+ *b++ = '\\';+ *b++ = '"';+ break;+ case '\\':+ *b++ = '\\';+ *b++ = '\\';+ break;+ case '\b':+ *b++ = '\\';+ *b++ = 'b';+ break;+ case '\f':+ *b++ = '\\';+ *b++ = 'f';+ break;+ case '\n':+ *b++ = '\\';+ *b++ = 'n';+ break;+ case '\r':+ *b++ = '\\';+ *b++ = 'r';+ break;+ case '\t':+ *b++ = '\\';+ *b++ = 't';+ break;+ default: {+ int len;+ + s--;+ len = utf8_validate_cz(s);+ + if (len == 0) {+ /*+ * Handle invalid UTF-8 character gracefully in production+ * by writing a replacement character (U+FFFD)+ * and skipping a single byte.+ *+ * This should never happen when assertions are enabled+ * due to the assertion at the beginning of this function.+ */+ assert(false);+ if (escape_unicode) {+ strcpy(b, "\\uFFFD");+ b += 6;+ } else {+ *b++ = 0xEFu;+ *b++ = 0xBFu;+ *b++ = 0xBDu;+ }+ s++;+ } else if (c < 0x1F || (c >= 0x80 && escape_unicode)) {+ /* Encode using \u.... */+ uint32_t unicode;+ + s += utf8_read_char(s, &unicode);+ + if (unicode <= 0xFFFF) {+ *b++ = '\\';+ *b++ = 'u';+ b += write_hex16(b, unicode);+ } else {+ /* Produce a surrogate pair. */+ uint16_t uc, lc;+ assert(unicode <= 0x10FFFF);+ to_surrogate_pair(unicode, &uc, &lc);+ *b++ = '\\';+ *b++ = 'u';+ b += write_hex16(b, uc);+ *b++ = '\\';+ *b++ = 'u';+ b += write_hex16(b, lc);+ }+ } else {+ /* Write the character directly. */+ while (len--)+ *b++ = *s++;+ }+ + break;+ }+ }+ + /*+ * Update *out to know about the new bytes,+ * and set up b to write another encoded character.+ */+ out->cur = b;+ sb_need(out, 14);+ b = out->cur;+ }+ *b++ = '"';+ + out->cur = b;+}++static void emit_number(SB *out, double num)+{+ /*+ * This isn't exactly how JavaScript renders numbers,+ * but it should produce valid JSON for reasonable numbers+ * preserve precision well enough, and avoid some oddities+ * like 0.3 -> 0.299999999999999988898 .+ */+ char buf[64];+ sprintf(buf, "%.16g", num);+ + if (number_is_valid(buf))+ sb_puts(out, buf);+ else+ sb_puts(out, "null");+}++static bool tag_is_valid(unsigned int tag)+{+ return (/* tag >= JSON_NULL && */ tag <= JSON_OBJECT);+}++static bool number_is_valid(const char *num)+{+ return (parse_number(&num, NULL) && *num == '\0');+}++static bool expect_literal(const char **sp, const char *str)+{+ const char *s = *sp;+ + while (*str != '\0')+ if (*s++ != *str++)+ return false;+ + *sp = s;+ return true;+}++/*+ * Parses exactly 4 hex characters (capital or lowercase).+ * Fails if any input chars are not [0-9A-Fa-f].+ */+static bool parse_hex16(const char **sp, uint16_t *out)+{+ const char *s = *sp;+ uint16_t ret = 0;+ uint16_t i;+ uint16_t tmp;+ char c;++ for (i = 0; i < 4; i++) {+ c = *s++;+ if (c >= '0' && c <= '9')+ tmp = c - '0';+ else if (c >= 'A' && c <= 'F')+ tmp = c - 'A' + 10;+ else if (c >= 'a' && c <= 'f')+ tmp = c - 'a' + 10;+ else+ return false;++ ret <<= 4;+ ret += tmp;+ }+ + if (out)+ *out = ret;+ *sp = s;+ return true;+}++/*+ * Encodes a 16-bit number into hexadecimal,+ * writing exactly 4 hex chars.+ */+static int write_hex16(char *out, uint16_t val)+{+ const char *hex = "0123456789ABCDEF";+ + *out++ = hex[(val >> 12) & 0xF];+ *out++ = hex[(val >> 8) & 0xF];+ *out++ = hex[(val >> 4) & 0xF];+ *out++ = hex[ val & 0xF];+ + return 4;+}++bool json_check(const JsonNode *node, char errmsg[256])+{+ #define problem(...) do { \+ if (errmsg != NULL) \+ snprintf(errmsg, 256, __VA_ARGS__); \+ return false; \+ } while (0)+ + if (node->key != NULL && !utf8_validate(node->key))+ problem("key contains invalid UTF-8");+ + if (!tag_is_valid(node->tag))+ problem("tag is invalid (%u)", node->tag);+ + if (node->tag == JSON_BOOL) {+ if (node->bool_ != false && node->bool_ != true)+ problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true);+ } else if (node->tag == JSON_STRING) {+ if (node->string_ == NULL)+ problem("string_ is NULL");+ if (!utf8_validate(node->string_))+ problem("string_ contains invalid UTF-8");+ } else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) {+ JsonNode *head = node->children.head;+ JsonNode *tail = node->children.tail;+ + if (head == NULL || tail == NULL) {+ if (head != NULL)+ problem("tail is NULL, but head is not");+ if (tail != NULL)+ problem("head is NULL, but tail is not");+ } else {+ JsonNode *child;+ JsonNode *last = NULL;+ + if (head->prev != NULL)+ problem("First child's prev pointer is not NULL");+ + for (child = head; child != NULL; last = child, child = child->next) {+ if (child == node)+ problem("node is its own child");+ if (child->next == child)+ problem("child->next == child (cycle)");+ if (child->next == head)+ problem("child->next == head (cycle)");+ + if (child->parent != node)+ problem("child does not point back to parent");+ if (child->next != NULL && child->next->prev != child)+ problem("child->next does not point back to child");+ + if (node->tag == JSON_ARRAY && child->key != NULL)+ problem("Array element's key is not NULL");+ if (node->tag == JSON_OBJECT && child->key == NULL)+ problem("Object member's key is NULL");+ + if (!json_check(child, errmsg))+ return false;+ }+ + if (last != tail)+ problem("tail does not match pointer found by starting at head and following next links");+ }+ }+ + return true;+ + #undef problem+}
+ libsass/json.hpp view
@@ -0,0 +1,117 @@+/*+ Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)+ All rights reserved.++ Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:++ The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+*/++#ifndef CCAN_JSON_H+#define CCAN_JSON_H++#include <stdbool.h>+#include <stddef.h>++typedef enum {+ JSON_NULL,+ JSON_BOOL,+ JSON_STRING,+ JSON_NUMBER,+ JSON_ARRAY,+ JSON_OBJECT,+} JsonTag;++typedef struct JsonNode JsonNode;++struct JsonNode+{+ /* only if parent is an object or array (NULL otherwise) */+ JsonNode *parent;+ JsonNode *prev, *next;+ + /* only if parent is an object (NULL otherwise) */+ char *key; /* Must be valid UTF-8. */+ + JsonTag tag;+ union {+ /* JSON_BOOL */+ bool bool_;+ + /* JSON_STRING */+ char *string_; /* Must be valid UTF-8. */+ + /* JSON_NUMBER */+ double number_;+ + /* JSON_ARRAY */+ /* JSON_OBJECT */+ struct {+ JsonNode *head, *tail;+ } children;+ };+};++/*** Encoding, decoding, and validation ***/++JsonNode *json_decode (const char *json);+char *json_encode (const JsonNode *node);+char *json_encode_string (const char *str);+char *json_stringify (const JsonNode *node, const char *space);+void json_delete (JsonNode *node);++bool json_validate (const char *json);++/*** Lookup and traversal ***/++JsonNode *json_find_element (JsonNode *array, int index);+JsonNode *json_find_member (JsonNode *object, const char *key);++JsonNode *json_first_child (const JsonNode *node);++#define json_foreach(i, object_or_array) \+ for ((i) = json_first_child(object_or_array); \+ (i) != NULL; \+ (i) = (i)->next)++/*** Construction and manipulation ***/++JsonNode *json_mknull(void);+JsonNode *json_mkbool(bool b);+JsonNode *json_mkstring(const char *s);+JsonNode *json_mknumber(double n);+JsonNode *json_mkarray(void);+JsonNode *json_mkobject(void);++void json_append_element(JsonNode *array, JsonNode *element);+void json_prepend_element(JsonNode *array, JsonNode *element);+void json_append_member(JsonNode *object, const char *key, JsonNode *value);+void json_prepend_member(JsonNode *object, const char *key, JsonNode *value);++void json_remove_from_parent(JsonNode *node);++/*** Debugging ***/++/*+ * Look for structure and encoding problems in a JsonNode or its descendents.+ *+ * If a problem is detected, return false, writing a description of the problem+ * to errmsg (unless errmsg is NULL).+ */+bool json_check(const JsonNode *node, char errmsg[256]);++#endif
+ libsass/kwd_arg_macros.hpp view
@@ -0,0 +1,28 @@+#ifndef SASS_KWD_ARG_MACROS_H+#define SASS_KWD_ARG_MACROS_H++// Example usage:+// KWD_ARG_SET(Args) {+// KWD_ARG(Args, string, foo);+// KWD_ARG(Args, int, bar);+// ...+// };+//+// ... and later ...+//+// something(Args().foo("hey").bar(3));++#define KWD_ARG_SET(set_name) class set_name++#define KWD_ARG(set_name, type, name) \+private: \+ type name##_; \+public: \+ set_name& name(type name##__) { \+ name##_ = name##__; \+ return *this; \+ } \+ type name() { return name##_; } \+private:++#endif
+ libsass/lexer.cpp view
@@ -0,0 +1,133 @@+#include <cctype>+#include <cstddef>+#include <iostream>+#include <iomanip>+#include "lexer.hpp"+#include "constants.hpp"+++namespace Sass {+ using namespace Constants;++ namespace Prelexer {++ //####################################+ // BASIC CHARACTER MATCHERS+ //####################################++ // Match standard control chars+ const char* kwd_at(const char* src) { return exactly<'@'>(src); }+ const char* kwd_dot(const char* src) { return exactly<'.'>(src); }+ const char* kwd_comma(const char* src) { return exactly<','>(src); };+ const char* kwd_colon(const char* src) { return exactly<':'>(src); };+ const char* kwd_star(const char* src) { return exactly<'*'>(src); };+ const char* kwd_plus(const char* src) { return exactly<'+'>(src); };+ const char* kwd_minus(const char* src) { return exactly<'-'>(src); };+ const char* kwd_slash(const char* src) { return exactly<'/'>(src); };++ //####################################+ // implement some function that do exist in the standard+ // but those are locale aware which brought some trouble+ // this even seems to improve performance by quite a bit+ //####################################++ const bool is_alpha(const char& chr)+ {+ return unsigned(chr - 'A') <= 'Z' - 'A' ||+ unsigned(chr - 'a') <= 'z' - 'a';+ }++ const bool is_space(const char& chr)+ {+ // adapted the technique from is_alpha+ return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';+ }++ const bool is_digit(const char& chr)+ {+ // adapted the technique from is_alpha+ return unsigned(chr - '0') <= '9' - '0';+ }++ const bool is_xdigit(const char& chr)+ {+ // adapted the technique from is_alpha+ return unsigned(chr - '0') <= '9' - '0' ||+ unsigned(chr - 'a') <= 'f' - 'a' ||+ unsigned(chr - 'A') <= 'F' - 'A';+ }++ const bool is_punct(const char& chr)+ {+ // locale independent+ return chr == '.';+ }++ const bool is_alnum(const char& chr)+ {+ return is_alpha(chr) || is_digit(chr);+ }++ // check if char is outside ascii range+ const bool is_unicode(const char& chr)+ {+ // check for unicode range+ return unsigned(chr) > 127;+ }++ // Match word character (look ahead)+ const bool is_character(const char& chr)+ {+ // valid alpha, numeric or unicode char (plus hyphen)+ return is_alnum(chr) || is_unicode(chr) || chr == '-';+ }++ //####################################+ // BASIC CLASS MATCHERS+ //####################################++ // create matchers that advance the position+ const char* space(const char* src) { return is_space(*src) ? src + 1 : 0; }+ const char* alpha(const char* src) { return is_alpha(*src) ? src + 1 : 0; }+ const char* unicode(const char* src) { return is_unicode(*src) ? src + 1 : 0; }+ const char* digit(const char* src) { return is_digit(*src) ? src + 1 : 0; }+ const char* xdigit(const char* src) { return is_xdigit(*src) ? src + 1 : 0; }+ const char* alnum(const char* src) { return is_alnum(*src) ? src + 1 : 0; }+ const char* punct(const char* src) { return is_punct(*src) ? src + 1 : 0; }+ const char* character(const char* src) { return is_character(*src) ? src + 1 : 0; }++ // Match multiple ctype characters.+ const char* spaces(const char* src) { return one_plus<space>(src); }+ const char* digits(const char* src) { return one_plus<digit>(src); }++ // Whitespace handling.+ const char* no_spaces(const char* src) { return negate< space >(src); }+ const char* optional_spaces(const char* src) { return zero_plus< space >(src); }++ // Match any single character.+ const char* any_char(const char* src) { return *src ? src + 1 : src; }++ // Match word boundary (zero-width lookahead).+ const char* word_boundary(const char* src) { return is_character(*src) ? 0 : src; }++ // Match linefeed /(?:\n|\r\n?)/+ const char* re_linebreak(const char* src)+ {+ // end of file or unix linefeed return here+ if (*src == 0 || *src == '\n') return src + 1;+ // a carriage return may optionally be followed by a linefeed+ if (*src == '\r') return *(src + 1) == '\n' ? src + 2 : src + 1;+ // no linefeed+ return 0;+ }++ // Assert string boundaries (/\Z|\z|\A/)+ // This is a zero-width positive lookahead+ const char* end_of_line(const char* src)+ {+ // end of file or unix linefeed return here+ return *src == 0 || *src == '\n' || *src == '\r' ? src : 0;+ }++ }+}
+ libsass/lexer.hpp view
@@ -0,0 +1,239 @@+#ifndef SASS_LEXER_H+#define SASS_LEXER_H++#include <cstring>++namespace Sass {+ namespace Prelexer {++ //####################################+ // BASIC CHARACTER MATCHERS+ //####################################++ // Match standard control chars+ const char* kwd_at(const char* src);+ const char* kwd_dot(const char* src);+ const char* kwd_comma(const char* src);+ const char* kwd_colon(const char* src);+ const char* kwd_star(const char* src);+ const char* kwd_plus(const char* src);+ const char* kwd_minus(const char* src);+ const char* kwd_slash(const char* src);++ //####################################+ // BASIC CLASS MATCHERS+ //####################################++ // These are locale independant+ const bool is_space(const char& src);+ const bool is_alpha(const char& src);+ const bool is_punct(const char& src);+ const bool is_digit(const char& src);+ const bool is_alnum(const char& src);+ const bool is_xdigit(const char& src);+ const bool is_unicode(const char& src);+ const bool is_character(const char& src);++ // Match a single ctype predicate.+ const char* space(const char* src);+ const char* alpha(const char* src);+ const char* digit(const char* src);+ const char* xdigit(const char* src);+ const char* alnum(const char* src);+ const char* punct(const char* src);+ const char* unicode(const char* src);+ const char* character(const char* src);++ // Match multiple ctype characters.+ const char* spaces(const char* src);+ const char* digits(const char* src);++ // Whitespace handling.+ const char* no_spaces(const char* src);+ const char* optional_spaces(const char* src);++ // Match any single character (/./).+ const char* any_char(const char* src);++ // Assert word boundary (/\b/)+ // Is a zero-width positive lookaheads+ const char* word_boundary(const char* src);++ // Match a single linebreak (/(?:\n|\r\n?)/).+ const char* re_linebreak(const char* src);++ // Assert string boundaries (/\Z|\z|\A/)+ // There are zero-width positive lookaheads+ const char* end_of_line(const char* src);+ // const char* end_of_string(const char* src);+ // const char* start_of_string(const char* src);++ // Type definition for prelexer functions+ typedef const char* (*prelexer)(const char*);++ //####################################+ // BASIC "REGEX" CONSTRUCTORS+ //####################################++ // Match a single character literal.+ // Regex equivalent: /(?:literal)/+ template <char chr>+ const char* exactly(const char* src) {+ return *src == chr ? src + 1 : 0;+ }++ // Match a string constant.+ // Regex equivalent: /[axy]/+ template <const char* str>+ const char* exactly(const char* src) {+ if (str == 0) return 0;+ const char* pre = str;+ if (src == 0) return 0;+ // there is a small chance that the search string+ // is longer than the rest of the string to look at+ while (*pre && *src == *pre) {+ ++src, ++pre;+ }+ return *pre ? 0 : src;+ }++ // Match for members of char class.+ // Regex equivalent: /[axy]/+ template <const char* char_class>+ const char* class_char(const char* src) {+ const char* cc = char_class;+ while (*cc && *src != *cc) ++cc;+ return *cc ? src + 1 : 0;+ }++ // Match for members of char class.+ // Regex equivalent: /[axy]/+ template <const char* char_class>+ const char* class_chars(const char* src) {+ const char* p = src;+ while (class_char<char_class>(p)) ++p;+ return p == src ? 0 : p;+ }++ // Match all except the supplied one.+ // Regex equivalent: /[^x]/+ template <const char chr>+ const char* any_char_but(const char* src) {+ return (*src && *src != chr) ? src + 1 : 0;+ }++ // Succeeds if the matcher fails.+ // Aka. zero-width negative lookahead.+ // Regex equivalent: /(?!literal)/+ template <prelexer mx>+ const char* negate(const char* src) {+ return mx(src) ? 0 : src;+ }++ // Succeeds if the matcher succeeds.+ // Aka. zero-width positive lookahead.+ // Regex equivalent: /(?=literal)/+ // just hangs around until we need it+ template <prelexer mx>+ const char* lookahead(const char* src) {+ return mx(src) ? src : 0;+ }++ // Tries supplied matchers in order.+ // Succeeds if one of them succeeds.+ // Regex equivalent: /(?:FOO|BAR)/+ template <prelexer... mxs>+ const char* alternatives(const char* src) {+ const char* rslt;+ for (prelexer mx : { mxs... }) {+ if ((rslt = mx(src))) return rslt;+ }+ return 0;+ }++ // Tries supplied matchers in order.+ // Succeeds if all of them succeeds.+ // Regex equivalent: /(?:FOO)(?:BAR)/+ template <prelexer... mxs>+ const char* sequence(const char* src) {+ const char* rslt = src;+ for (prelexer mx : { mxs... }) {+ if (!(rslt = mx(rslt))) return 0;+ }+ return rslt;+ }++ // Match a pattern or not. Always succeeds.+ // Regex equivalent: /(?:literal)?/+ template <prelexer mx>+ const char* optional(const char* src) {+ const char* p = mx(src);+ return p ? p : src;+ }++ // Match zero or more of the patterns.+ // Regex equivalent: /(?:literal)*/+ template <prelexer mx>+ const char* zero_plus(const char* src) {+ const char* p = mx(src);+ while (p) src = p, p = mx(src);+ return src;+ }++ // Match one or more of the patterns.+ // Regex equivalent: /(?:literal)+/+ template <prelexer mx>+ const char* one_plus(const char* src) {+ const char* p = mx(src);+ if (!p) return 0;+ while (p) src = p, p = mx(src);+ return src;+ }++ // Match mx non-greedy until delimiter.+ // Other prelexers are greedy by default.+ // Regex equivalent: /(?:$mx)*?(?=$delim)\b/+ template <prelexer mx, prelexer delim>+ const char* non_greedy(const char* src) {+ while (!delim(src)) {+ const char* p = mx(src);+ if (p == src) return 0;+ if (p == 0) return 0;+ src = p;+ }+ return src;+ }++ //####################################+ // ADVANCED "REGEX" CONSTRUCTORS+ //####################################++ // Match with word boundary rule.+ // Regex equivalent: /(?:$mx)\b/+ template <const char* str>+ const char* word(const char* src) {+ return sequence <+ exactly < str >,+ word_boundary+ >(src);+ }++ template <char chr>+ const char* loosely(const char* src) {+ return sequence <+ optional_spaces,+ exactly < chr >+ >(src);+ }+ template <const char* str>+ const char* loosely(const char* src) {+ return sequence <+ optional_spaces,+ exactly < str >+ >(src);+ }++ }+}++#endif
+ libsass/listize.cpp view
@@ -0,0 +1,83 @@+#include <iostream>+#include <typeinfo>++#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)+ { }++ Expression* Listize::operator()(Selector_List* sel)+ {+ List* l = new (ctx.mem) List(sel->pstate(), sel->length(), List::COMMA);+ for (size_t i = 0, L = sel->length(); i < L; ++i) {+ *l << (*sel)[i]->perform(this);+ }+ return l;+ }++ Expression* Listize::operator()(Compound_Selector* sel)+ {+ To_String to_string;+ 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);+ }+ return new (ctx.mem) String_Constant(sel->pstate(), str);+ }++ Expression* Listize::operator()(Complex_Selector* sel)+ {+ List* l = new (ctx.mem) List(sel->pstate(), 2);++ Compound_Selector* head = sel->head();+ if (head && !head->is_empty_reference())+ {+ Expression* hh = head->perform(this);+ if (hh) *l << hh;+ }++ switch(sel->combinator())+ {+ case Complex_Selector::PARENT_OF:+ *l << new (ctx.mem) String_Constant(sel->pstate(), ">");+ break;+ case Complex_Selector::ADJACENT_TO:+ *l << new (ctx.mem) String_Constant(sel->pstate(), "+");+ break;+ case Complex_Selector::PRECEDES:+ *l << new (ctx.mem) String_Constant(sel->pstate(), "~");+ break;+ case Complex_Selector::ANCESTOR_OF:+ break;+ }++ Complex_Selector* tail = sel->tail();+ if (tail)+ {+ Expression* tt = tail->perform(this);+ if (tt && tt->concrete_type() == Expression::LIST)+ { *l += static_cast<List*>(tt); }+ else if (tt) *l << static_cast<List*>(tt);+ }+ if (l->length() == 0) return 0;+ return l;+ }++ Expression* Listize::operator()(Selector_Reference* sel)+ {+ return 0;+ }++ Expression* Listize::fallback_impl(AST_Node* n)+ {+ return static_cast<Expression*>(n);+ }+}
+ libsass/listize.hpp view
@@ -0,0 +1,41 @@+#ifndef SASS_LISTIZE_H+#define SASS_LISTIZE_H++#include <vector>+#include <iostream>++#include "ast.hpp"+#include "context.hpp"+#include "operation.hpp"+#include "environment.hpp"++namespace Sass {+ using namespace std;++ typedef Environment<AST_Node*> Env;+ struct Backtrace;++ class Listize : public Operation_CRTP<Expression*, Listize> {++ Context& ctx;++ Expression* fallback_impl(AST_Node* n);++ public:+ Listize(Context&);+ virtual ~Listize() { }++ using Operation<Expression*>::operator();++ Expression* operator()(Selector_List*);+ Expression* operator()(Complex_Selector*);+ Expression* operator()(Compound_Selector*);+ Expression* operator()(Selector_Reference*);++ template <typename U>+ Expression* fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/mapping.hpp view
@@ -0,0 +1,18 @@+#ifndef SASS_MAPPING_H+#define SASS_MAPPING_H++#include "position.hpp"++namespace Sass {++ struct Mapping {+ Position original_position;+ Position generated_position;++ Mapping(const Position& original_position, const Position& generated_position)+ : original_position(original_position), generated_position(generated_position) { }+ };++}++#endif
+ libsass/memory_manager.hpp view
@@ -0,0 +1,57 @@+#ifndef SASS_MEMORY_MANAGER_H+#define SASS_MEMORY_MANAGER_H++#include <vector>+#include <iostream>++namespace Sass {+ using namespace std;+ /////////////////////////////////////////////////////////////////////////////+ // A class for tracking allocations of AST_Node objects. The intended usage+ // is something like: Some_Node* n = new (mem_mgr) Some_Node(...);+ // Then, at the end of the program, the memory manager will delete all of the+ // allocated nodes that have been passed to it.+ // In the future, this class may implement a custom allocator.+ /////////////////////////////////////////////////////////////////////////////+ template <typename T>+ class Memory_Manager {+ vector<T*> nodes;++ public:+ Memory_Manager(size_t size = 0) : nodes(vector<T*>())+ { nodes.reserve(size); }++ ~Memory_Manager()+ {+ for (size_t i = 0, S = nodes.size(); i < S; ++i) {+ // cout << "deleting " << typeid(*nodes[i]).name() << endl;+ delete nodes[i];+ }+ }++ T* operator()(T* np)+ {+ nodes.push_back(np);+ // cout << "registering " << typeid(*np).name() << endl;+ return np;+ }++ void remove(T* np)+ {+ nodes.erase(find(nodes.begin(), nodes.end(), np));+ }+ };+}++template <typename T>+inline void* operator new(size_t size, Sass::Memory_Manager<T>& mem_mgr)+{ return mem_mgr(static_cast<T*>(operator new(size))); }++template <typename T>+inline void operator delete(void *np, Sass::Memory_Manager<T>& mem_mgr)+{+ mem_mgr.remove(reinterpret_cast<T*>(np));+ operator delete(np);+}++#endif
+ libsass/node.cpp view
@@ -0,0 +1,251 @@+#include "node.hpp"+#include "to_string.hpp"+#include "context.hpp"+#include "parser.hpp"++namespace Sass {+++ Node Node::createCombinator(const Complex_Selector::Combinator& combinator) {+ NodeDequePtr null;+ return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/);+ }+++ Node Node::createSelector(Complex_Selector* pSelector, Context& ctx) {+ NodeDequePtr null;++ Complex_Selector* pStripped = pSelector->clone(ctx);+ pStripped->tail(NULL);+ pStripped->combinator(Complex_Selector::ANCESTOR_OF);++ return Node(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);+ }+++ Node Node::createCollection() {+ NodeDequePtr pEmptyCollection = make_shared<NodeDeque>();+ return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection);+ }+++ Node Node::createCollection(const NodeDeque& values) {+ NodeDequePtr pShallowCopiedCollection = make_shared<NodeDeque>(values);+ return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pShallowCopiedCollection);+ }+++ Node Node::createNil() {+ NodeDequePtr null;+ return Node(NIL, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, null /*pCollection*/);+ }+++ Node::Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection)+ : got_line_feed(false), mType(type), mCombinator(combinator), mpSelector(pSelector), mpCollection(pCollection)+ { /* if (pSelector) got_line_feed = pSelector->has_line_feed(); */ }+++ Node Node::clone(Context& ctx) const {+ NodeDequePtr pNewCollection = make_shared<NodeDeque>();+ if (mpCollection) {+ for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {+ Node& toClone = *iter;+ pNewCollection->push_back(toClone.clone(ctx));+ }+ }++ return Node(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection);+ }+++ bool Node::contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const {+ bool found = false;++ for (NodeDeque::iterator iter = mpCollection->begin(), iterEnd = mpCollection->end(); iter != iterEnd; iter++) {+ Node& toTest = *iter;++ if (nodesEqual(toTest, potentialChild, simpleSelectorOrderDependent)) {+ found = true;+ break;+ }+ }++ return found;+ }+++ bool Node::operator==(const Node& rhs) const {+ return nodesEqual(*this, rhs, true /*simpleSelectorOrderDependent*/);+ }+++ bool nodesEqual(const Node& lhs, const Node& rhs, bool simpleSelectorOrderDependent) {+ if (lhs.type() != rhs.type()) {+ return false;+ }++ if (lhs.isCombinator()) {++ return lhs.combinator() == rhs.combinator();++ } else if (lhs.isNil()) {++ return true; // no state to check++ } else if (lhs.isSelector()){++ return selectors_equal(*lhs.selector(), *rhs.selector(), simpleSelectorOrderDependent);++ } else if (lhs.isCollection()) {++ if (lhs.collection()->size() != rhs.collection()->size()) {+ return false;+ }++ for (NodeDeque::iterator lhsIter = lhs.collection()->begin(), lhsIterEnd = lhs.collection()->end(),+ rhsIter = rhs.collection()->begin(); lhsIter != lhsIterEnd; lhsIter++, rhsIter++) {++ if (!nodesEqual(*lhsIter, *rhsIter, simpleSelectorOrderDependent)) {+ return false;+ }++ }++ return true;++ }++ // We shouldn't get here.+ throw "Comparing unknown node types. A new type was probably added and this method wasn't implemented for it.";+ }+++ void Node::plus(Node& rhs) {+ if (!this->isCollection() || !rhs.isCollection()) {+ throw "Both the current node and rhs must be collections.";+ }+ this->collection()->insert(this->collection()->end(), rhs.collection()->begin(), rhs.collection()->end());+ }++#ifdef DEBUG+ ostream& operator<<(ostream& os, const Node& node) {++ if (node.isCombinator()) {++ switch (node.combinator()) {+ case Complex_Selector::ANCESTOR_OF: os << "\" \""; break;+ case Complex_Selector::PARENT_OF: os << "\">\""; break;+ case Complex_Selector::PRECEDES: os << "\"~\""; break;+ case Complex_Selector::ADJACENT_TO: os << "\"+\""; break;+ }++ } else if (node.isNil()) {++ os << "nil";++ } else if (node.isSelector()){++ To_String to_string;+ os << node.selector()->head()->perform(&to_string);++ } else if (node.isCollection()) {++ os << "[";++ for (NodeDeque::iterator iter = node.collection()->begin(), iterBegin = node.collection()->begin(), iterEnd = node.collection()->end(); iter != iterEnd; iter++) {+ if (iter != iterBegin) {+ os << ", ";+ }++ os << (*iter);+ }++ os << "]";++ }++ return os;++ }+#endif+++ Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx) {+ if (pToConvert == NULL) {+ return Node::createNil();+ }++ Node node = Node::createCollection();++ while (pToConvert) {++ // the first Complex_Selector may contain a dummy head pointer, skip it.+ if (pToConvert->head() != NULL && !pToConvert->head()->is_empty_reference()) {+ node.collection()->push_back(Node::createSelector(pToConvert, ctx));+ }++ if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) {+ node.collection()->push_back(Node::createCombinator(pToConvert->combinator()));+ }++ pToConvert = pToConvert->tail();+ }++ return node;+ }+++ Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx) {+ if (toConvert.isNil()) {+ return NULL;+ }+++ if (!toConvert.isCollection()) {+ throw "The node to convert to a Complex_Selector* must be a collection type or nil.";+ }+++ NodeDeque& childNodes = *toConvert.collection();++ string noPath("");+ Position noPosition(-1, -1, -1);+ Complex_Selector* pFirst = new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);+ Complex_Selector* pCurrent = pFirst;++ for (NodeDeque::iterator childIter = childNodes.begin(), childIterEnd = childNodes.end(); childIter != childIterEnd; childIter++) {++ Node& child = *childIter;++ if (child.isSelector()) {+ pCurrent->tail(child.selector()->clone(ctx)); // JMA - need to clone the selector, because they can end up getting shared across Node collections, and can result in an infinite loop during the call to parentSuperselector()+ pCurrent = pCurrent->tail();+ } else if (child.isCombinator()) {+ pCurrent->combinator(child.combinator());++ // if the next node is also a combinator, create another Complex_Selector to hold it so it doesn't replace the current combinator+ if (childIter+1 != childIterEnd) {+ Node& nextNode = *(childIter+1);+ if (nextNode.isCombinator()) {+ pCurrent->tail(new (ctx.mem) Complex_Selector(ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));+ pCurrent = pCurrent->tail();+ }+ }+ } else {+ throw "The node to convert's children must be only combinators or selectors.";+ }+ }++ // Put the dummy Compound_Selector in the first position, for consistency with the rest of libsass+ Compound_Selector* fakeHead = new (ctx.mem) Compound_Selector(ParserState("[NODE]"), 1);+ Selector_Reference* selectorRef = new (ctx.mem) Selector_Reference(ParserState("[NODE]"), NULL);+ fakeHead->elements().push_back(selectorRef);+ pFirst->head(fakeHead);+ pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed);++ return pFirst;+ }+++}
+ libsass/node.hpp view
@@ -0,0 +1,121 @@+#ifndef SASS_NODE_H+#define SASS_NODE_H++#include <deque>+#include <iostream>+#include <memory>++#include "ast.hpp"+++namespace Sass {+++ using namespace std;+++ class Context;++ /*+ There are a lot of stumbling blocks when trying to port the ruby extend code to C++. The biggest is the choice of+ data type. The ruby code will pretty seamlessly switch types between an Array<SimpleSequence or Op> (libsass'+ equivalent is the Complex_Selector) to a Sequence, which contains more metadata about the sequence than just the+ selector info. They also have the ability to have arbitrary nestings of arrays like [1, [2]], which is hard to+ implement using Array equivalents in C++ (like the deque or vector). They also have the ability to include nil+ in the arrays, like [1, nil, 3], which has potential semantic differences than an empty array [1, [], 3]. To be+ able to represent all of these as unique cases, we need to create a tree of variant objects. The tree nature allows+ the inconsistent nesting levels. The variant nature (while making some of the C++ code uglier) allows the code to+ more closely match the ruby code, which is a huge benefit when attempting to implement an complex algorithm like+ the Extend operator.++ Note that the current libsass data model also pairs the combinator with the Complex_Selector that follows it, but+ ruby sass has no such restriction, so we attempt to create a data structure that can handle them split apart.+ */++ class Node;+ typedef deque<Node> NodeDeque;+ typedef shared_ptr<NodeDeque> NodeDequePtr;++ class Node {+ public:+ enum TYPE {+ SELECTOR,+ COMBINATOR,+ COLLECTION,+ NIL+ };++ TYPE type() const { return mType; }+ bool isCombinator() const { return mType == COMBINATOR; }+ bool isSelector() const { return mType == SELECTOR; }+ bool isCollection() const { return mType == COLLECTION; }+ bool isNil() const { return mType == NIL; }+ bool got_line_feed;++ Complex_Selector::Combinator combinator() const { return mCombinator; }++ Complex_Selector* selector() { return mpSelector; }+ const Complex_Selector* selector() const { return mpSelector; }++ NodeDequePtr collection() { return mpCollection; }+ const NodeDequePtr collection() const { return mpCollection; }++ static Node createCombinator(const Complex_Selector::Combinator& combinator);++ // This method will clone the selector, stripping off the tail and combinator+ static Node createSelector(Complex_Selector* pSelector, Context& ctx);++ static Node createCollection();+ static Node createCollection(const NodeDeque& values);++ static Node createNil();++ Node clone(Context& ctx) const;++ bool operator==(const Node& rhs) const;+ inline bool operator!=(const Node& rhs) const { return !(*this == rhs); }+++ /*+ COLLECTION FUNCTIONS++ Most types don't need any helper methods (nil and combinator due to their simplicity and+ selector due to the fact that we leverage the non-node selector code on the Complex_Selector+ whereever possible). The following methods are intended to be called on Node objects whose+ type is COLLECTION only.+ */++ // rhs and this must be node collections. Shallow copy the nodes from rhs to the end of this.+ // This function DOES NOT remove the nodes from rhs.+ void plus(Node& rhs);++ // potentialChild must be a node collection of selectors/combinators. this must be a collection+ // of collections of nodes/combinators. This method checks if potentialChild is a child of this+ // Node.+ bool contains(const Node& potentialChild, bool simpleSelectorOrderDependent) const;++ private:+ // Private constructor; Use the static methods (like createCombinator and createSelector)+ // to instantiate this object. This is more expressive, and it allows us to break apart each+ // case into separate functions.+ Node(const TYPE& type, Complex_Selector::Combinator combinator, Complex_Selector* pSelector, NodeDequePtr& pCollection);++ TYPE mType;++ // TODO: can we union these to save on memory?+ Complex_Selector::Combinator mCombinator;+ Complex_Selector* mpSelector; // this is an AST_Node, so it will be handled by the Memory_Manager+ NodeDequePtr mpCollection;+ };++#ifdef DEBUG+ ostream& operator<<(ostream& os, const Node& node);+#endif+ Node complexSelectorToNode(Complex_Selector* pToConvert, Context& ctx);+ Complex_Selector* nodeToComplexSelector(const Node& toConvert, Context& ctx);++ bool nodesEqual(const Node& one, const Node& two, bool simpleSelectorOrderDependent);++}++#endif
+ libsass/operation.hpp view
@@ -0,0 +1,166 @@+#ifndef SASS_OPERATION_H+#define SASS_OPERATION_H++#include <iostream>+#include <typeinfo>++#include "ast_fwd_decl.hpp"++namespace Sass {+ using namespace std;++ template<typename T>+ class Operation {+ public:+ virtual T operator()(AST_Node* x) = 0;+ virtual ~Operation() { }+ // statements+ virtual T operator()(Block* x) = 0;+ virtual T operator()(Ruleset* x) = 0;+ virtual T operator()(Propset* x) = 0;+ virtual T operator()(Bubble* x) = 0;+ virtual T operator()(Feature_Block* x) = 0;+ virtual T operator()(Media_Block* x) = 0;+ virtual T operator()(At_Root_Block* x) = 0;+ virtual T operator()(At_Rule* x) = 0;+ virtual T operator()(Keyframe_Rule* x) = 0;+ virtual T operator()(Declaration* x) = 0;+ virtual T operator()(Assignment* x) = 0;+ virtual T operator()(Import* x) = 0;+ virtual T operator()(Import_Stub* x) = 0;+ virtual T operator()(Warning* x) = 0;+ virtual T operator()(Error* x) = 0;+ virtual T operator()(Debug* x) = 0;+ virtual T operator()(Comment* x) = 0;+ virtual T operator()(If* x) = 0;+ virtual T operator()(For* x) = 0;+ virtual T operator()(Each* x) = 0;+ virtual T operator()(While* x) = 0;+ virtual T operator()(Return* x) = 0;+ virtual T operator()(Content* x) = 0;+ virtual T operator()(Extension* x) = 0;+ virtual T operator()(Definition* x) = 0;+ virtual T operator()(Mixin_Call* x) = 0;+ // expressions+ virtual T operator()(List* x) = 0;+ virtual T operator()(Map* x) = 0;+ virtual T operator()(Binary_Expression* x) = 0;+ virtual T operator()(Unary_Expression* x) = 0;+ virtual T operator()(Function_Call* x) = 0;+ virtual T operator()(Function_Call_Schema* x) = 0;+ virtual T operator()(Variable* x) = 0;+ virtual T operator()(Textual* x) = 0;+ virtual T operator()(Number* x) = 0;+ virtual T operator()(Color* x) = 0;+ virtual T operator()(Boolean* x) = 0;+ virtual T operator()(String_Schema* x) = 0;+ virtual T operator()(String_Constant* x) = 0;+ virtual T operator()(Feature_Query* x) = 0;+ virtual T operator()(Feature_Query_Condition* x)= 0;+ virtual T operator()(Media_Query* x) = 0;+ virtual T operator()(Media_Query_Expression* x) = 0;+ virtual T operator()(At_Root_Expression* x) = 0;+ virtual T operator()(Null* x) = 0;+ virtual T operator()(Parent_Selector* x) = 0;+ // parameters and arguments+ virtual T operator()(Parameter* x) = 0;+ virtual T operator()(Parameters* x) = 0;+ virtual T operator()(Argument* x) = 0;+ virtual T operator()(Arguments* x) = 0;+ // selectors+ virtual T operator()(Selector_Schema* x) = 0;+ virtual T operator()(Selector_Reference* x) = 0;+ virtual T operator()(Selector_Placeholder* x) = 0;+ virtual T operator()(Type_Selector* x) = 0;+ virtual T operator()(Selector_Qualifier* x) = 0;+ virtual T operator()(Attribute_Selector* x) = 0;+ virtual T operator()(Pseudo_Selector* x) = 0;+ virtual T operator()(Wrapped_Selector* x) = 0;+ virtual T operator()(Compound_Selector* x) = 0;+ virtual T operator()(Complex_Selector* x) = 0;+ virtual T operator()(Selector_List* x) = 0;++ template <typename U>+ T fallback(U x) { return T(); }+ };++ template <typename T, typename D>+ class Operation_CRTP : public Operation<T> {+ public:+ virtual T operator()(AST_Node* x) { return static_cast<D*>(this)->fallback(x); }+ virtual ~Operation_CRTP() = 0;+ // statements+ virtual T operator()(Block* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Ruleset* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Propset* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Bubble* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Feature_Block* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Media_Block* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(At_Root_Block* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(At_Rule* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Keyframe_Rule* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Declaration* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Assignment* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Import* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Import_Stub* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Warning* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Error* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Debug* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Comment* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(If* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(For* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Each* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(While* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Return* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Content* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Extension* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Definition* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Mixin_Call* x) { return static_cast<D*>(this)->fallback(x); }+ // expressions+ virtual T operator()(List* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Map* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Binary_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Unary_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Function_Call* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Function_Call_Schema* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Variable* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Textual* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Number* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Color* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Boolean* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(String_Schema* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(String_Constant* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Feature_Query* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Feature_Query_Condition* x){ return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Media_Query* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Media_Query_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(At_Root_Expression* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Null* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Parent_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ // parameters and arguments+ virtual T operator()(Parameter* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Parameters* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Argument* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Arguments* x) { return static_cast<D*>(this)->fallback(x); }+ // selectors+ virtual T operator()(Selector_Schema* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Selector_Reference* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Selector_Placeholder* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Type_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Selector_Qualifier* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Attribute_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Pseudo_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Wrapped_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Compound_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Complex_Selector* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Selector_List* x) { return static_cast<D*>(this)->fallback(x); }++ template <typename U>+ T fallback(U x) { return T(); }+ };+ template<typename T, typename D>+ inline Operation_CRTP<T, D>::~Operation_CRTP() { }++}++#endif
+ libsass/output.cpp view
@@ -0,0 +1,400 @@+#include "ast.hpp"+#include "output.hpp"+#include "to_string.hpp"++namespace Sass {+ using namespace std;++ Output::Output(Context* ctx)+ : Inspect(Emitter(ctx)),+ charset(""),+ top_imports(0),+ top_comments(0)+ {}++ Output::~Output() { }++ void Output::fallback_impl(AST_Node* n)+ {+ return n->perform(this);+ }++ void Output::operator()(Import* imp)+ {+ top_imports.push_back(imp);+ }++ OutputBuffer Output::get_buffer(void)+ {++ Emitter emitter(ctx);+ Inspect inspect(emitter);++ size_t size_com = top_comments.size();+ for (size_t i = 0; i < size_com; i++) {+ top_comments[i]->perform(&inspect);+ inspect.append_mandatory_linefeed();+ }++ size_t size_imp = top_imports.size();+ for (size_t i = 0; i < size_imp; i++) {+ top_imports[i]->perform(&inspect);+ inspect.append_mandatory_linefeed();+ }++ // flush scheduled outputs+ inspect.finalize();+ // prepend buffer on top+ prepend_output(inspect.output());+ // make sure we end with a linefeed+ if (!ends_with(wbuf.buffer, ctx->linefeed)) {+ // if the output is not completely empty+ if (!wbuf.buffer.empty()) append_string(ctx->linefeed);+ }++ // search for unicode char+ for(const char& chr : wbuf.buffer) {+ // skip all ascii chars+ if (chr >= 0) continue;+ // declare the charset+ if (output_style() != COMPRESSED)+ charset = "@charset \"UTF-8\";"+ + ctx->linefeed;+ else charset = "\xEF\xBB\xBF";+ // abort search+ break;+ }++ // add charset as first line, before comments and imports+ if (!charset.empty()) prepend_string(charset);++ return wbuf;++ }++ void Output::operator()(Comment* c)+ {+ To_String to_string(ctx);+ string txt = c->text()->perform(&to_string);+ // if (indentation && txt == "/**/") return;+ bool important = c->is_important();+ if (output_style() != COMPRESSED || important) {+ if (buffer().size() + top_imports.size() == 0) {+ top_comments.push_back(c);+ } else {+ in_comment = true;+ append_indentation();+ c->text()->perform(this);+ in_comment = false;+ if (indentation == 0) {+ append_mandatory_linefeed();+ } else {+ append_optional_linefeed();+ }+ }+ }+ }++ void Output::operator()(Ruleset* r)+ {+ Selector* s = r->selector();+ Block* b = r->block();+ bool decls = false;++ // Filter out rulesets that aren't printable (process its children though)+ if (!Util::isPrintable(r, output_style())) {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (dynamic_cast<Has_Block*>(stm)) {+ stm->perform(this);+ }+ }+ return;+ }++ if (b->has_non_hoistable()) {+ decls = true;+ if (output_style() == NESTED) indentation += r->tabs();+ if (ctx && ctx->source_comments) {+ stringstream ss;+ append_indentation();+ ss << "/* line " << r->pstate().line+1 << ", " << r->pstate().path << " */";+ append_string(ss.str());+ append_optional_linefeed();+ }+ s->perform(this);+ append_scope_opener(b);+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ bool bPrintExpression = true;+ // Check print conditions+ if (typeid(*stm) == typeid(Declaration)) {+ Declaration* dec = static_cast<Declaration*>(stm);+ if (dec->value()->concrete_type() == Expression::STRING) {+ String_Constant* valConst = static_cast<String_Constant*>(dec->value());+ string val(valConst->value());+ if (dynamic_cast<String_Quoted*>(valConst)) {+ if (!valConst->quote_mark() && val.empty()) {+ bPrintExpression = false;+ }+ }+ }+ else if (dec->value()->concrete_type() == Expression::LIST) {+ List* list = static_cast<List*>(dec->value());+ bool all_invisible = true;+ for (size_t list_i = 0, list_L = list->length(); list_i < list_L; ++list_i) {+ Expression* item = (*list)[list_i];+ if (!item->is_invisible()) all_invisible = false;+ }+ if (all_invisible) bPrintExpression = false;+ }+ }+ // Print if OK+ if (!stm->is_hoistable() && bPrintExpression) {+ stm->perform(this);+ }+ }+ if (output_style() == NESTED) indentation -= r->tabs();+ append_scope_closer(b);+ }++ if (b->has_hoistable()) {+ if (decls) ++indentation;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (stm->is_hoistable()) {+ stm->perform(this);+ }+ }+ if (decls) --indentation;+ }+ }++ void Output::operator()(Keyframe_Rule* r)+ {+ Block* b = r->block();+ Selector* v = r->selector();++ if (v) {+ v->perform(this);+ }++ if (!b) {+ append_colon_separator();+ return;+ }++ append_scope_opener();+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable()) {+ stm->perform(this);+ if (i < L - 1) append_special_linefeed();+ }+ }++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (stm->is_hoistable()) {+ stm->perform(this);+ }+ }++ append_scope_closer();+ }++ void Output::operator()(Feature_Block* f)+ {+ if (f->is_invisible()) return;++ Feature_Query* q = f->feature_queries();+ Block* b = f->block();++ // Filter out feature blocks that aren't printable (process its children though)+ if (!Util::isPrintable(f, output_style())) {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (dynamic_cast<Has_Block*>(stm)) {+ stm->perform(this);+ }+ }+ return;+ }++ if (output_style() == NESTED) indentation += f->tabs();+ append_indentation();+ append_token("@supports", f);+ append_mandatory_space();+ q->perform(this);+ append_scope_opener();++ Selector* e = f->selector();+ if (e && b->has_non_hoistable()) {+ // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable+ e->perform(this);+ append_scope_opener();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable()) {+ stm->perform(this);+ }+ }++ append_scope_closer();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (stm->is_hoistable()) {+ stm->perform(this);+ }+ }+ }+ else {+ // JMA - not hoisted, just output in order+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ stm->perform(this);+ if (i < L - 1) append_special_linefeed();+ }+ }++ if (output_style() == NESTED) indentation -= f->tabs();++ append_scope_closer();++ }++ void Output::operator()(Media_Block* m)+ {+ if (m->is_invisible()) return;++ List* q = m->media_queries();+ Block* b = m->block();++ // Filter out media blocks that aren't printable (process its children though)+ if (!Util::isPrintable(m, output_style())) {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (dynamic_cast<Has_Block*>(stm)) {+ stm->perform(this);+ }+ }+ return;+ }+ if (output_style() == NESTED) indentation += m->tabs();+ append_indentation();+ append_token("@media", m);+ append_mandatory_space();+ in_media_block = true;+ q->perform(this);+ in_media_block = false;+ append_scope_opener();++ Selector* e = m->selector();+ if (e && b->has_non_hoistable()) {+ // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable+ e->perform(this);+ append_scope_opener();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable()) {+ stm->perform(this);+ }+ }++ append_scope_closer();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (stm->is_hoistable()) {+ stm->perform(this);+ }+ }+ }+ else {+ // JMA - not hoisted, just output in order+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ stm->perform(this);+ if (i < L - 1) append_special_linefeed();+ }+ }++ if (output_style() == NESTED) indentation -= m->tabs();+ append_scope_closer();+ }++ void Output::operator()(At_Rule* a)+ {+ string kwd = a->keyword();+ Selector* s = a->selector();+ Expression* v = a->value();+ Block* b = a->block();++ append_indentation();+ append_token(kwd, a);+ if (s) {+ append_mandatory_space();+ in_wrapped = true;+ s->perform(this);+ in_wrapped = false;+ }+ else if (v) {+ append_mandatory_space();+ v->perform(this);+ }+ if (!b) {+ append_delimiter();+ return;+ }++ if (b->is_invisible() || b->length() == 0) {+ return append_string(" {}");+ }++ append_scope_opener();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable()) {+ stm->perform(this);+ if (i < L - 1) append_special_linefeed();+ }+ }++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (stm->is_hoistable()) {+ stm->perform(this);+ if (i < L - 1) append_special_linefeed();+ }+ }++ append_scope_closer();+ }++ void Output::operator()(String_Quoted* s)+ {+ if (s->quote_mark()) {+ append_token(quote(s->value(), s->quote_mark()), s);+ } else if (!in_comment) {+ append_token(string_to_output(s->value()), s);+ } else {+ append_token(s->value(), s);+ }+ }++ void Output::operator()(String_Constant* s)+ {+ if (String_Quoted* quoted = dynamic_cast<String_Quoted*>(s)) {+ return Output::operator()(quoted);+ } else if (!in_comment) {+ append_token(string_to_output(s->value()), s);+ } else {+ append_token(s->value(), s);+ }+ }++}
+ libsass/output.hpp view
@@ -0,0 +1,56 @@+#ifndef SASS_OUTPUT_H+#define SASS_OUTPUT_H++#include <string>+#include <vector>++#include "util.hpp"+#include "inspect.hpp"+#include "operation.hpp"++namespace Sass {+ class Context;+ using namespace std;++ // Refactor to make it generic to find linefeed (look behind)+ inline bool ends_with(std::string const & value, std::string const & ending)+ {+ if (ending.size() > value.size()) return false;+ return std::equal(ending.rbegin(), ending.rend(), value.rbegin());+ }++ class Output : public Inspect {+ protected:+ using Inspect::operator();++ public:+ // change to Emitter+ Output(Context* ctx);+ virtual ~Output();++ protected:+ string charset;+ vector<Import*> top_imports;+ vector<Comment*> top_comments;++ public:+ OutputBuffer get_buffer(void);++ virtual void operator()(Ruleset*);+ // virtual void operator()(Propset*);+ virtual void operator()(Feature_Block*);+ virtual void operator()(Media_Block*);+ virtual void operator()(At_Rule*);+ virtual void operator()(Keyframe_Rule*);+ virtual void operator()(Import*);+ virtual void operator()(Comment*);+ virtual void operator()(String_Quoted*);+ virtual void operator()(String_Constant*);++ void fallback_impl(AST_Node* n);++ };++}++#endif
+ libsass/parser.cpp view
@@ -0,0 +1,2228 @@+#include <cstdlib>+#include <iostream>+#include <vector>+#include "parser.hpp"+#include "file.hpp"+#include "inspect.hpp"+#include "to_string.hpp"+#include "constants.hpp"+#include "util.hpp"+#include "prelexer.hpp"+#include "sass_functions.h"++#include <typeinfo>++namespace Sass {+ using namespace std;+ using namespace Constants;++ Parser Parser::from_c_str(const char* str, Context& ctx, ParserState pstate)+ {+ Parser p(ctx, pstate);+ p.source = str;+ p.position = p.source;+ p.end = str + strlen(str);+ Block* root = 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 p(ctx, pstate);+ p.source = beg;+ p.position = p.source;+ p.end = end;+ Block* root = new (ctx.mem) Block(pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ bool Parser::peek_newline(const char* start)+ {+ return peek_linefeed(start ? start : position);+ }++ Parser Parser::from_token(Token t, Context& ctx, ParserState pstate)+ {+ Parser p(ctx, pstate);+ p.source = t.begin;+ p.position = p.source;+ p.end = t.end;+ Block* root = new (ctx.mem) Block(pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ Block* Parser::parse()+ {+ Block* root = new (ctx.mem) Block(pstate);+ block_stack.push_back(root);+ root->is_root(true);+ read_bom();++ if (ctx.queue.size() == 1) {+ Import* pre = new (ctx.mem) Import(pstate);+ string load_path(ctx.queue[0].load_path);+ do_import(load_path, pre, ctx.c_headers, false);+ ctx.head_imports = ctx.queue.size() - 1;+ if (!pre->urls().empty()) (*root) << pre;+ if (!pre->files().empty()) {+ for (size_t i = 0, S = pre->files().size(); i < S; ++i) {+ (*root) << new (ctx.mem) Import_Stub(pstate, pre->files()[i]);+ }+ }+ }++ lex< optional_spaces >();+ Selector_Lookahead lookahead_result;+ while (position < end) {+ parse_block_comments(root);+ if (peek< kwd_import >()) {+ Import* imp = parse_import();+ if (!imp->urls().empty()) (*root) << imp;+ if (!imp->files().empty()) {+ for (size_t i = 0, S = imp->files().size(); i < S; ++i) {+ (*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);+ }+ }+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'", pstate);+ }+ else if (peek< kwd_mixin >() || peek< kwd_function >()) {+ (*root) << parse_definition();+ }+ else if (peek< variable >()) {+ (*root) << parse_assignment();+ if (!lex< one_plus< exactly<';'> > >()) error("top-level variable binding must be terminated by ';'", pstate);+ }+ /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {+ (*root) << parse_propset();+ }*/+ else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {+ Mixin_Call* mixin_call = parse_mixin_call();+ (*root) << mixin_call;+ if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'", pstate);+ }+ else if (peek< kwd_if_directive >()) {+ (*root) << parse_if_directive();+ }+ else if (peek< kwd_for_directive >()) {+ (*root) << parse_for_directive();+ }+ else if (peek< kwd_each_directive >()) {+ (*root) << parse_each_directive();+ }+ else if (peek< kwd_while_directive >()) {+ (*root) << parse_while_directive();+ }+ else if (peek< kwd_media >()) {+ (*root) << parse_media_block();+ }+ else if (peek< kwd_at_root >()) {+ (*root) << parse_at_root_block();+ }+ else if (peek< kwd_supports >()) {+ (*root) << parse_feature_block();+ }+ else if (peek< kwd_warn >()) {+ (*root) << parse_warning();+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'", pstate);+ }+ else if (peek< kwd_err >()) {+ (*root) << parse_error();+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'", pstate);+ }+ else if (peek< kwd_dbg >()) {+ (*root) << parse_debug();+ if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'", pstate);+ }+ // ignore the @charset directive for now+ else if (lex< exactly< charset_kwd > >()) {+ lex< quoted_string >();+ lex< one_plus< exactly<';'> > >();+ }+ else if (peek< at_keyword >()) {+ At_Rule* at_rule = parse_at_rule();+ (*root) << at_rule;+ if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'", pstate);+ }+ else if ((lookahead_result = lookahead_for_selector(position)).found) {+ (*root) << parse_ruleset(lookahead_result);+ }+ else if (peek< exactly<';'> >()) {+ lex< one_plus< exactly<';'> > >();+ }+ else {+ lex< css_whitespace >();+ if (position >= end) break;+ error("invalid top-level expression", after_token);+ }+ lex< optional_spaces >();+ }+ block_stack.pop_back();+ return root;+ }++ void Parser::add_single_file (Import* imp, string import_path) {++ string extension;+ string unquoted(unquote(import_path));+ if (unquoted.length() > 4) { // 2 quote marks + the 4 chars in .css+ // a string constant is guaranteed to end with a quote mark, so make sure to skip it when indexing from the end+ extension = unquoted.substr(unquoted.length() - 4, 4);+ }++ if (extension == ".css") {+ String_Constant* loc = new (ctx.mem) String_Constant(pstate, unquote(import_path));+ Argument* loc_arg = new (ctx.mem) Argument(pstate, loc);+ Arguments* loc_args = new (ctx.mem) Arguments(pstate);+ (*loc_args) << loc_arg;+ Function_Call* new_url = new (ctx.mem) Function_Call(pstate, "url", loc_args);+ imp->urls().push_back(new_url);+ }+ else {+ string current_dir = File::dir_name(path);+ string resolved(ctx.add_file(current_dir, unquoted));+ if (resolved.empty()) error("file to import not found or unreadable: " + unquoted + "\nCurrent dir: " + current_dir, pstate);+ imp->files().push_back(resolved);+ }++ }++ void Parser::import_single_file (Import* imp, string import_path) {++ if (!unquote(import_path).substr(0, 7).compare("http://") ||+ !unquote(import_path).substr(0, 8).compare("https://") ||+ !unquote(import_path).substr(0, 2).compare("//"))+ {+ imp->urls().push_back(new (ctx.mem) String_Quoted(pstate, import_path));+ }+ else {+ add_single_file(imp, import_path);+ }++ }++ bool Parser::do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one)+ {+ bool has_import = false;+ string load_path = unquote(import_path);+ for (auto importer : importers) {+ // int priority = sass_importer_get_priority(importer);+ Sass_Importer_Fn fn = sass_importer_get_function(importer);+ if (Sass_Import_List includes =+ fn(load_path.c_str(), importer, ctx.c_compiler)+ ) {+ Sass_Import_List list = includes;+ while (*includes) {+ Sass_Import_Entry include = *includes;+ const char *file = sass_import_get_path(include);+ char* source = sass_import_take_source(include);+ size_t line = sass_import_get_error_line(include);+ size_t column = sass_import_get_error_column(include);+ const char* message = sass_import_get_error_message(include);+ if (message) {+ if (line == string::npos && column == string::npos) error(message, pstate);+ else error(message, ParserState(message, source, Position(line, column)));+ } else if (source) {+ if (file) {+ ctx.add_source(file, load_path, source);+ imp->files().push_back(file);+ } else {+ ctx.add_source(load_path, load_path, source);+ imp->files().push_back(load_path);+ }+ } else if(file) {+ import_single_file(imp, file);+ }+ ++includes;+ }+ // deallocate returned memory+ sass_delete_import_list(list);+ // set success flag+ has_import = true;+ // break import chain+ if (only_one) return true;+ }+ }+ // return result+ return has_import;+ }++ Import* Parser::parse_import()+ {+ lex< kwd_import >();+ Import* imp = new (ctx.mem) Import(pstate);+ bool first = true;+ do {+ while (lex< block_comment >());+ if (lex< quoted_string >()) {+ if (!do_import(lexed, imp, ctx.c_importers, true))+ {+ // push single file import+ import_single_file(imp, lexed);+ }+ }+ else if (lex< uri_prefix >()) {+ Arguments* args = new (ctx.mem) Arguments(pstate);+ Function_Call* result = new (ctx.mem) Function_Call(pstate, "url", args);+ if (lex < uri_value >()) { // chunk seems to work too!+ String* the_url = parse_interpolated_chunk(lexed);+ *args << new (ctx.mem) Argument(the_url->pstate(), the_url);+ }+ else {+ error("malformed URL", pstate);+ }+ if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);+ imp->urls().push_back(result);+ }+ else {+ if (first) error("@import directive requires a url or quoted path", pstate);+ else error("expecting another url or quoted path in @import list", pstate);+ }+ first = false;+ } while (lex_css< exactly<','> >());+ return imp;+ }++ Definition* Parser::parse_definition()+ {+ Definition::Type which_type = Definition::MIXIN;+ if (lex< kwd_mixin >()) which_type = Definition::MIXIN;+ else if (lex< kwd_function >()) which_type = Definition::FUNCTION;+ string which_str(lexed);+ if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);+ string name(Util::normalize_underscores(lexed));+ if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))+ { error("Invalid function name \"" + name + "\".", pstate); }+ ParserState source_position_of_def = pstate;+ Parameters* params = parse_parameters();+ if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'", pstate);+ if (which_type == Definition::MIXIN) stack.push_back(mixin_def);+ else stack.push_back(function_def);+ Block* body = parse_block();+ stack.pop_back();+ Definition* def = new (ctx.mem) Definition(source_position_of_def, name, params, body, &ctx, which_type);+ return def;+ }++ Parameters* Parser::parse_parameters()+ {+ string name(lexed);+ Position position = after_token;+ Parameters* params = new (ctx.mem) Parameters(pstate);+ if (lex_css< exactly<'('> >()) {+ // if there's anything there at all+ if (!peek_css< exactly<')'> >()) {+ do (*params) << parse_parameter();+ while (lex_css< exactly<','> >());+ }+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);+ }+ return params;+ }++ Parameter* Parser::parse_parameter()+ {+ while (lex< alternatives < spaces, block_comment > >());+ lex< variable >();+ string name(Util::normalize_underscores(lexed));+ ParserState pos = pstate;+ Expression* val = 0;+ bool is_rest = false;+ while (lex< alternatives < spaces, block_comment > >());+ if (lex< exactly<':'> >()) { // there's a default value+ while (lex< block_comment >());+ val = parse_space_list();+ val->is_delayed(false);+ }+ else if (lex< exactly< ellipsis > >()) {+ is_rest = true;+ }+ Parameter* p = new (ctx.mem) Parameter(pos, name, val, is_rest);+ return p;+ }++ Mixin_Call* Parser::parse_mixin_call()+ {+ lex< kwd_include >() /* || lex< exactly<'+'> >() */;+ if (!lex< identifier >()) error("invalid name in @include directive", pstate);+ ParserState source_position_of_call = pstate;+ string name(Util::normalize_underscores(lexed));+ Arguments* args = parse_arguments();+ Block* content = 0;+ if (peek< exactly<'{'> >()) {+ content = parse_block();+ }+ Mixin_Call* the_call = new (ctx.mem) Mixin_Call(source_position_of_call, name, args, content);+ return the_call;+ }++ Arguments* Parser::parse_arguments(bool has_url)+ {+ string name(lexed);+ Position position = after_token;+ Arguments* args = new (ctx.mem) Arguments(pstate);+ if (lex_css< exactly<'('> >()) {+ // if there's anything there at all+ if (!peek_css< exactly<')'> >()) {+ do (*args) << parse_argument(has_url);+ while (lex_css< exactly<','> >());+ }+ if (!lex_css< exactly<')'> >()) error("expected a variable name (e.g. $x) or ')' for the parameter list for " + name, position);+ }+ return args;+ }++ Argument* Parser::parse_argument(bool has_url)+ {++ Argument* arg;+ // some urls can look like line comments (parse literally - chunk would not work)+ if (has_url && lex< sequence < uri_value, lookahead < loosely<')'> > > >(false)) {+ String* the_url = parse_interpolated_chunk(lexed);+ arg = new (ctx.mem) Argument(the_url->pstate(), the_url);+ }+ else if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {+ lex_css< variable >();+ string name(Util::normalize_underscores(lexed));+ ParserState p = pstate;+ lex_css< exactly<':'> >();+ Expression* val = parse_space_list();+ val->is_delayed(false);+ arg = new (ctx.mem) Argument(p, val, name);+ }+ else {+ bool is_arglist = false;+ bool is_keyword = false;+ Expression* val = parse_space_list();+ val->is_delayed(false);+ if (lex_css< exactly< ellipsis > >()) {+ if (val->concrete_type() == Expression::MAP) is_keyword = true;+ else is_arglist = true;+ }+ arg = new (ctx.mem) Argument(pstate, val, "", is_arglist, is_keyword);+ }+ return arg;+ }++ Assignment* Parser::parse_assignment()+ {+ lex< variable >();+ string name(Util::normalize_underscores(lexed));+ ParserState var_source_position = pstate;+ if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);+ Expression* val = parse_list();+ val->is_delayed(false);+ bool is_default = false;+ bool is_global = false;+ while (peek< default_flag >() || peek< global_flag >()) {+ is_default = lex< default_flag >() || is_default;+ is_global = lex< global_flag >() || is_global;+ }+ Assignment* var = new (ctx.mem) Assignment(var_source_position, name, val, is_default, is_global);+ return var;+ }++ /* not used anymore - remove?+ Propset* Parser::parse_propset()+ {+ String* property_segment;+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {+ property_segment = parse_identifier_schema();+ }+ else {+ lex< sequence< optional< exactly<'*'> >, identifier > >();+ property_segment = new (ctx.mem) String_Quoted(pstate, lexed);+ }+ Propset* propset = new (ctx.mem) Propset(pstate, property_segment);+ lex< exactly<':'> >();++ if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property", pstate);++ propset->block(parse_block());++ propset->tabs(indentation);++ return propset;+ } */++ Ruleset* Parser::parse_ruleset(Selector_Lookahead lookahead)+ {+ Selector* sel;+ if (lookahead.has_interpolants) {+ sel = parse_selector_schema(lookahead.found);+ }+ else {+ sel = parse_selector_group();+ }+ bool old_in_at_root = in_at_root;+ lex < css_comments >();+ in_at_root = false;+ ParserState r_source_position = pstate;+ if (!peek< exactly<'{'> >()) error("expected a '{' after the selector", pstate);+ Block* block = parse_block();+ in_at_root = old_in_at_root;+ old_in_at_root = false;+ Ruleset* ruleset = new (ctx.mem) Ruleset(r_source_position, sel, block);+ return ruleset;+ }++ Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)+ {+ lex< optional_spaces >();+ const char* i = position;+ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ while (i < end_of_selector) {+ // try to parse mutliple interpolants+ if (const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector)) {+ // accumulate the preceding segment if the position has advanced+ if (i < p) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));+ // skip to the delimiter by skipping occurences in quoted strings+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);+ Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();+ interpolant->is_interpolant(true);+ (*schema) << interpolant;+ i = j;+ }+ // no more interpolants have been found+ // add the last segment if there is one+ else {+ if (i < end_of_selector) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, end_of_selector));+ break;+ }+ }+ position = end_of_selector;+ Selector_Schema* selector_schema = new (ctx.mem) Selector_Schema(pstate, schema);+ selector_schema->media_block(last_media_block);+ selector_schema->last_block(block_stack.back());+ return selector_schema;+ }++ Selector_List* Parser::parse_selector_group()+ {+ bool reloop = true;+ To_String to_string(&ctx);+ lex< css_whitespace >();+ Selector_List* group = new (ctx.mem) Selector_List(pstate);+ group->media_block(last_media_block);+ group->last_block(block_stack.back());+ do {+ reloop = false;+ if (peek< alternatives <+ exactly<'{'>,+ exactly<'}'>,+ exactly<')'>,+ exactly<';'>+ > >())+ break; // in case there are superfluous commas at the end+ Complex_Selector* comb = parse_selector_combination();+ if (!comb->has_reference() && !in_at_root) {+ ParserState sel_source_position = pstate;+ Selector_Reference* ref = new (ctx.mem) Selector_Reference(sel_source_position);+ Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(sel_source_position);+ ref_wrap->media_block(last_media_block);+ ref_wrap->last_block(block_stack.back());+ (*ref_wrap) << ref;+ if (!comb->head()) {+ comb->head(ref_wrap);+ comb->has_reference(true);+ }+ else {+ comb = new (ctx.mem) Complex_Selector(sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb);+ comb->media_block(last_media_block);+ comb->last_block(block_stack.back());+ comb->has_reference(true);+ }+ if (peek_newline()) ref_wrap->has_line_break(true);+ }+ while (peek_css< exactly<','> >())+ {+ // consume everything up and including the comma speparator+ reloop = lex< sequence < optional_css_comments, exactly<','> > >() != 0;+ // remember line break (also between some commas)+ if (peek_newline()) comb->has_line_feed(true);+ if (comb->tail() && peek_newline()) comb->tail()->has_line_feed(true);+ if (comb->tail() && comb->tail()->head() && peek_newline()) comb->tail()->head()->has_line_feed(true);+ // remember line break (also between some commas)+ }+ (*group) << comb;+ }+ while (reloop);+ while (lex< optional >()) {+ group->is_optional(true);+ }+ return group;+ }++ Complex_Selector* Parser::parse_selector_combination()+ {+ Position sel_source_position(-1);+ Compound_Selector* lhs;+ if (peek_css< alternatives <+ exactly<'+'>,+ exactly<'~'>,+ exactly<'>'>+ > >())+ // no selector before the combinator+ { lhs = 0; }+ else {+ lhs = parse_simple_selector_sequence();+ sel_source_position = before_token;+ lhs->has_line_break(peek_newline());+ }++ Complex_Selector::Combinator cmb;+ if (lex< exactly<'+'> >()) cmb = Complex_Selector::ADJACENT_TO;+ else if (lex< exactly<'~'> >()) cmb = Complex_Selector::PRECEDES;+ else if (lex< exactly<'>'> >()) cmb = Complex_Selector::PARENT_OF;+ else cmb = Complex_Selector::ANCESTOR_OF;+ bool cpx_lf = peek_newline();++ Complex_Selector* rhs;+ if (peek_css< alternatives <+ exactly<','>,+ exactly<')'>,+ exactly<'{'>,+ exactly<'}'>,+ exactly<';'>,+ optional+ > >())+ // no selector after the combinator+ { rhs = 0; }+ else {+ rhs = parse_selector_combination();+ sel_source_position = before_token;+ }+ if (!sel_source_position.line) sel_source_position = before_token;+ Complex_Selector* cpx = new (ctx.mem) Complex_Selector(ParserState(path, source, sel_source_position), cmb, lhs, rhs);+ cpx->media_block(last_media_block);+ cpx->last_block(block_stack.back());+ if (cpx_lf) cpx->has_line_break(cpx_lf);+ return cpx;+ }++ Compound_Selector* Parser::parse_simple_selector_sequence()+ {+ Compound_Selector* seq = new (ctx.mem) Compound_Selector(pstate);+ seq->media_block(last_media_block);+ seq->last_block(block_stack.back());+ bool sawsomething = false;+ if (lex< exactly<'&'> >()) {+ // check if we have a parent selector on the root level block+ if (block_stack.back() && block_stack.back()->is_root()) {+ //error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);+ }+ (*seq) << new (ctx.mem) Selector_Reference(pstate);+ sawsomething = true;+ // if you see a space after a &, then you're done+ if(peek< spaces >() || peek< alternatives < spaces, exactly<';'> > >()) {+ return seq;+ }+ }+ if (sawsomething && lex_css< sequence< negate< functional >, alternatives< identifier_alnums, universal, quoted_string, dimension, percentage, number > > >()) {+ // saw an ampersand, then allow type selectors with arbitrary number of hyphens at the beginning+ (*seq) << new (ctx.mem) Type_Selector(pstate, unquote(lexed));+ } else if (lex_css< sequence< negate< functional >, alternatives< type_selector, universal, quoted_string, dimension, percentage, number > > >()) {+ // if you see a type selector+ (*seq) << new (ctx.mem) Type_Selector(pstate, lexed);+ sawsomething = true;+ }+ if (!sawsomething) {+ // don't blindly do this if you saw a & or selector+ (*seq) << parse_simple_selector();+ }++ while (!peek< spaces >(position) &&+ !(peek_css < alternatives <+ exactly<'+'>,+ exactly<'~'>,+ exactly<'>'>,+ exactly<','>,+ exactly<')'>,+ exactly<'{'>,+ exactly<'}'>,+ exactly<';'>+ > >(position))) {+ (*seq) << parse_simple_selector();+ }+ return seq;+ }++ Simple_Selector* Parser::parse_simple_selector()+ {+ lex < css_comments >();+ if (lex< alternatives < id_name, class_name > >()) {+ return new (ctx.mem) Selector_Qualifier(pstate, unquote(lexed));+ }+ else if (lex< quoted_string >()) {+ return new (ctx.mem) Type_Selector(pstate, unquote(lexed));+ }+ else if (lex< alternatives < number, kwd_sel_deep > >()) {+ return new (ctx.mem) Type_Selector(pstate, lexed);+ }+ else if (peek< pseudo_not >()) {+ return parse_negated_selector();+ }+ else if (peek< exactly<':'> >(position) || peek< functional >()) {+ return parse_pseudo_selector();+ }+ else if (peek< exactly<'['> >(position)) {+ return parse_attribute_selector();+ }+ else if (lex< placeholder >()) {+ Selector_Placeholder* sel = new (ctx.mem) Selector_Placeholder(pstate, unquote(lexed));+ sel->media_block(last_media_block);+ sel->last_block(block_stack.back());+ return sel;+ }+ else {+ error("invalid selector after " + lexed.to_string(), pstate);+ }+ // unreachable statement+ return 0;+ }++ Wrapped_Selector* Parser::parse_negated_selector()+ {+ lex< pseudo_not >();+ string name(lexed);+ ParserState nsource_position = pstate;+ Selector* negated = parse_selector_group();+ if (!lex< exactly<')'> >()) {+ error("negated selector is missing ')'", pstate);+ }+ return new (ctx.mem) Wrapped_Selector(nsource_position, name, negated);+ }++ Simple_Selector* Parser::parse_pseudo_selector() {+ if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) {+ string name(lexed);+ String* expr = 0;+ ParserState p = pstate;+ Selector* wrapped = 0;+ if (lex< alternatives< even, odd > >()) {+ expr = new (ctx.mem) String_Quoted(p, lexed);+ }+ else if (peek< binomial >(position)) {+ lex< sequence< optional< coefficient >, exactly<'n'> > >();+ String_Constant* var_coef = new (ctx.mem) String_Quoted(p, lexed);+ lex< sign >();+ String_Constant* op = new (ctx.mem) String_Quoted(p, lexed);+ // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);+ lex< one_plus < digit > >();+ String_Constant* constant = new (ctx.mem) String_Quoted(p, lexed);+ // expr = new (ctx.mem) Binary_Expression(p, op, var_coef, constant);+ String_Schema* schema = new (ctx.mem) String_Schema(p, 3);+ *schema << var_coef << op << constant;+ expr = schema;+ }+ else if (peek< sequence< optional<sign>,+ zero_plus<digit>,+ exactly<'n'>,+ optional_css_whitespace,+ exactly<')'> > >()) {+ lex< sequence< optional<sign>,+ zero_plus<digit>,+ exactly<'n'> > >();+ expr = new (ctx.mem) String_Quoted(p, lexed);+ }+ else if (lex< sequence< optional<sign>, one_plus < digit > > >()) {+ expr = new (ctx.mem) String_Quoted(p, lexed);+ }+ else if (peek< sequence< identifier, optional_css_whitespace, exactly<')'> > >()) {+ lex< identifier >();+ expr = new (ctx.mem) String_Quoted(p, lexed);+ }+ else if (lex< quoted_string >()) {+ expr = new (ctx.mem) String_Quoted(p, lexed);+ }+ else if (peek< exactly<')'> >()) {+ expr = new (ctx.mem) String_Constant(p, "");+ }+ else {+ wrapped = parse_selector_group();+ }+ if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)", pstate);+ if (wrapped) {+ return new (ctx.mem) Wrapped_Selector(p, name, wrapped);+ }+ return new (ctx.mem) Pseudo_Selector(p, name, expr);+ }+ else if (lex < sequence< pseudo_prefix, identifier > >()) {+ return new (ctx.mem) Pseudo_Selector(pstate, unquote(lexed));+ }+ else {+ error("unrecognized pseudo-class or pseudo-element", pstate);+ }+ // unreachable statement+ return 0;+ }++ Attribute_Selector* Parser::parse_attribute_selector()+ {+ lex_css< exactly<'['> >();+ ParserState p = pstate;+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);+ string name(lexed);+ if (lex_css< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(p, name, "", 0);+ if (!lex_css< alternatives< exact_match, class_match, dash_match,+ prefix_match, suffix_match, substring_match > >()) {+ error("invalid operator in attribute selector for " + name, pstate);+ }+ string matcher(lexed);++ String* value = 0;+ if (lex_css< identifier >()) {+ value = new (ctx.mem) String_Constant(p, lexed);+ }+ else if (lex_css< quoted_string >()) {+ value = parse_interpolated_chunk(lexed, true); // needed!+ }+ else {+ error("expected a string constant or identifier in attribute selector for " + name, pstate);+ }++ if (!lex_css< exactly<']'> >()) error("unterminated attribute selector for " + name, pstate);+ return new (ctx.mem) Attribute_Selector(p, name, matcher, value);+ }++ /* parse block comment and add to block */+ void Parser::parse_block_comments(Block* block)+ {+ while (lex< block_comment >()) {+ bool is_important = lexed.begin[2] == '!';+ String* contents = parse_interpolated_chunk(lexed);+ (*block) << new (ctx.mem) Comment(pstate, contents, is_important);+ }+ }++ Block* Parser::parse_block()+ {+ lex< exactly<'{'> >();+ bool semicolon = false;+ Selector_Lookahead lookahead_result;+ Block* block = new (ctx.mem) Block(pstate);+ block_stack.push_back(block);+ lex< zero_plus < alternatives < space, line_comment > > >();+ // JMA - ensure that a block containing only block_comments is parsed+ parse_block_comments(block);++ while (!lex< exactly<'}'> >()) {+ parse_block_comments(block);+ if (semicolon) {+ if (!lex< one_plus< exactly<';'> > >()) {+ error("non-terminal statement or declaration must end with ';'", pstate);+ }+ semicolon = false;+ parse_block_comments(block);+ if (lex< sequence< exactly<'}'>, zero_plus< exactly<';'> > > >()) break;+ }+ else if (peek< kwd_import >(position)) {+ if (stack.back() == mixin_def || stack.back() == function_def) {+ lex< kwd_import >(); // to adjust the before_token number+ error("@import directives are not allowed inside mixins and functions", pstate);+ }+ Import* imp = parse_import();+ if (!imp->urls().empty()) (*block) << imp;+ if (!imp->files().empty()) {+ for (size_t i = 0, S = imp->files().size(); i < S; ++i) {+ (*block) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);+ }+ }+ semicolon = true;+ }+ else if (lex< variable >()) {+ (*block) << parse_assignment();+ semicolon = true;+ }+ else if (lex< line_comment >()) {+ // throw line comments away+ }+ else if (peek< kwd_if_directive >()) {+ (*block) << parse_if_directive();+ }+ else if (peek< kwd_for_directive >()) {+ (*block) << parse_for_directive();+ }+ else if (peek< kwd_each_directive >()) {+ (*block) << parse_each_directive();+ }+ else if (peek < kwd_while_directive >()) {+ (*block) << parse_while_directive();+ }+ else if (lex < kwd_return_directive >()) {+ (*block) << new (ctx.mem) Return(pstate, parse_list());+ semicolon = true;+ }+ else if (peek< kwd_warn >()) {+ (*block) << parse_warning();+ semicolon = true;+ }+ else if (peek< kwd_err >()) {+ (*block) << parse_error();+ semicolon = true;+ }+ else if (peek< kwd_dbg >()) {+ (*block) << parse_debug();+ semicolon = true;+ }+ else if (stack.back() == function_def) {+ error("only variable declarations and control directives are allowed inside functions", pstate);+ }+ else if (peek< kwd_mixin >() || peek< kwd_function >()) {+ (*block) << parse_definition();+ }+ else if (peek< kwd_include >(position)) {+ Mixin_Call* the_call = parse_mixin_call();+ (*block) << the_call;+ // don't need a semicolon after a content block+ semicolon = (the_call->block()) ? false : true;+ }+ else if (lex< kwd_content >()) {+ if (stack.back() != mixin_def) {+ error("@content may only be used within a mixin", pstate);+ }+ (*block) << new (ctx.mem) Content(pstate);+ semicolon = true;+ }+ /*+ else if (peek< exactly<'+'> >()) {+ (*block) << parse_mixin_call();+ semicolon = true;+ }+ */+ else if (lex< kwd_extend >()) {+ Selector_Lookahead lookahead = lookahead_for_extension_target(position);+ if (!lookahead.found) error("invalid selector for @extend", pstate);+ Selector* target;+ if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);+ else target = parse_selector_group();+ (*block) << new (ctx.mem) Extension(pstate, target);+ semicolon = true;+ }+ else if (peek< kwd_media >()) {+ (*block) << parse_media_block();+ }+ else if (peek< kwd_supports >()) {+ (*block) << parse_feature_block();+ }+ else if (peek< kwd_at_root >()) {+ (*block) << parse_at_root_block();+ }+ // ignore the @charset directive for now+ else if (lex< exactly< charset_kwd > >()) {+ lex< quoted_string >();+ lex< one_plus< exactly<';'> > >();+ }+ else if (peek< at_keyword >()) {+ At_Rule* at_rule = parse_at_rule();+ (*block) << at_rule;+ if (!at_rule->block()) semicolon = true;+ }+ else if ((lookahead_result = lookahead_for_selector(position)).found) {+ (*block) << parse_ruleset(lookahead_result);+ }/* not used anymore - remove?+ else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {+ (*block) << parse_propset();+ }*/+ else if (!peek< exactly<';'> >()) {+ bool indent = ! peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position);+ /* not used anymore - remove?+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema, exactly<':'>, exactly<'{'> > >()) {+ (*block) << parse_propset();+ }+ else if (peek< sequence< optional< exactly<'*'> >, identifier, exactly<':'>, exactly<'{'> > >()) {+ (*block) << parse_propset();+ }+ else */ {+ Declaration* decl = parse_declaration();+ decl->tabs(indentation);+ (*block) << decl;+ if (peek< exactly<'{'> >()) {+ // parse a propset that rides on the declaration's property+ if (indent) indentation++;+ Propset* ps = new (ctx.mem) Propset(pstate, decl->property(), parse_block());+ if (indent) indentation--;+ (*block) << ps;+ }+ else {+ // finish and let the semicolon get munched+ semicolon = true;+ }+ }+ }+ else lex< one_plus< exactly<';'> > >();+ parse_block_comments(block);+ }+ block_stack.pop_back();+ return block;+ }++ Declaration* Parser::parse_declaration() {+ String* prop = 0;+ if (peek< sequence< optional< exactly<'*'> >, identifier_schema > >()) {+ prop = parse_identifier_schema();+ }+ else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {+ prop = new (ctx.mem) String_Quoted(pstate, lexed);+ }+ else {+ error("invalid property name", pstate);+ }+ const string property(lexed);+ if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);+ if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);+ if (peek_css< static_value >()) {+ return new (ctx.mem) Declaration(prop->pstate(), prop, parse_static_value()/*, lex<important>()*/);+ }+ else {+ Expression* list_ex = parse_list();+ if (List* list = dynamic_cast<List*>(list_ex)) {+ if (list->length() == 0 && !peek< exactly <'{'> >()) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ }+ return new (ctx.mem) Declaration(prop->pstate(), prop, list_ex/*, lex<important>()*/);+ }+ }++ // parse +/- and return false if negative+ bool Parser::parse_number_prefix()+ {+ bool positive = true;+ while(true) {+ if (lex < block_comment >()) continue;+ if (lex < number_prefix >()) continue;+ if (lex < exactly < '-' > >()) {+ positive = !positive;+ continue;+ }+ break;+ }+ return positive;+ }++ Expression* Parser::parse_map()+ {+ To_String to_string(&ctx);+ Expression* key = parse_list();++ // it's not a map so return the lexed value as a list value+ if (!peek< exactly<':'> >())+ { return key; }++ lex< exactly<':'> >();++ Expression* value = parse_space_list();++ Map* map = new (ctx.mem) Map(pstate, 1);+ (*map) << make_pair(key, value);++ while (lex_css< exactly<','> >())+ {+ // allow trailing commas - #495+ if (peek_css< exactly<')'> >(position))+ { break; }++ Expression* key = parse_list();++ if (!(lex< exactly<':'> >()))+ { error("invalid syntax", pstate); }++ Expression* value = parse_space_list();++ (*map) << make_pair(key, value);+ }++ if (map->has_duplicate_key())+ { error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + ".", pstate); }++ return map;+ }++ Expression* Parser::parse_list()+ {+ return parse_comma_list();+ }++ Expression* Parser::parse_comma_list()+ {+ if (peek< alternatives <+ // exactly<'!'>,+ // exactly<':'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<ellipsis>+ > >(position))+ { return new (ctx.mem) List(pstate, 0); }+ Expression* list1 = parse_space_list();+ // if it's a singleton, return it directly; don't wrap it+ if (!peek< exactly<','> >(position)) return list1;++ List* comma_list = new (ctx.mem) List(pstate, 2, List::COMMA);+ (*comma_list) << list1;++ while (lex< exactly<','> >())+ {+ if (peek< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<':'>,+ exactly<ellipsis>+ > >(position)+ ) { break; }+ Expression* list = parse_space_list();+ (*comma_list) << list;+ }++ return comma_list;+ }++ Expression* Parser::parse_space_list()+ {+ Expression* disj1 = parse_disjunction();+ // if it's a singleton, return it directly; don't wrap it+ if (peek< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<','>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position)+ ) { return disj1; }++ List* space_list = new (ctx.mem) List(pstate, 2, List::SPACE);+ (*space_list) << disj1;++ while (!(peek< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<','>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position))+ ) {+ (*space_list) << parse_disjunction();+ }++ return space_list;+ }++ Expression* Parser::parse_disjunction()+ {+ Expression* conj1 = parse_conjunction();+ // if it's a singleton, return it directly; don't wrap it+ if (!peek_css< kwd_or >()) return conj1;++ vector<Expression*> operands;+ while (lex_css< kwd_or >())+ operands.push_back(parse_conjunction());++ return fold_operands(conj1, operands, Binary_Expression::OR);+ }++ Expression* Parser::parse_conjunction()+ {+ Expression* rel1 = parse_relation();+ // if it's a singleton, return it directly; don't wrap it+ if (!peek_css< kwd_and >()) return rel1;++ vector<Expression*> operands;+ while (lex_css< kwd_and >())+ operands.push_back(parse_relation());++ return fold_operands(rel1, operands, Binary_Expression::AND);+ }++ Expression* Parser::parse_relation()+ {+ Expression* expr1 = parse_expression();+ // if it's a singleton, return it directly; don't wrap it+ if (!(peek< alternatives <+ kwd_eq,+ kwd_neq,+ kwd_gte,+ kwd_gt,+ kwd_lte,+ kwd_lt+ > >(position)))+ { return expr1; }++ Binary_Expression::Type op+ = lex<kwd_eq>() ? Binary_Expression::EQ+ : lex<kwd_neq>() ? Binary_Expression::NEQ+ : lex<kwd_gte>() ? Binary_Expression::GTE+ : lex<kwd_lte>() ? Binary_Expression::LTE+ : lex<kwd_gt>() ? Binary_Expression::GT+ : lex<kwd_lt>() ? Binary_Expression::LT+ : Binary_Expression::LT; // whatever++ Expression* expr2 = parse_expression();++ return new (ctx.mem) Binary_Expression(expr1->pstate(), op, expr1, expr2);+ }++ Expression* Parser::parse_expression()+ {+ Expression* term1 = parse_term();+ // if it's a singleton, return it directly; don't wrap it+ if (!(peek< exactly<'+'> >(position) ||+ (peek< 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))+ { return term1; }++ vector<Expression*> operands;+ vector<Binary_Expression::Type> operators;+ while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {+ operators.push_back(lexed.to_string() == "+" ? Binary_Expression::ADD : Binary_Expression::SUB);+ operands.push_back(parse_term());+ }++ return fold_operands(term1, operands, operators);+ }++ Expression* Parser::parse_term()+ {+ Expression* factor = parse_factor();+ // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant+ if (peek_css< exactly<'%'> >(position) && factor->concrete_type() == Expression::STRING) {+ String_Schema* ss = dynamic_cast<String_Schema*>(factor);+ if (ss && ss->has_interpolants()) return factor;+ }+ // if it's a singleton, return it directly; don't wrap it+ if (!peek< class_char< static_ops > >(position)) return factor;+ // parse more factors and operators+ vector<Expression*> operands; // factors+ vector<Binary_Expression::Type> operators; // ops+ while (lex_css< class_char< static_ops > >()) {+ switch(*lexed.begin) {+ case '*': operators.push_back(Binary_Expression::MUL); break;+ case '/': operators.push_back(Binary_Expression::DIV); break;+ case '%': operators.push_back(Binary_Expression::MOD); break;+ default: throw runtime_error("unknown static op parsed"); break;+ }+ operands.push_back(parse_factor());+ }+ // operands and operators to binary expression+ return fold_operands(factor, operands, operators);+ }++ Expression* Parser::parse_factor()+ {+ if (lex< exactly<'('> >()) {+ Expression* value = parse_map();+ if (!lex< exactly<')'> >()) error("unclosed parenthesis", pstate);+ 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<List*>(value);+ if (!l->empty()) (*l)[0]->is_delayed(false);+ } else if (typeid(*value) == typeid(Binary_Expression)) {+ Binary_Expression* b = static_cast<Binary_Expression*>(value);+ Binary_Expression* lhs = static_cast<Binary_Expression*>(b->left());+ if (lhs && lhs->type() == Binary_Expression::DIV) lhs->is_delayed(false);+ }+ return value;+ }+ else if (peek< ie_property >()) {+ return parse_ie_property();+ }+ else if (peek< ie_keyword_arg >()) {+ return parse_ie_keyword_arg();+ }+ else if (peek< exactly< calc_kwd > >() ||+ peek< exactly< moz_calc_kwd > >() ||+ peek< exactly< ms_calc_kwd > >() ||+ peek< exactly< webkit_calc_kwd > >()) {+ return parse_calc_function();+ }+ else if (peek< functional_schema >()) {+ return parse_function_call_schema();+ }+ else if (peek< sequence< identifier_schema, negate< exactly<'%'> > > >()) {+ return parse_identifier_schema();+ }+ else if (peek< functional >()) {+ return parse_function_call();+ }+ else if (lex< sequence< exactly<'+'>, optional_css_whitespace, negate< number > > >()) {+ return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::PLUS, parse_factor());+ }+ else if (lex< sequence< exactly<'-'>, optional_css_whitespace, negate< number> > >()) {+ return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_factor());+ }+ else if (lex< sequence< kwd_not, css_whitespace > >()) {+ return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::NOT, parse_factor());+ }+ else if (peek < sequence < one_plus < alternatives < css_whitespace, exactly<'-'>, exactly<'+'> > >, number > >()) {+ if (parse_number_prefix()) return parse_value(); // prefix is positive+ return new (ctx.mem) Unary_Expression(pstate, Unary_Expression::MINUS, parse_value());+ }+ else {+ return parse_value();+ }+ }++ Expression* Parser::parse_value()+ {+ lex< css_comments >();+ if (lex< ampersand >())+ {+ return new (ctx.mem) Parent_Selector(pstate, parse_selector_group()); }++ if (lex< important >())+ { return new (ctx.mem) String_Constant(pstate, "!important"); }++ const char* stop;+ if ((stop = peek< value_schema >()))+ { return parse_value_schema(stop); }++ if (lex< kwd_true >())+ { return new (ctx.mem) Boolean(pstate, true); }++ if (lex< kwd_false >())+ { return new (ctx.mem) Boolean(pstate, false); }++ if (lex< kwd_null >())+ { return new (ctx.mem) Null(pstate); }++ if (lex< identifier >()) {+ String_Constant* str = new (ctx.mem) String_Quoted(pstate, lexed);+ // Dont' delay this string if it is a name color. Fixes #652.+ str->is_delayed(ctx.names_to_colors.count(unquote(lexed)) == 0);+ return str;+ }++ if (lex< percentage >())+ { return new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed); }++ // match hex number first because 0x000 looks like a number followed by an indentifier+ if (lex< alternatives< hex, hex0 > >())+ { return new (ctx.mem) Textual(pstate, Textual::HEX, lexed); }++ // also handle the 10em- foo special case+ if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())+ { return new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed); }++ if (lex< number >())+ { return new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed); }++ if (peek< quoted_string >())+ { return parse_string(); }++ if (lex< variable >())+ { return new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed)); }++ // Special case handling for `%` proceeding an interpolant.+ if (lex< sequence< exactly<'%'>, optional< percentage > > >())+ { return new (ctx.mem) String_Quoted(pstate, lexed); }++ error("error reading values after " + lexed.to_string(), pstate);++ // unreachable statement+ return 0;+ }++ // this parses interpolation inside other strings+ // means the result should later be quoted again+ String* Parser::parse_interpolated_chunk(Token chunk, bool constant)+ {+ const char* i = chunk.begin;+ // see if there any interpolants+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);+ if (!p) {+ String_Quoted* str_quoted = new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));+ if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');+ str_quoted->is_delayed(true);+ return str_quoted;+ }++ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ while (i < chunk.end) {+ p = find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end);+ if (p) {+ if (i < p) {+ // accumulate the preceding segment if it's nonempty+ (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, p));+ }+ // we need to skip anything inside strings+ // create a new target in parser/prelexer+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(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();+ interp_node->is_interpolant(true);+ (*schema) << interp_node;+ i = j;+ }+ else {+ // throw an error if the interpolant is unterminated+ error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);+ }+ }+ else { // no interpolants left; add the last segment if nonempty+ // check if we need quotes here (was not sure after merge)+ if (i < chunk.end) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, chunk.end));+ break;+ }+ ++ i;+ }+ return schema;+ }++ String_Constant* Parser::parse_static_value()+ {+ lex< static_value >();+ Token str(lexed);+ --str.end;+ --position;+ String_Constant* str_node = new (ctx.mem) String_Constant(pstate, str);+ // str_node->is_delayed(true);+ return str_node;+ }++ String* Parser::parse_string()+ {+ lex< quoted_string >();+ Token token(lexed);+ return parse_interpolated_chunk(token);+ }++ String* Parser::parse_ie_property()+ {+ lex< ie_property >();+ Token str(lexed);+ const char* i = str.begin;+ // see if there any interpolants+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(str.begin, str.end);+ if (!p) {+ String_Constant* str_node = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(str.begin, str.end)));+ str_node->is_delayed(true);+ return str_node;+ }++ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ while (i < str.end) {+ p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);+ if (p) {+ if (i < p) {+ (*schema) << new (ctx.mem) String_Constant(pstate, string(i, p)); // accumulate the preceding segment if it's nonempty+ }+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(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();+ interp_node->is_interpolant(true);+ (*schema) << interp_node;+ i = j;+ }+ else {+ // throw an error if the interpolant is unterminated+ error("unterminated interpolant inside IE function " + str.to_string(), pstate);+ }+ }+ else { // no interpolants left; add the last segment if nonempty+ if (i < str.end) (*schema) << new (ctx.mem) String_Constant(pstate, string(i, str.end));+ break;+ }+ }+ return schema;+ }++ String* Parser::parse_ie_keyword_arg()+ {+ String_Schema* kwd_arg = new (ctx.mem) String_Schema(pstate, 3);+ if (lex< variable >()) {+ *kwd_arg << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));+ } else {+ lex< alternatives< identifier_schema, identifier > >();+ *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);+ }+ lex< exactly<'='> >();+ *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);+ if (peek< variable >()) *kwd_arg << parse_list();+ else if (lex< number >()) *kwd_arg << new (ctx.mem) Textual(pstate, Textual::NUMBER, Util::normalize_decimals(lexed));+ else if (lex< alternatives< identifier_schema, identifier, number, hexa, hex > >()) {+ *kwd_arg << new (ctx.mem) String_Quoted(pstate, lexed);+ }+ return kwd_arg;+ }++ String_Schema* Parser::parse_value_schema(const char* stop)+ {+ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ size_t num_items = 0;+ while (position < stop) {+ if (lex< interpolant >()) {+ Token insides(Token(lexed.begin + 2, lexed.end - 1));+ Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();+ interp_node->is_interpolant(true);+ (*schema) << interp_node;+ }+ else if (lex< exactly<'%'> >()) {+ (*schema) << new (ctx.mem) String_Constant(pstate, lexed);+ }+ else if (lex< identifier >()) {+ (*schema) << new (ctx.mem) String_Quoted(pstate, lexed);+ }+ else if (lex< percentage >()) {+ (*schema) << new (ctx.mem) Textual(pstate, Textual::PERCENTAGE, lexed);+ }+ else if (lex< dimension >()) {+ (*schema) << new (ctx.mem) Textual(pstate, Textual::DIMENSION, lexed);+ }+ else if (lex< number >()) {+ (*schema) << new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed);+ }+ else if (lex< hex >()) {+ (*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed));+ }+ else if (lex< quoted_string >()) {+ (*schema) << new (ctx.mem) String_Constant(pstate, lexed);+ }+ else if (lex< variable >()) {+ (*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));+ }+ else {+ error("error parsing interpolated value", pstate);+ }+ ++num_items;+ }+ return schema;+ }++ /* not used anymore - remove?+ String_Schema* Parser::parse_url_schema()+ {+ String_Schema* schema = new (ctx.mem) String_Schema(pstate);++ while (position < end) {+ if (position[0] == '/') {+ lexed = Token(position, position+1, before_token);+ (*schema) << new (ctx.mem) String_Quoted(pstate, lexed);+ ++position;+ }+ else if (lex< interpolant >()) {+ Token insides(Token(lexed.begin + 2, lexed.end - 1, before_token));+ Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();+ interp_node->is_interpolant(true);+ (*schema) << interp_node;+ }+ else if (lex< sequence< identifier, exactly<':'> > >()) {+ (*schema) << new (ctx.mem) String_Quoted(pstate, lexed);+ }+ else if (lex< filename >()) {+ (*schema) << new (ctx.mem) String_Quoted(pstate, lexed);+ }+ else {+ error("error parsing interpolated url", pstate);+ }+ }+ return schema;+ } */++ // this parses interpolation outside other strings+ // means the result must not be quoted again later+ String* Parser::parse_identifier_schema()+ {+ // first lex away whatever we have found+ lex< sequence< optional< exactly<'*'> >, identifier_schema > >();+ Token id(lexed);+ const char* i = id.begin;+ // see if there any interpolants+ const char* p = find_first_in_interval< exactly<hash_lbrace> >(id.begin, id.end);+ if (!p) {+ return new (ctx.mem) String_Quoted(pstate, string(id.begin, id.end));+ }++ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ while (i < id.end) {+ p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);+ if (p) {+ if (i < p) {+ // accumulate the preceding segment if it's nonempty+ (*schema) << new (ctx.mem) String_Constant(pstate, string(i, p));+ }+ // we need to skip anything inside strings+ // create a new target in parser/prelexer+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(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();+ interp_node->is_interpolant(true);+ (*schema) << interp_node;+ schema->has_interpolants(true);+ i = j;+ }+ else {+ // throw an error if the interpolant is unterminated+ error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);+ }+ }+ else { // no interpolants left; add the last segment if nonempty+ if (i < end) (*schema) << new (ctx.mem) String_Quoted(pstate, string(i, id.end));+ break;+ }+ }+ return schema;+ }++ Function_Call* Parser::parse_calc_function()+ {+ lex< identifier >();+ string name(lexed);+ ParserState call_pos = pstate;+ lex< exactly<'('> >();+ ParserState arg_pos = pstate;+ const char* arg_beg = position;+ parse_list();+ const char* arg_end = position;+ lex< exactly<')'> >();++ Argument* arg = new (ctx.mem) Argument(arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));+ Arguments* args = new (ctx.mem) Arguments(arg_pos);+ *args << arg;+ return new (ctx.mem) Function_Call(call_pos, name, args);+ }++ Function_Call* Parser::parse_function_call()+ {+ lex< identifier >();+ string name(lexed);+ Arguments* args = parse_arguments(name == "url");+ return new (ctx.mem) Function_Call(pstate, name, args);+ }++ Function_Call_Schema* Parser::parse_function_call_schema()+ {+ String* name = parse_identifier_schema();+ ParserState source_position_of_call = pstate;++ Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(source_position_of_call, name, parse_arguments());+ return the_call;+ }++ If* Parser::parse_if_directive(bool else_if)+ {+ lex< kwd_if_directive >() || (else_if && lex< exactly<if_after_else_kwd> >());+ ParserState if_source_position = pstate;+ Expression* predicate = parse_list();+ predicate->is_delayed(false);+ if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if", pstate);+ Block* consequent = parse_block();+ Block* alternative = 0;++ if (lex< elseif_directive >()) {+ alternative = new (ctx.mem) Block(pstate);+ (*alternative) << parse_if_directive(true);+ }+ else if (lex< kwd_else_directive >()) {+ if (!peek< exactly<'{'> >()) {+ error("expected '{' after @else", pstate);+ }+ else {+ alternative = parse_block();+ }+ }+ return new (ctx.mem) If(if_source_position, predicate, consequent, alternative);+ }++ For* Parser::parse_for_directive()+ {+ lex< kwd_for_directive >();+ ParserState for_source_position = pstate;+ if (!lex< variable >()) error("@for directive requires an iteration variable", pstate);+ string var(Util::normalize_underscores(lexed));+ if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);+ Expression* lower_bound = parse_expression();+ lower_bound->is_delayed(false);+ bool inclusive = false;+ if (lex< kwd_through >()) inclusive = true;+ else if (lex< kwd_to >()) inclusive = false;+ else error("expected 'through' or 'to' keyword in @for directive", pstate);+ Expression* upper_bound = parse_expression();+ upper_bound->is_delayed(false);+ if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive", pstate);+ Block* body = parse_block();+ return new (ctx.mem) For(for_source_position, var, lower_bound, upper_bound, body, inclusive);+ }++ Each* Parser::parse_each_directive()+ {+ lex < kwd_each_directive >();+ ParserState each_source_position = pstate;+ if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);+ vector<string> vars;+ vars.push_back(Util::normalize_underscores(lexed));+ while (lex< exactly<','> >()) {+ if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);+ vars.push_back(Util::normalize_underscores(lexed));+ }+ if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);+ Expression* list = parse_list();+ list->is_delayed(false);+ if (list->concrete_type() == Expression::LIST) {+ List* l = static_cast<List*>(list);+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ (*l)[i]->is_delayed(false);+ }+ }+ if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive", pstate);+ Block* body = parse_block();+ return new (ctx.mem) Each(each_source_position, vars, list, body);+ }++ While* Parser::parse_while_directive()+ {+ lex< kwd_while_directive >();+ ParserState while_source_position = pstate;+ Expression* predicate = parse_list();+ predicate->is_delayed(false);+ Block* body = parse_block();+ return new (ctx.mem) While(while_source_position, predicate, body);+ }++ Media_Block* Parser::parse_media_block()+ {+ lex< kwd_media >();+ ParserState media_source_position = pstate;++ List* media_queries = parse_media_queries();++ if (!peek< exactly<'{'> >()) {+ error("expected '{' in media query", pstate);+ }+ Media_Block* media_block = new (ctx.mem) Media_Block(media_source_position, media_queries, 0);+ Media_Block* prev_media_block = last_media_block;+ last_media_block = media_block;+ media_block->block(parse_block());+ last_media_block = prev_media_block;++ return media_block;+ }++ List* Parser::parse_media_queries()+ {+ List* media_queries = new (ctx.mem) List(pstate, 0, List::COMMA);+ if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query();+ while (lex< exactly<','> >()) (*media_queries) << parse_media_query();+ return media_queries;+ }++ // Expression* Parser::parse_media_query()+ Media_Query* Parser::parse_media_query()+ {+ Media_Query* media_query = new (ctx.mem) Media_Query(pstate);++ if (lex< exactly< not_kwd > >()) media_query->is_negated(true);+ else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true);++ if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema());+ else if (lex< identifier >()) media_query->media_type(parse_interpolated_chunk(lexed));+ else (*media_query) << parse_media_expression();++ while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();+ if (peek< identifier_schema >()) {+ String_Schema* schema = new (ctx.mem) String_Schema(pstate);+ *schema << media_query->media_type();+ *schema << new (ctx.mem) String_Constant(pstate, " ");+ *schema << parse_identifier_schema();+ media_query->media_type(schema);+ }+ while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression();+ return media_query;+ }++ Media_Query_Expression* Parser::parse_media_expression()+ {+ if (peek< identifier_schema >()) {+ String* ss = parse_identifier_schema();+ return new (ctx.mem) Media_Query_Expression(pstate, ss, 0, true);+ }+ if (!lex< exactly<'('> >()) {+ error("media query expression must begin with '('", pstate);+ }+ Expression* feature = 0;+ if (peek< exactly<')'> >()) {+ error("media feature required in media query expression", pstate);+ }+ feature = parse_expression();+ Expression* expression = 0;+ if (lex< exactly<':'> >()) {+ expression = parse_list();+ }+ if (!lex< exactly<')'> >()) {+ error("unclosed parenthesis in media query expression", pstate);+ }+ return new (ctx.mem) Media_Query_Expression(feature->pstate(), feature, expression);+ }++ Feature_Block* Parser::parse_feature_block()+ {+ lex< kwd_supports >();+ ParserState supports_source_position = pstate;++ Feature_Query* feature_queries = parse_feature_queries();++ if (!peek< exactly<'{'> >()) {+ error("expected '{' in feature query", pstate);+ }+ Block* block = parse_block();++ return new (ctx.mem) Feature_Block(supports_source_position, feature_queries, block);+ }++ Feature_Query* Parser::parse_feature_queries()+ {+ Feature_Query* fq = new (ctx.mem) Feature_Query(pstate);+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate);+ cond->is_root(true);+ while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))+ (*cond) << parse_feature_query();+ (*fq) << cond;++ if (fq->empty()) error("expected @supports condition (e.g. (display: flexbox))", pstate);++ return fq;+ }++ Feature_Query_Condition* Parser::parse_feature_query()+ {+ if (peek< kwd_not >(position)) return parse_supports_negation();+ else if (peek< kwd_and >(position)) return parse_supports_conjunction();+ else if (peek< kwd_or >(position)) return parse_supports_disjunction();+ else if (peek< exactly<'('> >(position)) return parse_feature_query_in_parens();+ else return parse_supports_declaration();+ }++ Feature_Query_Condition* Parser::parse_feature_query_in_parens()+ {+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(pstate);++ if (!lex< exactly<'('> >()) error("@supports declaration expected '('", pstate);+ while (!peek< exactly<')'> >(position) && !peek< exactly<'{'> >(position))+ (*cond) << parse_feature_query();+ if (!lex< exactly<')'> >()) error("unclosed parenthesis in @supports declaration", pstate);++ return (cond->length() == 1) ? (*cond)[0] : cond;+ }++ Feature_Query_Condition* Parser::parse_supports_negation()+ {+ lex< kwd_not >();++ Feature_Query_Condition* cond = parse_feature_query();+ cond->operand(Feature_Query_Condition::NOT);++ return cond;+ }++ Feature_Query_Condition* Parser::parse_supports_conjunction()+ {+ lex< kwd_and >();++ Feature_Query_Condition* cond = parse_feature_query();+ cond->operand(Feature_Query_Condition::AND);++ return cond;+ }++ Feature_Query_Condition* Parser::parse_supports_disjunction()+ {+ lex< kwd_or >();++ Feature_Query_Condition* cond = parse_feature_query();+ cond->operand(Feature_Query_Condition::OR);++ return cond;+ }++ Feature_Query_Condition* Parser::parse_supports_declaration()+ {+ Declaration* declaration = parse_declaration();+ Feature_Query_Condition* cond = new (ctx.mem) Feature_Query_Condition(declaration->pstate(),+ 1,+ declaration->property(),+ declaration->value());+ return cond;+ }++ At_Root_Block* Parser::parse_at_root_block()+ {+ lex<kwd_at_root>();+ ParserState at_source_position = pstate;+ Block* body = 0;+ At_Root_Expression* expr = 0;+ Selector_Lookahead lookahead_result;+ in_at_root = true;+ if (peek< exactly<'('> >()) {+ expr = parse_at_root_expression();+ body = parse_block();+ }+ else if (peek< exactly<'{'> >()) {+ body = parse_block();+ }+ else if ((lookahead_result = lookahead_for_selector(position)).found) {+ Ruleset* r = parse_ruleset(lookahead_result);+ body = new (ctx.mem) Block(r->pstate(), 1);+ *body << r;+ }+ in_at_root = false;+ At_Root_Block* at_root = new (ctx.mem) At_Root_Block(at_source_position, body);+ if (expr) at_root->expression(expr);+ return at_root;+ }++ At_Root_Expression* Parser::parse_at_root_expression()+ {+ lex< exactly<'('> >();+ if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);++ if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {+ css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");+ }++ Declaration* declaration = parse_declaration();+ List* value = new (ctx.mem) List(declaration->value()->pstate(), 1);++ if (declaration->value()->concrete_type() == Expression::LIST) {+ value = static_cast<List*>(declaration->value());+ }+ else *value << declaration->value();++ At_Root_Expression* cond = new (ctx.mem) At_Root_Expression(declaration->pstate(),+ declaration->property(),+ value);+ if (!lex< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);+ return cond;+ }++ At_Rule* Parser::parse_at_rule()+ {+ lex<at_keyword>();+ string kwd(lexed);+ ParserState at_source_position = pstate;+ Selector* sel = 0;+ Expression* val = 0;+ Selector_Lookahead lookahead = lookahead_for_extension_target(position);+ if (lookahead.found) {+ if (lookahead.has_interpolants) {+ sel = parse_selector_schema(lookahead.found);+ }+ else {+ sel = parse_selector_group();+ }+ }+ else if (!(peek<exactly<'{'> >() || peek<exactly<'}'> >() || peek<exactly<';'> >())) {+ val = parse_list();+ }+ Block* body = 0;+ if (peek< exactly<'{'> >()) body = parse_block();+ At_Rule* rule = new (ctx.mem) At_Rule(at_source_position, kwd, sel, body);+ if (!sel) rule->value(val);+ return rule;+ }++ Warning* Parser::parse_warning()+ {+ lex< kwd_warn >();+ return new (ctx.mem) Warning(pstate, parse_list());+ }++ Error* Parser::parse_error()+ {+ lex< kwd_err >();+ return new (ctx.mem) Error(pstate, parse_list());+ }++ Debug* Parser::parse_debug()+ {+ lex< kwd_dbg >();+ return new (ctx.mem) Debug(pstate, parse_list());+ }++ Selector_Lookahead Parser::lookahead_for_selector(const char* start)+ {+ const char* p = start ? start : position;+ const char* q;+ bool saw_stuff = false;+ bool saw_interpolant = false;++ while ((q = peek< identifier >(p)) ||+ (q = peek< hyphens_and_identifier >(p)) ||+ (q = peek< hyphens_and_name >(p)) ||+ (q = peek< type_selector >(p)) ||+ (q = peek< id_name >(p)) ||+ (q = peek< class_name >(p)) ||+ (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||+ (q = peek< percentage >(p)) ||+ (q = peek< dimension >(p)) ||+ (q = peek< quoted_string >(p)) ||+ (q = peek< exactly<'*'> >(p)) ||+ (q = peek< exactly<sel_deep_kwd> >(p)) ||+ (q = peek< exactly<'('> >(p)) ||+ (q = peek< exactly<')'> >(p)) ||+ (q = peek< exactly<'['> >(p)) ||+ (q = peek< exactly<']'> >(p)) ||+ (q = peek< exactly<'+'> >(p)) ||+ (q = peek< exactly<'~'> >(p)) ||+ (q = peek< exactly<'>'> >(p)) ||+ (q = peek< exactly<','> >(p)) ||+ (saw_stuff && (q = peek< exactly<'-'> >(p))) ||+ (q = peek< binomial >(p)) ||+ (q = peek< block_comment >(p)) ||+ (q = peek< sequence< optional<sign>,+ zero_plus<digit>,+ exactly<'n'> > >(p)) ||+ (q = peek< sequence< optional<sign>,+ one_plus<digit> > >(p)) ||+ (q = peek< number >(p)) ||+ (q = peek< sequence< exactly<'&'>,+ identifier_alnums > >(p)) ||+ (q = peek< exactly<'&'> >(p)) ||+ (q = peek< exactly<'%'> >(p)) ||+ (q = peek< alternatives<exact_match,+ class_match,+ dash_match,+ prefix_match,+ suffix_match,+ substring_match> >(p)) ||+ (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||+ (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||+ (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||+ (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||+ (q = peek< interpolant >(p))) {+ saw_stuff = true;+ p = q;+ if (*(p - 1) == '}') saw_interpolant = true;+ }++ Selector_Lookahead result;+ result.found = saw_stuff && peek< exactly<'{'> >(p) ? p : 0;+ result.has_interpolants = saw_interpolant;++ return result;+ }++ Selector_Lookahead Parser::lookahead_for_extension_target(const char* start)+ {+ const char* p = start ? start : position;+ const char* q;+ bool saw_interpolant = false;+ bool saw_stuff = false;++ while ((q = peek< identifier >(p)) ||+ (q = peek< type_selector >(p)) ||+ (q = peek< id_name >(p)) ||+ (q = peek< class_name >(p)) ||+ (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||+ (q = peek< percentage >(p)) ||+ (q = peek< dimension >(p)) ||+ (q = peek< quoted_string >(p)) ||+ (q = peek< exactly<'*'> >(p)) ||+ (q = peek< exactly<'('> >(p)) ||+ (q = peek< exactly<')'> >(p)) ||+ (q = peek< exactly<'['> >(p)) ||+ (q = peek< exactly<']'> >(p)) ||+ (q = peek< exactly<'+'> >(p)) ||+ (q = peek< exactly<'~'> >(p)) ||+ (q = peek< exactly<'>'> >(p)) ||+ (q = peek< exactly<','> >(p)) ||+ (saw_stuff && (q = peek< exactly<'-'> >(p))) ||+ (q = peek< binomial >(p)) ||+ (q = peek< block_comment >(p)) ||+ (q = peek< sequence< optional<sign>,+ zero_plus<digit>,+ exactly<'n'> > >(p)) ||+ (q = peek< sequence< optional<sign>,+ one_plus<digit> > >(p)) ||+ (q = peek< number >(p)) ||+ (q = peek< sequence< exactly<'&'>,+ identifier_alnums > >(p)) ||+ (q = peek< exactly<'&'> >(p)) ||+ (q = peek< exactly<'%'> >(p)) ||+ (q = peek< alternatives<exact_match,+ class_match,+ dash_match,+ prefix_match,+ suffix_match,+ substring_match> >(p)) ||+ (q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||+ (q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||+ (q = peek< sequence< one_plus< exactly<'-'> >, interpolant > >(p)) ||+ (q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||+ (q = peek< interpolant >(p)) ||+ (q = peek< optional >(p))) {+ p = q;+ if (*(p - 1) == '}') saw_interpolant = true;+ saw_stuff = true;+ }++ Selector_Lookahead result;+ result.found = peek< alternatives< exactly<';'>, exactly<'}'>, exactly<'{'> > >(p) && saw_stuff ? p : 0;+ result.has_interpolants = saw_interpolant;++ return result;+ }++ void Parser::read_bom()+ {+ size_t skip = 0;+ string encoding;+ bool utf_8 = false;+ switch ((unsigned char) source[0]) {+ case 0xEF:+ skip = check_bom_chars(source, end, utf_8_bom, 3);+ encoding = "UTF-8";+ utf_8 = true;+ break;+ case 0xFE:+ skip = check_bom_chars(source, end, utf_16_bom_be, 2);+ encoding = "UTF-16 (big endian)";+ break;+ case 0xFF:+ skip = check_bom_chars(source, end, utf_16_bom_le, 2);+ skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0);+ encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)");+ break;+ case 0x00:+ skip = check_bom_chars(source, end, utf_32_bom_be, 4);+ encoding = "UTF-32 (big endian)";+ break;+ case 0x2B:+ skip = check_bom_chars(source, end, utf_7_bom_1, 4)+ | check_bom_chars(source, end, utf_7_bom_2, 4)+ | check_bom_chars(source, end, utf_7_bom_3, 4)+ | check_bom_chars(source, end, utf_7_bom_4, 4)+ | check_bom_chars(source, end, utf_7_bom_5, 5);+ encoding = "UTF-7";+ break;+ case 0xF7:+ skip = check_bom_chars(source, end, utf_1_bom, 3);+ encoding = "UTF-1";+ break;+ case 0xDD:+ skip = check_bom_chars(source, end, utf_ebcdic_bom, 4);+ encoding = "UTF-EBCDIC";+ break;+ case 0x0E:+ skip = check_bom_chars(source, end, scsu_bom, 3);+ encoding = "SCSU";+ break;+ case 0xFB:+ skip = check_bom_chars(source, end, bocu_1_bom, 3);+ encoding = "BOCU-1";+ break;+ case 0x84:+ skip = check_bom_chars(source, end, gb_18030_bom, 4);+ encoding = "GB-18030";+ break;+ }+ if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);+ position += skip;+ }++ size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len)+ {+ size_t skip = 0;+ if (src + len > end) return 0;+ for (size_t i = 0; i < len; ++i, ++skip) {+ if ((unsigned char) src[i] != bom[i]) return 0;+ }+ return skip;+ }+++ Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op)+ {+ for (size_t i = 0, S = operands.size(); i < S; ++i) {+ base = new (ctx.mem) Binary_Expression(pstate, op, base, operands[i]);+ Binary_Expression* b = static_cast<Binary_Expression*>(base);+ if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {+ base->is_delayed(true);+ }+ else {+ b->left()->is_delayed(false);+ b->right()->is_delayed(false);+ }+ }+ return base;+ }++ Expression* Parser::fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops)+ {+ for (size_t i = 0, S = operands.size(); i < S; ++i) {+ base = new (ctx.mem) Binary_Expression(base->pstate(), ops[i], base, operands[i]);+ Binary_Expression* b = static_cast<Binary_Expression*>(base);+ if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) {+ base->is_delayed(true);+ }+ else {+ b->left()->is_delayed(false);+ b->right()->is_delayed(false);+ }+ }+ return base;+ }++ void Parser::error(string msg, Position pos)+ {+ throw Sass_Error(Sass_Error::syntax, ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);+ }++ // print a css parsing error with actual context information from parsed source+ void Parser::css_error(const string& msg, const string& prefix, const string& middle)+ {+ int max_len = 14;+ const char* pos = peek < optional_spaces >();+ bool ellipsis_left = false;+ const char* pos_left(pos);+ while (*pos_left && pos_left >= source) {+ if (pos - pos_left > max_len) {+ ellipsis_left = true;+ break;+ }+ if (*pos_left == '\r') break;+ if (*pos_left == '\n') break;+ -- pos_left;+ }+ bool ellipsis_right = false;+ const char* pos_right(pos);+ while (*pos_right && pos_right <= end) {+ if (pos_right - pos > max_len) {+ ellipsis_right = true;+ break;+ }+ if (*pos_right == '\r') break;+ if (*pos_right == '\n') break;+ ++ pos_right;+ }+ string left(pos_left, pos);+ string right(pos, pos_right);+ if (ellipsis_left) left = ellipsis + left;+ if (ellipsis_right) right = right + ellipsis;+ // now pass new message to the more generic error function+ error(msg + prefix + quote(left) + middle + quote(right), pstate);+ }++}
+ libsass/parser.hpp view
@@ -0,0 +1,280 @@+#ifndef SASS_PARSER_H+#define SASS_PARSER_H++#include <map>+#include <vector>+#include <iostream>++#include "ast.hpp"+#include "position.hpp"+#include "context.hpp"+#include "position.hpp"+#include "prelexer.hpp"++struct Selector_Lookahead {+ const char* found;+ bool has_interpolants;+};++namespace Sass {+ using std::string;+ using std::vector;+ using std::map;+ using namespace Prelexer;++ class Parser : public ParserState {+ private:+ void add_single_file (Import* imp, string import_path);+ void import_single_file (Import* imp, string import_path);+ public:+ class AST_Node;++ enum Syntactic_Context { nothing, mixin_def, function_def };+ bool do_import(const string& import_path, Import* imp, vector<Sass_Importer_Entry> importers, bool only_one = true);++ Context& ctx;+ vector<Block*> block_stack;+ vector<Syntactic_Context> stack;+ Media_Block* last_media_block;+ const char* source;+ const char* position;+ const char* end;+ Position before_token;+ Position after_token;+ ParserState pstate;+ int indentation;+++ Token lexed;+ bool in_at_root;++ Parser(Context& ctx, const ParserState& pstate)+ : ParserState(pstate), ctx(ctx), block_stack(0), stack(0), last_media_block(0),+ source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate("[NULL]"), indentation(0)+ { in_at_root = false; stack.push_back(nothing); }++ // static Parser from_string(const 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]"));++#ifdef __clang__++ // lex and peak uses the template parameter to branch on the action, which+ // triggers clangs tautological comparison on the single-comparison+ // branches. This is not a bug, just a merging of behaviour into+ // one function++#pragma clang diagnostic push+#pragma clang diagnostic ignored "-Wtautological-compare"++#endif+++ bool peek_newline(const char* start = 0);++ // skip over spaces, tabs and line comments+ template <prelexer mx>+ const char* sneak(const char* start = 0)+ {++ // maybe use optional start position from arguments?+ const char* it_position = start ? start : position;++ // skip white-space?+ if (mx == spaces ||+ mx == no_spaces ||+ mx == css_comments ||+ mx == css_whitespace ||+ mx == optional_spaces ||+ mx == optional_css_comments ||+ mx == optional_css_whitespace+ ) {+ return it_position;+ }++ // skip over spaces, tabs and sass line comments+ const char* pos = optional_css_whitespace(it_position);+ // always return a valid position+ return pos ? pos : it_position;++ }++ // peek will only skip over space, tabs and line comment+ // return the position where the lexer match will occur+ template <prelexer mx>+ const char* peek(const char* start = 0)+ {++ // sneak up to the actual token we want to lex+ // this should skip over white-space if desired+ const char* it_before_token = sneak < mx >(start);++ // match the given prelexer+ return mx(it_before_token);++ }++ // white-space handling is built into the lexer+ // this way you do not need to parse it yourself+ // some matchers don't accept certain white-space+ // we do not support start arg, since we manipulate+ // sourcemap offset and we modify the position pointer!+ // lex will only skip over space, tabs and line comment+ template <prelexer mx>+ const char* lex(bool lazy = true)+ {++ // position considered before lexed token+ // we can skip whitespace or comments for+ // lazy developers (but we need control)+ const char* it_before_token = position;++ // sneak up to the actual token we want to lex+ // this should skip over white-space if desired+ if (lazy) it_before_token = sneak < mx >(position);++ // now call matcher to get position after token+ const char* it_after_token = mx(it_before_token);++ // assertion that we got a valid match+ if (it_after_token == 0) return 0;+ // assertion that we actually lexed something+ if (it_after_token == it_before_token) return 0;++ // create new lexed token object (holds all parse result information)+ lexed = Token(position, it_before_token, it_after_token);++ // advance position (add whitespace before current token)+ before_token = after_token.add(position, it_before_token);++ // update after_token position for current token+ after_token.add(it_before_token, it_after_token);++ // ToDo: could probably do this incremetal on original object (API wants offset?)+ pstate = ParserState(path, source, lexed, before_token, after_token - before_token);++ // advance internal char iterator+ return position = it_after_token;++ }++ // lex_css skips over space, tabs, line and block comment+ // all block comments will be consumed and thrown away+ // source-map position will point to token after the comment+ template <prelexer mx>+ const char* lex_css()+ {+ // throw away comments+ // update srcmap position+ lex < css_comments >();+ // now lex a token+ return lex< mx >();+ }++ // all block comments will be skipped and thrown away+ template <prelexer mx>+ const char* peek_css(const char* start = 0)+ {+ // now peek a token (skip comments first)+ return peek< mx >(peek < css_comments >(start));+ }++#ifdef __clang__++#pragma clang diagnostic pop++#endif++ void error(string msg, Position pos);+ // generate message with given and expected sample+ // text before and in the middle are configurable+ void css_error(const string& msg,+ const string& prefix = " after ",+ const string& middle = ", was: ");+ void read_bom();++ Block* parse();+ Import* parse_import();+ Definition* parse_definition();+ Parameters* parse_parameters();+ Parameter* parse_parameter();+ Mixin_Call* parse_mixin_call();+ Arguments* parse_arguments(bool has_url = false);+ Argument* parse_argument(bool has_url = false);+ Assignment* parse_assignment();+ // Propset* parse_propset();+ Ruleset* parse_ruleset(Selector_Lookahead lookahead);+ Selector_Schema* parse_selector_schema(const char* end_of_selector);+ Selector_List* parse_selector_group();+ Complex_Selector* parse_selector_combination();+ Compound_Selector* parse_simple_selector_sequence();+ Simple_Selector* parse_simple_selector();+ Wrapped_Selector* parse_negated_selector();+ Simple_Selector* parse_pseudo_selector();+ Attribute_Selector* parse_attribute_selector();+ Block* parse_block();+ bool parse_number_prefix();+ Declaration* parse_declaration();+ Expression* parse_map_value();+ Expression* parse_map();+ Expression* parse_list();+ Expression* parse_comma_list();+ Expression* parse_space_list();+ Expression* parse_disjunction();+ Expression* parse_conjunction();+ Expression* parse_relation();+ Expression* parse_expression();+ Expression* parse_term();+ Expression* parse_factor();+ Expression* parse_value();+ Function_Call* parse_calc_function();+ Function_Call* parse_function_call();+ Function_Call_Schema* parse_function_call_schema();+ String* parse_interpolated_chunk(Token, bool constant = false);+ String* parse_string();+ String_Constant* parse_static_value();+ String* parse_ie_property();+ String* parse_ie_keyword_arg();+ String_Schema* parse_value_schema(const char* stop);+ String* parse_identifier_schema();+ // String_Schema* parse_url_schema();+ If* parse_if_directive(bool else_if = false);+ For* parse_for_directive();+ Each* parse_each_directive();+ While* parse_while_directive();+ Media_Block* parse_media_block();+ List* parse_media_queries();+ Media_Query* parse_media_query();+ Media_Query_Expression* parse_media_expression();+ Feature_Block* parse_feature_block();+ Feature_Query* parse_feature_queries();+ Feature_Query_Condition* parse_feature_query();+ Feature_Query_Condition* parse_feature_query_in_parens();+ Feature_Query_Condition* parse_supports_negation();+ Feature_Query_Condition* parse_supports_conjunction();+ Feature_Query_Condition* parse_supports_disjunction();+ Feature_Query_Condition* parse_supports_declaration();+ At_Root_Block* parse_at_root_block();+ At_Root_Expression* parse_at_root_expression();+ At_Rule* parse_at_rule();+ Warning* parse_warning();+ Error* parse_error();+ Debug* parse_debug();++ void parse_block_comments(Block* block);++ Selector_Lookahead lookahead_for_selector(const char* start = 0);+ Selector_Lookahead lookahead_for_extension_target(const char* start = 0);++ Expression* fold_operands(Expression* base, vector<Expression*>& operands, Binary_Expression::Type op);+ Expression* fold_operands(Expression* base, vector<Expression*>& operands, vector<Binary_Expression::Type>& ops);++ void throw_syntax_error(string message, size_t ln = 0);+ void throw_read_error(string message, size_t ln = 0);+ };++ size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);+}++#endif
+ libsass/paths.hpp view
@@ -0,0 +1,74 @@+#ifndef SASS_PATHS_H+#define SASS_PATHS_H++#include <string>+#include <vector>+#include <sstream>+#include <iostream>++using namespace std;++template<typename T>+string vector_to_string(vector<T> v)+{+ stringstream buffer;+ buffer << "[";++ if (!v.empty())+ { buffer << v[0]; }+ else+ { buffer << "]"; }++ if (v.size() == 1)+ { buffer << "]"; }+ else+ {+ for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];+ buffer << "]";+ }++ return buffer.str();+}++namespace Sass {++ using namespace std;++ template<typename T>+ vector<vector<T> > paths(vector<vector<T> > strata, size_t from_end = 0)+ {+ if (strata.empty()) {+ return vector<vector<T> >();+ }++ size_t end = strata.size() - from_end;+ if (end <= 1) {+ vector<vector<T> > starting_points;+ starting_points.reserve(strata[0].size());+ for (size_t i = 0, S = strata[0].size(); i < S; ++i) {+ vector<T> starting_point;+ starting_point.push_back(strata[0][i]);+ starting_points.push_back(starting_point);+ }+ return starting_points;+ }++ vector<vector<T> > up_to_here = paths(strata, from_end + 1);+ vector<T> here = strata[end-1];++ vector<vector<T> > branches;+ branches.reserve(up_to_here.size() * here.size());+ for (size_t i = 0, S1 = up_to_here.size(); i < S1; ++i) {+ for (size_t j = 0, S2 = here.size(); j < S2; ++j) {+ vector<T> branch = up_to_here[i];+ branch.push_back(here[j]);+ branches.push_back(branch);+ }+ }++ return branches;+ }++}++#endif
+ libsass/plugins.cpp view
@@ -0,0 +1,168 @@+#ifdef _WIN32+#include <windows.h>+#else+#include <sys/types.h>+#include <dirent.h>+#include <errno.h>+#include <dlfcn.h>+#endif++#include <iostream>+#include "output.hpp"+#include "plugins.hpp"++#define npos string::npos++namespace Sass {++ Plugins::Plugins(void) { }+ Plugins::~Plugins(void) { }++ // check if plugin is compatible with this version+ // plugins may be linked static against libsass+ // we try to be compatible between major versions+ inline bool compatibility(const char* their_version)+ {+// const char* their_version = "3.1.2";+ // first check if anyone has an unknown version+ const char* our_version = libsass_version();+ if (!strcmp(their_version, "[na]")) return false;+ if (!strcmp(our_version, "[na]")) return false;++ // find the position of the second dot+ size_t pos = string(our_version).find('.', 0);+ if (pos != npos) pos = string(our_version).find('.', pos + 1);++ // if we do not have two dots we fallback to compare complete string+ if (pos == npos) { return strcmp(their_version, our_version) ? 0 : 1; }+ // otherwise only compare up to the second dot (major versions)+ else { return strncmp(their_version, our_version, pos) ? 0 : 1; }++ }++ // load one specific plugin+ bool Plugins::load_plugin (const string& path)+ {++ typedef const char* (*__plugin_version__)(void);+ typedef Sass_Function_List (*__plugin_load_fns__)(void);+ typedef Sass_Importer_List (*__plugin_load_imps__)(void);++ if (LOAD_LIB(plugin, path))+ {+ // try to load initial function to query libsass version suppor+ if (LOAD_LIB_FN(__plugin_version__, plugin_version, "libsass_get_version"))+ {+ // get the libsass version of the plugin+ if (!compatibility(plugin_version())) return false;+ // try to get import address for "libsass_load_functions"+ if (LOAD_LIB_FN(__plugin_load_fns__, plugin_load_functions, "libsass_load_functions"))+ {+ Sass_Function_List fns = plugin_load_functions();+ while (fns && *fns) { functions.push_back(*fns); ++ fns; }+ }+ // try to get import address for "libsass_load_importers"+ if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_importers, "libsass_load_importers"))+ {+ Sass_Importer_List imps = plugin_load_importers();+ while (imps && *imps) { importers.push_back(*imps); ++ imps; }+ }+ // try to get import address for "libsass_load_headers"+ if (LOAD_LIB_FN(__plugin_load_imps__, plugin_load_headers, "libsass_load_headers"))+ {+ Sass_Importer_List imps = plugin_load_headers();+ while (imps && *imps) { headers.push_back(*imps); ++ imps; }+ }+ // success+ return true;+ }+ else+ {+ // print debug message to stderr (should not happen)+ cerr << "failed loading 'libsass_support' in <" << path << ">" << endl;+ if (const char* dlsym_error = dlerror()) cerr << dlsym_error << endl;+ CLOSE_LIB(plugin);+ }+ }+ else+ {+ // print debug message to stderr (should not happen)+ cerr << "failed loading plugin <" << path << ">" << endl;+ if (const char* dlopen_error = dlerror()) cerr << dlopen_error << endl;+ }++ return false;++ }++ size_t Plugins::load_plugins(const string& path)+ {++ // count plugins+ size_t loaded = 0;++ #ifdef _WIN32++ try+ {++ // use wchar (utf16)+ WIN32_FIND_DATAW data;+ // trailing slash is guaranteed+ string globsrch(path + "*.dll");+ // convert to wide chars (utf16) for system call+ wstring wglobsrch(UTF_8::convert_to_utf16(globsrch));+ HANDLE hFile = FindFirstFileW(wglobsrch.c_str(), &data);+ // check if system called returned a result+ // ToDo: maybe we should print a debug message+ if (hFile == INVALID_HANDLE_VALUE) return -1;++ // read directory+ while (true)+ {+ try+ {+ // the system will report the filenames with wide chars (utf16)+ string entry = UTF_8::convert_from_utf16(data.cFileName);+ // check if file ending matches exactly+ if (!ends_with(entry, ".dll")) continue;+ // load the plugin and increase counter+ if (load_plugin(path + entry)) ++ loaded;+ // check if there should be more entries+ if (GetLastError() == ERROR_NO_MORE_FILES) break;+ // load next entry (check for return type)+ if (!FindNextFileW(hFile, &data)) break;+ }+ catch (...)+ {+ // report the error to the console (should not happen)+ // seems like we got strange data from the system call?+ cerr << "filename in plugin path has invalid utf8?" << endl;+ }+ }+ }+ catch (utf8::invalid_utf8)+ {+ // report the error to the console (should not happen)+ // implementors should make sure to provide valid utf8+ cerr << "plugin path contains invalid utf8" << endl;+ }++ #else++ DIR *dp;+ struct dirent *dirp;+ if((dp = opendir(path.c_str())) == NULL) return -1;+ while ((dirp = readdir(dp)) != NULL) {+ if (!ends_with(dirp->d_name, ".so")) continue;+ if (load_plugin(path + dirp->d_name)) ++ loaded;+ }+ closedir(dp);++ #endif+ return loaded;++ }++}+
+ libsass/plugins.hpp view
@@ -0,0 +1,58 @@+#ifndef SASS_PLUGINS_H+#define SASS_PLUGINS_H++#include <string>+#include <vector>+#include "utf8_string.hpp"+#include "sass_functions.h"++#ifdef _WIN32++ #define LOAD_LIB(var, path) HMODULE var = LoadLibraryW(UTF_8::convert_to_utf16(path).c_str())+ #define LOAD_LIB_WCHR(var, path_wide_str) HMODULE var = LoadLibraryW(path_wide_str.c_str())+ #define LOAD_LIB_FN(type, var, name) type var = (type) GetProcAddress(plugin, name)+ #define CLOSE_LIB(var) FreeLibrary(var)++ #ifndef dlerror+ #define dlerror() 0+ #endif++#else++ #define LOAD_LIB(var, path) void* var = dlopen(path.c_str(), RTLD_LAZY)+ #define LOAD_LIB_FN(type, var, name) type var = (type) dlsym(plugin, name)+ #define CLOSE_LIB(var) dlclose(var)++#endif++namespace Sass {++ using namespace std;++ class Plugins {++ public: // c-tor+ Plugins(void);+ ~Plugins(void);++ public: // methods+ // load one specific plugin+ bool load_plugin(const string& path);+ // load all plugins from a directory+ size_t load_plugins(const string& path);++ public: // public accessors+ const vector<Sass_Importer_Entry> get_headers(void) { return headers; };+ const vector<Sass_Importer_Entry> get_importers(void) { return importers; };+ const vector<Sass_Function_Entry> get_functions(void) { return functions; };++ private: // private vars+ vector<Sass_Importer_Entry> headers;+ vector<Sass_Importer_Entry> importers;+ vector<Sass_Function_Entry> functions;++ };++}++#endif
+ libsass/position.cpp view
@@ -0,0 +1,163 @@+#include "position.hpp"++namespace Sass {++ using namespace std;++ Offset::Offset(const char* string)+ : line(0), column(0)+ {+ *this = inc(string, string + strlen(string));+ }++ Offset::Offset(const string& text)+ : line(0), column(0)+ {+ *this = inc(text.c_str(), text.c_str() + text.size());+ }++ Offset::Offset(const size_t line, const size_t column)+ : line(line), column(column) { }++ // init/create instance from const char substring+ Offset Offset::init(const char* beg, const char* end)+ {+ Offset offset(0, 0);+ if (end == 0) {+ end += strlen(beg);+ }+ offset.add(beg, end);+ return offset;+ }++ // increase offset by given string (mostly called by lexer)+ // increase line counter and count columns on the last line+ // ToDo: make the col count utf8 aware+ Offset Offset::add(const char* begin, const char* end)+ {+ if (end == 0) return *this;+ while (begin < end && *begin) {+ if (*begin == '\n') {+ ++ line;+ // start new line+ column = 0;+ } else {+ ++ column;+ }+ ++begin;+ }+ return *this;+ }++ // increase offset by given string (mostly called by lexer)+ // increase line counter and count columns on the last line+ Offset Offset::inc(const char* begin, const char* end) const+ {+ Offset offset(line, column);+ offset.add(begin, end);+ return offset;+ }++ bool Offset::operator== (const Offset &pos) const+ {+ return line == pos.line && column == pos.column;+ }++ bool Offset::operator!= (const Offset &pos) const+ {+ return line != pos.line || column != pos.column;+ }++ void Offset::operator+= (const Offset &off)+ {+ *this = Offset(line + off.line, off.line > 0 ? off.column : column + off.column);+ }++ Offset Offset::operator+ (const Offset &off) const+ {+ return Offset(line + off.line, off.line > 0 ? off.column : column + off.column);+ }++ Offset Offset::operator- (const Offset &off) const+ {+ return Offset(line - off.line, off.line == line ? column - off.column : column);+ }++ Position::Position(const size_t file)+ : Offset(0, 0), file(file) { }++ Position::Position(const size_t file, const Offset& offset)+ : Offset(offset), file(file) { }++ Position::Position(const size_t line, const size_t column)+ : Offset(line, column), file(-1) { }++ Position::Position(const size_t file, const size_t line, const size_t column)+ : Offset(line, column), file(file) { }+++ ParserState::ParserState(string path, const char* src, const size_t file)+ : Position(file, 0, 0), path(path), src(src), offset(0, 0), token() { }++ ParserState::ParserState(string path, const char* src, Position position, Offset offset)+ : Position(position), path(path), src(src), offset(offset), token() { }++ ParserState::ParserState(string path, const char* src, Token token, Position position, Offset offset)+ : Position(position), path(path), src(src), offset(offset), token(token) { }++ Position Position::add(const char* begin, const char* end)+ {+ Offset::add(begin, end);+ return *this;+ }++ Position Position::inc(const char* begin, const char* end) const+ {+ Offset offset(line, column);+ offset = offset.inc(begin, end);+ return Position(file, offset);+ }++ bool Position::operator== (const Position &pos) const+ {+ return file == pos.file && line == pos.line && column == pos.column;+ }++ bool Position::operator!= (const Position &pos) const+ {+ return file == pos.file || line != pos.line || column != pos.column;+ }++ void Position::operator+= (const Offset &off)+ {+ *this = Position(file, line + off.line, off.line > 0 ? off.column : column + off.column);+ }++ const Position Position::operator+ (const Offset &off) const+ {+ return Position(file, line + off.line, off.line > 0 ? off.column : column + off.column);+ }++ const Offset Position::operator- (const Offset &off) const+ {+ return Offset(line - off.line, off.line == line ? column - off.column : column);+ }++ /* not used anymore - remove?+ std::ostream& operator<<(std::ostream& strm, const Offset& off)+ {+ if (off.line == string::npos) strm << "-1:"; else strm << off.line << ":";+ if (off.column == string::npos) strm << "-1"; else strm << off.column;+ return strm;+ } */++ /* not used anymore - remove?+ std::ostream& operator<<(std::ostream& strm, const Position& pos)+ {+ if (pos.file != string::npos) strm << pos.file << ":";+ if (pos.line == string::npos) strm << "-1:"; else strm << pos.line << ":";+ if (pos.column == string::npos) strm << "-1"; else strm << pos.column;+ return strm;+ } */++}
+ libsass/position.hpp view
@@ -0,0 +1,120 @@+#ifndef SASS_POSITION_H+#define SASS_POSITION_H++#include <string>+#include <cstring>+#include <cstdlib>+#include <sstream>+#include <iostream>++namespace Sass {++ using namespace std;++ class Offset {++ public: // c-tor+ Offset(const char* string);+ Offset(const string& text);+ Offset(const size_t line, const size_t column);++ // return new position, incremented by the given string+ Offset add(const char* begin, const char* end);+ Offset inc(const char* begin, const char* end) const;++ // init/create instance from const char substring+ static Offset init(const char* beg, const char* end);++ public: // overload operators for position+ void operator+= (const Offset &pos);+ bool operator== (const Offset &pos) const;+ bool operator!= (const Offset &pos) const;+ Offset operator+ (const Offset &off) const;+ Offset operator- (const Offset &off) const;++ public: // overload output stream operator+ // friend ostream& operator<<(ostream& strm, const Offset& off);++ public:+ Offset off() { return *this; };++ public:+ size_t line;+ size_t column;++ };++ class Position : public Offset {++ public: // c-tor+ Position(const size_t file); // line(0), column(0)+ Position(const size_t file, const Offset& offset);+ Position(const size_t line, const size_t column); // file(-1)+ Position(const size_t file, const size_t line, const size_t column);++ public: // overload operators for position+ void operator+= (const Offset &off);+ bool operator== (const Position &pos) const;+ bool operator!= (const Position &pos) const;+ const Position operator+ (const Offset &off) const;+ const Offset operator- (const Offset &off) const;+ // return new position, incremented by the given string+ Position add(const char* begin, const char* end);+ Position inc(const char* begin, const char* end) const;++ public: // overload output stream operator+ // friend ostream& operator<<(ostream& strm, const Position& pos);++ public:+ size_t file;++ };++ // Token type for representing lexed chunks of text+ class Token {+ public:+ const char* prefix;+ const char* begin;+ const char* end;++ Token()+ : prefix(0), begin(0), end(0) { }+ Token(const char* b, const char* e)+ : prefix(b), begin(b), end(e) { }+ Token(const char* str)+ : prefix(str), begin(str), end(str + strlen(str)) { }+ Token(const char* p, const char* b, const char* e)+ : prefix(p), begin(b), end(e) { }++ size_t length() const { return end - begin; }+ string ws_before() const { return string(prefix, begin); }+ string to_string() const { return string(begin, end); }++ operator bool() { return begin && end && begin >= end; }+ operator string() { return to_string(); }++ bool operator==(Token t) { return to_string() == t.to_string(); }+ };++ class ParserState : public Position {++ public: // c-tor+ ParserState(string path, const char* src = 0, const size_t file = string::npos);+ ParserState(string path, const char* src, Position position, Offset offset = Offset(0, 0));+ ParserState(string path, const char* src, Token token, Position position, Offset offset = Offset(0, 0));++ public: // down casts+ Offset off() { return *this; };+ Position pos() { return *this; };++ public:+ string path;+ const char* src;+ Offset offset;+ Token token;++ };++}++#endif
+ libsass/posix/getopt.c view
@@ -0,0 +1,562 @@+/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */+/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */++/*+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>+ *+ * 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.+ *+ * Sponsored in part by the Defense Advanced Research Projects+ * Agency (DARPA) and Air Force Research Laboratory, Air Force+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.+ */+/*-+ * Copyright (c) 2000 The NetBSD Foundation, Inc.+ * All rights reserved.+ *+ * This code is derived from software contributed to The NetBSD Foundation+ * by Dieter Baron and Thomas Klausner.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ * notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ * notice, this list of conditions and the following disclaimer in the+ * documentation and/or other materials provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+ * POSSIBILITY OF SUCH DAMAGE.+ */++#include <errno.h>+#include <stdlib.h>+#include <string.h>+#include <getopt.h>+#include <stdarg.h>+#include <stdio.h>+#include <windows.h>++#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */++#ifdef REPLACE_GETOPT+int opterr = 1; /* if error message should be printed */+int optind = 1; /* index into parent argv vector */+int optopt = '?'; /* character checked for validity */+#undef optreset /* see getopt.h */+#define optreset __mingw_optreset+int optreset; /* reset getopt */+char *optarg; /* argument associated with option */+#endif++#define PRINT_ERROR ((opterr) && (*options != ':'))++#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */+#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */+#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */++/* return values */+#define BADCH (int)'?'+#define BADARG ((*options == ':') ? (int)':' : (int)'?')+#define INORDER (int)1++#ifndef __CYGWIN__+#define __progname __argv[0]+#else+extern char __declspec(dllimport) *__progname;+#endif++#ifdef __CYGWIN__+static char EMSG[] = "";+#else+#define EMSG ""+#endif++static int getopt_internal(int, char * const *, const char *,+ const struct option *, int *, int);+static int parse_long_options(char * const *, const char *,+ const struct option *, int *, int);+static int gcd(int, int);+static void permute_args(int, int, int, char * const *);++static char *place = EMSG; /* option letter processing */++/* XXX: set optreset to 1 rather than these two */+static int nonopt_start = -1; /* first non option argument (for permute) */+static int nonopt_end = -1; /* first option after non options (for permute) */++/* Error messages */+static const char recargchar[] = "option requires an argument -- %c";+static const char recargstring[] = "option requires an argument -- %s";+static const char ambig[] = "ambiguous option -- %.*s";+static const char noarg[] = "option doesn't take an argument -- %.*s";+static const char illoptchar[] = "unknown option -- %c";+static const char illoptstring[] = "unknown option -- %s";++static void+_vwarnx(const char *fmt,va_list ap)+{+ (void)fprintf(stderr,"%s: ",__progname);+ if (fmt != NULL)+ (void)vfprintf(stderr,fmt,ap);+ (void)fprintf(stderr,"\n");+}++static void+warnx(const char *fmt,...)+{+ va_list ap;+ va_start(ap,fmt);+ _vwarnx(fmt,ap);+ va_end(ap);+}++/*+ * Compute the greatest common divisor of a and b.+ */+static int+gcd(int a, int b)+{+ int c;++ c = a % b;+ while (c != 0) {+ a = b;+ b = c;+ c = a % b;+ }++ return (b);+}++/*+ * Exchange the block from nonopt_start to nonopt_end with the block+ * from nonopt_end to opt_end (keeping the same order of arguments+ * in each block).+ */+static void+permute_args(int panonopt_start, int panonopt_end, int opt_end,+ char * const *nargv)+{+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;+ char *swap;++ /*+ * compute lengths of blocks and number and size of cycles+ */+ nnonopts = panonopt_end - panonopt_start;+ nopts = opt_end - panonopt_end;+ ncycle = gcd(nnonopts, nopts);+ cyclelen = (opt_end - panonopt_start) / ncycle;++ for (i = 0; i < ncycle; i++) {+ cstart = panonopt_end+i;+ pos = cstart;+ for (j = 0; j < cyclelen; j++) {+ if (pos >= panonopt_end)+ pos -= nnonopts;+ else+ pos += nopts;+ swap = nargv[pos];+ /* LINTED const cast */+ ((char **) nargv)[pos] = nargv[cstart];+ /* LINTED const cast */+ ((char **)nargv)[cstart] = swap;+ }+ }+}++/*+ * parse_long_options --+ * Parse long options in argc/argv argument vector.+ * Returns -1 if short_too is set and the option does not match long_options.+ */+static int+parse_long_options(char * const *nargv, const char *options,+ const struct option *long_options, int *idx, int short_too)+{+ char *current_argv, *has_equal;+ size_t current_argv_len;+ int i, ambiguous, match;++#define IDENTICAL_INTERPRETATION(_x, _y) \+ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \+ long_options[(_x)].flag == long_options[(_y)].flag && \+ long_options[(_x)].val == long_options[(_y)].val)++ current_argv = place;+ match = -1;+ ambiguous = 0;++ optind++;++ if ((has_equal = strchr(current_argv, '=')) != NULL) {+ /* argument found (--option=arg) */+ current_argv_len = has_equal - current_argv;+ has_equal++;+ } else+ current_argv_len = strlen(current_argv);++ for (i = 0; long_options[i].name; i++) {+ /* find matching long option */+ if (strncmp(current_argv, long_options[i].name,+ current_argv_len))+ continue;++ if (strlen(long_options[i].name) == current_argv_len) {+ /* exact match */+ match = i;+ ambiguous = 0;+ break;+ }+ /*+ * If this is a known short option, don't allow+ * a partial match of a single character.+ */+ if (short_too && current_argv_len == 1)+ continue;++ if (match == -1) /* partial match */+ match = i;+ else if (!IDENTICAL_INTERPRETATION(i, match))+ ambiguous = 1;+ }+ if (ambiguous) {+ /* ambiguous abbreviation */+ if (PRINT_ERROR)+ warnx(ambig, (int)current_argv_len,+ current_argv);+ optopt = 0;+ return (BADCH);+ }+ if (match != -1) { /* option found */+ if (long_options[match].has_arg == no_argument+ && has_equal) {+ if (PRINT_ERROR)+ warnx(noarg, (int)current_argv_len,+ current_argv);+ /*+ * XXX: GNU sets optopt to val regardless of flag+ */+ if (long_options[match].flag == NULL)+ optopt = long_options[match].val;+ else+ optopt = 0;+ return (BADARG);+ }+ if (long_options[match].has_arg == required_argument ||+ long_options[match].has_arg == optional_argument) {+ if (has_equal)+ optarg = has_equal;+ else if (long_options[match].has_arg ==+ required_argument) {+ /*+ * optional argument doesn't use next nargv+ */+ optarg = nargv[optind++];+ }+ }+ if ((long_options[match].has_arg == required_argument)+ && (optarg == NULL)) {+ /*+ * Missing argument; leading ':' indicates no error+ * should be generated.+ */+ if (PRINT_ERROR)+ warnx(recargstring,+ current_argv);+ /*+ * XXX: GNU sets optopt to val regardless of flag+ */+ if (long_options[match].flag == NULL)+ optopt = long_options[match].val;+ else+ optopt = 0;+ --optind;+ return (BADARG);+ }+ } else { /* unknown option */+ if (short_too) {+ --optind;+ return (-1);+ }+ if (PRINT_ERROR)+ warnx(illoptstring, current_argv);+ optopt = 0;+ return (BADCH);+ }+ if (idx)+ *idx = match;+ if (long_options[match].flag) {+ *long_options[match].flag = long_options[match].val;+ return (0);+ } else+ return (long_options[match].val);+#undef IDENTICAL_INTERPRETATION+}++/*+ * getopt_internal --+ * Parse argc/argv argument vector. Called by user level routines.+ */+static int+getopt_internal(int nargc, char * const *nargv, const char *options,+ const struct option *long_options, int *idx, int flags)+{+ char *oli; /* option letter list index */+ int optchar, short_too;+ static int posixly_correct = -1;++ if (options == NULL)+ return (-1);++ /*+ * XXX Some GNU programs (like cvs) set optind to 0 instead of+ * XXX using optreset. Work around this braindamage.+ */+ if (optind == 0)+ optind = optreset = 1;++ /*+ * Disable GNU extensions if POSIXLY_CORRECT is set or options+ * string begins with a '+'.+ *+ * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or+ * optreset != 0 for GNU compatibility.+ */+ if (posixly_correct == -1 || optreset != 0)+ posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);+ if (*options == '-')+ flags |= FLAG_ALLARGS;+ else if (posixly_correct || *options == '+')+ flags &= ~FLAG_PERMUTE;+ if (*options == '+' || *options == '-')+ options++;++ optarg = NULL;+ if (optreset)+ nonopt_start = nonopt_end = -1;+start:+ if (optreset || !*place) { /* update scanning pointer */+ optreset = 0;+ if (optind >= nargc) { /* end of argument vector */+ place = EMSG;+ if (nonopt_end != -1) {+ /* do permutation, if we have to */+ permute_args(nonopt_start, nonopt_end,+ optind, nargv);+ optind -= nonopt_end - nonopt_start;+ }+ else if (nonopt_start != -1) {+ /*+ * If we skipped non-options, set optind+ * to the first of them.+ */+ optind = nonopt_start;+ }+ nonopt_start = nonopt_end = -1;+ return (-1);+ }+ if (*(place = nargv[optind]) != '-' ||+ (place[1] == '\0' && strchr(options, '-') == NULL)) {+ place = EMSG; /* found non-option */+ if (flags & FLAG_ALLARGS) {+ /*+ * GNU extension:+ * return non-option as argument to option 1+ */+ optarg = nargv[optind++];+ return (INORDER);+ }+ if (!(flags & FLAG_PERMUTE)) {+ /*+ * If no permutation wanted, stop parsing+ * at first non-option.+ */+ return (-1);+ }+ /* do permutation */+ if (nonopt_start == -1)+ nonopt_start = optind;+ else if (nonopt_end != -1) {+ permute_args(nonopt_start, nonopt_end,+ optind, nargv);+ nonopt_start = optind -+ (nonopt_end - nonopt_start);+ nonopt_end = -1;+ }+ optind++;+ /* process next argument */+ goto start;+ }+ if (nonopt_start != -1 && nonopt_end == -1)+ nonopt_end = optind;++ /*+ * If we have "-" do nothing, if "--" we are done.+ */+ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {+ optind++;+ place = EMSG;+ /*+ * We found an option (--), so if we skipped+ * non-options, we have to permute.+ */+ if (nonopt_end != -1) {+ permute_args(nonopt_start, nonopt_end,+ optind, nargv);+ optind -= nonopt_end - nonopt_start;+ }+ nonopt_start = nonopt_end = -1;+ return (-1);+ }+ }++ /*+ * Check long options if:+ * 1) we were passed some+ * 2) the arg is not just "-"+ * 3) either the arg starts with -- we are getopt_long_only()+ */+ if (long_options != NULL && place != nargv[optind] &&+ (*place == '-' || (flags & FLAG_LONGONLY))) {+ short_too = 0;+ if (*place == '-')+ place++; /* --foo long option */+ else if (*place != ':' && strchr(options, *place) != NULL)+ short_too = 1; /* could be short option too */++ optchar = parse_long_options(nargv, options, long_options,+ idx, short_too);+ if (optchar != -1) {+ place = EMSG;+ return (optchar);+ }+ }++ if ((optchar = (int)*place++) == (int)':' ||+ (optchar == (int)'-' && *place != '\0') ||+ (oli = strchr(options, optchar)) == NULL) {+ /*+ * If the user specified "-" and '-' isn't listed in+ * options, return -1 (non-option) as per POSIX.+ * Otherwise, it is an unknown option character (or ':').+ */+ if (optchar == (int)'-' && *place == '\0')+ return (-1);+ if (!*place)+ ++optind;+ if (PRINT_ERROR)+ warnx(illoptchar, optchar);+ optopt = optchar;+ return (BADCH);+ }+ if (long_options != NULL && optchar == 'W' && oli[1] == ';') {+ /* -W long-option */+ if (*place) /* no space */+ /* NOTHING */;+ else if (++optind >= nargc) { /* no arg */+ place = EMSG;+ if (PRINT_ERROR)+ warnx(recargchar, optchar);+ optopt = optchar;+ return (BADARG);+ } else /* white space */+ place = nargv[optind];+ optchar = parse_long_options(nargv, options, long_options,+ idx, 0);+ place = EMSG;+ return (optchar);+ }+ if (*++oli != ':') { /* doesn't take argument */+ if (!*place)+ ++optind;+ } else { /* takes (optional) argument */+ optarg = NULL;+ if (*place) /* no white space */+ optarg = place;+ else if (oli[1] != ':') { /* arg not optional */+ if (++optind >= nargc) { /* no arg */+ place = EMSG;+ if (PRINT_ERROR)+ warnx(recargchar, optchar);+ optopt = optchar;+ return (BADARG);+ } else+ optarg = nargv[optind];+ }+ place = EMSG;+ ++optind;+ }+ /* dump back option letter */+ return (optchar);+}++#ifdef REPLACE_GETOPT+/*+ * getopt --+ * Parse argc/argv argument vector.+ *+ * [eventually this will replace the BSD getopt]+ */+int+getopt(int nargc, char * const *nargv, const char *options)+{++ /*+ * We don't pass FLAG_PERMUTE to getopt_internal() since+ * the BSD getopt(3) (unlike GNU) has never done this.+ *+ * Furthermore, since many privileged programs call getopt()+ * before dropping privileges it makes sense to keep things+ * as simple (and bug-free) as possible.+ */+ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));+}+#endif /* REPLACE_GETOPT */++/*+ * getopt_long --+ * Parse argc/argv argument vector.+ */+int+getopt_long(int nargc, char * const *nargv, const char *options,+ const struct option *long_options, int *idx)+{++ return (getopt_internal(nargc, nargv, options, long_options, idx,+ FLAG_PERMUTE));+}++/*+ * getopt_long_only --+ * Parse argc/argv argument vector.+ */+int+getopt_long_only(int nargc, char * const *nargv, const char *options,+ const struct option *long_options, int *idx)+{++ return (getopt_internal(nargc, nargv, options, long_options, idx,+ FLAG_PERMUTE|FLAG_LONGONLY));+}
+ libsass/posix/getopt.h view
@@ -0,0 +1,95 @@+#ifndef __GETOPT_H__+/**+ * DISCLAIMER+ * 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.+ */++#define __GETOPT_H__++/* All the headers include this file. */+#include <crtdefs.h>++#ifdef __cplusplus+extern "C" {+#endif++extern int optind; /* index of first non-option in argv */+extern int optopt; /* single option character, as parsed */+extern int opterr; /* flag to enable built-in diagnostics... */+ /* (user may set to zero, to suppress) */++extern char *optarg; /* pointer to argument of current option */++extern int getopt(int nargc, char * const *nargv, const char *options);++#ifdef _BSD_SOURCE+/*+ * BSD adds the non-standard `optreset' feature, for reinitialisation+ * of `getopt' parsing. We support this feature, for applications which+ * proclaim their BSD heritage, before including this header; however,+ * to maintain portability, developers are advised to avoid it.+ */+# define optreset __mingw_optreset+extern int optreset;+#endif+#ifdef __cplusplus+}+#endif+/*+ * POSIX requires the `getopt' API to be specified in `unistd.h';+ * thus, `unistd.h' includes this header. However, we do not want+ * to expose the `getopt_long' or `getopt_long_only' APIs, when+ * included in this manner. Thus, close the standard __GETOPT_H__+ * declarations block, and open an additional __GETOPT_LONG_H__+ * specific block, only when *not* __UNISTD_H_SOURCED__, in which+ * to declare the extended API.+ */+#endif /* !defined(__GETOPT_H__) */++#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__)+#define __GETOPT_LONG_H__++#ifdef __cplusplus+extern "C" {+#endif++struct option /* specification for a long form option... */+{+ const char *name; /* option name, without leading hyphens */+ int has_arg; /* does it take an argument? */+ int *flag; /* where to save its status, or NULL */+ int val; /* its associated status value */+};++enum /* permitted values for its `has_arg' field... */+{+ no_argument = 0, /* option never takes an argument */+ required_argument, /* option always requires an argument */+ optional_argument /* option may take an argument */+};++extern int getopt_long(int nargc, char * const *nargv, const char *options,+ const struct option *long_options, int *idx);+extern int getopt_long_only(int nargc, char * const *nargv, const char *options,+ const struct option *long_options, int *idx);+/*+ * Previous MinGW implementation had...+ */+#ifndef HAVE_DECL_GETOPT+/*+ * ...for the long form API only; keep this for compatibility.+ */+# define HAVE_DECL_GETOPT 1+#endif++#ifdef __cplusplus+}+#endif++#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
+ libsass/prelexer.cpp view
@@ -0,0 +1,724 @@+#include <cctype>+#include <cstddef>+#include <iostream>+#include <iomanip>+#include "util.hpp"+#include "position.hpp"+#include "prelexer.hpp"+#include "constants.hpp"+++namespace Sass {+ // using namespace Lexer;+ using namespace Constants;++ namespace Prelexer {+++ // Match a line comment (/.*?(?=\n|\r\n?|\Z)/.+ const char* line_comment(const char* src)+ {+ return sequence<+ exactly <+ slash_slash+ >,+ non_greedy<+ any_char,+ end_of_line+ >+ >(src);+ }++ // Match a block comment.+ const char* block_comment(const char* src)+ {+ return sequence<+ zero_plus < space >,+ delimited_by<slash_star, star_slash, false> >(src);+ }+ /* not use anymore - remove?+ const char* block_comment_prefix(const char* src) {+ return exactly<slash_star>(src);+ }+ // Match either comment.+ const char* comment(const char* src) {+ return line_comment(src);+ }+ */++ // Match zero plus white-space or line_comments+ const char* optional_css_whitespace(const char* src) {+ return zero_plus< alternatives<spaces, line_comment> >(src);+ }+ const char* css_whitespace(const char* src) {+ return one_plus< alternatives<spaces, line_comment> >(src);+ }+ // Match optional_css_whitepace plus block_comments+ const char* optional_css_comments(const char* src) {+ return zero_plus< alternatives<spaces, line_comment, block_comment> >(src);+ }+ const char* css_comments(const char* src) {+ return one_plus< alternatives<spaces, line_comment, block_comment> >(src);+ }++ // Match one backslash escaped char /\\./+ const char* escape_seq(const char* src)+ {+ return sequence<+ exactly<'\\'>,+ any_char+ >(src);+ }++ // Match identifier start+ const char* identifier_alpha(const char* src)+ {+ return alternatives<+ alpha,+ unicode,+ exactly<'-'>,+ exactly<'_'>,+ escape_seq+ >(src);+ }++ // Match identifier after start+ const char* identifier_alnum(const char* src)+ {+ return alternatives<+ alnum,+ unicode,+ exactly<'-'>,+ exactly<'_'>,+ escape_seq+ >(src);+ }++ // Match CSS identifiers.+ const char* identifier(const char* src)+ {+ return sequence<+ zero_plus< exactly<'-'> >,+ one_plus < identifier_alpha >,+ zero_plus < identifier_alnum >+ // word_boundary not needed+ >(src);+ }++ const char* identifier_alnums(const char* src)+ {+ return one_plus< identifier_alnum >(src);+ }++ // Match number prefix ([\+\-]+)+ const char* number_prefix(const char* src) {+ return alternatives <+ exactly < '+' >,+ sequence <+ exactly < '-' >,+ optional_css_whitespace,+ exactly< '-' >+ >+ >(src);+ }++ // Match interpolant schemas+ const char* identifier_schema(const char* src) {+ // follows this pattern: (x*ix*)+ ... well, not quite+ return sequence< one_plus< sequence< zero_plus< alternatives< identifier, exactly<'-'> > >,+ interpolant,+ zero_plus< alternatives< identifier, number, exactly<'-'> > > > >,+ negate< exactly<'%'> > >(src);+ }++ // interpolants can be recursive/nested+ const char* interpolant(const char* src) {+ return recursive_scopes< exactly<hash_lbrace>, exactly<rbrace> >(src);+ }++ // $re_squote = /'(?:$re_itplnt|\\.|[^'])*'/+ const char* single_quoted_string(const char* src) {+ // match a single quoted string, while skipping interpolants+ return sequence <+ exactly <'\''>,+ zero_plus <+ alternatives <+ // skip all escaped chars first+ escape_seq,+ // skip interpolants+ interpolant,+ // skip non delimiters+ any_char_but < '\'' >+ >+ >,+ exactly <'\''>+ >(src);+ }++ // $re_dquote = /"(?:$re_itp|\\.|[^"])*"/+ const char* double_quoted_string(const char* src) {+ // match a single quoted string, while skipping interpolants+ return sequence <+ exactly <'"'>,+ zero_plus <+ alternatives <+ // skip all escaped chars first+ escape_seq,+ // skip interpolants+ interpolant,+ // skip non delimiters+ any_char_but < '"' >+ >+ >,+ exactly <'"'>+ >(src);+ }++ // $re_quoted = /(?:$re_squote|$re_dquote)/+ const char* quoted_string(const char* src) {+ // match a quoted string, while skipping interpolants+ return alternatives<+ single_quoted_string,+ double_quoted_string+ >(src);+ }++ const char* 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);+ }++ /* not used anymore - remove?+ const char* filename(const char* src) {+ return one_plus< alternatives< identifier, number, exactly<'.'> > >(src);+ }+ */++ // Match CSS '@' keywords.+ const char* at_keyword(const char* src) {+ return sequence<exactly<'@'>, identifier>(src);+ }++ const char* kwd_sel_deep(const char* src) {+ return word<sel_deep_kwd>(src);+ }++ const char* kwd_import(const char* src) {+ return word<import_kwd>(src);+ }++ const char* kwd_at_root(const char* src) {+ return word<at_root_kwd>(src);+ }++ const char* kwd_with_directive(const char* src) {+ return word<with_kwd>(src);+ }++ const char* kwd_without_directive(const char* src) {+ return word<without_kwd>(src);+ }++ const char* kwd_media(const char* src) {+ return word<media_kwd>(src);+ }++ const char* kwd_supports(const char* src) {+ return word<supports_kwd>(src);+ }++ const char* kwd_mixin(const char* src) {+ return word<mixin_kwd>(src);+ }++ const char* kwd_function(const char* src) {+ return word<function_kwd>(src);+ }++ const char* kwd_return_directive(const char* src) {+ return word<return_kwd>(src);+ }++ const char* kwd_include(const char* src) {+ return word<include_kwd>(src);+ }++ const char* kwd_content(const char* src) {+ return word<content_kwd>(src);+ }++ const char* kwd_extend(const char* src) {+ return word<extend_kwd>(src);+ }+++ const char* kwd_if_directive(const char* src) {+ return word<if_kwd>(src);+ }++ const char* kwd_else_directive(const char* src) {+ return word<else_kwd>(src);+ }+ const char* elseif_directive(const char* src) {+ return sequence< exactly< else_kwd >,+ optional_css_whitespace,+ word< if_after_else_kwd > >(src);+ }++ const char* kwd_for_directive(const char* src) {+ return word<for_kwd>(src);+ }++ const char* kwd_from(const char* src) {+ return word<from_kwd>(src);+ }++ const char* kwd_to(const char* src) {+ return word<to_kwd>(src);+ }++ const char* kwd_through(const char* src) {+ return word<through_kwd>(src);+ }++ const char* kwd_each_directive(const char* src) {+ return word<each_kwd>(src);+ }++ const char* kwd_in(const char* src) {+ return word<in_kwd>(src);+ }++ const char* kwd_while_directive(const char* src) {+ return word<while_kwd>(src);+ }++ const char* name(const char* src) {+ return one_plus< alternatives< alnum,+ exactly<'-'>,+ exactly<'_'>,+ exactly<'\\'> > >(src);+ }++ const char* kwd_warn(const char* src) {+ return word<warn_kwd>(src);+ }++ const char* kwd_err(const char* src) {+ return word<error_kwd>(src);+ }++ const char* kwd_dbg(const char* src) {+ return word<debug_kwd>(src);+ }++ /* not used anymore - remove?+ const char* directive(const char* src) {+ return sequence< exactly<'@'>, identifier >(src);+ } */++ const char* kwd_null(const char* src) {+ return word<null_kwd>(src);+ }++ // Match CSS type selectors+ const char* namespace_prefix(const char* src) {+ return sequence< optional< alternatives< identifier, exactly<'*'> > >,+ exactly<'|'> >(src);+ }+ const char* type_selector(const char* src) {+ return sequence< optional<namespace_prefix>, identifier>(src);+ }+ const char* hyphens_and_identifier(const char* src) {+ return sequence< zero_plus< exactly< '-' > >, identifier >(src);+ }+ const char* hyphens_and_name(const char* src) {+ return sequence< zero_plus< exactly< '-' > >, name >(src);+ }+ const char* universal(const char* src) {+ return sequence< optional<namespace_prefix>, exactly<'*'> >(src);+ }+ // Match CSS id names.+ const char* id_name(const char* src) {+ return sequence<exactly<'#'>, name>(src);+ }+ // Match CSS class names.+ const char* class_name(const char* src) {+ return sequence<exactly<'.'>, identifier>(src);+ }+ // Attribute name in an attribute selector.+ const char* attribute_name(const char* src) {+ return alternatives< sequence< optional<namespace_prefix>, identifier>,+ identifier >(src);+ }+ // match placeholder selectors+ const char* placeholder(const char* src) {+ return sequence<exactly<'%'>, identifier>(src);+ }+ // Match CSS numeric constants.++ const char* sign(const char* src) {+ return class_char<sign_chars>(src);+ }+ const char* unsigned_number(const char* src) {+ return alternatives<sequence< zero_plus<digits>,+ exactly<'.'>,+ one_plus<digits> >,+ digits>(src);+ }+ const char* number(const char* src) {+ return sequence< optional<sign>, unsigned_number>(src);+ }+ const char* coefficient(const char* src) {+ return alternatives< sequence< optional<sign>, digits >,+ sign >(src);+ }+ const char* binomial(const char* src) {+ return sequence< optional<sign>,+ optional<digits>,+ exactly<'n'>,+ zero_plus < space >,+ sign,+ zero_plus < space >,+ digits >(src);+ }+ const char* percentage(const char* src) {+ return sequence< number, exactly<'%'> >(src);+ }+ const char* ampersand(const char* src) {+ return exactly<'&'>(src);+ }++ /* not used anymore - remove?+ const char* em(const char* src) {+ return sequence< number, exactly<em_kwd> >(src);+ } */+ const char* dimension(const char* src) {+ return sequence<number, one_plus< alpha > >(src);+ }+ const char* hex(const char* src) {+ const char* p = sequence< exactly<'#'>, one_plus<xdigit> >(src);+ ptrdiff_t len = p - src;+ return (len != 4 && len != 7) ? 0 : p;+ }+ const char* hexa(const char* src) {+ const char* p = sequence< exactly<'#'>, one_plus<xdigit> >(src);+ ptrdiff_t len = p - src;+ return (len != 4 && len != 7 && len != 9) ? 0 : p;+ }+ const char* hex0(const char* src) {+ const char* p = sequence< exactly<'0'>, exactly<'x'>, one_plus<xdigit> >(src);+ ptrdiff_t len = p - src;+ return (len != 5 && len != 8) ? 0 : p;+ }++ /* no longer used - remove?+ const char* rgb_prefix(const char* src) {+ return word<rgb_kwd>(src);+ }*/+ // Match CSS uri specifiers.++ const char* uri_prefix(const char* src) {+ return exactly<url_kwd>(src);+ }+ const char* uri_value(const char* src)+ {+ return+ zero_plus <+ alternatives <+ alnum,+ exactly <'/'>,+ class_char < uri_chars >+ >+ >(src);+ }++ // TODO: rename the following two functions+ /* no longer used - remove?+ const char* uri(const char* src) {+ return sequence< exactly<url_kwd>,+ optional<spaces>,+ quoted_string,+ optional<spaces>,+ exactly<')'> >(src);+ }*/+ /* no longer used - remove?+ const char* url_value(const char* src) {+ return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol+ one_plus< sequence< zero_plus< exactly<'/'> >, filename > >, // one or more folders and/or trailing filename+ optional< exactly<'/'> > >(src);+ }*/+ /* no longer used - remove?+ const char* url_schema(const char* src) {+ return sequence< optional< sequence< identifier, exactly<':'> > >, // optional protocol+ filename_schema >(src); // optional trailing slash+ }*/+ // Match CSS "!important" keyword.+ const char* important(const char* src) {+ return sequence< exactly<'!'>,+ optional_css_whitespace,+ word<important_kwd> >(src);+ }+ // Match CSS "!optional" keyword.+ const char* optional(const char* src) {+ return sequence< exactly<'!'>,+ optional_css_whitespace,+ word<optional_kwd> >(src);+ }+ // Match Sass "!default" keyword.+ const char* default_flag(const char* src) {+ return sequence< exactly<'!'>,+ optional_css_whitespace,+ word<default_kwd> >(src);+ }+ // Match Sass "!global" keyword.+ const char* global_flag(const char* src) {+ return sequence< exactly<'!'>,+ optional_css_whitespace,+ word<global_kwd> >(src);+ }+ // Match CSS pseudo-class/element prefixes.+ const char* pseudo_prefix(const char* src) {+ return sequence< exactly<':'>, optional< exactly<':'> > >(src);+ }+ // Match CSS function call openers.+ const char* functional_schema(const char* src) {+ return sequence< identifier_schema, exactly<'('> >(src);+ }+ const char* functional(const char* src) {+ return sequence< identifier, exactly<'('> >(src);+ }+ // Match the CSS negation pseudo-class.+ const char* pseudo_not(const char* src) {+ return word< pseudo_not_kwd >(src);+ }+ // Match CSS 'odd' and 'even' keywords for functional pseudo-classes.+ const char* even(const char* src) {+ return word<even_kwd>(src);+ }+ const char* odd(const char* src) {+ return word<odd_kwd>(src);+ }+ // Match CSS attribute-matching operators.+ const char* exact_match(const char* src) { return exactly<'='>(src); }+ const char* class_match(const char* src) { return exactly<tilde_equal>(src); }+ const char* dash_match(const char* src) { return exactly<pipe_equal>(src); }+ const char* prefix_match(const char* src) { return exactly<caret_equal>(src); }+ const char* suffix_match(const char* src) { return exactly<dollar_equal>(src); }+ const char* substring_match(const char* src) { return exactly<star_equal>(src); }+ // Match CSS combinators.+ /* not used anymore - remove?+ const char* adjacent_to(const char* src) {+ return sequence< optional_spaces, exactly<'+'> >(src);+ }+ const char* precedes(const char* src) {+ return sequence< optional_spaces, exactly<'~'> >(src);+ }+ const char* parent_of(const char* src) {+ return sequence< optional_spaces, exactly<'>'> >(src);+ }+ const char* ancestor_of(const char* src) {+ return sequence< spaces, negate< exactly<'{'> > >(src);+ }*/++ // Match SCSS variable names.+ const char* variable(const char* src) {+ return sequence<exactly<'$'>, identifier>(src);+ }++ // Match Sass boolean keywords.+ const char* kwd_true(const char* src) {+ return word<true_kwd>(src);+ }+ const char* kwd_false(const char* src) {+ return word<false_kwd>(src);+ }+ const char* kwd_and(const char* src) {+ return word<and_kwd>(src);+ }+ const char* kwd_or(const char* src) {+ return word<or_kwd>(src);+ }+ const char* kwd_not(const char* src) {+ return word<not_kwd>(src);+ }+ const char* kwd_eq(const char* src) {+ return exactly<eq>(src);+ }+ const char* kwd_neq(const char* src) {+ return exactly<neq>(src);+ }+ const char* kwd_gt(const char* src) {+ return exactly<gt>(src);+ }+ const char* kwd_gte(const char* src) {+ return exactly<gte>(src);+ }+ const char* kwd_lt(const char* src) {+ return exactly<lt>(src);+ }+ const char* kwd_lte(const char* src) {+ return exactly<lte>(src);+ }++ // match specific IE syntax+ const char* ie_progid(const char* src) {+ return sequence <+ word<progid_kwd>,+ exactly<':'>,+ alternatives< identifier_schema, identifier >,+ zero_plus< sequence<+ exactly<'.'>,+ alternatives< identifier_schema, identifier >+ > >,+ zero_plus < sequence<+ exactly<'('>,+ optional_css_whitespace,+ optional < sequence<+ alternatives< variable, identifier_schema, identifier >,+ optional_css_whitespace,+ exactly<'='>,+ optional_css_whitespace,+ alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >,+ zero_plus< sequence<+ optional_css_whitespace,+ exactly<','>,+ optional_css_whitespace,+ sequence<+ alternatives< variable, identifier_schema, identifier >,+ optional_css_whitespace,+ exactly<'='>,+ optional_css_whitespace,+ alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >+ >+ > >+ > >,+ optional_css_whitespace,+ exactly<')'>+ > >+ >(src);+ }+ const char* ie_expression(const char* src) {+ return sequence < word<expression_kwd>, delimited_by< '(', ')', true> >(src);+ }+ const char* ie_property(const char* src) {+ return alternatives < ie_expression, ie_progid >(src);+ }++ // const char* ie_args(const char* src) {+ // return sequence< alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by< '(', ')', true> >,+ // zero_plus< sequence< optional_css_whitespace, exactly<','>, optional_css_whitespace, alternatives< ie_keyword_arg, value_schema, quoted_string, interpolant, number, identifier, delimited_by<'(', ')', true> > > > >(src);+ // }++ const char* ie_keyword_arg(const char* src) {+ return sequence<+ alternatives< variable, identifier_schema, identifier >,+ optional_css_whitespace,+ exactly<'='>,+ optional_css_whitespace,+ alternatives< variable, identifier_schema, identifier, quoted_string, number, hexa >+ >(src);+ }++ // Path matching functions.+ /* not used anymore - remove?+ const char* folder(const char* src) {+ return sequence< zero_plus< any_char_except<'/'> >,+ exactly<'/'> >(src);+ }+ const char* folders(const char* src) {+ return zero_plus< folder >(src);+ }*/+ /* not used anymore - remove?+ const char* chunk(const char* src) {+ char inside_str = 0;+ const char* p = src;+ size_t depth = 0;+ while (true) {+ if (!*p) {+ return 0;+ }+ else if (!inside_str && (*p == '"' || *p == '\'')) {+ inside_str = *p;+ }+ else if (*p == inside_str && *(p-1) != '\\') {+ inside_str = 0;+ }+ else if (*p == '(' && !inside_str) {+ ++depth;+ }+ else if (*p == ')' && !inside_str) {+ if (depth == 0) return p;+ else --depth;+ }+ ++p;+ }+ // unreachable+ return 0;+ }+ */++ // follow the CSS spec more closely and see if this helps us scan URLs correctly+ /* not used anymore - remove?+ const char* NL(const char* src) {+ return alternatives< exactly<'\n'>,+ sequence< exactly<'\r'>, exactly<'\n'> >,+ exactly<'\r'>,+ exactly<'\f'> >(src);+ }*/++ /* not used anymore - remove?+ const char* H(const char* src) {+ return std::isxdigit(*src) ? src+1 : 0;+ }*/++ /* not used anymore - remove?+ const char* unicode(const char* src) {+ return sequence< exactly<'\\'>,+ between<H, 1, 6>,+ optional< class_char<url_space_chars> > >(src);+ }*/++ /* not used anymore - remove?+ const char* ESCAPE(const char* src) {+ return alternatives< unicode, class_char<escape_chars> >(src);+ }*/++ const char* static_string(const char* src) {+ const char* pos = src;+ const char * s = quoted_string(pos);+ Token t(pos, s);+ const unsigned int p = count_interval< interpolant >(t.begin, t.end);+ return (p == 0) ? t.end : 0;+ }++ const char* static_component(const char* src) {+ return alternatives< identifier,+ static_string,+ percentage,+ hex,+ number,+ sequence< exactly<'!'>, word<important_kwd> >+ >(src);+ }++ const char* static_value(const char* src) {+ return sequence< static_component,+ zero_plus < sequence<+ alternatives<+ sequence< optional_spaces, alternatives<+ exactly < '/' >,+ exactly < ',' >,+ exactly < ' ' >+ >, optional_spaces >,+ spaces+ >,+ static_component+ > >,+ alternatives< exactly<';'>, exactly<'}'> >+ >(src);+ }+ }+}
+ libsass/prelexer.hpp view
@@ -0,0 +1,360 @@+#ifndef SASS_PRELEXER_H+#define SASS_PRELEXER_H++#include <cstring>+#include "lexer.hpp"++namespace Sass {+ // using namespace Lexer;+ namespace Prelexer {++ //####################################+ // KEYWORD "REGEX" MATCHERS+ //####################################++ // Match Sass boolean keywords.+ const char* kwd_true(const char* src);+ const char* kwd_false(const char* src);+ const char* kwd_and(const char* src);+ const char* kwd_or(const char* src);+ const char* kwd_not(const char* src);+ const char* kwd_eq(const char* src);+ const char* kwd_neq(const char* src);+ const char* kwd_gt(const char* src);+ const char* kwd_gte(const char* src);+ const char* kwd_lt(const char* src);+ const char* kwd_lte(const char* src);++ // Match standard control chars+ const char* kwd_at(const char* src);+ const char* kwd_dot(const char* src);+ const char* kwd_comma(const char* src);+ const char* kwd_colon(const char* src);+ const char* kwd_slash(const char* src);+ const char* kwd_star(const char* src);+ const char* kwd_plus(const char* src);+ const char* kwd_minus(const char* src);++ //####################################+ // SPECIAL "REGEX" CONSTRUCTS+ //####################################++ // Match a sequence of characters delimited by the supplied chars.+ template <char beg, char end, bool esc>+ const char* delimited_by(const char* src) {+ src = exactly<beg>(src);+ if (!src) return 0;+ const char* stop;+ while (1) {+ if (!*src) return 0;+ stop = exactly<end>(src);+ if (stop && (!esc || *(src - 1) != '\\')) return stop;+ src = stop ? stop : src + 1;+ }+ }++ // skip to delimiter (mx) inside given range+ // this will savely skip over all quoted strings+ // recursive skip stuff delimited by start/stop+ // first start/opener must be consumed already!+ template<prelexer start, prelexer stop>+ const char* skip_over_scopes(const char* src, const char* end = 0) {++ size_t level = 0;+ bool in_squote = false;+ bool in_dquote = false;+ // bool in_braces = false;++ while (*src) {++ // check for abort condition+ if (end && src >= end) break;++ // has escaped sequence?+ if (*src == '\\') {+ ++ src; // skip this (and next)+ }+ else if (*src == '"') {+ in_dquote = ! in_dquote;+ }+ else if (*src == '\'') {+ in_squote = ! in_squote;+ }+ else if (in_dquote || in_squote) {+ // take everything literally+ }++ // find another opener inside?+ else if (start(src)) {+ ++ level; // increase counter+ }++ // look for the closer (maybe final, maybe not)+ else if (const char* final = stop(src)) {+ // only close one level?+ if (level > 0) -- level;+ // return position at end of stop+ // delimiter may be multiple chars+ else return final;+ }++ // next+ ++ src;+ }++ return 0;+ }++ // Match a sequence of characters delimited by the supplied chars.+ template <prelexer start, prelexer stop>+ const char* recursive_scopes(const char* src) {+ // parse opener+ src = start(src);+ // abort if not found+ if (!src) return 0;+ // parse the rest until final closer+ return skip_over_scopes<start, stop>(src);+ }++ // Match a sequence of characters delimited by the supplied strings.+ template <const char* beg, const char* end, bool esc>+ const char* delimited_by(const char* src) {+ src = exactly<beg>(src);+ if (!src) return 0;+ const char* stop;+ while (1) {+ if (!*src) return 0;+ stop = exactly<end>(src);+ if (stop && (!esc || *(src - 1) != '\\')) return stop;+ src = stop ? stop : src + 1;+ }+ }++ // Tries to match a certain number of times (between the supplied interval).+ template<prelexer mx, size_t lo, size_t hi>+ const char* between(const char* src) {+ for (size_t i = 0; i < lo; ++i) {+ src = mx(src);+ if (!src) return 0;+ }+ for (size_t i = lo; i <= hi; ++i) {+ const char* new_src = mx(src);+ if (!new_src) return src;+ src = new_src;+ }+ return src;+ }++ // Match a line comment.+ const char* line_comment(const char* src);+ const char* line_comment_prefix(const char* src);++ // Match a block comment.+ const char* block_comment(const char* src);+ const char* block_comment_prefix(const char* src);+ // Match either.+ const char* comment(const char* src);+ // Match double- and single-quoted strings.+ const char* double_quoted_string(const char* src);+ const char* single_quoted_string(const char* src);+ const char* quoted_string(const char* src);+ // Match interpolants.+ const char* interpolant(const char* src);+ // Match number prefix ([\+\-]+)+ const char* number_prefix(const char* src);++ // Match zero plus white-space or line_comments+ const char* optional_css_whitespace(const char* src);+ const char* css_whitespace(const char* src);+ // Match optional_css_whitepace plus block_comments+ const char* optional_css_comments(const char* src);+ const char* css_comments(const char* src);++ // Match one backslash escaped char+ const char* escape_seq(const char* src);++ // Match CSS css variables.+ const char* custom_property_name(const char* src);+ // Match a CSS identifier.+ const char* identifier(const char* src);+ const char* identifier_alpha(const char* src);+ const char* identifier_alnum(const char* src);+ const char* identifier_alnums(const char* src);+ // Match selector names.+ // const char* sel_ident(const char* src);+ const char* kwd_sel_deep(const char* src);++ // Match interpolant schemas+ const char* identifier_schema(const char* src);+ const char* value_schema(const char* src);+ // const char* filename(const char* src);+ // const char* filename_schema(const char* src);+ // const char* url_schema(const char* src);+ // const char* url_value(const char* src);+ const char* vendor_prefix(const char* src);+ // Match CSS '@' keywords.+ const char* at_keyword(const char* src);+ const char* kwd_import(const char* src);+ const char* kwd_at_root(const char* src);+ const char* kwd_with_directive(const char* src);+ const char* kwd_without_directive(const char* src);+ const char* kwd_media(const char* src);+ const char* kwd_supports(const char* src);+ // const char* keyframes(const char* src);+ // const char* keyf(const char* src);+ const char* kwd_mixin(const char* src);+ const char* kwd_function(const char* src);+ const char* kwd_return_directive(const char* src);+ const char* kwd_include(const char* src);+ const char* kwd_content(const char* src);+ const char* kwd_extend(const char* src);++ const char* kwd_if_directive(const char* src);+ const char* kwd_else_directive(const char* src);+ const char* elseif_directive(const char* src);++ const char* kwd_for_directive(const char* src);+ const char* kwd_from(const char* src);+ const char* kwd_to(const char* src);+ const char* kwd_through(const char* src);++ const char* kwd_each_directive(const char* src);+ const char* kwd_in(const char* src);++ const char* kwd_while_directive(const char* src);++ const char* kwd_warn(const char* src);+ const char* kwd_err(const char* src);+ const char* kwd_dbg(const char* src);++ const char* kwd_null(const char* src);++ // Match CSS type selectors+ const char* namespace_prefix(const char* src);+ const char* type_selector(const char* src);+ const char* hyphens_and_identifier(const char* src);+ const char* hyphens_and_name(const char* src);+ const char* universal(const char* src);+ // Match CSS id names.+ const char* id_name(const char* src);+ // Match CSS class names.+ const char* class_name(const char* src);+ // Attribute name in an attribute selector+ const char* attribute_name(const char* src);+ // Match placeholder selectors.+ const char* placeholder(const char* src);+ // Match CSS numeric constants.+ const char* sign(const char* src);+ const char* unsigned_number(const char* src);+ const char* number(const char* src);+ const char* coefficient(const char* src);+ const char* binomial(const char* src);+ const char* percentage(const char* src);+ const char* ampersand(const char* src);+ const char* dimension(const char* src);+ const char* hex(const char* src);+ const char* hexa(const char* src);+ const char* hex0(const char* src);+ // const char* rgb_prefix(const char* src);+ // Match CSS uri specifiers.+ const char* uri_prefix(const char* src);+ const char* uri_value(const char* src);+ // Match CSS "!important" keyword.+ const char* important(const char* src);+ // Match CSS "!optional" keyword.+ const char* optional(const char* src);+ // Match Sass "!default" keyword.+ const char* default_flag(const char* src);+ const char* global_flag(const char* src);+ // Match CSS pseudo-class/element prefixes+ const char* pseudo_prefix(const char* src);+ // Match CSS function call openers.+ const char* functional(const char* src);+ const char* functional_schema(const char* src);+ const char* pseudo_not(const char* src);+ // Match CSS 'odd' and 'even' keywords for functional pseudo-classes.+ const char* even(const char* src);+ const char* odd(const char* src);+ // Match CSS attribute-matching operators.+ const char* exact_match(const char* src);+ const char* class_match(const char* src);+ const char* dash_match(const char* src);+ const char* prefix_match(const char* src);+ const char* suffix_match(const char* src);+ const char* substring_match(const char* src);+ // Match CSS combinators.+ // const char* adjacent_to(const char* src);+ // const char* precedes(const char* src);+ // const char* parent_of(const char* src);+ // const char* ancestor_of(const char* src);++ // Match SCSS variable names.+ const char* variable(const char* src);++ // IE stuff+ const char* ie_progid(const char* src);+ const char* ie_expression(const char* src);+ const char* ie_property(const char* src);+ const char* ie_keyword_arg(const char* src);++ // match urls+ const char* url(const char* src);++ // Path matching functions.+ // const char* folder(const char* src);+ // const char* folders(const char* src);+++ const char* static_string(const char* src);+ const char* static_component(const char* src);+ const char* static_value(const char* src);++ // Utility functions for finding and counting characters in a string.+ template<char c>+ const char* find_first(const char* src) {+ while (*src && *src != c) ++src;+ return *src ? src : 0;+ }+ template<prelexer mx>+ const char* find_first(const char* src) {+ while (*src && !mx(src)) ++src;+ return *src ? src : 0;+ }+ template<prelexer mx>+ const char* find_first_in_interval(const char* beg, const char* end) {+ bool esc = false;+ while ((beg < end) && *beg) {+ if (esc) esc = false;+ else if (*beg == '\\') esc = true;+ else if (mx(beg)) return beg;+ ++beg;+ }+ return 0;+ }+ template <prelexer mx>+ unsigned int count_interval(const char* beg, const char* end) {+ unsigned int counter = 0;+ bool esc = false;+ while (beg < end && *beg) {+ const char* p;+ if (esc) {+ esc = false;+ ++beg;+ } else if (*beg == '\\') {+ esc = true;+ ++beg;+ } else if ((p = mx(beg))) {+ ++counter;+ beg = p;+ }+ else {+ ++beg;+ }+ }+ return counter;+ }++ }+}++#endif
+ libsass/remove_placeholders.cpp view
@@ -0,0 +1,59 @@+#include "remove_placeholders.hpp"+#include "context.hpp"+#include "inspect.hpp"+#include "to_string.hpp"+#include <iostream>++namespace Sass {++ Remove_Placeholders::Remove_Placeholders(Context& ctx)+ : ctx(ctx)+ { }++ template<typename T>+ void Remove_Placeholders::clean_selector_list(T r) {++ // Create a new selector group without placeholders+ Selector_List* sl = static_cast<Selector_List*>(r->selector());++ if (sl) {+ Selector_List* new_sl = new (ctx.mem) Selector_List(sl->pstate());++ for (size_t i = 0, L = sl->length(); i < L; ++i) {+ if (!(*sl)[i]->has_placeholder()) {+ *new_sl << (*sl)[i];+ }+ }++ // Set the new placeholder selector list+ r->selector(new_sl);+ }++ // Iterate into child blocks+ Block* b = r->block();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ stm->perform(this);+ }+ }++ void Remove_Placeholders::operator()(Block* b) {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ (*b)[i]->perform(this);+ }+ }++ void Remove_Placeholders::operator()(Ruleset* r) {+ clean_selector_list(r);+ }++ void Remove_Placeholders::operator()(Media_Block* m) {+ clean_selector_list(m);+ }++ void Remove_Placeholders::operator()(At_Rule* a) {+ if (a->block()) a->block()->perform(this);+ }++}
+ libsass/remove_placeholders.hpp view
@@ -0,0 +1,43 @@+#ifndef SASS_REMOVE_PLACEHOLDERS_H+#define SASS_REMOVE_PLACEHOLDERS_H++#pragma once++#include <iostream>++#include "ast.hpp"+#include "operation.hpp"++namespace Sass {++ using namespace std;++ class Context;++ class Remove_Placeholders : public Operation_CRTP<void, Remove_Placeholders> {++ Context& ctx;++ void fallback_impl(AST_Node* n) {};++ public:+ Remove_Placeholders(Context&);+ virtual ~Remove_Placeholders() { }++ using Operation<void>::operator();++ void operator()(Block*);+ void operator()(Ruleset*);+ void operator()(Media_Block*);+ void operator()(At_Rule*);++ template <typename T>+ void clean_selector_list(T r);++ template <typename U>+ void fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/sass.cpp view
@@ -0,0 +1,43 @@+#include <cstdlib>+#include <cstring>+#include <vector>+#include <sstream>++#include "sass.h"+#include "file.hpp"+#include "util.hpp"++extern "C" {+ using namespace std;+ using namespace Sass;+ using namespace File;++ // caller must free the returned memory+ char* ADDCALL sass_string_quote (const char *str, const char quote_mark)+ {+ string quoted = quote(str, quote_mark);+ return sass_strdup(quoted.c_str());+ }++ // caller must free the returned memory+ char* ADDCALL sass_string_unquote (const char *str)+ {+ string unquoted = unquote(str);+ return sass_strdup(unquoted.c_str());+ }++ // Make sure to free the returned value!+ // Incs array has to be null terminated!+ char* ADDCALL sass_resolve_file (const char* file, const char* paths[])+ {+ string resolved(find_file(file, paths));+ return sass_strdup(resolved.c_str());+ }++ // Get compiled libsass version+ const char* ADDCALL libsass_version(void)+ {+ return LIBSASS_VERSION;+ }++}
+ libsass/sass.h view
@@ -0,0 +1,68 @@+#ifndef SASS_H+#define SASS_H++#include <stddef.h>+#include <stdbool.h>++#ifdef __GNUC__+ #define DEPRECATED(func) func __attribute__ ((deprecated))+#elif defined(_MSC_VER)+ #define DEPRECATED(func) __declspec(deprecated) func+#else+ #pragma message("WARNING: You need to implement DEPRECATED for this compiler")+ #define DEPRECATED(func) func+#endif++#ifdef _WIN32++ /* You should define ADD_EXPORTS *only* when building the DLL. */+ #ifdef ADD_EXPORTS+ #define ADDAPI __declspec(dllexport)+ #define ADDCALL __cdecl+ #else+ #define ADDAPI+ #define ADDCALL+ #endif++#else /* _WIN32 not defined. */++ /* Define with no value on non-Windows OSes. */+ #define ADDAPI+ #define ADDCALL++#endif++// include API headers+#include "sass_version.h"+#include "sass_values.h"+#include "sass_functions.h"++/* Make sure functions are exported with C linkage under C++ compilers. */+#ifdef __cplusplus+extern "C" {+#endif+++// Different render styles+enum Sass_Output_Style {+ SASS_STYLE_NESTED,+ SASS_STYLE_EXPANDED,+ SASS_STYLE_COMPACT,+ SASS_STYLE_COMPRESSED+};++// Some convenient string helper function+ADDAPI char* ADDCALL sass_string_quote (const char* str, const char quote_mark);+ADDAPI char* ADDCALL sass_string_unquote (const char* str);++// Resolve a file via the given include paths in the include char* array+ADDAPI char* ADDCALL sass_resolve_file (const char* path, const char* incs[]);++// Get compiled libsass version+ADDAPI const char* ADDCALL libsass_version(void);++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/sass2scss.cpp view
@@ -0,0 +1,835 @@+// include library+#include <stack>+#include <string>+#include <cstring>+#include <cstdlib>+#include <sstream>+#include <iostream>+#include <stdio.h>++///*+//+// src comments: comments in sass syntax (staring with //)+// css comments: multiline comments in css syntax (starting with /*)+//+// KEEP_COMMENT: keep src comments in the resulting css code+// STRIP_COMMENT: strip out all comments (either src or css)+// CONVERT_COMMENT: convert all src comments to css comments+//+//*/++// our own header+#include "sass2scss.h"++// using std::string+using namespace std;++// add namespace for c+++namespace Sass+{++ // return the actual prettify value from options+ #define PRETTIFY(converter) (converter.options - (converter.options & 248))+ // query the options integer to check if the option is enables+ #define KEEP_COMMENT(converter) ((converter.options & SASS2SCSS_KEEP_COMMENT) == SASS2SCSS_KEEP_COMMENT)+ #define STRIP_COMMENT(converter) ((converter.options & SASS2SCSS_STRIP_COMMENT) == SASS2SCSS_STRIP_COMMENT)+ #define CONVERT_COMMENT(converter) ((converter.options & SASS2SCSS_CONVERT_COMMENT) == SASS2SCSS_CONVERT_COMMENT)++ // some makros to access the indentation stack+ #define INDENT(converter) (converter.indents.top())++ // some makros to query comment parser status+ #define IS_PARSING(converter) (converter.comment == "")+ #define IS_COMMENT(converter) (converter.comment != "")+ #define IS_SRC_COMMENT(converter) (converter.comment == "//" && ! CONVERT_COMMENT(converter))+ #define IS_CSS_COMMENT(converter) (converter.comment == "/*" || (converter.comment == "//" && CONVERT_COMMENT(converter)))++ // pretty printer helper function+ static string closer (const converter& converter)+ {+ return PRETTIFY(converter) == 0 ? " }" :+ PRETTIFY(converter) <= 1 ? " }" :+ "\n" + INDENT(converter) + "}";+ }++ // pretty printer helper function+ static string opener (const converter& converter)+ {+ return PRETTIFY(converter) == 0 ? " { " :+ PRETTIFY(converter) <= 2 ? " {" :+ "\n" + INDENT(converter) + "{";+ }++ // check if the given string is a pseudo selector+ // needed to differentiate from sass property syntax+ static bool isPseudoSelector (string& sel)+ {++ size_t len = sel.length();+ if (len < 1) return false;+ size_t pos = sel.find_first_not_of("abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1);+ if (pos != string::npos) sel.erase(pos, string::npos);+ size_t i = sel.length();+ while (i -- > 0) { sel.at(i) = tolower(sel.at(i)); }++ // CSS Level 1 - Recommendation+ if (sel == ":link") return true;+ if (sel == ":visited") return true;+ if (sel == ":active") return true;++ // CSS Level 2 (Revision 1) - Recommendation+ if (sel == ":lang") return true;+ if (sel == ":first-child") return true;+ if (sel == ":hover") return true;+ if (sel == ":focus") return true;+ // disabled - also valid properties+ // if (sel == ":left") return true;+ // if (sel == ":right") return true;+ if (sel == ":first") return true;++ // Selectors Level 3 - Recommendation+ if (sel == ":target") return true;+ if (sel == ":root") return true;+ if (sel == ":nth-child") return true;+ if (sel == ":nth-last-of-child") return true;+ if (sel == ":nth-of-type") return true;+ if (sel == ":nth-last-of-type") return true;+ if (sel == ":last-child") return true;+ if (sel == ":first-of-type") return true;+ if (sel == ":last-of-type") return true;+ if (sel == ":only-child") return true;+ if (sel == ":only-of-type") return true;+ if (sel == ":empty") return true;+ if (sel == ":not") return true;++ // CSS Basic User Interface Module Level 3 - Working Draft+ if (sel == ":default") return true;+ if (sel == ":valid") return true;+ if (sel == ":invalid") return true;+ if (sel == ":in-range") return true;+ if (sel == ":out-of-range") return true;+ if (sel == ":required") return true;+ if (sel == ":optional") return true;+ if (sel == ":read-only") return true;+ if (sel == ":read-write") return true;+ if (sel == ":dir") return true;+ if (sel == ":enabled") return true;+ if (sel == ":disabled") return true;+ if (sel == ":checked") return true;+ if (sel == ":indeterminate") return true;+ if (sel == ":nth-last-child") return true;++ // Selectors Level 4 - Working Draft+ if (sel == ":any-link") return true;+ if (sel == ":local-link") return true;+ if (sel == ":scope") return true;+ if (sel == ":active-drop-target") return true;+ if (sel == ":valid-drop-target") return true;+ if (sel == ":invalid-drop-target") return true;+ if (sel == ":current") return true;+ if (sel == ":past") return true;+ if (sel == ":future") return true;+ if (sel == ":placeholder-shown") return true;+ if (sel == ":user-error") return true;+ if (sel == ":blank") return true;+ if (sel == ":nth-match") return true;+ if (sel == ":nth-last-match") return true;+ if (sel == ":nth-column") return true;+ if (sel == ":nth-last-column") return true;+ if (sel == ":matches") return true;++ // Fullscreen API - Living Standard+ if (sel == ":fullscreen") return true;++ // not a pseudo selector+ return false;++ }++ // check if there is some char data+ // will ignore everything in comments+ static bool hasCharData (string& sass)+ {++ size_t col_pos = 0;++ while (true)+ {++ // try to find some meaningfull char+ col_pos = sass.find_first_not_of(" \t\n\v\f\r", col_pos);++ // there was no meaningfull char found+ if (col_pos == string::npos) return false;++ // found a multiline comment opener+ if (sass.substr(col_pos, 2) == "/*")+ {+ // find the multiline comment closer+ col_pos = sass.find("*/", col_pos);+ // maybe we did not find the closer here+ if (col_pos == string::npos) return false;+ // skip closer+ col_pos += 2;+ }+ else+ {+ return true;+ }++ }++ }+ // EO hasCharData++ // find src comment opener+ // correctly skips quoted strings+ static size_t findCommentOpener (string& sass)+ {++ size_t col_pos = 0;+ bool apoed = false;+ bool quoted = false;+ bool comment = false;+ size_t brackets = 0;++ while (col_pos != string::npos)+ {++ // process all interesting chars+ col_pos = sass.find_first_of("\"\'/\\*()", col_pos);++ // assertion for valid result+ if (col_pos != string::npos)+ {+ char character = sass.at(col_pos);++ if (character == '(')+ {+ if (!quoted && !apoed) brackets ++;+ }+ else if (character == ')')+ {+ if (!quoted && !apoed) brackets --;+ }+ else if (character == '\"')+ {+ // invert quote bool+ if (!apoed && !comment) quoted = !quoted;+ }+ else if (character == '\'')+ {+ // invert quote bool+ if (!quoted && !comment) apoed = !apoed;+ }+ else if (col_pos > 0 && character == '/')+ {+ if (sass.at(col_pos - 1) == '*')+ {+ comment = false;+ }+ // next needs to be a slash too+ else if (sass.at(col_pos - 1) == '/')+ {+ // only found if not in single or double quote, bracket or comment+ if (!quoted && !apoed && !comment && brackets == 0) return col_pos - 1;+ }+ }+ else if (character == '\\')+ {+ // skip next char if in quote+ if (quoted || apoed) col_pos ++;+ }+ // this might be a comment opener+ else if (col_pos > 0 && character == '*')+ {+ // opening a multiline comment+ if (sass.at(col_pos - 1) == '/')+ {+ // we are now in a comment+ if (!quoted && !apoed) comment = true;+ }+ }++ // skip char+ col_pos ++;++ }++ }+ // EO while++ return col_pos;++ }+ // EO findCommentOpener++ // remove multiline comments from sass string+ // correctly skips quoted strings+ static string removeMultilineComment (string &sass)+ {++ string clean = "";+ size_t col_pos = 0;+ size_t open_pos = 0;+ size_t close_pos = 0;+ bool apoed = false;+ bool quoted = false;+ bool comment = false;++ // process sass til string end+ while (col_pos != string::npos)+ {++ // process all interesting chars+ col_pos = sass.find_first_of("\"\'/\\*", col_pos);++ // assertion for valid result+ if (col_pos != string::npos)+ {+ char character = sass.at(col_pos);++ // found quoted string delimiter+ if (character == '\"')+ {+ if (!apoed && !comment) quoted = !quoted;+ }+ else if (character == '\'')+ {+ if (!quoted && !comment) apoed = !apoed;+ }+ // found possible comment closer+ else if (character == '/')+ {+ // look back to see if it is actually a closer+ if (comment && col_pos > 0 && sass.at(col_pos - 1) == '*')+ {+ close_pos = col_pos + 1; comment = false;+ }+ }+ else if (character == '\\')+ {+ // skip escaped char+ if (quoted || apoed) col_pos ++;+ }+ // this might be a comment opener+ else if (character == '*')+ {+ // look back to see if it is actually an opener+ if (!quoted && !apoed && col_pos > 0 && sass.at(col_pos - 1) == '/')+ {+ comment = true; open_pos = col_pos - 1;+ clean += sass.substr(close_pos, open_pos - close_pos);+ }+ }++ // skip char+ col_pos ++;++ }++ }+ // EO while++ // add final parts (add half open comment text)+ if (comment) clean += sass.substr(open_pos);+ else clean += sass.substr(close_pos);++ // return string+ return clean;++ }+ // EO removeMultilineComment++ // right trim a given string+ string rtrim(const string &sass)+ {+ string trimmed = sass;+ size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");+ if (pos_ws != string::npos)+ { trimmed.erase(pos_ws + 1); }+ else { trimmed.clear(); }+ return trimmed;+ }+ // EO rtrim++ // flush whitespace and print additional text, but+ // only print additional chars and buffer whitespace+ string flush (string& sass, converter& converter)+ {++ // return flushed+ string scss = "";++ // print whitespace buffer+ scss += PRETTIFY(converter) > 0 ?+ converter.whitespace : "";+ // reset whitespace buffer+ converter.whitespace = "";++ // remove possible newlines from string+ size_t pos_right = sass.find_last_not_of("\n\r");+ if (pos_right == string::npos) return scss;++ // get the linefeeds from the string+ string lfs = sass.substr(pos_right + 1);+ sass = sass.substr(0, pos_right + 1);++ // find some source comment opener+ size_t comment_pos = findCommentOpener(sass);+ // check if there was a source comment+ if (comment_pos != string::npos)+ {+ // convert comment (but only outside other coments)+ if (CONVERT_COMMENT(converter) && !IS_COMMENT(converter))+ {+ // convert to multiline comment+ sass.at(comment_pos + 1) = '*';+ // add comment node to the whitespace+ sass += " */";+ }+ // not at line start+ if (comment_pos > 0)+ {+ // also include whitespace before the actual comment opener+ size_t ws_pos = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE, comment_pos - 1);+ comment_pos = ws_pos == string::npos ? 0 : ws_pos + 1;+ }+ if (!STRIP_COMMENT(converter))+ {+ // add comment node to the whitespace+ converter.whitespace += sass.substr(comment_pos);+ }+ else+ {+ // sass = removeMultilineComments(sass);+ }+ // update the actual sass code+ sass = sass.substr(0, comment_pos);+ }++ // add newline as getline discharged it+ converter.whitespace += lfs + "\n";++ // maybe remove any leading whitespace+ if (PRETTIFY(converter) == 0)+ {+ // remove leading whitespace and update string+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);+ if (pos_left != string::npos) sass = sass.substr(pos_left);+ }++ // add flushed data+ scss += sass;++ // return string+ return scss;++ }+ // EO flush++ // process a line of the sass text+ string process (string& sass, converter& converter)+ {++ // resulting string+ string scss = "";++ // strip multi line comments+ if (STRIP_COMMENT(converter))+ {+ sass = removeMultilineComment(sass);+ }++ // right trim input+ sass = rtrim(sass);++ // get postion of first meaningfull character in string+ size_t pos_left = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE);++ // special case for final run+ if (converter.end_of_file) pos_left = 0;++ // maybe has only whitespace+ if (pos_left == string::npos)+ {+ // just add complete whitespace+ converter.whitespace += sass + "\n";+ }+ // have meaningfull first char+ else+ {++ // extract and store indentation string+ string indent = sass.substr(0, pos_left);++ // check if current line starts a comment+ string open = sass.substr(pos_left, 2);++ // line has less or same indentation+ // finalize previous open parser context+ if (indent.length() <= INDENT(converter).length())+ {++ // close multilinie comment+ if (IS_CSS_COMMENT(converter))+ {+ // check if comments will be stripped anyway+ if (!STRIP_COMMENT(converter)) scss += " */";+ }+ // close src comment comment+ else if (IS_SRC_COMMENT(converter))+ {+ // add a newline to avoid closer on same line+ // this would put the bracket in the comment node+ // no longer needed since we parse them correctly+ // if (KEEP_COMMENT(converter)) scss += "\n";+ }+ // close css properties+ else if (converter.property)+ {+ // add closer unless in concat mode+ if (!converter.comma)+ {+ // if there was no colon we have a selector+ // looks like there were no inner properties+ if (converter.selector) scss += " {}";+ // add final semicolon+ else if (!converter.semicolon) scss += ";";+ }+ }++ // reset comment state+ converter.comment = "";++ }++ // make sure we close every "higher" block+ while (indent.length() < INDENT(converter).length())+ {+ // pop stacked context+ converter.indents.pop();+ // print close bracket+ if (IS_PARSING(converter))+ { scss += closer(converter); }+ else { scss += " */"; }+ // reset comment state+ converter.comment = "";+ }++ // reset converter state+ converter.selector = false;++ // check if we have sass property syntax+ if (sass.substr(pos_left, 1) == ":" && sass.substr(pos_left, 2) != "::")+ {++ // default to a selector+ // change back if property found+ converter.selector = true;+ // get postion of first whitespace char+ size_t pos_wspace = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left);+ // assertion check for valid result+ if (pos_wspace != string::npos)+ {+ // get the possible pseudo selector+ string pseudo = sass.substr(pos_left, pos_wspace - pos_left);+ // get position of the first real property value char+ // pseudo selectors get this far, but have no actual value+ size_t pos_value = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_wspace);+ // assertion check for valid result+ if (pos_value != string::npos)+ {+ // only process if not (fallowed by a semicolon or is a pseudo selector)+ if (!(sass.at(pos_value) == ':' || isPseudoSelector(pseudo)))+ {+ // create new string by interchanging the colon sign for property and value+ sass = indent + sass.substr(pos_left + 1, pos_wspace - pos_left - 1) + ":" + sass.substr(pos_wspace);+ // try to find a colon in the current line, but only ...+ size_t pos_colon = sass.find_first_not_of(":", pos_left);+ // assertion for valid result+ if (pos_colon != string::npos)+ {+ // ... after the first word (skip begining colons)+ pos_colon = sass.find_first_of(":", pos_colon);+ // it is a selector if there was no colon found+ converter.selector = pos_colon == string::npos;+ }+ }+ }+ }++ }++ // terminate some statements immediately+ else if (+ sass.substr(pos_left, 5) == "@warn" ||+ sass.substr(pos_left, 6) == "@debug" ||+ sass.substr(pos_left, 6) == "@error" ||+ sass.substr(pos_left, 8) == "@charset"+ ) { sass = indent + sass.substr(pos_left); }+ // replace some specific sass shorthand directives (if not fallowed by a white space character)+ else if (sass.substr(pos_left, 1) == "=" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)+ { sass = indent + "@mixin " + sass.substr(pos_left + 1); }+ else if (sass.substr(pos_left, 1) == "+" && sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left) != pos_left + 1)+ { sass = indent + "@include " + sass.substr(pos_left + 1); }++ // add quotes for import if needed+ else if (sass.substr(pos_left, 7) == "@import")+ {+ // get positions for the actual import url+ size_t pos_import = sass.find_first_of(SASS2SCSS_FIND_WHITESPACE, pos_left + 7);+ size_t pos_quote = sass.find_first_not_of(SASS2SCSS_FIND_WHITESPACE, pos_import);+ // leave proper urls untouched+ if (sass.substr(pos_quote, 4) != "url(")+ {+ // check if the url appears to be already quoted+ if (sass.substr(pos_quote, 1) != "\"" && sass.substr(pos_quote, 1) != "\'")+ {+ // get position of the last char on the line+ size_t pos_end = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);+ // assertion check for valid result+ if (pos_end != string::npos)+ {+ // add quotes around the full line after the import statement+ sass = sass.substr(0, pos_quote) + "\"" + sass.substr(pos_quote, pos_end - pos_quote + 1) + "\"";+ }+ }+ }++ }+ else if (+ sass.substr(pos_left, 7) != "@return" &&+ sass.substr(pos_left, 7) != "@extend" &&+ sass.substr(pos_left, 8) != "@content"+ ) {++ // try to find a colon in the current line, but only ...+ size_t pos_colon = sass.find_first_not_of(":", pos_left);+ // assertion for valid result+ if (pos_colon != string::npos)+ {+ // ... after the first word (skip begining colons)+ pos_colon = sass.find_first_of(":", pos_colon);+ // it is a selector if there was no colon found+ converter.selector = pos_colon == string::npos;+ }++ }++ // current line has more indentation+ if (indent.length() >= INDENT(converter).length())+ {+ // not in comment mode+ if (IS_PARSING(converter))+ {+ // has meaningfull chars+ if (hasCharData(sass))+ {+ // is probably a property+ // also true for selectors+ converter.property = true;+ }+ }+ }+ // current line has more indentation+ if (indent.length() > INDENT(converter).length())+ {+ // not in comment mode+ if (IS_PARSING(converter))+ {+ // had meaningfull chars+ if (converter.property)+ {+ // print block opener+ scss += opener(converter);+ // push new stack context+ converter.indents.push("");+ // store block indentation+ INDENT(converter) = indent;+ }+ }+ // is and will be a src comment+ else if (!IS_CSS_COMMENT(converter))+ {+ // scss does not allow multiline src comments+ // therefore add forward slashes to all lines+ sass.at(INDENT(converter).length()+0) = '/';+ // there is an edge case here if indentation+ // is minimal (will overwrite the fist char)+ sass.at(INDENT(converter).length()+1) = '/';+ // could code around that, but I dont' think+ // this will ever be the cause for any trouble+ }+ }++ // line is opening a new comment+ if (open == "/*" || open == "//")+ {+ // reset the property state+ converter.property = false;+ // close previous comment+ if (IS_CSS_COMMENT(converter) && open != "")+ {+ if (!STRIP_COMMENT(converter) && !CONVERT_COMMENT(converter)) scss += " */";+ }+ // force single line comments+ // into a correct css comment+ if (CONVERT_COMMENT(converter))+ {+ if (IS_PARSING(converter))+ { sass.at(pos_left + 1) = '*'; }+ }+ // set comment flag+ converter.comment = open;++ }++ // flush data only under certain conditions+ if (!(+ // strip css and src comments if option is set+ (IS_COMMENT(converter) && STRIP_COMMENT(converter)) ||+ // strip src comment even if strip option is not set+ // but only if the keep src comment option is not set+ (IS_SRC_COMMENT(converter) && ! KEEP_COMMENT(converter))+ ))+ {+ // flush data and buffer whitespace+ scss += flush(sass, converter);+ }++ // get postion of last meaningfull char+ size_t pos_right = sass.find_last_not_of(SASS2SCSS_FIND_WHITESPACE);++ // check for invalid result+ if (pos_right != string::npos)+ {++ // get the last meaningfull char+ string close = sass.substr(pos_right, 1);++ // check if next line should be concatenated (list mode)+ converter.comma = IS_PARSING(converter) && close == ",";+ converter.semicolon = IS_PARSING(converter) && close == ";";++ // check if we have more than+ // one meaningfull char+ if (pos_right > 0)+ {++ // get the last two chars from string+ string close = sass.substr(pos_right - 1, 2);+ // update parser status for expicitly closed comment+ if (close == "*/") converter.comment = "";++ }++ }+ // EO have meaningfull chars from end++ }+ // EO have meaningfull chars from start++ // return scss+ return scss;++ }+ // EO process++ // read line with either CR, LF or CR LF format+ // http://stackoverflow.com/a/6089413/1550314+ static std::istream& safeGetline(std::istream& is, std::string& t)+ {+ t.clear();++ // The characters in the stream are read one-by-one using a std::streambuf.+ // That is faster than reading them one-by-one using the std::istream.+ // Code that uses streambuf this way must be guarded by a sentry object.+ // The sentry object performs various tasks,+ // such as thread synchronization and updating the stream state.++ std::istream::sentry se(is, true);+ std::streambuf* sb = is.rdbuf();++ for(;;) {+ int c = sb->sbumpc();+ switch (c) {+ case '\n':+ return is;+ case '\r':+ if(sb->sgetc() == '\n')+ sb->sbumpc();+ return is;+ case EOF:+ // Also handle the case when the last line has no line ending+ if(t.empty())+ is.setstate(std::ios::eofbit);+ return is;+ default:+ t += (char)c;+ }+ }+ }++ // the main converter function for c+++ char* sass2scss (const string sass, const int options)+ {++ // local variables+ string line;+ string scss = "";+ stringstream stream(sass);++ // create converter variable+ converter converter;+ // initialise all options+ converter.comma = false;+ converter.property = false;+ converter.selector = false;+ converter.semicolon = false;+ converter.end_of_file = false;+ converter.comment = "";+ converter.whitespace = "";+ converter.indents.push("");+ converter.options = options;++ // read line by line and process them+ while(safeGetline(stream, line) && !stream.eof())+ { scss += process(line, converter); }++ // create mutable string+ string closer = "";+ // set the end of file flag+ converter.end_of_file = true;+ // process to close all open blocks+ scss += process(closer, converter);++ // allocate new memory on the heap+ // caller has to free it after use+ char * cstr = (char*) malloc (scss.length() + 1);+ // create a copy of the string+ strcpy (cstr, scss.c_str());+ // return pointer+ return &cstr[0];++ }+ // EO sass2scss++}+// EO namespace++// implement for c+extern "C"+{++ char* ADDCALL sass2scss (const char* sass, const int options)+ {+ return Sass::sass2scss(sass, options);+ }++ // Get compiled sass2scss version+ const char* ADDCALL sass2scss_version(void) {+ return SASS2SCSS_VERSION;+ }++}
+ libsass/sass2scss.h view
@@ -0,0 +1,115 @@+#ifndef SASS2SCSS_H+#define SASS2SCSS_H++#ifdef _WIN32++ /* You should define ADD_EXPORTS *only* when building the DLL. */+ #ifdef ADD_EXPORTS+ #define ADDAPI __declspec(dllexport)+ #define ADDCALL __cdecl+ #else+ #define ADDAPI+ #define ADDCALL+ #endif++#else /* _WIN32 not defined. */++ /* Define with no value on non-Windows OSes. */+ #define ADDAPI+ #define ADDCALL++#endif++#ifdef __cplusplus++#include <stack>+#include <string>+#include <cstring>+#include <sstream>+#include <iostream>++#ifndef SASS2SCSS_VERSION+#define SASS2SCSS_VERSION "1.0.3"+#endif++// using std::string+using namespace std;++// add namespace for c+++namespace Sass+{++ // pretty print options+ const int SASS2SCSS_PRETTIFY_0 = 0;+ const int SASS2SCSS_PRETTIFY_1 = 1;+ const int SASS2SCSS_PRETTIFY_2 = 2;+ const int SASS2SCSS_PRETTIFY_3 = 3;++ // remove one-line comment+ const int SASS2SCSS_KEEP_COMMENT = 32;+ // remove multi-line comments+ const int SASS2SCSS_STRIP_COMMENT = 64;+ // convert one-line to multi-line+ const int SASS2SCSS_CONVERT_COMMENT = 128;++ // String for finding something interesting+ const string SASS2SCSS_FIND_WHITESPACE = " \t\n\v\f\r";++ // converter struct+ // holding all states+ struct converter+ {+ // bit options+ int options;+ // is selector+ bool selector;+ // concat lists+ bool comma;+ // has property+ bool property;+ // has semicolon+ bool semicolon;+ // comment context+ string comment;+ // flag end of file+ bool end_of_file;+ // whitespace buffer+ string whitespace;+ // context/block stack+ stack<string> indents;+ };++ // function only available in c++ code+ char* sass2scss (const string sass, const int options);++}+// EO namespace++// declare for c+extern "C" {+#endif++ // prettyfy print options+ #define SASS2SCSS_PRETTIFY_0 0+ #define SASS2SCSS_PRETTIFY_1 1+ #define SASS2SCSS_PRETTIFY_2 2+ #define SASS2SCSS_PRETTIFY_3 3++ // keep one-line comments+ #define SASS2SCSS_KEEP_COMMENT 32+ // remove multi-line comments+ #define SASS2SCSS_STRIP_COMMENT 64+ // convert one-line to multi-line+ #define SASS2SCSS_CONVERT_COMMENT 128++ // available to c and c++ code+ ADDAPI char* ADDCALL sass2scss (const char* sass, const int options);++ // Get compiled sass2scss version+ ADDAPI const char* ADDCALL sass2scss_version(void);++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/sass_context.cpp view
@@ -0,0 +1,894 @@+#ifdef _WIN32+#include <io.h>+#define LFEED "\n"+#else+#include <unistd.h>+#define LFEED "\n"+#endif++#include <cstring>+#include <stdexcept>+#include "file.hpp"+#include "json.hpp"+#include "util.hpp"+#include "context.hpp"+#include "sass_values.h"+#include "sass_context.h"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++extern "C" {+ using namespace std;+ using namespace 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_Options {++ // Precision for fractional numbers+ int precision;++ // Output style for the generated css code+ // A value from above SASS_STYLE_* constants+ enum Sass_Output_Style output_style;++ // Emit comments in the generated CSS indicating+ // the corresponding source line.+ bool source_comments;++ // embed sourceMappingUrl as data uri+ bool source_map_embed;++ // embed include contents in maps+ bool source_map_contents;++ // Disable sourceMappingUrl in css output+ bool omit_source_map_url;++ // Treat source_string as sass (as opposed to scss)+ bool is_indented_syntax_src;++ // The input path is used for source map+ // generation. It can be used to define+ // something with string compilation or to+ // overload the input file path. It is+ // set to "stdin" for data contexts and+ // to the input file on file contexts.+ char* input_path;++ // The output path is used for source map+ // generation. Libsass will not write to+ // this file, it is just used to create+ // information in source-maps etc.+ char* output_path;++ // String to be used for indentation+ const char* indent;+ // String to be used to for line feeds+ const char* linefeed;++ // Colon-separated list of paths+ // Semicolon-separated on Windows+ // Maybe use array interface instead?+ char* include_path;+ char* plugin_path;++ // Include paths (linked string list)+ struct string_list* include_paths;+ // Plugin paths (linked string list)+ struct string_list* plugin_paths;++ // Path to source map file+ // Enables source map generation+ // Used to create sourceMappingUrl+ char* source_map_file;++ // Directly inserted in source maps+ char* source_map_root;++ // Custom functions that can be called from sccs code+ Sass_Function_List c_functions;++ // List of custom importers+ Sass_Importer_List c_importers;++ // List of custom headers+ Sass_Importer_List c_headers;++ };++ // base for all contexts+ struct Sass_Context : Sass_Options+ {++ // store context type info+ enum Sass_Input_Style type;++ // generated output data+ char* output_string;++ // generated source map json+ char* source_map_string;++ // error status+ int error_status;+ char* error_json;+ char* error_text;+ char* error_message;+ // error position+ char* error_file;+ size_t error_line;+ size_t error_column;+ const char* error_src;++ // report imported files+ char** included_files;++ };++ // struct for file compilation+ struct Sass_File_Context : Sass_Context {++ // no additional fields required+ // input_path is already on options++ };++ // struct for data compilation+ struct Sass_Data_Context : Sass_Context {++ // provided source string+ char* source_string;++ };++ // link c and cpp context+ struct Sass_Compiler {+ // progress status+ Sass_Compiler_State state;+ // original c context+ Sass_Context* c_ctx;+ // Sass::Context+ Context* cpp_ctx;+ // Sass::Block+ Block* root;+ };++ static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { *to = *from; }++ #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \+ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; }+ #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \+ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \+ { free(options->option); options->option = option ? sass_strdup(option) : 0; }++ #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \+ type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; }+ #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \+ type sass_context_take_##option (struct Sass_Context* ctx) \+ { type foo = ctx->option; ctx->option = 0; return foo; }++ // helper for safe access to c_ctx+ static const char* safe_str (const char* str) {+ return str == NULL ? "" : str;+ }++ static void copy_strings(const std::vector<std::string>& strings, char*** array) {+ int num = static_cast<int>(strings.size());+ char** arr = (char**) malloc(sizeof(char*) * (num + 1));+ if (arr == 0) throw(bad_alloc());++ for(int i = 0; i < num; i++) {+ arr[i] = (char*) malloc(sizeof(char) * (strings[i].size() + 1));+ if (arr[i] == 0) throw(bad_alloc());+ std::copy(strings[i].begin(), strings[i].end(), arr[i]);+ arr[i][strings[i].size()] = '\0';+ }++ arr[num] = 0;+ *array = arr;+ }++ static void free_string_array(char ** arr) {+ if(!arr)+ return;++ char **it = arr;+ while (it && (*it)) {+ free(*it);+ ++it;+ }++ free(arr);+ }++ static int handle_errors(Sass_Context* c_ctx) {+ try {+ throw;+ }+ catch (Sass_Error& e) {+ stringstream msg_stream;+ string cwd(Sass::File::get_cwd());+ JsonNode* json_err = json_mkobject();+ json_append_member(json_err, "status", json_mknumber(1));+ json_append_member(json_err, "file", json_mkstring(e.pstate.path.c_str()));+ json_append_member(json_err, "line", json_mknumber(e.pstate.line+1));+ json_append_member(json_err, "column", json_mknumber(e.pstate.column+1));+ json_append_member(json_err, "message", json_mkstring(e.message.c_str()));+ string rel_path(Sass::File::resolve_relative_path(e.pstate.path, cwd, cwd));++ string msg_prefix("Error: ");+ bool got_newline = false;+ msg_stream << msg_prefix;+ for (char chr : e.message) {+ if (chr == '\n') {+ got_newline = true;+ } else if (got_newline) {+ msg_stream << string(msg_prefix.size(), ' ');+ got_newline = false;+ }+ msg_stream << chr;+ }+ if (!got_newline) msg_stream << "\n";+ msg_stream << string(msg_prefix.size(), ' ');+ 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 != string::npos && e.pstate.column != string::npos) {+ size_t line = e.pstate.line;+ const char* line_beg = e.pstate.src;+ while (line_beg && *line_beg && line) {+ if (*line_beg == '\n') -- line;+ ++ line_beg;+ }+ const char* line_end = line_beg;+ while (line_end && *line_end && *line_end != '\n') {+ if (*line_end == '\n') break;+ if (*line_end == '\r') break;+ line_end ++;+ }+ size_t max_left = 42; size_t max_right = 78;+ size_t move_in = e.pstate.column > max_left ? e.pstate.column - max_left : 0;+ size_t shorten = (line_end - line_beg) - move_in > max_right ?+ (line_end - line_beg) - move_in - max_right : 0;+ msg_stream << ">> " << string(line_beg + move_in, line_end - shorten) << "\n";+ msg_stream << " " << string(e.pstate.column - move_in, '-') << "^\n";+ }++ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = strdup(e.message.c_str());+ c_ctx->error_status = 1;+ c_ctx->error_file = sass_strdup(e.pstate.path.c_str());+ c_ctx->error_line = e.pstate.line+1;+ c_ctx->error_column = e.pstate.column+1;+ c_ctx->error_src = e.pstate.src;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch(bad_alloc& ba) {+ stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Unable to allocate memory: " << ba.what() << endl;+ json_append_member(json_err, "status", json_mknumber(2));+ json_append_member(json_err, "message", json_mkstring(ba.what()));+ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = strdup(ba.what());+ c_ctx->error_status = 2;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (std::exception& e) {+ stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Error: " << e.what() << endl;+ json_append_member(json_err, "status", json_mknumber(3));+ json_append_member(json_err, "message", json_mkstring(e.what()));+ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = strdup(e.what());+ c_ctx->error_status = 3;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (string& e) {+ stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Error: " << e << endl;+ json_append_member(json_err, "status", json_mknumber(4));+ json_append_member(json_err, "message", json_mkstring(e.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 = strdup(e.c_str());+ c_ctx->error_status = 4;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (...) {+ stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Unknown error occurred" << endl;+ json_append_member(json_err, "status", json_mknumber(5));+ json_append_member(json_err, "message", json_mkstring("unknown"));+ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = strdup("unknown");+ c_ctx->error_status = 5;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ return c_ctx->error_status;+ }++ // generic compilation function (not exported, use file/data compile instead)+ static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context::Data cpp_opt) throw()+ {+ try {++ // get input/output path from options+ string input_path = safe_str(c_ctx->input_path);+ string output_path = safe_str(c_ctx->output_path);+ // maybe we can extract an output path from input path+ if (output_path == "" && input_path != "") {+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+ }++ // convert include path linked list to static array+ struct string_list* inc = c_ctx->include_paths;+ // very poor loop to get the length of the linked list+ size_t inc_size = 0; while (inc) { inc_size ++; inc = inc->next; }+ // create char* array to hold all paths plus null terminator+ const char** include_paths = (const char**) calloc(inc_size + 1, sizeof(char*));+ if (include_paths == 0) throw(bad_alloc());+ // reset iterator+ inc = c_ctx->include_paths;+ // copy over the paths+ for (size_t i = 0; inc; i++) {+ include_paths[i] = inc->string;+ inc = inc->next;+ }++ // convert plugin path linked list to static array+ struct string_list* imp = c_ctx->plugin_paths;+ // very poor loop to get the length of the linked list+ size_t imp_size = 0; while (imp) { imp_size ++; imp = imp->next; }+ // create char* array to hold all paths plus null terminator+ const char** plugin_paths = (const char**) calloc(imp_size + 1, sizeof(char*));+ if (plugin_paths == 0) throw(bad_alloc());+ // reset iterator+ imp = c_ctx->plugin_paths;+ // copy over the paths+ for (size_t i = 0; imp; i++) {+ plugin_paths[i] = imp->string;+ imp = imp->next;+ }++ // transfer the options to c+++ cpp_opt.c_compiler(0)+ .c_options(c_ctx)+ .input_path(input_path)+ .output_path(output_path)+ .output_style((Output_Style) c_ctx->output_style)+ .is_indented_syntax_src(c_ctx->is_indented_syntax_src)+ .source_comments(c_ctx->source_comments)+ .source_map_file(safe_str(c_ctx->source_map_file))+ .source_map_root(safe_str(c_ctx->source_map_root))+ .source_map_embed(c_ctx->source_map_embed)+ .source_map_contents(c_ctx->source_map_contents)+ .omit_source_map_url(c_ctx->omit_source_map_url)+ .include_paths_c_str(c_ctx->include_path)+ .plugin_paths_c_str(c_ctx->plugin_path)+ // .include_paths_array(include_paths)+ // .plugin_paths_array(plugin_paths)+ .include_paths(vector<string>())+ .plugin_paths(vector<string>())+ .precision(c_ctx->precision)+ .linefeed(c_ctx->linefeed)+ .indent(c_ctx->indent);++ // create new c++ Context+ Context* cpp_ctx = new Context(cpp_opt);+ // free intermediate data+ free(include_paths);+ free(plugin_paths);++ // register our custom functions+ if (c_ctx->c_functions) {+ auto this_func_data = c_ctx->c_functions;+ while (this_func_data && *this_func_data) {+ cpp_ctx->add_c_function(*this_func_data);+ ++this_func_data;+ }+ }++ // register our custom headers+ if (c_ctx->c_headers) {+ auto this_head_data = c_ctx->c_headers;+ while (this_head_data && *this_head_data) {+ cpp_ctx->add_c_header(*this_head_data);+ ++this_head_data;+ }+ }++ // register our custom importers+ if (c_ctx->c_importers) {+ auto this_imp_data = c_ctx->c_importers;+ while (this_imp_data && *this_imp_data) {+ cpp_ctx->add_c_importer(*this_imp_data);+ ++this_imp_data;+ }+ }++ // reset error status+ c_ctx->error_json = 0;+ c_ctx->error_text = 0;+ c_ctx->error_message = 0;+ c_ctx->error_status = 0;+ // reset error position+ c_ctx->error_src = 0;+ c_ctx->error_file = 0;+ c_ctx->error_line = string::npos;+ c_ctx->error_column = string::npos;++ // allocate a new compiler instance+ Sass_Compiler* compiler = (struct Sass_Compiler*) calloc(1, sizeof(struct Sass_Compiler));+ compiler->state = SASS_COMPILER_CREATED;++ // store in sass compiler+ compiler->c_ctx = c_ctx;+ compiler->cpp_ctx = cpp_ctx;+ cpp_ctx->c_compiler = compiler;++ // use to parse block+ return compiler;++ }+ // pass errors to generic error handler+ catch (...) { handle_errors(c_ctx); }++ // error+ return 0;++ }++ static Block* sass_parse_block (Sass_Compiler* compiler) throw()+ {++ // assert valid pointer+ if (compiler == 0) return 0;+ // The cpp context must be set by now+ Context* cpp_ctx = compiler->cpp_ctx;+ Sass_Context* c_ctx = compiler->c_ctx;+ // We will take care to wire up the rest+ compiler->cpp_ctx->c_compiler = compiler;+ compiler->state = SASS_COMPILER_PARSED;++ try {++ // get input/output path from options+ string input_path = safe_str(c_ctx->input_path);+ string output_path = safe_str(c_ctx->output_path);++ // parsed root block+ Block* root = 0;++ // maybe skip some entries of included files+ // we do not include stdin for data contexts+ size_t skip = 0;++ // dispatch to the correct render function+ if (c_ctx->type == SASS_CONTEXT_FILE) {+ root = cpp_ctx->parse_file();+ } else if (c_ctx->type == SASS_CONTEXT_DATA) {+ root = cpp_ctx->parse_string();+ skip = 1; // skip first entry of includes+ }++ // skip all prefixed files?+ skip += cpp_ctx->head_imports;++ // copy the included files on to the context (dont forget to free)+ if (root) copy_strings(cpp_ctx->get_included_files(skip), &c_ctx->included_files);++ // return parsed block+ return root;++ }+ // pass errors to generic error handler+ catch (...) { handle_errors(c_ctx); }++ // error+ return 0;++ }++ // generic compilation function (not exported, use file/data compile instead)+ static int sass_compile_context (Sass_Context* c_ctx, Context::Data cpp_opt)+ {++ // prepare sass compiler with context and options+ Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_opt);++ try {+ // call each compiler step+ sass_compiler_parse(compiler);+ sass_compiler_execute(compiler);+ }+ // pass errors to generic error handler+ catch (...) { handle_errors(c_ctx); }++ sass_delete_compiler(compiler);++ return c_ctx->error_status;+ }++ inline void init_options (struct Sass_Options* options)+ {+ options->precision = 5;+ options->indent = " ";+ options->linefeed = LFEED;+ }++ Sass_Options* ADDCALL sass_make_options (void)+ {+ struct Sass_Options* options = (struct Sass_Options*) calloc(1, sizeof(struct Sass_Options));+ if (options == 0) { cerr << "Error allocating memory for options" << endl; return 0; }+ init_options(options);+ return options;+ }++ Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path)+ {+ struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(1, sizeof(struct Sass_File_Context));+ if (ctx == 0) { cerr << "Error allocating memory for file context" << endl; return 0; }+ ctx->type = SASS_CONTEXT_FILE;+ init_options(ctx);+ try {+ if (input_path == 0) { throw(runtime_error("File context created without an input path")); }+ if (*input_path == 0) { throw(runtime_error("File context created with empty input path")); }+ sass_option_set_input_path(ctx, input_path);+ } catch (...) {+ handle_errors(ctx);+ }+ return ctx;+ }++ Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string)+ {+ struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(1, sizeof(struct Sass_Data_Context));+ if (ctx == 0) { cerr << "Error allocating memory for data context" << endl; return 0; }+ ctx->type = SASS_CONTEXT_DATA;+ init_options(ctx);+ try {+ if (source_string == 0) { throw(runtime_error("Data context created without a source string")); }+ if (*source_string == 0) { throw(runtime_error("Data context created with empty source string")); }+ ctx->source_string = source_string;+ } catch (...) {+ handle_errors(ctx);+ }+ return ctx;+ }++ struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* c_ctx)+ {+ if (c_ctx == 0) return 0;+ Context::Data cpp_opt = Context::Data();+ cpp_opt.entry_point(c_ctx->input_path);+ return sass_prepare_context(c_ctx, cpp_opt);+ }++ struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* c_ctx)+ {+ if (c_ctx == 0) return 0;+ Context::Data cpp_opt = Context::Data();+ cpp_opt.source_c_str(c_ctx->source_string);+ return sass_prepare_context(c_ctx, cpp_opt);+ }++ int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx)+ {+ if (data_ctx == 0) return 1;+ Sass_Context* c_ctx = data_ctx;+ if (c_ctx->error_status)+ return c_ctx->error_status;+ Context::Data cpp_opt = Context::Data();+ try {+ if (data_ctx->source_string == 0) { throw(runtime_error("Data context has no source string")); }+ if (*data_ctx->source_string == 0) { throw(runtime_error("Data context has empty source string")); }+ cpp_opt.source_c_str(data_ctx->source_string);+ }+ catch (...) { return handle_errors(c_ctx) | 1; }+ return sass_compile_context(c_ctx, cpp_opt);+ }++ int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx)+ {+ if (file_ctx == 0) return 1;+ Sass_Context* c_ctx = file_ctx;+ if (c_ctx->error_status)+ return c_ctx->error_status;+ Context::Data cpp_opt = Context::Data();+ try {+ if (file_ctx->input_path == 0) { throw(runtime_error("File context has no input path")); }+ if (*file_ctx->input_path == 0) { throw(runtime_error("File context has empty input path")); }+ cpp_opt.entry_point(file_ctx->input_path);+ }+ catch (...) { return handle_errors(c_ctx) | 1; }+ return sass_compile_context(c_ctx, cpp_opt);+ }++ int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler)+ {+ if (compiler == 0) return 1;+ if (compiler->state == SASS_COMPILER_PARSED) return 0;+ if (compiler->state != SASS_COMPILER_CREATED) return -1;+ if (compiler->c_ctx == NULL) return 1;+ if (compiler->cpp_ctx == NULL) return 1;+ if (compiler->c_ctx->error_status)+ return compiler->c_ctx->error_status;+ // parse the context we have set up (file or data)+ compiler->root = sass_parse_block(compiler);+ // success+ return 0;+ }++ int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler)+ {+ if (compiler == 0) return 1;+ if (compiler->state == SASS_COMPILER_EXECUTED) return 0;+ if (compiler->state != SASS_COMPILER_PARSED) return -1;+ if (compiler->c_ctx == NULL) return 1;+ if (compiler->cpp_ctx == NULL) return 1;+ if (compiler->root == NULL) return 1;+ if (compiler->c_ctx->error_status)+ return compiler->c_ctx->error_status;+ compiler->state = SASS_COMPILER_EXECUTED;+ Context* cpp_ctx = (Context*) compiler->cpp_ctx;+ Block* root = (Block*) compiler->root;+ // compile the parsed root block+ try { compiler->c_ctx->output_string = cpp_ctx->compile_block(root); }+ // pass catched errors to generic error handler+ catch (...) { return handle_errors(compiler->c_ctx) | 1; }+ // generate source map json and store on context+ compiler->c_ctx->source_map_string = cpp_ctx->generate_source_map();+ // success+ return 0;+ }++ // helper function, not exported, only accessible locally+ static void sass_clear_options (struct Sass_Options* options)+ {+ if (options == 0) return;+ // Deallocate custom functions+ if (options->c_functions) {+ Sass_Function_List this_func_data = options->c_functions;+ while (this_func_data && *this_func_data) {+ free(*this_func_data);+ ++this_func_data;+ }+ }+ // Deallocate custom headers+ if (options->c_headers) {+ Sass_Importer_List this_head_data = options->c_headers;+ while (this_head_data && *this_head_data) {+ free(*this_head_data);+ ++this_head_data;+ }+ }+ // Deallocate custom importers+ if (options->c_importers) {+ Sass_Importer_List this_imp_data = options->c_importers;+ while (this_imp_data && *this_imp_data) {+ free(*this_imp_data);+ ++this_imp_data;+ }+ }+ // Deallocate inc paths+ if (options->plugin_paths) {+ struct string_list* cur;+ struct string_list* next;+ cur = options->plugin_paths;+ while (cur) {+ next = cur->next;+ free(cur->string);+ free(cur);+ cur = next;+ }+ }+ // Deallocate inc paths+ if (options->include_paths) {+ struct string_list* cur;+ struct string_list* next;+ cur = options->include_paths;+ while (cur) {+ next = cur->next;+ free(cur->string);+ free(cur);+ cur = next;+ }+ }+ // Free custom functions+ free(options->c_functions);+ // Free custom importers+ free(options->c_importers);+ free(options->c_headers);+ // Reset our pointers+ options->c_functions = 0;+ options->c_importers = 0;+ options->c_headers = 0;+ options->plugin_paths = 0;+ options->include_paths = 0;+ }++ // helper function, not exported, only accessible locally+ // sass_free_context is also defined in old sass_interface+ static void sass_clear_context (struct Sass_Context* ctx)+ {+ if (ctx == 0) return;+ // release the allocated memory (mostly via sass_strdup)+ if (ctx->output_string) free(ctx->output_string);+ if (ctx->source_map_string) free(ctx->source_map_string);+ if (ctx->error_message) free(ctx->error_message);+ if (ctx->error_text) free(ctx->error_text);+ if (ctx->error_json) free(ctx->error_json);+ if (ctx->error_file) free(ctx->error_file);+ if (ctx->input_path) free(ctx->input_path);+ if (ctx->output_path) free(ctx->output_path);+ if (ctx->include_path) free(ctx->include_path);+ if (ctx->source_map_file) free(ctx->source_map_file);+ if (ctx->source_map_root) free(ctx->source_map_root);+ free_string_array(ctx->included_files);+ // play safe and reset properties+ ctx->output_string = 0;+ ctx->source_map_string = 0;+ ctx->error_message = 0;+ ctx->error_text = 0;+ ctx->error_json = 0;+ ctx->error_file = 0;+ ctx->input_path = 0;+ ctx->output_path = 0;+ ctx->include_path = 0;+ ctx->source_map_file = 0;+ ctx->source_map_root = 0;+ ctx->included_files = 0;+ // now clear the options+ sass_clear_options(ctx);+ }++ void ADDCALL sass_delete_compiler (struct Sass_Compiler* compiler)+ {+ if (compiler == 0) return;+ Context* cpp_ctx = (Context*) compiler->cpp_ctx;+ compiler->cpp_ctx = 0;+ delete cpp_ctx;+ free(compiler);+ }++ // Deallocate all associated memory with contexts+ void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx) { sass_clear_context(ctx); free(ctx); }+ void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx) { sass_clear_context(ctx); free(ctx); }++ // Getters for sass context from specific implementations+ struct Sass_Context* ADDCALL sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; }+ struct Sass_Context* ADDCALL sass_data_context_get_context(struct Sass_Data_Context* ctx) { return ctx; }++ // Getters for context options from Sass_Context+ struct Sass_Options* ADDCALL sass_context_get_options(struct Sass_Context* ctx) { return ctx; }+ struct Sass_Options* ADDCALL sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; }+ struct Sass_Options* ADDCALL sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; }+ void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); }+ void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); }++ // Getters for Sass_Compiler options (get conected sass context)+ enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; }+ struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; }+ // Getters for Sass_Compiler options (query import stack)+ size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); }+ Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); }+ Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx) { return compiler->cpp_ctx->import_stack[idx]; }++ // Calculate the size of the stored null terminated array+ size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx)+ { size_t l = 0; auto i = ctx->included_files; while (i && *i) { ++i; ++l; } return l; }++ // Create getter and setters for options+ IMPLEMENT_SASS_OPTION_ACCESSOR(int, precision);+ IMPLEMENT_SASS_OPTION_ACCESSOR(enum Sass_Output_Style, output_style);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_comments);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url);+ IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src);+ IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions);+ IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_importers);+ IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_headers);+ IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent);+ IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root);++ // Create getter and setters for context+ IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_json);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_text);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file);+ IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line);+ IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string);+ IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string);+ IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files);++ // Take ownership of memory (value on context is set to 0)+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_json);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_message);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_text);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_file);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, output_string);+ IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string);+ IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files);++ // Push function for include paths (no manipulation support for now)+ void ADDCALL sass_option_push_include_path(struct Sass_Options* options, const char* path)+ {++ struct string_list* include_path = (struct string_list*) calloc(1, sizeof(struct string_list));+ if (include_path == 0) return;+ include_path->string = path ? sass_strdup(path) : 0;+ struct string_list* last = options->include_paths;+ if (!options->include_paths) {+ options->include_paths = include_path;+ } else {+ while (last->next)+ last = last->next;+ last->next = include_path;+ }++ }++ // Push function for plugin paths (no manipulation support for now)+ void ADDCALL sass_option_push_plugin_path(struct Sass_Options* options, const char* path)+ {++ struct string_list* plugin_path = (struct string_list*) calloc(1, sizeof(struct string_list));+ if (plugin_path == 0) return;+ plugin_path->string = path ? sass_strdup(path) : 0;+ struct string_list* last = options->plugin_paths;+ if (!options->plugin_paths) {+ options->plugin_paths = plugin_path;+ } else {+ while (last->next)+ last = last->next;+ last->next = plugin_path;+ }++ }++}
+ libsass/sass_context.h view
@@ -0,0 +1,149 @@+#ifndef SASS_C_CONTEXT_H+#define SASS_C_CONTEXT_H++#include <stddef.h>+#include <stdbool.h>+#include "sass.h"++#ifdef __cplusplus+extern "C" {+#endif+++// Forward declaration+struct Sass_Compiler;++// Forward declaration+struct Sass_Options; // base struct+struct Sass_Context; // : Sass_Options+struct Sass_File_Context; // : Sass_Context+struct Sass_Data_Context; // : Sass_Context++// Compiler states+enum Sass_Compiler_State {+ SASS_COMPILER_CREATED,+ SASS_COMPILER_PARSED,+ SASS_COMPILER_EXECUTED+};++// Create and initialize an option struct+ADDAPI struct Sass_Options* ADDCALL sass_make_options (void);+// Create and initialize a specific context+ADDAPI struct Sass_File_Context* ADDCALL sass_make_file_context (const char* input_path);+ADDAPI struct Sass_Data_Context* ADDCALL sass_make_data_context (char* source_string);++// Call the compilation step for the specific context+ADDAPI int ADDCALL sass_compile_file_context (struct Sass_File_Context* ctx);+ADDAPI int ADDCALL sass_compile_data_context (struct Sass_Data_Context* ctx);++// Create a sass compiler instance for more control+ADDAPI struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx);+ADDAPI struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx);++// Execute the different compilation steps individually+// Usefull if you only want to query the included files+ADDAPI int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler);+ADDAPI int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler);++// Release all memory allocated with the compiler+// This does _not_ include any contexts or options+ADDAPI void ADDCALL sass_delete_compiler(struct Sass_Compiler* compiler);++// Release all memory allocated and also ourself+ADDAPI void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx);+ADDAPI void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx);++// Getters for context from specific implementation+ADDAPI struct Sass_Context* ADDCALL sass_file_context_get_context (struct Sass_File_Context* file_ctx);+ADDAPI struct Sass_Context* ADDCALL sass_data_context_get_context (struct Sass_Data_Context* data_ctx);++// Getters for Context_Options from Sass_Context+ADDAPI struct Sass_Options* ADDCALL sass_context_get_options (struct Sass_Context* ctx);+ADDAPI struct Sass_Options* ADDCALL sass_file_context_get_options (struct Sass_File_Context* file_ctx);+ADDAPI struct Sass_Options* ADDCALL sass_data_context_get_options (struct Sass_Data_Context* data_ctx);+ADDAPI void ADDCALL sass_file_context_set_options (struct Sass_File_Context* file_ctx, struct Sass_Options* opt);+ADDAPI void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* data_ctx, struct Sass_Options* opt);+++// Getters for Context_Option values+ADDAPI int ADDCALL sass_option_get_precision (struct Sass_Options* options);+ADDAPI enum Sass_Output_Style ADDCALL sass_option_get_output_style (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_source_comments (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_source_map_embed (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_source_map_contents (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_omit_source_map_url (struct Sass_Options* options);+ADDAPI bool ADDCALL sass_option_get_is_indented_syntax_src (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_indent (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_linefeed (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_input_path (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_output_path (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_plugin_path (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_include_path (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_source_map_file (struct Sass_Options* options);+ADDAPI const char* ADDCALL sass_option_get_source_map_root (struct Sass_Options* options);+ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_headers (struct Sass_Options* options);+ADDAPI Sass_Importer_List ADDCALL sass_option_get_c_importers (struct Sass_Options* options);+ADDAPI Sass_Function_List ADDCALL sass_option_get_c_functions (struct Sass_Options* options);++// Setters for Context_Option values+ADDAPI void ADDCALL sass_option_set_precision (struct Sass_Options* options, int precision);+ADDAPI void ADDCALL sass_option_set_output_style (struct Sass_Options* options, enum Sass_Output_Style output_style);+ADDAPI void ADDCALL sass_option_set_source_comments (struct Sass_Options* options, bool source_comments);+ADDAPI void ADDCALL sass_option_set_source_map_embed (struct Sass_Options* options, bool source_map_embed);+ADDAPI void ADDCALL sass_option_set_source_map_contents (struct Sass_Options* options, bool source_map_contents);+ADDAPI void ADDCALL sass_option_set_omit_source_map_url (struct Sass_Options* options, bool omit_source_map_url);+ADDAPI void ADDCALL sass_option_set_is_indented_syntax_src (struct Sass_Options* options, bool is_indented_syntax_src);+ADDAPI void ADDCALL sass_option_set_indent (struct Sass_Options* options, const char* indent);+ADDAPI void ADDCALL sass_option_set_linefeed (struct Sass_Options* options, const char* linefeed);+ADDAPI void ADDCALL sass_option_set_input_path (struct Sass_Options* options, const char* input_path);+ADDAPI void ADDCALL sass_option_set_output_path (struct Sass_Options* options, const char* output_path);+ADDAPI void ADDCALL sass_option_set_plugin_path (struct Sass_Options* options, const char* plugin_path);+ADDAPI void ADDCALL sass_option_set_include_path (struct Sass_Options* options, const char* include_path);+ADDAPI void ADDCALL sass_option_set_source_map_file (struct Sass_Options* options, const char* source_map_file);+ADDAPI void ADDCALL sass_option_set_source_map_root (struct Sass_Options* options, const char* source_map_root);+ADDAPI void ADDCALL sass_option_set_c_headers (struct Sass_Options* options, Sass_Importer_List c_headers);+ADDAPI void ADDCALL sass_option_set_c_importers (struct Sass_Options* options, Sass_Importer_List c_importers);+ADDAPI void ADDCALL sass_option_set_c_functions (struct Sass_Options* options, Sass_Function_List c_functions);+++// Getters for Sass_Context values+ADDAPI const char* ADDCALL sass_context_get_output_string (struct Sass_Context* ctx);+ADDAPI int ADDCALL sass_context_get_error_status (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_error_json (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_error_text (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_error_message (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_error_file (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_error_src (struct Sass_Context* ctx);+ADDAPI size_t ADDCALL sass_context_get_error_line (struct Sass_Context* ctx);+ADDAPI size_t ADDCALL sass_context_get_error_column (struct Sass_Context* ctx);+ADDAPI const char* ADDCALL sass_context_get_source_map_string (struct Sass_Context* ctx);+ADDAPI char** ADDCALL sass_context_get_included_files (struct Sass_Context* ctx);++// Calculate the size of the stored null terminated array+ADDAPI size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx);++// Take ownership of memory (value on context is set to 0)+ADDAPI char* ADDCALL sass_context_take_error_json (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_error_text (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_error_message (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_error_file (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_output_string (struct Sass_Context* ctx);+ADDAPI char* ADDCALL sass_context_take_source_map_string (struct Sass_Context* ctx);+ADDAPI char** ADDCALL sass_context_take_included_files (struct Sass_Context* ctx);++// Getters for Sass_Compiler options+ADDAPI enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler);+ADDAPI struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler);+ADDAPI size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler);+ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler);+ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx);++// Push function for paths (no manipulation support for now)+ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path);+ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path);++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/sass_functions.cpp view
@@ -0,0 +1,173 @@+#ifdef _WIN32+#include <io.h>+#else+#include <unistd.h>+#endif++#include <cstring>+#include "util.hpp"+#include "context.hpp"+#include "sass_functions.h"++extern "C" {+ using namespace std;+ using namespace Sass;++ // Struct to hold custom function callback+ struct Sass_Function {+ const char* signature;+ Sass_Function_Fn function;+ void* cookie;+ };++ Sass_Function_List ADDCALL sass_make_function_list(size_t length)+ {+ return (Sass_Function_List) calloc(length + 1, sizeof(Sass_Function_Entry));+ }++ Sass_Function_Entry ADDCALL sass_make_function(const char* signature, Sass_Function_Fn function, void* cookie)+ {+ Sass_Function_Entry cb = (Sass_Function_Entry) calloc(1, sizeof(Sass_Function));+ if (cb == 0) return 0;+ cb->signature = signature;+ cb->function = function;+ cb->cookie = cookie;+ return cb;+ }++ // Setters and getters for callbacks on function lists+ Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos) { return list[pos]; }+ void sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb) { list[pos] = cb; }++ const char* ADDCALL sass_function_get_signature(Sass_Function_Entry cb) { return cb->signature; }+ Sass_Function_Fn ADDCALL sass_function_get_function(Sass_Function_Entry cb) { return cb->function; }+ void* ADDCALL sass_function_get_cookie(Sass_Function_Entry cb) { return cb->cookie; }++ // External import entry+ struct Sass_Import {+ char* path;+ char* base;+ char* source;+ char* srcmap;+ // error handling+ char* error;+ size_t line;+ size_t column;+ };++ // Struct to hold importer callback+ struct Sass_Importer {+ Sass_Importer_Fn importer;+ double priority;+ void* cookie;+ };++ Sass_Importer_Entry ADDCALL sass_make_importer(Sass_Importer_Fn importer, double priority, void* cookie)+ {+ Sass_Importer_Entry cb = (Sass_Importer_Entry) calloc(1, sizeof(Sass_Importer));+ if (cb == 0) return 0;+ cb->importer = importer;+ cb->priority = priority;+ cb->cookie = cookie;+ return cb;+ }++ Sass_Importer_Fn ADDCALL sass_importer_get_function(Sass_Importer_Entry cb) { return cb->importer; }+ double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb) { return cb->priority; }+ void* ADDCALL sass_importer_get_cookie(Sass_Importer_Entry cb) { return cb->cookie; }++ // Just in case we have some stray import structs+ void ADDCALL sass_delete_importer (Sass_Importer_Entry cb)+ {+ free(cb);+ }++ // Creator for sass custom importer function list+ Sass_Importer_List ADDCALL sass_make_importer_list(size_t length)+ {+ return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry));+ }++ // Creator for sass custom importer return argument list+ Sass_Import_List ADDCALL sass_make_import_list(size_t length)+ {+ return (Sass_Import**) calloc(length + 1, sizeof(Sass_Import*));+ }++ // Creator for a single import entry returned by the custom importer inside the list+ // We take ownership of the memory for source and srcmap (freed when context is destroyd)+ Sass_Import_Entry ADDCALL sass_make_import(const char* path, const char* base, char* source, char* srcmap)+ {+ Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import));+ if (v == 0) return 0;+ v->path = path ? sass_strdup(path) : 0;+ v->base = base ? sass_strdup(base) : 0;+ v->source = source;+ v->srcmap = srcmap;+ v->error = 0;+ v->line = -1;+ v->column = -1;+ return v;+ }++ // Older style, but somehow still valid - keep around or deprecate?+ Sass_Import_Entry ADDCALL sass_make_import_entry(const char* path, char* source, char* srcmap)+ {+ return sass_make_import(path, path, source, srcmap);+ }++ // Upgrade a normal import entry to throw an error (original path can be re-used by error reporting)+ Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* error, size_t line, size_t col)+ {+ if (import == 0) return 0;+ if (import->error) free(import->error);+ import->error = error ? strdup(error) : 0;+ import->line = line ? line : -1;+ import->column = col ? col : -1;+ return import;+ }++ // Setters and getters for entries on the import list+ void ADDCALL sass_import_set_list_entry(Sass_Import_List list, size_t idx, Sass_Import_Entry entry) { list[idx] = entry; }+ Sass_Import_Entry ADDCALL sass_import_get_list_entry(Sass_Import_List list, size_t idx) { return list[idx]; }++ // Deallocator for the allocated memory+ void ADDCALL sass_delete_import_list(Sass_Import_List list)+ {+ Sass_Import_List it = list;+ if (list == 0) return;+ while(*list) {+ sass_delete_import(*list);+ ++list;+ }+ free(it);+ }++ // Just in case we have some stray import structs+ void ADDCALL sass_delete_import(Sass_Import_Entry import)+ {+ free(import->path);+ free(import->base);+ free(import->source);+ free(import->srcmap);+ free(import->error);+ free(import);+ }++ // Getter for import entry+ const char* ADDCALL sass_import_get_path(Sass_Import_Entry entry) { return entry->path; }+ const char* ADDCALL sass_import_get_base(Sass_Import_Entry entry) { return entry->base; }+ const char* ADDCALL sass_import_get_source(Sass_Import_Entry entry) { return entry->source; }+ const char* ADDCALL sass_import_get_srcmap(Sass_Import_Entry entry) { return entry->srcmap; }++ // Getter for import error entry+ size_t ADDCALL sass_import_get_error_line(Sass_Import_Entry entry) { return entry->line; }+ size_t ADDCALL sass_import_get_error_column(Sass_Import_Entry entry) { return entry->column; }+ const char* ADDCALL sass_import_get_error_message(Sass_Import_Entry entry) { return entry->error; }++ // Explicit functions to take ownership of the memory+ // Resets our own property since we do not know if it is still alive+ char* ADDCALL sass_import_take_source(Sass_Import_Entry entry) { char* ptr = entry->source; entry->source = 0; return ptr; }+ char* ADDCALL sass_import_take_srcmap(Sass_Import_Entry entry) { char* ptr = entry->srcmap; entry->srcmap = 0; return ptr; }++}
+ libsass/sass_functions.h view
@@ -0,0 +1,108 @@+#ifndef SASS_C_FUNCTIONS_H+#define SASS_C_FUNCTIONS_H++#include <stddef.h>+#include <stdbool.h>+#include "sass.h"++#ifdef __cplusplus+extern "C" {+#endif+++// Forward declaration+struct Sass_Import;+struct Sass_Options;+struct Sass_Compiler;+struct Sass_Importer;+struct Sass_Function;++// Typedef helpers for import lists+typedef struct Sass_Import (*Sass_Import_Entry);+typedef struct Sass_Import* (*Sass_Import_List);+// Typedef helpers for custom importer lists+typedef struct Sass_Importer (*Sass_Importer_Entry);+typedef struct Sass_Importer* (*Sass_Importer_List);+// Typedef defining importer signature and return type+typedef Sass_Import_List (*Sass_Importer_Fn)+ (const char* url, Sass_Importer_Entry cb, struct Sass_Compiler* compiler);++// Typedef helpers for custom functions lists+typedef struct Sass_Function (*Sass_Function_Entry);+typedef struct Sass_Function* (*Sass_Function_List);+// Typedef defining function signature and return type+typedef union Sass_Value* (*Sass_Function_Fn)+ (const union Sass_Value*, Sass_Function_Entry cb, struct Sass_Options* options);+++// Creator for sass custom importer return argument list+ADDAPI Sass_Importer_List ADDCALL sass_make_importer_list (size_t length);+ADDAPI Sass_Importer_Entry ADDCALL sass_importer_get_list_entry (Sass_Importer_List list, size_t idx);+ADDAPI void ADDCALL sass_importer_set_list_entry (Sass_Importer_List list, size_t idx, Sass_Importer_Entry entry);+++// Creators for custom importer callback (with some additional pointer)+// The pointer is mostly used to store the callback into the actual binding+ADDAPI Sass_Importer_Entry ADDCALL sass_make_importer (Sass_Importer_Fn importer, double priority, void* cookie);++// Getters for import function descriptors+ADDAPI Sass_Importer_Fn ADDCALL sass_importer_get_function (Sass_Importer_Entry cb);+ADDAPI double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb);+ADDAPI void* ADDCALL sass_importer_get_cookie (Sass_Importer_Entry cb);++// Deallocator for associated memory+ADDAPI void ADDCALL sass_delete_importer (Sass_Importer_Entry cb);++// Creator for sass custom importer return argument list+ADDAPI Sass_Import_List ADDCALL sass_make_import_list (size_t length);+// Creator for a single import entry returned by the custom importer inside the list+ADDAPI Sass_Import_Entry ADDCALL sass_make_import_entry (const char* path, char* source, char* srcmap);+ADDAPI Sass_Import_Entry ADDCALL sass_make_import (const char* path, const char* base, char* source, char* srcmap);+// set error message to abort import and to print out a message (path from existing object is used in output)+ADDAPI Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col);++// Setters to insert an entry into the import list (you may also use [] access directly)+// Since we are dealing with pointers they should have a guaranteed and fixed size+ADDAPI void ADDCALL sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry);+ADDAPI Sass_Import_Entry ADDCALL sass_import_get_list_entry (Sass_Import_List list, size_t idx);++// Getters for import entry+ADDAPI const char* ADDCALL sass_import_get_path (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_base (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_source (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_srcmap (Sass_Import_Entry);+// Explicit functions to take ownership of these items+// The property on our struct will be reset to NULL+ADDAPI char* ADDCALL sass_import_take_source (Sass_Import_Entry);+ADDAPI char* ADDCALL sass_import_take_srcmap (Sass_Import_Entry);+// Getters from import error entry+ADDAPI size_t ADDCALL sass_import_get_error_line (Sass_Import_Entry);+ADDAPI size_t ADDCALL sass_import_get_error_column (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_error_message (Sass_Import_Entry);++// Deallocator for associated memory (incl. entries)+ADDAPI void ADDCALL sass_delete_import_list (Sass_Import_List);+// Just in case we have some stray import structs+ADDAPI void ADDCALL sass_delete_import (Sass_Import_Entry);++++// Creators for sass function list and function descriptors+ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length);+ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie);++// Setters and getters for callbacks on function lists+ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos);+ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb);++// Getters for custom function descriptors+ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb);+ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb);+ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb);+++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/sass_interface.cpp view
@@ -0,0 +1,287 @@+#ifdef _WIN32+#include <io.h>+#define LFEED "\n"+#else+#include <unistd.h>+#define LFEED "\n"+#endif++#include <string>+#include <cstdlib>+#include <cstring>+#include <sstream>+#include <iostream>++#include "util.hpp"+#include "context.hpp"+#include "inspect.hpp"+#include "error_handling.hpp"+#include "sass_interface.h"+++extern "C" {+ using namespace std;++ sass_context* sass_new_context()+ { return (sass_context*) calloc(1, sizeof(sass_context)); }++ // helper for safe access to c_ctx+ static const char* safe_str (const char* str) {+ return str == NULL ? "" : str;+ }++ static void copy_strings(const std::vector<std::string>& strings, char*** array, int skip = 0) {+ int num = static_cast<int>(strings.size());+ char** arr = (char**) malloc(sizeof(char*) * (num + 1));++ for(int i = skip; i < num; i++) {+ arr[i-skip] = (char*) malloc(sizeof(char) * (strings[i].size() + 1));+ std::copy(strings[i].begin(), strings[i].end(), arr[i-skip]);+ arr[i-skip][strings[i].size()] = '\0';+ }++ arr[num-skip] = 0;+ *array = arr;+ }++ static void free_string_array(char ** arr) {+ if(!arr)+ return;++ char **it = arr;+ while (it && (*it)) {+ free(*it);+ ++it;+ }++ free(arr);+ }++ void sass_free_context(sass_context* ctx)+ {+ if (ctx->output_string) free(ctx->output_string);+ if (ctx->source_map_string) free(ctx->source_map_string);+ if (ctx->error_message) free(ctx->error_message);+ if (ctx->c_functions) free(ctx->c_functions);++ free_string_array(ctx->included_files);++ free(ctx);+ }++ sass_file_context* sass_new_file_context()+ { return (sass_file_context*) calloc(1, sizeof(sass_file_context)); }++ void sass_free_file_context(sass_file_context* ctx)+ {+ if (ctx->output_string) free(ctx->output_string);+ if (ctx->source_map_string) free(ctx->source_map_string);+ if (ctx->error_message) free(ctx->error_message);+ if (ctx->c_functions) free(ctx->c_functions);++ free_string_array(ctx->included_files);++ free(ctx);+ }++ sass_folder_context* sass_new_folder_context()+ { return (sass_folder_context*) calloc(1, sizeof(sass_folder_context)); }++ void sass_free_folder_context(sass_folder_context* ctx)+ {+ free_string_array(ctx->included_files);+ free(ctx);+ }++ int sass_compile(sass_context* c_ctx)+ {+ using namespace Sass;+ try {+ string input_path = safe_str(c_ctx->input_path);+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ string output_path;+ if (!c_ctx->output_path) {+ if (input_path != "") {+ output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+ }+ }+ else {+ output_path = c_ctx->output_path;+ }+ Context cpp_ctx(+ Context::Data().source_c_str(c_ctx->source_string)+ .output_path(output_path)+ .output_style((Output_Style) c_ctx->options.output_style)+ .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src)+ .source_comments(c_ctx->options.source_comments)+ .source_map_file(safe_str(c_ctx->options.source_map_file))+ .source_map_root(safe_str(c_ctx->options.source_map_root))+ .source_map_embed(c_ctx->options.source_map_embed)+ .source_map_contents(c_ctx->options.source_map_contents)+ .omit_source_map_url(c_ctx->options.omit_source_map_url)+ .include_paths_c_str(c_ctx->options.include_paths)+ .plugin_paths_c_str(c_ctx->options.plugin_paths)+ // .include_paths_array(0)+ // .plugin_paths_array(0)+ .include_paths(vector<string>())+ .plugin_paths(vector<string>())+ .precision(c_ctx->options.precision ? c_ctx->options.precision : 5)+ .indent(c_ctx->options.indent ? c_ctx->options.indent : " ")+ .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED)+ );+ if (c_ctx->c_functions) {+ Sass_Function_List this_func_data = c_ctx->c_functions;+ while ((this_func_data) && (*this_func_data)) {+ cpp_ctx.c_functions.push_back(*this_func_data);+ ++this_func_data;+ }+ }+ c_ctx->output_string = cpp_ctx.compile_string();+ c_ctx->source_map_string = cpp_ctx.generate_source_map();+ c_ctx->error_message = 0;+ c_ctx->error_status = 0;++ copy_strings(cpp_ctx.get_included_files(1), &c_ctx->included_files, 1);+ }+ catch (Sass_Error& e) {+ stringstream msg_stream;+ msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.message << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch(bad_alloc& ba) {+ stringstream msg_stream;+ msg_stream << "Unable to allocate memory: " << ba.what() << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (std::exception& e) {+ stringstream msg_stream;+ msg_stream << "Error: " << e.what() << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (string& e) {+ stringstream msg_stream;+ msg_stream << "Error: " << e << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (...) {+ // couldn't find the specified file in the include paths; report an error+ stringstream msg_stream;+ msg_stream << "Unknown error occurred" << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ return 0;+ }++ int sass_compile_file(sass_file_context* c_ctx)+ {+ using namespace Sass;+ try {+ string input_path = safe_str(c_ctx->input_path);+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ string output_path;+ if (!c_ctx->output_path) {+ output_path = (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+ }+ else {+ output_path = c_ctx->output_path;+ }+ Context cpp_ctx(+ Context::Data().entry_point(input_path)+ .output_path(output_path)+ .output_style((Output_Style) c_ctx->options.output_style)+ .is_indented_syntax_src(c_ctx->options.is_indented_syntax_src)+ .source_comments(c_ctx->options.source_comments)+ .source_map_file(safe_str(c_ctx->options.source_map_file))+ .source_map_root(safe_str(c_ctx->options.source_map_root))+ .source_map_embed(c_ctx->options.source_map_embed)+ .source_map_contents(c_ctx->options.source_map_contents)+ .omit_source_map_url(c_ctx->options.omit_source_map_url)+ .include_paths_c_str(c_ctx->options.include_paths)+ .plugin_paths_c_str(c_ctx->options.plugin_paths)+ // .include_paths_array(0)+ // .plugin_paths_array(0)+ .include_paths(vector<string>())+ .plugin_paths(vector<string>())+ .precision(c_ctx->options.precision ? c_ctx->options.precision : 5)+ .indent(c_ctx->options.indent ? c_ctx->options.indent : " ")+ .linefeed(c_ctx->options.linefeed ? c_ctx->options.linefeed : LFEED)+ );+ if (c_ctx->c_functions) {+ Sass_Function_List this_func_data = c_ctx->c_functions;+ while ((this_func_data) && (*this_func_data)) {+ cpp_ctx.c_functions.push_back(*this_func_data);+ ++this_func_data;+ }+ }+ c_ctx->output_string = cpp_ctx.compile_file();+ c_ctx->source_map_string = cpp_ctx.generate_source_map();+ c_ctx->error_message = 0;+ c_ctx->error_status = 0;++ copy_strings(cpp_ctx.get_included_files(), &c_ctx->included_files);+ }+ catch (Sass_Error& e) {+ stringstream msg_stream;+ msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.message << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch(bad_alloc& ba) {+ stringstream msg_stream;+ msg_stream << "Unable to allocate memory: " << ba.what() << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (std::exception& e) {+ stringstream msg_stream;+ msg_stream << "Error: " << e.what() << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (string& e) {+ stringstream msg_stream;+ msg_stream << "Error: " << e << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ catch (...) {+ // couldn't find the specified file in the include paths; report an error+ stringstream msg_stream;+ msg_stream << "Unknown error occurred" << endl;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_status = 1;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ }+ return 0;+ }++ int sass_compile_folder(sass_folder_context* c_ctx)+ {+ return 1;+ }++}
+ libsass/sass_interface.h view
@@ -0,0 +1,102 @@+#ifndef SASS_C_INTERFACE_H+#define SASS_C_INTERFACE_H++#include <stddef.h>+#include <stdbool.h>+#include "sass.h"++#ifdef __cplusplus+extern "C" {+#endif+++// Please ensure there are no null values.+// Thar be dragons.+struct sass_options {+ // Output style for the generated css code+ // A value from above SASS_STYLE_* constants+ int output_style;+ // If you want inline source comments+ bool source_comments;+ // Path to source map file+ // Enables the source map generating+ // Used to create sourceMappingUrl+ const char* source_map_file;+ // Disable sourceMappingUrl in css output+ bool omit_source_map_url;+ // embed sourceMappingUrl as data uri+ bool source_map_embed;+ // embed include contents in maps+ bool source_map_contents;+ // Pass-through as sourceRoot property+ const char* source_map_root;+ // Treat source_string as sass (as opposed to scss)+ bool is_indented_syntax_src;+ // Colon-separated list of paths+ // Semicolon-separated on Windows+ const char* include_paths;+ const char* plugin_paths;+ // String to be used for indentation+ const char* indent;+ // String to be used to for line feeds+ const char* linefeed;+ // Precision for outputting fractional numbers+ int precision;+};++struct sass_context {+ const char* input_path;+ const char* output_path;+ const char* source_string;+ char* output_string;+ char* source_map_string;+ struct sass_options options;+ int error_status;+ char* error_message;+ Sass_Function_List c_functions;+ char** included_files;+ int num_included_files;+};++struct sass_file_context {+ const char* input_path;+ const char* output_path;+ char* output_string;+ char* source_map_string;+ struct sass_options options;+ int error_status;+ char* error_message;+ Sass_Function_List c_functions;+ char** included_files;+ int num_included_files;+};++struct sass_folder_context {+ const char* search_path;+ const char* output_path;+ struct sass_options options;+ int error_status;+ char* error_message;+ Sass_Function_List c_functions;+ char** included_files;+ int num_included_files;+};++struct sass_context* sass_new_context (void);+struct sass_file_context* sass_new_file_context (void);+struct sass_folder_context* sass_new_folder_context (void);++void sass_free_context (struct sass_context* ctx);+void sass_free_file_context (struct sass_file_context* ctx);+void sass_free_folder_context(struct sass_folder_context* ctx);++int sass_compile (struct sass_context* ctx);+int sass_compile_file (struct sass_file_context* ctx);+int sass_compile_folder (struct sass_folder_context* ctx);+++#ifdef __cplusplus+}+#endif++#endif
+ libsass/sass_util.cpp view
@@ -0,0 +1,132 @@+#include "node.hpp"+#include "to_string.hpp"++namespace Sass {+++ /*+ This is the equivalent of ruby's Sass::Util.paths.++ # Return an array of all possible paths through the given arrays.+ #+ # @param arrs [NodeCollection<NodeCollection<Node>>]+ # @return [NodeCollection<NodeCollection<Node>>]+ #+ # @example+ # paths([[1, 2], [3, 4], [5]]) #=>+ # # [[1, 3, 5],+ # # [2, 3, 5],+ # # [1, 4, 5],+ # # [2, 4, 5]]++ The following is the modified version of the ruby code that was more portable to C++. You+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.++ def paths(arrs)+ // I changed the inject and maps to an iterative approach to make it easier to implement in C+++ loopStart = [[]]++ for arr in arrs do+ permutations = []+ for e in arr do+ for path in loopStart do+ permutations.push(path + [e])+ end+ end+ loopStart = permutations+ end+ end+ */+ Node paths(const Node& arrs, Context& ctx) {+ To_String to_string(&ctx);++ Node loopStart = Node::createCollection();+ loopStart.collection()->push_back(Node::createCollection());++ for (NodeDeque::iterator arrsIter = arrs.collection()->begin(), arrsEndIter = arrs.collection()->end();+ arrsIter != arrsEndIter; ++arrsIter) {++ Node& arr = *arrsIter;++ Node permutations = Node::createCollection();++ for (NodeDeque::iterator arrIter = arr.collection()->begin(), arrIterEnd = arr.collection()->end();+ arrIter != arrIterEnd; ++arrIter) {++ Node& e = *arrIter;++ for (NodeDeque::iterator loopStartIter = loopStart.collection()->begin(), loopStartIterEnd = loopStart.collection()->end();+ loopStartIter != loopStartIterEnd; ++loopStartIter) {++ Node& path = *loopStartIter;++ Node newPermutation = Node::createCollection();+ newPermutation.plus(path);+ newPermutation.collection()->push_back(e);++ permutations.collection()->push_back(newPermutation);+ }+ }++ loopStart = permutations;+ }++ return loopStart;+ }+++ /*+ This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.+ Sass::Util.flatten requires the number of levels to flatten, while+ [].flatten doesn't and will flatten the entire array. This function+ supports both.++ # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened+ #+ # @param arr [NodeCollection] The array to flatten+ # @param n [int] The number of levels to flatten+ # @return [NodeCollection] The flattened array++ The following is the modified version of the ruby code that was more portable to C++. You+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.++ def flatten(arr, n = -1)+ if n != -1 and n == 0 then+ return arr+ end++ flattened = []++ for e in arr do+ if e.is_a?(Array) then+ flattened.concat(flatten(e, n - 1))+ else+ flattened << e+ end+ end++ return flattened+ end+ */+ Node flatten(const Node& arr, Context& ctx, int n = -1) {+ if (n != -1 && n == 0) {+ return arr;+ }++ Node flattened = Node::createCollection();++ for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end();+ iter != iterEnd; iter++) {+ Node& e = *iter;++ if (e.isCollection()) {+ Node recurseFlattened = flatten(e, ctx, n - 1);+ flattened.collection()->insert(flattened.collection()->end(), recurseFlattened.collection()->begin(), recurseFlattened.collection()->end());+ } else {+ flattened.collection()->push_back(e);+ }+ }++ return flattened;+ }+}
+ libsass/sass_util.hpp view
@@ -0,0 +1,260 @@+#ifndef SASS_SASS_UTIL_H+#define SASS_SASS_UTIL_H++#include <deque>+#include <iostream>++#include "ast.hpp"+#include "node.hpp"+#include "debug.hpp"++namespace Sass {+++ using namespace std;+++ /*+ This is for ports of functions in the Sass:Util module.+ */+++ /*+ # Return a Node collection of all possible paths through the given Node collection of Node collections.+ #+ # @param arrs [NodeCollection<NodeCollection<Node>>]+ # @return [NodeCollection<NodeCollection<Node>>]+ #+ # @example+ # paths([[1, 2], [3, 4], [5]]) #=>+ # # [[1, 3, 5],+ # # [2, 3, 5],+ # # [1, 4, 5],+ # # [2, 4, 5]]+ */+ Node paths(const Node& arrs, Context& ctx);+++ /*+ This class is a default implementation of a Node comparator that can be passed to the lcs function below.+ It uses operator== for equality comparision. It then returns one if the Nodes are equal.+ */+ class DefaultLcsComparator {+ public:+ bool operator()(const Node& one, const Node& two, Node& out) const {+ // TODO: Is this the correct C++ interpretation?+ // block ||= proc {|a, b| a == b && a}+ if (one == two) {+ out = one;+ return true;+ }++ return false;+ }+ };+++ typedef vector<vector<int> > LCSTable;+++ /*+ This is the equivalent of ruby's Sass::Util.lcs_backtrace.++ # Computes a single longest common subsequence for arrays x and y.+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS+ */+ template<typename ComparatorType>+ Node lcs_backtrace(const LCSTable& c, const Node& x, const Node& y, int i, int j, const ComparatorType& comparator) {+ DEBUG_PRINTLN(LCS, "LCSBACK: X=" << x << " Y=" << y << " I=" << i << " J=" << j)++ if (i == 0 || j == 0) {+ DEBUG_PRINTLN(LCS, "RETURNING EMPTY")+ return Node::createCollection();+ }++ NodeDeque& xChildren = *(x.collection());+ NodeDeque& yChildren = *(y.collection());++ Node compareOut = Node::createNil();+ if (comparator(xChildren[i], yChildren[j], compareOut)) {+ DEBUG_PRINTLN(LCS, "RETURNING AFTER ELEM COMPARE")+ Node result = lcs_backtrace(c, x, y, i - 1, j - 1, comparator);+ result.collection()->push_back(compareOut);+ return result;+ }++ if (c[i][j - 1] > c[i - 1][j]) {+ DEBUG_PRINTLN(LCS, "RETURNING AFTER TABLE COMPARE")+ return lcs_backtrace(c, x, y, i, j - 1, comparator);+ }++ DEBUG_PRINTLN(LCS, "FINAL RETURN")+ return lcs_backtrace(c, x, y, i - 1, j, comparator);+ }+++ /*+ This is the equivalent of ruby's Sass::Util.lcs_table.++ # Calculates the memoization table for the Least Common Subsequence algorithm.+ # Algorithm from http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS+ */+ template<typename ComparatorType>+ void lcs_table(const Node& x, const Node& y, const ComparatorType& comparator, LCSTable& out) {+ DEBUG_PRINTLN(LCS, "LCSTABLE: X=" << x << " Y=" << y)++ NodeDeque& xChildren = *(x.collection());+ NodeDeque& yChildren = *(y.collection());++ LCSTable c(xChildren.size(), vector<int>(yChildren.size()));++ // These shouldn't be necessary since the vector will be initialized to 0 already.+ // x.size.times {|i| c[i][0] = 0}+ // y.size.times {|j| c[0][j] = 0}++ for (size_t i = 1; i < xChildren.size(); i++) {+ for (size_t j = 1; j < yChildren.size(); j++) {+ Node compareOut = Node::createNil();++ if (comparator(xChildren[i], yChildren[j], compareOut)) {+ c[i][j] = c[i - 1][j - 1] + 1;+ } else {+ c[i][j] = max(c[i][j - 1], c[i - 1][j]);+ }+ }+ }++ out = c;+ }+++ /*+ This is the equivalent of ruby's Sass::Util.lcs.++ # Computes a single longest common subsequence for `x` and `y`.+ # If there are more than one longest common subsequences,+ # the one returned is that which starts first in `x`.++ # @param x [NodeCollection]+ # @param y [NodeCollection]+ # @comparator An equality check between elements of `x` and `y`.+ # @return [NodeCollection] The LCS++ http://en.wikipedia.org/wiki/Longest_common_subsequence_problem+ */+ template<typename ComparatorType>+ Node lcs(Node& x, Node& y, const ComparatorType& comparator, Context& ctx) {+ DEBUG_PRINTLN(LCS, "LCS: X=" << x << " Y=" << y)++ Node newX = Node::createCollection();+ newX.collection()->push_back(Node::createNil());+ newX.plus(x);++ Node newY = Node::createCollection();+ newY.collection()->push_back(Node::createNil());+ newY.plus(y);++ LCSTable table;+ lcs_table(newX, newY, comparator, table);++ return lcs_backtrace(table, newX, newY, static_cast<int>(newX.collection()->size()) - 1, static_cast<int>(newY.collection()->size()) - 1, comparator);+ }+++ /*+ This is the equivalent of ruby sass' Sass::Util.flatten and [].flatten.+ Sass::Util.flatten requires the number of levels to flatten, while+ [].flatten doesn't and will flatten the entire array. This function+ supports both.++ # Flattens the first `n` nested arrays. If n == -1, all arrays will be flattened+ #+ # @param arr [NodeCollection] The array to flatten+ # @param n [int] The number of levels to flatten+ # @return [NodeCollection] The flattened array+ */+ Node flatten(const Node& arr, Context& ctx, int n = -1);+++ /*+ This is the equivalent of ruby's Sass::Util.group_by_to_a.++ # Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed+ # order. Unlike [#hash_to_a], the resulting order isn't sorted key order;+ # instead, it's the same order as `#group_by` has under Ruby 1.9 (key+ # appearance order).+ #+ # @param enum [Enumerable]+ # @return [Array<[Object, Array]>] An array of pairs.++ TODO: update @param and @return once I know what those are.++ The following is the modified version of the ruby code that was more portable to C++. You+ should be able to drop it into ruby 3.2.19 and get the same results from ruby sass.++ def group_by_to_a(enum, &block)+ order = {}++ arr = []++ grouped = {}++ for e in enum do+ key = block[e]+ unless order.include?(key)+ order[key] = order.size+ end++ if not grouped.has_key?(key) then+ grouped[key] = [e]+ else+ grouped[key].push(e)+ end+ end++ grouped.each do |key, vals|+ arr[order[key]] = [key, vals]+ end++ arr+ end++ */+ template<typename EnumType, typename KeyType, typename KeyFunctorType>+ void group_by_to_a(vector<EnumType>& enumeration, KeyFunctorType& keyFunc, vector<pair<KeyType, vector<EnumType> > >& arr /*out*/) {++ map<unsigned int, KeyType> order;++ map<KeyType, vector<EnumType> > grouped;++ for (typename vector<EnumType>::iterator enumIter = enumeration.begin(), enumIterEnd = enumeration.end(); enumIter != enumIterEnd; enumIter++) {+ EnumType& e = *enumIter;++ KeyType key = keyFunc(e);++ if (grouped.find(key) == grouped.end()) {+ order.insert(make_pair(order.size(), key));++ vector<EnumType> newCollection;+ newCollection.push_back(e);+ grouped.insert(make_pair(key, newCollection));+ } else {+ vector<EnumType>& collection = grouped.at(key);+ collection.push_back(e);+ }+ }++ for (unsigned int index = 0; index < order.size(); index++) {+ KeyType& key = order.at(index);+ vector<EnumType>& values = grouped.at(key);++ pair<KeyType, vector<EnumType> > grouping = make_pair(key, values);++ arr.push_back(grouping);+ }+ }+++}++#endif
+ libsass/sass_values.cpp view
@@ -0,0 +1,339 @@+#ifdef _WIN32+#include <io.h>+#else+#include <unistd.h>+#endif++#include <cstdlib>+#include <cstring>+#include "util.hpp"+#include "sass_values.h"++extern "C" {+ using namespace std;+ using namespace Sass;++ struct Sass_Unknown {+ enum Sass_Tag tag;+ };++ struct Sass_Boolean {+ enum Sass_Tag tag;+ bool value;+ };++ struct Sass_Number {+ enum Sass_Tag tag;+ double value;+ char* unit;+ };++ struct Sass_Color {+ enum Sass_Tag tag;+ double r;+ double g;+ double b;+ double a;+ };++ struct Sass_String {+ enum Sass_Tag tag;+ char* value;+ };++ struct Sass_List {+ enum Sass_Tag tag;+ enum Sass_Separator separator;+ size_t length;+ // null terminated "array"+ union Sass_Value** values;+ };++ struct Sass_Map {+ enum Sass_Tag tag;+ size_t length;+ struct Sass_MapPair* pairs;+ };++ struct Sass_Null {+ enum Sass_Tag tag;+ };++ struct Sass_Error {+ enum Sass_Tag tag;+ char* message;+ };++ struct Sass_Warning {+ enum Sass_Tag tag;+ char* message;+ };++ union Sass_Value {+ struct Sass_Unknown unknown;+ struct Sass_Boolean boolean;+ struct Sass_Number number;+ struct Sass_Color color;+ struct Sass_String string;+ struct Sass_List list;+ struct Sass_Map map;+ struct Sass_Null null;+ struct Sass_Error error;+ struct Sass_Warning warning;+ };++ struct Sass_MapPair {+ union Sass_Value* key;+ union Sass_Value* value;+ };++ // Return the sass tag for a generic sass value+ enum Sass_Tag ADDCALL sass_value_get_tag(const union Sass_Value* v) { return v->unknown.tag; }++ // Check value for specified type+ bool ADDCALL sass_value_is_null(const union Sass_Value* v) { return v->unknown.tag == SASS_NULL; }+ bool ADDCALL sass_value_is_number(const union Sass_Value* v) { return v->unknown.tag == SASS_NUMBER; }+ bool ADDCALL sass_value_is_string(const union Sass_Value* v) { return v->unknown.tag == SASS_STRING; }+ bool ADDCALL sass_value_is_boolean(const union Sass_Value* v) { return v->unknown.tag == SASS_BOOLEAN; }+ bool ADDCALL sass_value_is_color(const union Sass_Value* v) { return v->unknown.tag == SASS_COLOR; }+ bool ADDCALL sass_value_is_list(const union Sass_Value* v) { return v->unknown.tag == SASS_LIST; }+ bool ADDCALL sass_value_is_map(const union Sass_Value* v) { return v->unknown.tag == SASS_MAP; }+ bool ADDCALL sass_value_is_error(const union Sass_Value* v) { return v->unknown.tag == SASS_ERROR; }+ bool ADDCALL sass_value_is_warning(const union Sass_Value* v) { return v->unknown.tag == SASS_WARNING; }++ // Getters and setters for Sass_Number+ double ADDCALL sass_number_get_value(const union Sass_Value* v) { return v->number.value; }+ void ADDCALL sass_number_set_value(union Sass_Value* v, double value) { v->number.value = value; }+ const char* ADDCALL sass_number_get_unit(const union Sass_Value* v) { return v->number.unit; }+ void ADDCALL sass_number_set_unit(union Sass_Value* v, char* unit) { v->number.unit = unit; }++ // Getters and setters for Sass_String+ const char* ADDCALL sass_string_get_value(const union Sass_Value* v) { return v->string.value; }+ void ADDCALL sass_string_set_value(union Sass_Value* v, char* value) { v->string.value = value; }++ // Getters and setters for Sass_Boolean+ bool ADDCALL sass_boolean_get_value(const union Sass_Value* v) { return v->boolean.value; }+ void ADDCALL sass_boolean_set_value(union Sass_Value* v, bool value) { v->boolean.value = value; }++ // Getters and setters for Sass_Color+ double ADDCALL sass_color_get_r(const union Sass_Value* v) { return v->color.r; }+ void ADDCALL sass_color_set_r(union Sass_Value* v, double r) { v->color.r = r; }+ double ADDCALL sass_color_get_g(const union Sass_Value* v) { return v->color.g; }+ void ADDCALL sass_color_set_g(union Sass_Value* v, double g) { v->color.g = g; }+ double ADDCALL sass_color_get_b(const union Sass_Value* v) { return v->color.b; }+ void ADDCALL sass_color_set_b(union Sass_Value* v, double b) { v->color.b = b; }+ double ADDCALL sass_color_get_a(const union Sass_Value* v) { return v->color.a; }+ void ADDCALL sass_color_set_a(union Sass_Value* v, double a) { v->color.a = a; }++ // Getters and setters for Sass_List+ size_t ADDCALL sass_list_get_length(const union Sass_Value* v) { return v->list.length; }+ enum Sass_Separator ADDCALL sass_list_get_separator(const union Sass_Value* v) { return v->list.separator; }+ void ADDCALL sass_list_set_separator(union Sass_Value* v, enum Sass_Separator separator) { v->list.separator = separator; }+ // Getters and setters for Sass_List values+ union Sass_Value* ADDCALL sass_list_get_value(const union Sass_Value* v, size_t i) { return v->list.values[i]; }+ void ADDCALL sass_list_set_value(union Sass_Value* v, size_t i, union Sass_Value* value) { v->list.values[i] = value; }++ // Getters and setters for Sass_Map+ size_t ADDCALL sass_map_get_length(const union Sass_Value* v) { return v->map.length; }+ // Getters and setters for Sass_List keys and values+ union Sass_Value* ADDCALL sass_map_get_key(const union Sass_Value* v, size_t i) { return v->map.pairs[i].key; }+ union Sass_Value* ADDCALL sass_map_get_value(const union Sass_Value* v, size_t i) { return v->map.pairs[i].value; }+ void ADDCALL sass_map_set_key(union Sass_Value* v, size_t i, union Sass_Value* key) { v->map.pairs[i].key = key; }+ void ADDCALL sass_map_set_value(union Sass_Value* v, size_t i, union Sass_Value* val) { v->map.pairs[i].value = val; }++ // Getters and setters for Sass_Error+ char* ADDCALL sass_error_get_message(const union Sass_Value* v) { return v->error.message; };+ void ADDCALL sass_error_set_message(union Sass_Value* v, char* msg) { v->error.message = msg; };++ // Getters and setters for Sass_Warning+ char* ADDCALL sass_warning_get_message(const union Sass_Value* v) { return v->warning.message; };+ void ADDCALL sass_warning_set_message(union Sass_Value* v, char* msg) { v->warning.message = msg; };++ // Creator functions for all value types++ union Sass_Value* ADDCALL sass_make_boolean(bool val)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->boolean.tag = SASS_BOOLEAN;+ v->boolean.value = val;+ return v;+ }++ union Sass_Value* ADDCALL sass_make_number(double val, const char* unit)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->number.tag = SASS_NUMBER;+ v->number.value = val;+ v->number.unit = unit ? sass_strdup(unit) : 0;+ if (v->number.unit == 0) { free(v); return 0; }+ return v;+ }++ union Sass_Value* ADDCALL sass_make_color(double r, double g, double b, double a)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->color.tag = SASS_COLOR;+ v->color.r = r;+ v->color.g = g;+ v->color.b = b;+ v->color.a = a;+ return v;+ }++ union Sass_Value* ADDCALL sass_make_string(const char* val)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->string.tag = SASS_STRING;+ v->string.value = val ? sass_strdup(val) : 0;+ if (v->string.value == 0) { free(v); return 0; }+ return v;+ }++ union Sass_Value* ADDCALL sass_make_list(size_t len, enum Sass_Separator sep)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->list.tag = SASS_LIST;+ v->list.length = len;+ v->list.separator = sep;+ v->list.values = (union Sass_Value**) calloc(len, sizeof(union Sass_Value));+ if (v->list.values == 0) { free(v); return 0; }+ return v;+ }++ union Sass_Value* ADDCALL sass_make_map(size_t len)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->map.tag = SASS_MAP;+ v->map.length = len;+ v->map.pairs = (struct Sass_MapPair*) calloc(len, sizeof(struct Sass_MapPair));+ if (v->map.pairs == 0) { free(v); return 0; }+ return v;+ }++ union Sass_Value* ADDCALL sass_make_null(void)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->null.tag = SASS_NULL;+ return v;+ }++ union Sass_Value* ADDCALL sass_make_error(const char* msg)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->error.tag = SASS_ERROR;+ v->error.message = msg ? sass_strdup(msg) : 0;+ if (v->error.message == 0) { free(v); return 0; }+ return v;+ }++ union Sass_Value* ADDCALL sass_make_warning(const char* msg)+ {+ Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->warning.tag = SASS_WARNING;+ v->warning.message = msg ? sass_strdup(msg) : 0;+ if (v->warning.message == 0) { free(v); return 0; }+ return v;+ }++ // will free all associated sass values+ void ADDCALL sass_delete_value(union Sass_Value* val) {++ size_t i;+ if (val == 0) return;+ switch(val->unknown.tag) {+ case SASS_NULL: {+ } break;+ case SASS_BOOLEAN: {+ } break;+ case SASS_NUMBER: {+ free(val->number.unit);+ } break;+ case SASS_COLOR: {+ } break;+ case SASS_STRING: {+ free(val->string.value);+ } break;+ case SASS_LIST: {+ for (i=0; i<val->list.length; i++) {+ sass_delete_value(val->list.values[i]);+ }+ free(val->list.values);+ } break;+ case SASS_MAP: {+ for (i=0; i<val->map.length; i++) {+ sass_delete_value(val->map.pairs[i].key);+ sass_delete_value(val->map.pairs[i].value);+ }+ free(val->map.pairs);+ } break;+ case SASS_ERROR: {+ free(val->error.message);+ } break;+ case SASS_WARNING: {+ free(val->error.message);+ } break;+ }++ free(val);++ }++ // Make a deep cloned copy of the given sass value+ union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val)+ {++ size_t i;+ if (val == 0) return 0;+ switch(val->unknown.tag) {+ case SASS_NULL: {+ return sass_make_null();+ } break;+ case SASS_BOOLEAN: {+ return sass_make_boolean(val->boolean.value);+ } break;+ case SASS_NUMBER: {+ return sass_make_number(val->number.value, val->number.unit);+ } break;+ case SASS_COLOR: {+ return sass_make_color(val->color.r, val->color.g, val->color.b, val->color.a);+ } break;+ case SASS_STRING: {+ return sass_make_string(val->string.value);+ } break;+ case SASS_LIST: {+ union Sass_Value* list = sass_make_list(val->list.length, val->list.separator);+ for (i = 0; i < list->list.length; i++) {+ list->list.values[i] = sass_clone_value(val->list.values[i]);+ }+ return list;+ } break;+ case SASS_MAP: {+ union Sass_Value* map = sass_make_map(val->map.length);+ for (i = 0; i < val->map.length; i++) {+ map->map.pairs[i].key = sass_clone_value(val->map.pairs[i].key);+ map->map.pairs[i].value = sass_clone_value(val->map.pairs[i].value);+ }+ return map;+ } break;+ case SASS_ERROR: {+ return sass_make_error(val->error.message);+ } break;+ case SASS_WARNING: {+ return sass_make_warning(val->warning.message);+ } break;+ }++ return 0;++ }++}
+ libsass/sass_values.h view
@@ -0,0 +1,124 @@+#ifndef SASS_C_VALUES_H+#define SASS_C_VALUES_H++#include <stddef.h>+#include <stdbool.h>+#include "sass.h"++#ifdef __cplusplus+extern "C" {+#endif+++// Forward declaration+union Sass_Value;++// Type for Sass values+enum Sass_Tag {+ SASS_BOOLEAN,+ SASS_NUMBER,+ SASS_COLOR,+ SASS_STRING,+ SASS_LIST,+ SASS_MAP,+ SASS_NULL,+ SASS_ERROR,+ SASS_WARNING+};++// Tags for denoting Sass list separators+enum Sass_Separator {+ SASS_COMMA,+ SASS_SPACE+};++// Return the sass tag for a generic sass value+// Check is needed before accessing specific values!+ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);++// Check value to be of a specific type+// Can also be used before accessing properties!+ADDAPI bool ADDCALL sass_value_is_null (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_number (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_string (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_boolean (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_color (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_list (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_map (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_error (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_warning (const union Sass_Value* v);++// Getters and setters for Sass_Number+ADDAPI double ADDCALL sass_number_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_number_set_value (union Sass_Value* v, double value);+ADDAPI const char* ADDCALL sass_number_get_unit (const union Sass_Value* v);+ADDAPI void ADDCALL sass_number_set_unit (union Sass_Value* v, char* unit);++// Getters and setters for Sass_String+ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_string_set_value (union Sass_Value* v, char* value);++// Getters and setters for Sass_Boolean+ADDAPI bool ADDCALL sass_boolean_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_boolean_set_value (union Sass_Value* v, bool value);++// Getters and setters for Sass_Color+ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r);+ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_g (union Sass_Value* v, double g);+ADDAPI double ADDCALL sass_color_get_b (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_b (union Sass_Value* v, double b);+ADDAPI double ADDCALL sass_color_get_a (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_a (union Sass_Value* v, double a);++// Getter for the number of items in list+ADDAPI size_t ADDCALL sass_list_get_length (const union Sass_Value* v);+// Getters and setters for Sass_List+ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v);+ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value);+// Getters and setters for Sass_List values+ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value);++// Getter for the number of items in map+ADDAPI size_t ADDCALL sass_map_get_length (const union Sass_Value* v);+// Getters and setters for Sass_Map keys and values+ADDAPI union Sass_Value* ADDCALL sass_map_get_key (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*);+ADDAPI union Sass_Value* ADDCALL sass_map_get_value (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*);++// Getters and setters for Sass_Error+ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);+ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);++// Getters and setters for Sass_Warning+ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v);+ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg);++// Creator functions for all value types+ADDAPI union Sass_Value* ADDCALL sass_make_null (void);+ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);+ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);+ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a);+ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep);+ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len);+ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg);+ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);++// Generic destructor function for all types+// Will release memory of all associated Sass_Values+// Means we will delete recursively for lists and maps+ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);++// Make a deep cloned copy of the given sass value+ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);+++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/sass_version.h view
@@ -0,0 +1,8 @@+#ifndef SASS_VERSION_H+#define SASS_VERSION_H++#ifndef LIBSASS_VERSION+#define LIBSASS_VERSION "[NA]"+#endif++#endif
+ libsass/source_map.cpp view
@@ -0,0 +1,184 @@+#include <string>+#include <sstream>+#include <iostream>+#include <cstddef>+#include <iomanip>++#include "ast.hpp"+#include "json.hpp"+#include "context.hpp"+#include "position.hpp"+#include "source_map.hpp"++namespace Sass {+ using std::ptrdiff_t;+ SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { }+ SourceMap::SourceMap(const string& file) : current_position(0, 0, 0), file(file) { }++ string SourceMap::generate_source_map(Context &ctx) {++ const bool include_sources = ctx.source_map_contents;+ const vector<string> includes = ctx.include_links;+ const vector<const char*> sources = ctx.sources;++ JsonNode* json_srcmap = json_mkobject();++ json_append_member(json_srcmap, "version", json_mknumber(3));++ // pass-through sourceRoot option+ if (!ctx.source_map_root.empty()) {+ JsonNode* root = json_mkstring(ctx.source_map_root.c_str());+ json_append_member(json_srcmap, "sourceRoot", root);+ }++ const char *include = file.c_str();+ JsonNode *json_include = json_mkstring(include);+ json_append_member(json_srcmap, "file", json_include);++ JsonNode *json_includes = json_mkarray();+ for (size_t i = 0; i < source_index.size(); ++i) {+ const char *include = includes[source_index[i]].c_str();+ JsonNode *json_include = json_mkstring(include);+ json_append_element(json_includes, json_include);+ }+ json_append_member(json_srcmap, "sources", json_includes);++ JsonNode *json_contents = json_mkarray();+ if (include_sources) {+ for (size_t i = 0; i < source_index.size(); ++i) {+ const char *content = sources[source_index[i]];+ JsonNode *json_content = json_mkstring(content);+ json_append_element(json_contents, json_content);+ }+ }+ json_append_member(json_srcmap, "sourcesContent", json_contents);++ string mappings = serialize_mappings();+ JsonNode *json_mappings = json_mkstring(mappings.c_str());+ json_append_member(json_srcmap, "mappings", json_mappings);++ JsonNode *json_names = json_mkarray();+ // so far we have no implementation for names+ // no problem as we do not alter any identifiers+ json_append_member(json_srcmap, "names", json_names);++ char *str = json_stringify(json_srcmap, "\t");+ string result = string(str);+ free(str);+ json_delete(json_srcmap);+ return result;+ }++ string SourceMap::serialize_mappings() {+ string result = "";++ size_t previous_generated_line = 0;+ size_t previous_generated_column = 0;+ size_t previous_original_line = 0;+ size_t previous_original_column = 0;+ size_t previous_original_file = 0;+ for (size_t i = 0; i < mappings.size(); ++i) {+ const size_t generated_line = mappings[i].generated_position.line;+ const size_t generated_column = mappings[i].generated_position.column;+ const size_t original_line = mappings[i].original_position.line;+ const size_t original_column = mappings[i].original_position.column;+ const size_t original_file = mappings[i].original_position.file;++ if (generated_line != previous_generated_line) {+ previous_generated_column = 0;+ if (generated_line > previous_generated_line) {+ result += std::string(generated_line - previous_generated_line, ';');+ previous_generated_line = generated_line;+ }+ }+ else if (i > 0) {+ result += ",";+ }++ // generated column+ result += base64vlq.encode(static_cast<int>(generated_column) - static_cast<int>(previous_generated_column));+ previous_generated_column = generated_column;+ // file+ result += base64vlq.encode(static_cast<int>(original_file) - static_cast<int>(previous_original_file));+ previous_original_file = original_file;+ // source line+ result += base64vlq.encode(static_cast<int>(original_line) - static_cast<int>(previous_original_line));+ previous_original_line = original_line;+ // source column+ result += base64vlq.encode(static_cast<int>(original_column) - static_cast<int>(previous_original_column));+ previous_original_column = original_column;+ }++ return result;+ }++ void SourceMap::prepend(const OutputBuffer& out)+ {+ Offset size(out.smap.current_position);+ for (Mapping mapping : out.smap.mappings) {+ if (mapping.generated_position.line > size.line) {+ throw(runtime_error("prepend sourcemap has illegal line"));+ }+ if (mapping.generated_position.line == size.line) {+ if (mapping.generated_position.column > size.column) {+ throw(runtime_error("prepend sourcemap has illegal column"));+ }+ }+ }+ // will adjust the offset+ prepend(Offset(out.buffer));+ // now add the new mappings+ VECTOR_UNSHIFT(mappings, out.smap.mappings);+ }++ void SourceMap::append(const OutputBuffer& out)+ {+ append(Offset(out.buffer));+ }++ void SourceMap::prepend(const Offset& offset)+ {+ if (offset.line != 0 || offset.column != 0) {+ for (Mapping& mapping : mappings) {+ // move stuff on the first old line+ if (mapping.generated_position.line == 0) {+ mapping.generated_position.column += offset.column;+ }+ // make place for the new lines+ mapping.generated_position.line += offset.line;+ }+ }+ if (current_position.line == 0) {+ current_position.column += offset.column;+ }+ current_position.line += offset.line;+ }++ void SourceMap::append(const Offset& offset)+ {+ current_position += offset;+ }++ void SourceMap::add_open_mapping(AST_Node* node)+ {+ mappings.push_back(Mapping(node->pstate(), current_position));+ }++ void SourceMap::add_close_mapping(AST_Node* node)+ {+ mappings.push_back(Mapping(node->pstate() + node->pstate().offset, current_position));+ }++ ParserState SourceMap::remap(const ParserState& pstate) {+ for (size_t i = 0; i < mappings.size(); ++i) {+ if (+ mappings[i].generated_position.file == pstate.file &&+ mappings[i].generated_position.line == pstate.line &&+ mappings[i].generated_position.column == pstate.column+ ) return ParserState(pstate.path, pstate.src, mappings[i].original_position, pstate.offset);+ }+ return ParserState(pstate.path, pstate.src, Position(-1, -1, -1), Offset(0, 0));++ }++}
+ libsass/source_map.hpp view
@@ -0,0 +1,65 @@+#ifndef SASS_SOURCE_MAP_H+#define SASS_SOURCE_MAP_H++#include <vector>++#include "ast_fwd_decl.hpp"+#include "base64vlq.hpp"+#include "position.hpp"+#include "mapping.hpp"++#define VECTOR_PUSH(vec, ins) vec.insert(vec.end(), ins.begin(), ins.end())+#define VECTOR_UNSHIFT(vec, ins) vec.insert(vec.begin(), ins.begin(), ins.end())++namespace Sass {+ using std::vector;++ class Context;+ class OutputBuffer;++ class SourceMap {++ public:+ vector<size_t> source_index;+ SourceMap();+ SourceMap(const string& file);++ void setFile(const string& str) {+ file = str;+ }+ void append(const Offset& offset);+ 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);++ string generate_source_map(Context &ctx);+ ParserState remap(const ParserState& pstate);++ private:++ string serialize_mappings();++ vector<Mapping> mappings;+ Position current_position;+public:+ string file;+private:+ Base64VLQ base64vlq;+ };++ class OutputBuffer {+ public:+ OutputBuffer(void)+ : buffer(""),+ smap()+ { }+ public:+ string buffer;+ SourceMap smap;+ };++}++#endif
+ libsass/subset_map.hpp view
@@ -0,0 +1,147 @@+#ifndef SASS_SUBSET_MAP_H+#define SASS_SUBSET_MAP_H++#include <map>+#include <set>+#include <vector>+#include <algorithm>+#include <iterator>+#include <iostream>+#include <sstream>++// using namespace std;++// template<typename T>+// string vector_to_string(vector<T> v)+// {+// stringstream buffer;+// buffer << "[";++// if (!v.empty())+// { buffer << v[0]; }+// else+// { buffer << "]"; }++// if (v.size() == 1)+// { buffer << "]"; }+// else+// {+// for (size_t i = 1, S = v.size(); i < S; ++i) buffer << ", " << v[i];+// buffer << "]";+// }++// return buffer.str();+// }++// template<typename T>+// string set_to_string(set<T> v)+// {+// stringstream buffer;+// buffer << "[";+// typename set<T>::iterator i = v.begin();+// if (!v.empty())+// { buffer << *i; }+// else+// { buffer << "]"; }++// if (v.size() == 1)+// { buffer << "]"; }+// else+// {+// for (++i; i != v.end(); ++i) buffer << ", " << *i;+// buffer << "]";+// }++// return buffer.str();+// }++namespace Sass {+ using namespace std;++ template<typename F, typename S, typename T>+ struct triple {+ F first;+ S second;+ T third;++ triple(const F& f, const S& s, const T& t) : first(f), second(s), third(t) { }+ };++ template<typename F, typename S, typename T>+ triple<F, S, T> make_triple(const F& f, const S& s, const T& t)+ { return triple<F, S, T>(f, s, t); }++ template<typename K, typename V>+ class Subset_Map {+ private:+ vector<V> values_;+ map<K, vector<triple<vector<K>, set<K>, size_t> > > hash_;+ public:+ void put(const vector<K>& s, const V& value);+ vector<pair<V, vector<K> > > get_kv(const vector<K>& s);+ vector<V> get_v(const vector<K>& s);+ bool empty() { return values_.empty(); }+ void clear() { values_.clear(); hash_.clear(); }+ const vector<V> values(void) { return values_; }+ };++ template<typename K, typename V>+ void Subset_Map<K, V>::put(const vector<K>& s, const V& value)+ {+ if (s.empty()) throw "internal error: subset map keys may not be empty";+ size_t index = values_.size();+ values_.push_back(value);+ set<K> ss;+ for (size_t i = 0, S = s.size(); i < S; ++i)+ { ss.insert(s[i]); }+ for (size_t i = 0, S = s.size(); i < S; ++i)+ {+ hash_[s[i]];+ hash_[s[i]].push_back(make_triple(s, ss, index));+ }+ }++ template<typename K, typename V>+ vector<pair<V, vector<K> > > Subset_Map<K, V>::get_kv(const vector<K>& s)+ {+ vector<K> sorted = s;+ sort(sorted.begin(), sorted.end());+ vector<pair<size_t, vector<K> > > indices;+ for (size_t i = 0, S = s.size(); i < S; ++i) {+ if (!hash_.count(s[i])) {+ continue;+ }+ vector<triple<vector<K>, set<K>, size_t> > subsets = hash_[s[i]];+ // cerr << "length of subsets: " << subsets.size() << endl;+ for (size_t j = 0, T = subsets.size(); j < T; ++j) {+ if (!includes(sorted.begin(), sorted.end(), subsets[j].second.begin(), subsets[j].second.end())) {+ // cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << endl;+ continue;+ }+ indices.push_back(make_pair(subsets[j].third, subsets[j].first));+ // cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << endl;+ }+ }+ sort(indices.begin(), indices.end());+ typename vector<pair<size_t, vector<K> > >::iterator indices_end = unique(indices.begin(), indices.end());+ indices.resize(distance(indices.begin(), indices_end));++ vector<pair<V, vector<K> > > results;+ for (size_t i = 0, S = indices.size(); i < S; ++i) {+ results.push_back(make_pair(values_[indices[i].first], indices[i].second));+ }+ return results;+ }++ template<typename K, typename V>+ vector<V> Subset_Map<K, V>::get_v(const vector<K>& s)+ {+ vector<pair<V, vector<K> > > kvs = get_kv(s);+ vector<V> results;+ for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first);+ return results;+ }++}++#endif
+ libsass/to_c.cpp view
@@ -0,0 +1,61 @@+#include "to_c.hpp"+#include "ast.hpp"++#include "sass_values.h"++namespace Sass {+ using namespace std;++ Sass_Value* To_C::fallback_impl(AST_Node* n)+ { return sass_make_null(); }++ Sass_Value* To_C::operator()(Boolean* b)+ { return sass_make_boolean(b->value()); }++ Sass_Value* To_C::operator()(Number* n)+ { return sass_make_number(n->value(), n->unit().c_str()); }++ Sass_Value* To_C::operator()(Color* c)+ { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }++ Sass_Value* To_C::operator()(String_Constant* s)+ { return sass_make_string(s->value().c_str()); }++ Sass_Value* To_C::operator()(List* l)+ {+ Sass_Value* v = sass_make_list(l->length(), l->separator() == List::COMMA ? SASS_COMMA : SASS_SPACE);+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ sass_list_set_value(v, i, (*l)[i]->perform(this));+ }+ return v;+ }++ Sass_Value* To_C::operator()(Map* m)+ {+ Sass_Value* v = sass_make_map(m->length());+ int i = 0;+ for (auto key : m->keys()) {+ sass_map_set_key(v, i, key->perform(this));+ sass_map_set_value(v, i, m->at(key)->perform(this));+ i++;+ }+ return v;+ }++ Sass_Value* To_C::operator()(Arguments* a)+ {+ Sass_Value* v = sass_make_list(a->length(), SASS_COMMA);+ for (size_t i = 0, L = a->length(); i < L; ++i) {+ sass_list_set_value(v, i, (*a)[i]->perform(this));+ }+ return v;+ }++ Sass_Value* To_C::operator()(Argument* a)+ { return a->value()->perform(this); }++ // not strictly necessary because of the fallback+ Sass_Value* To_C::operator()(Null* n)+ { return sass_make_null(); }++};
+ libsass/to_c.hpp view
@@ -0,0 +1,44 @@+#ifndef SASS_TO_C_H+#define SASS_TO_C_H++#include "operation.hpp"+#include "sass_values.h"++namespace Sass {+ using namespace std;++ class AST_Node;+ class Boolean;+ class Number;+ class String_Constant;+ class List;+ class Map;+ class Null;++ class To_C : public Operation_CRTP<Sass_Value*, To_C> {++ Sass_Value* fallback_impl(AST_Node* n);++ public:++ To_C() { }+ virtual ~To_C() { }+ using Operation<Sass_Value*>::operator();++ Sass_Value* operator()(Boolean*);+ Sass_Value* operator()(Number*);+ Sass_Value* operator()(Color*);+ Sass_Value* operator()(String_Constant*);+ Sass_Value* operator()(List*);+ Sass_Value* operator()(Map*);+ Sass_Value* operator()(Null*);+ Sass_Value* operator()(Arguments*);+ Sass_Value* operator()(Argument*);++ template <typename U>+ Sass_Value* fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/to_string.cpp view
@@ -0,0 +1,34 @@+#include <cmath>+#include <sstream>+#include <iomanip>+#include <iostream>++#include "ast.hpp"+#include "inspect.hpp"+#include "context.hpp"+#include "to_string.hpp"++namespace Sass {+ using namespace std;++ To_String::To_String(Context* ctx)+ : ctx(ctx) { }+ To_String::~To_String() { }++ inline string To_String::fallback_impl(AST_Node* n)+ {+ Emitter emitter(ctx);+ Inspect i(emitter);+ i.in_declaration_list = true;+ n->perform(&i);+ return i.get_buffer();+ }++ inline string To_String::operator()(String_Constant* s)+ {+ return s->value();+ }++ inline string To_String::operator()(Null* n)+ { return ""; }+}
+ libsass/to_string.hpp view
@@ -0,0 +1,34 @@+#ifndef SASS_TO_STRING_H+#define SASS_TO_STRING_H++#include <string>++#include "operation.hpp"++namespace Sass {+ using namespace std;++ class Context;+ class Null;++ class To_String : public Operation_CRTP<string, To_String> {+ // import all the class-specific methods and override as desired+ using Operation<string>::operator();+ // override this to define a catch-all+ string fallback_impl(AST_Node* n);++ Context* ctx;++ public:+ To_String(Context* ctx = 0);+ virtual ~To_String();++ string operator()(Null* n);+ string operator()(String_Constant*);++ template <typename U>+ string fallback(U n) { return fallback_impl(n); }+ };+}++#endif
+ libsass/units.cpp view
@@ -0,0 +1,55 @@+#include "units.hpp"++#define PI 3.14159265358979323846++namespace Sass {++ double conversion_factors[10][10] = {+ /* in cm pc mm pt px deg grad rad turn */+ /* in */ { 1, 2.54, 6, 25.4, 72, 96, 1, 1, 1, 1 },+ /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54, 1, 1, 1, 1 },+ /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0, 1, 1, 1, 1 },+ /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4, 1, 1, 1, 1 },+ /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0, 1, 1, 1, 1 },+ /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1, 1, 1, 1, 1 },+ /* deg */ { 1 , 1 , 1 , 1 , 1 , 1, 1, 40.0/36.0, PI/180.0, 1.0/360.0 },+ /* grad */ { 1 , 1 , 1 , 1 , 1 , 1, 36.0/40.0, 1, PI/200.0, 1.0/400.0 },+ /* rad */ { 1 , 1 , 1 , 1 , 1 , 1, 180.0/PI, 200.0/PI, 1, PI/2.0 },+ /* turn */ { 1 , 1 , 1 , 1 , 1 , 1, 360.0/1.0, 400.0/1.0, 2.0*PI, 1 }+ };++ Unit string_to_unit(const string& s)+ {+ if (s == "in") return IN;+ else if (s == "cm") return CM;+ else if (s == "pc") return PC;+ else if (s == "mm") return MM;+ else if (s == "pt") return PT;+ else if (s == "px") return PX;+ else if (s == "deg") return DEG;+ else if (s == "grad") return GRAD;+ else if (s == "rad") return RAD;+ else if (s == "turn") return TURN;+ else return INCOMMENSURABLE;+ }++ double conversion_factor(const string& s1, const string& s2)+ {+ Unit u1 = string_to_unit(s1);+ Unit u2 = string_to_unit(s2);+ double factor;+ if (u1 == INCOMMENSURABLE || u2 == INCOMMENSURABLE)+ factor = 0;+ else+ factor = conversion_factors[u1][u2];+ return factor;+ }++ /* not used anymore - remove?+ double convert(double n, const string& from, const string& to)+ {+ double factor = conversion_factor(from, to);+ return factor ? factor * n : n;+ } */++}
+ libsass/units.hpp view
@@ -0,0 +1,15 @@+#ifndef SASS_UNITS_H+#define SASS_UNITS_H++#include <string>++namespace Sass {+ using namespace std;+ enum Unit { IN, CM, PC, MM, PT, PX, DEG, GRAD, RAD, TURN, INCOMMENSURABLE };+ extern double conversion_factors[10][10];+ Unit string_to_unit(const string&);+ double conversion_factor(const string&, const string&);+ // double convert(double, const string&, const string&);+}++#endif
+ libsass/utf8.h view
@@ -0,0 +1,34 @@+// Copyright 2006 Nemanja Trifunovic++/*+Permission is hereby granted, free of charge, to any person or organization+obtaining a copy of the software and accompanying documentation covered by+this license (the "Software") to use, reproduce, display, distribute,+execute, and transmit the Software, and to prepare derivative works of the+Software, and to permit third-parties to whom the Software is furnished to+do so, all subject to the following:++The copyright notices in the Software and this entire statement, including+the above license grant, this restriction and the following disclaimer,+must be included in all copies of the Software, in whole or in part, and+all derivative works of the Software, unless such copies or derivative+works are solely in the form of machine-executable object code generated by+a source language processor.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER+DEALINGS IN THE SOFTWARE.+*/+++#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731+#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731++#include "utf8/checked.h"+#include "utf8/unchecked.h"++#endif // header guard
+ libsass/utf8/checked.h view
@@ -0,0 +1,327 @@+// Copyright 2006 Nemanja Trifunovic++/*+Permission is hereby granted, free of charge, to any person or organization+obtaining a copy of the software and accompanying documentation covered by+this license (the "Software") to use, reproduce, display, distribute,+execute, and transmit the Software, and to prepare derivative works of the+Software, and to permit third-parties to whom the Software is furnished to+do so, all subject to the following:++The copyright notices in the Software and this entire statement, including+the above license grant, this restriction and the following disclaimer,+must be included in all copies of the Software, in whole or in part, and+all derivative works of the Software, unless such copies or derivative+works are solely in the form of machine-executable object code generated by+a source language processor.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER+DEALINGS IN THE SOFTWARE.+*/+++#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731+#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731++#include "core.h"+#include <stdexcept>++namespace utf8+{+ // Base for the exceptions that may be thrown from the library+ class exception : public ::std::exception {+ };++ // Exceptions that may be thrown from the library functions.+ class invalid_code_point : public exception {+ uint32_t cp;+ public:+ invalid_code_point(uint32_t cp) : cp(cp) {}+ virtual const char* what() const throw() { return "Invalid code point"; }+ uint32_t code_point() const {return cp;}+ };++ class invalid_utf8 : public exception {+ uint8_t u8;+ public:+ invalid_utf8 (uint8_t u) : u8(u) {}+ virtual const char* what() const throw() { return "Invalid UTF-8"; }+ uint8_t utf8_octet() const {return u8;}+ };++ class invalid_utf16 : public exception {+ uint16_t u16;+ public:+ invalid_utf16 (uint16_t u) : u16(u) {}+ virtual const char* what() const throw() { return "Invalid UTF-16"; }+ uint16_t utf16_word() const {return u16;}+ };++ class not_enough_room : public exception {+ public:+ virtual const char* what() const throw() { return "Not enough space"; }+ };++ /// The library API - functions intended to be called by the users++ template <typename octet_iterator>+ octet_iterator append(uint32_t cp, octet_iterator result)+ {+ if (!utf8::internal::is_code_point_valid(cp))+ throw invalid_code_point(cp);++ if (cp < 0x80) // one octet+ *(result++) = static_cast<uint8_t>(cp);+ else if (cp < 0x800) { // two octets+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ else if (cp < 0x10000) { // three octets+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ else { // four octets+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);+ *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ return result;+ }++ template <typename octet_iterator, typename output_iterator>+ output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)+ {+ while (start != end) {+ octet_iterator sequence_start = start;+ internal::utf_error err_code = utf8::internal::validate_next(start, end);+ switch (err_code) {+ case internal::UTF8_OK :+ for (octet_iterator it = sequence_start; it != start; ++it)+ *out++ = *it;+ break;+ case internal::NOT_ENOUGH_ROOM:+ throw not_enough_room();+ case internal::INVALID_LEAD:+ out = utf8::append (replacement, out);+ ++start;+ break;+ case internal::INCOMPLETE_SEQUENCE:+ case internal::OVERLONG_SEQUENCE:+ case internal::INVALID_CODE_POINT:+ out = utf8::append (replacement, out);+ ++start;+ // just one replacement mark for the sequence+ while (start != end && utf8::internal::is_trail(*start))+ ++start;+ break;+ }+ }+ return out;+ }++ template <typename octet_iterator, typename output_iterator>+ inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)+ {+ static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);+ return utf8::replace_invalid(start, end, out, replacement_marker);+ }++ template <typename octet_iterator>+ uint32_t next(octet_iterator& it, octet_iterator end)+ {+ uint32_t cp = 0;+ internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);+ switch (err_code) {+ case internal::UTF8_OK :+ break;+ case internal::NOT_ENOUGH_ROOM :+ throw not_enough_room();+ case internal::INVALID_LEAD :+ case internal::INCOMPLETE_SEQUENCE :+ case internal::OVERLONG_SEQUENCE :+ throw invalid_utf8(*it);+ case internal::INVALID_CODE_POINT :+ throw invalid_code_point(cp);+ }+ return cp;+ }++ template <typename octet_iterator>+ uint32_t peek_next(octet_iterator it, octet_iterator end)+ {+ return utf8::next(it, end);+ }++ template <typename octet_iterator>+ uint32_t prior(octet_iterator& it, octet_iterator start)+ {+ // can't do much if it == start+ if (it == start)+ throw not_enough_room();++ octet_iterator end = it;+ // Go back until we hit either a lead octet or start+ while (utf8::internal::is_trail(*(--it)))+ if (it == start)+ throw invalid_utf8(*it); // error - no lead byte in the sequence+ return utf8::peek_next(it, end);+ }++ /// Deprecated in versions that include "prior"+ template <typename octet_iterator>+ uint32_t previous(octet_iterator& it, octet_iterator pass_start)+ {+ octet_iterator end = it;+ while (utf8::internal::is_trail(*(--it)))+ if (it == pass_start)+ throw invalid_utf8(*it); // error - no lead byte in the sequence+ octet_iterator temp = it;+ return utf8::next(temp, end);+ }++ template <typename octet_iterator, typename distance_type>+ void advance (octet_iterator& it, distance_type n, octet_iterator end)+ {+ for (distance_type i = 0; i < n; ++i)+ utf8::next(it, end);+ }++ template <typename octet_iterator>+ typename std::iterator_traits<octet_iterator>::difference_type+ distance (octet_iterator first, octet_iterator last)+ {+ typename std::iterator_traits<octet_iterator>::difference_type dist;+ for (dist = 0; first < last; ++dist)+ utf8::next(first, last);+ return dist;+ }++ template <typename u16bit_iterator, typename octet_iterator>+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)+ {+ while (start != end) {+ uint32_t cp = utf8::internal::mask16(*start++);+ // Take care of surrogate pairs first+ if (utf8::internal::is_lead_surrogate(cp)) {+ if (start != end) {+ uint32_t trail_surrogate = utf8::internal::mask16(*start++);+ if (utf8::internal::is_trail_surrogate(trail_surrogate))+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;+ else+ throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));+ }+ else+ throw invalid_utf16(static_cast<uint16_t>(cp));++ }+ // Lone trail surrogate+ else if (utf8::internal::is_trail_surrogate(cp))+ throw invalid_utf16(static_cast<uint16_t>(cp));++ result = utf8::append(cp, result);+ }+ return result;+ }++ template <typename u16bit_iterator, typename octet_iterator>+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)+ {+ while (start != end) {+ uint32_t cp = utf8::next(start, end);+ if (cp > 0xffff) { //make a surrogate pair+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);+ }+ else+ *result++ = static_cast<uint16_t>(cp);+ }+ return result;+ }++ template <typename octet_iterator, typename u32bit_iterator>+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)+ {+ while (start != end)+ result = utf8::append(*(start++), result);++ return result;+ }++ template <typename octet_iterator, typename u32bit_iterator>+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)+ {+ while (start != end)+ (*result++) = utf8::next(start, end);++ return result;+ }++ // The iterator class+ template <typename octet_iterator>+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {+ octet_iterator it;+ octet_iterator range_start;+ octet_iterator range_end;+ public:+ iterator () {}+ explicit iterator (const octet_iterator& octet_it,+ const octet_iterator& range_start,+ const octet_iterator& range_end) :+ it(octet_it), range_start(range_start), range_end(range_end)+ {+ if (it < range_start || it > range_end)+ throw std::out_of_range("Invalid utf-8 iterator position");+ }+ // the default "big three" are OK+ octet_iterator base () const { return it; }+ uint32_t operator * () const+ {+ octet_iterator temp = it;+ return utf8::next(temp, range_end);+ }+ bool operator == (const iterator& rhs) const+ {+ if (range_start != rhs.range_start || range_end != rhs.range_end)+ throw std::logic_error("Comparing utf-8 iterators defined with different ranges");+ return (it == rhs.it);+ }+ bool operator != (const iterator& rhs) const+ {+ return !(operator == (rhs));+ }+ iterator& operator ++ ()+ {+ utf8::next(it, range_end);+ return *this;+ }+ iterator operator ++ (int)+ {+ iterator temp = *this;+ utf8::next(it, range_end);+ return temp;+ }+ iterator& operator -- ()+ {+ utf8::prior(it, range_start);+ return *this;+ }+ iterator operator -- (int)+ {+ iterator temp = *this;+ utf8::prior(it, range_start);+ return temp;+ }+ }; // class iterator++} // namespace utf8++#endif //header guard++
+ libsass/utf8/core.h view
@@ -0,0 +1,329 @@+// Copyright 2006 Nemanja Trifunovic++/*+Permission is hereby granted, free of charge, to any person or organization+obtaining a copy of the software and accompanying documentation covered by+this license (the "Software") to use, reproduce, display, distribute,+execute, and transmit the Software, and to prepare derivative works of the+Software, and to permit third-parties to whom the Software is furnished to+do so, all subject to the following:++The copyright notices in the Software and this entire statement, including+the above license grant, this restriction and the following disclaimer,+must be included in all copies of the Software, in whole or in part, and+all derivative works of the Software, unless such copies or derivative+works are solely in the form of machine-executable object code generated by+a source language processor.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER+DEALINGS IN THE SOFTWARE.+*/+++#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731+#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731++#include <iterator>++namespace utf8+{+ // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers+ // You may need to change them to match your system.+ // These typedefs have the same names as ones from cstdint, or boost/cstdint+ typedef unsigned char uint8_t;+ typedef unsigned short uint16_t;+ typedef unsigned int uint32_t;++// Helper code - not intended to be directly called by the library users. May be changed at any time+namespace internal+{+ // Unicode constants+ // Leading (high) surrogates: 0xd800 - 0xdbff+ // Trailing (low) surrogates: 0xdc00 - 0xdfff+ const uint16_t LEAD_SURROGATE_MIN = 0xd800u;+ const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;+ const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;+ const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;+ const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);+ const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;++ // Maximum valid value for a Unicode code point+ const uint32_t CODE_POINT_MAX = 0x0010ffffu;++ template<typename octet_type>+ inline uint8_t mask8(octet_type oc)+ {+ return static_cast<uint8_t>(0xff & oc);+ }+ template<typename u16_type>+ inline uint16_t mask16(u16_type oc)+ {+ return static_cast<uint16_t>(0xffff & oc);+ }+ template<typename octet_type>+ inline bool is_trail(octet_type oc)+ {+ return ((utf8::internal::mask8(oc) >> 6) == 0x2);+ }++ template <typename u16>+ inline bool is_lead_surrogate(u16 cp)+ {+ return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);+ }++ template <typename u16>+ inline bool is_trail_surrogate(u16 cp)+ {+ return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);+ }++ template <typename u16>+ inline bool is_surrogate(u16 cp)+ {+ return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);+ }++ template <typename u32>+ inline bool is_code_point_valid(u32 cp)+ {+ return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));+ }++ template <typename octet_iterator>+ inline typename std::iterator_traits<octet_iterator>::difference_type+ sequence_length(octet_iterator lead_it)+ {+ uint8_t lead = utf8::internal::mask8(*lead_it);+ if (lead < 0x80)+ return 1;+ else if ((lead >> 5) == 0x6)+ return 2;+ else if ((lead >> 4) == 0xe)+ return 3;+ else if ((lead >> 3) == 0x1e)+ return 4;+ else+ return 0;+ }++ template <typename octet_difference_type>+ inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)+ {+ if (cp < 0x80) {+ if (length != 1) + return true;+ }+ else if (cp < 0x800) {+ if (length != 2) + return true;+ }+ else if (cp < 0x10000) {+ if (length != 3) + return true;+ }++ return false;+ }++ enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};++ /// Helper for get_sequence_x+ template <typename octet_iterator>+ utf_error increase_safely(octet_iterator& it, octet_iterator end)+ {+ if (++it == end)+ return NOT_ENOUGH_ROOM;++ if (!utf8::internal::is_trail(*it))+ return INCOMPLETE_SEQUENCE;+ + return UTF8_OK;+ }++ #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} ++ /// get_sequence_x functions decode utf-8 sequences of the length x+ template <typename octet_iterator>+ utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)+ {+ if (it == end)+ return NOT_ENOUGH_ROOM;++ code_point = utf8::internal::mask8(*it);++ return UTF8_OK;+ }++ template <typename octet_iterator>+ utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)+ {+ if (it == end) + return NOT_ENOUGH_ROOM;+ + code_point = utf8::internal::mask8(*it);++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);++ return UTF8_OK;+ }++ template <typename octet_iterator>+ utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)+ {+ if (it == end)+ return NOT_ENOUGH_ROOM;+ + code_point = utf8::internal::mask8(*it);++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point += (*it) & 0x3f;++ return UTF8_OK;+ }++ template <typename octet_iterator>+ utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)+ {+ if (it == end)+ return NOT_ENOUGH_ROOM;++ code_point = utf8::internal::mask8(*it);++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;++ UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)++ code_point += (*it) & 0x3f;++ return UTF8_OK;+ }++ #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR++ template <typename octet_iterator>+ utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)+ {+ // Save the original value of it so we can go back in case of failure+ // Of course, it does not make much sense with i.e. stream iterators+ octet_iterator original_it = it;++ uint32_t cp = 0;+ // Determine the sequence length based on the lead octet+ typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;+ const octet_difference_type length = utf8::internal::sequence_length(it);++ // Get trail octets and calculate the code point+ utf_error err = UTF8_OK;+ switch (length) {+ case 0: + return INVALID_LEAD;+ case 1:+ err = utf8::internal::get_sequence_1(it, end, cp);+ break;+ case 2:+ err = utf8::internal::get_sequence_2(it, end, cp);+ break;+ case 3:+ err = utf8::internal::get_sequence_3(it, end, cp);+ break;+ case 4:+ err = utf8::internal::get_sequence_4(it, end, cp);+ break;+ }++ if (err == UTF8_OK) {+ // Decoding succeeded. Now, security checks...+ if (utf8::internal::is_code_point_valid(cp)) {+ if (!utf8::internal::is_overlong_sequence(cp, length)){+ // Passed! Return here.+ code_point = cp;+ ++it;+ return UTF8_OK;+ }+ else+ err = OVERLONG_SEQUENCE;+ }+ else + err = INVALID_CODE_POINT;+ }++ // Failure branch - restore the original value of the iterator+ it = original_it;+ return err;+ }++ template <typename octet_iterator>+ inline utf_error validate_next(octet_iterator& it, octet_iterator end) {+ uint32_t ignored;+ return utf8::internal::validate_next(it, end, ignored);+ }++} // namespace internal++ /// The library API - functions intended to be called by the users++ // Byte order mark+ const uint8_t bom[] = {0xef, 0xbb, 0xbf};++ template <typename octet_iterator>+ octet_iterator find_invalid(octet_iterator start, octet_iterator end)+ {+ octet_iterator result = start;+ while (result != end) {+ utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);+ if (err_code != internal::UTF8_OK)+ return result;+ }+ return result;+ }++ template <typename octet_iterator>+ inline bool is_valid(octet_iterator start, octet_iterator end)+ {+ return (utf8::find_invalid(start, end) == end);+ }++ template <typename octet_iterator>+ inline bool starts_with_bom (octet_iterator it, octet_iterator end)+ {+ return (+ ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&+ ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&+ ((it != end) && (utf8::internal::mask8(*it)) == bom[2])+ );+ }+ + //Deprecated in release 2.3 + template <typename octet_iterator>+ inline bool is_bom (octet_iterator it)+ {+ return (+ (utf8::internal::mask8(*it++)) == bom[0] &&+ (utf8::internal::mask8(*it++)) == bom[1] &&+ (utf8::internal::mask8(*it)) == bom[2]+ );+ }+} // namespace utf8++#endif // header guard++
+ libsass/utf8/unchecked.h view
@@ -0,0 +1,228 @@+// Copyright 2006 Nemanja Trifunovic++/*+Permission is hereby granted, free of charge, to any person or organization+obtaining a copy of the software and accompanying documentation covered by+this license (the "Software") to use, reproduce, display, distribute,+execute, and transmit the Software, and to prepare derivative works of the+Software, and to permit third-parties to whom the Software is furnished to+do so, all subject to the following:++The copyright notices in the Software and this entire statement, including+the above license grant, this restriction and the following disclaimer,+must be included in all copies of the Software, in whole or in part, and+all derivative works of the Software, unless such copies or derivative+works are solely in the form of machine-executable object code generated by+a source language processor.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER+DEALINGS IN THE SOFTWARE.+*/+++#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731+#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731++#include "core.h"++namespace utf8+{+ namespace unchecked + {+ template <typename octet_iterator>+ octet_iterator append(uint32_t cp, octet_iterator result)+ {+ if (cp < 0x80) // one octet+ *(result++) = static_cast<uint8_t>(cp); + else if (cp < 0x800) { // two octets+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ else if (cp < 0x10000) { // three octets+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ else { // four octets+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);+ *(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f)| 0x80);+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);+ }+ return result;+ }++ template <typename octet_iterator>+ uint32_t next(octet_iterator& it)+ {+ uint32_t cp = utf8::internal::mask8(*it);+ typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);+ switch (length) {+ case 1:+ break;+ case 2:+ it++;+ cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);+ break;+ case 3:+ ++it; + cp = ((cp << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);+ ++it;+ cp += (*it) & 0x3f;+ break;+ case 4:+ ++it;+ cp = ((cp << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + ++it;+ cp += (utf8::internal::mask8(*it) << 6) & 0xfff;+ ++it;+ cp += (*it) & 0x3f; + break;+ }+ ++it;+ return cp; + }++ template <typename octet_iterator>+ uint32_t peek_next(octet_iterator it)+ {+ return utf8::unchecked::next(it); + }++ template <typename octet_iterator>+ uint32_t prior(octet_iterator& it)+ {+ while (utf8::internal::is_trail(*(--it))) ;+ octet_iterator temp = it;+ return utf8::unchecked::next(temp);+ }++ // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)+ template <typename octet_iterator>+ inline uint32_t previous(octet_iterator& it)+ {+ return utf8::unchecked::prior(it);+ }++ template <typename octet_iterator, typename distance_type>+ void advance (octet_iterator& it, distance_type n)+ {+ for (distance_type i = 0; i < n; ++i)+ utf8::unchecked::next(it);+ }++ template <typename octet_iterator>+ typename std::iterator_traits<octet_iterator>::difference_type+ distance (octet_iterator first, octet_iterator last)+ {+ typename std::iterator_traits<octet_iterator>::difference_type dist;+ for (dist = 0; first < last; ++dist) + utf8::unchecked::next(first);+ return dist;+ }++ template <typename u16bit_iterator, typename octet_iterator>+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)+ { + while (start != end) {+ uint32_t cp = utf8::internal::mask16(*start++);+ // Take care of surrogate pairs first+ if (utf8::internal::is_lead_surrogate(cp)) {+ uint32_t trail_surrogate = utf8::internal::mask16(*start++);+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;+ }+ result = utf8::unchecked::append(cp, result);+ }+ return result; + }++ template <typename u16bit_iterator, typename octet_iterator>+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)+ {+ while (start < end) {+ uint32_t cp = utf8::unchecked::next(start);+ if (cp > 0xffff) { //make a surrogate pair+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);+ }+ else+ *result++ = static_cast<uint16_t>(cp);+ }+ return result;+ }++ template <typename octet_iterator, typename u32bit_iterator>+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)+ {+ while (start != end)+ result = utf8::unchecked::append(*(start++), result);++ return result;+ }++ template <typename octet_iterator, typename u32bit_iterator>+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)+ {+ while (start < end)+ (*result++) = utf8::unchecked::next(start);++ return result;+ }++ // The iterator class+ template <typename octet_iterator>+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> { + octet_iterator it;+ public:+ iterator () {}+ explicit iterator (const octet_iterator& octet_it): it(octet_it) {}+ // the default "big three" are OK+ octet_iterator base () const { return it; }+ uint32_t operator * () const+ {+ octet_iterator temp = it;+ return utf8::unchecked::next(temp);+ }+ bool operator == (const iterator& rhs) const + { + return (it == rhs.it);+ }+ bool operator != (const iterator& rhs) const+ {+ return !(operator == (rhs));+ }+ iterator& operator ++ () + {+ ::std::advance(it, utf8::internal::sequence_length(it));+ return *this;+ }+ iterator operator ++ (int)+ {+ iterator temp = *this;+ ::std::advance(it, utf8::internal::sequence_length(it));+ return temp;+ } + iterator& operator -- ()+ {+ utf8::unchecked::prior(it);+ return *this;+ }+ iterator operator -- (int)+ {+ iterator temp = *this;+ utf8::unchecked::prior(it);+ return temp;+ }+ }; // class iterator++ } // namespace utf8::unchecked+} // namespace utf8 +++#endif // header guard+
+ libsass/utf8_string.cpp view
@@ -0,0 +1,97 @@+#include <string>+#include <vector>+#include <cstdlib>+#include <cmath>++#include "utf8.h"++namespace Sass {+ namespace UTF_8 {+ using std::string;++ // naming conventions:+ // offset: raw byte offset (0 based)+ // position: code point offset (0 based)+ // index: code point offset (1 based or negative)++ // function that will count the number of code points (utf-8 characters) from the given beginning to the given end+ size_t code_point_count(const string& str, size_t start, size_t end) {+ return utf8::distance(str.begin() + start, str.begin() + end);+ }++ size_t code_point_count(const string& str) {+ return utf8::distance(str.begin(), str.end());+ }++ // function that will return the byte offset at a code point position+ size_t offset_at_position(const string& str, size_t position) {+ string::const_iterator it = str.begin();+ utf8::advance(it, position, str.end());+ return distance(str.begin(), it);+ }++ // function that returns number of bytes in a character at offset+ size_t code_point_size_at_offset(const string& str, size_t offset) {+ // get iterator from string and forward by offset+ string::const_iterator stop = str.begin() + offset;+ // check if beyond boundary+ if (stop == str.end()) return 0;+ // advance by one code point+ utf8::advance(stop, 1, str.end());+ // calculate offset for code point+ return stop - str.begin() - offset;+ }++ // function that will return a normalized index, given a crazy one+ size_t normalize_index(int index, size_t len) {+ long signed_len = static_cast<long>(len);+ // assuming the index is 1-based+ // we are returning a 0-based index+ if (index > 0 && index <= signed_len) {+ // positive and within string length+ return index-1;+ }+ else if (index > signed_len) {+ // positive and past string length+ return len;+ }+ else if (index == 0) {+ return 0;+ }+ else if (std::abs((double)index) <= signed_len) {+ // negative and within string length+ return index + signed_len;+ }+ else {+ // negative and past string length+ return 0;+ }+ }++ // utf16 functions+ using std::wstring;++ // convert from utf16/wide string to utf8 string+ string convert_from_utf16(const wstring& utf16)+ {+ string utf8;+ // pre-allocate expected memory+ utf8.reserve(sizeof(utf16)/2);+ utf8::utf16to8(utf16.begin(), utf16.end(),+ back_inserter(utf8));+ return utf8;+ }++ // convert from utf8 string to utf16/wide string+ wstring convert_to_utf16(const string& utf8)+ {+ wstring utf16;+ // pre-allocate expected memory+ utf16.reserve(code_point_count(utf8)*2);+ utf8::utf8to16(utf8.begin(), utf8.end(),+ back_inserter(utf16));+ return utf16;+ }++ }+}
+ libsass/utf8_string.hpp view
@@ -0,0 +1,37 @@+#ifndef SASS_UTF8_STRING_H+#define SASS_UTF8_STRING_H++#include <string>+#include "utf8.h"++namespace Sass {+ namespace UTF_8 {++ // naming conventions:+ // offset: raw byte offset (0 based)+ // position: code point offset (0 based)+ // index: code point offset (1 based or negative)++ // function that will count the number of code points (utf-8 characters) from the beginning to the given end+ size_t code_point_count(const string& str, size_t start, size_t end);+ size_t code_point_count(const string& str);++ // function that will return the byte offset of a code point in a+ size_t offset_at_position(const string& str, size_t position);++ // function that returns number of bytes in a character in a string+ size_t code_point_size_at_offset(const string& str, size_t offset);++ // function that will return a normalized index, given a crazy one+ size_t normalize_index(int index, size_t len);++ #ifdef _WIN32+ // functions to handle unicode paths on windows+ string convert_from_utf16(const wstring& wstr);+ wstring convert_to_utf16(const string& str);+ #endif++ }+}++#endif
+ libsass/util.cpp view
@@ -0,0 +1,626 @@+#include<stdint.h>+#include "ast.hpp"+#include "util.hpp"+#include "prelexer.hpp"+#include "utf8/checked.h"++namespace Sass {++ #define out_of_memory() do { \+ fprintf(stderr, "Out of memory.\n"); \+ exit(EXIT_FAILURE); \+ } while (0)++ /* Sadly, sass_strdup is not portable. */+ char *sass_strdup(const char *str)+ {+ char *ret = (char*) malloc(strlen(str) + 1);+ if (ret == NULL)+ out_of_memory();+ strcpy(ret, str);+ return ret;+ }++ /* Locale unspecific atof function. */+ double sass_atof(const char *str)+ {+ char separator = *(localeconv()->decimal_point);+ if(separator != '.'){+ // The current locale specifies another+ // separator. convert the separator to the+ // one understood by the locale if needed+ const char *found = strchr(str, '.');+ if(found != NULL){+ // substitution is required. perform the substitution on a copy+ // of the string. This is slower but it is thread safe.+ char *copy = sass_strdup(str);+ *(copy + (found - str)) = separator;+ double res = atof(copy);+ free(copy);+ return res;+ }+ }++ return atof(str);+ }++ // double escape every escape sequences+ // escape unescaped quotes and backslashes+ string string_escape(const string& str)+ {+ 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+ string string_unescape(const string& str)+ {+ 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)+ string read_css_string(const string& str)+ {+ string out("");+ bool esc = false;+ for (auto i : str) {+ if (i == '\\') {+ esc = ! esc;+ } else if (esc && i == '\r') {+ continue;+ } else if (esc && i == '\n') {+ out.resize (out.size () - 1);+ esc = false;+ continue;+ } else {+ esc = false;+ }+ out.push_back(i);+ }+ if (esc) out += '\\';+ return out;+ }++ // evacuate unescaped quoted+ // leave everything else untouched+ string evacuate_quotes(const string& str)+ {+ 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+ string evacuate_escapes(const string& str)+ {+ string out("");+ bool esc = false;+ for (auto i : str) {+ if (i == '\\' && !esc) {+ out += '\\';+ out += '\\';+ esc = true;+ } else if (esc && i == '"') {+ out += '\\';+ out += i;+ esc = false;+ } else if (esc && i == '\'') {+ out += '\\';+ out += i;+ esc = false;+ } else if (esc && i == '\\') {+ out += '\\';+ out += i;+ esc = false;+ } else {+ esc = false;+ out += i;+ }+ }+ // happens when parsing does not correctly skip+ // over escaped sequences for ie. interpolations+ // one example: foo\#{interpolate}+ // if (esc) out += '\\';+ return out;+ }++ // bell character is replaces with space+ string string_to_output(const string& str)+ {+ string out("");+ for (auto i : str) {+ if (i == 10) {+ out += ' ';+ } else {+ out += i;+ }+ }+ return out;+ }++ string comment_to_string(const string& text)+ {+ string str = "";+ size_t has = 0;+ bool clean = false;+ for (auto i : text) {+ if (clean) {+ if (i == '\n') { has = 0; }+ else if (i == '\r') { has = 0; }+ else if (i == '\t') { ++ has; }+ else if (i == ' ') { ++ has; }+ else if (i == '*') {}+ else {+ clean = false;+ str += ' ';+ str += i;+ }+ } else if (i == '\n') {+ clean = true;+ } else if (i == '\r') {+ clean = true;+ } else {+ str += i;+ }+ }+ if (has) return str;+ else return text;+ }++ string normalize_wspace(const string& str)+ {+ bool ws = false;+ bool esc = false;+ char inside_str = 0;+ string text = "";+ for(auto i : str) {+ if (!esc && i == '\\') {+ esc = true;+ text += i;+ } else if (esc) {+ esc = false;+ text += i;+ } else if (!inside_str && (i == '"' || i == '\'')) {+ inside_str = i;+ text += i;+ } else if (inside_str) {+ if (i == inside_str)+ inside_str = 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+ // quotes, which will trigger the use of single quotes as best quote_mark.+ char detect_best_quotemark(const char* s, char qm)+ {+ // ensure valid fallback quote_mark+ char quote_mark = qm && qm != '*' ? qm : '"';+ while (*s) {+ // force double quotes as soon+ // as one single quote is found+ if (*s == '\'') { return '"'; }+ // a single does not force quote_mark+ // maybe we see a double quote later+ else if (*s == '"') { quote_mark = '\''; }+ ++ s;+ }+ return quote_mark;+ }++ string unquote(const string& s, char* qd)+ {++ // not enough room for quotes+ // no possibility to unquote+ if (s.length() < 2) return s;++ char q;+ bool skipped = false;++ // this is no guarantee that the unquoting will work+ // what about whitespace before/after the quote_mark?+ if (*s.begin() == '"' && *s.rbegin() == '"') q = '"';+ else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\'';+ else return s;++ string unq;+ unq.reserve(s.length()-2);++ for (size_t i = 1, L = s.length() - 1; i < L; ++i) {++ // implement the same strange ruby sass behavior+ // an escape sequence can also mean a unicode char+ if (s[i] == '\\' && !skipped) {+ // remember+ skipped = true;++ // skip it+ // ++ i;++ // if (i == L) break;++ // escape length+ size_t len = 1;++ // parse as many sequence chars as possible+ // ToDo: Check if ruby aborts after possible max+ while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;++ // hex string?+ if (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(), nullptr, 16);++ // assert invalid code points+ if (cp == 0) cp = 0xFFFD;+ // replace bell character+ // if (cp == 10) cp = 32;++ // use a very simple approach to convert via utf8 lib+ // maybe there is a more elegant way; maybe we shoud+ // convert the whole output from string to a stream!?+ // allocate memory for utf8 char and convert to utf8+ unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);+ for(size_t m = 0; u[m] && m < 5; m++) unq.push_back(u[m]);++ // skip some more chars?+ i += len - 1; skipped = false;++ }+++ }+ // check for unexpected delimiter+ // be strict and throw error back+ else if (!skipped && q == s[i]) {+ // don't be that strict+ return s;+ // this basically always means an internal error and not users fault+ error("Unescaped delimiter in string to unquote found. [" + s + "]", ParserState("[UNQUOTE]"));+ }+ else {+ skipped = false;+ unq.push_back(s[i]);+ }++ }+ if (skipped) { return s; }+ if (qd) *qd = q;+ return unq;++ }++ string quote(const string& s, char q)+ {++ // autodetect with fallback to given quote+ q = detect_best_quotemark(s.c_str(), q);++ // return an empty quoted string+ if (s.empty()) return string(2, q ? q : '"');++ string quoted;+ quoted.reserve(s.length()+2);+ quoted.push_back(q);++ const char* it = s.c_str();+ const char* end = it + strlen(it) + 1;+ while (*it && it < end) {+ const char* now = it;++ if (*it == q) {+ quoted.push_back('\\');+ } else if (*it == '\\') {+ quoted.push_back('\\');+ }++ int cp = utf8::next(it, end);++ if (cp == 10) {+ quoted.push_back('\\');+ quoted.push_back('a');+ } else if (cp < 127) {+ quoted.push_back((char) cp);+ } else {+ while (now < it) {+ quoted.push_back(*now);+ ++ now;+ }+ }+ }++ quoted.push_back(q);+ return quoted;+ }++ bool is_hex_doublet(double n)+ {+ return n == 0x00 || n == 0x11 || n == 0x22 || n == 0x33 ||+ n == 0x44 || n == 0x55 || n == 0x66 || n == 0x77 ||+ n == 0x88 || n == 0x99 || n == 0xAA || n == 0xBB ||+ n == 0xCC || n == 0xDD || n == 0xEE || n == 0xFF ;+ }++ bool is_color_doublet(double r, double g, double b)+ {+ return is_hex_doublet(r) && is_hex_doublet(g) && is_hex_doublet(b);+ }++ bool peek_linefeed(const char* start)+ {+ while (*start) {+ if (*start == '\n' || *start == '\r') return true;+ if (*start != ' ' && *start != '\t') return false;+ ++ start;+ }+ return false;+ }++ namespace Util {+ using std::string;++ string normalize_underscores(const string& str) {+ string normalized = str;+ for(size_t i = 0, L = normalized.length(); i < L; ++i) {+ if(normalized[i] == '_') {+ normalized[i] = '-';+ }+ }+ return normalized;+ }++ string normalize_decimals(const string& str) {+ string prefix = "0";+ string normalized = str;++ return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;+ }++ // compress a color sixtuplet if possible+ // input: "#CC9900" -> output: "#C90"+ string normalize_sixtuplet(const string& col) {+ if(+ col.substr(1, 1) == col.substr(2, 1) &&+ col.substr(3, 1) == col.substr(4, 1) &&+ col.substr(5, 1) == col.substr(6, 1)+ ) {+ return string("#" + col.substr(1, 1)+ + col.substr(3, 1)+ + col.substr(5, 1));+ } else {+ return string(col);+ }+ }++ bool isPrintable(Ruleset* r, Output_Style style) {+ if (r == NULL) {+ return false;+ }++ Block* b = r->block();++ bool hasSelectors = static_cast<Selector_List*>(r->selector())->length() > 0;++ if (!hasSelectors) {+ return false;+ }++ bool hasDeclarations = false;+ bool hasPrintableChildBlocks = false;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (dynamic_cast<Has_Block*>(stm)) {+ Block* pChildBlock = ((Has_Block*)stm)->block();+ if (isPrintable(pChildBlock, style)) {+ hasPrintableChildBlocks = true;+ }+ } else if (Comment* c = dynamic_cast<Comment*>(stm)) {+ if (style == COMPRESSED) {+ hasDeclarations = c->is_important();+ } else {+ hasDeclarations = true;+ }+ } else {+ hasDeclarations = true;+ }++ if (hasDeclarations || hasPrintableChildBlocks) {+ return true;+ }+ }++ return false;+ }++ bool isPrintable(Feature_Block* f, Output_Style style) {+ if (f == NULL) {+ return false;+ }++ Block* b = f->block();++ bool hasSelectors = f->selector() && static_cast<Selector_List*>(f->selector())->length() > 0;++ bool hasDeclarations = false;+ bool hasPrintableChildBlocks = false;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable() && f->selector() != NULL && !hasSelectors) {+ // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0),+ // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector+ // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with+ // a declaration in it).+ }+ else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+ hasDeclarations = true;+ }+ else if (dynamic_cast<Has_Block*>(stm)) {+ Block* pChildBlock = ((Has_Block*)stm)->block();+ if (isPrintable(pChildBlock, style)) {+ hasPrintableChildBlocks = true;+ }+ }++ if (hasDeclarations || hasPrintableChildBlocks) {+ return true;+ }+ }++ return false;+ }++ bool isPrintable(Media_Block* m, Output_Style style) {+ if (m == NULL) {+ return false;+ }++ Block* b = m->block();++ bool hasSelectors = m->selector() && static_cast<Selector_List*>(m->selector())->length() > 0;++ bool hasDeclarations = false;+ bool hasPrintableChildBlocks = false;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (!stm->is_hoistable() && m->selector() != NULL && !hasSelectors) {+ // If a statement isn't hoistable, the selectors apply to it. If there are no selectors (a selector list of length 0),+ // then those statements aren't considered printable. That means there was a placeholder that was removed. If the selector+ // is NULL, then that means there was never a wrapping selector and it is printable (think of a top level media block with+ // a declaration in it).+ }+ else if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+ hasDeclarations = true;+ }+ else if (dynamic_cast<Has_Block*>(stm)) {+ Block* pChildBlock = ((Has_Block*)stm)->block();+ if (isPrintable(pChildBlock, style)) {+ hasPrintableChildBlocks = true;+ }+ }++ if (hasDeclarations || hasPrintableChildBlocks) {+ return true;+ }+ }++ return false;+ }++ bool isPrintable(Block* b, Output_Style style) {+ if (b == NULL) {+ return false;+ }++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (typeid(*stm) == typeid(Declaration) || typeid(*stm) == typeid(At_Rule)) {+ return true;+ }+ else if (typeid(*stm) == typeid(Comment)) {++ }+ else if (typeid(*stm) == typeid(Ruleset)) {+ Ruleset* r = (Ruleset*) stm;+ if (isPrintable(r, style)) {+ return true;+ }+ }+ else if (typeid(*stm) == typeid(Feature_Block)) {+ Feature_Block* f = (Feature_Block*) stm;+ if (isPrintable(f, style)) {+ return true;+ }+ }+ else if (typeid(*stm) == typeid(Media_Block)) {+ Media_Block* m = (Media_Block*) stm;+ if (isPrintable(m, style)) {+ return true;+ }+ }+ else if (dynamic_cast<Has_Block*>(stm) && isPrintable(((Has_Block*)stm)->block(), style)) {+ return true;+ }+ }++ return false;+ }++ string vecJoin(const vector<string>& vec, const string& sep)+ {+ switch (vec.size())+ {+ case 0:+ return string("");+ case 1:+ return vec[0];+ default:+ std::ostringstream os;+ os << vec[0];+ for (size_t i = 1; i < vec.size(); i++) {+ os << sep << vec[i];+ }+ return os.str();+ }+ }++ bool isAscii(const char chr) {+ return unsigned(chr) < 128;+ }++ }+}
+ libsass/util.hpp view
@@ -0,0 +1,48 @@+#ifndef SASS_UTIL_H+#define SASS_UTIL_H++#include <vector>+#include <string>+#include "ast_fwd_decl.hpp"++namespace Sass {+ using namespace std;++ char* sass_strdup(const char* str);+ double sass_atof(const char* str);+ string string_escape(const string& str);+ string string_unescape(const string& str);+ string read_css_string(const string& str);+ string evacuate_quotes(const string& str);+ string evacuate_escapes(const string& str);+ string string_to_output(const string& str);+ string comment_to_string(const string& text);+ string normalize_wspace(const string& str);++ string quote(const string&, char q = 0);+ string unquote(const string&, char* q = 0);+ char detect_best_quotemark(const char* s, char qm = '"');++ bool is_hex_doublet(double n);+ bool is_color_doublet(double r, double g, double b);++ bool peek_linefeed(const char* start);++ namespace Util {++ string normalize_underscores(const string& str);+ string normalize_decimals(const string& str);+ string normalize_sixtuplet(const string& col);++ string vecJoin(const vector<string>& vec, const string& sep);+ bool containsAnyPrintableStatements(Block* b);++ bool isPrintable(Ruleset* r, Output_Style style = NESTED);+ bool isPrintable(Feature_Block* r, Output_Style style = NESTED);+ bool isPrintable(Media_Block* r, Output_Style style = NESTED);+ bool isPrintable(Block* b, Output_Style style = NESTED);+ bool isAscii(const char chr);++ }+}+#endif
+ tests/Main.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE BangPatterns #-}+import Binding.Libsass+import Foreign+import Foreign.C+import Test.Hspec++simpleCompile :: String -> IO String+simpleCompile str = do+ cstr <- newCString str+ ctx <- sass_make_data_context cstr+ status <- sass_compile_data_context ctx+ if status /= 0+ then do+ sass_delete_data_context ctx+ return ""+ else do+ cres <- sass_context_get_output_string (castPtr ctx)+ !res <- peekCString cres+ sass_delete_data_context ctx+ return res++sampleInput :: String+sampleInput = "foo { margin: 21px * 2; }"++sampleOutput :: String+sampleOutput = "foo {\n margin: 42px; }\n"++main :: IO ()+main = hspec $ do+ describe "Libsass" $ do+ it "should correctly compile simple expression" $ do+ simpleCompile sampleInput `shouldReturn` sampleOutput