hlibsass 0.1.4.0 → 0.1.5.0
raw patch · 225 files changed
+33702/−29729 lines, 225 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Bindings.Libsass.Functions: sass_import_get_base :: SassImportEntry -> IO CString
- Bindings.Libsass.Functions: sass_import_get_path :: SassImportEntry -> IO CString
- Bindings.Libsass.Types: instance Enum SassCompilerState
- Bindings.Libsass.Types: instance Enum SassOutputStyle
- Bindings.Libsass.Types: instance Enum SassSeparator
- Bindings.Libsass.Types: instance Enum SassTag
- Bindings.Libsass.Types: instance Eq SassCompilerState
- Bindings.Libsass.Types: instance Eq SassOutputStyle
- Bindings.Libsass.Types: instance Eq SassSeparator
- Bindings.Libsass.Types: instance Eq SassTag
- Bindings.Libsass.Types: instance Show SassCompilerState
- Bindings.Libsass.Types: instance Show SassOutputStyle
- Bindings.Libsass.Types: instance Show SassSeparator
- Bindings.Libsass.Types: instance Show SassTag
+ Bindings.Libsass.Functions: sass_import_get_abs_path :: SassImportEntry -> IO CString
+ Bindings.Libsass.Functions: sass_import_get_imp_path :: SassImportEntry -> IO CString
+ Bindings.Libsass.Functions: sass_importer_get_list_entry :: SassImporterList -> CSize -> IO SassImporterEntry
+ Bindings.Libsass.Functions: sass_importer_set_list_entry :: SassImporterList -> CSize -> SassImporterEntry -> IO ()
+ Bindings.Libsass.Types: SassAdd :: SassOp
+ Bindings.Libsass.Types: SassAnd :: SassOp
+ Bindings.Libsass.Types: SassDiv :: SassOp
+ Bindings.Libsass.Types: SassEq :: SassOp
+ Bindings.Libsass.Types: SassGt :: SassOp
+ Bindings.Libsass.Types: SassGte :: SassOp
+ Bindings.Libsass.Types: SassLt :: SassOp
+ Bindings.Libsass.Types: SassLte :: SassOp
+ Bindings.Libsass.Types: SassMod :: SassOp
+ Bindings.Libsass.Types: SassMul :: SassOp
+ Bindings.Libsass.Types: SassNeq :: SassOp
+ Bindings.Libsass.Types: SassNumOps :: SassOp
+ Bindings.Libsass.Types: SassOr :: SassOp
+ Bindings.Libsass.Types: SassSub :: SassOp
+ Bindings.Libsass.Types: data SassOp
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassCompilerState
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassOp
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassOutputStyle
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassSeparator
+ Bindings.Libsass.Types: instance GHC.Classes.Eq Bindings.Libsass.Types.SassTag
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassCompilerState
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassOp
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassOutputStyle
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassSeparator
+ Bindings.Libsass.Types: instance GHC.Enum.Enum Bindings.Libsass.Types.SassTag
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassCompilerState
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassOp
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassOutputStyle
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassSeparator
+ Bindings.Libsass.Types: instance GHC.Show.Show Bindings.Libsass.Types.SassTag
+ Bindings.Libsass.Values: sass_value_op :: CInt -> Ptr SassValue -> Ptr SassValue -> IO (Ptr SassValue)
+ Bindings.Libsass.Values: sass_value_stringify :: Ptr SassValue -> Bool -> CInt -> IO (Ptr SassValue)
Files
- Bindings/Libsass.hs +1/−1
- Bindings/Libsass/Context.hs +81/−81
- Bindings/Libsass/Functions.hs +39/−41
- Bindings/Libsass/Types.hs +50/−1
- Bindings/Libsass/Values.hs +70/−58
- Bindings/Libsass/Wrappers.hs +7/−7
- CHANGELOG.md +11/−0
- hlibsass.cabal +13/−9
- libsass/Makefile +162/−77
- libsass/Makefile.conf +47/−0
- libsass/VERSION +1/−1
- libsass/ast.cpp +0/−945
- libsass/ast.hpp +0/−2141
- libsass/ast_def_macros.hpp +0/−21
- libsass/ast_factory.hpp +0/−92
- libsass/ast_fwd_decl.hpp +0/−85
- libsass/b64/cencode.h +0/−32
- libsass/b64/encode.h +0/−77
- libsass/backtrace.hpp +0/−77
- libsass/base64vlq.cpp +0/−43
- libsass/base64vlq.hpp +0/−31
- libsass/bind.cpp +0/−255
- libsass/bind.hpp +0/−18
- libsass/cencode.c +0/−102
- libsass/color_names.hpp +0/−327
- libsass/constants.cpp +0/−168
- libsass/constants.hpp +0/−173
- libsass/context.cpp +0/−561
- libsass/context.hpp +0/−157
- libsass/contextualize.cpp +0/−148
- libsass/contextualize.hpp +0/−46
- libsass/contextualize_eval.cpp +0/−93
- libsass/contextualize_eval.hpp +0/−44
- libsass/cssize.cpp +0/−551
- libsass/cssize.hpp +0/−82
- libsass/debug.hpp +0/−39
- libsass/debugger.hpp +0/−558
- libsass/emitter.cpp +0/−258
- libsass/emitter.hpp +0/−89
- libsass/environment.hpp +0/−163
- libsass/error_handling.cpp +0/−35
- libsass/error_handling.hpp +0/−32
- libsass/eval.cpp +0/−1379
- libsass/eval.hpp +0/−88
- libsass/expand.cpp +0/−575
- libsass/expand.hpp +0/−78
- libsass/extend.cpp +0/−2010
- libsass/extend.hpp +0/−45
- libsass/file.cpp +0/−349
- libsass/file.hpp +0/−60
- libsass/functions.cpp +0/−1592
- libsass/functions.hpp +0/−184
- libsass/include/sass.h +15/−0
- libsass/include/sass/base.h +76/−0
- libsass/include/sass/context.h +152/−0
- libsass/include/sass/functions.h +108/−0
- libsass/include/sass/interface.h +105/−0
- libsass/include/sass/values.h +141/−0
- libsass/include/sass/version.h +8/−0
- libsass/include/sass/version.h.in +8/−0
- libsass/include/sass2scss.h +120/−0
- libsass/inspect.cpp +0/−945
- libsass/inspect.hpp +0/−98
- libsass/json.cpp +0/−1408
- libsass/json.hpp +0/−117
- libsass/kwd_arg_macros.hpp +0/−28
- libsass/lexer.cpp +0/−133
- libsass/lexer.hpp +0/−239
- libsass/listize.cpp +0/−83
- libsass/listize.hpp +0/−41
- libsass/mapping.hpp +0/−18
- libsass/memory_manager.hpp +0/−57
- libsass/node.cpp +0/−251
- libsass/node.hpp +0/−121
- libsass/operation.hpp +0/−166
- libsass/output.cpp +0/−401
- libsass/output.hpp +0/−55
- libsass/parser.cpp +0/−2408
- libsass/parser.hpp +0/−291
- libsass/paths.hpp +0/−74
- libsass/plugins.cpp +0/−168
- libsass/plugins.hpp +0/−58
- libsass/position.cpp +0/−163
- libsass/position.hpp +0/−126
- libsass/posix/getopt.c +0/−562
- libsass/posix/getopt.h +0/−95
- libsass/prelexer.cpp +0/−784
- libsass/prelexer.hpp +0/−378
- libsass/remove_placeholders.cpp +0/−59
- libsass/remove_placeholders.hpp +0/−43
- libsass/sass.cpp +0/−43
- libsass/sass.h +0/−68
- libsass/sass2scss.cpp +0/−835
- libsass/sass2scss.h +0/−115
- libsass/sass_context.cpp +0/−894
- libsass/sass_context.h +0/−149
- libsass/sass_functions.cpp +0/−173
- libsass/sass_functions.h +0/−108
- libsass/sass_interface.cpp +0/−287
- libsass/sass_interface.h +0/−102
- libsass/sass_util.cpp +0/−132
- libsass/sass_util.hpp +0/−260
- libsass/sass_values.cpp +0/−354
- libsass/sass_values.h +0/−127
- libsass/sass_version.h +0/−8
- libsass/source_map.cpp +0/−184
- libsass/source_map.hpp +0/−65
- libsass/src/GNUmakefile.am +54/−0
- libsass/src/ast.cpp +2134/−0
- libsass/src/ast.hpp +2347/−0
- libsass/src/ast_def_macros.hpp +55/−0
- libsass/src/ast_factory.hpp +93/−0
- libsass/src/ast_fwd_decl.hpp +91/−0
- libsass/src/b64/cencode.h +32/−0
- libsass/src/b64/encode.h +77/−0
- libsass/src/backtrace.hpp +76/−0
- libsass/src/base64vlq.cpp +43/−0
- libsass/src/base64vlq.hpp +30/−0
- libsass/src/bind.cpp +301/−0
- libsass/src/bind.hpp +18/−0
- libsass/src/c99func.c +54/−0
- libsass/src/cencode.c +102/−0
- libsass/src/color_maps.cpp +643/−0
- libsass/src/color_maps.hpp +333/−0
- libsass/src/constants.cpp +176/−0
- libsass/src/constants.hpp +179/−0
- libsass/src/context.cpp +848/−0
- libsass/src/context.hpp +147/−0
- libsass/src/cssize.cpp +565/−0
- libsass/src/cssize.hpp +78/−0
- libsass/src/debug.hpp +43/−0
- libsass/src/debugger.hpp +686/−0
- libsass/src/emitter.cpp +261/−0
- libsass/src/emitter.hpp +91/−0
- libsass/src/environment.cpp +184/−0
- libsass/src/environment.hpp +92/−0
- libsass/src/error_handling.cpp +117/−0
- libsass/src/error_handling.hpp +75/−0
- libsass/src/eval.cpp +1531/−0
- libsass/src/eval.hpp +107/−0
- libsass/src/expand.cpp +689/−0
- libsass/src/expand.hpp +79/−0
- libsass/src/extend.cpp +2069/−0
- libsass/src/extend.hpp +44/−0
- libsass/src/file.cpp +407/−0
- libsass/src/file.hpp +123/−0
- libsass/src/functions.cpp +1923/−0
- libsass/src/functions.hpp +197/−0
- libsass/src/inspect.cpp +851/−0
- libsass/src/inspect.hpp +98/−0
- libsass/src/json.cpp +1398/−0
- libsass/src/json.hpp +117/−0
- libsass/src/kwd_arg_macros.hpp +28/−0
- libsass/src/lexer.cpp +169/−0
- libsass/src/lexer.hpp +285/−0
- libsass/src/listize.cpp +87/−0
- libsass/src/listize.hpp +39/−0
- libsass/src/mapping.hpp +18/−0
- libsass/src/memory_manager.cpp +76/−0
- libsass/src/memory_manager.hpp +48/−0
- libsass/src/node.cpp +322/−0
- libsass/src/node.hpp +120/−0
- libsass/src/operation.hpp +172/−0
- libsass/src/output.cpp +393/−0
- libsass/src/output.hpp +56/−0
- libsass/src/parser.cpp +2436/−0
- libsass/src/parser.hpp +311/−0
- libsass/src/paths.hpp +71/−0
- libsass/src/plugins.cpp +165/−0
- libsass/src/plugins.hpp +57/−0
- libsass/src/position.cpp +162/−0
- libsass/src/position.hpp +123/−0
- libsass/src/prelexer.cpp +1141/−0
- libsass/src/prelexer.hpp +416/−0
- libsass/src/remove_placeholders.cpp +55/−0
- libsass/src/remove_placeholders.hpp +37/−0
- libsass/src/sass.cpp +41/−0
- libsass/src/sass2scss.cpp +845/−0
- libsass/src/sass_context.cpp +708/−0
- libsass/src/sass_context.hpp +156/−0
- libsass/src/sass_functions.cpp +144/−0
- libsass/src/sass_functions.hpp +32/−0
- libsass/src/sass_interface.cpp +216/−0
- libsass/src/sass_util.cpp +150/−0
- libsass/src/sass_util.hpp +256/−0
- libsass/src/sass_values.cpp +351/−0
- libsass/src/sass_values.hpp +81/−0
- libsass/src/source_map.cpp +184/−0
- libsass/src/source_map.hpp +62/−0
- libsass/src/subset_map.hpp +145/−0
- libsass/src/support/libsass.pc.in +11/−0
- libsass/src/to_c.cpp +73/−0
- libsass/src/to_c.hpp +41/−0
- libsass/src/to_string.cpp +48/−0
- libsass/src/to_string.hpp +38/−0
- libsass/src/to_value.cpp +109/−0
- libsass/src/to_value.hpp +50/−0
- libsass/src/units.cpp +160/−0
- libsass/src/units.hpp +88/−0
- libsass/src/utf8.h +34/−0
- libsass/src/utf8/checked.h +327/−0
- libsass/src/utf8/core.h +329/−0
- libsass/src/utf8/unchecked.h +228/−0
- libsass/src/utf8_string.cpp +97/−0
- libsass/src/utf8_string.hpp +37/−0
- libsass/src/util.cpp +780/−0
- libsass/src/util.hpp +61/−0
- libsass/src/values.cpp +137/−0
- libsass/src/values.hpp +12/−0
- libsass/subset_map.hpp +0/−147
- libsass/to_c.cpp +0/−61
- libsass/to_c.hpp +0/−44
- libsass/to_string.cpp +0/−34
- libsass/to_string.hpp +0/−35
- libsass/units.cpp +0/−155
- libsass/units.hpp +0/−89
- libsass/utf8.h +0/−34
- libsass/utf8/checked.h +0/−327
- libsass/utf8/core.h +0/−329
- libsass/utf8/unchecked.h +0/−228
- libsass/utf8_string.cpp +0/−97
- libsass/utf8_string.hpp +0/−37
- libsass/util.cpp +0/−704
- libsass/util.hpp +0/−54
- tests/Main.hs +11/−1
Bindings/Libsass.hs view
@@ -16,4 +16,4 @@ import Bindings.Libsass.Values as Libsass import Foreign.C.String -foreign import ccall unsafe "sass.h" libsass_version :: CString+foreign import ccall unsafe "sass/base.h" libsass_version :: CString
Bindings/Libsass/Context.hs view
@@ -6,10 +6,10 @@ import Foreign.C.String import Foreign.C.Types -foreign import ccall unsafe "sass_context.h" sass_make_options+foreign import ccall unsafe "sass/context.h" sass_make_options :: IO (Ptr SassOptions) -foreign import ccall unsafe "sass_context.h" sass_make_file_context+foreign import ccall unsafe "sass/context.h" sass_make_file_context :: CString -> IO (Ptr SassFileContext) @@ -18,341 +18,341 @@ -- -- 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+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+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+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+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+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+foreign import ccall safe "sass/context.h" sass_compiler_parse :: Ptr SassCompiler -> IO CInt -foreign import ccall safe "sass_context.h" sass_compiler_execute+foreign import ccall safe "sass/context.h" sass_compiler_execute :: Ptr SassCompiler -> IO CInt -foreign import ccall unsafe "sass_context.h" sass_delete_compiler+foreign import ccall unsafe "sass/context.h" sass_delete_compiler :: Ptr SassCompiler -> IO () -foreign import ccall unsafe "sass_context.h" sass_delete_file_context+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+foreign import ccall unsafe "sass/context.h" sass_option_push_include_path :: Ptr SassOptions -> CString -> IO ()
Bindings/Libsass/Functions.hs view
@@ -5,152 +5,150 @@ import Foreign import Foreign.C -foreign import ccall unsafe "sass_function.h" sass_make_importer_list+foreign import ccall unsafe "sass/functions.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/functions.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/functions.h" sass_importer_set_list_entry+ :: SassImporterList+ -> CSize+ -> SassImporterEntry+ -> IO () -foreign import ccall unsafe "sass_function.h" sass_make_importer+foreign import ccall unsafe "sass/functions.h" sass_make_importer :: SassImporterFn -> CDouble -> Ptr () -> IO SassImporterEntry -foreign import ccall unsafe "sass_function.h" sass_importer_get_function+foreign import ccall unsafe "sass/functions.h" sass_importer_get_function :: SassImporterEntry -> IO SassImporterFn -foreign import ccall unsafe "sass_function.h" sass_importer_get_priority+foreign import ccall unsafe "sass/functions.h" sass_importer_get_priority :: SassImporterEntry -> IO CDouble-foreign import ccall unsafe "sass_function.h" sass_importer_get_cookie+foreign import ccall unsafe "sass/functions.h" sass_importer_get_cookie :: SassImporterEntry -> IO (Ptr ()) -foreign import ccall unsafe "sass_function.h" sass_delete_importer+foreign import ccall unsafe "sass/functions.h" sass_delete_importer :: SassImporterEntry -> IO () -foreign import ccall unsafe "sass_function.h" sass_make_import_list+foreign import ccall unsafe "sass/functions.h" sass_make_import_list :: CSize -> IO SassImportList -foreign import ccall unsafe "sass_function.h" sass_make_import_entry+foreign import ccall unsafe "sass/functions.h" sass_make_import_entry :: CString -> CString -> CString -> IO SassImportEntry -foreign import ccall unsafe "sass_function.h" sass_make_import+foreign import ccall unsafe "sass/functions.h" sass_make_import :: CString -> CString -> CString -> CString -> IO SassImportEntry -foreign import ccall unsafe "sass_function.h" sass_import_set_error+foreign import ccall unsafe "sass/functions.h" sass_import_set_error :: SassImportEntry -> CString -> CSize -> CSize -> IO SassImportEntry -foreign import ccall unsafe "sass_function.h" sass_import_set_list_entry+foreign import ccall unsafe "sass/functions.h" sass_import_set_list_entry :: SassImportList -> CSize -> SassImportEntry -> IO () -foreign import ccall unsafe "sass_function.h" sass_import_get_list_entry+foreign import ccall unsafe "sass/functions.h" sass_import_get_list_entry :: SassImportList -> CSize -> IO SassImportEntry -foreign import ccall unsafe "sass_function.h" sass_import_get_path+foreign import ccall unsafe "sass/functions.h" sass_import_get_imp_path :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_get_base+foreign import ccall unsafe "sass/functions.h" sass_import_get_abs_path :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_get_source+foreign import ccall unsafe "sass/functions.h" sass_import_get_source :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_get_srcmap+foreign import ccall unsafe "sass/functions.h" sass_import_get_srcmap :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_take_source+foreign import ccall unsafe "sass/functions.h" sass_import_take_source :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_take_srcmap+foreign import ccall unsafe "sass/functions.h" sass_import_take_srcmap :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_import_get_error_line+foreign import ccall unsafe "sass/functions.h" sass_import_get_error_line :: SassImportEntry -> IO CSize -foreign import ccall unsafe "sass_function.h" sass_import_get_error_column+foreign import ccall unsafe "sass/functions.h" sass_import_get_error_column :: SassImportEntry -> IO CSize -foreign import ccall unsafe "sass_function.h" sass_import_get_error_message+foreign import ccall unsafe "sass/functions.h" sass_import_get_error_message :: SassImportEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_delete_import_list+foreign import ccall unsafe "sass/functions.h" sass_delete_import_list :: SassImportList -> IO () -foreign import ccall unsafe "sass_function.h" sass_delete_import+foreign import ccall unsafe "sass/functions.h" sass_delete_import :: SassImportEntry -> IO () -foreign import ccall unsafe "sass_function.h" sass_make_function_list+foreign import ccall unsafe "sass/functions.h" sass_make_function_list :: CSize -> IO SassFunctionList -foreign import ccall unsafe "sass_function.h" sass_make_function+foreign import ccall unsafe "sass/functions.h" sass_make_function :: CString -> SassFunctionFn -> Ptr () -> IO SassFunctionEntry -foreign import ccall unsafe "sass_function.h" sass_function_get_list_entry+foreign import ccall unsafe "sass/functions.h" sass_function_get_list_entry :: SassFunctionList -> CSize -> IO SassFunctionEntry -foreign import ccall unsafe "sass_function.h" sass_function_set_list_entry+foreign import ccall unsafe "sass/functions.h" sass_function_set_list_entry :: SassFunctionList -> CSize -> SassFunctionEntry -> IO () -foreign import ccall unsafe "sass_function.h" sass_function_get_signature+foreign import ccall unsafe "sass/functions.h" sass_function_get_signature :: SassFunctionEntry -> IO CString -foreign import ccall unsafe "sass_function.h" sass_function_get_function+foreign import ccall unsafe "sass/functions.h" sass_function_get_function :: SassFunctionEntry -> IO SassFunctionFn -foreign import ccall unsafe "sass_function.h" sass_function_get_cookie+foreign import ccall unsafe "sass/functions.h" sass_function_get_cookie :: SassFunctionEntry -> IO (Ptr ())
Bindings/Libsass/Types.hs view
@@ -59,7 +59,7 @@ type SassImporterEntry = Ptr SassImporter type SassImporterList = Ptr (Ptr SassImporter) -type SassImporterFnType = +type SassImporterFnType = CString -> SassImporterEntry -> Ptr SassCompiler@@ -125,3 +125,52 @@ toEnum 0 = SassSeparatorComma toEnum 1 = SassSeparatorSpace toEnum u = error ("SassSeparator.toEnum: Cannot match " ++ show u)++-- ^ Operator used to combine two 'SassValue's.+data SassOp = SassAnd+ | SassOr+ | SassEq+ | SassNeq+ | SassGt+ | SassGte+ | SassLt+ | SassLte+ | SassAdd+ | SassSub+ | SassMul+ | SassDiv+ | SassMod+ | SassNumOps+ deriving (Eq, Show)++instance Enum SassOp where+ fromEnum SassAnd = 0+ fromEnum SassOr = 1+ fromEnum SassEq = 2+ fromEnum SassNeq = 3+ fromEnum SassGt = 4+ fromEnum SassGte = 5+ fromEnum SassLt = 6+ fromEnum SassLte = 7+ fromEnum SassAdd = 8+ fromEnum SassSub = 9+ fromEnum SassMul = 10+ fromEnum SassDiv = 11+ fromEnum SassMod = 12+ fromEnum SassNumOps = 13++ toEnum 0 = SassAnd+ toEnum 1 = SassOr+ toEnum 2 = SassEq+ toEnum 3 = SassNeq+ toEnum 4 = SassGt+ toEnum 5 = SassGte+ toEnum 6 = SassLt+ toEnum 7 = SassLte+ toEnum 8 = SassAdd+ toEnum 9 = SassSub+ toEnum 10 = SassMul+ toEnum 11 = SassDiv+ toEnum 12 = SassMod+ toEnum 13 = SassNumOps+ toEnum u = error ("SassOps.toEnum: Cannot match " ++ show u)
Bindings/Libsass/Values.hs view
@@ -5,243 +5,255 @@ import Foreign import Foreign.C -foreign import ccall unsafe "sass_values.h" sass_value_get_tag+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+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+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+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+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+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+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+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+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+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+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+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+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+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+foreign import ccall unsafe "sass/values.h" sass_string_get_value :: Ptr SassValue -> IO CString -foreign import ccall unsafe "sass_values.h" sass_string_is_quoted+foreign import ccall unsafe "sass/values.h" sass_string_set_value :: Ptr SassValue- -> IO Bool+ -> CString+ -> IO () -foreign import ccall unsafe "sass_values.h" sass_string_set_quoted+foreign import ccall unsafe "sass/values.h" sass_string_is_quoted :: Ptr SassValue- -> Bool- -> IO ()+ -> IO Bool -foreign import ccall unsafe "sass_values.h" sass_string_set_value+foreign import ccall unsafe "sass/values.h" sass_string_set_quoted :: Ptr SassValue- -> CString+ -> Bool -> IO () -foreign import ccall unsafe "sass_values.h" sass_boolean_get_value+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+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+foreign import ccall unsafe "sass/values.h" sass_make_null :: IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_boolean+foreign import ccall unsafe "sass/values.h" sass_make_boolean :: Bool -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_string+foreign import ccall unsafe "sass/values.h" sass_make_string :: CString -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_qstring+foreign import ccall unsafe "sass/values.h" sass_make_qstring :: CString -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_number+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+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+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+foreign import ccall unsafe "sass/values.h" sass_make_map :: CSize -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_error+foreign import ccall unsafe "sass/values.h" sass_make_error :: CString -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_make_warning+foreign import ccall unsafe "sass/values.h" sass_make_warning :: CString -> IO (Ptr SassValue) -foreign import ccall unsafe "sass_values.h" sass_delete_value+foreign import ccall unsafe "sass/values.h" sass_delete_value :: Ptr SassValue -> IO () -foreign import ccall unsafe "sass_values.h" sass_clone_value+foreign import ccall unsafe "sass/values.h" sass_clone_value :: Ptr SassValue+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_stringify+ :: Ptr SassValue+ -> Bool+ -> CInt+ -> IO (Ptr SassValue)++foreign import ccall unsafe "sass/values.h" sass_value_op+ :: CInt -- ^ 'SassOp'+ -> Ptr SassValue+ -> Ptr SassValue -> IO (Ptr SassValue)
Bindings/Libsass/Wrappers.hs view
@@ -12,23 +12,23 @@ :: SassFunctionFnType -> IO SassFunctionFn -foreign import ccall "sass_context.h &sass_delete_compiler"+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"+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"+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"+foreign import ccall "sass/functions.h &sass_delete_import_list" p_sass_delete_import_list :: FunPtr (SassImportList -> IO ()) -foreign import ccall "sass_function.h &sass_delete_import"+foreign import ccall "sass/functions.h &sass_delete_import" p_sass_delete_import :: FunPtr (SassImportEntry -> IO ()) -foreign import ccall "sass_function.h &sass_delete_importer"+foreign import ccall "sass/functions.h &sass_delete_importer" p_sass_delete_importer :: FunPtr (SassImporterEntry -> IO ()) -foreign import ccall "sass_values.h &sass_delete_value"+foreign import ccall "sass/valuess.h &sass_delete_value" p_sass_delete_value :: FunPtr (Ptr SassValue -> IO ())
CHANGELOG.md view
@@ -1,6 +1,16 @@ # Change Log All notable changes to this project will be documented in this file. +## [0.1.5.0] - 2015-12-19+### Added+- Bindings to `sass_value_op`, `sass_import_get_imp_path` and+ `sass_import_get_abs_path`++### Changed+- Uses Libsass 3.3.2+- `sass_import_get_path` and `sass_import_get_base` have been removed from+ Libsass+ ## [0.1.4.0] - 2015-07-10 ### Fixed - Setup copies `libsass.a` even when Cabal-1.18 is used.@@ -49,6 +59,7 @@ - Bindings to Libsass C API - Basic tests +[0.1.5.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.4.0...v0.1.5.0 [0.1.4.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.3.0...v0.1.4.0 [0.1.3.0]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.2.1...v0.1.3.0 [0.1.2.1]: https://github.com/jakubfijalkowski/hlibsass/compare/v0.1.2.0...v0.1.2.1
hlibsass.cabal view
@@ -1,5 +1,5 @@ name: hlibsass-version: 0.1.4.0+version: 0.1.5.0 license: MIT license-file: LICENSE author: Jakub Fijałkowski <fiolek94@gmail.com>@@ -18,15 +18,19 @@ 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/src/*.cpp+ libsass/src/*.c+ libsass/src/*.h+ libsass/src/*.hpp+ libsass/src/*.am+ libsass/src/b64/*.h+ libsass/src/utf8/*.h+ libsass/src/support/*.pc.in+ libsass/include/*.h+ libsass/include/sass/*.h+ libsass/include/sass/*.h.in libsass/Makefile+ libsass/Makefile.conf libsass/LICENSE libsass/VERSION CHANGELOG.md
libsass/Makefile view
@@ -3,7 +3,9 @@ RM ?= rm -f CP ?= cp -a MKDIR ?= mkdir+RMDIR ?= rmdir WINDRES ?= windres+INSTALL ?= install CFLAGS ?= -Wall CXXFLAGS ?= -Wall LDFLAGS ?= -Wall@@ -33,48 +35,82 @@ endif endif -ifeq "$(LIBSASS_VERSION)" ""- ifneq "$(wildcard ./.git/ )" ""+ifeq ($(SASS_LIBSASS_PATH),)+ SASS_LIBSASS_PATH = $(abspath $(CURDIR))+endif++ifeq ($(LIBSASS_VERSION),)+ ifneq ($(wildcard ./.git/ ),) LIBSASS_VERSION ?= $(shell git describe --abbrev=4 --dirty --always --tags) endif endif -ifeq "$(LIBSASS_VERSION)" ""- ifneq ("$(wildcard VERSION)","")+ifeq ($(LIBSASS_VERSION),)+ ifneq ($(wildcard VERSION),) LIBSASS_VERSION ?= $(shell $(CAT) VERSION) endif endif -ifneq "$(LIBSASS_VERSION)" ""+ifneq ($(LIBSASS_VERSION),) CFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" CXXFLAGS += -DLIBSASS_VERSION="\"$(LIBSASS_VERSION)\"" endif # enable mandatory flag ifeq (MinGW,$(UNAME))+ ifneq ($(BUILD),shared)+ STATIC_ALL ?= 1+ endif+ STATIC_LIBGCC ?= 1+ STATIC_LIBSTDCPP ?= 1 CXXFLAGS += -std=gnu++0x LDFLAGS += -std=gnu++0x else+ STATIC_ALL ?= 0+ STATIC_LIBGCC ?= 0+ STATIC_LIBSTDCPP ?= 0 CXXFLAGS += -std=c++0x LDFLAGS += -std=c++0x endif -ifneq "$(SASS_LIBSASS_PATH)" ""- CFLAGS += -I $(SASS_LIBSASS_PATH)- CXXFLAGS += -I $(SASS_LIBSASS_PATH)+ifneq ($(SASS_LIBSASS_PATH),)+ CFLAGS += -I $(SASS_LIBSASS_PATH)/include+ CXXFLAGS += -I $(SASS_LIBSASS_PATH)/include+else+ # this is needed for mingw+ CFLAGS += -I include+ CXXFLAGS += -I include endif -ifneq "$(EXTRA_CFLAGS)" ""+ifneq ($(EXTRA_CFLAGS),) CFLAGS += $(EXTRA_CFLAGS) endif-ifneq "$(EXTRA_CXXFLAGS)" ""+ifneq ($(EXTRA_CXXFLAGS),) CXXFLAGS += $(EXTRA_CXXFLAGS) endif-ifneq "$(EXTRA_LDFLAGS)" ""+ifneq ($(EXTRA_LDFLAGS),) LDFLAGS += $(EXTRA_LDFLAGS) endif -LDLIBS = -lstdc++ -lm+LDLIBS = -lm++ifneq ($(BUILD),shared)+ LDLIBS += -lstdc+++endif++# link statically into lib+# makes it a lot more portable+# increases size by about 50KB+ifeq ($(STATIC_ALL),1)+ LDFLAGS += -static+endif+ifeq ($(STATIC_LIBGCC),1)+ LDFLAGS += -static-libgcc+endif+ifeq ($(STATIC_LIBSTDCPP),1)+ LDFLAGS += -static-libstdc+++endif+ ifeq ($(UNAME),Darwin) CFLAGS += -stdlib=libc++ CXXFLAGS += -stdlib=libc++@@ -82,8 +118,10 @@ endif ifneq (MinGW,$(UNAME))- LDFLAGS += -ldl- LDLIBS += -ldl+ ifneq (FreeBSD,$(UNAME))+ LDFLAGS += -ldl+ LDLIBS += -ldl+ endif endif ifneq ($(BUILD),shared)@@ -104,79 +142,62 @@ SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc RUBY_BIN = ruby +LIB_STATIC = $(SASS_LIBSASS_PATH)/lib/libsass.a+LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.so+ ifeq (MinGW,$(UNAME))+ ifeq (shared,$(BUILD))+ CFLAGS += -D ADD_EXPORTS+ CXXFLAGS += -D ADD_EXPORTS+ LIB_SHARED = $(SASS_LIBSASS_PATH)/lib/libsass.dll+ endif+else+ ifneq (Cygwin,$(UNAME))+ CFLAGS += -fPIC+ CXXFLAGS += -fPIC+ LDFLAGS += -fPIC+ endif+endif++ifeq (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+include Makefile.conf RESOURCES =--LIBRARIES = lib/libsass.so-+STATICLIB = lib/libsass.a+SHAREDLIB = lib/libsass.so ifeq (MinGW,$(UNAME))+ RESOURCES += res/resource.rc+ SHAREDLIB = lib/libsass.dll 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+ ifneq (Cygwin,$(UNAME))+ CFLAGS += -fPIC+ CXXFLAGS += -fPIC+ LDFLAGS += -fPIC+ endif endif -OBJECTS = $(SOURCES:.cpp=.o)-COBJECTS = $(CSOURCES:.c=.o)+OBJECTS = $(addprefix src/,$(SOURCES:.cpp=.o))+COBJECTS = $(addprefix src/,$(CSOURCES:.c=.o)) RCOBJECTS = $(RESOURCES:.rc=.o) DEBUG_LVL ?= NONE +CLEANUPS ?=+CLEANUPS += $(RCOBJECTS)+CLEANUPS += $(COBJECTS)+CLEANUPS += $(OBJECTS)+CLEANUPS += $(LIBSASS_LIB)+ all: $(BUILD) debug: $(BUILD)@@ -191,9 +212,6 @@ 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 @@ -220,18 +238,61 @@ install: install-$(BUILD) -install-static: lib/libsass.a- install -pm0755 $< $(DESTDIR)$(PREFIX)/$<+static: $(STATICLIB)+shared: $(SHAREDLIB) -install-shared: lib/libsass.so- install -pm0755 $< $(DESTDIR)$(PREFIX)/$<+$(DESTDIR)$(PREFIX):+ $(MKDIR) $(DESTDIR)$(PREFIX) +$(DESTDIR)$(PREFIX)/lib: $(DESTDIR)$(PREFIX)+ $(MKDIR) $(DESTDIR)$(PREFIX)/lib++$(DESTDIR)$(PREFIX)/include: $(DESTDIR)$(PREFIX)+ $(MKDIR) $(DESTDIR)$(PREFIX)/include++$(DESTDIR)$(PREFIX)/include/sass: $(DESTDIR)$(PREFIX)/include+ $(MKDIR) $(DESTDIR)$(PREFIX)/include/sass++$(DESTDIR)$(PREFIX)/include/%.h: include/%.h \+ $(DESTDIR)$(PREFIX)/include \+ $(DESTDIR)$(PREFIX)/include/sass+ $(INSTALL) -v -m0644 "$<" "$@"++install-headers: $(DESTDIR)$(PREFIX)/include/sass.h \+ $(DESTDIR)$(PREFIX)/include/sass2scss.h \+ $(DESTDIR)$(PREFIX)/include/sass/base.h \+ $(DESTDIR)$(PREFIX)/include/sass/version.h \+ $(DESTDIR)$(PREFIX)/include/sass/values.h \+ $(DESTDIR)$(PREFIX)/include/sass/context.h \+ $(DESTDIR)$(PREFIX)/include/sass/functions.h++$(DESTDIR)$(PREFIX)/lib/%.a: lib/%.a \+ $(DESTDIR)$(PREFIX)/lib+ @$(INSTALL) -v -m0755 "$<" "$@"++$(DESTDIR)$(PREFIX)/lib/%.so: lib/%.so \+ $(DESTDIR)$(PREFIX)/lib+ @$(INSTALL) -v -m0755 "$<" "$@"++$(DESTDIR)$(PREFIX)/lib/%.dll: lib/%.dll \+ $(DESTDIR)$(PREFIX)/lib+ @$(INSTALL) -v -m0755 "$<" "$@"++install-static: $(DESTDIR)$(PREFIX)/lib/libsass.a++install-shared: $(DESTDIR)$(PREFIX)/lib/libsass.so \+ install-headers+ $(SASSC_BIN): $(BUILD)- cd $(SASS_SASSC_PATH) && $(MAKE)+ $(MAKE) -C $(SASS_SASSC_PATH) sassc: $(SASSC_BIN) $(SASSC_BIN) -v +version: $(SASSC_BIN)+ $(SASSC_BIN) -h+ $(SASSC_BIN) -v+ test: $(SASSC_BIN) $(RUBY_BIN) $(SASS_SPEC_PATH)/sass-spec.rb -c $(SASSC_BIN) -s $(LOG_FLAGS) $(SASS_SPEC_PATH)/$(SASS_SPEC_SPEC_DIR) @@ -241,8 +302,32 @@ 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+clean-objects: lib+ -$(RM) lib/*.a lib/*.so lib/*.dll lib/*.la+ -$(RMDIR) lib+clean: clean-objects+ $(RM) $(CLEANUPS) +clean-all:+ $(MAKE) -C $(SASS_SASSC_PATH) clean -.PHONY: all debug debug-static debug-shared static shared install install-static install-shared sassc clean+lib-file: lib-file-$(BUILD)+lib-opts: lib-opts-$(BUILD)++lib-file-static:+ @echo $(LIB_STATIC)+lib-file-shared:+ @echo $(LIB_SHARED)+lib-opts-static:+ @echo -L"$(SASS_LIBSASS_PATH)/lib"+lib-opts-shared:+ @echo -L"$(SASS_LIBSASS_PATH)/lib -lsass"++.PHONY: all static shared sassc \+ version install-headers \+ clean clean-all clean-objects \+ debug debug-static debug-shared \+ install install-static install-shared \+ lib-opts lib-opts-shared lib-opts-static \+ lib-file lib-file-shared lib-file-static+.DELETE_ON_ERROR:
+ libsass/Makefile.conf view
@@ -0,0 +1,47 @@+# this is merely a common Makefile multiple implementers can use++SOURCES = \+ ast.cpp \+ base64vlq.cpp \+ bind.cpp \+ color_maps.cpp \+ constants.cpp \+ context.cpp \+ cssize.cpp \+ emitter.cpp \+ environment.cpp \+ error_handling.cpp \+ eval.cpp \+ expand.cpp \+ extend.cpp \+ file.cpp \+ functions.cpp \+ inspect.cpp \+ json.cpp \+ lexer.cpp \+ listize.cpp \+ memory_manager.cpp \+ node.cpp \+ output.cpp \+ parser.cpp \+ plugins.cpp \+ position.cpp \+ prelexer.cpp \+ remove_placeholders.cpp \+ sass.cpp \+ sass_util.cpp \+ sass_values.cpp \+ sass_context.cpp \+ sass_functions.cpp \+ sass_interface.cpp \+ sass2scss.cpp \+ source_map.cpp \+ to_c.cpp \+ to_string.cpp \+ to_value.cpp \+ units.cpp \+ utf8_string.cpp \+ values.cpp \+ util.cpp++CSOURCES = cencode.c
libsass/VERSION view
@@ -1,1 +1,1 @@-3.2.4-18-g3672+3.3.2
− libsass/ast.cpp
@@ -1,945 +0,0 @@-#include "ast.hpp"-#include "context.hpp"-#include "to_string.hpp"-#include <set>-#include <algorithm>-#include <iostream>--namespace Sass {- using namespace std;-- static Null sass_null(Sass::Null(ParserState("null")));-- 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)- {- Selector* lhs = (*this)[i];- // very special case for wrapped matches selector- if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {- if (wrapped->name() == ":matches(" || wrapped->name() == ":-moz-any(") {- if (Selector_List* list = dynamic_cast<Selector_List*>(wrapped->selector())) {- if (Compound_Selector* comp = dynamic_cast<Compound_Selector*>(rhs)) {- if (list->is_superselector_of(comp)) return true;- }- }- }- }- // match from here on as strings- lset.insert(lhs->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)- {- 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()); }-- // we have to look one tail deeper, since we cary the- // combinator around for it (which is important here)- if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {- Complex_Selector* lhs_tail = lhs->tail();- Complex_Selector* rhs_tail = rhs->tail();- if (lhs_tail->combinator() != rhs_tail->combinator()) return false;- if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;- }--- bool found = false;- Complex_Selector* marker = rhs;- for (size_t i = 0, L = rhs->length(); i < L; ++i) {- if (i == L-1)- { return false; }- if (lhs->head()->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- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Complex_Selector::is_superselector_of(Selector_List *sub)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i])) return false;- }- return true;- }-- // it's a superselector if every selector of the right side- // list is a superselector of the given left side selector- bool Selector_List::is_superselector_of(Selector_List *sub)- {- // Check every rhs selector against left hand list- for(size_t i = 0, L = sub->length(); i < L; ++i) {- if (!is_superselector_of((*sub)[i])) return false;- }- return true;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Compound_Selector *sub)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub)) return true;- }- return false;- }-- // it's a superselector if every selector on the right side- // is a superselector of any one of the left side selectors- bool Selector_List::is_superselector_of(Complex_Selector *sub)- {- // Check every lhs selector against right hand- for(size_t i = 0, L = length(); i < L; ++i) {- if ((*this)[i]->is_superselector_of(sub)) return true;- }- return false;- }-- /* 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;- }*/-- Number::Number(ParserState pstate, double val, string u, bool zero)- : Expression(pstate),- value_(val),- zero_(zero),- numerator_units_(vector<string>()),- denominator_units_(vector<string>()),- hash_(0)- {- size_t l = 0, r = 0;- if (!u.empty()) {- bool nominator = true;- while (true) {- r = u.find_first_of("*/", l);- string unit(u.substr(l, r - l));- if (nominator) numerator_units_.push_back(unit);- else denominator_units_.push_back(unit);- if (u[r] == '/') nominator = false;- if (r == string::npos) break;- else l = r + 1;- }- }- concrete_type(NUMBER);- }-- string Number::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 Number::is_unitless()- { return numerator_units_.empty() && denominator_units_.empty(); }-- void Number::normalize(const string& prefered)- {-- // first make sure same units cancel each other out- // it seems that a map table will fit nicely to do this- // we basically construct exponents for each unit- // has the advantage that they will be pre-sorted- map<string, int> exponents;-- // initialize by summing up occurences in unit vectors- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];-- // the final conversion factor- double factor = 1;-- // get the first entry of numerators- // forward it when entry is converted- vector<string>::iterator nom_it = numerator_units_.begin();- vector<string>::iterator nom_end = numerator_units_.end();- vector<string>::iterator denom_it = denominator_units_.begin();- vector<string>::iterator denom_end = denominator_units_.end();-- // main normalization loop- // should be close to optimal- while (denom_it != denom_end)- {- // get and increment afterwards- const string denom = *(denom_it ++);- // skip already canceled out unit- if (exponents[denom] >= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(denom) == UNKNOWN) continue;- // now search for nominator- while (nom_it != nom_end)- {- // get and increment afterwards- const string nom = *(nom_it ++);- // skip already canceled out unit- if (exponents[nom] <= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(nom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(nom, denom);- // update nominator/denominator exponent- -- exponents[nom]; ++ exponents[denom];- // inner loop done- break;- }- }-- // now we can build up the new unit arrays- numerator_units_.clear();- denominator_units_.clear();-- // build them by iterating over the exponents- for (auto exp : exponents)- {- // maybe there is more effecient way to push- // the same item multiple times to a vector?- for(size_t i = 0, S = abs(exp.second); i < S; ++i)- {- // opted to have these switches in the inner loop- // makes it more readable and should not cost much- if (exp.second < 0) denominator_units_.push_back(exp.first);- else if (exp.second > 0) numerator_units_.push_back(exp.first);- }- }-- // apply factor to value_- // best precision this way- value_ *= factor;-- // maybe convert to other unit- // easier implemented on its own- try { convert(prefered); }- catch (incompatibleUnits& err)- { error(err.what(), pstate()); }- catch (...) { throw; }-- }-- void Number::convert(const string& prefered)- {- // abort if unit is empty- if (prefered.empty()) return;-- // first make sure same units cancel each other out- // it seems that a map table will fit nicely to do this- // we basically construct exponents for each unit- // has the advantage that they will be pre-sorted- map<string, int> exponents;-- // initialize by summing up occurences in unit vectors- for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];-- // the final conversion factor- double factor = 1;-- vector<string>::iterator denom_it = denominator_units_.begin();- vector<string>::iterator denom_end = denominator_units_.end();-- // main normalization loop- // should be close to optimal- while (denom_it != denom_end)- {- // get and increment afterwards- const string denom = *(denom_it ++);- // check if conversion is needed- if (denom == prefered) continue;- // skip already canceled out unit- if (exponents[denom] >= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(denom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(denom, prefered);- // update nominator/denominator exponent- ++ exponents[denom]; -- exponents[prefered];- }-- vector<string>::iterator nom_it = numerator_units_.begin();- vector<string>::iterator nom_end = numerator_units_.end();-- // now search for nominator- while (nom_it != nom_end)- {- // get and increment afterwards- const string nom = *(nom_it ++);- // check if conversion is needed- if (nom == prefered) continue;- // skip already canceled out unit- if (exponents[nom] <= 0) continue;- // skip all units we don't know how to convert- if (string_to_unit(nom) == UNKNOWN) continue;- // we now have two convertable units- // add factor for current conversion- factor *= conversion_factor(nom, prefered);- // update nominator/denominator exponent- -- exponents[nom]; ++ exponents[prefered];- }-- // now we can build up the new unit arrays- numerator_units_.clear();- denominator_units_.clear();-- // build them by iterating over the exponents- for (auto exp : exponents)- {- // maybe there is more effecient way to push- // the same item multiple times to a vector?- for(size_t i = 0, S = abs(exp.second); i < S; ++i)- {- // opted to have these switches in the inner loop- // makes it more readable and should not cost much- if (exp.second < 0) denominator_units_.push_back(exp.first);- else if (exp.second > 0) numerator_units_.push_back(exp.first);- }- }-- // apply factor to value_- // best precision this way- value_ *= factor;-- }-- // useful for making one number compatible with another- string Number::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) != UNKNOWN) return u;- }- for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {- string u(denominator_units_[i]);- if (string_to_unit(u) != UNKNOWN) return u;- }- return string();- }--- bool Number::operator== (Expression* rhs) const- {- try- {- Number l(pstate_, value_, unit());- Number& r = dynamic_cast<Number&>(*rhs);- l.normalize(find_convertible_unit());- r.normalize(find_convertible_unit());- return l.unit() == r.unit() &&- l.value() == r.value();- }- catch (std::bad_cast&) {}- catch (...) { throw; }- return false;- }-- bool Number::operator== (Expression& rhs) const- {- return operator==(&rhs);- }-- bool List::operator==(Expression* rhs) const- {- try- {- List* r = dynamic_cast<List*>(rhs);- if (!r || length() != r->length()) return false;- if (separator() != r->separator()) return false;- for (size_t i = 0, L = r->length(); i < L; ++i)- if (*elements()[i] != *(*r)[i]) return false;- return true;- }- catch (std::bad_cast&) {}- catch (...) { throw; }- return false;- }-- bool List::operator== (Expression& rhs) const- {- return operator==(&rhs);- }-- size_t List::size() const {- if (!is_arglist_) return length();- // arglist expects a list of arguments- // so we need to break before keywords- for (size_t i = 0, L = length(); i < L; ++i) {- if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {- if (!arg->name().empty()) return i;- }- }- return length();- }-- Expression* Hashed::at(Expression* k) const- {- if (elements_.count(k))- { return elements_.at(k); }- else { return &sass_null; }- }--}-
− libsass/ast.hpp
@@ -1,2141 +0,0 @@-#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 void set_delayed(bool delayed) { is_delayed(delayed); }- 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;- bool has_duplicate_key() const { return duplicate_key_ != 0; }- Expression* get_duplicate_key() const { return duplicate_key_; }- const unordered_map<Expression*, Expression*> elements() { return elements_; }- 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 size_t size() const;- virtual bool operator==(Expression& rhs) const;- virtual bool operator==(Expression* rhs) const;-- 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_;- }-- virtual void set_delayed(bool delayed)- {- for (size_t i = 0, L = length(); i < L; ++i)- (elements()[i])->set_delayed(delayed);- is_delayed(delayed);- }-- 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);- size_t hash_;- public:- Binary_Expression(ParserState pstate,- Type t, Expression* lhs, Expression* rhs)- : Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0)- { }- const string type_name() {- switch (type_) {- case AND: return "and"; break;- case OR: return "or"; break;- case EQ: return "eq"; break;- case NEQ: return "neq"; break;- case GT: return "gt"; break;- case GTE: return "gte"; break;- case LT: return "lt"; break;- case LTE: return "lte"; break;- case ADD: return "add"; break;- case SUB: return "sub"; break;- case MUL: return "mul"; break;- case DIV: return "div"; break;- case MOD: return "mod"; break;- case NUM_OPS: return "num_ops"; break;- default: return "invalid"; break;- }- }- virtual void set_delayed(bool delayed)- {- right()->set_delayed(delayed);- left()->set_delayed(delayed);- is_delayed(delayed);- }- virtual bool operator==(Expression& rhs) const- {- try- {- Binary_Expression& m = dynamic_cast<Binary_Expression&>(rhs);- if (m == 0) return false;- return type() == m.type() &&- left() == m.left() &&- right() == m.right();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ > 0) return hash_;- hash_ = left()->hash() ^ right()->hash() ^ std::hash<size_t>()(type_);- return hash_;- }- 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);- size_t hash_;- public:- Unary_Expression(ParserState pstate, Type t, Expression* o)- : Expression(pstate), type_(t), operand_(o), hash_(0)- { }- const string type_name() {- switch (type_) {- case PLUS: return "plus"; break;- case MINUS: return "minus"; break;- case NOT: return "not"; break;- default: return "invalid"; break;- }- }- virtual bool operator==(Expression& rhs) const- {- try- {- Unary_Expression& m = dynamic_cast<Unary_Expression&>(rhs);- if (m == 0) return false;- return type() == m.type() &&- operand() == m.operand();- }- catch (std::bad_cast&)- {- return false;- }- catch (...) { throw; }- }- virtual size_t hash()- {- if (hash_ > 0) return hash_;- hash_ = operand()->hash() ^ std::hash<size_t>()(type_);- return hash_;- }- 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() == *m.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);- 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;-- bool is_unitless();- void convert(const string& unit = "");- void normalize(const string& unit = "");- // useful for making one number compatible with another- string find_convertible_unit() const;-- virtual bool operator== (Expression& rhs) const;- virtual bool operator== (Expression* rhs) const;-- 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(bool, can_compress_whitespace);- ADD_PROPERTY(string, value);- protected:- size_t hash_;- public:- String_Constant(ParserState pstate, string val)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(string(beg))), hash_(0)- { }- String_Constant(ParserState pstate, const char* beg, const char* end)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(string(beg, end-beg))), hash_(0)- { }- String_Constant(ParserState pstate, const Token& tok)- : String(pstate), quote_mark_(0), can_compress_whitespace_(false), 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* sub);- // bool is_superselector_of(Complex_Selector* sub);- // bool is_superselector_of(Selector_List* sub);- 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* sub);- bool is_superselector_of(Complex_Selector* sub);- bool is_superselector_of(Selector_List* sub);- // 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();- bool is_superselector_of(Compound_Selector* sub);- bool is_superselector_of(Complex_Selector* sub);- bool is_superselector_of(Selector_List* sub);- 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
@@ -1,21 +0,0 @@-#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
@@ -1,92 +0,0 @@-#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
@@ -1,85 +0,0 @@-#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
@@ -1,32 +0,0 @@-/*-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
@@ -1,77 +0,0 @@-// :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
@@ -1,77 +0,0 @@-#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 + 1- << " of "- << rel_path;- } else {- ss << endl- << "\t"- << rel_path- << ":"- << this_point->pstate.line + 1- << 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
@@ -1,43 +0,0 @@-#include "base64vlq.hpp"--namespace Sass {-- string Base64VLQ::encode(const int number) const- {- string encoded = "";-- int vlq = to_vlq_signed(number);-- do {- int digit = vlq & VLQ_BASE_MASK;- vlq >>= VLQ_BASE_SHIFT;- if (vlq > 0) {- digit |= VLQ_CONTINUATION_BIT;- }- encoded += base64_encode(digit);- } while (vlq > 0);-- return encoded;- }-- char Base64VLQ::base64_encode(const int number) const- {- int index = number;- if (index < 0) index = 0;- if (index > 63) index = 63;- return CHARACTERS[index];- }-- int Base64VLQ::to_vlq_signed(const int number) const- {- return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0;- }-- const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";-- const int Base64VLQ::VLQ_BASE_SHIFT = 5;- const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT;- const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1;- const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE;--}
− libsass/base64vlq.hpp
@@ -1,31 +0,0 @@-#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
@@ -1,255 +0,0 @@-#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) {- Argument* a = (*as)[ia];- if (ip >= LP) {- // skip empty rest arguments- if (a->is_rest_argument()) {- if (List* l = dynamic_cast<List*>(a->value())) {- if (l->length() == 0) {- ++ ia; continue;- }- }- }- stringstream msg;- msg << callee << " only takes " << LP << " arguments; "- << "given " << LA;- error(msg.str(), as->pstate());- }- Parameter* p = (*ps)[ip];-- // If the current parameter is the rest parameter, process and break the loop- if (p->is_rest_parameter()) {- // The next argument by coincidence provides a rest argument- if (a->is_rest_argument()) {- // We should always get a list for rest arguments- if (List* rest = dynamic_cast<List*>(a->value())) {- // arg contains a list- List* args = rest;- // make sure it's an arglist- if (rest->is_arglist()) {- // can pass it through as it was- env->local_frame()[p->name()] = args;- }- // create a new list and wrap each item as an argument- // otherwise we will not be able to fetch it again- else {- // create a new list object for wrapped items- List* arglist = new (ctx.mem) List(p->pstate(),- 0,- rest->separator(),- true);- // wrap each item from list as an argument- for (Expression* item : rest->elements()) {- (*arglist) << new (ctx.mem) Argument(item->pstate(),- item,- "",- false,- false);- }- // assign new arglist to environment- env->local_frame()[p->name()] = arglist;- }- }- // invalid state- else {- throw runtime_error("invalid state");- }- } else if (a->is_keyword_argument()) {-- // expand keyword arguments into their parameters- List* arglist = new (ctx.mem) List(p->pstate(), 0, List::COMMA, true);- env->local_frame()[p->name()] = arglist;- Map* argmap = static_cast<Map*>(a->value());- for (auto key : argmap->keys()) {- string name = unquote(static_cast<String_Constant*>(key)->value());- (*arglist) << new (ctx.mem) Argument(key->pstate(),- argmap->at(key),- name,- false,- false);- }-- } else {-- // create a new list object for wrapped items- List* arglist = new (ctx.mem) List(p->pstate(),- 0,- List::COMMA,- true);- // consume the next args- while (ia < LA) {- // get and post inc- a = (*as)[ia++];- // wrap current argument into new object- (*arglist) << new (ctx.mem) Argument(a->pstate(),- a->value(),- a->name(),- false,- false);- // check if we have rest argument- if (a->is_rest_argument()) {- // preserve the list separator from rest args- if (List* rest = dynamic_cast<List*>(a->value())) {- arglist->separator(rest->separator());- }- // no more arguments- break;- }- }- // assign new arglist to environment- env->local_frame()[p->name()] = arglist;- }- // consumed parameter- ++ip;- // no more paramaters- 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,- 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();- }- }- // EO while ia-- // 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
@@ -1,18 +0,0 @@-#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
@@ -1,102 +0,0 @@-/*-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
@@ -1,327 +0,0 @@-#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
@@ -1,168 +0,0 @@-#include "constants.hpp"--namespace Sass {- namespace Constants {-- extern const unsigned long MaxCallStack = 1024;-- // 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
@@ -1,173 +0,0 @@-#ifndef SASS_CONSTANTS_H-#define SASS_CONSTANTS_H--namespace Sass {- namespace Constants {-- // The maximum call stack that can be created- extern const unsigned long MaxCallStack;-- // 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
@@ -1,561 +0,0 @@-#ifdef _WIN32-#define PATH_SEP ';'-#else-#define PATH_SEP ':'-#endif--#include <string>-#include <cstdlib>-#include <cstring>-#include <iomanip>-#include <sstream>-#include <iostream>--#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"--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, 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);- // Selector functions- register_function(ctx, is_superselector_sig, is_superselector, 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
@@ -1,157 +0,0 @@-#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
@@ -1,148 +0,0 @@-#include "contextualize.hpp"-#include "ast.hpp"-#include "eval.hpp"-#include "backtrace.hpp"-#include "to_string.hpp"-#include "parser.hpp"--namespace Sass {-- Contextualize::Contextualize(Context& ctx, 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());- if (s->length() == 0) {- Complex_Selector* comb = static_cast<Complex_Selector*>(parent->perform(this));- if (parent->has_line_feed()) comb->has_line_feed(true);- if (comb) *ss << comb;- else cerr << "Warning: contextualize returned null" << endl;- }- 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 cerr << "Warning: contextualize returned null" << endl;- }- }- }- 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
@@ -1,46 +0,0 @@-#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
@@ -1,93 +0,0 @@-#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(parent, 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
@@ -1,44 +0,0 @@-#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
@@ -1,551 +0,0 @@-#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));- if (mq->length()) {- 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
@@ -1,82 +0,0 @@-#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
@@ -1,39 +0,0 @@-#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
@@ -1,558 +0,0 @@-#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 string pstate_source_position(AST_Node* node)-{- stringstream str;- Position start(node->pstate());- Position end(start + node->pstate().offset);- str << (start.file == string::npos ? -1 : start.file)- << "@[" << start.line << ":" << start.column << "]"- << "-[" << end.line << ":" << end.column << "]";- return str.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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << bubble->tabs();- cerr << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << root_block->tabs();- cerr << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [block:" << selector->last_block() << "]";- 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 << 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 << " (" << pstate_source_position(node) << ")";- 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- << " (" << pstate_source_position(node) << ")"- << " [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 << " (" << pstate_source_position(node) << ")";- 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " @ref " << selector->selector() << endl;- } else if (dynamic_cast<Simple_Selector*>(node)) {- Simple_Selector* selector = dynamic_cast<Simple_Selector*>(node);- cerr << ind << "Simple_Selector " << selector;- cerr << " (" << pstate_source_position(node) << ")";- cerr << (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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << (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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << (block->is_interpolated() ? " [is_interpolated]": " -")- << endl;- debug_ast(block->feature(), ind + " feature) ");- debug_ast(block->value(), ind + " value) ");-- } else if (dynamic_cast<Media_Query*>(node)) {- Media_Query* block = dynamic_cast<Media_Query*>(node);- cerr << ind << "Media_Query " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << (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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- if (root_block->is_root()) cerr << " [root]";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Error*>(node)) {- Error* block = dynamic_cast<Error*>(node);- cerr << ind << "Error " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Debug*>(node)) {- Debug* block = dynamic_cast<Debug*>(node);- cerr << ind << "Debug " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Comment*>(node)) {- Comment* block = dynamic_cast<Comment*>(node);- cerr << ind << "Comment " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- debug_ast(block->predicate(), ind + " = ");- debug_ast(block->consequent(), ind + " <>");- debug_ast(block->alternative(), ind + " ><");- } else if (dynamic_cast<Return*>(node)) {- Return* block = dynamic_cast<Return*>(node);- cerr << ind << "Return " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Extension*>(node)) {- Extension* block = dynamic_cast<Extension*>(node);- cerr << ind << "Extension " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Import_Stub*>(node)) {- Import_Stub* block = dynamic_cast<Import_Stub*>(node);- cerr << ind << "Import_Stub " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- } else if (dynamic_cast<Import*>(node)) {- Import* block = dynamic_cast<Import*>(node);- cerr << ind << "Import " << block;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << block->tabs() << endl;- // debug_ast(block->media_queries(), ind + " @ ");- // 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " <<" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [name: " << block->name() << "] ";- cerr << " [type: " << (block->type() == Sass::Definition::Type::MIXIN ? "Mixin " : "Function ") << "] ";- // this seems to lead to segfaults some times?- // cerr << " [signature: " << block->signature() << "] ";- cerr << " [native: " << block->native_function() << "] ";- cerr << " " << block->tabs() << endl;- debug_ast(block->parameters(), ind + " params: ", env);- 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();- cerr << " [" << block->name() << "]" << endl;- debug_ast(block->arguments(), ind + " args: ");- 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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() << "]";- if (expression->is_delayed()) cerr << " [delayed]";- cerr << endl;- } else if (dynamic_cast<Variable*>(node)) {- Variable* expression = dynamic_cast<Variable*>(node);- cerr << ind << "Variable " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << "" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- if (expression->is_delayed()) cerr << " [delayed]";- cerr << " (" << pstate_source_position(node) << ")";- cerr << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << expression->value() << "]";- cerr << " [name: " << expression->name() << "] ";- cerr << " [rest: " << expression->is_rest_argument() << "] ";- cerr << " [keyword: " << expression->is_keyword_argument() << "] " << endl;- debug_ast(expression->value(), ind + " value: ", env);- } else if (dynamic_cast<Parameters*>(node)) {- Parameters* expression = dynamic_cast<Parameters*>(node);- cerr << ind << "Parameters " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";- cerr << " [has_rest: " << expression->has_rest_parameter() << "] ";- cerr << endl;- for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }- } else if (dynamic_cast<Parameter*>(node)) {- Parameter* expression = dynamic_cast<Parameter*>(node);- cerr << ind << "Parameter " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [name: " << expression->name() << "] ";- cerr << " [default: " << expression->default_value() << "] ";- cerr << " [rest: " << expression->is_rest_parameter() << "] " << endl;- } else if (dynamic_cast<Unary_Expression*>(node)) {- Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);- cerr << ind << "Unary_Expression " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << expression->type_name() << "]" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [Hashed]" << endl;- // for (auto i : expression->elements()) {- // debug_ast(i.first, ind + " key: ");- // debug_ast(i.second, ind + " val: ");- // }- } else if (dynamic_cast<List*>(node)) {- List* expression = dynamic_cast<List*>(node);- cerr << ind << "List " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " (" << expression->length() << ") " <<- (expression->separator() == Sass::List::Separator::COMMA ? "Comma " : "Space ") <<- " [delayed: " << expression->is_delayed() << "] " <<- " [interpolant: " << expression->is_interpolant() << "] " <<- " [arglist: " << expression->is_arglist() << "] " <<- 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [Statement]" << endl;- } else if (dynamic_cast<Boolean*>(node)) {- Boolean* expression = dynamic_cast<Boolean*>(node);- cerr << ind << "Boolean " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << expression->value() << "]" << endl;- } else if (dynamic_cast<Color*>(node)) {- Color* expression = dynamic_cast<Color*>(node);- cerr << ind << "Color " << expression;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << prettyprint(expression->value()) << "]";- if (expression->is_delayed()) cerr << " [delayed]";- if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";- if (expression->quote_mark()) cerr << " [quote_mark: " << expression->quote_mark() << "]";- cerr << " <" << 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;- if (expression->concrete_type()) {- cerr << " " << expression->concrete_type();- }- cerr << " (" << pstate_source_position(node) << ")";- cerr << " [" << prettyprint(expression->value()) << "]";- if (expression->is_delayed()) cerr << " [delayed]";- if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";- cerr << " <" << 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;- cerr << " " << expression->concrete_type();- if (expression->is_delayed()) cerr << " [delayed]";- if (expression->has_interpolants()) cerr << " [has_interpolants]";- cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << 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;- cerr << " " << expression->concrete_type();- cerr << " (" << pstate_source_position(node) << ")";- if (expression->sass_fix_1291()) cerr << " [sass_fix_1291]";- cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << endl;- } else if (dynamic_cast<Expression*>(node)) {- Expression* expression = dynamic_cast<Expression*>(node);- cerr << ind << "Expression " << expression;- cerr << " (" << pstate_source_position(node) << ")";- 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << 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;- cerr << " (" << pstate_source_position(node) << ")";- cerr << " " << statement->tabs() << endl;- }-- if (ind == "") cerr << "####################################################################\n";-}--#endif // SASS_DEBUGGER
− libsass/emitter.cpp
@@ -1,258 +0,0 @@-#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_space_array(false),- in_comma_array(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) || scheduled_delimiter) {- 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
@@ -1,89 +0,0 @@-#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:- // output strings different in comments- bool in_comment;- // selector list does not get linefeeds- bool in_wrapped;- // lists always get a space after delimiter- bool in_media_block;- // nested list must not have parentheses- bool in_declaration;- // nested lists need parentheses- bool in_space_array;- bool in_comma_array;-- 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
@@ -1,163 +0,0 @@-#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)- {- auto cur = this;- while (cur->is_lexical()) {- if (cur->has_local(key)) {- cur->set_local(key, val);- return;- }- cur = cur->parent_;- }- 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
@@ -1,35 +0,0 @@-#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 warn(string msg, ParserState pstate)- {- cerr << "Warning: " << msg<< endl;- }-- void warn(string msg, ParserState pstate, Backtrace* bt)- {- Backtrace top(bt, pstate, "");- msg += top.to_string();- warn(msg, pstate);- }-- 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 += "\n" + top.to_string();- error(msg, pstate);- }--}
− libsass/error_handling.hpp
@@ -1,32 +0,0 @@-#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 warn(string msg, ParserState pstate);- void warn(string msg, ParserState pstate, Backtrace* bt);-- void error(string msg, ParserState pstate);- void error(string msg, ParserState pstate, Backtrace* bt);--}--#endif
− libsass/eval.cpp
@@ -1,1379 +0,0 @@-#include <cstdlib>-#include <cmath>-#include <iostream>-#include <sstream>-#include <iomanip>-#include <typeinfo>--#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"--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()) {- if (a->is_default()) {- if (env->has_global(var)) {- Expression* e = dynamic_cast<Expression*>(env->get_global(var));- if (!e || 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_global(var, a->value()->perform(this));- }- }- else if (a->is_default()) {- if (env->has_lexical(var)) {- auto cur = env;- while (cur && cur->is_lexical()) {- if (cur->has_local(var)) {- if (AST_Node* node = cur->get_local(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- cur->set_local(var, a->value()->perform(this));- }- }- else {- throw runtime_error("Env not in sync");- }- return 0;- }- cur = cur->parent();- }- throw runtime_error("Env not in sync");- }- else if (env->has_global(var)) {- if (AST_Node* node = env->get_global(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- env->set_global(var, a->value()->perform(this));- }- }- }- else if (env->is_lexical()) {- env->set_local(var, a->value()->perform(this));- }- else {- env->set_local(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->has_local(variable) ? env->get_local(variable) : 0;- 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->has_local(variables[i]) ? env->get_local(variables[i]) : 0;- 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));- }-- // check for duplicate keys- if (mm->has_duplicate_key()) {- To_String to_string(&ctx);- error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());- }-- 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);- // maybe fully evaluate structure- if (op_type == Binary_Expression::EQ ||- op_type == Binary_Expression::NEQ ||- op_type == Binary_Expression::GT ||- op_type == Binary_Expression::GTE ||- op_type == Binary_Expression::LT ||- op_type == Binary_Expression::LTE)- {- rhs->is_expanded(false);- rhs->set_delayed(false);- rhs = rhs->perform(this);- }- else- {- rhs->is_delayed(false);- 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)- {- if (backtrace->parent != NULL && backtrace->depth() > Constants::MaxCallStack) {- ostringstream stm;- stm << "Stack depth exceeded max of " << Constants::MaxCallStack;- error(stm.str(), c->pstate(), backtrace);- }- 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)) {- if (str->quote_mark()) {- value = new (ctx.mem) String_Quoted(str->pstate(), str->perform(&to_string));- } else {- value = new (ctx.mem) String_Constant(str->pstate(), unquote(str->value()));- }- }- }- 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)- {- n->normalize();- // 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()) {- if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {- return interpolation(new (ctx.mem) String_Constant(*str_quoted));- } else {- return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));- }- } else {- return evacuate_escapes(str_quoted->value());- }- } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {- string str = str_constant->value();- if (!str_constant->quote_mark()) str = unquote(str);- return evacuate_escapes(str);- } 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)) {- var->is_delayed(false);- 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) {- // if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>((*s)[i])) {- // if (!str_quoted->is_delayed()) str_quoted->value(string_eval_escapes(str_quoted->value()));- // }- 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);- if (feature && dynamic_cast<String_Quoted*>(feature)) {- feature = new (ctx.mem) String_Constant(feature->pstate(),- dynamic_cast<String_Quoted*>(feature)->value());- }- Expression* value = e->value();- value = (value ? value->perform(this) : 0);- if (value && dynamic_cast<String_Quoted*>(value)) {- value = new (ctx.mem) String_Constant(value->pstate(),- dynamic_cast<String_Quoted*>(value)->value());- }- 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)- {- // no idea why both calls are needed- Selector* s = p->perform(contextualize);- if (!s) s = p->selector()->perform(contextualize);- // access to parent selector may return 0- Selector_List* l = static_cast<Selector_List*>(s);- // some spec tests cause this (might be a valid case!)- // if (!s) { cerr << "Parent Selector eval error" << endl; }- 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: {- return *static_cast<Number*>(lhs) ==- *static_cast<Number*>(rhs);- } 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: {- string slhs = static_cast<String_Quoted*>(lhs)->value();- string srhs = static_cast<String_Quoted*>(rhs)->value();- return unquote(slhs) == unquote(srhs) &&- (!(is_quoted(slhs) || is_quoted(srhs)) || slhs[0] == srhs[0]);- } 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();- }-- if (op == Binary_Expression::MUL) {- v->value(ops[op](lv, rv));- 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) {- v->value(ops[op](lv, rv));- 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]);- }- } else {- v->value(ops[op](lv, tmp.value()));- }- 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
@@ -1,88 +0,0 @@-#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
@@ -1,575 +0,0 @@-#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(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()) {- if (a->is_default()) {- if (env->has_global(var)) {- Expression* e = dynamic_cast<Expression*>(env->get_global(var));- if (!e || 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_global(var, a->value()->perform(eval->with(p, env, backtrace)));- }- }- else if (a->is_default()) {- if (env->has_lexical(var)) {- auto cur = env;- while (cur && cur->is_lexical()) {- if (cur->has_local(var)) {- if (AST_Node* node = cur->get_local(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- cur->set_local(var, a->value()->perform(eval->with(p, env, backtrace)));- }- }- else {- throw runtime_error("Env not in sync");- }- return 0;- }- cur = cur->parent();- }- throw runtime_error("Env not in sync");- }- else if (env->has_global(var)) {- if (AST_Node* node = env->get_global(var)) {- Expression* e = dynamic_cast<Expression*>(node);- if (!e || e->concrete_type() == Expression::NULL_VAL) {- env->set_global(var, a->value()->perform(eval->with(p, env, backtrace)));- }- }- }- else if (env->is_lexical()) {- env->set_local(var, a->value()->perform(eval->with(p, env, backtrace)));- }- else {- env->set_local(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->has_local(variable) ? env->get_local(variable) : 0;- 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->has_local(variables[i]) ? env->get_local(variables[i]) : 0;- 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
@@ -1,78 +0,0 @@-#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
@@ -1,2010 +0,0 @@-#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
@@ -1,45 +0,0 @@-#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
@@ -1,349 +0,0 @@-#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
@@ -1,60 +0,0 @@-#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
@@ -1,1592 +0,0 @@-#include "functions.hpp"-#include "ast.hpp"-#include "context.hpp"-#include "backtrace.hpp"-#include "parser.hpp"-#include "constants.hpp"-#include "to_string.hpp"-#include "inspect.hpp"-#include "eval.hpp"-#include "util.hpp"-#include "utf8_string.hpp"-#include "utf8.h"--#include <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::round(w1*color1->r() + w2*color2->r()),- std::round(w1*color1->g() + w2*color2->g()),- std::round(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 / (max + min);- else s = del / (2.0 - max - min);-- if (r == max) h = (g - b) / del + (g < b ? 6 : 0);- else if (g == max) h = (b - r) / del + 2;- else if (b == max) h = (r - g) / del + 4;- }-- HSL hsl_struct;- hsl_struct.h = h / 6 * 360;- 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;-- if (l < 0) l = 0;- if (s < 0) s = 0;- if (l > 1) l = 1;- if (s > 1) s = 1;- while (h < 0) h += 1;- while (h > 1) h -= 1;-- // 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.0)-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)- {- AST_Node* arg = env["$string"];- if (dynamic_cast<Null*>(arg)) {- return new (ctx.mem) Null(pstate);- }- else 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;- }- To_String to_string(&ctx);- return new (ctx.mem) String_Constant(pstate, unquote(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);- double start_at = ARG("$start-at", Number)->value();- double end_at = ARG("$end-at", Number)->value();-- string str = unquote(s->value());-- size_t size = utf8::distance(str.begin(), str.end());- if (end_at <= size * -1.0) { end_at += size; }- if (end_at < 0) { end_at += size + 1; }- if (end_at > size) { end_at = size; }- if (start_at < 0) { start_at += size + 1; }- else if (start_at == 0) { ++ start_at; }-- if (start_at <= end_at)- {- string::iterator start = str.begin();- utf8::advance(start, start_at - 1, str.end());- string::iterator end = start;- utf8::advance(end, end_at - start_at + 1, str.end());- newstr = string(start, 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) {- if (trunc(l->value()) != l->value() || l->value() == 0) error("argument $limit of `" + string(sig) + "` must be a positive integer", pstate);- 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);- double distributed = static_cast<double>(distributor(rand));- return new (ctx.mem) Number(pstate, 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->size() : 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;- bool is_arglist = l->is_arglist();- result->is_arglist(is_arglist);- if (is_arglist) {- *result << new (ctx.mem) Argument(v->pstate(),- v,- "",- false,- false);-- } else {- *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 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);- for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {- string name = string(((Argument*)(*arglist)[i])->name());- name = name.erase(0, 1); // sanitize name (remove dollar sign)- *result << make_pair(new (ctx.mem) String_Constant(pstate, 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*>((*arglist)[i]);- *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 {- bool parentheses = v->concrete_type() == Expression::MAP ||- v->concrete_type() == Expression::LIST;- Output_Style old_style;- old_style = ctx.output_style;- ctx.output_style = NESTED;- To_String to_string(&ctx, false);- string inspect = v->perform(&to_string);- if (inspect.empty() && parentheses) inspect = "()";- ctx.output_style = old_style;- return new (ctx.mem) String_Constant(pstate, inspect);--- }- // return v;- }-- Signature is_superselector_sig = "is-superselector($super, $sub)";- BUILT_IN(is_superselector)- {- To_String to_string(&ctx, false);- Expression* ex_sup = ARG("$super", Expression);- Expression* ex_sub = ARG("$sub", Expression);- string sup_src = ex_sup->perform(&to_string) + "{";- string sub_src = ex_sub->perform(&to_string) + "{";- Selector_List* sel_sup = Parser::parse_selector(sup_src.c_str(), ctx);- Selector_List* sel_sub = Parser::parse_selector(sub_src.c_str(), ctx);- bool result = sel_sup->is_superselector_of(sel_sub);- return new (ctx.mem) Boolean(pstate, result);- }-- 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
@@ -1,184 +0,0 @@-#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 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;- extern Signature is_superselector_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(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);- BUILT_IN(is_superselector);-- }-}--#endif
+ libsass/include/sass.h view
@@ -0,0 +1,15 @@+#ifndef SASS_H+#define SASS_H++// #define DEBUG 1++// include API headers+#include <sass/base.h>+#include <sass/version.h>+#include <sass/values.h>+#include <sass/functions.h>+#include <sass/context.h>+#include <sass2scss.h>++#endif+
+ libsass/include/sass/base.h view
@@ -0,0 +1,76 @@+#ifndef SASS_BASE_H+#define SASS_BASE_H++#ifdef _MSC_VER+ #pragma warning(disable : 4503)+ #ifndef _SCL_SECURE_NO_WARNINGS+ #define _SCL_SECURE_NO_WARNINGS+ #endif+ #ifndef _CRT_SECURE_NO_WARNINGS+ #define _CRT_SECURE_NO_WARNINGS+ #endif+ #ifndef _CRT_NONSTDC_NO_DEPRECATE+ #define _CRT_NONSTDC_NO_DEPRECATE+ #endif+#endif++#include <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++/* 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/include/sass/context.h view
@@ -0,0 +1,152 @@+#ifndef SASS_C_CONTEXT_H+#define SASS_C_CONTEXT_H++#include <stddef.h>+#include <stdbool.h>+#include <sass/base.h>+#include <sass/values.h>+#include <sass/functions.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 struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler);+ADDAPI size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler);+ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler);+ADDAPI Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx);++// Push function for paths (no manipulation support for now)+ADDAPI void ADDCALL sass_option_push_plugin_path (struct Sass_Options* options, const char* path);+ADDAPI void ADDCALL sass_option_push_include_path (struct Sass_Options* options, const char* path);++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/include/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/base.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_Compiler* compiler);+++// Creator for sass custom importer return argument list+ADDAPI Sass_Importer_List ADDCALL sass_make_importer_list (size_t length);+ADDAPI Sass_Importer_Entry ADDCALL sass_importer_get_list_entry (Sass_Importer_List list, size_t idx);+ADDAPI void ADDCALL sass_importer_set_list_entry (Sass_Importer_List list, size_t idx, Sass_Importer_Entry entry);+++// Creators for custom importer callback (with some additional pointer)+// The pointer is mostly used to store the callback into the actual binding+ADDAPI Sass_Importer_Entry ADDCALL sass_make_importer (Sass_Importer_Fn importer, double priority, void* cookie);++// Getters for import function descriptors+ADDAPI Sass_Importer_Fn ADDCALL sass_importer_get_function (Sass_Importer_Entry cb);+ADDAPI double ADDCALL sass_importer_get_priority (Sass_Importer_Entry cb);+ADDAPI void* ADDCALL sass_importer_get_cookie (Sass_Importer_Entry cb);++// Deallocator for associated memory+ADDAPI void ADDCALL sass_delete_importer (Sass_Importer_Entry cb);++// Creator for sass custom importer return argument list+ADDAPI Sass_Import_List ADDCALL sass_make_import_list (size_t length);+// Creator for a single import entry returned by the custom importer inside the list+ADDAPI Sass_Import_Entry ADDCALL sass_make_import_entry (const char* path, char* source, char* srcmap);+ADDAPI Sass_Import_Entry ADDCALL sass_make_import (const char* imp_path, const char* abs_base, char* source, char* srcmap);+// set error message to abort import and to print out a message (path from existing object is used in output)+ADDAPI Sass_Import_Entry ADDCALL sass_import_set_error(Sass_Import_Entry import, const char* message, size_t line, size_t col);++// Setters to insert an entry into the import list (you may also use [] access directly)+// Since we are dealing with pointers they should have a guaranteed and fixed size+ADDAPI void ADDCALL sass_import_set_list_entry (Sass_Import_List list, size_t idx, Sass_Import_Entry entry);+ADDAPI Sass_Import_Entry ADDCALL sass_import_get_list_entry (Sass_Import_List list, size_t idx);++// Getters for import entry+ADDAPI const char* ADDCALL sass_import_get_imp_path (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_abs_path (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_source (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_srcmap (Sass_Import_Entry);+// Explicit functions to take ownership of these items+// The property on our struct will be reset to NULL+ADDAPI char* ADDCALL sass_import_take_source (Sass_Import_Entry);+ADDAPI char* ADDCALL sass_import_take_srcmap (Sass_Import_Entry);+// Getters from import error entry+ADDAPI size_t ADDCALL sass_import_get_error_line (Sass_Import_Entry);+ADDAPI size_t ADDCALL sass_import_get_error_column (Sass_Import_Entry);+ADDAPI const char* ADDCALL sass_import_get_error_message (Sass_Import_Entry);++// Deallocator for associated memory (incl. entries)+ADDAPI void ADDCALL sass_delete_import_list (Sass_Import_List);+// Just in case we have some stray import structs+ADDAPI void ADDCALL sass_delete_import (Sass_Import_Entry);++++// Creators for sass function list and function descriptors+ADDAPI Sass_Function_List ADDCALL sass_make_function_list (size_t length);+ADDAPI Sass_Function_Entry ADDCALL sass_make_function (const char* signature, Sass_Function_Fn cb, void* cookie);++// Setters and getters for callbacks on function lists+ADDAPI Sass_Function_Entry ADDCALL sass_function_get_list_entry(Sass_Function_List list, size_t pos);+ADDAPI void ADDCALL sass_function_set_list_entry(Sass_Function_List list, size_t pos, Sass_Function_Entry cb);++// Getters for custom function descriptors+ADDAPI const char* ADDCALL sass_function_get_signature (Sass_Function_Entry cb);+ADDAPI Sass_Function_Fn ADDCALL sass_function_get_function (Sass_Function_Entry cb);+ADDAPI void* ADDCALL sass_function_get_cookie (Sass_Function_Entry cb);+++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/include/sass/interface.h view
@@ -0,0 +1,105 @@+#ifndef SASS_C_INTERFACE_H+#define SASS_C_INTERFACE_H++// the API in this header has been deprecated+// please use the new API from sass/context.h++#include <stddef.h>+#include <stdbool.h>+#include <sass/base.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;+ 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/include/sass/values.h view
@@ -0,0 +1,141 @@+#ifndef SASS_C_VALUES_H+#define SASS_C_VALUES_H++#include <stddef.h>+#include <stdbool.h>+#include <sass/base.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+};++// Value Operators+enum Sass_OP {+ AND, OR, // logical connectives+ EQ, NEQ, GT, GTE, LT, LTE, // arithmetic relations+ ADD, SUB, MUL, DIV, MOD, // arithmetic functions+ NUM_OPS // so we know how big to make the op table+};++// Return the sass tag for a generic sass value+// Check is needed before accessing specific values!+ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);++// Check value to be of a specific type+// Can also be used before accessing properties!+ADDAPI bool ADDCALL sass_value_is_null (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_number (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_string (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_boolean (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_color (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_list (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_map (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_error (const union Sass_Value* v);+ADDAPI bool ADDCALL sass_value_is_warning (const union Sass_Value* v);++// Getters and setters for Sass_Number+ADDAPI double ADDCALL sass_number_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_number_set_value (union Sass_Value* v, double value);+ADDAPI const char* ADDCALL sass_number_get_unit (const union Sass_Value* v);+ADDAPI void ADDCALL sass_number_set_unit (union Sass_Value* v, char* unit);++// Getters and setters for Sass_String+ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_string_set_value (union Sass_Value* v, char* value);+ADDAPI bool ADDCALL sass_string_is_quoted(const union Sass_Value* v);+ADDAPI void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted);++// Getters and setters for Sass_Boolean+ADDAPI bool ADDCALL sass_boolean_get_value (const union Sass_Value* v);+ADDAPI void ADDCALL sass_boolean_set_value (union Sass_Value* v, bool value);++// Getters and setters for Sass_Color+ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r);+ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_g (union Sass_Value* v, double g);+ADDAPI double ADDCALL sass_color_get_b (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_b (union Sass_Value* v, double b);+ADDAPI double ADDCALL sass_color_get_a (const union Sass_Value* v);+ADDAPI void ADDCALL sass_color_set_a (union Sass_Value* v, double a);++// Getter for the number of items in list+ADDAPI size_t ADDCALL sass_list_get_length (const union Sass_Value* v);+// Getters and setters for Sass_List+ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v);+ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value);+// Getters and setters for Sass_List values+ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value);++// Getter for the number of items in map+ADDAPI size_t ADDCALL sass_map_get_length (const union Sass_Value* v);+// Getters and setters for Sass_Map keys and values+ADDAPI union Sass_Value* ADDCALL sass_map_get_key (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*);+ADDAPI union Sass_Value* ADDCALL sass_map_get_value (const union Sass_Value* v, size_t i);+ADDAPI void ADDCALL sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*);++// Getters and setters for Sass_Error+ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);+ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);++// Getters and setters for Sass_Warning+ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v);+ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg);++// Creator functions for all value types+ADDAPI union Sass_Value* ADDCALL sass_make_null (void);+ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);+ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);+ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);+ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a);+ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep);+ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len);+ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg);+ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);++// Generic destructor function for all types+// Will release memory of all associated Sass_Values+// Means we will delete recursively for lists and maps+ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);++// Make a deep cloned copy of the given sass value+ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);++// Stringify a Sass_Values and also return the result as a Sass_Value (of type STRING)+ADDAPI union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* a, bool compressed, int precision);++// Execute an operation for two Sass_Values and return the result as a Sass_Value too+ADDAPI union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b);+++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
+ libsass/include/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/include/sass/version.h.in view
@@ -0,0 +1,8 @@+#ifndef SASS_VERSION_H+#define SASS_VERSION_H++#ifndef LIBSASS_VERSION+#define LIBSASS_VERSION "@PACKAGE_VERSION@"+#endif++#endif
+ libsass/include/sass2scss.h view
@@ -0,0 +1,120 @@+/**+ * sass2scss+ * Licensed under the MIT License+ * Copyright (c) Marcel Greter+ */++#ifndef SASS2SCSS_H+#define SASS2SCSS_H++#ifdef _WIN32++ /* You should define ADD_EXPORTS *only* when building the DLL. */+ #ifdef ADD_EXPORTS+ #define ADDAPI __declspec(dllexport)+ #define ADDCALL __cdecl+ #else+ #define ADDAPI+ #define ADDCALL+ #endif++#else /* _WIN32 not defined. */++ /* Define with no value on non-Windows OSes. */+ #define ADDAPI+ #define ADDCALL++#endif++#ifdef __cplusplus++#include <stack>+#include <string>+#include <cstring>+#include <sstream>+#include <iostream>++#ifndef SASS2SCSS_VERSION+// Hardcode once the file is copied from+// https://github.com/mgreter/sass2scss+#define SASS2SCSS_VERSION "1.0.5"+#endif++// add namespace for c+++namespace Sass+{++ // pretty print options+ const int SASS2SCSS_PRETTIFY_0 = 0;+ const int SASS2SCSS_PRETTIFY_1 = 1;+ const int SASS2SCSS_PRETTIFY_2 = 2;+ const int SASS2SCSS_PRETTIFY_3 = 3;++ // remove one-line comment+ const int SASS2SCSS_KEEP_COMMENT = 32;+ // remove multi-line comments+ const int SASS2SCSS_STRIP_COMMENT = 64;+ // convert one-line to multi-line+ const int SASS2SCSS_CONVERT_COMMENT = 128;++ // String for finding something interesting+ const std::string SASS2SCSS_FIND_WHITESPACE = " \t\n\v\f\r";++ // converter struct+ // holding all states+ struct converter+ {+ // bit options+ int options;+ // is selector+ bool selector;+ // concat lists+ bool comma;+ // has property+ bool property;+ // has semicolon+ bool semicolon;+ // comment context+ std::string comment;+ // flag end of file+ bool end_of_file;+ // whitespace buffer+ std::string whitespace;+ // context/block stack+ std::stack<std::string> indents;+ };++ // function only available in c++ code+ char* sass2scss (const std::string& sass, const int options);++}+// EO namespace++// declare for c+extern "C" {+#endif++ // prettyfy print options+ #define SASS2SCSS_PRETTIFY_0 0+ #define SASS2SCSS_PRETTIFY_1 1+ #define SASS2SCSS_PRETTIFY_2 2+ #define SASS2SCSS_PRETTIFY_3 3++ // keep one-line comments+ #define SASS2SCSS_KEEP_COMMENT 32+ // remove multi-line comments+ #define SASS2SCSS_STRIP_COMMENT 64+ // convert one-line to multi-line+ #define SASS2SCSS_CONVERT_COMMENT 128++ // available to c and c++ code+ ADDAPI char* ADDCALL sass2scss (const char* sass, const int options);++ // Get compiled sass2scss version+ ADDAPI const char* ADDCALL sass2scss_version(void);++#ifdef __cplusplus+} // __cplusplus defined.+#endif++#endif
− libsass/inspect.cpp
@@ -1,945 +0,0 @@-#include <cmath>-#include <string>-#include <iostream>-#include <iomanip>-#include <stdint.h>-#include <stdint.h>--#include "ast.hpp"-#include "inspect.hpp"-#include "context.hpp"-#include "utf8/checked.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();- bool was_wrapped = in_wrapped;- in_wrapped = true;- at_rule->selector()->perform(this);- in_wrapped = was_wrapped;- }- 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;- bool was_decl = in_declaration;- 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 = was_decl;- }-- 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;-- bool was_space_array = in_space_array;- bool was_comma_array = in_comma_array;- if (!in_declaration && (- (list->separator() == List::SPACE && in_space_array) ||- (list->separator() == List::COMMA && in_comma_array)- )) {- append_string("(");- }-- if (list->separator() == List::SPACE) in_space_array = true;- else if (list->separator() == List::COMMA) in_comma_array = true;-- for (size_t i = 0, L = list->size(); 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_comma_array = was_comma_array;- in_space_array = was_space_array;- if (!in_declaration && (- (list->separator() == List::SPACE && in_space_array) ||- (list->separator() == List::COMMA && in_comma_array)- )) {- append_string(")");- }-- }-- 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)- {-- string res;-- // init stuff- n->normalize();- int precision = 5;- double value = n->value();- // get option from optional context- if (ctx) precision = ctx->precision;-- // check if the fractional part of the value equals to zero- // neat trick from http://stackoverflow.com/a/1521682/1550314- // double int_part; bool is_int = modf(value, &int_part) == 0.0;-- // this all cannot be done with one run only, since fixed- // output differs from normal output and regular output- // can contain scientific notation which we do not want!-- // first sample- stringstream ss;- ss.precision(12);- ss << value;-- // check if we got scientific notation in result- if (ss.str().find_first_of("e") != string::npos) {- ss.clear(); ss.str(string());- ss.precision(max(12, precision));- ss << fixed << value;- }-- string tmp = ss.str();- size_t pos_point = tmp.find_first_of(".,");- size_t pos_fract = tmp.find_last_not_of("0");- bool is_int = pos_point == pos_fract ||- pos_point == string::npos;-- // reset stream for another run- ss.clear(); ss.str(string());-- // take a shortcut for integers- if (is_int)- {- ss.precision(0);- ss << fixed << value;- res = string(ss.str());- }- // process floats- else- {- // do we have have too much precision?- if (pos_fract < precision + pos_point)- { precision = pos_fract - pos_point; }- // round value again- ss.precision(precision);- ss << fixed << value;- res = string(ss.str());- // maybe we truncated up to decimal point- size_t pos = res.find_last_not_of("0");- bool at_dec_point = res[pos] == '.' ||- res[pos] == ',';- // don't leave a blank point- if (at_dec_point) ++ pos;- res.resize (pos + 1);- }-- // some final cosmetics- if (res == "-0.0") res.erase(0, 1);- else if (res == "-0") res.erase(0, 1);-- // add unit now- res += n->unit();-- // check for a valid unit here- // includes result for reporting- if (n->numerator_units().size() > 1 ||- n->denominator_units().size() > 0 ||- (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != string::npos) ||- (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != string::npos)- ) {- error(res + " isn't a valid CSS value.", n->pstate());- }-- // output the final token- append_token(res, 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(), true), 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
@@ -1,98 +0,0 @@-#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
@@ -1,1408 +0,0 @@-/*- 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.- *- * We use uint32_t Type for Unicode codepoints.- * We need our own because wchar_t might be 16 bits.- */--/*- * 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, uint32_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 = ((uint32_t)c[0] & 0x1F) << 6 |- ((uint32_t)c[1] & 0x3F);- return 2;- } else if (c[0] <= 0xEF) {- /* E0..EF */- *out = ((uint32_t)c[0] & 0xF) << 12 |- ((uint32_t)c[1] & 0x3F) << 6 |- ((uint32_t)c[2] & 0x3F);- return 3;- } else {- /* F0..F4 (unless input is invalid) */- *out = ((uint32_t)c[0] & 0x7) << 18 |- ((uint32_t)c[1] & 0x3F) << 12 |- ((uint32_t)c[2] & 0x3F) << 6 |- ((uint32_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(uint32_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, uint32_t *unicode)-{- if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {- *unicode = 0x10000 + ((((uint32_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(uint32_t unicode, uint16_t *uc, uint16_t *lc)-{- uint32_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;- uint32_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
@@ -1,117 +0,0 @@-/*- 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
@@ -1,28 +0,0 @@-#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
@@ -1,133 +0,0 @@-#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- //####################################-- bool is_alpha(const char& chr)- {- return unsigned(chr - 'A') <= 'Z' - 'A' ||- unsigned(chr - 'a') <= 'z' - 'a';- }-- bool is_space(const char& chr)- {- // adapted the technique from is_alpha- return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';- }-- bool is_digit(const char& chr)- {- // adapted the technique from is_alpha- return unsigned(chr - '0') <= '9' - '0';- }-- 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';- }-- bool is_punct(const char& chr)- {- // locale independent- return chr == '.';- }-- bool is_alnum(const char& chr)- {- return is_alpha(chr) || is_digit(chr);- }-- // check if char is outside ascii range- bool is_unicode(const char& chr)- {- // check for unicode range- return unsigned(chr) > 127;- }-- // Match word character (look ahead)- 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
@@ -1,239 +0,0 @@-#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- bool is_space(const char& src);- bool is_alpha(const char& src);- bool is_punct(const char& src);- bool is_digit(const char& src);- bool is_alnum(const char& src);- bool is_xdigit(const char& src);- bool is_unicode(const char& src);- 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
@@ -1,83 +0,0 @@-#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
@@ -1,41 +0,0 @@-#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
@@ -1,18 +0,0 @@-#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
@@ -1,57 +0,0 @@-#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
@@ -1,251 +0,0 @@-#include "node.hpp"-#include "to_string.hpp"-#include "context.hpp"-#include "parser.hpp"--namespace Sass {--- Node Node::createCombinator(const Complex_Selector::Combinator& combinator) {- NodeDequePtr null;- return Node(COMBINATOR, combinator, NULL /*pSelector*/, null /*pCollection*/);- }--- Node Node::createSelector(Complex_Selector* pSelector, Context& ctx) {- NodeDequePtr null;-- Complex_Selector* pStripped = pSelector->clone(ctx);- pStripped->tail(NULL);- pStripped->combinator(Complex_Selector::ANCESTOR_OF);-- return Node(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);- }--- Node Node::createCollection() {- NodeDequePtr pEmptyCollection = make_shared<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
@@ -1,121 +0,0 @@-#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
@@ -1,166 +0,0 @@-#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
@@ -1,401 +0,0 @@-#include "ast.hpp"-#include "output.hpp"-#include "to_string.hpp"--namespace Sass {- using namespace std;-- Output::Output(Context* ctx)- : Inspect(Emitter(ctx)),- charset(""),- top_nodes(0)- {}-- Output::~Output() { }-- void Output::fallback_impl(AST_Node* n)- {- return n->perform(this);- }-- void Output::operator()(Import* imp)- {- top_nodes.push_back(imp);- }-- OutputBuffer Output::get_buffer(void)- {-- Emitter emitter(ctx);- Inspect inspect(emitter);-- size_t size_nodes = top_nodes.size();- for (size_t i = 0; i < size_nodes; i++) {- top_nodes[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() == 0) {- top_nodes.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();-- bool format = kwd != "@font-face";;-- 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 && format) 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 && format) 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 {- string value(s->value());- if (s->can_compress_whitespace() && output_style() == COMPRESSED) {- value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());- }- if (!in_comment) {- append_token(string_to_output(value), s);- } else {- append_token(value, s);- }- }- }--}
− libsass/output.hpp
@@ -1,55 +0,0 @@-#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<AST_Node*> top_nodes;-- 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
@@ -1,2408 +0,0 @@-#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;- }-- Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)- {- Parser p = Parser::from_c_str(src, ctx, pstate);- // ToDo: ruby sass errors on parent references- // ToDo: remap the source-map entries somehow- return p.parse_selector_group();- }-- 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]);- }- }- }-- bool semicolon = false;- string(error_message);- 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]);- }- }- semicolon = true;- error_message = "top-level @import directive must be terminated by ';'";- }- else if (peek< kwd_mixin >() || peek< kwd_function >()) {- (*root) << parse_definition();- }- else if (peek< variable >()) {- (*root) << parse_assignment();- semicolon = true;- error_message = "top-level variable binding must be terminated by ';'";- }- /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {- (*root) << parse_propset();- }*/- else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {- Mixin_Call* mixin_call = parse_mixin_call();- (*root) << mixin_call;- if (!mixin_call->block()) {- semicolon = true;- error_message = "top-level @include directive must be terminated by ';'";- }- }- 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();- semicolon = true;- error_message = "top-level @warn directive must be terminated by ';'";- }- else if (peek< kwd_err >()) {- (*root) << parse_error();- semicolon = true;- error_message = "top-level @error directive must be terminated by ';'";- }- else if (peek< kwd_dbg >()) {- (*root) << parse_debug();- semicolon = true;- error_message = "top-level @debug directive must be terminated by ';'";- }- // 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()){- semicolon = true;- error_message = "top-level directive must be terminated by ';'";- }- }- 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);- }- if (semicolon) {- if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end)- { error(error_message, pstate); }- semicolon = false;- }- 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< quoted_string >()) {- Expression* the_url = parse_string();- *args << new (ctx.mem) Argument(the_url->pstate(), the_url);- }- else if (lex < uri_value >(position)) { // chunk seems to work too!- String* the_url = parse_interpolated_chunk(lexed);- *args << new (ctx.mem) Argument(the_url->pstate(), the_url);- }- else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {- Expression* the_url = parse_list(); // 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;- Selector_Lookahead lookahead = lookahead_for_value(position);- if (lookahead.has_interpolants && lookahead.found) {- val = parse_value_schema(lookahead.found);- } else {- 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;- ParserState r_source_position = pstate;- lex < css_comments >();- in_at_root = false;- 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- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- 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_css< 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 (lex< binomial >(position)) {- expr = new (ctx.mem) String_Constant(p, lexed);- ((String_Constant*)expr)->can_compress_whitespace(true);- }- 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* value;- Selector_Lookahead lookahead = lookahead_for_value(position);- if (lookahead.found) {- if (lookahead.has_interpolants) {- value = parse_value_schema(lookahead.found);- } else {- value = parse_list();- }- }- else {- value = parse_list();- if (List* list = dynamic_cast<List*>(value)) {- 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, value/*, 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()- {- ParserState opstate = pstate;- Expression* key = parse_list();- if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {- if (!str->quote_mark() && !str->is_delayed()) {- if (ctx.names_to_colors.count(str->value())) {- Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);- c->pstate(str->pstate());- c->disp(str->value());- key = c;- }- }- }-- // 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(opstate, 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 (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {- if (!str->quote_mark() && !str->is_delayed()) {- if (ctx.names_to_colors.count(str->value())) {- Color* c = new (ctx.mem) Color(*ctx.names_to_colors[str->value()]);- c->pstate(str->pstate());- c->disp(str->value());- key = c;- }- }- }-- if (!(lex< exactly<':'> >()))- { error("invalid syntax", pstate); }-- Expression* value = parse_space_list();-- (*map) << make_pair(key, value);- }-- // Check was moved to eval step- // if (map->has_duplicate_key()) {- // To_String to_string(&ctx);- // error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + ".", pstate);- // }-- ParserState ps = map->pstate();- ps.offset = pstate - ps + pstate.offset;- map->pstate(ps);-- return map;- }-- Expression* Parser::parse_list()- {- return parse_comma_list();- }-- Expression* Parser::parse_comma_list()- {- if (peek_css< 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_css< exactly<','> >(position)) return list1;-- List* comma_list = new (ctx.mem) List(pstate, 2, List::COMMA);- (*comma_list) << list1;-- while (lex_css< exactly<','> >())- {- if (peek_css< 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_css< 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_css< alternatives <- // exactly<'!'>,- exactly<';'>,- exactly<'}'>,- exactly<'{'>,- exactly<')'>,- exactly<','>,- exactly<':'>,- exactly<ellipsis>,- default_flag,- global_flag- > >(position)) && peek_css< optional_css_whitespace >() != end- ) {- (*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;- return parse_operators(factor);- }-- Expression* Parser::parse_operators(Expression* 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_css< exactly<'('> >()) {- Expression* value = parse_map();- if (!lex_css< 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- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- 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_expression()- {- if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {- return parse_static_value();- }- return 0;- }-- 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.time_wspace());- // 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);- str_node->quote_mark('*');- 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) {- String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, p))); // accumulate the preceding segment if it's nonempty- part->is_delayed(true);- part->quote_mark('*'); // avoid unquote in interpolation- (*schema) << part;- }- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- 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) {- String_Constant* part = new (ctx.mem) String_Constant(pstate, normalize_wspace(string(i, str.end)));- part->is_delayed(true);- part->quote_mark('*'); // avoid unquote in interpolation- (*schema) << part;- }- 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_Constant(pstate, lexed);- }- lex< exactly<'='> >();- *kwd_arg << new (ctx.mem) String_Constant(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 (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }- 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;- if (peek<exactly<'}'>>()) {- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- while (position < stop) {- if (lex< spaces >() && num_items) {- (*schema) << new (ctx.mem) String_Constant(pstate, " ");- }- else if (lex< interpolant >()) {- Token insides(Token(lexed.begin + 2, lexed.end - 1));- Expression* interp_node;- Parser p = Parser::from_token(insides, ctx, pstate);- if (!(interp_node = p.parse_static_expression())) {- interp_node = p.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 >()) {- Expression* factor = new (ctx.mem) Textual(pstate, Textual::NUMBER, lexed);- if (peek< class_char< static_ops > >()) {- (*schema) << parse_operators(factor);- } else {- (*schema) << factor;- }- }- else if (lex< hex >()) {- (*schema) << new (ctx.mem) Textual(pstate, Textual::HEX, unquote(lexed));- }- else if (lex < exactly < '-' > >()) {- (*schema) << new (ctx.mem) String_Constant(pstate, lexed);- }- else if (lex< quoted_string >()) {- (*schema) << new (ctx.mem) String_Quoted(pstate, lexed);- }- else if (lex< variable >()) {- (*schema) << new (ctx.mem) Variable(pstate, Util::normalize_underscores(lexed));- }- else if (peek< parenthese_scope >()) {- (*schema) << parse_factor();- }- 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- if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;- css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");- }- 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);-- ParserState call_pos = pstate;- Arguments* args = parse_arguments(name == "url");- return new (ctx.mem) Function_Call(call_pos, 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< variable >(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;- }--- Selector_Lookahead Parser::lookahead_for_value(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< percentage >(p)) ||- (q = peek< dimension >(p)) ||- (q = peek< quoted_string >(p)) ||- (q = peek< variable >(p)) ||- (q = peek< exactly<'*'> >(p)) ||- (q = peek< exactly<'+'> >(p)) ||- (q = peek< exactly<'~'> >(p)) ||- (q = peek< exactly<'>'> >(p)) ||- (q = peek< exactly<','> >(p)) ||- (q = peek< sequence<parenthese_scope, interpolant>>(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< 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;- }- const char* prev = pos_left - 1;- if (*prev == '\r') break;- if (*prev == '\n') break;- if (*prev == 10) break;- pos_left = prev;- }- 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;- if (*pos_left == 10) 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
@@ -1,291 +0,0 @@-#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(pstate), 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]"));- // special static parsers to convert strings into certain selectors- static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));--#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()- {- // copy old token- Token prev = lexed;- // throw away comments- // update srcmap position- lex < css_comments >();- // now lex a new token- const char* pos = lex< mx >();- // maybe restore prev token- if (pos == 0) lexed = prev;- // return match- return pos;- }-- // 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_expression();- String_Constant* parse_static_value();- String* parse_ie_property();- String* parse_ie_keyword_arg();- String_Schema* parse_value_schema(const char* stop);- Expression* parse_operators(Expression* factor);- 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_value(const char* start = 0);- 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
@@ -1,74 +0,0 @@-#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
@@ -1,168 +0,0 @@-#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
@@ -1,58 +0,0 @@-#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
@@ -1,163 +0,0 @@-#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
@@ -1,126 +0,0 @@-#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); }- string time_wspace() const {- string str(to_string());- string whitespaces(" \t\f\v\n\r");- return str.erase(str.find_last_not_of(whitespaces)+1);- }-- 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; };- ParserState pstate() { return *this; };-- public:- string path;- const char* src;- Offset offset;- Token token;-- };--}--#endif
− libsass/posix/getopt.c
@@ -1,562 +0,0 @@-/* $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
@@ -1,95 +0,0 @@-#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
@@ -1,784 +0,0 @@-#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 escapes- sequence <- exactly < '\\' >,- exactly < '\r' >,- exactly < '\n' >- >,- 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 escapes- sequence <- exactly < '\\' >,- exactly < '\r' >,- exactly < '\n' >- >,- 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- sequence <- negate <- exactly < '$' >- >,- 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>, exactly<'('>, skip_over_scopes< exactly<'('>, exactly<')'> > >(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_property(const char* src) {- return alternatives <- variable,- identifier_schema,- identifier- >(src);- }- const char* ie_keyword_arg_value(const char* src) {- return alternatives <- variable,- identifier_schema,- identifier,- quoted_string,- number,- hexa,- sequence <- exactly < '(' >,- skip_over_scopes <- exactly < '(' >,- exactly < ')' >- >- >- >(src);- }-- const char* ie_keyword_arg(const char* src) {- return sequence <- ie_keyword_arg_property,- optional_css_whitespace,- exactly<'='>,- optional_css_whitespace,- ie_keyword_arg_value- >(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< sequence<- static_component,- zero_plus< identifier >- >,- zero_plus < sequence<- alternatives<- sequence< optional_spaces, alternatives<- exactly < '/' >,- exactly < ',' >,- exactly < ' ' >- >, optional_spaces >,- spaces- >,- static_component- > >,- optional_css_whitespace,- alternatives< exactly<';'>, exactly<'}'> >- >(src);- }-- const char* parenthese_scope(const char* src) {- return sequence <- exactly < '(' >,- skip_over_scopes <- exactly < '(' >,- exactly < ')' >- >- >(src);- }-- }-}
− libsass/prelexer.hpp
@@ -1,378 +0,0 @@-#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) {-- 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 (const char* pos = start(src)) {- ++ level; // increase counter- src = pos - 1; // advance position- }-- // 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;- // advance position- src = final - 1;- }-- // next- ++ src;- }-- return 0;- }-- // skip to a skip delimited by parentheses- // uses smart `skip_over_scopes` internally- const char* parenthese_scope(const char* src);-- // 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) {- return skip_over_scopes<start, stop>(src, 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);- const char* ie_keyword_arg_value(const char* src);- const char* ie_keyword_arg_property(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
@@ -1,59 +0,0 @@-#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
@@ -1,43 +0,0 @@-#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
@@ -1,43 +0,0 @@-#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
@@ -1,68 +0,0 @@-#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
@@ -1,835 +0,0 @@-// 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
@@ -1,115 +0,0 @@-#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
@@ -1,894 +0,0 @@-#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
@@ -1,149 +0,0 @@-#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
@@ -1,173 +0,0 @@-#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
@@ -1,108 +0,0 @@-#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
@@ -1,287 +0,0 @@-#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
@@ -1,102 +0,0 @@-#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
@@ -1,132 +0,0 @@-#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
@@ -1,260 +0,0 @@-#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
@@ -1,354 +0,0 @@-#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;- bool quoted;- 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; }- bool ADDCALL sass_string_is_quoted(const union Sass_Value* v) { return v->string.quoted; }- void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted) { v->string.quoted = quoted; }-- // 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.quoted = false;- 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_qstring(const char* val)- {- Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));- if (v == 0) return 0;- v->string.quoted = true;- 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
@@ -1,127 +0,0 @@-#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);-ADDAPI bool ADDCALL sass_string_is_quoted(const union Sass_Value* v);-ADDAPI void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted);--// Getters and setters for Sass_Boolean-ADDAPI bool ADDCALL sass_boolean_get_value (const union Sass_Value* v);-ADDAPI void ADDCALL sass_boolean_set_value (union Sass_Value* v, bool value);--// Getters and setters for Sass_Color-ADDAPI double ADDCALL sass_color_get_r (const union Sass_Value* v);-ADDAPI void ADDCALL sass_color_set_r (union Sass_Value* v, double r);-ADDAPI double ADDCALL sass_color_get_g (const union Sass_Value* v);-ADDAPI void ADDCALL sass_color_set_g (union Sass_Value* v, double g);-ADDAPI double ADDCALL sass_color_get_b (const union Sass_Value* v);-ADDAPI void ADDCALL sass_color_set_b (union Sass_Value* v, double b);-ADDAPI double ADDCALL sass_color_get_a (const union Sass_Value* v);-ADDAPI void ADDCALL sass_color_set_a (union Sass_Value* v, double a);--// Getter for the number of items in list-ADDAPI size_t ADDCALL sass_list_get_length (const union Sass_Value* v);-// Getters and setters for Sass_List-ADDAPI enum Sass_Separator ADDCALL sass_list_get_separator (const union Sass_Value* v);-ADDAPI void ADDCALL sass_list_set_separator (union Sass_Value* v, enum Sass_Separator value);-// Getters and setters for Sass_List values-ADDAPI union Sass_Value* ADDCALL sass_list_get_value (const union Sass_Value* v, size_t i);-ADDAPI void ADDCALL sass_list_set_value (union Sass_Value* v, size_t i, union Sass_Value* value);--// Getter for the number of items in map-ADDAPI size_t ADDCALL sass_map_get_length (const union Sass_Value* v);-// Getters and setters for Sass_Map keys and values-ADDAPI union Sass_Value* ADDCALL sass_map_get_key (const union Sass_Value* v, size_t i);-ADDAPI void ADDCALL sass_map_set_key (union Sass_Value* v, size_t i, union Sass_Value*);-ADDAPI union Sass_Value* ADDCALL sass_map_get_value (const union Sass_Value* v, size_t i);-ADDAPI void ADDCALL sass_map_set_value (union Sass_Value* v, size_t i, union Sass_Value*);--// Getters and setters for Sass_Error-ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);-ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);--// Getters and setters for Sass_Warning-ADDAPI char* ADDCALL sass_warning_get_message (const union Sass_Value* v);-ADDAPI void ADDCALL sass_warning_set_message (union Sass_Value* v, char* msg);--// Creator functions for all value types-ADDAPI union Sass_Value* ADDCALL sass_make_null (void);-ADDAPI union Sass_Value* ADDCALL sass_make_boolean (bool val);-ADDAPI union Sass_Value* ADDCALL sass_make_string (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_qstring (const char* val);-ADDAPI union Sass_Value* ADDCALL sass_make_number (double val, const char* unit);-ADDAPI union Sass_Value* ADDCALL sass_make_color (double r, double g, double b, double a);-ADDAPI union Sass_Value* ADDCALL sass_make_list (size_t len, enum Sass_Separator sep);-ADDAPI union Sass_Value* ADDCALL sass_make_map (size_t len);-ADDAPI union Sass_Value* ADDCALL sass_make_error (const char* msg);-ADDAPI union Sass_Value* ADDCALL sass_make_warning (const char* msg);--// Generic destructor function for all types-// Will release memory of all associated Sass_Values-// Means we will delete recursively for lists and maps-ADDAPI void ADDCALL sass_delete_value (union Sass_Value* val);--// Make a deep cloned copy of the given sass value-ADDAPI union Sass_Value* ADDCALL sass_clone_value (const union Sass_Value* val);---#ifdef __cplusplus-} // __cplusplus defined.-#endif--#endif
− libsass/sass_version.h
@@ -1,8 +0,0 @@-#ifndef SASS_VERSION_H-#define SASS_VERSION_H--#ifndef LIBSASS_VERSION-#define LIBSASS_VERSION "[NA]"-#endif--#endif
− libsass/source_map.cpp
@@ -1,184 +0,0 @@-#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
@@ -1,65 +0,0 @@-#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/src/GNUmakefile.am view
@@ -0,0 +1,54 @@+ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4 -I script++AM_COPT = -Wall -O2+AM_COVLDFLAGS =++if ENABLE_COVERAGE+ AM_COPT = -O0 --coverage+ AM_COVLDFLAGS += -lgcov+endif++AM_CPPFLAGS = -I$(top_srcdir)/include+AM_CFLAGS = $(AM_COPT)+AM_CXXFLAGS = $(AM_COPT)+AM_LDFLAGS = $(AM_COPT) $(AM_COVLDFLAGS)++if COMPILER_IS_MINGW32+ AM_CXXFLAGS += -std=gnu++0x+else+ AM_CXXFLAGS += -std=c++0x+endif++EXTRA_DIST = \+ COPYING \+ INSTALL \+ LICENSE \+ Readme.md++pkgconfigdir = $(libdir)/pkgconfig+pkgconfig_DATA = support/libsass.pc++lib_LTLIBRARIES = libsass.la++include $(top_srcdir)/Makefile.conf++libsass_la_SOURCES = ${CSOURCES} ${SOURCES}++libsass_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 0:9:0++if ENABLE_TESTS+if ENABLE_COVERAGE+nodist_EXTRA_libsass_la_SOURCES = non-existent-file-to-force-CXX-linking.cxx+endif+endif++include_HEADERS = $(top_srcdir)/include/sass.h \+ $(top_srcdir)/include/sass2scss.h++sass_includedir = $(includedir)/sass++sass_include_HEADERS = $(top_srcdir)/include/sass/base.h \+ $(top_srcdir)/include/sass/values.h \+ $(top_srcdir)/include/sass/version.h \+ $(top_srcdir)/include/sass/context.h \+ $(top_srcdir)/include/sass/functions.h
+ libsass/src/ast.cpp view
@@ -0,0 +1,2134 @@+#include "ast.hpp"+#include "context.hpp"+#include "node.hpp"+#include "extend.hpp"+#include "to_string.hpp"+#include "color_maps.hpp"+#include <set>+#include <iomanip>+#include <algorithm>+#include <iostream>++namespace Sass {++ static Null sass_null(Sass::Null(ParserState("null")));++ bool Supports_Operator::needs_parens(Supports_Condition* cond) const {+ return dynamic_cast<Supports_Negation*>(cond) ||+ (dynamic_cast<Supports_Operator*>(cond) &&+ dynamic_cast<Supports_Operator*>(cond)->operand() != operand());+ }++ bool Supports_Negation::needs_parens(Supports_Condition* cond) const {+ return dynamic_cast<Supports_Negation*>(cond) ||+ dynamic_cast<Supports_Operator*>(cond);+ }++ void AST_Node::update_pstate(const ParserState& pstate)+ {+ pstate_.offset += pstate - pstate_ + pstate.offset;+ }++ inline bool is_ns_eq(const std::string& l, const std::string& r)+ {+ if (l.empty() && r.empty()) return true;+ else if (l.empty() && r == "*") return true;+ else if (r.empty() && l == "*") return true;+ else return l == r;+ }++++ bool Compound_Selector::operator< (const Compound_Selector& rhs) const+ {+ size_t L = std::min(length(), rhs.length());+ for (size_t i = 0; i < L; ++i)+ {+ Simple_Selector* l = (*this)[i];+ Simple_Selector* r = rhs[i];+ if (!l && !r) return false;+ else if (!r) return false;+ else if (!l) return true;+ else if (*l != *r)+ { return *l < *r; }+ }+ // just compare the length now+ return length() < rhs.length();+ }++ bool Compound_Selector::has_parent_ref()+ {+ return has_parent_reference();+ }++ bool Complex_Selector::has_parent_ref()+ {+ return (head() && head()->has_parent_ref()) ||+ (tail() && tail()->has_parent_ref());+ }++ bool Complex_Selector::operator< (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ const Complex_Selector* l = this;+ const Complex_Selector* r = &rhs;+ Compound_Selector* l_h = l ? l->head() : 0;+ Compound_Selector* r_h = r ? r->head() : 0;+ // process all tails+ while (true)+ {+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l = l->tail();+ l_h = l ? l->head() : 0;+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r = r->tail();+ r_h = r ? r->head() : 0;+ continue;+ }+ // check for valid selectors+ if (!l) return !!r;+ if (!r) return false;+ // both are null+ else if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next headers+ l_h = l ? l->head() : 0;+ r_h = r ? r->head() : 0;+ }+ // one side is null+ else if (!r_h) return true;+ else if (!l_h) return false;+ // heads ok and equal+ else if (*l_h == *r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next headers+ l_h = l ? l->head() : 0;+ r_h = r ? r->head() : 0;+ }+ // heads are not equal+ else return *l_h < *r_h;+ }+ return true;+ }++ bool Complex_Selector::operator== (const Complex_Selector& rhs) const+ {+ // const iterators for tails+ const Complex_Selector* l = this;+ const Complex_Selector* r = &rhs;+ Compound_Selector* l_h = l ? l->head() : 0;+ Compound_Selector* r_h = r ? r->head() : 0;+ // process all tails+ while (true)+ {+ // skip empty ancestor first+ if (l && l->is_empty_ancestor())+ {+ l = l->tail();+ l_h = l ? l->head() : 0;+ continue;+ }+ // skip empty ancestor first+ if (r && r->is_empty_ancestor())+ {+ r = r->tail();+ r_h = r ? r->head() : 0;+ continue;+ }+ // check the pointers+ if (!r) return !l;+ if (!l) return !r;+ // both are null+ if (!l_h && !r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() < r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next heads+ l_h = l ? l->head() : 0;+ r_h = r ? r->head() : 0;+ }+ // fail if only one is null+ else if (!r_h) return !l_h;+ else if (!l_h) return !r_h;+ // heads ok and equal+ else if (*l_h == *r_h)+ {+ // check combinator after heads+ if (l->combinator() != r->combinator())+ { return l->combinator() == r->combinator(); }+ // advance to next tails+ l = l->tail();+ r = r->tail();+ // fetch the next heads+ l_h = l ? l->head() : 0;+ r_h = r ? r->head() : 0;+ }+ // abort+ else break;+ }+ // unreachable+ return false;+ }++ Compound_Selector* Compound_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ Compound_Selector* unified = rhs;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if (!unified) break;+ unified = (*this)[i]->unify_with(unified, ctx);+ }+ return unified;+ }++ bool Simple_Selector::operator== (const Simple_Selector& rhs) const+ {+ const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);+ const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);+ if (ll && rr) return *ll == *rr;++ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Simple_Selector::operator< (const Simple_Selector& rhs) const+ {+ const Attribute_Selector* ll = dynamic_cast<const Attribute_Selector*>(this);+ const Attribute_Selector* rr = dynamic_cast<const Attribute_Selector*>(&rhs);+ if (ll && rr) return *ll < *rr;++ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Selector_List::operator== (const Selector& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (const Selector_List* ls = dynamic_cast<const Selector_List*>(&rhs)) { return *this == *ls; }+ else if (const Complex_Selector* ls = dynamic_cast<const Complex_Selector*>(&rhs)) { return *this == *ls; }+ else if (const Compound_Selector* ls = dynamic_cast<const Compound_Selector*>(&rhs)) { return *this == *ls; }+ // no compare method+ return this == &rhs;+ }++ // Selector lists can be compared to comma lists+ bool Selector_List::operator==(const Expression& rhs) const+ {+ // solve the double dispatch problem by using RTTI information via dynamic cast+ if (const List* ls = dynamic_cast<const List*>(&rhs)) { return *this == *ls; }+ if (const Selector* ls = dynamic_cast<const Selector*>(&rhs)) { return *this == *ls; }+ // compare invalid (maybe we should error?)+ return false;+ }++ bool Selector_List::operator== (const Selector_List& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Complex_Selector*> l_lst = this->elements();+ std::vector<Complex_Selector*> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), cmp_complex_selector());+ std::sort(r_lst.begin(), r_lst.end(), cmp_complex_selector());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Complex_Selector* l = l_lst[i];+ Complex_Selector* r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ else if (!r) ++n;+ // do the check+ else if (*l != *r)+ { return false; }+ // advance+ ++i; ++n;+ }+ // no mismatch+ return true;+ }++ Compound_Selector* Simple_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ 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 are always last+ size_t i, L;+ bool found = false;+ if (typeid(*this) == typeid(Pseudo_Selector) || typeid(*this) == typeid(Wrapped_Selector))+ {+ for (i = 0, L = rhs->length(); i < L; ++i)+ {+ if ((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 = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);+ (*cpy) << this;+ return cpy;+ }+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());+ for (size_t j = 0; j < i; ++j)+ { (*cpy) << (*rhs)[j]; }+ (*cpy) << this;+ for (size_t j = i; j < L; ++j)+ { (*cpy) << (*rhs)[j]; }+ return cpy;+ }++ Simple_Selector* Type_Selector::unify_with(Simple_Selector* rhs, Context& ctx)+ {+ // check if ns can be extended+ // true for no ns or universal+ if (has_universal_ns())+ {+ // but dont extend with universal+ // true for valid ns and universal+ if (!rhs->is_universal_ns())+ {+ // creaty the copy inside (avoid unnecessary copies)+ Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);+ // overwrite the name if star is given as name+ if (ts->name() == "*") { ts->name(rhs->name()); }+ // now overwrite the namespace name and flag+ ts->ns(rhs->ns()); ts->has_ns(rhs->has_ns());+ // return copy+ return ts;+ }+ }+ // namespace may changed, check the name now+ // overwrite star (but not with another star)+ if (name() == "*" && rhs->name() != "*")+ {+ // creaty the copy inside (avoid unnecessary copies)+ Type_Selector* ts = SASS_MEMORY_NEW(ctx.mem, Type_Selector, *this);+ // simply set the new name+ ts->name(rhs->name());+ // return copy+ return ts;+ }+ // return original+ return this;+ }++ Compound_Selector* Type_Selector::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ // TODO: handle namespaces++ // if the rhs is empty, just return a copy of this+ if (rhs->length() == 0) {+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());+ (*cpy) << this;+ return cpy;+ }++ Simple_Selector* rhs_0 = (*rhs)[0];+ // otherwise, this is a tag name+ if (name() == "*")+ {+ if (typeid(*rhs_0) == typeid(Type_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);+ Type_Selector* ts = static_cast<Type_Selector*>(rhs_0);+ (*cpy)[0] = this->unify_with(ts, ctx);+ return cpy;+ }+ else if (dynamic_cast<Selector_Qualifier*>(rhs_0)) {+ // qualifier is `.class`, so we can prefix with `ns|*.class`+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());+ if (has_ns() && !rhs_0->has_ns()) {+ if (ns() != "*") (*cpy) << this;+ }+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ { (*cpy) << (*rhs)[i]; }+ return cpy;+ }+++ return rhs;+ }++ if (typeid(*rhs_0) == typeid(Type_Selector))+ {+ // if rhs is universal, just return this tagname + rhs's qualifiers+ if (rhs_0->name() != "*" && rhs_0->ns() != "*" && rhs_0->name() != name()) return 0;+ // otherwise create new compound and unify first simple selector+ Compound_Selector* copy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *rhs);+ (*copy)[0] = this->unify_with(rhs_0, ctx);+ return copy;++ }+ // else it's a tag name and a bunch of qualifiers -- just append them+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, rhs->pstate());+ if (name() != "*") (*cpy) << this;+ (*cpy) += rhs;+ return cpy;+ }++ Compound_Selector* Selector_Qualifier::unify_with(Compound_Selector* rhs, Context& ctx)+ {+ if (name()[0] == '#')+ {+ for (size_t i = 0, L = rhs->length(); i < L; ++i)+ {+ Simple_Selector* rhs_i = (*rhs)[i];+ if (typeid(*rhs_i) == typeid(Selector_Qualifier) &&+ static_cast<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 Attribute_Selector::operator< (const Attribute_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns())) {+ if (name() == rhs.name()) {+ if (matcher() == rhs.matcher()) {+ return value() < rhs.value();+ } else { return matcher() < rhs.matcher(); }+ } else { return name() < rhs.name(); }+ }+ else return false;+ }++ bool Attribute_Selector::operator< (const Simple_Selector& rhs) const+ {+ if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))+ {+ return *this < *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() < rhs.name(); }+ return ns() < rhs.ns();+ }++ bool Attribute_Selector::operator== (const Attribute_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ { return matcher() == rhs.matcher() && value() == rhs.value(); }+ else return false;+ }++ bool Attribute_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (const Attribute_Selector* w = dynamic_cast<const Attribute_Selector*>(&rhs))+ {+ return *this == *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Wrapped_Selector::operator== (const Wrapped_Selector& rhs) const+ {+ if (is_ns_eq(ns(), rhs.ns()) && name() == rhs.name())+ { return *(selector()) == *(rhs.selector()); }+ else return false;+ }++ bool Wrapped_Selector::operator== (const Simple_Selector& rhs) const+ {+ if (const Wrapped_Selector* w = dynamic_cast<const Wrapped_Selector*>(&rhs))+ {+ return *this == *w;+ }+ if (is_ns_eq(ns(), rhs.ns()))+ { return name() == rhs.name(); }+ return ns() == rhs.ns();+ }++ bool Wrapped_Selector::is_superselector_of(Wrapped_Selector* sub)+ {+ if (this->name() != sub->name()) return false;+ if (this->name() == ":current") return false;+ if (Selector_List* rhs_list = dynamic_cast<Selector_List*>(sub->selector())) {+ if (Selector_List* lhs_list = dynamic_cast<Selector_List*>(selector())) {+ return lhs_list->is_superselector_of(rhs_list);+ }+ error("is_superselector expected a Selector_List", sub->pstate());+ } else {+ error("is_superselector expected a Selector_List", sub->pstate());+ }+ return false;+ }++ bool Compound_Selector::is_superselector_of(Selector_List* rhs, std::string wrapped)+ {+ for (Complex_Selector* item : rhs->elements()) {+ if (is_superselector_of(item, wrapped)) return true;+ }+ return false;+ }++ bool Compound_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapped)+ {+ if (rhs->head()) return is_superselector_of(rhs->head(), wrapped);+ return false;+ }++ bool Compound_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)+ {+ To_String to_string;++ Compound_Selector* lhs = this;+ Simple_Selector* lbase = lhs->base();+ Simple_Selector* rbase = rhs->base();++ // Check if pseudo-elements are the same between the selectors++ std::set<std::string> lpsuedoset, rpsuedoset;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ if ((*this)[i]->is_pseudo_element()) {+ std::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()) {+ std::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;+ }++ std::set<std::string> lset, rset;++ if (lbase && rbase)+ {+ if (lbase->perform(&to_string) == rbase->perform(&to_string)) {+ for (size_t i = 1, L = length(); i < L; ++i)+ { lset.insert((*this)[i]->perform(&to_string)); }+ 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());+ }+ return false;+ }++ for (size_t i = 0, iL = length(); i < iL; ++i)+ {+ Selector* lhs = (*this)[i];+ // very special case for wrapped matches selector+ if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(lhs)) {+ if (wrapped->name() == ":not") {+ if (Selector_List* not_list = dynamic_cast<Selector_List*>(wrapped->selector())) {+ if (not_list->is_superselector_of(rhs, wrapped->name())) return false;+ } else {+ throw std::runtime_error("wrapped not selector is not a list");+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ lhs = wrapped->selector();+ if (Selector_List* list = dynamic_cast<Selector_List*>(wrapped->selector())) {+ if (Compound_Selector* comp = dynamic_cast<Compound_Selector*>(rhs)) {+ if (!wrapping.empty() && wrapping != wrapped->name()) return false;+ if (wrapping.empty() || wrapping != wrapped->name()) {;+ if (list->is_superselector_of(comp, wrapped->name())) return true;+ }+ }+ }+ }+ Simple_Selector* rhs_sel = rhs->elements().size() > i ? (*rhs)[i] : 0;+ if (Wrapped_Selector* wrapped_r = dynamic_cast<Wrapped_Selector*>(rhs_sel)) {+ if (wrapped->name() == wrapped_r->name()) {+ if (wrapped->is_superselector_of(wrapped_r)) {+ continue;+ rset.insert(lhs->perform(&to_string));++ }}+ }+ }+ // match from here on as strings+ lset.insert(lhs->perform(&to_string));+ }++ for (size_t n = 0, nL = rhs->length(); n < nL; ++n)+ {+ auto r = (*rhs)[n];+ if (Wrapped_Selector* wrapped = dynamic_cast<Wrapped_Selector*>(r)) {+ if (wrapped->name() == ":not") {+ if (Selector_List* ls = dynamic_cast<Selector_List*>(wrapped->selector())) {+ ls->remove_parent_selectors();+ if (is_superselector_of(ls, wrapped->name())) return false;+ }+ }+ if (wrapped->name() == ":matches" || wrapped->name() == ":-moz-any") {+ if (!wrapping.empty()) {+ if (wrapping != wrapped->name()) return false;+ }+ if (Selector_List* ls = dynamic_cast<Selector_List*>(wrapped->selector())) {+ ls->remove_parent_selectors();+ return (is_superselector_of(ls, wrapped->name()));+ }+ }+ }+ rset.insert(r->perform(&to_string));+ }++ //for (auto l : lset) { cerr << "l: " << l << endl; }+ //for (auto r : rset) { cerr << "r: " << r << endl; }++ if (lset.empty()) return true;+ // return true if rset contains all the elements of lset+ return includes(rset.begin(), rset.end(), lset.begin(), lset.end());++ }++ // create complex selector (ancestor of) from compound selector+ Complex_Selector* Compound_Selector::to_complex(Memory_Manager& mem)+ {+ // create an intermediate complex selector+ return SASS_MEMORY_NEW(mem, Complex_Selector,+ pstate(),+ Complex_Selector::ANCESTOR_OF,+ this,+ 0);+ }++ Selector_List* Complex_Selector::unify_with(Complex_Selector* other, Context& ctx)+ {++ // get last tails (on the right side)+ Complex_Selector* l_last = this->last();+ Complex_Selector* r_last = other->last();++ // check valid pointers (assertion)+ SASS_ASSERT(l_last, "lhs is null");+ SASS_ASSERT(r_last, "rhs is null");++ // Not sure about this check, but closest way I could check+ // was to see if this is a ruby 'SimpleSequence' equivalent.+ // It seems to do the job correctly as some specs react to this+ if (l_last->combinator() != Combinator::ANCESTOR_OF) return 0;+ if (r_last->combinator() != Combinator::ANCESTOR_OF ) return 0;++ // get the headers for the last tails+ Compound_Selector* l_last_head = l_last->head();+ Compound_Selector* r_last_head = r_last->head();++ // check valid head pointers (assertion)+ SASS_ASSERT(l_last_head, "lhs head is null");+ SASS_ASSERT(r_last_head, "rhs head is null");++ // get the unification of the last compound selectors+ Compound_Selector* unified = r_last_head->unify_with(l_last_head, ctx);++ // abort if we could not unify heads+ if (unified == 0) return 0;++ // check for universal (star: `*`) selector+ bool is_universal = l_last_head->is_universal() ||+ r_last_head->is_universal();++ if (is_universal)+ {+ // move the head+ l_last->head(0);+ r_last->head(unified);+ }++ // create nodes from both selectors+ Node lhsNode = complexSelectorToNode(this, ctx);+ Node rhsNode = complexSelectorToNode(other, ctx);++ // overwrite universal base+ if (!is_universal)+ {+ // create some temporaries to convert to node+ Complex_Selector* fake = unified->to_complex(ctx.mem);+ Node unified_node = complexSelectorToNode(fake, ctx);+ // add to permutate the list?+ rhsNode.plus(unified_node);+ }++ // do some magic we inherit from node and extend+ Node node = Extend::subweave(lhsNode, rhsNode, ctx);+ Selector_List* result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ NodeDequePtr col = node.collection(); // move from collection to list+ for (NodeDeque::iterator it = col->begin(), end = col->end(); it != end; it++)+ { (*result) << nodeToComplexSelector(Node::naiveTrim(*it, ctx), ctx); }++ // only return if list has some entries+ return result->length() ? result : 0;++ }++ bool Compound_Selector::operator== (const Compound_Selector& rhs) const+ {+ // for array access+ size_t i = 0, n = 0;+ size_t iL = length();+ size_t nL = rhs.length();+ // create temporary vectors and sort them+ std::vector<Simple_Selector*> l_lst = this->elements();+ std::vector<Simple_Selector*> r_lst = rhs.elements();+ std::sort(l_lst.begin(), l_lst.end(), cmp_simple_selector());+ std::sort(r_lst.begin(), r_lst.end(), cmp_simple_selector());+ // process loop+ while (true)+ {+ // first check for valid index+ if (i == iL) return iL == nL;+ else if (n == nL) return iL == nL;+ // the access the vector items+ Simple_Selector* l = l_lst[i];+ Simple_Selector* r = r_lst[n];+ // skip nulls+ if (!l) ++i;+ if (!r) ++n;+ // do the check now+ else if (*l != *r)+ { return false; }+ // advance now+ ++i; ++n;+ }+ // no mismatch+ return true;+ }++ bool Complex_Selector_Pointer_Compare::operator() (const Complex_Selector* const pLeft, const Complex_Selector* const pRight) const {+ return *pLeft < *pRight;+ }++ bool Complex_Selector::is_superselector_of(Compound_Selector* rhs, std::string wrapping)+ {+ return last()->head() && last()->head()->is_superselector_of(rhs, wrapping);+ }++ bool Complex_Selector::is_superselector_of(Complex_Selector* rhs, std::string wrapping)+ {+ Complex_Selector* lhs = this;+ To_String to_string;+ // check for selectors with leading or trailing combinators+ if (!lhs->head() || !rhs->head())+ { return false; }+ const Complex_Selector* l_innermost = lhs->innermost();+ if (l_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ const Complex_Selector* r_innermost = rhs->innermost();+ if (r_innermost->combinator() != Complex_Selector::ANCESTOR_OF)+ { return false; }+ // more complex (i.e., longer) selectors are always more specific+ size_t l_len = lhs->length(), r_len = rhs->length();+ if (l_len > r_len)+ { return false; }++ if (l_len == 1)+ { return lhs->head()->is_superselector_of(rhs->last()->head(), wrapping); }++ // we have to look one tail deeper, since we cary the+ // combinator around for it (which is important here)+ if (rhs->tail() && lhs->tail() && combinator() != Complex_Selector::ANCESTOR_OF) {+ Complex_Selector* lhs_tail = lhs->tail();+ Complex_Selector* rhs_tail = rhs->tail();+ if (lhs_tail->combinator() != rhs_tail->combinator()) return false;+ if (lhs_tail->head() && !rhs_tail->head()) return false;+ if (!lhs_tail->head() && rhs_tail->head()) return false;+ if (lhs_tail->head() && lhs_tail->head()) {+ if (!lhs_tail->head()->is_superselector_of(rhs_tail->head())) return false;+ }+ }++ bool found = false;+ Complex_Selector* marker = rhs;+ for (size_t i = 0, L = rhs->length(); i < L; ++i) {+ if (i == L-1)+ { return false; }+ if (lhs->head() && marker->head() && lhs->head()->is_superselector_of(marker->head(), wrapping))+ { found = true; break; }+ marker = marker->tail();+ }+ if (!found)+ { return false; }++ /*+ Hmm, I hope I have the logic right:++ if lhs has a combinator:+ if !(marker has a combinator) return false+ if !(lhs.combinator == '~' ? marker.combinator != '>' : lhs.combinator == marker.combinator) return false+ return lhs.tail-without-innermost.is_superselector_of(marker.tail-without-innermost)+ else if marker has a combinator:+ if !(marker.combinator == ">") return false+ return lhs.tail.is_superselector_of(marker.tail)+ else+ return lhs.tail.is_superselector_of(marker.tail)+ */+ if (lhs->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() == Complex_Selector::ANCESTOR_OF)+ { return false; }+ if (!(lhs->combinator() == Complex_Selector::PRECEDES ? marker->combinator() != Complex_Selector::PARENT_OF : lhs->combinator() == marker->combinator()))+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ else if (marker->combinator() != Complex_Selector::ANCESTOR_OF)+ {+ if (marker->combinator() != Complex_Selector::PARENT_OF)+ { return false; }+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ else+ {+ return lhs->tail()->is_superselector_of(marker->tail());+ }+ // catch-all+ return false;+ }++ size_t Complex_Selector::length() const+ {+ // TODO: make this iterative+ if (!tail()) return 1;+ return 1 + tail()->length();+ }++ Complex_Selector* Complex_Selector::context(Context& ctx)+ {+ if (!tail()) return 0;+ if (!head()) return tail()->context(ctx);+ Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate(), combinator(), head(), tail()->context(ctx));+ cpy->media_block(media_block());+ return cpy;+ }++ // append another complex selector at the end+ // check if we need to append some headers+ // then we need to check for the combinator+ // only then we can safely set the new tail+ void Complex_Selector::append(Context& ctx, Complex_Selector* ss)+ {++ Complex_Selector* t = ss->tail();+ Combinator c = ss->combinator();+ String* r = ss->reference();+ Compound_Selector* h = ss->head();++ if (ss->has_line_feed()) has_line_feed(true);+ if (ss->has_line_break()) has_line_break(true);++ // append old headers+ if (h && h->length()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ error("Invalid parent selector", pstate_);+ } else if (last()->head_ && last()->head_->length()) {+ Compound_Selector* rh = last()->head();+ size_t i = 0, L = h->length();+ if (dynamic_cast<Type_Selector*>(h->first())) {+ if (Selector_Qualifier* sq = dynamic_cast<Selector_Qualifier*>(rh->last())) {+ Selector_Qualifier* sqs = new Selector_Qualifier(*sq);+ sqs->name(sqs->name() + (*h)[0]->name());+ (*rh)[rh->length()-1] = sqs;+ for (i = 1; i < L; ++i) *rh << (*h)[i];+ } else if (Type_Selector* ts = dynamic_cast<Type_Selector*>(rh->last())) {+ Type_Selector* tss = new Type_Selector(*ts);+ tss->name(tss->name() + (*h)[0]->name());+ (*rh)[rh->length()-1] = tss;+ for (i = 1; i < L; ++i) *rh << (*h)[i];+ } else if (Selector_Placeholder* ps = dynamic_cast<Selector_Placeholder*>(rh->last())) {+ Selector_Placeholder* pss = new Selector_Placeholder(*ps);+ pss->name(pss->name() + (*h)[0]->name());+ (*rh)[rh->length()-1] = pss;+ for (i = 1; i < L; ++i) *rh << (*h)[i];+ } else {+ *last()->head_ += h;+ }+ } else {+ *last()->head_ += h;+ }+ } else {+ *last()->head_ += h;+ }+ } else {+ // std::cerr << "has no or empty head\n";+ }++ if (last()) {+ if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {+ Complex_Selector* inter = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate());+ inter->reference(r);+ inter->combinator(c);+ inter->tail(t);+ last()->tail(inter);+ } else {+ if (last()->combinator() == ANCESTOR_OF) {+ last()->combinator(c);+ last()->reference(r);+ }+ last()->tail(t);+ }+ }+++ }++ Selector_List* Selector_List::parentize(Selector_List* ps, Context& ctx)+ {+ Selector_List* ss = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {+ Selector_List* list = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ *list << (*ps)[pi];+ for (size_t si = 0, sL = this->length(); si < sL; ++si) {+ *ss += (*this)[si]->parentize(list, ctx);+ }+ }+ return ss;+ }++ Selector_List* Complex_Selector::parentize(Selector_List* parents, Context& ctx)+ {++ Complex_Selector* tail = this->tail();+ Compound_Selector* head = this->head();++ // first parentize the tail (which may return an expanded list)+ Selector_List* tails = tail ? tail->parentize(parents, ctx) : 0;++ if (head && head->length() > 0) {++ // we have a parent selector in a simple compound list+ // mix parent complex selector into the compound list+ if (dynamic_cast<Parent_Selector*>((*head)[0])) {+ if (parents && parents->length()) {+ Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector* t = (*tails)[n];+ Complex_Selector* parent = (*parents)[i];+ Complex_Selector* s = parent->cloneFully(ctx);+ Complex_Selector* ss = this->clone(ctx);+ ss->tail(t ? t->clone(ctx) : 0);+ Compound_Selector* h = head_->clone(ctx);+ if (h->length()) h->erase(h->begin());+ ss->head(h->length() ? h : 0);+ s->append(ctx, ss);+ *retval << s;+ }+ }+ }+ // have no tails but parents+ // loop above is inside out+ else {+ for (size_t i = 0, iL = parents->length(); i < iL; ++i) {+ Complex_Selector* parent = (*parents)[i];+ Complex_Selector* s = parent->cloneFully(ctx);+ Complex_Selector* ss = this->clone(ctx);+ // this is only if valid if the parent has no trailing op+ // otherwise we cannot append more simple selectors to head+ if (parent->last()->combinator() != ANCESTOR_OF) {+ throw Exception::InvalidParent(parent, ss);+ }+ ss->tail(tail ? tail->clone(ctx) : 0);+ Compound_Selector* h = head_->clone(ctx);+ if (h->length()) h->erase(h->begin());+ ss->head(h->length() ? h : 0);+ // \/ IMO ruby sass bug \/+ ss->has_line_feed(false);+ s->append(ctx, ss);+ *retval << s;+ }+ }+ return retval;+ }+ // have no parent but some tails+ else {+ Selector_List* retval = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ if (tails && tails->length() > 0) {+ for (size_t n = 0, nL = tails->length(); n < nL; ++n) {+ Complex_Selector* cpy = this->clone(ctx);+ cpy->tail((*tails)[n]->cloneFully(ctx));+ cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ *cpy->head() << (*this->head())[i];+ if (!cpy->head()->length()) cpy->head(0);+ *retval << cpy->skip_empty_reference();+ }+ }+ // have no parent nor tails+ else {+ Complex_Selector* cpy = this->clone(ctx);+ cpy->head(SASS_MEMORY_NEW(ctx.mem, Compound_Selector, head->pstate()));+ for (size_t i = 1, L = this->head()->length(); i < L; ++i)+ *cpy->head() << (*this->head())[i];+ if (!cpy->head()->length()) cpy->head(0);+ *retval << cpy->skip_empty_reference();+ }+ return retval;+ }+ }+ // no parent selector in head+ else {+ return this->tails(ctx, tails);+ }++ }+ // has no head+ else {+ return this->tails(ctx, tails);+ }++ // unreachable+ return 0;+ }++ Selector_List* Complex_Selector::tails(Context& ctx, Selector_List* tails)+ {+ Selector_List* rv = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate_);+ if (tails && tails->length()) {+ for (size_t i = 0, iL = tails->length(); i < iL; ++i) {+ Complex_Selector* pr = this->clone(ctx);+ pr->tail((*tails)[i]);+ *rv << pr;+ }+ }+ else {+ *rv << this;+ }+ return rv;+ }++ // return the last tail that is defined+ Complex_Selector* Complex_Selector::first()+ {+ // declare variables used in loop+ Complex_Selector* cur = this->tail_;+ const Compound_Selector* head = head_;+ // processing loop+ while (cur)+ {+ // get the head+ head = cur->head_;+ // check for single parent ref+ if (head && head->length() == 1)+ {+ // abort (and return) if it is not a parent selector+ if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;+ }+ // advance to next+ cur = cur->tail_;+ }+ // result+ return cur;+ }++ // return the last tail that is defined+ const Complex_Selector* Complex_Selector::first() const+ {+ // declare variables used in loop+ const Complex_Selector* cur = this->tail_;+ const Compound_Selector* head = head_;+ // processing loop+ while (cur)+ {+ // get the head+ head = cur->head_;+ // check for single parent ref+ if (head && head->length() == 1)+ {+ // abort (and return) if it is not a parent selector+ if (!dynamic_cast<Parent_Selector*>((*head)[0])) break;+ }+ // advance to next+ cur = cur->tail_;+ }+ // result+ return cur;+ }++ // return the last tail that is defined+ Complex_Selector* Complex_Selector::last()+ {+ // ToDo: implement with a while loop+ return tail_? tail_->last() : this;+ }++ // return the last tail that is defined+ const Complex_Selector* Complex_Selector::last() const+ {+ // ToDo: implement with a while loop+ return tail_? tail_->last() : this;+ }+++ Complex_Selector::Combinator Complex_Selector::clear_innermost()+ {+ Combinator c;+ if (!tail() || tail()->tail() == 0)+ { c = combinator(); combinator(ANCESTOR_OF); tail(0); }+ else+ { c = tail()->clear_innermost(); }+ return c;+ }++ void Complex_Selector::set_innermost(Complex_Selector* val, Combinator c)+ {+ if (!tail())+ { tail(val); combinator(c); }+ else+ { tail()->set_innermost(val, c); }+ }++ Complex_Selector* Complex_Selector::clone(Context& ctx) const+ {+ Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);+ cpy->media_block(this->media_block());+ if (tail()) cpy->tail(tail()->clone(ctx));+ return cpy;+ }++ Complex_Selector* Complex_Selector::cloneFully(Context& ctx) const+ {+ Complex_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, *this);++ if (head()) {+ cpy->head(head()->clone(ctx));+ }++ if (tail()) {+ cpy->tail(tail()->cloneFully(ctx));+ }++ return cpy;+ }++ Compound_Selector* Compound_Selector::clone(Context& ctx) const+ {+ Compound_Selector* cpy = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, *this);+ cpy->media_block(this->media_block());+ return cpy;+ }++ Selector_List* Selector_List::clone(Context& ctx) const+ {+ Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, *this);+ cpy->media_block(this->media_block());+ return cpy;+ }++ Selector_List* Selector_List::cloneFully(Context& ctx) const+ {+ Selector_List* cpy = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ for (size_t i = 0, L = length(); i < L; ++i) {+ *cpy << (*this)[i]->cloneFully(ctx);+ }+ return cpy;+ }++ /* not used anymore - remove?+ Selector_Placeholder* Selector::find_placeholder()+ {+ return 0;+ }*/++ // remove parent selector references+ // basically unwraps parsed selectors+ void Selector_List::remove_parent_selectors()+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = length(); i < L; ++i) {+ if (!(*this)[i]->head()) continue;+ if ((*this)[i]->head()->is_empty_reference()) {+ // simply move to the next tail if we have "no" combinator+ if ((*this)[i]->combinator() == Complex_Selector::ANCESTOR_OF) {+ if ((*this)[i]->tail() && (*this)[i]->has_line_feed()) {+ (*this)[i]->tail()->has_line_feed(true);+ }+ (*this)[i] = (*this)[i]->tail();+ }+ // otherwise remove the first item from head+ else {+ (*this)[i]->head()->erase((*this)[i]->head()->begin());+ }+ }+ }+ }++ void Selector_List::adjust_after_pushing(Complex_Selector* c)+ {+ if (c->has_reference()) has_reference(true);+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Complex_Selector::is_superselector_of(Selector_List *sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of((*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector of the right side+ // list is a superselector of the given left side selector+ bool Selector_List::is_superselector_of(Selector_List *sub, std::string wrapping)+ {+ // Check every rhs selector against left hand list+ for(size_t i = 0, L = sub->length(); i < L; ++i) {+ if (!is_superselector_of((*sub)[i], wrapping)) return false;+ }+ return true;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Compound_Selector *sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub, wrapping)) return true;+ }+ return false;+ }++ // it's a superselector if every selector on the right side+ // is a superselector of any one of the left side selectors+ bool Selector_List::is_superselector_of(Complex_Selector *sub, std::string wrapping)+ {+ // Check every lhs selector against right hand+ for(size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_superselector_of(sub)) return true;+ }+ return false;+ }++ Selector_List* Selector_List::unify_with(Selector_List* rhs, Context& ctx) {+ std::vector<Complex_Selector*> unified_complex_selectors;+ // Unify all of children with RHS's children, storing the results in `unified_complex_selectors`+ for (size_t lhs_i = 0, lhs_L = length(); lhs_i < lhs_L; ++lhs_i) {+ Complex_Selector* seq1 = (*this)[lhs_i];+ for(size_t rhs_i = 0, rhs_L = rhs->length(); rhs_i < rhs_L; ++rhs_i) {+ Complex_Selector* seq2 = (*rhs)[rhs_i];++ Selector_List* result = seq1->unify_with(seq2, ctx);+ if( result ) {+ for(size_t i = 0, L = result->length(); i < L; ++i) {+ unified_complex_selectors.push_back( (*result)[i] );+ }+ }+ }+ }++ // Creates the final Selector_List by combining all the complex selectors+ Selector_List* final_result = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate());+ for (auto itr = unified_complex_selectors.begin(); itr != unified_complex_selectors.end(); ++itr) {+ *final_result << *itr;+ }+ return final_result;+ }++ void Selector_List::populate_extends(Selector_List* extendee, Context& ctx, ExtensionSubsetMap& extends) {+ To_String to_string;++ Selector_List* extender = this;+ for (auto complex_sel : extendee->elements()) {+ Complex_Selector* c = complex_sel;+++ // Ignore any parent selectors, until we find the first non Selector_Reference head+ Compound_Selector* compound_sel = c->head();+ Complex_Selector* pIter = complex_sel;+ while (pIter) {+ Compound_Selector* pHead = pIter->head();+ if (pHead && dynamic_cast<Parent_Selector*>(pHead->elements()[0]) == NULL) {+ compound_sel = pHead;+ break;+ }++ pIter = pIter->tail();+ }++ if (!pIter->head() || pIter->tail()) {+ error("nested selectors may not be extended", c->pstate());+ }++ compound_sel->is_optional(extendee->is_optional());++ for (size_t i = 0, L = extender->length(); i < L; ++i) {+ extends.put(compound_sel->to_str_vec(), std::make_pair((*extender)[i], compound_sel));+ }+ }+ };++ std::vector<std::string> Compound_Selector::to_str_vec()+ {+ To_String to_string;+ std::vector<std::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 = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate());+ // result->has_parent_reference(has_parent_reference());++ // not very efficient because it needs to preserve order+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ bool found = false;+ std::string thisSelector((*this)[i]->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));+ }+ }++ void Arguments::adjust_after_pushing(Argument* a)+ {+ if (!a->name().empty()) {+ if (/* has_rest_argument_ || */ has_keyword_argument_) {+ error("named arguments must precede variable-length argument", a->pstate());+ }+ has_named_arguments_ = true;+ }+ else if (a->is_rest_argument()) {+ if (has_rest_argument_) {+ error("functions and mixins may only be called with one variable-length argument", a->pstate());+ }+ if (has_keyword_argument_) {+ error("only keyword arguments may follow variable arguments", a->pstate());+ }+ has_rest_argument_ = true;+ }+ else if (a->is_keyword_argument()) {+ if (has_keyword_argument_) {+ error("functions and mixins may only be called with one keyword argument", a->pstate());+ }+ has_keyword_argument_ = true;+ }+ else {+ if (has_rest_argument_) {+ error("ordinal arguments must precede variable-length arguments", a->pstate());+ }+ if (has_named_arguments_) {+ error("ordinal arguments must precede named arguments", a->pstate());+ }+ }+ }++ bool Ruleset::is_invisible() const {+ Selector_List* sl = static_cast<Selector_List*>(selector());+ for (size_t i = 0, L = sl->length(); i < L; ++i)+ if (!(*sl)[i]->has_placeholder()) return false;+ return true;+ }++ bool Media_Block::is_invisible() const {+ for (size_t i = 0, L = block()->length(); i < L; ++i) {+ if (!(*block())[i]->is_invisible()) return false;+ }+ return true;+ }++ Number::Number(ParserState pstate, double val, std::string u, bool zero)+ : Value(pstate),+ value_(val),+ zero_(zero),+ numerator_units_(std::vector<std::string>()),+ denominator_units_(std::vector<std::string>()),+ hash_(0)+ {+ size_t l = 0, r = 0;+ if (!u.empty()) {+ bool nominator = true;+ while (true) {+ r = u.find_first_of("*/", l);+ std::string unit(u.substr(l, r == std::string::npos ? r : r - l));+ if (!unit.empty()) {+ if (nominator) numerator_units_.push_back(unit);+ else denominator_units_.push_back(unit);+ }+ if (r == std::string::npos) break;+ // ToDo: should error for multiple slashes+ // if (!nominator && u[r] == '/') error(...)+ if (u[r] == '/')+ nominator = false;+ l = r + 1;+ }+ }+ concrete_type(NUMBER);+ }++ std::string Number::unit() const+ {+ std::string u;+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ if (i) u += '*';+ u += numerator_units_[i];+ }+ if (!denominator_units_.empty()) u += '/';+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ if (i) u += '*';+ u += denominator_units_[i];+ }+ return u;+ }++ bool Number::is_unitless()+ { return numerator_units_.empty() && denominator_units_.empty(); }++ void Number::normalize(const std::string& prefered, bool strict)+ {++ // first make sure same units cancel each other out+ // it seems that a map table will fit nicely to do this+ // we basically construct exponents for each unit+ // has the advantage that they will be pre-sorted+ std::map<std::string, int> exponents;++ // initialize by summing up occurences in unit vectors+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++ // the final conversion factor+ double factor = 1;++ // get the first entry of numerators+ // forward it when entry is converted+ std::vector<std::string>::iterator nom_it = numerator_units_.begin();+ std::vector<std::string>::iterator nom_end = numerator_units_.end();+ std::vector<std::string>::iterator denom_it = denominator_units_.begin();+ std::vector<std::string>::iterator denom_end = denominator_units_.end();++ // main normalization loop+ // should be close to optimal+ while (denom_it != denom_end)+ {+ // get and increment afterwards+ const std::string denom = *(denom_it ++);+ // skip already canceled out unit+ if (exponents[denom] >= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(denom) == UNKNOWN) continue;+ // now search for nominator+ while (nom_it != nom_end)+ {+ // get and increment afterwards+ const std::string nom = *(nom_it ++);+ // skip already canceled out unit+ if (exponents[nom] <= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(nom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(nom, denom, strict);+ // update nominator/denominator exponent+ -- exponents[nom]; ++ exponents[denom];+ // inner loop done+ break;+ }+ }++ // now we can build up the new unit arrays+ numerator_units_.clear();+ denominator_units_.clear();++ // build them by iterating over the exponents+ for (auto exp : exponents)+ {+ // maybe there is more effecient way to push+ // the same item multiple times to a vector?+ for(size_t i = 0, S = abs(exp.second); i < S; ++i)+ {+ // opted to have these switches in the inner loop+ // makes it more readable and should not cost much+ if (!exp.first.empty()) {+ if (exp.second < 0) denominator_units_.push_back(exp.first);+ else if (exp.second > 0) numerator_units_.push_back(exp.first);+ }+ }+ }++ // apply factor to value_+ // best precision this way+ value_ *= factor;++ // maybe convert to other unit+ // easier implemented on its own+ try { convert(prefered, strict); }+ catch (incompatibleUnits& err)+ { error(err.what(), pstate()); }+ catch (...) { throw; }++ }++ void Number::convert(const std::string& prefered, bool strict)+ {+ // abort if unit is empty+ if (prefered.empty()) return;++ // first make sure same units cancel each other out+ // it seems that a map table will fit nicely to do this+ // we basically construct exponents for each unit+ // has the advantage that they will be pre-sorted+ std::map<std::string, int> exponents;++ // initialize by summing up occurences in unit vectors+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) ++ exponents[numerator_units_[i]];+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) -- exponents[denominator_units_[i]];++ // the final conversion factor+ double factor = 1;++ std::vector<std::string>::iterator denom_it = denominator_units_.begin();+ std::vector<std::string>::iterator denom_end = denominator_units_.end();++ // main normalization loop+ // should be close to optimal+ while (denom_it != denom_end)+ {+ // get and increment afterwards+ const std::string denom = *(denom_it ++);+ // check if conversion is needed+ if (denom == prefered) continue;+ // skip already canceled out unit+ if (exponents[denom] >= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(denom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(denom, prefered, strict);+ // update nominator/denominator exponent+ ++ exponents[denom]; -- exponents[prefered];+ }++ std::vector<std::string>::iterator nom_it = numerator_units_.begin();+ std::vector<std::string>::iterator nom_end = numerator_units_.end();++ // now search for nominator+ while (nom_it != nom_end)+ {+ // get and increment afterwards+ const std::string nom = *(nom_it ++);+ // check if conversion is needed+ if (nom == prefered) continue;+ // skip already canceled out unit+ if (exponents[nom] <= 0) continue;+ // skip all units we don't know how to convert+ if (string_to_unit(nom) == UNKNOWN) continue;+ // we now have two convertable units+ // add factor for current conversion+ factor *= conversion_factor(nom, prefered, strict);+ // update nominator/denominator exponent+ -- exponents[nom]; ++ exponents[prefered];+ }++ // now we can build up the new unit arrays+ numerator_units_.clear();+ denominator_units_.clear();++ // build them by iterating over the exponents+ for (auto exp : exponents)+ {+ // maybe there is more effecient way to push+ // the same item multiple times to a vector?+ for(size_t i = 0, S = abs(exp.second); i < S; ++i)+ {+ // opted to have these switches in the inner loop+ // makes it more readable and should not cost much+ if (!exp.first.empty()) {+ if (exp.second < 0) denominator_units_.push_back(exp.first);+ else if (exp.second > 0) numerator_units_.push_back(exp.first);+ }+ }+ }++ // apply factor to value_+ // best precision this way+ value_ *= factor;++ }++ // useful for making one number compatible with another+ std::string Number::find_convertible_unit() const+ {+ for (size_t i = 0, S = numerator_units_.size(); i < S; ++i) {+ std::string u(numerator_units_[i]);+ if (string_to_unit(u) != UNKNOWN) return u;+ }+ for (size_t i = 0, S = denominator_units_.size(); i < S; ++i) {+ std::string u(denominator_units_[i]);+ if (string_to_unit(u) != UNKNOWN) return u;+ }+ return std::string();+ }++ bool Custom_Warning::operator== (const Expression& rhs) const+ {+ if (const Custom_Warning* r = dynamic_cast<const Custom_Warning*>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Custom_Error::operator== (const Expression& rhs) const+ {+ if (const Custom_Error* r = dynamic_cast<const Custom_Error*>(&rhs)) {+ return message() == r->message();+ }+ return false;+ }++ bool Number::operator== (const Expression& rhs) const+ {+ if (const Number* r = dynamic_cast<const Number*>(&rhs)) {+ return (numerator_units_ == r->numerator_units_) &&+ (denominator_units_ == r->denominator_units_) &&+ std::fabs(value() - r->value()) < NUMBER_EPSILON;+ }+ return false;+ }++ bool Number::operator< (const Number& rhs) const+ {+ Number tmp_r(rhs);+ tmp_r.normalize(find_convertible_unit());+ std::string l_unit(unit());+ std::string r_unit(tmp_r.unit());+ if (!l_unit.empty() && !r_unit.empty() && unit() != tmp_r.unit()) {+ error("cannot compare numbers with incompatible units", pstate());+ }+ return value() < tmp_r.value();+ }++ bool String_Quoted::operator== (const Expression& rhs) const+ {+ if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {+ return (value() == qstr->value());+ } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Constant::operator== (const Expression& rhs) const+ {+ if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(&rhs)) {+ return (value() == qstr->value());+ } else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(&rhs)) {+ return (value() == cstr->value());+ }+ return false;+ }++ bool String_Schema::operator== (const Expression& rhs) const+ {+ if (const String_Schema* r = dynamic_cast<const String_Schema*>(&rhs)) {+ if (length() != r->length()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression* rv = (*r)[i];+ Expression* lv = (*this)[i];+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Boolean::operator== (const Expression& rhs) const+ {+ if (const Boolean* r = dynamic_cast<const Boolean*>(&rhs)) {+ return (value() == r->value());+ }+ return false;+ }++ bool Color::operator== (const Expression& rhs) const+ {+ if (const Color* r = dynamic_cast<const Color*>(&rhs)) {+ return r_ == r->r() &&+ g_ == r->g() &&+ b_ == r->b() &&+ a_ == r->a();+ }+ return false;+ }++ bool List::operator== (const Expression& rhs) const+ {+ if (const List* r = dynamic_cast<const List*>(&rhs)) {+ if (length() != r->length()) return false;+ if (separator() != r->separator()) return false;+ for (size_t i = 0, L = length(); i < L; ++i) {+ Expression* rv = (*r)[i];+ Expression* lv = (*this)[i];+ if (!lv || !rv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Map::operator== (const Expression& rhs) const+ {+ if (const Map* r = dynamic_cast<const Map*>(&rhs)) {+ if (length() != r->length()) return false;+ for (auto key : keys()) {+ Expression* lv = at(key);+ Expression* rv = r->at(key);+ if (!rv || !lv) return false;+ if (!(*lv == *rv)) return false;+ }+ return true;+ }+ return false;+ }++ bool Null::operator== (const Expression& rhs) const+ {+ return rhs.concrete_type() == NULL_VAL;+ }++ size_t List::size() const {+ if (!is_arglist_) return length();+ // arglist expects a list of arguments+ // so we need to break before keywords+ for (size_t i = 0, L = length(); i < L; ++i) {+ if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {+ if (!arg->name().empty()) return i;+ }+ }+ return length();+ }++ Expression* Hashed::at(Expression* k) const+ {+ if (elements_.count(k))+ { return elements_.at(k); }+ else { return &sass_null; }+ }++ std::string Map::to_string(bool compressed, int precision) const+ {+ std::string res("");+ if (empty()) return res;+ if (is_invisible()) return res;+ bool items_output = false;+ for (auto key : keys()) {+ if (key->is_invisible()) continue;+ if (at(key)->is_invisible()) continue;+ if (items_output) res += compressed ? "," : ", ";+ Value* v_key = dynamic_cast<Value*>(key);+ Value* v_val = dynamic_cast<Value*>(at(key));+ if (v_key) res += v_key->to_string(compressed, precision);+ res += compressed ? ":" : ": ";+ if (v_val) res += v_val->to_string(compressed, precision);+ items_output = true;+ }+ return res;+ }++ std::string List::to_string(bool compressed, int precision) const+ {+ std::string res("");+ if (empty()) return res;+ if (is_invisible()) return res;+ bool items_output = false;+ std::string sep = separator() == SASS_COMMA ? "," : " ";+ if (!compressed && sep == ",") sep += " ";+ for (size_t i = 0, L = size(); i < L; ++i) {+ Expression* item = (*this)[i];+ if (item->is_invisible()) continue;+ if (items_output) res += sep;+ if (Value* v_val = dynamic_cast<Value*>(item))+ { res += v_val->to_string(compressed, precision); }+ items_output = true;+ }+ return res;+ }++ std::string String_Schema::to_string(bool compressed, int precision) const+ {+ std::string res("");+ for (size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->is_interpolant()) res += "#{";+ if (Value* val = dynamic_cast<Value*>((*this)[i]))+ { res += val->to_string(compressed, precision); }+ if ((*this)[i]->is_interpolant()) res += "}";+ }+ return res;+ }++ std::string Null::to_string(bool compressed, int precision) const+ {+ return "null";+ }++ std::string Boolean::to_string(bool compressed, int precision) const+ {+ return value_ ? "true" : "false";+ }++ // helper function for serializing colors+ template <size_t range>+ static double cap_channel(double c) {+ if (c > range) return range;+ else if (c < 0) return 0;+ else return c;+ }++ std::string Color::to_string(bool compressed, int precision) const+ {+ std::stringstream ss;++ // original color name+ // maybe an unknown token+ std::string name = disp();++ // resolved color+ std::string res_name = name;++ double r = Sass::round(cap_channel<0xff>(r_));+ double g = Sass::round(cap_channel<0xff>(g_));+ double b = Sass::round(cap_channel<0xff>(b_));+ double a = cap_channel<1> (a_);++ // get color from given name (if one was given at all)+ if (name != "" && name_to_color(name)) {+ const Color* n = name_to_color(name);+ r = Sass::round(cap_channel<0xff>(n->r()));+ g = Sass::round(cap_channel<0xff>(n->g()));+ b = Sass::round(cap_channel<0xff>(n->b()));+ a = cap_channel<1> (n->a());+ }+ // otherwise get the possible resolved color name+ else {+ double numval = r * 0x10000 + g * 0x100 + b;+ if (color_to_name(numval))+ res_name = color_to_name(numval);+ }++ std::stringstream hexlet;+ hexlet << '#' << std::setw(1) << std::setfill('0');+ // create a short color hexlet if there is any need for it+ if (compressed && is_color_doublet(r, g, b) && a == 1) {+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(r) >> 4);+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(g) >> 4);+ hexlet << std::hex << std::setw(1) << (static_cast<unsigned long>(b) >> 4);+ } else {+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(r);+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(g);+ hexlet << std::hex << std::setw(2) << static_cast<unsigned long>(b);+ }++ if (compressed && !this->is_delayed()) name = "";++ // retain the originally specified color definition if unchanged+ if (name != "") {+ ss << name;+ }+ else if (r == 0 && g == 0 && b == 0 && a == 0) {+ ss << "transparent";+ }+ else if (a >= 1) {+ if (res_name != "") {+ if (compressed && hexlet.str().size() < res_name.size()) {+ ss << hexlet.str();+ } else {+ ss << res_name;+ }+ }+ else {+ ss << hexlet.str();+ }+ }+ else {+ ss << "rgba(";+ ss << static_cast<unsigned long>(r) << ",";+ if (!compressed) ss << " ";+ ss << static_cast<unsigned long>(g) << ",";+ if (!compressed) ss << " ";+ ss << static_cast<unsigned long>(b) << ",";+ if (!compressed) ss << " ";+ ss << a << ')';+ }++ return ss.str();++ }++ std::string Number::to_string(bool compressed, int precision) const+ {++ std::string res;++ // check if the fractional part of the value equals to zero+ // neat trick from http://stackoverflow.com/a/1521682/1550314+ // double int_part; bool is_int = modf(value, &int_part) == 0.0;++ // this all cannot be done with one run only, since fixed+ // output differs from normal output and regular output+ // can contain scientific notation which we do not want!++ // first sample+ std::stringstream ss;+ ss.precision(12);+ ss << value_;++ // check if we got scientific notation in result+ if (ss.str().find_first_of("e") != std::string::npos) {+ ss.clear(); ss.str(std::string());+ ss.precision(std::max(12, precision));+ ss << std::fixed << value_;+ }++ std::string tmp = ss.str();+ size_t pos_point = tmp.find_first_of(".,");+ size_t pos_fract = tmp.find_last_not_of("0");+ bool is_int = pos_point == pos_fract ||+ pos_point == std::string::npos;++ // reset stream for another run+ ss.clear(); ss.str(std::string());++ // take a shortcut for integers+ if (is_int)+ {+ ss.precision(0);+ ss << std::fixed << value_;+ res = std::string(ss.str());+ }+ // process floats+ else+ {+ // do we have have too much precision?+ if (pos_fract < precision + pos_point)+ { precision = (int)(pos_fract - pos_point); }+ // round value again+ ss.precision(precision);+ ss << std::fixed << value_;+ res = std::string(ss.str());+ // maybe we truncated up to decimal point+ size_t pos = res.find_last_not_of("0");+ bool at_dec_point = res[pos] == '.' ||+ res[pos] == ',';+ // don't leave a blank point+ if (at_dec_point) ++ pos;+ res.resize (pos + 1);+ }++ // some final cosmetics+ if (res == "-0.0") res.erase(0, 1);+ else if (res == "-0") res.erase(0, 1);+ else if (res == "") res = "0";++ // add unit now+ res += unit();++ // and return+ return res;++ }++ std::string String_Quoted::to_string(bool compressed, int precision) const+ {+ return quote_mark_ ? quote(value_, quote_mark_, true) : value_;+ }++ std::string String_Constant::to_string(bool compressed, int precision) const+ {+ return quote_mark_ ? quote(value_, quote_mark_, true) : value_;+ }++ std::string Custom_Error::to_string(bool compressed, int precision) const+ {+ return message();+ }+ std::string Custom_Warning::to_string(bool compressed, int precision) const+ {+ return message();+ }++ std::string Selector_List::to_string(bool compressed, int precision) const+ {+ std::string str("");+ auto end = this->end();+ auto start = this->begin();+ while (start < end && *start) {+ Complex_Selector* sel = *start;+ if (!str.empty()) str += ", ";+ str += sel->to_string(compressed, precision);+ ++ start;+ }+ return str;+ }++ std::string Compound_Selector::to_string(bool compressed, int precision) const+ {+ std::string str("");+ auto end = this->end();+ auto start = this->begin();+ while (start < end && *start) {+ Simple_Selector* sel = *start;+ str += sel->to_string(compressed, precision);+ ++ start;+ }+ return str;+ }++ std::string Complex_Selector::to_string(bool compressed, int precision) const+ {+ // first render head and tail if they are available+ std::string str_head(head() ? head()->to_string(compressed, precision) : "");+ std::string str_tail(tail() ? tail()->to_string(compressed, precision) : "");+ std::string str_ref(reference() ? reference()->to_string(compressed, precision) : "");+ // combinator in between+ std::string str_op("");+ // use a switch statement+ switch (combinator()) {+ case ANCESTOR_OF: str_op = " "; break;+ case PARENT_OF: str_op = ">"; break;+ case PRECEDES: str_op = "~"; break;+ case ADJACENT_TO: str_op = "+"; break;+ case REFERENCE: str_op = "/" + str_ref + "/"; break;+ }+ // prettify for non ancestors+ if (combinator() != ANCESTOR_OF) {+ // no spaces needed for compressed+ if (compressed == false) {+ // make sure we add some spaces where needed+ if (str_tail != "") str_op += " ";+ if (str_head != "") str_head += " ";+ }+ }+ // is ancestor with no tail+ else if (str_tail == "") {+ str_op = ""; // superflous+ }+ // now build the final result+ return str_head + str_op + str_tail;+ }++ std::string Selector_Schema::to_string(bool compressed, int precision) const+ {+ return contents()->to_string(compressed, precision);+ }++ std::string Parent_Selector::to_string(bool compressed, int precision) const+ {+ return "&";+ }++ std::string Attribute_Selector::to_string(bool compressed, int precision) const+ {+ std::string val(value() ? value()->to_string(compressed, precision) : "");+ return "[" + this->ns_name() + this->matcher() + val + "]";+ }++ std::string Wrapped_Selector::to_string(bool compressed, int precision) const+ {+ // first render the+ std::string main(this->Simple_Selector::to_string(compressed, precision));+ std::string wrapped(selector() ? selector()->to_string(compressed, precision) : "");+ // now build the final result+ return main + "(" + wrapped + ")";+ }++ //////////////////////////////////////////////////////////////////////////////////////////+ // Additional method on Lists to retrieve values directly or from an encompassed Argument.+ //////////////////////////////////////////////////////////////////////////////////////////+ Expression* List::value_at_index(size_t i) {+ if (is_arglist_) {+ if (Argument* arg = dynamic_cast<Argument*>((*this)[i])) {+ return arg->value();+ } else {+ return (*this)[i];+ }+ } else {+ return (*this)[i];+ }+ }++}+
+ libsass/src/ast.hpp view
@@ -0,0 +1,2347 @@+#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>+#include "sass/base.h"++#ifdef __clang__++/*+ * There are some overloads used here that trigger the clang overload+ * hiding warning. Specifically:+ *+ * Type type() which hides string type() from Expression+ *+ * and+ *+ * Block* block() which hides virtual Block* block() from Statement+ *+ */++#pragma clang diagnostic push+#pragma clang diagnostic ignored "-Woverloaded-virtual"++#endif++#include "util.hpp"+#include "units.hpp"+#include "context.hpp"+#include "position.hpp"+#include "constants.hpp"+#include "operation.hpp"+#include "position.hpp"+#include "inspect.hpp"+#include "source_map.hpp"+#include "environment.hpp"+#include "error_handling.hpp"+#include "ast_def_macros.hpp"+#include "ast_fwd_decl.hpp"+#include "to_string.hpp"+#include "source_map.hpp"++#include "sass.h"++namespace Sass {++ const double NUMBER_EPSILON = 0.00000000000001;++ // from boost (functional/hash):+ // http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html+ // Boost Software License - Version 1.0+ // http://www.boost.org/users/license.html+ template <typename T>+ void hash_combine (std::size_t& seed, const T& val)+ {+ seed ^= std::hash<T>()(val) + 0x9e3779b9+ + (seed<<6) + (seed>>2);+ }++ //////////////////////////////////////////////////////////+ // Abstract base class for all abstract syntax tree nodes.+ //////////////////////////////////////////////////////////+ class AST_Node : public Memory_Object {+ ADD_PROPERTY(ParserState, pstate)+ public:+ AST_Node(ParserState pstate)+ : pstate_(pstate)+ { }+ virtual ~AST_Node() = 0;+ // virtual Block* block() { return 0; }+ public:+ void update_pstate(const ParserState& pstate);+ public:+ Offset off() { return pstate(); }+ Position pos() { return pstate(); }+ ATTACH_OPERATIONS()+ };+ inline AST_Node::~AST_Node() { }+++ //////////////////////////////////////////////////////////////////////+ // Abstract base class for expressions. This side of the AST hierarchy+ // represents elements in value contexts, which exist primarily to be+ // evaluated and returned.+ //////////////////////////////////////////////////////////////////////+ class Expression : public AST_Node {+ public:+ enum Concrete_Type {+ NONE,+ BOOLEAN,+ NUMBER,+ COLOR,+ STRING,+ LIST,+ MAP,+ SELECTOR,+ NULL_VAL,+ C_WARNING,+ C_ERROR,+ NUM_TYPES+ };+ private:+ // expressions in some contexts shouldn't be evaluated+ ADD_PROPERTY(bool, is_delayed)+ ADD_PROPERTY(bool, is_expanded)+ ADD_PROPERTY(bool, is_interpolant)+ ADD_PROPERTY(Concrete_Type, concrete_type)+ public:+ Expression(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : AST_Node(pstate),+ is_delayed_(d),+ is_expanded_(d),+ is_interpolant_(i),+ concrete_type_(ct)+ { }+ virtual operator bool() { return true; }+ virtual ~Expression() { }+ virtual std::string type() { return ""; /* TODO: raise an error? */ }+ virtual bool is_invisible() const { return false; }+ static std::string type_name() { return ""; }+ virtual bool is_false() { return false; }+ virtual bool operator== (const Expression& rhs) const { return false; }+ virtual void set_delayed(bool delayed) { is_delayed(delayed); }+ virtual size_t hash() { return 0; }+ };++ //////////////////////////////////////////////////////////////////////+ // base class for values that support operations+ //////////////////////////////////////////////////////////////////////+ class Value : public Expression {+ public:+ Value(ParserState pstate,+ bool d = false, bool e = false, bool i = false, Concrete_Type ct = NONE)+ : Expression(pstate, d, e, i, ct)+ { }+ virtual bool operator== (const Expression& rhs) const = 0;+ virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;+ };+}++/////////////////////////////////////////////////////////////////////////////////////+// Hash method specializations for std::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->hash() == rhs->hash();+ }+ };+}++namespace Sass {++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like vectors. Uses the+ // "Template Method" design pattern to allow subclasses to adjust their flags+ // when certain objects are pushed.+ /////////////////////////////////////////////////////////////////////////////+ template <typename T>+ class Vectorized {+ std::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_(std::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 first() { return elements_.front(); }+ T& operator[](size_t i) { return elements_[i]; }+ virtual const T& at(size_t i) const { return elements_.at(i); }+ const T& operator[](size_t i) const { return elements_[i]; }+ Vectorized& operator<<(T element)+ {+ if (!element) return *this;+ reset_hash();+ elements_.push_back(element);+ adjust_after_pushing(element);+ return *this;+ }+ Vectorized& operator+=(Vectorized* v)+ {+ for (size_t i = 0, L = v->length(); i < L; ++i) *this << (*v)[i];+ return *this;+ }+ Vectorized& unshift(T element)+ {+ elements_.insert(elements_.begin(), element);+ return *this;+ }+ std::vector<T>& elements() { return elements_; }+ const std::vector<T>& elements() const { return elements_; }+ std::vector<T>& elements(std::vector<T>& e) { elements_ = e; return elements_; }++ typename std::vector<T>::iterator end() { return elements_.end(); }+ typename std::vector<T>::iterator begin() { return elements_.begin(); }+ typename std::vector<T>::const_iterator end() const { return elements_.end(); }+ typename std::vector<T>::const_iterator begin() const { return elements_.begin(); }+ typename std::vector<T>::iterator erase(typename std::vector<T>::iterator el) { return elements_.erase(el); }+ typename std::vector<T>::const_iterator erase(typename std::vector<T>::const_iterator el) { return elements_.erase(el); }++ };+ template <typename T>+ inline Vectorized<T>::~Vectorized() { }++ /////////////////////////////////////////////////////////////////////////////+ // Mixin class for AST nodes that should behave like a hash table. Uses an+ // extra <std::vector> internally to maintain insertion order for interation.+ /////////////////////////////////////////////////////////////////////////////+ class Hashed {+ private:+ std::unordered_map<Expression*, Expression*> elements_;+ std::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_(std::unordered_map<Expression*, Expression*>(s)), list_(std::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;+ bool has_duplicate_key() const { return duplicate_key_ != 0; }+ Expression* get_duplicate_key() const { return duplicate_key_; }+ const std::unordered_map<Expression*, Expression*> elements() { return elements_; }+ Hashed& operator<<(std::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 << std::make_pair(key, h->at(key));+ }++ reset_duplicate_key();+ return *this;+ }+ const std::unordered_map<Expression*, Expression*>& pairs() const { return elements_; }+ const std::vector<Expression*>& keys() const { return list_; }++ std::unordered_map<Expression*, Expression*>::iterator end() { return elements_.end(); }+ std::unordered_map<Expression*, Expression*>::iterator begin() { return elements_.begin(); }+ std::unordered_map<Expression*, Expression*>::const_iterator end() const { return elements_.end(); }+ std::unordered_map<Expression*, Expression*>::const_iterator begin() const { return elements_.begin(); }++ };+ inline Hashed::~Hashed() { }+++ /////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements. This side of the AST hierarchy+ // represents elements in expansion contexts, which exist primarily to be+ // rewritten and macro-expanded.+ /////////////////////////////////////////////////////////////////////////+ class Statement : public AST_Node {+ public:+ enum Statement_Type {+ NONE,+ RULESET,+ MEDIA,+ DIRECTIVE,+ SUPPORTS,+ ATROOT,+ BUBBLE,+ CONTENT,+ KEYFRAMERULE,+ DECLARATION,+ ASSIGNMENT,+ IMPORT_STUB,+ IMPORT,+ COMMENT,+ WARNING,+ RETURN,+ EXTEND,+ ERROR,+ DEBUGSTMT,+ WHILE,+ EACH,+ FOR,+ IF+ };+ private:+ ADD_PROPERTY(Block*, block)+ ADD_PROPERTY(Statement_Type, statement_type)+ ADD_PROPERTY(size_t, tabs)+ ADD_PROPERTY(bool, group_end)+ public:+ Statement(ParserState pstate, Statement_Type st = NONE, size_t t = 0)+ : AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)+ { }+ virtual ~Statement() = 0;+ // needed for rearranging nested rulesets during CSS emission+ virtual bool is_hoistable() { return false; }+ virtual bool is_invisible() const { return false; }+ virtual bool bubbles() { return false; }+ virtual Block* block() { return 0; }+ virtual bool has_content()+ {+ return statement_type_ == CONTENT;+ }+ };+ inline Statement::~Statement() { }++ ////////////////////////+ // Blocks of statements.+ ////////////////////////+ class Block : public Statement, public Vectorized<Statement*> {+ ADD_PROPERTY(bool, is_root)+ ADD_PROPERTY(bool, is_at_root);+ // needed for properly formatted CSS emission+ ADD_PROPERTY(bool, has_hoistable)+ ADD_PROPERTY(bool, has_non_hoistable)+ protected:+ void adjust_after_pushing(Statement* s)+ {+ if (s->is_hoistable()) has_hoistable_ = true;+ else has_non_hoistable_ = true;+ }+ public:+ Block(ParserState pstate, size_t s = 0, bool r = false)+ : Statement(pstate),+ Vectorized<Statement*>(s),+ is_root_(r),+ is_at_root_(false),+ has_hoistable_(false),+ has_non_hoistable_(false)+ { }+ virtual bool has_content()+ {+ for (size_t i = 0, L = elements().size(); i < L; ++i) {+ if (elements()[i]->has_content()) return true;+ }+ return Statement::has_content();+ }+ Block* block() { return this; }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for statements that contain blocks of statements.+ ////////////////////////////////////////////////////////////////////////+ class Has_Block : public Statement {+ ADD_PROPERTY(Block*, block)+ public:+ Has_Block(ParserState pstate, Block* b)+ : Statement(pstate), block_(b)+ { }+ virtual bool has_content()+ {+ return (block_ && block_->has_content()) || Statement::has_content();+ }+ virtual ~Has_Block() = 0;+ };+ inline Has_Block::~Has_Block() { }++ /////////////////////////////////////////////////////////////////////////////+ // Rulesets (i.e., sets of styles headed by a selector and containing a block+ // of style declarations.+ /////////////////////////////////////////////////////////////////////////////+ class Ruleset : public Has_Block {+ ADD_PROPERTY(Selector*, selector)+ ADD_PROPERTY(bool, at_root);+ ADD_PROPERTY(bool, is_root);+ public:+ Ruleset(ParserState pstate, Selector* s = 0, Block* b = 0)+ : Has_Block(pstate, b), selector_(s), at_root_(false), is_root_(false)+ { statement_type(RULESET); }+ bool is_invisible() const;+ // nested rulesets need to be hoisted out of their enclosing blocks+ bool is_hoistable() { return true; }+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////+ // Nested declaration sets (i.e., namespaced properties).+ /////////////////////////////////////////////////////////+ class Propset : public Has_Block {+ ADD_PROPERTY(String*, property_fragment)+ public:+ Propset(ParserState pstate, String* pf, Block* b = 0)+ : Has_Block(pstate, b), property_fragment_(pf)+ { }+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Bubble.+ /////////////////+ class Bubble : public Statement {+ ADD_PROPERTY(Statement*, node)+ ADD_PROPERTY(bool, group_end)+ public:+ Bubble(ParserState pstate, Statement* n, Statement* g = 0, size_t t = 0)+ : Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == 0)+ { }+ bool bubbles() { return true; }+ ATTACH_OPERATIONS()+ };++ /////////////////+ // Media queries.+ /////////////////+ class Media_Block : public Has_Block {+ ADD_PROPERTY(List*, media_queries)+ public:+ Media_Block(ParserState pstate, List* mqs, Block* b)+ : Has_Block(pstate, b), media_queries_(mqs)+ { statement_type(MEDIA); }+ Media_Block(ParserState pstate, List* mqs, Block* b, Selector* s)+ : Has_Block(pstate, b), media_queries_(mqs)+ { statement_type(MEDIA); }+ bool bubbles() { return true; }+ bool is_hoistable() { return true; }+ bool is_invisible() const;+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // At-rules -- arbitrary directives beginning with "@" that may have an+ // optional statement block.+ ///////////////////////////////////////////////////////////////////////+ class At_Rule : public Has_Block {+ ADD_PROPERTY(std::string, keyword)+ ADD_PROPERTY(Selector*, selector)+ ADD_PROPERTY(Expression*, value)+ public:+ At_Rule(ParserState pstate, std::string kwd, Selector* sel = 0, Block* b = 0, Expression* val = 0)+ : Has_Block(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed+ { statement_type(DIRECTIVE); }+ bool bubbles() { return is_keyframes() || is_media(); }+ bool is_media() {+ return keyword_.compare("@-webkit-media") == 0 ||+ keyword_.compare("@-moz-media") == 0 ||+ keyword_.compare("@-o-media") == 0 ||+ keyword_.compare("@media") == 0;+ }+ bool is_keyframes() {+ return keyword_.compare("@-webkit-keyframes") == 0 ||+ keyword_.compare("@-moz-keyframes") == 0 ||+ keyword_.compare("@-o-keyframes") == 0 ||+ keyword_.compare("@keyframes") == 0;+ }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Keyframe-rules -- the child blocks of "@keyframes" nodes.+ ///////////////////////////////////////////////////////////////////////+ class Keyframe_Rule : public Has_Block {+ ADD_PROPERTY(Selector*, selector)+ public:+ Keyframe_Rule(ParserState pstate, Block* b)+ : Has_Block(pstate, b), selector_(0)+ { statement_type(KEYFRAMERULE); }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Declarations -- style rules consisting of a property name and values.+ ////////////////////////////////////////////////////////////////////////+ class Declaration : public Statement {+ ADD_PROPERTY(String*, property)+ ADD_PROPERTY(Expression*, value)+ ADD_PROPERTY(bool, is_important)+ ADD_PROPERTY(bool, is_indented)+ public:+ Declaration(ParserState pstate,+ String* prop, Expression* val, bool i = false)+ : Statement(pstate), property_(prop), value_(val), is_important_(i), is_indented_(false)+ { statement_type(DECLARATION); }+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////+ // Assignments -- variable and value.+ /////////////////////////////////////+ class Assignment : public Statement {+ ADD_PROPERTY(std::string, variable)+ ADD_PROPERTY(Expression*, value)+ ADD_PROPERTY(bool, is_default)+ ADD_PROPERTY(bool, is_global)+ public:+ Assignment(ParserState pstate,+ std::string var, Expression* val,+ bool is_default = false,+ bool is_global = false)+ : Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)+ { statement_type(ASSIGNMENT); }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Import directives. CSS and Sass import lists can be intermingled, so it's+ // necessary to store a list of each in an Import node.+ ////////////////////////////////////////////////////////////////////////////+ class Import : public Statement {+ std::vector<Expression*> urls_;+ std::vector<Include> incs_;+ ADD_PROPERTY(List*, media_queries);+ public:+ Import(ParserState pstate)+ : Statement(pstate),+ urls_(std::vector<Expression*>()),+ incs_(std::vector<Include>()),+ media_queries_(0)+ { statement_type(IMPORT); }+ std::vector<Expression*>& urls() { return urls_; }+ std::vector<Include>& incs() { return incs_; }+ ATTACH_OPERATIONS()+ };++ // not yet resolved single import+ // so far we only know requested name+ class Import_Stub : public Statement {+ Include resource_;+ public:+ std::string abs_path() { return resource_.abs_path; };+ std::string imp_path() { return resource_.imp_path; };+ Include resource() { return resource_; };++ Import_Stub(ParserState pstate, Include res)+ : Statement(pstate), resource_(res)+ { statement_type(IMPORT_STUB); }+ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // The Sass `@warn` directive.+ //////////////////////////////+ class Warning : public Statement {+ ADD_PROPERTY(Expression*, message)+ public:+ Warning(ParserState pstate, Expression* msg)+ : Statement(pstate), message_(msg)+ { statement_type(WARNING); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////+ // The Sass `@error` directive.+ ///////////////////////////////+ class Error : public Statement {+ ADD_PROPERTY(Expression*, message)+ public:+ Error(ParserState pstate, Expression* msg)+ : Statement(pstate), message_(msg)+ { statement_type(ERROR); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////+ // The Sass `@debug` directive.+ ///////////////////////////////+ class Debug : public Statement {+ ADD_PROPERTY(Expression*, value)+ public:+ Debug(ParserState pstate, Expression* val)+ : Statement(pstate), value_(val)+ { statement_type(DEBUGSTMT); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////+ // CSS comments. These may be interpolated.+ ///////////////////////////////////////////+ class Comment : public Statement {+ ADD_PROPERTY(String*, text)+ ADD_PROPERTY(bool, is_important)+ public:+ Comment(ParserState pstate, String* txt, bool is_important)+ : Statement(pstate), text_(txt), is_important_(is_important)+ { statement_type(COMMENT); }+ virtual bool is_invisible() const+ { return is_important() == false; }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////+ // The Sass `@if` control directive.+ ////////////////////////////////////+ class If : public Has_Block {+ ADD_PROPERTY(Expression*, predicate)+ ADD_PROPERTY(Block*, alternative)+ public:+ If(ParserState pstate, Expression* pred, Block* con, Block* alt = 0)+ : Has_Block(pstate, con), predicate_(pred), alternative_(alt)+ { statement_type(IF); }+ virtual bool has_content()+ {+ return Has_Block::has_content() || (alternative_ && alternative_->has_content());+ }+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////+ // The Sass `@for` control directive.+ /////////////////////////////////////+ class For : public Has_Block {+ ADD_PROPERTY(std::string, variable)+ ADD_PROPERTY(Expression*, lower_bound)+ ADD_PROPERTY(Expression*, upper_bound)+ ADD_PROPERTY(bool, is_inclusive)+ public:+ For(ParserState pstate,+ std::string var, Expression* lo, Expression* hi, Block* b, bool inc)+ : Has_Block(pstate, b),+ variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)+ { statement_type(FOR); }+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////+ // The Sass `@each` control directive.+ //////////////////////////////////////+ class Each : public Has_Block {+ ADD_PROPERTY(std::vector<std::string>, variables)+ ADD_PROPERTY(Expression*, list)+ public:+ Each(ParserState pstate, std::vector<std::string> vars, Expression* lst, Block* b)+ : Has_Block(pstate, b), variables_(vars), list_(lst)+ { statement_type(EACH); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////+ // The Sass `@while` control directive.+ ///////////////////////////////////////+ class While : public Has_Block {+ ADD_PROPERTY(Expression*, predicate)+ public:+ While(ParserState pstate, Expression* pred, Block* b)+ : Has_Block(pstate, b), predicate_(pred)+ { statement_type(WHILE); }+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////+ // The @return directive for use inside SassScript functions.+ /////////////////////////////////////////////////////////////+ class Return : public Statement {+ ADD_PROPERTY(Expression*, value)+ public:+ Return(ParserState pstate, Expression* val)+ : Statement(pstate), value_(val)+ { statement_type(RETURN); }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////+ // The Sass `@extend` directive.+ ////////////////////////////////+ class Extension : public Statement {+ ADD_PROPERTY(Selector*, selector)+ public:+ Extension(ParserState pstate, Selector* s)+ : Statement(pstate), selector_(s)+ { statement_type(EXTEND); }+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////////////+ // Definitions for both mixins and functions. The two cases are distinguished+ // by a type tag.+ /////////////////////////////////////////////////////////////////////////////+ struct Backtrace;+ typedef Environment<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(std::string, name)+ ADD_PROPERTY(Parameters*, parameters)+ ADD_PROPERTY(Env*, environment)+ ADD_PROPERTY(Type, type)+ ADD_PROPERTY(Native_Function, native_function)+ ADD_PROPERTY(Sass_Function_Entry, c_function)+ ADD_PROPERTY(void*, cookie)+ ADD_PROPERTY(bool, is_overload_stub)+ ADD_PROPERTY(Signature, signature)+ public:+ Definition(ParserState pstate,+ std::string n,+ Parameters* params,+ Block* b,+ Type t)+ : Has_Block(pstate, b),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(t),+ native_function_(0),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(false),+ signature_(0)+ { }+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters* params,+ Native_Function func_ptr,+ bool overload_stub = false)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(func_ptr),+ c_function_(0),+ cookie_(0),+ is_overload_stub_(overload_stub),+ signature_(sig)+ { }+ Definition(ParserState pstate,+ Signature sig,+ std::string n,+ Parameters* params,+ Sass_Function_Entry c_func,+ bool whatever,+ bool whatever2)+ : Has_Block(pstate, 0),+ name_(n),+ parameters_(params),+ environment_(0),+ type_(FUNCTION),+ native_function_(0),+ c_function_(c_func),+ cookie_(sass_function_get_cookie(c_func)),+ is_overload_stub_(false),+ signature_(sig)+ { }+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////+ // Mixin calls (i.e., `@include ...`).+ //////////////////////////////////////+ class Mixin_Call : public Has_Block {+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(Arguments*, arguments)+ public:+ Mixin_Call(ParserState pstate, std::string n, Arguments* args, Block* b = 0)+ : Has_Block(pstate, b), name_(n), arguments_(args)+ { }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////+ // The @content directive for mixin content blocks.+ ///////////////////////////////////////////////////+ class Content : public Statement {+ public:+ Content(ParserState pstate) : Statement(pstate)+ { statement_type(CONTENT); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Lists of values, both comma- and space-separated (distinguished by a+ // type-tag.) Also used to represent variable-length argument lists.+ ///////////////////////////////////////////////////////////////////////+ class List : public Value, public Vectorized<Expression*> {+ void adjust_after_pushing(Expression* e) { is_expanded(false); }+ private:+ ADD_PROPERTY(enum Sass_Separator, separator)+ ADD_PROPERTY(bool, is_arglist)+ public:+ List(ParserState pstate,+ size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false)+ : Value(pstate),+ Vectorized<Expression*>(size),+ separator_(sep), is_arglist_(argl)+ { concrete_type(LIST); }+ std::string type() { return is_arglist_ ? "arglist" : "list"; }+ static std::string type_name() { return "list"; }+ const char* sep_string(bool compressed = false) const {+ return separator() == SASS_COMMA ?+ (compressed ? "," : ", ") : " ";+ }+ bool is_invisible() const { return empty(); }+ Expression* value_at_index(size_t i);++ virtual size_t size() const;++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(sep_string());+ for (size_t i = 0, L = length(); i < L; ++i)+ hash_combine(hash_, (elements()[i])->hash());+ }+ return hash_;+ }++ virtual void set_delayed(bool delayed)+ {+ for (size_t i = 0, L = length(); i < L; ++i)+ (elements()[i])->set_delayed(delayed);+ is_delayed(delayed);+ }++ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////////////////////////////+ // Key value paris.+ ///////////////////////////////////////////////////////////////////////+ class Map : public Value, public Hashed {+ void adjust_after_pushing(std::pair<Expression*, Expression*> p) { is_expanded(false); }+ public:+ Map(ParserState pstate,+ size_t size = 0)+ : Value(pstate),+ Hashed(size)+ { concrete_type(MAP); }+ std::string type() { return "map"; }+ static std::string type_name() { return "map"; }+ bool is_invisible() const { return empty(); }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ for (auto key : keys()) {+ hash_combine(hash_, key->hash());+ hash_combine(hash_, at(key)->hash());+ }+ }++ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////////////////////////////////////+ // Binary expressions. Represents logical, relational, and arithmetic+ // operations. Templatized to avoid large switch statements and repetitive+ // subclassing.+ //////////////////////////////////////////////////////////////////////////+ class Binary_Expression : public Expression {+ private:+ ADD_HASHED(enum Sass_OP, type)+ ADD_HASHED(Expression*, left)+ ADD_HASHED(Expression*, right)+ size_t hash_;+ public:+ Binary_Expression(ParserState pstate,+ enum Sass_OP t, Expression* lhs, Expression* rhs)+ : Expression(pstate), type_(t), left_(lhs), right_(rhs), hash_(0)+ { }+ const std::string type_name() {+ switch (type_) {+ case AND: return "and"; break;+ case OR: return "or"; break;+ case EQ: return "eq"; break;+ case NEQ: return "neq"; break;+ case GT: return "gt"; break;+ case GTE: return "gte"; break;+ case LT: return "lt"; break;+ case LTE: return "lte"; break;+ case ADD: return "add"; break;+ case SUB: return "sub"; break;+ case MUL: return "mul"; break;+ case DIV: return "div"; break;+ case MOD: return "mod"; break;+ case NUM_OPS: return "num_ops"; break;+ default: return "invalid"; break;+ }+ }+ virtual void set_delayed(bool delayed)+ {+ right()->set_delayed(delayed);+ left()->set_delayed(delayed);+ is_delayed(delayed);+ }+ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Binary_Expression* m = dynamic_cast<const Binary_Expression*>(&rhs);+ if (m == 0) return false;+ return type() == m->type() &&+ left() == m->left() &&+ right() == m->right();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(type_);+ hash_combine(hash_, left()->hash());+ hash_combine(hash_, right()->hash());+ }+ return hash_;+ }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////////+ // Arithmetic negation (logical negation is just an ordinary function call).+ ////////////////////////////////////////////////////////////////////////////+ class Unary_Expression : public Expression {+ public:+ enum Type { PLUS, MINUS, NOT };+ private:+ ADD_HASHED(Type, type)+ ADD_HASHED(Expression*, operand)+ size_t hash_;+ public:+ Unary_Expression(ParserState pstate, Type t, Expression* o)+ : Expression(pstate), type_(t), operand_(o), hash_(0)+ { }+ const std::string type_name() {+ switch (type_) {+ case PLUS: return "plus"; break;+ case MINUS: return "minus"; break;+ case NOT: return "not"; break;+ default: return "invalid"; break;+ }+ }+ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Unary_Expression* m = dynamic_cast<const Unary_Expression*>(&rhs);+ if (m == 0) return false;+ return type() == m->type() &&+ operand() == m->operand();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }+ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<size_t>()(type_);+ hash_combine(hash_, operand()->hash());+ };+ return hash_;+ }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////+ // Individual argument objects for mixin and function calls.+ ////////////////////////////////////////////////////////////+ class Argument : public Expression {+ ADD_HASHED(Expression*, value)+ ADD_HASHED(std::string, name)+ ADD_PROPERTY(bool, is_rest_argument)+ ADD_PROPERTY(bool, is_keyword_argument)+ size_t hash_;+ public:+ Argument(ParserState pstate, Expression* val, std::string n = "", bool rest = false, bool keyword = false)+ : Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)+ {+ if (!name_.empty() && is_rest_argument_) {+ error("variable-length argument may not be passed by name", pstate);+ }+ }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Argument* m = dynamic_cast<const Argument*>(&rhs);+ if (!(m && name() == m->name())) return false;+ return *value() == *m->value();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(name());+ hash_combine(hash_, value()->hash());+ }+ return hash_;+ }++ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Argument lists -- in their own class to facilitate context-sensitive+ // error checking (e.g., ensuring that all ordinal arguments precede all+ // named arguments).+ ////////////////////////////////////////////////////////////////////////+ class Arguments : public Expression, public Vectorized<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);+ 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_HASHED(std::string, name)+ ADD_HASHED(Arguments*, arguments)+ ADD_PROPERTY(void*, cookie)+ size_t hash_;+ public:+ Function_Call(ParserState pstate, std::string n, Arguments* args, void* cookie)+ : Expression(pstate), name_(n), arguments_(args), cookie_(cookie), hash_(0)+ { concrete_type(STRING); }+ Function_Call(ParserState pstate, std::string n, Arguments* args)+ : Expression(pstate), name_(n), arguments_(args), cookie_(0), hash_(0)+ { concrete_type(STRING); }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Function_Call* m = dynamic_cast<const 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) {+ hash_ = std::hash<std::string>()(name());+ for (auto argument : arguments()->elements())+ hash_combine(hash_, argument->hash());+ }+ return hash_;+ }++ ATTACH_OPERATIONS()+ };++ /////////////////////////+ // Function call schemas.+ /////////////////////////+ class Function_Call_Schema : public Expression {+ ADD_PROPERTY(String*, name)+ ADD_PROPERTY(Arguments*, arguments)+ public:+ Function_Call_Schema(ParserState pstate, String* n, Arguments* args)+ : Expression(pstate), name_(n), arguments_(args)+ { concrete_type(STRING); }+ ATTACH_OPERATIONS()+ };++ ///////////////////////+ // Variable references.+ ///////////////////////+ class Variable : public Expression {+ ADD_PROPERTY(std::string, name)+ public:+ Variable(ParserState pstate, std::string n)+ : Expression(pstate), name_(n)+ { }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Variable* e = dynamic_cast<const Variable*>(&rhs);+ return e && name() == e->name();+ }+ catch (std::bad_cast&)+ {+ return false;+ }+ catch (...) { throw; }+ }++ virtual size_t hash()+ {+ return std::hash<std::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_HASHED(Type, type)+ ADD_HASHED(std::string, value)+ size_t hash_;+ public:+ Textual(ParserState pstate, Type t, std::string val)+ : Expression(pstate, true), type_(t), value_(val),+ hash_(0)+ { }++ virtual bool operator==(const Expression& rhs) const+ {+ try+ {+ const Textual* e = dynamic_cast<const 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<std::string>()(value_);+ hash_combine(hash_, std::hash<int>()(type_));+ }+ return hash_;+ }++ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////+ // Numbers, percentages, dimensions, and colors.+ ////////////////////////////////////////////////+ class Number : public Value {+ ADD_HASHED(double, value)+ ADD_PROPERTY(bool, zero)+ std::vector<std::string> numerator_units_;+ std::vector<std::string> denominator_units_;+ size_t hash_;+ public:+ Number(ParserState pstate, double val, std::string u = "", bool zero = true);+ bool zero() { return zero_; }+ std::vector<std::string>& numerator_units() { return numerator_units_; }+ std::vector<std::string>& denominator_units() { return denominator_units_; }+ const std::vector<std::string>& numerator_units() const { return numerator_units_; }+ const std::vector<std::string>& denominator_units() const { return denominator_units_; }+ std::string type() { return "number"; }+ static std::string type_name() { return "number"; }+ std::string unit() const;++ bool is_unitless();+ void convert(const std::string& unit = "", bool strict = false);+ void normalize(const std::string& unit = "", bool strict = false);+ // useful for making one number compatible with another+ std::string find_convertible_unit() const;++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<double>()(value_);+ for (const auto numerator : numerator_units())+ hash_combine(hash_, std::hash<std::string>()(numerator));+ for (const auto denominator : denominator_units())+ hash_combine(hash_, std::hash<std::string>()(denominator));+ }+ return hash_;+ }++ virtual bool operator< (const Number& rhs) const;+ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ //////////+ // Colors.+ //////////+ class Color : public Value {+ ADD_HASHED(double, r)+ ADD_HASHED(double, g)+ ADD_HASHED(double, b)+ ADD_HASHED(double, a)+ ADD_PROPERTY(bool, sixtuplet)+ ADD_PROPERTY(std::string, disp)+ size_t hash_;+ public:+ Color(ParserState pstate, double r, double g, double b, double a = 1, bool sixtuplet = true, const std::string disp = "")+ : Value(pstate), r_(r), g_(g), b_(b), a_(a), sixtuplet_(sixtuplet), disp_(disp),+ hash_(0)+ { concrete_type(COLOR); }+ std::string type() { return "color"; }+ static std::string type_name() { return "color"; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<double>()(a_);+ hash_combine(hash_, std::hash<double>()(r_));+ hash_combine(hash_, std::hash<double>()(g_));+ hash_combine(hash_, std::hash<double>()(b_));+ }+ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // Errors from Sass_Values.+ //////////////////////////////+ class Custom_Error : public Value {+ ADD_PROPERTY(std::string, message)+ public:+ Custom_Error(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_ERROR); }+ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ //////////////////////////////+ // Warnings from Sass_Values.+ //////////////////////////////+ class Custom_Warning : public Value {+ ADD_PROPERTY(std::string, message)+ public:+ Custom_Warning(ParserState pstate, std::string msg)+ : Value(pstate), message_(msg)+ { concrete_type(C_WARNING); }+ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ ////////////+ // Booleans.+ ////////////+ class Boolean : public Value {+ ADD_HASHED(bool, value)+ size_t hash_;+ public:+ Boolean(ParserState pstate, bool val)+ : Value(pstate), value_(val),+ hash_(0)+ { concrete_type(BOOLEAN); }+ virtual operator bool() { return value_; }+ std::string type() { return "bool"; }+ static std::string type_name() { return "bool"; }+ virtual bool is_false() { return !value_; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<bool>()(value_);+ }+ return hash_;+ }++ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////////////////+ // Abstract base class for Sass string values. Includes interpolated and+ // "flat" strings.+ ////////////////////////////////////////////////////////////////////////+ class String : public Value {+ ADD_PROPERTY(bool, sass_fix_1291)+ public:+ String(ParserState pstate, bool delayed = false, bool sass_fix_1291 = false)+ : Value(pstate, delayed), sass_fix_1291_(sass_fix_1291)+ { concrete_type(STRING); }+ static std::string type_name() { return "string"; }+ virtual ~String() = 0;+ virtual bool operator==(const Expression& rhs) const = 0;+ virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;+ ATTACH_OPERATIONS()+ };+ inline String::~String() { };++ ///////////////////////////////////////////////////////////////////////+ // 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)+ { concrete_type(STRING); }+ std::string type() { return "string"; }+ static std::string type_name() { return "string"; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ for (auto string : elements())+ hash_combine(hash_, string->hash());+ }+ return hash_;+ }++ virtual bool operator==(const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////+ // Flat strings -- the lowest level of raw textual data.+ ////////////////////////////////////////////////////////+ class String_Constant : public String {+ ADD_PROPERTY(char, quote_mark)+ ADD_PROPERTY(bool, can_compress_whitespace)+ ADD_HASHED(std::string, value)+ protected:+ size_t hash_;+ public:+ String_Constant(ParserState pstate, std::string val)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const char* beg, const char* end)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)+ { }+ String_Constant(ParserState pstate, const Token& tok)+ : String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)+ { }+ std::string type() { return "string"; }+ static std::string type_name() { return "string"; }++ virtual size_t hash()+ {+ if (hash_ == 0) {+ hash_ = std::hash<std::string>()(value_);+ }+ return hash_;+ }++ virtual bool operator==(const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ // 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, std::string val, char q = 0, bool keep_utf8_escapes = false)+ : String_Constant(pstate, val)+ {+ value_ = unquote(value_, "e_mark_, keep_utf8_escapes);+ if (q && quote_mark_) quote_mark_ = q;+ }+ virtual bool operator==(const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ 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()+ };++ ////////////////////+ // `@supports` rule.+ ////////////////////+ class Supports_Block : public Has_Block {+ ADD_PROPERTY(Supports_Condition*, condition)+ public:+ Supports_Block(ParserState pstate, Supports_Condition* condition, Block* block = 0)+ : Has_Block(pstate, block), condition_(condition)+ { statement_type(SUPPORTS); }+ bool is_hoistable() { return true; }+ bool bubbles() { return true; }+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////////////////+ // The abstract superclass of all Supports conditions.+ //////////////////////////////////////////////////////+ class Supports_Condition : public Expression {+ public:+ Supports_Condition(ParserState pstate)+ : Expression(pstate)+ { }+ virtual bool needs_parens(Supports_Condition* cond) const { return false; }+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////////////////+ // An operator condition (e.g. `CONDITION1 and CONDITION2`).+ ////////////////////////////////////////////////////////////+ class Supports_Operator : public Supports_Condition {+ public:+ enum Operand { AND, OR };+ private:+ ADD_PROPERTY(Supports_Condition*, left);+ ADD_PROPERTY(Supports_Condition*, right);+ ADD_PROPERTY(Operand, operand);+ public:+ Supports_Operator(ParserState pstate, Supports_Condition* l, Supports_Condition* r, Operand o)+ : Supports_Condition(pstate), left_(l), right_(r), operand_(o)+ { }+ virtual bool needs_parens(Supports_Condition* cond) const;+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////+ // A negation condition (`not CONDITION`).+ //////////////////////////////////////////+ class Supports_Negation : public Supports_Condition {+ private:+ ADD_PROPERTY(Supports_Condition*, condition);+ public:+ Supports_Negation(ParserState pstate, Supports_Condition* c)+ : Supports_Condition(pstate), condition_(c)+ { }+ virtual bool needs_parens(Supports_Condition* cond) const;+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////+ // A declaration condition (e.g. `(feature: value)`).+ /////////////////////////////////////////////////////+ class Supports_Declaration : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression*, feature);+ ADD_PROPERTY(Expression*, value);+ public:+ Supports_Declaration(ParserState pstate, Expression* f, Expression* v)+ : Supports_Condition(pstate), feature_(f), value_(v)+ { }+ virtual bool needs_parens(Supports_Condition* cond) const { return false; }+ ATTACH_OPERATIONS()+ };++ ///////////////////////////////////////////////+ // An interpolation condition (e.g. `#{$var}`).+ ///////////////////////////////////////////////+ class Supports_Interpolation : public Supports_Condition {+ private:+ ADD_PROPERTY(Expression*, value);+ public:+ Supports_Interpolation(ParserState pstate, Expression* v)+ : Supports_Condition(pstate), value_(v)+ { }+ virtual bool needs_parens(Supports_Condition* cond) const { return false; }+ 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(std::string str)+ {+ To_String to_string;+ bool with = feature() && unquote(feature()->perform(&to_string)).compare("with") == 0;+ List* l = static_cast<List*>(value());+ std::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::SUPPORTS)+ {+ 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 Value {+ public:+ Null(ParserState pstate) : Value(pstate) { concrete_type(NULL_VAL); }+ std::string type() { return "null"; }+ static std::string type_name() { return "null"; }+ bool is_invisible() const { return true; }+ operator bool() { return false; }+ bool is_false() { return true; }++ virtual size_t hash()+ {+ return -1;+ }++ virtual bool operator== (const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;++ 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(std::string, name)+ ADD_PROPERTY(Expression*, default_value)+ ADD_PROPERTY(bool, is_rest_parameter)+ public:+ Parameter(ParserState pstate,+ std::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()+ };++ /////////////////////////////////////////+ // Abstract base class for CSS selectors.+ /////////////////////////////////////////+ class Selector : public Expression {+ 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(Media_Block*, media_block)+ public:+ Selector(ParserState pstate, bool r = false, bool h = false)+ : Expression(pstate),+ has_reference_(r),+ has_placeholder_(h),+ has_line_feed_(false),+ has_line_break_(false),+ is_optional_(false),+ media_block_(0)+ { concrete_type(SELECTOR); }+ virtual ~Selector() = 0;+ virtual unsigned long specificity() {+ return Constants::Specificity_Universal;+ }+ virtual std::string to_string(bool compressed = false, int precision = 5) const = 0;+ };+ 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)+ ADD_PROPERTY(bool, at_root);+ public:+ Selector_Schema(ParserState pstate, String* c)+ : Selector(pstate), contents_(c), at_root_(false)+ { }+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////+ // Abstract base class for simple selectors.+ ////////////////////////////////////////////+ class Simple_Selector : public Selector {+ ADD_PROPERTY(std::string, ns);+ ADD_PROPERTY(std::string, name)+ ADD_PROPERTY(bool, has_ns)+ public:+ Simple_Selector(ParserState pstate, std::string n = "")+ : Selector(pstate), ns_(""), name_(n), has_ns_(false)+ {+ size_t pos = n.find('|');+ // found some namespace+ if (pos != std::string::npos) {+ has_ns_ = true;+ ns_ = n.substr(0, pos);+ name_ = n.substr(pos + 1);+ }+ }+ virtual std::string ns_name() const+ {+ std::string name("");+ if (has_ns_)+ name += ns_ + "|";+ return name + name_;+ }+ // namespace query functions+ bool is_universal_ns() const+ {+ return has_ns_ && ns_ == "*";+ }+ bool has_universal_ns() const+ {+ return !has_ns_ || ns_ == "*";+ }+ bool is_empty_ns() const+ {+ return !has_ns_ || ns_ == "";+ }+ bool has_empty_ns() const+ {+ return has_ns_ && ns_ == "";+ }+ bool has_qualified_ns() const+ {+ return has_ns_ && ns_ != "" && ns_ != "*";+ }+ // name query functions+ bool is_universal() const+ {+ return name_ == "*";+ }++ virtual ~Simple_Selector() = 0;+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ virtual bool has_parent_ref() { return false; };+ virtual bool is_pseudo_element() { return false; }+ virtual bool is_pseudo_class() { return false; }++ virtual bool is_superselector_of(Compound_Selector* sub) { 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;+ // default implementation should work for most of the simple selectors (otherwise overload)+ virtual std::string to_string(bool compressed = false, int precision = 5) const { return this->ns_name(); };+ ATTACH_OPERATIONS();+ };+ inline Simple_Selector::~Simple_Selector() { }+++ //////////////////////////////////+ // The Parent Selector Expression.+ //////////////////////////////////+ // parent selectors can occur in selectors but also+ // inside strings in declarations (Compound_Selector).+ // only one simple parent selector means the first case.+ class Parent_Selector : public Simple_Selector {+ public:+ Parent_Selector(ParserState pstate)+ : Simple_Selector(pstate, "&")+ { has_reference(true); }+ virtual bool has_parent_ref() { return true; };+ virtual unsigned long specificity()+ {+ return 0;+ }+ std::string type() { return "selector"; }+ static std::string type_name() { return "selector"; }+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////////+ // Placeholder selectors (e.g., "%foo") for use in extend-only selectors.+ /////////////////////////////////////////////////////////////////////////+ class Selector_Placeholder : public Simple_Selector {+ public:+ Selector_Placeholder(ParserState pstate, std::string n)+ : Simple_Selector(pstate, n)+ { has_placeholder(true); }+ // virtual Selector_Placeholder* find_placeholder();+ virtual ~Selector_Placeholder() {};+ ATTACH_OPERATIONS()+ };++ /////////////////////////////////////////////////////////////////////+ // Type selectors (and the universal selector) -- e.g., div, span, *.+ /////////////////////////////////////////////////////////////////////+ class Type_Selector : public Simple_Selector {+ public:+ Type_Selector(ParserState pstate, std::string n)+ : Simple_Selector(pstate, 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 Simple_Selector* unify_with(Simple_Selector*, Context&);+ virtual Compound_Selector* unify_with(Compound_Selector*, Context&);+ ATTACH_OPERATIONS()+ };++ ////////////////////////////////////////////////+ // Selector qualifiers -- i.e., classes and ids.+ ////////////////////////////////////////////////+ class Selector_Qualifier : public Simple_Selector {+ public:+ Selector_Qualifier(ParserState pstate, std::string n)+ : Simple_Selector(pstate, 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(std::string, matcher)+ ADD_PROPERTY(String*, value) // might be interpolated+ public:+ Attribute_Selector(ParserState pstate, std::string n, std::string m, String* v)+ : Simple_Selector(pstate, n), matcher_(m), value_(v)+ { }+ virtual unsigned long specificity()+ {+ return Constants::Specificity_Attr;+ }+ bool operator==(const Simple_Selector& rhs) const;+ bool operator==(const Attribute_Selector& rhs) const;+ bool operator<(const Simple_Selector& rhs) const;+ bool operator<(const Attribute_Selector& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ //////////////////////////////////////////////////////////////////+ // 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 std::string& name)+ {+ return name == ":before" ||+ name == ":after" ||+ name == ":first-line" ||+ name == ":first-letter";+ }++ class Pseudo_Selector : public Simple_Selector {+ ADD_PROPERTY(String*, expression)+ public:+ Pseudo_Selector(ParserState pstate, std::string n, String* expr = 0)+ : Simple_Selector(pstate, 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(Selector*, selector)+ public:+ Wrapped_Selector(ParserState pstate, std::string n, Selector* sel)+ : Simple_Selector(pstate, n), selector_(sel)+ { }+ virtual bool is_superselector_of(Wrapped_Selector* sub);+ // Selectors inside the negation pseudo-class are counted like any+ // other, but the negation itself does not count as a pseudo-class.+ virtual unsigned long specificity()+ {+ return selector_ ? selector_->specificity() : 0;+ }+ bool operator==(const Simple_Selector& rhs) const;+ bool operator==(const Wrapped_Selector& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ 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 std::set<Complex_Selector*, Complex_Selector_Pointer_Compare> SourcesSet;+ class Compound_Selector : public Selector, public Vectorized<Simple_Selector*> {+ private:+ SourcesSet sources_;+ ADD_PROPERTY(bool, has_parent_reference);+ 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),+ has_parent_reference_(false)+ { }+ bool contains_placeholder() {+ for (size_t i = 0, L = length(); i < L; ++i) {+ if ((*this)[i]->has_placeholder()) return true;+ }+ return false;+ };++ bool is_universal() const+ {+ return length() == 1 && (*this)[0]->is_universal();+ }++ Complex_Selector* to_complex(Memory_Manager& mem);+ Compound_Selector* unify_with(Compound_Selector* rhs, Context& ctx);+ // virtual Selector_Placeholder* find_placeholder();+ virtual bool has_parent_ref();+ 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) return 0;+ if (typeid(*(*this)[0]) == typeid(Type_Selector))+ return (*this)[0];+// else cerr << "SERIOUSELY " << "\n";+ return 0;+ }+ virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapped = "");+ virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapped = "");+ virtual bool is_superselector_of(Selector_List* sub, std::string wrapped = "");+ virtual 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(Parent_Selector);+ }+ std::vector<std::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);+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ 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, REFERENCE };+ private:+ ADD_PROPERTY(Combinator, combinator)+ ADD_PROPERTY(Compound_Selector*, head)+ ADD_PROPERTY(Complex_Selector*, tail)+ ADD_PROPERTY(String*, reference);+ public:+ bool contains_placeholder() {+ if (head() && head()->contains_placeholder()) return true;+ if (tail() && tail()->contains_placeholder()) return true;+ return false;+ };+ Complex_Selector(ParserState pstate,+ Combinator c = ANCESTOR_OF,+ Compound_Selector* h = 0,+ Complex_Selector* t = 0,+ String* r = 0)+ : Selector(pstate),+ combinator_(c),+ head_(h), tail_(t),+ reference_(r)+ {+ if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true);+ if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true);+ }+ virtual bool has_parent_ref();++ Complex_Selector* skip_empty_reference()+ {+ if ((!head_ || !head_->length() || head_->is_empty_reference()) &&+ combinator() == Combinator::ANCESTOR_OF)+ {+ if (!tail_) return 0;+ tail_->has_line_feed_ = this->has_line_feed_;+ // tail_->has_line_break_ = this->has_line_break_;+ return tail_->skip_empty_reference();+ }+ return this;+ }++ // can still have a tail+ bool is_empty_ancestor() const+ {+ return (!head() || head()->length() == 0) &&+ combinator() == Combinator::ANCESTOR_OF;+ }++ Complex_Selector* context(Context&);+++ // front returns the first real tail+ // skips over parent and empty ones+ const Complex_Selector* first() const;++ // last returns the last real tail+ const Complex_Selector* last() const;++ Selector_List* tails(Context& ctx, Selector_List* tails);++ // unconstant accessors+ Complex_Selector* first();+ Complex_Selector* last();++ // some shortcuts that should be removed+ const Complex_Selector* innermost() const { return last(); };+ Complex_Selector* innermost() { return last(); };++ size_t length() const;+ Selector_List* parentize(Selector_List* parents, Context& ctx);+ virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");+ virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");+ virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");+ // virtual Selector_Placeholder* find_placeholder();+ Selector_List* unify_with(Complex_Selector* rhs, Context& ctx);+ Combinator clear_innermost();+ void append(Context&, Complex_Selector*);+ 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+ // std::vector<Compound_Selector*> to_vector();+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ typedef std::deque<Complex_Selector*> ComplexSelectorDeque;+ typedef Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > ExtensionSubsetMap;++ ///////////////////////////////////+ // Comma-separated selector groups.+ ///////////////////////////////////+ class Selector_List : public Selector, public Vectorized<Complex_Selector*> {+ ADD_PROPERTY(std::vector<std::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)+ { }+ std::string type() { return "list"; }+ // remove parent selector references+ // basically unwraps parsed selectors+ void remove_parent_selectors();+ // virtual Selector_Placeholder* find_placeholder();+ Selector_List* parentize(Selector_List* parents, Context& ctx);+ virtual bool is_superselector_of(Compound_Selector* sub, std::string wrapping = "");+ virtual bool is_superselector_of(Complex_Selector* sub, std::string wrapping = "");+ virtual bool is_superselector_of(Selector_List* sub, std::string wrapping = "");+ Selector_List* unify_with(Selector_List*, Context&);+ void populate_extends(Selector_List*, Context&, ExtensionSubsetMap&);+ virtual unsigned long specificity()+ {+ unsigned long sum = 0;+ unsigned long specificity = 0;+ for (size_t i = 0, L = length(); i < L; ++i)+ {+ specificity = (*this)[i]->specificity();+ if (sum < specificity) sum = specificity;+ }+ return sum;+ }+ Selector_List* clone(Context&) const; // does not clone Compound_Selector*s+ Selector_List* cloneFully(Context&) const; // clones Compound_Selector*s+ virtual bool operator==(const Selector& rhs) const;+ virtual bool operator==(const Selector_List& rhs) const;+ // Selector Lists can be compared to comma lists+ virtual bool operator==(const Expression& rhs) const;+ virtual std::string to_string(bool compressed = false, int precision = 5) const;+ ATTACH_OPERATIONS()+ };++ 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;+ }+ }++ // compare function for sorting and probably other other uses+ struct cmp_complex_selector { inline bool operator() (const Complex_Selector* l, const Complex_Selector* r) { return (*l < *r); } };+ struct cmp_compound_selector { inline bool operator() (const Compound_Selector* l, const Compound_Selector* r) { return (*l < *r); } };+ struct cmp_simple_selector { inline bool operator() (const Simple_Selector* l, const Simple_Selector* r) { return (*l < *r); } };++}++#ifdef __clang__++#pragma clang diagnostic pop++#endif++#endif
+ libsass/src/ast_def_macros.hpp view
@@ -0,0 +1,55 @@+#ifndef SASS_AST_DEF_MACROS_H+#define SASS_AST_DEF_MACROS_H++// Helper class to switch a flag and revert once we go out of scope+template <class T>+class LocalOption {+ private:+ T* var; // pointer to original variable+ T orig; // copy of the original option+ public:+ LocalOption(T& var)+ {+ this->var = &var;+ this->orig = var;+ }+ LocalOption(T& var, T orig)+ {+ this->var = &var;+ this->orig = var;+ *(this->var) = orig;+ }+ ~LocalOption() {+ *(this->var) = this->orig;+ }+};++#define LOCAL_FLAG(name,opt) LocalOption<bool> flag_##name(name, opt)++#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 std::string perform(Operation<std::string>* op) { return (*op)(this); }\+virtual union Sass_Value* perform(Operation<union Sass_Value*>* op) { return (*op)(this); }\+virtual Value* perform(Operation<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:++#define ADD_HASHED(type, name)\+protected:\+ type name##_;\+public:\+ type name() const { return name##_; }\+ type name(type name##__) { hash_ = 0; return name##_ = name##__; }\+private:++#endif
+ libsass/src/ast_factory.hpp view
@@ -0,0 +1,93 @@+#ifndef SASS_AST_FACTORY_H+#define SASS_AST_FACTORY_H++#include <vector>++#include "ast.hpp"++namespace Sass {++ class AST_Factory {+ std::vector<AST_Node*> nodes;+ public:+ // statements+ Block* new_Block(std::string p, size_t l, size_t s = 0, bool r = false);+ Ruleset* new_Ruleset(std::string p, size_t l, Selector* s, Block* b);+ Propset* new_Propset(std::string p, size_t l, String* pf, Block* b);+ Supports_Query* new_Supports_Query(std::string p, size_t l, Supports_Query* f, Block* b);+ Media_Query* new_Media_Query(std::string p, size_t l, List* q, Block* b);+ At_Root_Block* new_At_Root_Block(std::string p, size_t l, Selector* sel, Block* b);+ At_Rule* new_At_Rule(std::string p, size_t l, std::string kwd, Selector* sel, Block* b);+ Keyframe_Rule* new_Keyframe_Rule(std::string p, size_t l, Block* b);+ Declaration* new_Declaration(std::string p, size_t l, String* prop, List* vals);+ Assignment* new_Assignment(std::string p, size_t l, std::string var, Expression* val, bool guarded = false);+ Import<Function_Call*>* new_CSS_Import(std::string p, size_t l, Function_Call* loc);+ Import<String*>* new_SASS_Import(std::string p, size_t l, String* loc);+ Custom_Warning* new_Custom_Warning(std::string msg, size_t l, std::string msg);+ Custom_Error* new_Custom_Error(std::string p, size_t l, std::string msg);+ Warning* new_Warning(std::string p, size_t l, Expression* msg);+ Error* new_Error(std::string p, size_t l, Expression* msg);+ Debug* new_Debug(std::string p, size_t l, Expression* val);+ Comment* new_Comment(std::string p, size_t l, String* txt);+ If* new_If(std::string p, size_t l, Expression* pred, Block* con, Block* alt = 0);+ For* new_For(std::string p, size_t l, std::string var, Expression* lo, Expression* hi, Block* b, bool inc);+ Each* new_Each(std::string p, size_t l, std::vector<std::string> vars, Expression* lst, Block* b);+ While* new_While(std::string p, size_t l, Expression* pred, Block* b);+ Extension* new_Extension(std::string p, size_t l, Selector* s);+ Definition<MIXIN>* new_Mixin_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);+ Definition<FUNCTION>* new_Function_Definition(std::string p, size_t l, std::string n, Parameters* params, Block* b);+ Mixin_Call* new_Mixin_Call(std::string p, size_t l, std::string n, Arguments* args, Block* b = 0);+ // expressions+ List* new_List(std::string p, size_t l, size_t size = 0, enum Sass_Separator sep = List::space, bool argl = false);+ Map* new_Map(std::string p, size_t l, size_t size = 0);+ Binary_Expression<AND>* new_And(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<OR>* new_Or(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<EQ>* new_Eq(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<NEQ>* new_Neq(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<GT>* new_Gt(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<GTE>* new_Gte(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<LT>* new_Lt(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<LTE>* new_Lte(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<ADD>* new_Add(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<SUB>* new_Sub(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<MUL>* new_Mul(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Binary_Expression<DIV>* new_Div(std::string p, size_t l, Expression* lhs, Expression* rhs);+ Negation* new_Negation(std::string p, size_t l, Expression* o);+ Function_Call* new_Function_Call(std::string p, size_t l, String* n, Arguments* args);+ Variable* new_Variable(std::string p, size_t l, std::string n);+ Textual<NUMBER>* new_Textual_Number(std::string p, size_t l, std::string val);+ Textual<PERCENTAGE>* new_Textual_Percentage(std::string p, size_t l, std::string val);+ Textual<DIMENSION>* new_Textual_Dimension(std::string p, size_t l, std::string val);+ Textual<HEX>* new_Textual_Hex(std::string p, size_t l, std::string val);+ Number* new_Number(std::string p, size_t l, double val);+ Percentage* new_Percentage(std::string p, size_t l, double val);+ Dimension* new_Dimension(std::string p, size_t l, double val, std::string unit);+ Color* new_Color(std::string p, size_t l, double r, double g, double b, double a = 1, std::string disp = "");+ Boolean* new_Boolean(std::string p, size_t l, bool val);+ String_Schema* new_String_Schema(std::string p, size_t l, size_t size = 0);+ String_Constant* new_String_Constant(std::string p, size_t l, std::string val);+ String_Constant* new_String_Constant(std::string p, size_t l, const char* beg);+ String_Constant* new_String_Constant(std::string p, size_t l, const char* beg, const char* end);+ Supports_Condition* new_Supports_Condition(std::string p, size_t l, String* f, Expression* v);+ Media_Expression* new_Media_Expression(std::string p, size_t l, String* f, Expression* v);+ Parent_Selector* new_Parent_Selector(std::string p, size_t l, Selector* s);+ // parameters and arguments+ Parameter* new_Parameter(std::string p, size_t l, std::string n, Expression* def = 0, bool rest = false);+ Parameters* new_Parameters(std::string p, size_t l);+ Argument* new_Argument(std::string p, size_t l, Expression* val, std::string n = "", bool rest = false);+ Arguments* new_Arguments(std::string p, size_t l);+ // selectors+ Selector_Schema* new_Selector_Schema(std::string p, size_t l, String* c);+ Attribute_Selector* new_Attribute_Selector(std::string p, size_t l, std::string n, std::string m, String* v);+ Simple_Selector* new_Simple_Selector(std::string p, size_t l, std::string c);+ Reference_Selector* new_Reference_Selector(std::string p, size_t l);+ Placeholder_Selector* new_Placeholder_Selector(std::string p, size_t l, std::string n);+ Pseudo_Selector* new_Pseudo_Selector(std::string p, size_t l, std::string n, Expression* expr = 0);+ Wrapped_Selector* new_Wrapped_Selector(std::string p, size_t l, std::string n, Simple_Base* sel);+ Compound_Selector* new_Compound_Selector(std::string p, size_t l, size_t s = 0);+ Complex_Selector* new_Complex_Selector(std::string p, size_t l, Complex_Selector::Combinator c, Complex_Selector* ctx, Compound_Selector* sel);+ Selector_List* new_Selector_List(std::string p, size_t l, size_t s = 0);+ };+}++#endif
+ libsass/src/ast_fwd_decl.hpp view
@@ -0,0 +1,91 @@+#ifndef SASS_AST_FWD_DECL_H+#define SASS_AST_FWD_DECL_H++/////////////////////////////////////////////+// Forward declarations for the AST visitors.+/////////////////////////////////////////////+namespace Sass {++ class AST_Node;+ // statements+ class Statement;+ class Block;+ class Ruleset;+ class Propset;+ class Bubble;+ class Media_Block;+ class Supports_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 Value;+ class Expression;+ class List;+ class Map;+ class Binary_Expression;+ class Unary_Expression;+ class Function_Call;+ class Function_Call_Schema;+ class Custom_Warning;+ class Custom_Error;+ 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 Supports_Condition;+ class Supports_Operator;+ class Supports_Negation;+ class Supports_Declaration;+ class Supports_Interpolation;+ 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_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;++ // common classes+ class Context;++}++#endif
+ libsass/src/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/src/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/src/backtrace.hpp view
@@ -0,0 +1,76 @@+#ifndef SASS_BACKTRACE_H+#define SASS_BACKTRACE_H++#include <sstream>++#include "file.hpp"+#include "position.hpp"++namespace Sass {+++ struct Backtrace {++ Backtrace* parent;+ ParserState pstate;+ std::string caller;++ Backtrace(Backtrace* prn, ParserState pstate, std::string c)+ : parent(prn),+ pstate(pstate),+ caller(c)+ { }++ std::string to_string(bool warning = false)+ {+ size_t i = -1;+ std::stringstream ss;+ std::string cwd(Sass::File::get_cwd());+ Backtrace* this_point = this;++ if (!warning) ss << std::endl << "Backtrace:";+ // the first tracepoint (which is parent-less) is an empty placeholder+ while (this_point->parent) {++ // make path relative to the current directory+ std::string rel_path(Sass::File::abs2rel(this_point->pstate.path, cwd, cwd));++ if (warning) {+ ss << std::endl+ << "\t"+ << (++i == 0 ? "on" : "from")+ << " line "+ << this_point->pstate.line + 1+ << " of "+ << rel_path;+ } else {+ ss << std::endl+ << "\t"+ << rel_path+ << ":"+ << this_point->pstate.line + 1+ << 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/src/base64vlq.cpp view
@@ -0,0 +1,43 @@+#include "base64vlq.hpp"++namespace Sass {++ std::string Base64VLQ::encode(const int number) const+ {+ std::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/src/base64vlq.hpp view
@@ -0,0 +1,30 @@+#ifndef SASS_BASE64VLQ_H+#define SASS_BASE64VLQ_H++#include <string>++namespace Sass {++ class Base64VLQ {++ public:++ std::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/src/bind.cpp view
@@ -0,0 +1,301 @@+#include "bind.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "eval.hpp"+#include <map>+#include <iostream>+#include <sstream>+#include "to_string.hpp"++namespace Sass {++ void bind(std::string type, std::string name, Parameters* ps, Arguments* as, Context* ctx, Env* env, Eval* eval)+ {+ std::string callee(type + " " + name);+ Listize listize(*ctx);+ std::map<std::string, Parameter*> param_map;++ for (size_t i = 0, L = as->length(); i < L; ++i) {+ if (auto str = dynamic_cast<String_Quoted*>((*as)[i]->value())) {+ // force optional quotes (only if needed)+ if (str->quote_mark()) {+ str->quote_mark('*');+ str->is_delayed(true);+ }+ }+ }++ // 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) {+ Argument* a = (*as)[ia];+ if (ip >= LP) {+ // skip empty rest arguments+ if (a->is_rest_argument()) {+ if (List* l = dynamic_cast<List*>(a->value())) {+ if (l->length() == 0) {+ ++ ia; continue;+ }+ }+ }+ std::stringstream msg;+ msg << "wrong number of arguments (" << LA << " for " << LP << ")";+ msg << " for `" << name << "'";+ return error(msg.str(), as->pstate());+ }+ Parameter* p = (*ps)[ip];++ // If the current parameter is the rest parameter, process and break the loop+ if (p->is_rest_parameter()) {+ // The next argument by coincidence provides a rest argument+ if (a->is_rest_argument()) {+ // We should always get a list for rest arguments+ if (List* rest = dynamic_cast<List*>(a->value())) {+ // arg contains a list+ List* args = rest;+ // make sure it's an arglist+ if (rest->is_arglist()) {+ // can pass it through as it was+ env->local_frame()[p->name()] = args;+ }+ // create a new list and wrap each item as an argument+ // otherwise we will not be able to fetch it again+ else {+ // create a new list object for wrapped items+ List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+ p->pstate(),+ 0,+ rest->separator(),+ true);+ // wrap each item from list as an argument+ for (Expression* item : rest->elements()) {+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+ item->pstate(),+ item,+ "",+ false,+ false);+ }+ // assign new arglist to environment+ env->local_frame()[p->name()] = arglist;+ }+ }+ // invalid state+ else {+ throw std::runtime_error("invalid state");+ }+ } else if (a->is_keyword_argument()) {++ // expand keyword arguments into their parameters+ List* arglist = SASS_MEMORY_NEW(ctx->mem, List, p->pstate(), 0, SASS_COMMA, true);+ env->local_frame()[p->name()] = arglist;+ Map* argmap = static_cast<Map*>(a->value());+ for (auto key : argmap->keys()) {+ std::string name = unquote(static_cast<String_Constant*>(key)->value());+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+ key->pstate(),+ argmap->at(key),+ "$" + name,+ false,+ false);+ }++ } else {++ // create a new list object for wrapped items+ List* arglist = SASS_MEMORY_NEW(ctx->mem, List,+ p->pstate(),+ 0,+ SASS_COMMA,+ true);+ // consume the next args+ while (ia < LA) {+ // get and post inc+ a = (*as)[ia++];+ // maybe we have another list as argument+ List* ls = dynamic_cast<List*>(a->value());+ // skip any list completely if empty+ if (ls && ls->empty() && a->is_rest_argument()) continue;+ // flatten all nested arglists+ if (ls && ls->is_arglist()) {+ for (size_t i = 0, L = ls->size(); i < L; ++i) {+ // already have a wrapped argument+ if (Argument* arg = dynamic_cast<Argument*>((*ls)[i])) {+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);+ }+ // wrap all other value types into Argument+ else {+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+ (*ls)[i]->pstate(),+ (*ls)[i],+ "",+ false,+ false);+ }+ }+ }+ // already have a wrapped argument+ else if (Argument* arg = dynamic_cast<Argument*>(a->value())) {+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument, *arg);+ }+ // wrap all other value types into Argument+ else {+ (*arglist) << SASS_MEMORY_NEW(ctx->mem, Argument,+ a->pstate(),+ a->value(),+ a->name(),+ false,+ false);+ }+ // check if we have rest argument+ if (a->is_rest_argument()) {+ // preserve the list separator from rest args+ if (List* rest = dynamic_cast<List*>(a->value())) {+ arglist->separator(rest->separator());+ }+ // no more arguments+ break;+ }+ }+ // assign new arglist to environment+ env->local_frame()[p->name()] = arglist;+ }+ // consumed parameter+ ++ip;+ // no more paramaters+ 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;+ } else {+ if (arglist->length() + ia > LP && !ps->has_rest_parameter()) {+ int arg_count = (arglist->length() + LA - 1);+ std::stringstream msg;+ msg << callee << " takes " << LP;+ msg << (LP == 1 ? " argument" : " arguments");+ msg << " but " << arg_count;+ msg << (arg_count == 1 ? " was passed" : " were passed.");+ deprecated_bind(msg.str(), as->pstate());+ }+ }+ // otherwise move one of the rest args into the param, converting to argument if necessary+ if (!(a = dynamic_cast<Argument*>((*arglist)[0]))) {+ Expression* a_to_convert = (*arglist)[0];+ a = SASS_MEMORY_NEW(ctx->mem, Argument,+ a_to_convert->pstate(),+ a_to_convert,+ "",+ false,+ 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()) {+ std::string name = "$" + unquote(static_cast<String_Constant*>(key)->value());++ if (!param_map.count(name)) {+ std::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())) {+ std::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())) {+ std::stringstream msg;+ msg << callee << " has no parameter named " << a->name();+ error(msg.str(), a->pstate());+ }+ if (param_map[a->name()]->is_rest_parameter()) {+ std::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())) {+ std::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();+ }+ }+ // EO while ia++ // 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()] = SASS_MEMORY_NEW(ctx->mem, List,+ leftover->pstate(),+ 0,+ SASS_COMMA,+ true);+ }+ else if (leftover->default_value()) {+ Expression* dv = leftover->default_value()->perform(eval);+ env->local_frame()[leftover->name()] = dv;+ }+ else {+ // param is unbound and has no default value -- error+ std::stringstream msg;+ msg << "required parameter " << leftover->name()+ << " is missing in call to " << callee;+ error(msg.str(), as->pstate());+ }+ }+ }++ return;+ }+++}
+ libsass/src/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(std::string type, std::string name, Parameters*, Arguments*, Context*, Env*, Eval*);+}++#endif
+ libsass/src/c99func.c view
@@ -0,0 +1,54 @@+/*+ 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.+*/++#if defined(_MSC_VER) && _MSC_VER < 1900++#include <stdio.h>+#include <stdlib.h>+#include <stdarg.h>++static 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;+}++int 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
+ libsass/src/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 (int)(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 (int)(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 (int)(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 (int)(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 (int)(codechar - code_out);+}+
+ libsass/src/color_maps.cpp view
@@ -0,0 +1,643 @@+#include "ast.hpp"+#include "color_maps.hpp"++namespace Sass {++ namespace ColorNames+ {+ const char aliceblue [] = "aliceblue";+ const char antiquewhite [] = "antiquewhite";+ const char cyan [] = "cyan";+ const char aqua [] = "aqua";+ const char aquamarine [] = "aquamarine";+ const char azure [] = "azure";+ const char beige [] = "beige";+ const char bisque [] = "bisque";+ const char black [] = "black";+ const char blanchedalmond [] = "blanchedalmond";+ const char blue [] = "blue";+ const char blueviolet [] = "blueviolet";+ const char brown [] = "brown";+ const char burlywood [] = "burlywood";+ const char cadetblue [] = "cadetblue";+ const char chartreuse [] = "chartreuse";+ const char chocolate [] = "chocolate";+ const char coral [] = "coral";+ const char cornflowerblue [] = "cornflowerblue";+ const char cornsilk [] = "cornsilk";+ const char crimson [] = "crimson";+ const char darkblue [] = "darkblue";+ const char darkcyan [] = "darkcyan";+ const char darkgoldenrod [] = "darkgoldenrod";+ const char darkgray [] = "darkgray";+ const char darkgrey [] = "darkgrey";+ const char darkgreen [] = "darkgreen";+ const char darkkhaki [] = "darkkhaki";+ const char darkmagenta [] = "darkmagenta";+ const char darkolivegreen [] = "darkolivegreen";+ const char darkorange [] = "darkorange";+ const char darkorchid [] = "darkorchid";+ const char darkred [] = "darkred";+ const char darksalmon [] = "darksalmon";+ const char darkseagreen [] = "darkseagreen";+ const char darkslateblue [] = "darkslateblue";+ const char darkslategray [] = "darkslategray";+ const char darkslategrey [] = "darkslategrey";+ const char darkturquoise [] = "darkturquoise";+ const char darkviolet [] = "darkviolet";+ const char deeppink [] = "deeppink";+ const char deepskyblue [] = "deepskyblue";+ const char dimgray [] = "dimgray";+ const char dimgrey [] = "dimgrey";+ const char dodgerblue [] = "dodgerblue";+ const char firebrick [] = "firebrick";+ const char floralwhite [] = "floralwhite";+ const char forestgreen [] = "forestgreen";+ const char magenta [] = "magenta";+ const char fuchsia [] = "fuchsia";+ const char gainsboro [] = "gainsboro";+ const char ghostwhite [] = "ghostwhite";+ const char gold [] = "gold";+ const char goldenrod [] = "goldenrod";+ const char gray [] = "gray";+ const char grey [] = "grey";+ const char green [] = "green";+ const char greenyellow [] = "greenyellow";+ const char honeydew [] = "honeydew";+ const char hotpink [] = "hotpink";+ const char indianred [] = "indianred";+ const char indigo [] = "indigo";+ const char ivory [] = "ivory";+ const char khaki [] = "khaki";+ const char lavender [] = "lavender";+ const char lavenderblush [] = "lavenderblush";+ const char lawngreen [] = "lawngreen";+ const char lemonchiffon [] = "lemonchiffon";+ const char lightblue [] = "lightblue";+ const char lightcoral [] = "lightcoral";+ const char lightcyan [] = "lightcyan";+ const char lightgoldenrodyellow [] = "lightgoldenrodyellow";+ const char lightgray [] = "lightgray";+ const char lightgrey [] = "lightgrey";+ const char lightgreen [] = "lightgreen";+ const char lightpink [] = "lightpink";+ const char lightsalmon [] = "lightsalmon";+ const char lightseagreen [] = "lightseagreen";+ const char lightskyblue [] = "lightskyblue";+ const char lightslategray [] = "lightslategray";+ const char lightslategrey [] = "lightslategrey";+ const char lightsteelblue [] = "lightsteelblue";+ const char lightyellow [] = "lightyellow";+ const char lime [] = "lime";+ const char limegreen [] = "limegreen";+ const char linen [] = "linen";+ const char maroon [] = "maroon";+ const char mediumaquamarine [] = "mediumaquamarine";+ const char mediumblue [] = "mediumblue";+ const char mediumorchid [] = "mediumorchid";+ const char mediumpurple [] = "mediumpurple";+ const char mediumseagreen [] = "mediumseagreen";+ const char mediumslateblue [] = "mediumslateblue";+ const char mediumspringgreen [] = "mediumspringgreen";+ const char mediumturquoise [] = "mediumturquoise";+ const char mediumvioletred [] = "mediumvioletred";+ const char midnightblue [] = "midnightblue";+ const char mintcream [] = "mintcream";+ const char mistyrose [] = "mistyrose";+ const char moccasin [] = "moccasin";+ const char navajowhite [] = "navajowhite";+ const char navy [] = "navy";+ const char oldlace [] = "oldlace";+ const char olive [] = "olive";+ const char olivedrab [] = "olivedrab";+ const char orange [] = "orange";+ const char orangered [] = "orangered";+ const char orchid [] = "orchid";+ const char palegoldenrod [] = "palegoldenrod";+ const char palegreen [] = "palegreen";+ const char paleturquoise [] = "paleturquoise";+ const char palevioletred [] = "palevioletred";+ const char papayawhip [] = "papayawhip";+ const char peachpuff [] = "peachpuff";+ const char peru [] = "peru";+ const char pink [] = "pink";+ const char plum [] = "plum";+ const char powderblue [] = "powderblue";+ const char purple [] = "purple";+ const char red [] = "red";+ const char rosybrown [] = "rosybrown";+ const char royalblue [] = "royalblue";+ const char saddlebrown [] = "saddlebrown";+ const char salmon [] = "salmon";+ const char sandybrown [] = "sandybrown";+ const char seagreen [] = "seagreen";+ const char seashell [] = "seashell";+ const char sienna [] = "sienna";+ const char silver [] = "silver";+ const char skyblue [] = "skyblue";+ const char slateblue [] = "slateblue";+ const char slategray [] = "slategray";+ const char slategrey [] = "slategrey";+ const char snow [] = "snow";+ const char springgreen [] = "springgreen";+ const char steelblue [] = "steelblue";+ const char tan [] = "tan";+ const char teal [] = "teal";+ const char thistle [] = "thistle";+ const char tomato [] = "tomato";+ const char turquoise [] = "turquoise";+ const char violet [] = "violet";+ const char wheat [] = "wheat";+ const char white [] = "white";+ const char whitesmoke [] = "whitesmoke";+ const char yellow [] = "yellow";+ const char yellowgreen [] = "yellowgreen";+ const char rebeccapurple [] = "rebeccapurple";+ const char transparent [] = "transparent";+ }++ namespace Colors {+ const ParserState color_table("[COLOR TABLE]");+ const Color aliceblue(color_table, 240, 248, 255, 1);+ const Color antiquewhite(color_table, 250, 235, 215, 1);+ const Color cyan(color_table, 0, 255, 255, 1);+ const Color aqua(color_table, 0, 255, 255, 1);+ const Color aquamarine(color_table, 127, 255, 212, 1);+ const Color azure(color_table, 240, 255, 255, 1);+ const Color beige(color_table, 245, 245, 220, 1);+ const Color bisque(color_table, 255, 228, 196, 1);+ const Color black(color_table, 0, 0, 0, 1);+ const Color blanchedalmond(color_table, 255, 235, 205, 1);+ const Color blue(color_table, 0, 0, 255, 1);+ const Color blueviolet(color_table, 138, 43, 226, 1);+ const Color brown(color_table, 165, 42, 42, 1);+ const Color burlywood(color_table, 222, 184, 135, 1);+ const Color cadetblue(color_table, 95, 158, 160, 1);+ const Color chartreuse(color_table, 127, 255, 0, 1);+ const Color chocolate(color_table, 210, 105, 30, 1);+ const Color coral(color_table, 255, 127, 80, 1);+ const Color cornflowerblue(color_table, 100, 149, 237, 1);+ const Color cornsilk(color_table, 255, 248, 220, 1);+ const Color crimson(color_table, 220, 20, 60, 1);+ const Color darkblue(color_table, 0, 0, 139, 1);+ const Color darkcyan(color_table, 0, 139, 139, 1);+ const Color darkgoldenrod(color_table, 184, 134, 11, 1);+ const Color darkgray(color_table, 169, 169, 169, 1);+ const Color darkgrey(color_table, 169, 169, 169, 1);+ const Color darkgreen(color_table, 0, 100, 0, 1);+ const Color darkkhaki(color_table, 189, 183, 107, 1);+ const Color darkmagenta(color_table, 139, 0, 139, 1);+ const Color darkolivegreen(color_table, 85, 107, 47, 1);+ const Color darkorange(color_table, 255, 140, 0, 1);+ const Color darkorchid(color_table, 153, 50, 204, 1);+ const Color darkred(color_table, 139, 0, 0, 1);+ const Color darksalmon(color_table, 233, 150, 122, 1);+ const Color darkseagreen(color_table, 143, 188, 143, 1);+ const Color darkslateblue(color_table, 72, 61, 139, 1);+ const Color darkslategray(color_table, 47, 79, 79, 1);+ const Color darkslategrey(color_table, 47, 79, 79, 1);+ const Color darkturquoise(color_table, 0, 206, 209, 1);+ const Color darkviolet(color_table, 148, 0, 211, 1);+ const Color deeppink(color_table, 255, 20, 147, 1);+ const Color deepskyblue(color_table, 0, 191, 255, 1);+ const Color dimgray(color_table, 105, 105, 105, 1);+ const Color dimgrey(color_table, 105, 105, 105, 1);+ const Color dodgerblue(color_table, 30, 144, 255, 1);+ const Color firebrick(color_table, 178, 34, 34, 1);+ const Color floralwhite(color_table, 255, 250, 240, 1);+ const Color forestgreen(color_table, 34, 139, 34, 1);+ const Color magenta(color_table, 255, 0, 255, 1);+ const Color fuchsia(color_table, 255, 0, 255, 1);+ const Color gainsboro(color_table, 220, 220, 220, 1);+ const Color ghostwhite(color_table, 248, 248, 255, 1);+ const Color gold(color_table, 255, 215, 0, 1);+ const Color goldenrod(color_table, 218, 165, 32, 1);+ const Color gray(color_table, 128, 128, 128, 1);+ const Color grey(color_table, 128, 128, 128, 1);+ const Color green(color_table, 0, 128, 0, 1);+ const Color greenyellow(color_table, 173, 255, 47, 1);+ const Color honeydew(color_table, 240, 255, 240, 1);+ const Color hotpink(color_table, 255, 105, 180, 1);+ const Color indianred(color_table, 205, 92, 92, 1);+ const Color indigo(color_table, 75, 0, 130, 1);+ const Color ivory(color_table, 255, 255, 240, 1);+ const Color khaki(color_table, 240, 230, 140, 1);+ const Color lavender(color_table, 230, 230, 250, 1);+ const Color lavenderblush(color_table, 255, 240, 245, 1);+ const Color lawngreen(color_table, 124, 252, 0, 1);+ const Color lemonchiffon(color_table, 255, 250, 205, 1);+ const Color lightblue(color_table, 173, 216, 230, 1);+ const Color lightcoral(color_table, 240, 128, 128, 1);+ const Color lightcyan(color_table, 224, 255, 255, 1);+ const Color lightgoldenrodyellow(color_table, 250, 250, 210, 1);+ const Color lightgray(color_table, 211, 211, 211, 1);+ const Color lightgrey(color_table, 211, 211, 211, 1);+ const Color lightgreen(color_table, 144, 238, 144, 1);+ const Color lightpink(color_table, 255, 182, 193, 1);+ const Color lightsalmon(color_table, 255, 160, 122, 1);+ const Color lightseagreen(color_table, 32, 178, 170, 1);+ const Color lightskyblue(color_table, 135, 206, 250, 1);+ const Color lightslategray(color_table, 119, 136, 153, 1);+ const Color lightslategrey(color_table, 119, 136, 153, 1);+ const Color lightsteelblue(color_table, 176, 196, 222, 1);+ const Color lightyellow(color_table, 255, 255, 224, 1);+ const Color lime(color_table, 0, 255, 0, 1);+ const Color limegreen(color_table, 50, 205, 50, 1);+ const Color linen(color_table, 250, 240, 230, 1);+ const Color maroon(color_table, 128, 0, 0, 1);+ const Color mediumaquamarine(color_table, 102, 205, 170, 1);+ const Color mediumblue(color_table, 0, 0, 205, 1);+ const Color mediumorchid(color_table, 186, 85, 211, 1);+ const Color mediumpurple(color_table, 147, 112, 219, 1);+ const Color mediumseagreen(color_table, 60, 179, 113, 1);+ const Color mediumslateblue(color_table, 123, 104, 238, 1);+ const Color mediumspringgreen(color_table, 0, 250, 154, 1);+ const Color mediumturquoise(color_table, 72, 209, 204, 1);+ const Color mediumvioletred(color_table, 199, 21, 133, 1);+ const Color midnightblue(color_table, 25, 25, 112, 1);+ const Color mintcream(color_table, 245, 255, 250, 1);+ const Color mistyrose(color_table, 255, 228, 225, 1);+ const Color moccasin(color_table, 255, 228, 181, 1);+ const Color navajowhite(color_table, 255, 222, 173, 1);+ const Color navy(color_table, 0, 0, 128, 1);+ const Color oldlace(color_table, 253, 245, 230, 1);+ const Color olive(color_table, 128, 128, 0, 1);+ const Color olivedrab(color_table, 107, 142, 35, 1);+ const Color orange(color_table, 255, 165, 0, 1);+ const Color orangered(color_table, 255, 69, 0, 1);+ const Color orchid(color_table, 218, 112, 214, 1);+ const Color palegoldenrod(color_table, 238, 232, 170, 1);+ const Color palegreen(color_table, 152, 251, 152, 1);+ const Color paleturquoise(color_table, 175, 238, 238, 1);+ const Color palevioletred(color_table, 219, 112, 147, 1);+ const Color papayawhip(color_table, 255, 239, 213, 1);+ const Color peachpuff(color_table, 255, 218, 185, 1);+ const Color peru(color_table, 205, 133, 63, 1);+ const Color pink(color_table, 255, 192, 203, 1);+ const Color plum(color_table, 221, 160, 221, 1);+ const Color powderblue(color_table, 176, 224, 230, 1);+ const Color purple(color_table, 128, 0, 128, 1);+ const Color red(color_table, 255, 0, 0, 1);+ const Color rosybrown(color_table, 188, 143, 143, 1);+ const Color royalblue(color_table, 65, 105, 225, 1);+ const Color saddlebrown(color_table, 139, 69, 19, 1);+ const Color salmon(color_table, 250, 128, 114, 1);+ const Color sandybrown(color_table, 244, 164, 96, 1);+ const Color seagreen(color_table, 46, 139, 87, 1);+ const Color seashell(color_table, 255, 245, 238, 1);+ const Color sienna(color_table, 160, 82, 45, 1);+ const Color silver(color_table, 192, 192, 192, 1);+ const Color skyblue(color_table, 135, 206, 235, 1);+ const Color slateblue(color_table, 106, 90, 205, 1);+ const Color slategray(color_table, 112, 128, 144, 1);+ const Color slategrey(color_table, 112, 128, 144, 1);+ const Color snow(color_table, 255, 250, 250, 1);+ const Color springgreen(color_table, 0, 255, 127, 1);+ const Color steelblue(color_table, 70, 130, 180, 1);+ const Color tan(color_table, 210, 180, 140, 1);+ const Color teal(color_table, 0, 128, 128, 1);+ const Color thistle(color_table, 216, 191, 216, 1);+ const Color tomato(color_table, 255, 99, 71, 1);+ const Color turquoise(color_table, 64, 224, 208, 1);+ const Color violet(color_table, 238, 130, 238, 1);+ const Color wheat(color_table, 245, 222, 179, 1);+ const Color white(color_table, 255, 255, 255, 1);+ const Color whitesmoke(color_table, 245, 245, 245, 1);+ const Color yellow(color_table, 255, 255, 0, 1);+ const Color yellowgreen(color_table, 154, 205, 50, 1);+ const Color rebeccapurple(color_table, 102, 51, 153, 1);+ const Color transparent(color_table, 0, 0, 0, 0);+ }++ const std::map<const int, const char*> colors_to_names {+ { 240 * 0x10000 + 248 * 0x100 + 255, ColorNames::aliceblue },+ { 250 * 0x10000 + 235 * 0x100 + 215, ColorNames::antiquewhite },+ { 0 * 0x10000 + 255 * 0x100 + 255, ColorNames::cyan },+ { 127 * 0x10000 + 255 * 0x100 + 212, ColorNames::aquamarine },+ { 240 * 0x10000 + 255 * 0x100 + 255, ColorNames::azure },+ { 245 * 0x10000 + 245 * 0x100 + 220, ColorNames::beige },+ { 255 * 0x10000 + 228 * 0x100 + 196, ColorNames::bisque },+ { 0 * 0x10000 + 0 * 0x100 + 0, ColorNames::black },+ { 255 * 0x10000 + 235 * 0x100 + 205, ColorNames::blanchedalmond },+ { 0 * 0x10000 + 0 * 0x100 + 255, ColorNames::blue },+ { 138 * 0x10000 + 43 * 0x100 + 226, ColorNames::blueviolet },+ { 165 * 0x10000 + 42 * 0x100 + 42, ColorNames::brown },+ { 222 * 0x10000 + 184 * 0x100 + 135, ColorNames::burlywood },+ { 95 * 0x10000 + 158 * 0x100 + 160, ColorNames::cadetblue },+ { 127 * 0x10000 + 255 * 0x100 + 0, ColorNames::chartreuse },+ { 210 * 0x10000 + 105 * 0x100 + 30, ColorNames::chocolate },+ { 255 * 0x10000 + 127 * 0x100 + 80, ColorNames::coral },+ { 100 * 0x10000 + 149 * 0x100 + 237, ColorNames::cornflowerblue },+ { 255 * 0x10000 + 248 * 0x100 + 220, ColorNames::cornsilk },+ { 220 * 0x10000 + 20 * 0x100 + 60, ColorNames::crimson },+ { 0 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkblue },+ { 0 * 0x10000 + 139 * 0x100 + 139, ColorNames::darkcyan },+ { 184 * 0x10000 + 134 * 0x100 + 11, ColorNames::darkgoldenrod },+ { 169 * 0x10000 + 169 * 0x100 + 169, ColorNames::darkgray },+ { 0 * 0x10000 + 100 * 0x100 + 0, ColorNames::darkgreen },+ { 189 * 0x10000 + 183 * 0x100 + 107, ColorNames::darkkhaki },+ { 139 * 0x10000 + 0 * 0x100 + 139, ColorNames::darkmagenta },+ { 85 * 0x10000 + 107 * 0x100 + 47, ColorNames::darkolivegreen },+ { 255 * 0x10000 + 140 * 0x100 + 0, ColorNames::darkorange },+ { 153 * 0x10000 + 50 * 0x100 + 204, ColorNames::darkorchid },+ { 139 * 0x10000 + 0 * 0x100 + 0, ColorNames::darkred },+ { 233 * 0x10000 + 150 * 0x100 + 122, ColorNames::darksalmon },+ { 143 * 0x10000 + 188 * 0x100 + 143, ColorNames::darkseagreen },+ { 72 * 0x10000 + 61 * 0x100 + 139, ColorNames::darkslateblue },+ { 47 * 0x10000 + 79 * 0x100 + 79, ColorNames::darkslategray },+ { 0 * 0x10000 + 206 * 0x100 + 209, ColorNames::darkturquoise },+ { 148 * 0x10000 + 0 * 0x100 + 211, ColorNames::darkviolet },+ { 255 * 0x10000 + 20 * 0x100 + 147, ColorNames::deeppink },+ { 0 * 0x10000 + 191 * 0x100 + 255, ColorNames::deepskyblue },+ { 105 * 0x10000 + 105 * 0x100 + 105, ColorNames::dimgray },+ { 30 * 0x10000 + 144 * 0x100 + 255, ColorNames::dodgerblue },+ { 178 * 0x10000 + 34 * 0x100 + 34, ColorNames::firebrick },+ { 255 * 0x10000 + 250 * 0x100 + 240, ColorNames::floralwhite },+ { 34 * 0x10000 + 139 * 0x100 + 34, ColorNames::forestgreen },+ { 255 * 0x10000 + 0 * 0x100 + 255, ColorNames::magenta },+ { 220 * 0x10000 + 220 * 0x100 + 220, ColorNames::gainsboro },+ { 248 * 0x10000 + 248 * 0x100 + 255, ColorNames::ghostwhite },+ { 255 * 0x10000 + 215 * 0x100 + 0, ColorNames::gold },+ { 218 * 0x10000 + 165 * 0x100 + 32, ColorNames::goldenrod },+ { 128 * 0x10000 + 128 * 0x100 + 128, ColorNames::gray },+ { 0 * 0x10000 + 128 * 0x100 + 0, ColorNames::green },+ { 173 * 0x10000 + 255 * 0x100 + 47, ColorNames::greenyellow },+ { 240 * 0x10000 + 255 * 0x100 + 240, ColorNames::honeydew },+ { 255 * 0x10000 + 105 * 0x100 + 180, ColorNames::hotpink },+ { 205 * 0x10000 + 92 * 0x100 + 92, ColorNames::indianred },+ { 75 * 0x10000 + 0 * 0x100 + 130, ColorNames::indigo },+ { 255 * 0x10000 + 255 * 0x100 + 240, ColorNames::ivory },+ { 240 * 0x10000 + 230 * 0x100 + 140, ColorNames::khaki },+ { 230 * 0x10000 + 230 * 0x100 + 250, ColorNames::lavender },+ { 255 * 0x10000 + 240 * 0x100 + 245, ColorNames::lavenderblush },+ { 124 * 0x10000 + 252 * 0x100 + 0, ColorNames::lawngreen },+ { 255 * 0x10000 + 250 * 0x100 + 205, ColorNames::lemonchiffon },+ { 173 * 0x10000 + 216 * 0x100 + 230, ColorNames::lightblue },+ { 240 * 0x10000 + 128 * 0x100 + 128, ColorNames::lightcoral },+ { 224 * 0x10000 + 255 * 0x100 + 255, ColorNames::lightcyan },+ { 250 * 0x10000 + 250 * 0x100 + 210, ColorNames::lightgoldenrodyellow },+ { 211 * 0x10000 + 211 * 0x100 + 211, ColorNames::lightgray },+ { 144 * 0x10000 + 238 * 0x100 + 144, ColorNames::lightgreen },+ { 255 * 0x10000 + 182 * 0x100 + 193, ColorNames::lightpink },+ { 255 * 0x10000 + 160 * 0x100 + 122, ColorNames::lightsalmon },+ { 32 * 0x10000 + 178 * 0x100 + 170, ColorNames::lightseagreen },+ { 135 * 0x10000 + 206 * 0x100 + 250, ColorNames::lightskyblue },+ { 119 * 0x10000 + 136 * 0x100 + 153, ColorNames::lightslategray },+ { 176 * 0x10000 + 196 * 0x100 + 222, ColorNames::lightsteelblue },+ { 255 * 0x10000 + 255 * 0x100 + 224, ColorNames::lightyellow },+ { 0 * 0x10000 + 255 * 0x100 + 0, ColorNames::lime },+ { 50 * 0x10000 + 205 * 0x100 + 50, ColorNames::limegreen },+ { 250 * 0x10000 + 240 * 0x100 + 230, ColorNames::linen },+ { 128 * 0x10000 + 0 * 0x100 + 0, ColorNames::maroon },+ { 102 * 0x10000 + 205 * 0x100 + 170, ColorNames::mediumaquamarine },+ { 0 * 0x10000 + 0 * 0x100 + 205, ColorNames::mediumblue },+ { 186 * 0x10000 + 85 * 0x100 + 211, ColorNames::mediumorchid },+ { 147 * 0x10000 + 112 * 0x100 + 219, ColorNames::mediumpurple },+ { 60 * 0x10000 + 179 * 0x100 + 113, ColorNames::mediumseagreen },+ { 123 * 0x10000 + 104 * 0x100 + 238, ColorNames::mediumslateblue },+ { 0 * 0x10000 + 250 * 0x100 + 154, ColorNames::mediumspringgreen },+ { 72 * 0x10000 + 209 * 0x100 + 204, ColorNames::mediumturquoise },+ { 199 * 0x10000 + 21 * 0x100 + 133, ColorNames::mediumvioletred },+ { 25 * 0x10000 + 25 * 0x100 + 112, ColorNames::midnightblue },+ { 245 * 0x10000 + 255 * 0x100 + 250, ColorNames::mintcream },+ { 255 * 0x10000 + 228 * 0x100 + 225, ColorNames::mistyrose },+ { 255 * 0x10000 + 228 * 0x100 + 181, ColorNames::moccasin },+ { 255 * 0x10000 + 222 * 0x100 + 173, ColorNames::navajowhite },+ { 0 * 0x10000 + 0 * 0x100 + 128, ColorNames::navy },+ { 253 * 0x10000 + 245 * 0x100 + 230, ColorNames::oldlace },+ { 128 * 0x10000 + 128 * 0x100 + 0, ColorNames::olive },+ { 107 * 0x10000 + 142 * 0x100 + 35, ColorNames::olivedrab },+ { 255 * 0x10000 + 165 * 0x100 + 0, ColorNames::orange },+ { 255 * 0x10000 + 69 * 0x100 + 0, ColorNames::orangered },+ { 218 * 0x10000 + 112 * 0x100 + 214, ColorNames::orchid },+ { 238 * 0x10000 + 232 * 0x100 + 170, ColorNames::palegoldenrod },+ { 152 * 0x10000 + 251 * 0x100 + 152, ColorNames::palegreen },+ { 175 * 0x10000 + 238 * 0x100 + 238, ColorNames::paleturquoise },+ { 219 * 0x10000 + 112 * 0x100 + 147, ColorNames::palevioletred },+ { 255 * 0x10000 + 239 * 0x100 + 213, ColorNames::papayawhip },+ { 255 * 0x10000 + 218 * 0x100 + 185, ColorNames::peachpuff },+ { 205 * 0x10000 + 133 * 0x100 + 63, ColorNames::peru },+ { 255 * 0x10000 + 192 * 0x100 + 203, ColorNames::pink },+ { 221 * 0x10000 + 160 * 0x100 + 221, ColorNames::plum },+ { 176 * 0x10000 + 224 * 0x100 + 230, ColorNames::powderblue },+ { 128 * 0x10000 + 0 * 0x100 + 128, ColorNames::purple },+ { 255 * 0x10000 + 0 * 0x100 + 0, ColorNames::red },+ { 188 * 0x10000 + 143 * 0x100 + 143, ColorNames::rosybrown },+ { 65 * 0x10000 + 105 * 0x100 + 225, ColorNames::royalblue },+ { 139 * 0x10000 + 69 * 0x100 + 19, ColorNames::saddlebrown },+ { 250 * 0x10000 + 128 * 0x100 + 114, ColorNames::salmon },+ { 244 * 0x10000 + 164 * 0x100 + 96, ColorNames::sandybrown },+ { 46 * 0x10000 + 139 * 0x100 + 87, ColorNames::seagreen },+ { 255 * 0x10000 + 245 * 0x100 + 238, ColorNames::seashell },+ { 160 * 0x10000 + 82 * 0x100 + 45, ColorNames::sienna },+ { 192 * 0x10000 + 192 * 0x100 + 192, ColorNames::silver },+ { 135 * 0x10000 + 206 * 0x100 + 235, ColorNames::skyblue },+ { 106 * 0x10000 + 90 * 0x100 + 205, ColorNames::slateblue },+ { 112 * 0x10000 + 128 * 0x100 + 144, ColorNames::slategray },+ { 255 * 0x10000 + 250 * 0x100 + 250, ColorNames::snow },+ { 0 * 0x10000 + 255 * 0x100 + 127, ColorNames::springgreen },+ { 70 * 0x10000 + 130 * 0x100 + 180, ColorNames::steelblue },+ { 210 * 0x10000 + 180 * 0x100 + 140, ColorNames::tan },+ { 0 * 0x10000 + 128 * 0x100 + 128, ColorNames::teal },+ { 216 * 0x10000 + 191 * 0x100 + 216, ColorNames::thistle },+ { 255 * 0x10000 + 99 * 0x100 + 71, ColorNames::tomato },+ { 64 * 0x10000 + 224 * 0x100 + 208, ColorNames::turquoise },+ { 238 * 0x10000 + 130 * 0x100 + 238, ColorNames::violet },+ { 245 * 0x10000 + 222 * 0x100 + 179, ColorNames::wheat },+ { 255 * 0x10000 + 255 * 0x100 + 255, ColorNames::white },+ { 245 * 0x10000 + 245 * 0x100 + 245, ColorNames::whitesmoke },+ { 255 * 0x10000 + 255 * 0x100 + 0, ColorNames::yellow },+ { 154 * 0x10000 + 205 * 0x100 + 50, ColorNames::yellowgreen },+ { 102 * 0x10000 + 51 * 0x100 + 153, ColorNames::rebeccapurple }+ };++ const std::map<const char*, const Color*, map_cmp_str> names_to_colors+ {+ { ColorNames::aliceblue, &Colors::aliceblue },+ { ColorNames::antiquewhite, &Colors::antiquewhite },+ { ColorNames::cyan, &Colors::cyan },+ { ColorNames::aqua, &Colors::aqua },+ { ColorNames::aquamarine, &Colors::aquamarine },+ { ColorNames::azure, &Colors::azure },+ { ColorNames::beige, &Colors::beige },+ { ColorNames::bisque, &Colors::bisque },+ { ColorNames::black, &Colors::black },+ { ColorNames::blanchedalmond, &Colors::blanchedalmond },+ { ColorNames::blue, &Colors::blue },+ { ColorNames::blueviolet, &Colors::blueviolet },+ { ColorNames::brown, &Colors::brown },+ { ColorNames::burlywood, &Colors::burlywood },+ { ColorNames::cadetblue, &Colors::cadetblue },+ { ColorNames::chartreuse, &Colors::chartreuse },+ { ColorNames::chocolate, &Colors::chocolate },+ { ColorNames::coral, &Colors::coral },+ { ColorNames::cornflowerblue, &Colors::cornflowerblue },+ { ColorNames::cornsilk, &Colors::cornsilk },+ { ColorNames::crimson, &Colors::crimson },+ { ColorNames::darkblue, &Colors::darkblue },+ { ColorNames::darkcyan, &Colors::darkcyan },+ { ColorNames::darkgoldenrod, &Colors::darkgoldenrod },+ { ColorNames::darkgray, &Colors::darkgray },+ { ColorNames::darkgrey, &Colors::darkgrey },+ { ColorNames::darkgreen, &Colors::darkgreen },+ { ColorNames::darkkhaki, &Colors::darkkhaki },+ { ColorNames::darkmagenta, &Colors::darkmagenta },+ { ColorNames::darkolivegreen, &Colors::darkolivegreen },+ { ColorNames::darkorange, &Colors::darkorange },+ { ColorNames::darkorchid, &Colors::darkorchid },+ { ColorNames::darkred, &Colors::darkred },+ { ColorNames::darksalmon, &Colors::darksalmon },+ { ColorNames::darkseagreen, &Colors::darkseagreen },+ { ColorNames::darkslateblue, &Colors::darkslateblue },+ { ColorNames::darkslategray, &Colors::darkslategray },+ { ColorNames::darkslategrey, &Colors::darkslategrey },+ { ColorNames::darkturquoise, &Colors::darkturquoise },+ { ColorNames::darkviolet, &Colors::darkviolet },+ { ColorNames::deeppink, &Colors::deeppink },+ { ColorNames::deepskyblue, &Colors::deepskyblue },+ { ColorNames::dimgray, &Colors::dimgray },+ { ColorNames::dimgrey, &Colors::dimgrey },+ { ColorNames::dodgerblue, &Colors::dodgerblue },+ { ColorNames::firebrick, &Colors::firebrick },+ { ColorNames::floralwhite, &Colors::floralwhite },+ { ColorNames::forestgreen, &Colors::forestgreen },+ { ColorNames::magenta, &Colors::magenta },+ { ColorNames::fuchsia, &Colors::fuchsia },+ { ColorNames::gainsboro, &Colors::gainsboro },+ { ColorNames::ghostwhite, &Colors::ghostwhite },+ { ColorNames::gold, &Colors::gold },+ { ColorNames::goldenrod, &Colors::goldenrod },+ { ColorNames::gray, &Colors::gray },+ { ColorNames::grey, &Colors::grey },+ { ColorNames::green, &Colors::green },+ { ColorNames::greenyellow, &Colors::greenyellow },+ { ColorNames::honeydew, &Colors::honeydew },+ { ColorNames::hotpink, &Colors::hotpink },+ { ColorNames::indianred, &Colors::indianred },+ { ColorNames::indigo, &Colors::indigo },+ { ColorNames::ivory, &Colors::ivory },+ { ColorNames::khaki, &Colors::khaki },+ { ColorNames::lavender, &Colors::lavender },+ { ColorNames::lavenderblush, &Colors::lavenderblush },+ { ColorNames::lawngreen, &Colors::lawngreen },+ { ColorNames::lemonchiffon, &Colors::lemonchiffon },+ { ColorNames::lightblue, &Colors::lightblue },+ { ColorNames::lightcoral, &Colors::lightcoral },+ { ColorNames::lightcyan, &Colors::lightcyan },+ { ColorNames::lightgoldenrodyellow, &Colors::lightgoldenrodyellow },+ { ColorNames::lightgray, &Colors::lightgray },+ { ColorNames::lightgrey, &Colors::lightgrey },+ { ColorNames::lightgreen, &Colors::lightgreen },+ { ColorNames::lightpink, &Colors::lightpink },+ { ColorNames::lightsalmon, &Colors::lightsalmon },+ { ColorNames::lightseagreen, &Colors::lightseagreen },+ { ColorNames::lightskyblue, &Colors::lightskyblue },+ { ColorNames::lightslategray, &Colors::lightslategray },+ { ColorNames::lightslategrey, &Colors::lightslategrey },+ { ColorNames::lightsteelblue, &Colors::lightsteelblue },+ { ColorNames::lightyellow, &Colors::lightyellow },+ { ColorNames::lime, &Colors::lime },+ { ColorNames::limegreen, &Colors::limegreen },+ { ColorNames::linen, &Colors::linen },+ { ColorNames::maroon, &Colors::maroon },+ { ColorNames::mediumaquamarine, &Colors::mediumaquamarine },+ { ColorNames::mediumblue, &Colors::mediumblue },+ { ColorNames::mediumorchid, &Colors::mediumorchid },+ { ColorNames::mediumpurple, &Colors::mediumpurple },+ { ColorNames::mediumseagreen, &Colors::mediumseagreen },+ { ColorNames::mediumslateblue, &Colors::mediumslateblue },+ { ColorNames::mediumspringgreen, &Colors::mediumspringgreen },+ { ColorNames::mediumturquoise, &Colors::mediumturquoise },+ { ColorNames::mediumvioletred, &Colors::mediumvioletred },+ { ColorNames::midnightblue, &Colors::midnightblue },+ { ColorNames::mintcream, &Colors::mintcream },+ { ColorNames::mistyrose, &Colors::mistyrose },+ { ColorNames::moccasin, &Colors::moccasin },+ { ColorNames::navajowhite, &Colors::navajowhite },+ { ColorNames::navy, &Colors::navy },+ { ColorNames::oldlace, &Colors::oldlace },+ { ColorNames::olive, &Colors::olive },+ { ColorNames::olivedrab, &Colors::olivedrab },+ { ColorNames::orange, &Colors::orange },+ { ColorNames::orangered, &Colors::orangered },+ { ColorNames::orchid, &Colors::orchid },+ { ColorNames::palegoldenrod, &Colors::palegoldenrod },+ { ColorNames::palegreen, &Colors::palegreen },+ { ColorNames::paleturquoise, &Colors::paleturquoise },+ { ColorNames::palevioletred, &Colors::palevioletred },+ { ColorNames::papayawhip, &Colors::papayawhip },+ { ColorNames::peachpuff, &Colors::peachpuff },+ { ColorNames::peru, &Colors::peru },+ { ColorNames::pink, &Colors::pink },+ { ColorNames::plum, &Colors::plum },+ { ColorNames::powderblue, &Colors::powderblue },+ { ColorNames::purple, &Colors::purple },+ { ColorNames::red, &Colors::red },+ { ColorNames::rosybrown, &Colors::rosybrown },+ { ColorNames::royalblue, &Colors::royalblue },+ { ColorNames::saddlebrown, &Colors::saddlebrown },+ { ColorNames::salmon, &Colors::salmon },+ { ColorNames::sandybrown, &Colors::sandybrown },+ { ColorNames::seagreen, &Colors::seagreen },+ { ColorNames::seashell, &Colors::seashell },+ { ColorNames::sienna, &Colors::sienna },+ { ColorNames::silver, &Colors::silver },+ { ColorNames::skyblue, &Colors::skyblue },+ { ColorNames::slateblue, &Colors::slateblue },+ { ColorNames::slategray, &Colors::slategray },+ { ColorNames::slategrey, &Colors::slategrey },+ { ColorNames::snow, &Colors::snow },+ { ColorNames::springgreen, &Colors::springgreen },+ { ColorNames::steelblue, &Colors::steelblue },+ { ColorNames::tan, &Colors::tan },+ { ColorNames::teal, &Colors::teal },+ { ColorNames::thistle, &Colors::thistle },+ { ColorNames::tomato, &Colors::tomato },+ { ColorNames::turquoise, &Colors::turquoise },+ { ColorNames::violet, &Colors::violet },+ { ColorNames::wheat, &Colors::wheat },+ { ColorNames::white, &Colors::white },+ { ColorNames::whitesmoke, &Colors::whitesmoke },+ { ColorNames::yellow, &Colors::yellow },+ { ColorNames::yellowgreen, &Colors::yellowgreen },+ { ColorNames::rebeccapurple, &Colors::rebeccapurple },+ { ColorNames::transparent, &Colors::transparent }+ };++ const Color* name_to_color(const char* key)+ {+ auto p = names_to_colors.find(key);+ if (p != names_to_colors.end()) {+ return p->second;+ }+ return 0;+ }++ const Color* name_to_color(const std::string& key)+ {+ return name_to_color(key.c_str());+ }++ const char* color_to_name(const int key)+ {+ auto p = colors_to_names.find(key);+ if (p != colors_to_names.end()) {+ return p->second;+ }+ return 0;+ }++ const char* color_to_name(const double key)+ {+ return color_to_name((int)key);+ }++ const char* color_to_name(const Color& c)+ {+ double key = c.r() * 0x10000+ + c.g() * 0x100+ + c.b();+ return color_to_name(key);+ }++}
+ libsass/src/color_maps.hpp view
@@ -0,0 +1,333 @@+#ifndef SASS_COLOR_MAPS_H+#define SASS_COLOR_MAPS_H++#include <map>+#include "ast.hpp"++namespace Sass {++ struct map_cmp_str+ {+ bool operator()(char const *a, char const *b) const+ {+ return std::strcmp(a, b) < 0;+ }+ };++ namespace ColorNames+ {+ extern const char aliceblue[];+ extern const char antiquewhite[];+ extern const char cyan[];+ extern const char aqua[];+ extern const char aquamarine[];+ extern const char azure[];+ extern const char beige[];+ extern const char bisque[];+ extern const char black[];+ extern const char blanchedalmond[];+ extern const char blue[];+ extern const char blueviolet[];+ extern const char brown[];+ extern const char burlywood[];+ extern const char cadetblue[];+ extern const char chartreuse[];+ extern const char chocolate[];+ extern const char coral[];+ extern const char cornflowerblue[];+ extern const char cornsilk[];+ extern const char crimson[];+ extern const char darkblue[];+ extern const char darkcyan[];+ extern const char darkgoldenrod[];+ extern const char darkgray[];+ extern const char darkgrey[];+ extern const char darkgreen[];+ extern const char darkkhaki[];+ extern const char darkmagenta[];+ extern const char darkolivegreen[];+ extern const char darkorange[];+ extern const char darkorchid[];+ extern const char darkred[];+ extern const char darksalmon[];+ extern const char darkseagreen[];+ extern const char darkslateblue[];+ extern const char darkslategray[];+ extern const char darkslategrey[];+ extern const char darkturquoise[];+ extern const char darkviolet[];+ extern const char deeppink[];+ extern const char deepskyblue[];+ extern const char dimgray[];+ extern const char dimgrey[];+ extern const char dodgerblue[];+ extern const char firebrick[];+ extern const char floralwhite[];+ extern const char forestgreen[];+ extern const char magenta[];+ extern const char fuchsia[];+ extern const char gainsboro[];+ extern const char ghostwhite[];+ extern const char gold[];+ extern const char goldenrod[];+ extern const char gray[];+ extern const char grey[];+ extern const char green[];+ extern const char greenyellow[];+ extern const char honeydew[];+ extern const char hotpink[];+ extern const char indianred[];+ extern const char indigo[];+ extern const char ivory[];+ extern const char khaki[];+ extern const char lavender[];+ extern const char lavenderblush[];+ extern const char lawngreen[];+ extern const char lemonchiffon[];+ extern const char lightblue[];+ extern const char lightcoral[];+ extern const char lightcyan[];+ extern const char lightgoldenrodyellow[];+ extern const char lightgray[];+ extern const char lightgrey[];+ extern const char lightgreen[];+ extern const char lightpink[];+ extern const char lightsalmon[];+ extern const char lightseagreen[];+ extern const char lightskyblue[];+ extern const char lightslategray[];+ extern const char lightslategrey[];+ extern const char lightsteelblue[];+ extern const char lightyellow[];+ extern const char lime[];+ extern const char limegreen[];+ extern const char linen[];+ extern const char maroon[];+ extern const char mediumaquamarine[];+ extern const char mediumblue[];+ extern const char mediumorchid[];+ extern const char mediumpurple[];+ extern const char mediumseagreen[];+ extern const char mediumslateblue[];+ extern const char mediumspringgreen[];+ extern const char mediumturquoise[];+ extern const char mediumvioletred[];+ extern const char midnightblue[];+ extern const char mintcream[];+ extern const char mistyrose[];+ extern const char moccasin[];+ extern const char navajowhite[];+ extern const char navy[];+ extern const char oldlace[];+ extern const char olive[];+ extern const char olivedrab[];+ extern const char orange[];+ extern const char orangered[];+ extern const char orchid[];+ extern const char palegoldenrod[];+ extern const char palegreen[];+ extern const char paleturquoise[];+ extern const char palevioletred[];+ extern const char papayawhip[];+ extern const char peachpuff[];+ extern const char peru[];+ extern const char pink[];+ extern const char plum[];+ extern const char powderblue[];+ extern const char purple[];+ extern const char red[];+ extern const char rosybrown[];+ extern const char royalblue[];+ extern const char saddlebrown[];+ extern const char salmon[];+ extern const char sandybrown[];+ extern const char seagreen[];+ extern const char seashell[];+ extern const char sienna[];+ extern const char silver[];+ extern const char skyblue[];+ extern const char slateblue[];+ extern const char slategray[];+ extern const char slategrey[];+ extern const char snow[];+ extern const char springgreen[];+ extern const char steelblue[];+ extern const char tan[];+ extern const char teal[];+ extern const char thistle[];+ extern const char tomato[];+ extern const char turquoise[];+ extern const char violet[];+ extern const char wheat[];+ extern const char white[];+ extern const char whitesmoke[];+ extern const char yellow[];+ extern const char yellowgreen[];+ extern const char rebeccapurple[];+ extern const char transparent[];+ }++ namespace Colors {+ extern const Color aliceblue;+ extern const Color antiquewhite;+ extern const Color cyan;+ extern const Color aqua;+ extern const Color aquamarine;+ extern const Color azure;+ extern const Color beige;+ extern const Color bisque;+ extern const Color black;+ extern const Color blanchedalmond;+ extern const Color blue;+ extern const Color blueviolet;+ extern const Color brown;+ extern const Color burlywood;+ extern const Color cadetblue;+ extern const Color chartreuse;+ extern const Color chocolate;+ extern const Color coral;+ extern const Color cornflowerblue;+ extern const Color cornsilk;+ extern const Color crimson;+ extern const Color darkblue;+ extern const Color darkcyan;+ extern const Color darkgoldenrod;+ extern const Color darkgray;+ extern const Color darkgrey;+ extern const Color darkgreen;+ extern const Color darkkhaki;+ extern const Color darkmagenta;+ extern const Color darkolivegreen;+ extern const Color darkorange;+ extern const Color darkorchid;+ extern const Color darkred;+ extern const Color darksalmon;+ extern const Color darkseagreen;+ extern const Color darkslateblue;+ extern const Color darkslategray;+ extern const Color darkslategrey;+ extern const Color darkturquoise;+ extern const Color darkviolet;+ extern const Color deeppink;+ extern const Color deepskyblue;+ extern const Color dimgray;+ extern const Color dimgrey;+ extern const Color dodgerblue;+ extern const Color firebrick;+ extern const Color floralwhite;+ extern const Color forestgreen;+ extern const Color magenta;+ extern const Color fuchsia;+ extern const Color gainsboro;+ extern const Color ghostwhite;+ extern const Color gold;+ extern const Color goldenrod;+ extern const Color gray;+ extern const Color grey;+ extern const Color green;+ extern const Color greenyellow;+ extern const Color honeydew;+ extern const Color hotpink;+ extern const Color indianred;+ extern const Color indigo;+ extern const Color ivory;+ extern const Color khaki;+ extern const Color lavender;+ extern const Color lavenderblush;+ extern const Color lawngreen;+ extern const Color lemonchiffon;+ extern const Color lightblue;+ extern const Color lightcoral;+ extern const Color lightcyan;+ extern const Color lightgoldenrodyellow;+ extern const Color lightgray;+ extern const Color lightgrey;+ extern const Color lightgreen;+ extern const Color lightpink;+ extern const Color lightsalmon;+ extern const Color lightseagreen;+ extern const Color lightskyblue;+ extern const Color lightslategray;+ extern const Color lightslategrey;+ extern const Color lightsteelblue;+ extern const Color lightyellow;+ extern const Color lime;+ extern const Color limegreen;+ extern const Color linen;+ extern const Color maroon;+ extern const Color mediumaquamarine;+ extern const Color mediumblue;+ extern const Color mediumorchid;+ extern const Color mediumpurple;+ extern const Color mediumseagreen;+ extern const Color mediumslateblue;+ extern const Color mediumspringgreen;+ extern const Color mediumturquoise;+ extern const Color mediumvioletred;+ extern const Color midnightblue;+ extern const Color mintcream;+ extern const Color mistyrose;+ extern const Color moccasin;+ extern const Color navajowhite;+ extern const Color navy;+ extern const Color oldlace;+ extern const Color olive;+ extern const Color olivedrab;+ extern const Color orange;+ extern const Color orangered;+ extern const Color orchid;+ extern const Color palegoldenrod;+ extern const Color palegreen;+ extern const Color paleturquoise;+ extern const Color palevioletred;+ extern const Color papayawhip;+ extern const Color peachpuff;+ extern const Color peru;+ extern const Color pink;+ extern const Color plum;+ extern const Color powderblue;+ extern const Color purple;+ extern const Color red;+ extern const Color rosybrown;+ extern const Color royalblue;+ extern const Color saddlebrown;+ extern const Color salmon;+ extern const Color sandybrown;+ extern const Color seagreen;+ extern const Color seashell;+ extern const Color sienna;+ extern const Color silver;+ extern const Color skyblue;+ extern const Color slateblue;+ extern const Color slategray;+ extern const Color slategrey;+ extern const Color snow;+ extern const Color springgreen;+ extern const Color steelblue;+ extern const Color tan;+ extern const Color teal;+ extern const Color thistle;+ extern const Color tomato;+ extern const Color turquoise;+ extern const Color violet;+ extern const Color wheat;+ extern const Color white;+ extern const Color whitesmoke;+ extern const Color yellow;+ extern const Color yellowgreen;+ extern const Color rebeccapurple;+ extern const Color transparent;+ }++ extern const std::map<const int, const char*> colors_to_names;+ extern const std::map<const char*, const Color*, map_cmp_str> names_to_colors;++ extern const Color* name_to_color(const char*);+ extern const Color* name_to_color(const std::string&);+ extern const char* color_to_name(const int);+ extern const char* color_to_name(const Color&);+ extern const char* color_to_name(const double);++}++#endif
+ libsass/src/constants.cpp view
@@ -0,0 +1,176 @@+#include "constants.hpp"++namespace Sass {+ namespace Constants {++ extern const unsigned long MaxCallStack = 1024;++ // 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 url_prefix_kwd[] = "url-prefix(";+ 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";+ // 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[] = ":;/?!%&#@|[]{}'`^\"*+-.,_=~";+ extern const char real_uri_chars[] = "#%&";++ // some specific constant character classes+ // they must be static to be useable by lexer+ extern const char static_ops[] = "*/%";+ // some character classes for the parser+ extern const char selector_list_delims[] = "){};!";+ extern const char complex_selector_delims[] = ",){};!";+ extern const char selector_combinator_ops[] = "+~>";+ // optional modifiers for alternative compare context+ extern const char attribute_compare_modifiers[] = "~|^$*";+ extern const char selector_lookahead_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/src/constants.hpp view
@@ -0,0 +1,179 @@+#ifndef SASS_CONSTANTS_H+#define SASS_CONSTANTS_H++namespace Sass {+ namespace Constants {++ // The maximum call stack that can be created+ extern const unsigned long MaxCallStack;++ // 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 url_prefix_kwd[];+ extern const char image_url_kwd[];+ extern const char important_kwd[];+ extern const char pseudo_not_kwd[];+ extern const char even_kwd[];+ 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[];++ // 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[];+ extern const char real_uri_chars[];++ // some specific constant character classes+ // they must be static to be useable by lexer+ extern const char static_ops[];+ extern const char selector_list_delims[];+ extern const char complex_selector_delims[];+ extern const char selector_combinator_ops[];+ extern const char attribute_compare_modifiers[];+ extern const char selector_lookahead_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/src/context.cpp view
@@ -0,0 +1,848 @@+#ifdef _WIN32+#define PATH_SEP ';'+#else+#define PATH_SEP ':'+#endif++#include <string>+#include <cstdlib>+#include <cstring>+#include <iomanip>+#include <sstream>+#include <iostream>++#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 "cssize.hpp"+#include "listize.hpp"+#include "extend.hpp"+#include "remove_placeholders.hpp"+#include "functions.hpp"+#include "backtrace.hpp"+#include "sass2scss.h"+#include "prelexer.hpp"+#include "emitter.hpp"++namespace Sass {+ using namespace Constants;+ using namespace File;+ using namespace Sass;++ 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); }++ static std::string safe_input(const char* in_path)+ {+ // enforce some safe defaults+ // used to create relative file links+ std::string safe_path(in_path ? in_path : "");+ return safe_path == "" ? "stdin" : safe_path;+ }++ static std::string safe_output(const char* out_path, const std::string& input_path = "")+ {+ std::string safe_path(out_path ? out_path : "");+ // maybe we can extract an output path from input path+ if (safe_path == "" && input_path != "") {+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ return (lastindex > -1 ? input_path.substr(0, lastindex) : input_path) + ".css";+ }+ // enforce some safe defaults+ // used to create relative file links+ return safe_path == "" ? "stdout" : safe_path;+ }++ Context::Context(struct Sass_Context* c_ctx)+ : CWD(File::get_cwd()),+ entry_path(""),+ head_imports(0),+ mem(Memory_Manager()),+ plugins(),+ emitter(this),++ strings(),+ resources(),+ sheets(),+ subset_map(),+ import_stack(),++ c_options (c_ctx),++ c_headers (std::vector<Sass_Importer_Entry>()),+ c_importers (std::vector<Sass_Importer_Entry>()),+ c_functions (std::vector<Sass_Function_Entry>()),++ indent (safe_str(c_options->indent, " ")),+ linefeed (safe_str(c_options->linefeed, "\n")),++ input_path (make_canonical_path(safe_input(c_options->input_path))),+ output_path (make_canonical_path(safe_output(c_options->output_path, input_path))),+ source_map_file (make_canonical_path(safe_str(c_options->source_map_file, ""))),+ source_map_root (make_canonical_path(safe_str(c_options->source_map_root, "")))++ {++ // add cwd to include paths+ include_paths.push_back(CWD);++ // collect more paths from different options+ collect_include_paths(sass_option_get_include_path(c_options));+ // collect_include_paths(initializers.include_paths_array());+ collect_plugin_paths(sass_option_get_plugin_path(c_options));+ // collect_plugin_paths(initializers.plugin_paths_array());++ // load plugins and register custom behaviors+ for(auto plug : plugin_paths) plugins.load_plugins(plug);+ for(auto fn : plugins.get_headers()) c_headers.push_back(fn);+ for(auto fn : plugins.get_importers()) c_importers.push_back(fn);+ for(auto fn : plugins.get_functions()) c_functions.push_back(fn);++ // sort the items by priority (lowest first)+ sort (c_headers.begin(), c_headers.end(), sort_importers);+ sort (c_importers.begin(), c_importers.end(), sort_importers);++ emitter.set_filename(abs2rel(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()+ {+ // resources were allocated by strdup or malloc+ for (size_t i = 0; i < resources.size(); ++i) {+ free(resources[i].contents);+ free(resources[i].srcmap);+ }+ // free all strings we kept alive during compiler execution+ for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);+ // everything that gets put into sources will be freed by us+ // this shouldn't have anything in it anyway!?+ for (size_t m = 0; m < import_stack.size(); ++m) {+ sass_import_take_source(import_stack[m]);+ sass_import_take_srcmap(import_stack[m]);+ sass_delete_import(import_stack[m]);+ }+ // clear inner structures (vectors) and input source+ resources.clear(); import_stack.clear();+ }++ Data_Context::~Data_Context()+ {+ // --> this will be freed by resources+ // make sure we free the source even if not processed!+ // if (resources.size() == 0 && source_c_str) free(source_c_str);+ // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);+ // source_c_str = 0; srcmap_c_str = 0;+ }++ File_Context::~File_Context()+ {+ }++ 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) {+ std::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);+ }++ std::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) return;+ 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) {+ std::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);+ }++ std::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) return;+ for (size_t i = 0; paths_array[i]; i++) {+ collect_plugin_paths(paths_array[i]);+ }+ }+++ // resolve the imp_path in base_path or include_paths+ // looks for alternatives and returns a list from one directory+ std::vector<Include> Context::find_includes(const Importer& import)+ {+ // make sure we resolve against an absolute path+ std::string base_path(rel2abs(import.base_path));+ // first try to resolve the load path relative to the base path+ std::vector<Include> vec(resolve_includes(base_path, import.imp_path));+ // then search in every include path (but only if nothing found yet)+ for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)+ {+ // call resolve_includes and individual base path and append all results+ std::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));+ if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());+ }+ // return vector+ return vec;+ }+++ // register include with resolved path and its content+ // memory of the resources will be freed by us on exit+ void Context::register_resource(const Include& inc, const Resource& res)+ {++ // do not parse same resource twice+ // maybe raise an error in this case+ // if (sheets.count(inc.abs_path)) {+ // free(res.contents); free(res.srcmap);+ // throw std::runtime_error("duplicate resource registered");+ // return;+ // }++ // get index for this resource+ size_t idx = resources.size();++ // tell emitter about new resource+ emitter.add_source_index(idx);++ // put resources under our control+ // the memory will be freed later+ resources.push_back(res);++ // add a relative link to the working directory+ included_files.push_back(inc.abs_path);+ // add a relative link to the source map output file+ srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));++ // get pointer to the loaded content+ Sass_Import_Entry import = sass_make_import(+ inc.imp_path.c_str(),+ inc.abs_path.c_str(),+ res.contents,+ res.srcmap+ );+ // add the entry to the stack+ import_stack.push_back(import);++ // get pointer to the loaded content+ const char* contents = resources[idx].contents;+ // keep a copy of the path around (for parserstates)+ // ToDo: we clean it, but still not very elegant!?+ strings.push_back(sass_strdup(inc.abs_path.c_str()));+ // create the initial parser state from resource+ ParserState pstate(strings.back(), contents, idx);+ // create a parser instance from the given c_str buffer+ Parser p(Parser::from_c_str(contents, *this, pstate));+ // do not yet dispose these buffers+ sass_import_take_source(import);+ sass_import_take_srcmap(import);+ // then parse the root block+ Block* root = p.parse();+ // delete memory of current stack frame+ sass_delete_import(import_stack.back());+ // remove current stack frame+ import_stack.pop_back();+ // create key/value pair for ast node+ std::pair<const std::string, const StyleSheet>+ ast_pair(inc.abs_path, { res, root });+ // register resulting resource+ sheets.insert(ast_pair);++ }++ // Add a new import to the context (called from `import_url`)+ Include Context::load_import(const Importer& imp, ParserState pstate)+ {++ // search for valid imports (ie. partials) on the filesystem+ // this may return more than one valid result (ambiguous imp_path)+ const std::vector<Include> resolved(find_includes(imp));++ // error nicely on ambiguous imp_path+ if (resolved.size() > 1) {+ std::stringstream msg_stream;+ msg_stream << "It's not clear which file to import for ";+ msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";+ msg_stream << "Candidates:" << "\n";+ for (size_t i = 0, L = resolved.size(); i < L; ++i)+ { msg_stream << " " << resolved[i].imp_path << "\n"; }+ msg_stream << "Please delete or rename all but one of these files." << "\n";+ error(msg_stream.str(), pstate);+ }++ // process the resolved entry+ else if (resolved.size() == 1) {+ // use cache for the resource loading+ if (sheets.count(resolved[0].abs_path)) return resolved[0];+ // try to read the content of the resolved file entry+ // the memory buffer returned must be freed by us!+ if (char* contents = read_file(resolved[0].abs_path)) {+ // register the newly resolved file resource+ register_resource(resolved[0], { contents, 0 });+ // return resolved entry+ return resolved[0];+ }+ }++ // nothing found+ return { imp, "" };++ }++ void Context::import_url (Import* imp, std::string load_path, const std::string& ctx_path) {++ ParserState pstate(imp->pstate());+ std::string imp_path(unquote(load_path));+ std::string protocol("file");++ using namespace Prelexer;+ if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {++ protocol = std::string(imp_path.c_str(), proto - 3);+ // std::cerr << "==================== " << protocol << "\n";+ if (protocol.compare("file") && true) {++ }+ }++ // add urls (protocol other than file) and urls without procotol to `urls` member+ // ToDo: if ctx_path is already a file resource, we should not add it here?+ if (imp->media_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {+ imp->urls().push_back(SASS_MEMORY_NEW(mem, String_Quoted, imp->pstate(), load_path));+ }+ else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {+ String_Constant* loc = SASS_MEMORY_NEW(mem, String_Constant, pstate, unquote(load_path));+ Argument* loc_arg = SASS_MEMORY_NEW(mem, Argument, pstate, loc);+ Arguments* loc_args = SASS_MEMORY_NEW(mem, Arguments, pstate);+ (*loc_args) << loc_arg;+ Function_Call* new_url = SASS_MEMORY_NEW(mem, Function_Call, pstate, "url", loc_args);+ imp->urls().push_back(new_url);+ }+ else {+ const Importer importer(imp_path, ctx_path);+ Include include(load_import(importer, pstate));+ if (include.abs_path.empty()) {+ error("File to import not found or unreadable: " + imp_path + "\nParent style sheet: " + ctx_path, pstate);+ }+ imp->incs().push_back(include);+ }++ }+++ // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet+ bool Context::call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one)+ {+ // unique counter+ size_t count = 0;+ // need one correct import+ bool has_import = false;+ // process all custom importers (or custom headers)+ for (Sass_Importer_Entry& importer : importers) {+ // int priority = sass_importer_get_priority(importer);+ Sass_Importer_Fn fn = sass_importer_get_function(importer);+ // skip importer if it returns NULL+ if (Sass_Import_List includes =+ fn(load_path.c_str(), importer, c_compiler)+ ) {+ // get c pointer copy to iterate over+ Sass_Import_List it_includes = includes;+ while (*it_includes) { ++count;+ // create unique path to use as key+ std::string uniq_path = load_path;+ if (!only_one && count) {+ std::stringstream path_strm;+ path_strm << uniq_path << ":" << count;+ uniq_path = path_strm.str();+ }+ // create the importer struct+ Importer importer(uniq_path, ctx_path);+ // query data from the current include+ Sass_Import_Entry include = *it_includes;+ char* source = sass_import_take_source(include);+ char* srcmap = sass_import_take_source(include);+ size_t line = sass_import_get_error_line(include);+ size_t column = sass_import_get_error_column(include);+ const char *abs_path = sass_import_get_abs_path(include);+ // handle error message passed back from custom importer+ // it may (or may not) override the line and column info+ if (const char* err_message = sass_import_get_error_message(include)) {+ if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap });+ if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);+ else error(err_message, ParserState(ctx_path, source, Position(line, column)));+ }+ // content for import was set+ else if (source) {+ // resolved abs_path should be set by custom importer+ // use the created uniq_path as fallback (maybe enforce)+ std::string path_key(abs_path ? abs_path : uniq_path);+ // create the importer struct+ Include include(importer, path_key);+ // attach information to AST node+ imp->incs().push_back(include);+ // register the resource buffers+ register_resource(include, { source, srcmap });+ }+ // only a path was retuned+ // try to load it like normal+ else if(abs_path) {+ // checks some urls to preserve+ // `http://`, `https://` and `//`+ // or dispatchs to `import_file`+ // which will check for a `.css` extension+ // or resolves the file on the filesystem+ // added and resolved via `add_file`+ // finally stores everything on `imp`+ import_url(imp, abs_path, ctx_path);+ }+ // move to next+ ++it_includes;+ }+ // deallocate the returned memory+ sass_delete_import_list(includes);+ // set success flag+ has_import = true;+ // break out of loop+ if (only_one) break;+ }+ }+ // return result+ return has_import;+ }++ 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&, std::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::render(Block* root)+ {+ // check for valid block+ if (!root) return 0;+ // start the render process+ root->perform(&emitter);+ // finish emitter stream+ emitter.finalize();+ // get the resulting buffer from stream+ OutputBuffer emitted = emitter.get_buffer();+ // should we append a source map url?+ if (!c_options->omit_source_map_url) {+ // generate an embeded source map+ if (c_options->source_map_embed) {+ emitted.buffer += linefeed;+ emitted.buffer += format_embedded_source_map();+ }+ // or just link the generated one+ else if (source_map_file != "") {+ emitted.buffer += linefeed;+ emitted.buffer += format_source_mapping_url(source_map_file);+ }+ }+ // create a copy of the resulting buffer string+ // this must be freed or taken over by implementor+ return sass_strdup(emitted.buffer.c_str());+ }++ void Context::apply_custom_headers(Block* root, const char* ctx_path, ParserState pstate)+ {+ // create a custom import to resolve headers+ Import* imp = SASS_MEMORY_NEW(mem, Import, pstate);+ // dispatch headers which will add custom functions+ // custom headers are added to the import instance+ call_headers(entry_path, ctx_path, pstate, imp);+ // increase head count to skip later+ head_imports += resources.size() - 1;+ // add the statement if we have urls+ if (!imp->urls().empty()) (*root) << imp;+ // process all other resources (add Import_Stub nodes)+ for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {+ (*root) << SASS_MEMORY_NEW(mem, Import_Stub, pstate, imp->incs()[i]);+ }+ }++ Block* File_Context::parse()+ {++ // check if entry file is given+ if (input_path.empty()) return 0;++ // create absolute path from input filename+ // ToDo: this should be resolved via custom importers+ std::string abs_path(rel2abs(input_path, CWD));++ // try to load the entry file+ char* contents = read_file(abs_path);++ // alternatively also look inside each include path folder+ // I think this differs from ruby sass (IMO too late to remove)+ for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {+ // build absolute path for this include path entry+ abs_path = rel2abs(input_path, include_paths[i]);+ // try to load the resulting path+ contents = read_file(abs_path);+ }++ // abort early if no content could be loaded (various reasons)+ if (!contents) throw "File to read not found or unreadable: " + input_path;++ // store entry path+ entry_path = abs_path;++ // create entry only for import stack+ Sass_Import_Entry import = sass_make_import(+ input_path.c_str(),+ entry_path.c_str(),+ contents,+ 0+ );+ // add the entry to the stack+ import_stack.push_back(import);++ // create the source entry for file entry+ register_resource({{ input_path, "." }, abs_path }, { contents, 0 });++ // create root ast tree node+ return compile();++ }++ Block* Data_Context::parse()+ {++ // check if source string is given+ if (!source_c_str) return 0;++ // convert indented sass syntax+ if(c_options->is_indented_syntax_src) {+ // call sass2scss to convert the string+ char * converted = sass2scss(source_c_str,+ // preserve the structure as much as possible+ SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);+ // replace old source_c_str with converted+ free(source_c_str); source_c_str = converted;+ }++ // remember entry path (defaults to stdin for string)+ entry_path = input_path.empty() ? "stdin" : input_path;++ // ToDo: this may be resolved via custom importers+ std::string abs_path(rel2abs(entry_path));+ char* abs_path_c_str = sass_strdup(abs_path.c_str());+ strings.push_back(abs_path_c_str);++ // create entry only for the import stack+ Sass_Import_Entry import = sass_make_import(+ entry_path.c_str(),+ abs_path_c_str,+ source_c_str,+ srcmap_c_str+ );+ // add the entry to the stack+ import_stack.push_back(import);++ // register a synthetic resource (path does not really exist, skip in includes)+ register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });++ // create root ast tree node+ return compile();+ }++++ // parse root block from includes+ Block* Context::compile()+ {+ // abort if there is no data+ if (resources.size() == 0) return 0;+ // get root block from the first style sheet+ Block* root = sheets.at(entry_path).root;+ // abort on invalid root+ if (root == 0) return 0;++ Env global; // create root environment+ // register built-in functions on env+ register_built_in_functions(*this, &global);+ // register custom functions (defined via C-API)+ for (size_t i = 0, S = c_functions.size(); i < S; ++i)+ { register_c_function(*this, &global, c_functions[i]); }+ // create initial backtrace entry+ Backtrace backtrace(0, ParserState("", 0), "");+ // create crtp visitor objects+ Expand expand(*this, &global, &backtrace);+ Cssize cssize(*this, &backtrace);+ // expand and eval the tree+ root = root->perform(&expand)->block();+ // merge and bubble certain rules+ root = root->perform(&cssize)->block();+ // should we extend something?+ if (!subset_map.empty()) {+ // create crtp visitor object+ Extend extend(*this, subset_map);+ // extend tree nodes+ root->perform(&extend);+ }++ // clean up by removing empty placeholders+ // ToDo: maybe we can do this somewhere else?+ Remove_Placeholders remove_placeholders(*this);+ root->perform(&remove_placeholders);+ // return processed tree+ return root;+ }+ // EO compile++ std::string Context::format_embedded_source_map()+ {+ std::string map = emitter.render_srcmap(*this);+ std::istringstream is( map );+ std::ostringstream buffer;+ base64::encoder E;+ E.encode(is, buffer);+ std::string url = "data:application/json;base64," + buffer.str();+ url.erase(url.size() - 1);+ return "/*# sourceMappingURL=" + url + " */";+ }++ std::string Context::format_source_mapping_url(const std::string& file)+ {+ std::string url = abs2rel(file, output_path, CWD);+ return "/*# sourceMappingURL=" + url + " */";+ }++ char* Context::render_srcmap()+ {+ if (source_map_file == "") return 0;+ char* result = 0;+ std::string map = emitter.render_srcmap(*this);+ result = sass_strdup(map.c_str());+ return result;+ }+++ // for data context we want to start after "stdin"+ // we probably always want to skip the header includes?+ std::vector<std::string> Context::get_included_files(bool skip, size_t headers)+ {+ // create a copy of the vector for manipulations+ std::vector<std::string> includes = included_files;+ if (includes.size() == 0) return includes;+ if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }+ else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); }+ includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );+ std::sort( includes.begin() + (skip ? 0 : 1), includes.end() );+ return includes;+ }++ 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);+ std::stringstream ss;+ ss << def->name() << "[f]" << arity;+ def->environment(env);+ (*env)[ss.str()] = def;+ }++ void register_overload_stub(Context& ctx, std::string name, Env* env)+ {+ Definition* stub = SASS_MEMORY_NEW(ctx.mem, Definition,+ ParserState("[built-in function]"),+ 0,+ name,+ 0,+ 0,+ true);+ (*env)[name + "[f]"] = stub;+ }+++ void register_built_in_functions(Context& ctx, Env* env)+ {+ using namespace Functions;+ // RGB Functions+ register_function(ctx, rgb_sig, rgb, env);+ register_overload_stub(ctx, "rgba", env);+ register_function(ctx, rgba_4_sig, rgba_4, 4, env);+ register_function(ctx, rgba_2_sig, rgba_2, 2, env);+ register_function(ctx, red_sig, red, env);+ register_function(ctx, green_sig, green, env);+ register_function(ctx, blue_sig, blue, env);+ register_function(ctx, mix_sig, mix, env);+ // HSL Functions+ register_function(ctx, hsl_sig, hsl, env);+ register_function(ctx, hsla_sig, hsla, env);+ register_function(ctx, hue_sig, hue, env);+ register_function(ctx, saturation_sig, saturation, env);+ register_function(ctx, lightness_sig, lightness, env);+ register_function(ctx, adjust_hue_sig, adjust_hue, env);+ register_function(ctx, lighten_sig, lighten, env);+ register_function(ctx, darken_sig, darken, env);+ register_function(ctx, saturate_sig, saturate, env);+ register_function(ctx, desaturate_sig, desaturate, env);+ register_function(ctx, grayscale_sig, grayscale, env);+ register_function(ctx, complement_sig, complement, env);+ register_function(ctx, invert_sig, invert, env);+ // Opacity Functions+ register_function(ctx, alpha_sig, alpha, env);+ register_function(ctx, opacity_sig, alpha, env);+ register_function(ctx, opacify_sig, opacify, env);+ register_function(ctx, fade_in_sig, opacify, env);+ register_function(ctx, transparentize_sig, transparentize, env);+ register_function(ctx, fade_out_sig, transparentize, env);+ // Other Color Functions+ register_function(ctx, adjust_color_sig, adjust_color, env);+ register_function(ctx, scale_color_sig, scale_color, env);+ register_function(ctx, change_color_sig, change_color, env);+ register_function(ctx, ie_hex_str_sig, ie_hex_str, env);+ // String Functions+ register_function(ctx, unquote_sig, sass_unquote, env);+ register_function(ctx, quote_sig, sass_quote, env);+ register_function(ctx, str_length_sig, str_length, env);+ register_function(ctx, str_insert_sig, str_insert, env);+ register_function(ctx, str_index_sig, str_index, env);+ register_function(ctx, str_slice_sig, str_slice, env);+ register_function(ctx, to_upper_case_sig, to_upper_case, env);+ register_function(ctx, to_lower_case_sig, to_lower_case, env);+ // Number Functions+ register_function(ctx, percentage_sig, percentage, env);+ register_function(ctx, round_sig, round, env);+ register_function(ctx, ceil_sig, ceil, env);+ register_function(ctx, floor_sig, floor, env);+ register_function(ctx, abs_sig, abs, env);+ register_function(ctx, min_sig, min, env);+ register_function(ctx, max_sig, max, env);+ 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, 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);+ // Selector functions+ register_function(ctx, selector_nest_sig, selector_nest, env);+ register_function(ctx, selector_append_sig, selector_append, env);+ register_function(ctx, selector_extend_sig, selector_extend, env);+ register_function(ctx, selector_replace_sig, selector_replace, env);+ register_function(ctx, selector_unify_sig, selector_unify, env);+ register_function(ctx, is_superselector_sig, is_superselector, env);+ register_function(ctx, simple_selectors_sig, simple_selectors, env);+ register_function(ctx, selector_parse_sig, selector_parse, 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/src/context.hpp view
@@ -0,0 +1,147 @@+#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 "ast_fwd_decl.hpp"+#include "sass_context.hpp"+#include "environment.hpp"+#include "source_map.hpp"+#include "subset_map.hpp"+#include "output.hpp"+#include "plugins.hpp"+#include "file.hpp"+++struct Sass_Function;++namespace Sass {++ class Context {+ public:+ void import_url (Import* imp, std::string load_path, const std::string& ctx_path);+ bool call_headers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)+ { return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };+ bool call_importers(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp)+ { return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); };++ private:+ bool call_loader(const std::string& load_path, const char* ctx_path, ParserState& pstate, Import* imp, std::vector<Sass_Importer_Entry> importers, bool only_one = true);++ public:+ const std::string CWD;+ std::string entry_path;+ size_t head_imports;+ Memory_Manager mem;+ Plugins plugins;+ Output emitter;++ // resources add under our control+ // these are guaranteed to be freed+ std::vector<char*> strings;+ std::vector<Resource> resources;+ std::map<const std::string, const StyleSheet> sheets;+ Subset_Map<std::string, std::pair<Complex_Selector*, Compound_Selector*> > subset_map;+ std::vector<Sass_Import_Entry> import_stack;++ struct Sass_Compiler* c_compiler;+ struct Sass_Options* c_options;++ // absolute paths to includes+ std::vector<std::string> included_files;+ // relative includes for sourcemap+ std::vector<std::string> srcmap_links;+ // vectors above have same size++ std::vector<std::string> plugin_paths; // relative paths to load plugins+ std::vector<std::string> include_paths; // lookup paths for includes++++++ void apply_custom_headers(Block* root, const char* path, ParserState pstate);++ std::vector<Sass_Importer_Entry> c_headers;+ std::vector<Sass_Importer_Entry> c_importers;+ std::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);++ const std::string indent; // String to be used for indentation+ const std::string linefeed; // String to be used for line feeds+ const std::string input_path; // for relative paths in src-map+ const std::string output_path; // for relative paths to the output+ const std::string source_map_file; // path to source map file (enables feature)+ const std::string source_map_root; // path for sourceRoot property (pass-through)++ virtual ~Context();+ Context(struct Sass_Context*);+ virtual Block* parse() = 0;+ virtual Block* compile();+ virtual char* render(Block* root);+ virtual char* render_srcmap();++ void register_resource(const Include&, const Resource&);+ std::vector<Include> find_includes(const Importer& import);+ Include load_import(const Importer&, ParserState pstate);++ Sass_Output_Style output_style() { return c_options->output_style; };+ std::vector<std::string> get_included_files(bool skip = false, size_t headers = 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);+ std::string format_embedded_source_map();+ std::string format_source_mapping_url(const std::string& out_path);+++ // 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(std::string name, Env* env);++ public:+ const std::string& cwd() { return CWD; };+ };++ class File_Context : public Context {+ public:+ File_Context(struct Sass_File_Context* ctx)+ : Context(ctx)+ { }+ virtual ~File_Context();+ virtual Block* parse();+ };++ class Data_Context : public Context {+ public:+ char* source_c_str;+ char* srcmap_c_str;+ Data_Context(struct Sass_Data_Context* ctx)+ : Context(ctx)+ {+ source_c_str = ctx->source_string;+ srcmap_c_str = ctx->srcmap_string;+ ctx->source_string = 0; // passed away+ ctx->srcmap_string = 0; // passed away+ }+ virtual ~Data_Context();+ virtual Block* parse();+ };++}++#endif
+ libsass/src/cssize.cpp view
@@ -0,0 +1,565 @@+#include <iostream>+#include <typeinfo>+#include <vector>++#include "cssize.hpp"+#include "to_string.hpp"+#include "context.hpp"+#include "backtrace.hpp"++namespace Sass {++ Cssize::Cssize(Context& ctx, Backtrace* bt)+ : ctx(ctx),+ block_stack(std::vector<Block*>()),+ p_stack(std::vector<Statement*>()),+ backtrace(bt)+ { }++ Statement* Cssize::parent()+ {+ return p_stack.size() ? p_stack.back() : block_stack.front();+ }++ Statement* Cssize::operator()(Block* b)+ {+ Block* bb = SASS_MEMORY_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();+ 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()) ? SASS_MEMORY_NEW(ctx.mem, Bubble, r->pstate(), r) : bubble(r);+ }++ p_stack.push_back(r);+ At_Rule* rr = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, rr->pstate());+ if (!(directive_exists || rr->is_keyframes()))+ {+ At_Rule* empty_node = static_cast<At_Rule*>(rr);+ empty_node->block(SASS_MEMORY_NEW(ctx.mem, Block, rr->block() ? rr->block()->pstate() : rr->pstate()));+ *result << empty_node;+ }++ Statement* ss = debubble(rr->block() ? rr->block() : SASS_MEMORY_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 = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ r->pstate(),+ r->selector(),+ r->block()->perform(this)->block());+ // rr->tabs(r->block()->tabs());+ p_stack.pop_back();++ Block* props = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());+ Block* rules = SASS_MEMORY_NEW(ctx.mem, Block, rr->block()->pstate());+ for (size_t i = 0, L = rr->block()->length(); i < L; i++)+ {+ Statement* s = (*rr->block())[i];+ if (bubblable(s)) *rules << s;+ if (!bubblable(s)) *props << s;+ }++ if (props->length())+ {+ Block* bb = SASS_MEMORY_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()(Null* m)+ {+ return 0;+ }++ Statement* Cssize::operator()(Media_Block* m)+ {+ if (parent()->statement_type() == Statement::RULESET)+ { return bubble(m); }++ if (parent()->statement_type() == Statement::MEDIA)+ { return SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m); }++ p_stack.push_back(m);++ Media_Block* mm = SASS_MEMORY_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()(Supports_Block* m)+ {+ if (!m->block()->length())+ { return m; }++ if (parent()->statement_type() == Statement::RULESET)+ { return bubble(m); }++ p_stack.push_back(m);++ Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ m->pstate(),+ m->condition(),+ 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 SASS_MEMORY_NEW(ctx.mem, Bubble, m->pstate(), m);+ }++ return bubble(m);+ }++ Statement* Cssize::bubble(At_Rule* m)+ {+ Block* bb = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, m->block() ? m->block()->pstate() : m->pstate());+ *wrapper_block << new_rule;+ At_Rule* mm = SASS_MEMORY_NEW(ctx.mem, At_Rule,+ m->pstate(),+ m->keyword(),+ m->selector(),+ wrapper_block);+ if (m->value()) mm->value(m->value());++ Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+ return bubble;+ }++ Statement* Cssize::bubble(At_Root_Block* m)+ {+ Block* bb = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());+ *wrapper_block << new_rule;+ At_Root_Block* mm = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+ m->pstate(),+ wrapper_block,+ m->expression());+ Bubble* bubble = SASS_MEMORY_NEW(ctx.mem, Bubble, mm->pstate(), mm);+ return bubble;+ }++ Statement* Cssize::bubble(Supports_Block* m)+ {+ Ruleset* parent = static_cast<Ruleset*>(shallow_copy(this->parent()));++ Block* bb = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());+ Ruleset* new_rule = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());+ *wrapper_block << new_rule;+ Supports_Block* mm = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ m->pstate(),+ m->condition(),+ wrapper_block);++ mm->tabs(m->tabs());++ Bubble* bubble = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, parent->block()->pstate());+ Ruleset* new_rule = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Block, m->block()->pstate());+ *wrapper_block << new_rule;+ Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+ m->pstate(),+ m->media_queries(),+ wrapper_block,+ 0);++ mm->tabs(m->tabs());++ Bubble* bubble = SASS_MEMORY_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 = SASS_MEMORY_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;+ }++ std::vector<std::pair<bool, Block*>> Cssize::slice_by_bubble(Statement* b)+ {+ std::vector<std::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 = SASS_MEMORY_NEW(ctx.mem, Block, value->pstate());+ *wrapper_block << value;+ results.push_back(std::make_pair(key, wrapper_block));+ }+ }+ return results;+ }++ Statement* Cssize::shallow_copy(Statement* s)+ {+ switch (s->statement_type())+ {+ case Statement::RULESET:+ return SASS_MEMORY_NEW(ctx.mem, Ruleset, *static_cast<Ruleset*>(s));+ case Statement::MEDIA:+ return SASS_MEMORY_NEW(ctx.mem, Media_Block, *static_cast<Media_Block*>(s));+ case Statement::BUBBLE:+ return SASS_MEMORY_NEW(ctx.mem, Bubble, *static_cast<Bubble*>(s));+ case Statement::DIRECTIVE:+ return SASS_MEMORY_NEW(ctx.mem, At_Rule, *static_cast<At_Rule*>(s));+ case Statement::SUPPORTS:+ return SASS_MEMORY_NEW(ctx.mem, Supports_Block, *static_cast<Supports_Block*>(s));+ case Statement::ATROOT:+ return SASS_MEMORY_NEW(ctx.mem, At_Root_Block, *static_cast<At_Root_Block*>(s));+ case Statement::KEYFRAMERULE:+ return SASS_MEMORY_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_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), std::string("`CSSize` can't clone ") + typeid(*s).name());+ return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);+ }+ }++ Statement* Cssize::debubble(Block* children, Statement* parent)+ {+ Has_Block* previous_parent = 0;+ std::vector<std::pair<bool, Block*>> baz = slice_by_bubble(children);+ Block* result = SASS_MEMORY_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 = SASS_MEMORY_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));+ if (!mq->length()) continue;+ 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 = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List,+ m1->media_queries()->pstate(),+ m1->media_queries()->length(),+ SASS_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);++ std::string type;+ std::string mod;++ std::string m1 = std::string(mq1->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");+ std::string t1 = mq1->media_type() ? mq1->media_type()->perform(&to_string) : "";+ std::string m2 = std::string(mq2->is_restricted() ? "only" : mq1->is_negated() ? "not" : "");+ std::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 = SASS_MEMORY_NEW(ctx.mem, Media_Query,++mq1->pstate(), 0,+mq1->length() + mq2->length(), mod == "not", mod == "only"+);++ if (!type.empty()) {+ mm->media_type(SASS_MEMORY_NEW(ctx.mem, String_Quoted, mq1->pstate(), type));+ }++ *mm += mq2;+ *mm += mq1;+ return mm;+ }+}
+ libsass/src/cssize.hpp view
@@ -0,0 +1,78 @@+#ifndef SASS_CSSIZE_H+#define SASS_CSSIZE_H++#include "ast.hpp"+#include "context.hpp"+#include "operation.hpp"+#include "environment.hpp"++namespace Sass {++ typedef Environment<AST_Node*> Env;+ struct Backtrace;++ class Cssize : public Operation_CRTP<Statement*, Cssize> {++ Context& ctx;+ std::vector<Block*> block_stack;+ std::vector<Statement*> p_stack;+ Backtrace* backtrace;++ Statement* fallback_impl(AST_Node* n);++ public:+ Cssize(Context&, 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()(Supports_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* operator()(Null*);++ Statement* parent();+ std::vector<std::pair<bool, Block*>> slice_by_bubble(Statement*);+ Statement* bubble(At_Rule*);+ Statement* bubble(At_Root_Block*);+ Statement* bubble(Media_Block*);+ Statement* bubble(Supports_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/src/debug.hpp view
@@ -0,0 +1,43 @@+#ifndef SASS_DEBUG_H+#define SASS_DEBUG_H++#include <stdint.h>++#ifndef UINT32_MAX+ #define UINT32_MAX 0xffffffffU+#endif++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/src/debugger.hpp view
@@ -0,0 +1,686 @@+#ifndef SASS_DEBUGGER_H+#define SASS_DEBUGGER_H++#include <string>+#include <sstream>+#include "node.hpp"+#include "ast_fwd_decl.hpp"++using namespace Sass;++inline void debug_ast(AST_Node* node, std::string ind = "", Env* env = 0);++inline void debug_sources_set(SourcesSet& set, std::string ind = "")+{+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+ for(auto const &pair : set) {+ debug_ast(pair, ind + "");+ // debug_ast(set[pair], ind + "first: ");+ }+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+}++inline std::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 std::string prettyprint(const std::string& str) {+ std::string clean = str_replace(str, "\n", "\\n");+ clean = str_replace(clean, " ", "\\t");+ clean = str_replace(clean, "\r", "\\r");+ return clean;+}++inline std::string longToHex(long long t) {+ std::stringstream is;+ is << std::hex << t;+ return is.str();+}++inline std::string pstate_source_position(AST_Node* node)+{+ std::stringstream str;+ Position start(node->pstate());+ Position end(start + node->pstate().offset);+ str << (start.file == std::string::npos ? -1 : start.file)+ << "@[" << start.line << ":" << start.column << "]"+ << "-[" << end.line << ":" << end.column << "]";+ return str.str();+}++inline void debug_ast(AST_Node* node, std::string ind, Env* env)+{+ if (node == 0) return;+ if (ind == "") std::cerr << "####################################################################\n";+ if (dynamic_cast<Bubble*>(node)) {+ Bubble* bubble = dynamic_cast<Bubble*>(node);+ std::cerr << ind << "Bubble " << bubble;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << bubble->tabs();+ std::cerr << std::endl;+ } else if (dynamic_cast<At_Root_Block*>(node)) {+ At_Root_Block* root_block = dynamic_cast<At_Root_Block*>(node);+ std::cerr << ind << "At_Root_Block " << root_block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << root_block->tabs();+ std::cerr << std::endl;+ debug_ast(root_block->expression(), ind + ":", env);+ debug_ast(root_block->block(), ind + " ", env);+ } else if (dynamic_cast<Selector_List*>(node)) {+ Selector_List* selector = dynamic_cast<Selector_List*>(node);+ std::cerr << ind << "Selector_List " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [@media:" << selector->media_block() << "]";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << std::endl;++ for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }++// } else if (dynamic_cast<Expression*>(node)) {+// Expression* expression = dynamic_cast<Expression*>(node);+// std::cerr << ind << "Expression " << expression << " " << expression->concrete_type() << std::endl;++ } else if (dynamic_cast<Parent_Selector*>(node)) {+ Parent_Selector* selector = dynamic_cast<Parent_Selector*>(node);+ std::cerr << ind << "Parent_Selector " << selector;+// if (selector->not_selector()) cerr << " [in_declaration]";+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+// debug_ast(selector->selector(), ind + "->", env);++ } else if (dynamic_cast<Complex_Selector*>(node)) {+ Complex_Selector* selector = dynamic_cast<Complex_Selector*>(node);+ std::cerr << ind << "Complex_Selector " << selector+ << " (" << pstate_source_position(node) << ")"+ << " [weight:" << longToHex(selector->specificity()) << "]"+ << " [@media:" << selector->media_block() << "]"+ << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << (selector->has_line_break() ? " [line-break]": " -")+ << " -- ";+ std::string del;+ switch (selector->combinator()) {+ case Complex_Selector::PARENT_OF: del = ">"; break;+ case Complex_Selector::PRECEDES: del = "~"; break;+ case Complex_Selector::ADJACENT_TO: del = "+"; break;+ case Complex_Selector::ANCESTOR_OF: del = " "; break;+ case Complex_Selector::REFERENCE: del = "//"; break;+ }+ // if (del = "/") del += selector->reference()->perform(&to_string) + "/";+ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+ debug_ast(selector->head(), ind + " " /* + "[" + del + "]" */, env);+ if (selector->tail()) {+ debug_ast(selector->tail(), ind + "{" + del + "}", env);+ } else if(del != " ") {+ std::cerr << ind << " |" << del << "| {trailing op}" << std::endl;+ }+ SourcesSet set = selector->sources();+ // debug_sources_set(set, ind + " @--> ");+ } else if (dynamic_cast<Compound_Selector*>(node)) {+ Compound_Selector* selector = dynamic_cast<Compound_Selector*>(node);+ std::cerr << ind << "Compound_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [weight:" << longToHex(selector->specificity()) << "]";+ std::cerr << " [@media:" << selector->media_block() << "]";+ std::cerr << (selector->is_optional() ? " [is_optional]": " -");+ std::cerr << (selector->has_line_break() ? " [line-break]": " -");+ std::cerr << (selector->has_line_feed() ? " [line-feed]": " -");+ std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+ for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Propset*>(node)) {+ Propset* selector = dynamic_cast<Propset*>(node);+ std::cerr << ind << "Propset " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << selector->tabs() << std::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);+ std::cerr << ind << "Wrapped_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;+ debug_ast(selector->selector(), ind + " () ", env);+ } else if (dynamic_cast<Pseudo_Selector*>(node)) {+ Pseudo_Selector* selector = dynamic_cast<Pseudo_Selector*>(node);+ std::cerr << ind << "Pseudo_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;+ debug_ast(selector->expression(), ind + " <= ", env);+ } else if (dynamic_cast<Attribute_Selector*>(node)) {+ Attribute_Selector* selector = dynamic_cast<Attribute_Selector*>(node);+ std::cerr << ind << "Attribute_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;+ debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);+ } else if (dynamic_cast<Selector_Qualifier*>(node)) {+ Selector_Qualifier* selector = dynamic_cast<Selector_Qualifier*>(node);+ std::cerr << ind << "Selector_Qualifier " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;+ } else if (dynamic_cast<Type_Selector*>(node)) {+ Type_Selector* selector = dynamic_cast<Type_Selector*>(node);+ std::cerr << ind << "Type_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << selector->ns_name() << ">>" << (selector->has_line_break() ? " [line-break]": " -") <<+ " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl;+ } else if (dynamic_cast<Selector_Placeholder*>(node)) {++ Selector_Placeholder* selector = dynamic_cast<Selector_Placeholder*>(node);+ std::cerr << ind << "Selector_Placeholder [" << selector->ns_name() << "] " << selector+ << " [@media:" << selector->media_block() << "]"+ << (selector->is_optional() ? " [is_optional]": " -")+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << std::endl;++ } else if (dynamic_cast<Simple_Selector*>(node)) {+ Simple_Selector* selector = dynamic_cast<Simple_Selector*>(node);+ std::cerr << ind << "Simple_Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << (selector->has_line_break() ? " [line-break]": " -") << (selector->has_line_feed() ? " [line-feed]": " -") << std::endl;++ } else if (dynamic_cast<Selector_Schema*>(node)) {+ Selector_Schema* selector = dynamic_cast<Selector_Schema*>(node);+ std::cerr << ind << "Selector_Schema " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << (selector->at_root() && selector->at_root() ? " [@ROOT]" : "")+ << " [@media:" << selector->media_block() << "]"+ << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << std::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);+ std::cerr << ind << "Selector " << selector;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << (selector->has_line_break() ? " [line-break]": " -")+ << (selector->has_line_feed() ? " [line-feed]": " -")+ << std::endl;++ } else if (dynamic_cast<Media_Query_Expression*>(node)) {+ Media_Query_Expression* block = dynamic_cast<Media_Query_Expression*>(node);+ std::cerr << ind << "Media_Query_Expression " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << (block->is_interpolated() ? " [is_interpolated]": " -")+ << std::endl;+ debug_ast(block->feature(), ind + " feature) ");+ debug_ast(block->value(), ind + " value) ");++ } else if (dynamic_cast<Media_Query*>(node)) {+ Media_Query* block = dynamic_cast<Media_Query*>(node);+ std::cerr << ind << "Media_Query " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << (block->is_negated() ? " [is_negated]": " -")+ << (block->is_restricted() ? " [is_restricted]": " -")+ << std::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);+ std::cerr << ind << "Media_Block " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->media_queries(), ind + " =@ ");+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Supports_Block*>(node)) {+ Supports_Block* block = dynamic_cast<Supports_Block*>(node);+ std::cerr << ind << "Supports_Block " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->condition(), ind + " =@ ");+ } else if (dynamic_cast<Supports_Operator*>(node)) {+ Supports_Operator* block = dynamic_cast<Supports_Operator*>(node);+ std::cerr << ind << "Supports_Operator " << block;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << std::endl;+ debug_ast(block->left(), ind + " left) ");+ debug_ast(block->right(), ind + " right) ");+ } else if (dynamic_cast<Supports_Negation*>(node)) {+ Supports_Negation* block = dynamic_cast<Supports_Negation*>(node);+ std::cerr << ind << "Supports_Negation " << block;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << std::endl;+ debug_ast(block->condition(), ind + " condition) ");+ } else if (dynamic_cast<Supports_Declaration*>(node)) {+ Supports_Declaration* block = dynamic_cast<Supports_Declaration*>(node);+ std::cerr << ind << "Supports_Declaration " << block;+ std::cerr << " (" << pstate_source_position(node) << ")"+ << std::endl;+ debug_ast(block->feature(), ind + " feature) ");+ debug_ast(block->value(), ind + " value) ");+ } else if (dynamic_cast<Block*>(node)) {+ Block* root_block = dynamic_cast<Block*>(node);+ std::cerr << ind << "Block " << root_block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ if (root_block->is_root()) std::cerr << " [root]";+ std::cerr << " " << root_block->tabs() << std::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);+ std::cerr << ind << "Warning " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Error*>(node)) {+ Error* block = dynamic_cast<Error*>(node);+ std::cerr << ind << "Error " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Debug*>(node)) {+ Debug* block = dynamic_cast<Debug*>(node);+ std::cerr << ind << "Debug " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Comment*>(node)) {+ Comment* block = dynamic_cast<Comment*>(node);+ std::cerr << ind << "Comment " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() <<+ " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl;+ debug_ast(block->text(), ind + "// ", env);+ } else if (dynamic_cast<If*>(node)) {+ If* block = dynamic_cast<If*>(node);+ std::cerr << ind << "If " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->predicate(), ind + " = ");+ debug_ast(block->block(), ind + " <>");+ debug_ast(block->alternative(), ind + " ><");+ } else if (dynamic_cast<Return*>(node)) {+ Return* block = dynamic_cast<Return*>(node);+ std::cerr << ind << "Return " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Extension*>(node)) {+ Extension* block = dynamic_cast<Extension*>(node);+ std::cerr << ind << "Extension " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->selector(), ind + "-> ", env);+ } else if (dynamic_cast<Content*>(node)) {+ Content* block = dynamic_cast<Content*>(node);+ std::cerr << ind << "Content " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Import_Stub*>(node)) {+ Import_Stub* block = dynamic_cast<Import_Stub*>(node);+ std::cerr << ind << "Import_Stub " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ } else if (dynamic_cast<Import*>(node)) {+ Import* block = dynamic_cast<Import*>(node);+ std::cerr << ind << "Import " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->media_queries(), ind + " @ ");+ // std::vector<std::string> files_;+ for (auto imp : block->urls()) debug_ast(imp, "@ ", env);+ } else if (dynamic_cast<Assignment*>(node)) {+ Assignment* block = dynamic_cast<Assignment*>(node);+ std::cerr << ind << "Assignment " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " <<" << block->variable() << ">> " << block->tabs() << std::endl;+ debug_ast(block->value(), ind + "=", env);+ } else if (dynamic_cast<Declaration*>(node)) {+ Declaration* block = dynamic_cast<Declaration*>(node);+ std::cerr << ind << "Declaration " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::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);+ std::cerr << ind << "Keyframe_Rule " << has_block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << has_block->tabs() << std::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);+ std::cerr << ind << "At_Rule " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << block->keyword() << "] " << block->tabs() << std::endl;+ debug_ast(block->selector(), ind + "~", env);+ debug_ast(block->value(), 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);+ std::cerr << ind << "Each " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::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);+ std::cerr << ind << "For " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::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);+ std::cerr << ind << "While " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << block->tabs() << std::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);+ std::cerr << ind << "Definition " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [name: " << block->name() << "] ";+ std::cerr << " [type: " << (block->type() == Sass::Definition::Type::MIXIN ? "Mixin " : "Function ") << "] ";+ // this seems to lead to segfaults some times?+ // std::cerr << " [signature: " << block->signature() << "] ";+ std::cerr << " [native: " << block->native_function() << "] ";+ std::cerr << " " << block->tabs() << std::endl;+ debug_ast(block->parameters(), ind + " params: ", env);+ 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);+ std::cerr << ind << "Mixin_Call " << block << " " << block->tabs();+ std::cerr << " [" << block->name() << "]";+ std::cerr << " [has_content: " << block->has_content() << "] " << std::endl;+ debug_ast(block->arguments(), ind + " args: ");+ if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }+ } else if (Ruleset* ruleset = dynamic_cast<Ruleset*>(node)) {+ std::cerr << ind << "Ruleset " << ruleset;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [indent: " << ruleset->tabs() << "]";+ std::cerr << (ruleset->at_root() ? " [@ROOT]" : "");+ std::cerr << (ruleset->is_root() ? " [root]" : "");+ std::cerr << std::endl;+ debug_ast(ruleset->selector(), ind + ">");+ debug_ast(ruleset->block(), ind + " ");+ } else if (dynamic_cast<Block*>(node)) {+ Block* block = dynamic_cast<Block*>(node);+ std::cerr << ind << "Block " << block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [indent: " << block->tabs() << "]" << std::endl;+ for(auto i : block->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Textual*>(node)) {+ Textual* expression = dynamic_cast<Textual*>(node);+ std::cerr << ind << "Textual ";+ if (expression->type() == Textual::NUMBER) std::cerr << " [NUMBER]";+ else if (expression->type() == Textual::PERCENTAGE) std::cerr << " [PERCENTAGE]";+ else if (expression->type() == Textual::DIMENSION) std::cerr << " [DIMENSION]";+ else if (expression->type() == Textual::HEX) std::cerr << " [HEX]";+ std::cerr << expression << " [" << expression->value() << "]";+ if (expression->is_delayed()) std::cerr << " [delayed]";+ std::cerr << std::endl;+ } else if (dynamic_cast<Variable*>(node)) {+ Variable* expression = dynamic_cast<Variable*>(node);+ std::cerr << ind << "Variable " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->name() << "]" << std::endl;+ std::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);+ std::cerr << ind << "Function_Call_Schema " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << "" << std::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);+ std::cerr << ind << "Function_Call " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->name() << "]" << std::endl;+ debug_ast(expression->arguments(), ind + " args: ", env);+ } else if (dynamic_cast<Arguments*>(node)) {+ Arguments* expression = dynamic_cast<Arguments*>(node);+ std::cerr << ind << "Arguments " << expression;+ if (expression->is_delayed()) std::cerr << " [delayed]";+ std::cerr << " (" << pstate_source_position(node) << ")";+ if (expression->has_named_arguments()) std::cerr << " [has_named_arguments]";+ if (expression->has_rest_argument()) std::cerr << " [has_rest_argument]";+ if (expression->has_keyword_argument()) std::cerr << " [has_keyword_argument]";+ std::cerr << std::endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Argument*>(node)) {+ Argument* expression = dynamic_cast<Argument*>(node);+ std::cerr << ind << "Argument " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->value() << "]";+ std::cerr << " [name: " << expression->name() << "] ";+ std::cerr << " [rest: " << expression->is_rest_argument() << "] ";+ std::cerr << " [keyword: " << expression->is_keyword_argument() << "] " << std::endl;+ debug_ast(expression->value(), ind + " value: ", env);+ } else if (dynamic_cast<Parameters*>(node)) {+ Parameters* expression = dynamic_cast<Parameters*>(node);+ std::cerr << ind << "Parameters " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [has_optional: " << expression->has_optional_parameters() << "] ";+ std::cerr << " [has_rest: " << expression->has_rest_parameter() << "] ";+ std::cerr << std::endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Parameter*>(node)) {+ Parameter* expression = dynamic_cast<Parameter*>(node);+ std::cerr << ind << "Parameter " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [name: " << expression->name() << "] ";+ std::cerr << " [default: " << expression->default_value() << "] ";+ std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;+ } else if (dynamic_cast<Unary_Expression*>(node)) {+ Unary_Expression* expression = dynamic_cast<Unary_Expression*>(node);+ std::cerr << ind << "Unary_Expression " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->type() << "]" << std::endl;+ debug_ast(expression->operand(), ind + " operand: ", env);+ } else if (dynamic_cast<Binary_Expression*>(node)) {+ Binary_Expression* expression = dynamic_cast<Binary_Expression*>(node);+ std::cerr << ind << "Binary_Expression " << expression;+ std::cerr << " [delayed: " << expression->is_delayed() << "] ";+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->type_name() << "]" << std::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);+ std::cerr << ind << "Map " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [Hashed]" << std::endl;+ for (auto i : expression->elements()) {+ debug_ast(i.first, ind + " key: ");+ debug_ast(i.second, ind + " val: ");+ }+ } else if (dynamic_cast<List*>(node)) {+ List* expression = dynamic_cast<List*>(node);+ std::cerr << ind << "List " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " (" << expression->length() << ") " <<+ (expression->separator() == SASS_COMMA ? "Comma " : "Space ") <<+ " [delayed: " << expression->is_delayed() << "] " <<+ " [interpolant: " << expression->is_interpolant() << "] " <<+ " [arglist: " << expression->is_arglist() << "] " <<+ " [hash: " << expression->hash() << "] " <<+ std::endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<Content*>(node)) {+ Content* expression = dynamic_cast<Content*>(node);+ std::cerr << ind << "Content " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [Statement]" << std::endl;+ } else if (dynamic_cast<Boolean*>(node)) {+ Boolean* expression = dynamic_cast<Boolean*>(node);+ std::cerr << ind << "Boolean " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->value() << "]" << std::endl;+ } else if (dynamic_cast<Color*>(node)) {+ Color* expression = dynamic_cast<Color*>(node);+ std::cerr << ind << "Color " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;+ } else if (dynamic_cast<Number*>(node)) {+ Number* expression = dynamic_cast<Number*>(node);+ std::cerr << ind << "Number " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << expression->value() << expression->unit() << "]" <<+ " [hash: " << expression->hash() << "] " <<+ std::endl;+ } else if (dynamic_cast<String_Quoted*>(node)) {+ String_Quoted* expression = dynamic_cast<String_Quoted*>(node);+ std::cerr << ind << "String_Quoted " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << prettyprint(expression->value()) << "]";+ if (expression->is_delayed()) std::cerr << " [delayed]";+ if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";+ if (expression->is_interpolant()) std::cerr << " [interpolant]";+ if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]";+ std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;+ } else if (dynamic_cast<String_Constant*>(node)) {+ String_Constant* expression = dynamic_cast<String_Constant*>(node);+ std::cerr << ind << "String_Constant " << expression;+ if (expression->concrete_type()) {+ std::cerr << " " << expression->concrete_type();+ }+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " [" << prettyprint(expression->value()) << "]";+ if (expression->is_delayed()) std::cerr << " [delayed]";+ if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";+ if (expression->is_interpolant()) std::cerr << " [interpolant]";+ std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;+ } else if (dynamic_cast<String_Schema*>(node)) {+ String_Schema* expression = dynamic_cast<String_Schema*>(node);+ std::cerr << ind << "String_Schema " << expression;+ std::cerr << " " << expression->concrete_type();+ if (expression->is_delayed()) std::cerr << " [delayed]";+ if (expression->has_interpolants()) std::cerr << " [has_interpolants]";+ if (expression->is_interpolant()) std::cerr << " [interpolant]";+ std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;+ for(auto i : expression->elements()) { debug_ast(i, ind + " ", env); }+ } else if (dynamic_cast<String*>(node)) {+ String* expression = dynamic_cast<String*>(node);+ std::cerr << ind << "String " << expression;+ std::cerr << " " << expression->concrete_type();+ std::cerr << " (" << pstate_source_position(node) << ")";+ if (expression->sass_fix_1291()) std::cerr << " [sass_fix_1291]";+ if (expression->is_interpolant()) std::cerr << " [interpolant]";+ std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl;+ } else if (dynamic_cast<Expression*>(node)) {+ Expression* expression = dynamic_cast<Expression*>(node);+ std::cerr << ind << "Expression " << expression;+ std::cerr << " (" << pstate_source_position(node) << ")";+ switch (expression->concrete_type()) {+ case Expression::Concrete_Type::NONE: std::cerr << " [NONE]"; break;+ case Expression::Concrete_Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;+ case Expression::Concrete_Type::NUMBER: std::cerr << " [NUMBER]"; break;+ case Expression::Concrete_Type::COLOR: std::cerr << " [COLOR]"; break;+ case Expression::Concrete_Type::STRING: std::cerr << " [STRING]"; break;+ case Expression::Concrete_Type::LIST: std::cerr << " [LIST]"; break;+ case Expression::Concrete_Type::MAP: std::cerr << " [MAP]"; break;+ case Expression::Concrete_Type::SELECTOR: std::cerr << " [SELECTOR]"; break;+ case Expression::Concrete_Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;+ case Expression::Concrete_Type::C_WARNING: std::cerr << " [C_WARNING]"; break;+ case Expression::Concrete_Type::C_ERROR: std::cerr << " [C_ERROR]"; break;+ case Expression::Concrete_Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;+ }+ std::cerr << std::endl;+ } else if (dynamic_cast<Has_Block*>(node)) {+ Has_Block* has_block = dynamic_cast<Has_Block*>(node);+ std::cerr << ind << "Has_Block " << has_block;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << has_block->tabs() << std::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);+ std::cerr << ind << "Statement " << statement;+ std::cerr << " (" << pstate_source_position(node) << ")";+ std::cerr << " " << statement->tabs() << std::endl;+ }++ if (ind == "") std::cerr << "####################################################################\n";+}++inline void debug_node(Node* node, std::string ind = "")+{+ if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";+ if (node->isCombinator()) {+ std::cerr << ind;+ std::cerr << "Combinator ";+ std::cerr << node << " ";+ if (node->got_line_feed) std::cerr << "[LF] ";+ switch (node->combinator()) {+ case Complex_Selector::ADJACENT_TO: std::cerr << "{+} "; break;+ case Complex_Selector::PARENT_OF: std::cerr << "{>} "; break;+ case Complex_Selector::PRECEDES: std::cerr << "{~} "; break;+ case Complex_Selector::REFERENCE: std::cerr << "{@} "; break;+ case Complex_Selector::ANCESTOR_OF: std::cerr << "{ } "; break;+ }+ std::cerr << std::endl;+ // debug_ast(node->combinator(), ind + " ");+ } else if (node->isSelector()) {+ std::cerr << ind;+ std::cerr << "Selector ";+ std::cerr << node << " ";+ if (node->got_line_feed) std::cerr << "[LF] ";+ std::cerr << std::endl;+ debug_ast(node->selector(), ind + " ");+ } else if (node->isCollection()) {+ std::cerr << ind;+ std::cerr << "Collection ";+ std::cerr << node << " ";+ if (node->got_line_feed) std::cerr << "[LF] ";+ std::cerr << std::endl;+ for(auto n : (*node->collection())) {+ debug_node(&n, ind + " ");+ }+ } else if (node->isNil()) {+ std::cerr << ind;+ std::cerr << "Nil ";+ std::cerr << node << " ";+ if (node->got_line_feed) std::cerr << "[LF] ";+ std::cerr << std::endl;+ } else {+ std::cerr << ind;+ std::cerr << "OTHER ";+ std::cerr << node << " ";+ if (node->got_line_feed) std::cerr << "[LF] ";+ std::cerr << std::endl;+ }+ if (ind == "") std::cerr << "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n";+}++inline void debug_ast(const AST_Node* node, std::string ind = "", Env* env = 0)+{+ debug_ast(const_cast<AST_Node*>(node), ind, env);+}++inline void debug_node(const Node* node, std::string ind = "")+{+ debug_node(const_cast<Node*>(node), ind);+}++inline void debug_extenstion_map(Sass::ExtensionSubsetMap* map, std::string ind = "")+{+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+ for(auto const &it : map->values()) {+ debug_ast(it.first, ind + "first: ");+ debug_ast(it.second, ind + "second: ");+ }+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+}++typedef std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+typedef std::vector<ExtensionPair> SubsetMapEntries;++inline void debug_subset_entries(SubsetMapEntries* entries, std::string ind = "")+{+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+ for(auto const &pair : *entries) {+ debug_ast(pair.first, ind + "first: ");+ debug_ast(pair.second, ind + "second: ");+ }+ if (ind == "") std::cerr << "#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n";+}++#endif // SASS_DEBUGGER
+ libsass/src/emitter.cpp view
@@ -0,0 +1,261 @@+#include "util.hpp"+#include "context.hpp"+#include "output.hpp"+#include "emitter.hpp"+#include "utf8_string.hpp"++namespace Sass {++ 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_space_array(false),+ in_comma_array(false),+ in_debug(false)+ { }++ // return buffer as string+ std::string Emitter::get_buffer(void)+ {+ return wbuf.buffer;+ }++ Sass_Output_Style Emitter::output_style(void)+ {+ return ctx ? ctx->output_style() : SASS_STYLE_COMPRESSED;+ }++ // PROXY METHODS FOR SOURCE MAPS++ void Emitter::add_source_index(size_t idx)+ { wbuf.smap.source_index.push_back(idx); }++ std::string Emitter::render_srcmap(Context &ctx)+ { return wbuf.smap.render_srcmap(ctx); }++ void Emitter::set_filename(const std::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) {+ std::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) {+ std::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 std::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 std::string& text)+ {+ // write space/lf+ flush_schedules();++ if (in_comment && output_style() == SASS_STYLE_COMPACT) {+ // unescape comment nodes+ std::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 std::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 std::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() == SASS_STYLE_COMPRESSED) return;+ if (output_style() == SASS_STYLE_COMPACT) return;+ if (in_declaration && in_comma_array) return;+ if (scheduled_linefeed && indentation)+ scheduled_linefeed = 1;+ std::string indent = "";+ for (size_t i = 0; i < indentation; i++)+ indent += ctx ? ctx->indent : " ";+ append_string(indent);+ }++ void Emitter::append_delimiter()+ {+ scheduled_delimiter = true;+ if (output_style() == SASS_STYLE_COMPACT) {+ if (indentation == 0) {+ append_mandatory_linefeed();+ } else {+ append_mandatory_space();+ }+ } else if (output_style() != SASS_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() != SASS_STYLE_COMPRESSED || in_debug) && buffer().size()) {+ char lst = buffer().at(buffer().length() - 1);+ if (!isspace(lst) || scheduled_delimiter) {+ append_mandatory_space();+ }+ }+ }++ void Emitter::append_special_linefeed()+ {+ if (output_style() == SASS_STYLE_COMPACT) {+ append_mandatory_linefeed();+ for (size_t p = 0; p < indentation; p++)+ append_string(ctx ? ctx->indent : " ");+ }+ }++ void Emitter::append_optional_linefeed()+ {+ if (in_declaration && in_comma_array) return;+ if (output_style() == SASS_STYLE_COMPACT) {+ append_mandatory_space();+ } else {+ append_mandatory_linefeed();+ }+ }++ void Emitter::append_mandatory_linefeed()+ {+ if (output_style() != SASS_STYLE_COMPRESSED) {+ scheduled_linefeed = 1;+ scheduled_space = 0;+ // flush_schedules();+ }+ }++ void Emitter::append_scope_opener(AST_Node* node)+ {+ scheduled_linefeed = 0;+ 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() == SASS_STYLE_COMPRESSED)+ scheduled_delimiter = false;+ if (output_style() == SASS_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() != SASS_STYLE_COMPRESSED)+ scheduled_linefeed = 2;+ }++}
+ libsass/src/emitter.hpp view
@@ -0,0 +1,91 @@+#ifndef SASS_EMITTER_H+#define SASS_EMITTER_H++#include <string>+#include "sass/base.h"+#include "source_map.hpp"+#include "ast_fwd_decl.hpp"++namespace Sass {+ class Context;++ class Emitter {++ public:+ Emitter(Context* ctx);+ virtual ~Emitter() { }++ protected:+ OutputBuffer wbuf;+ public:+ const std::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 std::string& str);+ void add_open_mapping(AST_Node* node);+ void add_close_mapping(AST_Node* node);+ std::string render_srcmap(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:+ // output strings different in comments+ bool in_comment;+ // selector list does not get linefeeds+ bool in_wrapped;+ // lists always get a space after delimiter+ bool in_media_block;+ // nested list must not have parentheses+ bool in_declaration;+ // nested lists need parentheses+ bool in_space_array;+ bool in_comma_array;+ // list separators don't get compressed in @debug+ bool in_debug;++ public:+ // return buffer as std::string+ std::string get_buffer(void);+ // flush scheduled space/linefeed+ Sass_Output_Style output_style(void);+ // 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 std::string& text);+ void prepend_output(const OutputBuffer& out);+ // append some text or token to the buffer+ void append_string(const std::string& text);+ // append some white-space only text+ void append_wspace(const std::string& text);+ // append some text or token to the buffer+ // this adds source-mappings for node start and end+ void append_token(const std::string& text, AST_Node* node);++ 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/src/environment.cpp view
@@ -0,0 +1,184 @@+#include "ast.hpp"+#include "environment.hpp"++namespace Sass {++ template <typename T>+ Environment<T>::Environment() : local_frame_(std::map<std::string, T>()), parent_(0) { }+ template <typename T>+ Environment<T>::Environment(Environment<T>* env) : local_frame_(std::map<std::string, T>()), parent_(env) { }+ template <typename T>+ Environment<T>::Environment(Environment<T>& env) : local_frame_(std::map<std::string, T>()), parent_(&env) { }++ // link parent to create a stack+ template <typename T>+ void Environment<T>::link(Environment& env) { parent_ = &env; }+ template <typename T>+ void Environment<T>::link(Environment* env) { parent_ = env; }++ // this is used to find the global frame+ // which is the second last on the stack+ template <typename T>+ bool Environment<T>::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+ template <typename T>+ bool Environment<T>::is_global() const+ {+ return parent_ && ! parent_->parent_;+ }++ template <typename T>+ std::map<std::string, T>& Environment<T>::local_frame() {+ return local_frame_;+ }++ template <typename T>+ bool Environment<T>::has_local(const std::string& key) const+ { return local_frame_.find(key) != local_frame_.end(); }++ template <typename T>+ T& Environment<T>::get_local(const std::string& key)+ { return local_frame_[key]; }++ template <typename T>+ void Environment<T>::set_local(const std::string& key, T val)+ {+ local_frame_[key] = val;+ }++ template <typename T>+ void Environment<T>::del_local(const std::string& key)+ { local_frame_.erase(key); }++ template <typename T>+ Environment<T>* Environment<T>::global_env()+ {+ Environment* cur = this;+ while (cur->is_lexical()) {+ cur = cur->parent_;+ }+ return cur;+ }++ template <typename T>+ bool Environment<T>::has_global(const std::string& key)+ { return global_env()->has(key); }++ template <typename T>+ T& Environment<T>::get_global(const std::string& key)+ { return (*global_env())[key]; }++ template <typename T>+ void Environment<T>::set_global(const std::string& key, T val)+ {+ global_env()->local_frame_[key] = val;+ }++ template <typename T>+ void Environment<T>::del_global(const std::string& key)+ { global_env()->local_frame_.erase(key); }++ template <typename T>+ Environment<T>* Environment<T>::lexical_env(const std::string& key)+ {+ Environment* cur = this;+ while (cur) {+ if (cur->has_local(key)) {+ return cur;+ }+ cur = cur->parent_;+ }+ return this;+ }++ // see if we have a lexical variable+ // move down the stack but stop before we+ // reach the global frame (is not included)+ template <typename T>+ bool Environment<T>::has_lexical(const std::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+ template <typename T>+ void Environment<T>::set_lexical(const std::string& key, T val)+ {+ auto cur = this;+ while (cur->is_lexical()) {+ if (cur->has_local(key)) {+ cur->set_local(key, val);+ return;+ }+ cur = cur->parent_;+ }+ set_local(key, val);+ }++ // look on the full stack for key+ // include all scopes available+ template <typename T>+ bool Environment<T>::has(const std::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+ template <typename T>+ T& Environment<T>::operator[](const std::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+ template <typename T>+ size_t Environment<T>::print(std::string prefix)+ {+ size_t indent = 0;+ if (parent_) indent = parent_->print(prefix) + 1;+ std::cerr << prefix << std::string(indent, ' ') << "== " << this << std::endl;+ for (typename std::map<std::string, T>::iterator i = local_frame_.begin(); i != local_frame_.end(); ++i) {+ if (!ends_with(i->first, "[f]") && !ends_with(i->first, "[f]4") && !ends_with(i->first, "[f]2")) {+ std::cerr << prefix << std::string(indent, ' ') << i->first << " " << i->second;+ if (Value* val = dynamic_cast<Value*>(i->second))+ { std::cerr << " : " << val->to_string(true, 5); }+ std::cerr << std::endl;+ }+ }+ return indent ;+ }+ #endif++ // compile implementation for AST_Node+ template class Environment<AST_Node*>;++}+
+ libsass/src/environment.hpp view
@@ -0,0 +1,92 @@+#ifndef SASS_ENVIRONMENT_H+#define SASS_ENVIRONMENT_H++#include <string>+#include <map>++#include "ast_fwd_decl.hpp"+#include "ast_def_macros.hpp"+#include "memory_manager.hpp"++namespace Sass {++ template <typename T>+ class Environment {+ // TODO: test with map+ std::map<std::string, T> local_frame_;+ ADD_PROPERTY(Environment*, parent)++ public:+ Memory_Manager mem;+ Environment();+ Environment(Environment* env);+ Environment(Environment& env);++ // link parent to create a stack+ void link(Environment& env);+ void link(Environment* env);++ // this is used to find the global frame+ // which is the second last on the stack+ bool is_lexical() const;++ // 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_global() const;++ // scope operates on the current frame++ std::map<std::string, T>& local_frame();++ bool has_local(const std::string& key) const;++ T& get_local(const std::string& key);++ // set variable on the current frame+ void set_local(const std::string& key, T val);++ void del_local(const std::string& key);++ // global operates on the global frame+ // which is the second last on the stack+ Environment* global_env();+ // get the env where the variable already exists+ // if it does not yet exist, we return current env+ Environment* lexical_env(const std::string& key);++ bool has_global(const std::string& key);++ T& get_global(const std::string& key);++ // set a variable on the global frame+ void set_global(const std::string& key, T val);++ void del_global(const std::string& 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 std::string& key) const;++ // see if we have a lexical we could update+ // either update already existing lexical value+ // or we create a new one on the current frame+ void set_lexical(const std::string& key, T val);++ // look on the full stack for key+ // include all scopes available+ bool has(const std::string& key) const;++ // use array access for getter and setter functions+ T& operator[](const std::string& key);++ #ifdef DEBUG+ size_t print(std::string prefix = "");+ #endif++ };+}++#endif
+ libsass/src/error_handling.cpp view
@@ -0,0 +1,117 @@+#include "ast.hpp"+#include "prelexer.hpp"+#include "backtrace.hpp"+#include "to_string.hpp"+#include "error_handling.hpp"++#include <iostream>++namespace Sass {++ namespace Exception {++ Base::Base(ParserState pstate, std::string msg)+ : std::runtime_error(msg),+ msg(msg), pstate(pstate)+ { }++ const char* Base::what() const throw()+ {+ return msg.c_str();+ }++ InvalidSass::InvalidSass(ParserState pstate, std::string msg)+ : Base(pstate, msg)+ { }+++ InvalidParent::InvalidParent(Selector* parent, Selector* selector)+ : Base(selector->pstate()), parent(parent), selector(selector)+ {+ msg = "Invalid parent selector for \"";+ msg += selector->to_string(false);+ msg += "\": \"";+ msg += parent->to_string(false);;+ msg += "\"";+ }++ InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value)+ : Base(pstate), fn(fn), arg(arg), type(type), value(value)+ {+ msg = arg + ": \"";+ msg += value->to_string(true, 5);+ msg += "\" is not a " + type;+ msg += " for `" + fn + "'";+ }++ InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)+ : Base(pstate, msg)+ { }++ }+++ void warn(std::string msg, ParserState pstate)+ {+ std::cerr << "Warning: " << msg<< std::endl;+ }++ void warn(std::string msg, ParserState pstate, Backtrace* bt)+ {+ Backtrace top(bt, pstate, "");+ msg += top.to_string();+ warn(msg, pstate);+ }++ void deprecated_function(std::string msg, ParserState pstate)+ {+ std::string cwd(Sass::File::get_cwd());+ std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.path));++ std::cerr << "DEPRECATION WARNING: " << msg << std::endl;+ std::cerr << "will be an error in future versions of Sass." << std::endl;+ std::cerr << " on line " << pstate.line+1 << " of " << output_path << std::endl;+ }++ void deprecated(std::string msg, std::string msg2, ParserState pstate)+ {+ std::string cwd(Sass::File::get_cwd());+ std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, pstate.path, pstate.path));++ std::cerr << "DEPRECATION WARNING on line " << pstate.line + 1;+ if (output_path.length()) std::cerr << " of " << output_path;+ std::cerr << ":" << std::endl;+ std::cerr << msg << " and will be an error in future versions of Sass." << std::endl;+ if (msg2.length()) std::cerr << msg2 << std::endl;+ std::cerr << std::endl;+ }++ void deprecated_bind(std::string msg, ParserState pstate)+ {+ std::string cwd(Sass::File::get_cwd());+ std::string abs_path(Sass::File::rel2abs(pstate.path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.path));++ std::cerr << "WARNING: " << msg << std::endl;+ std::cerr << " on line " << pstate.line+1 << " of " << output_path << std::endl;+ std::cerr << "This will be an error in future versions of Sass." << std::endl;+ }++ void error(std::string msg, ParserState pstate)+ {+ throw Exception::InvalidSyntax(pstate, msg);+ }++ void error(std::string msg, ParserState pstate, Backtrace* bt)+ {+ Backtrace top(bt, pstate, "");+ msg += "\n" + top.to_string();+ error(msg, pstate);+ }++}
+ libsass/src/error_handling.hpp view
@@ -0,0 +1,75 @@+#ifndef SASS_ERROR_HANDLING_H+#define SASS_ERROR_HANDLING_H++#include <string>+#include <sstream>+#include <stdexcept>+#include "position.hpp"++namespace Sass {++ struct Backtrace;++ namespace Exception {++ const std::string def_msg = "Invalid sass";++ class Base : public std::runtime_error {+ protected:+ std::string msg;+ public:+ ParserState pstate;+ public:+ Base(ParserState pstate, std::string msg = def_msg);+ virtual const char* what() const throw();+ virtual ~Base() throw() {};+ };++ class InvalidSass : public Base {+ public:+ InvalidSass(ParserState pstate, std::string msg);+ virtual ~InvalidSass() throw() {};+ };++ class InvalidParent : public Base {+ protected:+ Selector* parent;+ Selector* selector;+ public:+ InvalidParent(Selector* parent, Selector* selector);+ virtual ~InvalidParent() throw() {};+ };++ class InvalidArgumentType : public Base {+ protected:+ std::string fn;+ std::string arg;+ std::string type;+ const Value* value;+ public:+ InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value* value = 0);+ virtual ~InvalidArgumentType() throw() {};+ };++ class InvalidSyntax : public Base {+ public:+ InvalidSyntax(ParserState pstate, std::string msg);+ virtual ~InvalidSyntax() throw() {};+ };++ }++ void warn(std::string msg, ParserState pstate);+ void warn(std::string msg, ParserState pstate, Backtrace* bt);++ void deprecated_function(std::string msg, ParserState pstate);+ void deprecated(std::string msg, std::string msg2, ParserState pstate);+ void deprecated_bind(std::string msg, ParserState pstate);+ // void deprecated(std::string msg, ParserState pstate, Backtrace* bt);++ void error(std::string msg, ParserState pstate);+ void error(std::string msg, ParserState pstate, Backtrace* bt);++}++#endif
+ libsass/src/eval.cpp view
@@ -0,0 +1,1531 @@+#include <cstdlib>+#include <cmath>+#include <iostream>+#include <sstream>+#include <iomanip>+#include <typeinfo>++#include "file.hpp"+#include "eval.hpp"+#include "ast.hpp"+#include "bind.hpp"+#include "util.hpp"+#include "to_string.hpp"+#include "inspect.hpp"+#include "environment.hpp"+#include "position.hpp"+#include "sass/values.h"+#include "to_value.hpp"+#include "to_c.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "lexer.hpp"+#include "prelexer.hpp"+#include "parser.hpp"+#include "expand.hpp"+#include "color_maps.hpp"++namespace Sass {++ 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+ inline double mod(double x, double y) { return std::abs(std::fmod(x, y)); } // x/0 checked by caller+ typedef double (*bop)(double, double);+ bop ops[Sass_OP::NUM_OPS] = {+ 0, 0, // and, or+ 0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte+ add, sub, mul, div, mod+ };++ Eval::Eval(Expand& exp)+ : exp(exp),+ ctx(exp.ctx),+ listize(exp.ctx)+ { }+ Eval::~Eval() { }++ Context& Eval::context()+ {+ return ctx;+ }++ Env* Eval::environment()+ {+ return exp.environment();+ }++ Selector_List* Eval::selector()+ {+ return exp.selector();+ }++ Backtrace* Eval::backtrace()+ {+ return exp.backtrace();+ }++ 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)+ {+ Env* env = exp.environment();+ std::string var(a->variable());+ if (a->is_global()) {+ if (a->is_default()) {+ if (env->has_global(var)) {+ Expression* e = dynamic_cast<Expression*>(env->get_global(var));+ if (!e || 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_global(var, a->value()->perform(this));+ }+ }+ else if (a->is_default()) {+ if (env->has_lexical(var)) {+ auto cur = env;+ while (cur && cur->is_lexical()) {+ if (cur->has_local(var)) {+ if (AST_Node* node = cur->get_local(var)) {+ Expression* e = dynamic_cast<Expression*>(node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ cur->set_local(var, a->value()->perform(this));+ }+ }+ else {+ throw std::runtime_error("Env not in sync");+ }+ return 0;+ }+ cur = cur->parent();+ }+ throw std::runtime_error("Env not in sync");+ }+ else if (env->has_global(var)) {+ if (AST_Node* node = env->get_global(var)) {+ Expression* e = dynamic_cast<Expression*>(node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(this));+ }+ }+ }+ else if (env->is_lexical()) {+ env->set_local(var, a->value()->perform(this));+ }+ else {+ env->set_local(var, a->value()->perform(this));+ }+ }+ else {+ env->set_lexical(var, a->value()->perform(this));+ }+ return 0;+ }++ Expression* Eval::operator()(If* i)+ {+ Expression* rv = 0;+ Env env(exp.environment());+ exp.env_stack.push_back(&env);+ if (*i->predicate()->perform(this)) {+ rv = i->block()->perform(this);+ }+ else {+ Block* alt = i->alternative();+ if (alt) rv = alt->perform(this);+ }+ exp.env_stack.pop_back();+ return rv;+ }++ // For does not create a new env scope+ // But iteration vars are reset afterwards+ Expression* Eval::operator()(For* f)+ {+ std::string variable(f->variable());+ Expression* low = f->lower_bound()->perform(this);+ if (low->concrete_type() != Expression::NUMBER) {+ error("lower bound of `@for` directive must be numeric", low->pstate());+ }+ 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()) {+ std::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+ Env* env = exp.environment();+ Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());+ AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;+ env->set_local(variable, it);+ 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)+ {+ std::vector<std::string> variables(e->variables());+ Expression* expr = e->list()->perform(this);+ Env* env = exp.environment();+ 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 = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);+ *list << expr;+ }+ else {+ list = static_cast<List*>(expr);+ }+ // remember variables and then reset them+ std::vector<AST_Node*> old_vars(variables.size());+ for (size_t i = 0, L = variables.size(); i < L; ++i) {+ old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;+ env->set_local(variables[i], 0);+ }+ Block* body = e->block();+ Expression* val = 0;++ if (map) {+ for (auto key : map->keys()) {+ Expression* value = map->at(key);++ if (variables.size() == 1) {+ List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_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) {+ Expression* e = (*list)[i];+ // unwrap value if the expression is an argument+ if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();+ // check if we got passed a list of args (investigate)+ if (List* scalars = dynamic_cast<List*>(e)) {+ if (variables.size() == 1) {+ Expression* var = scalars;+ env->set_local(variables[0], var);+ } else {+ for (size_t j = 0, K = variables.size(); j < K; ++j) {+ Expression* res = j >= scalars->length()+ ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())+ : (*scalars)[j];+ env->set_local(variables[j], res);+ }+ }+ } else {+ if (variables.size() > 0) {+ env->set_local(variables[0], e);+ for (size_t j = 1, K = variables.size(); j < K; ++j) {+ Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());+ env->set_local(variables[j], res);+ }+ }+ }+ val = body->perform(this);+ 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);+ Env* env = exp.environment();++ // 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));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string result(unquote(message->perform(&to_string)));+ Backtrace top(backtrace(), w->pstate(), "");+ std::cerr << "WARNING: " << result;+ std::cerr << top.to_string(true);+ std::cerr << std::endl << std::endl;+ return 0;+ }++ Expression* Eval::operator()(Error* e)+ {+ Expression* message = e->message()->perform(this);+ To_String to_string(&ctx);+ Env* env = exp.environment();++ // 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));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::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, false, true);+ Env* env = exp.environment();++ // 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));+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ sass_delete_value(c_args);+ sass_delete_value(c_val);+ return 0;++ }++ std::string cwd(ctx.cwd());+ std::string result(unquote(message->perform(&to_string)));+ std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));+ std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));+ std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));++ std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;+ std::cerr << std::endl;+ return 0;+ }++ Expression* Eval::operator()(List* l)+ {+ if (l->is_expanded()) return l;+ List* ll = SASS_MEMORY_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;++ // make sure we're not starting with duplicate keys.+ // the duplicate key state will have been set in the parser phase.+ if (m->has_duplicate_key()) {+ To_String to_string(&ctx);+ error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());+ }++ Map* mm = SASS_MEMORY_NEW(ctx.mem, Map,+ m->pstate(),+ m->length());+ for (auto key : m->keys()) {+ Expression* ex_key = key->perform(this);+ Expression* ex_val = m->at(key)->perform(this);+ *mm << std::make_pair(ex_key, ex_val);+ }++ // check the evaluated keys aren't duplicates.+ if (mm->has_duplicate_key()) {+ To_String to_string(&ctx);+ error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", mm->pstate());+ }++ mm->is_expanded(true);+ return mm;+ }++ Expression* Eval::operator()(Binary_Expression* b)+ {+ enum Sass_OP op_type = b->type();+ // don't eval delayed expressions (the '/' when used as a separator)+ if (op_type == Sass_OP::DIV && b->is_delayed()) return b;+ b->is_delayed(false);+ // if one of the operands is a '/' then make sure it's evaluated+ Expression* lhs = b->left()->perform(this);+ lhs->is_delayed(false);+ while (typeid(*lhs) == typeid(Binary_Expression)) {+ Binary_Expression* lhs_ex = static_cast<Binary_Expression*>(lhs);+ if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break;+ lhs = Eval::operator()(lhs_ex);+ }++ switch (op_type) {+ case Sass_OP::AND:+ return *lhs ? b->right()->perform(this) : lhs;+ break;++ case Sass_OP::OR:+ return *lhs ? lhs : b->right()->perform(this);+ break;++ default:+ break;+ }+ // not a logical connective, so go ahead and eval the rhs+ Expression* rhs = b->right()->perform(this);+ // maybe fully evaluate structure+ if (op_type == Sass_OP::EQ ||+ op_type == Sass_OP::NEQ ||+ op_type == Sass_OP::GT ||+ op_type == Sass_OP::GTE ||+ op_type == Sass_OP::LT ||+ op_type == Sass_OP::LTE)+ {+ rhs->is_expanded(false);+ rhs->set_delayed(false);+ rhs = rhs->perform(this);+ }+ else+ {+ // rhs->set_delayed(false);+ // rhs = rhs->perform(this);+ }++ // upgrade string to number if possible (issue #948)+ if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) {+ if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::number >(start) != 0) {+ rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());+ rhs->is_delayed(false); rhs = rhs->perform(this);+ }+ }+ }++ // see if it's a relational expression+ switch(op_type) {+ case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs));+ case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs));+ case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs) && !eq(lhs, rhs));+ case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs));+ case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs));+ case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs) || eq(lhs, rhs));++ default: break;+ }+++ Expression::Concrete_Type l_type = lhs->concrete_type();+ Expression::Concrete_Type r_type = rhs->concrete_type();++ // Is one of the operands an interpolant?+ String_Schema* s1 = dynamic_cast<String_Schema*>(b->left());+ String_Schema* s2 = dynamic_cast<String_Schema*>(b->right());++ if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants())) {+ std::string sep;+ switch (op_type) {+ case Sass_OP::SUB: sep = "-"; break;+ case Sass_OP::DIV: sep = "/"; break;+ case Sass_OP::ADD: sep = "+"; break;+ case Sass_OP::MUL: sep = "*"; break;+ default: break;+ }++ // If possible upgrade LHS to a number+ if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB) {+ if (String_Constant* str = dynamic_cast<String_Constant*>(lhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::number >(start) != 0) {+ lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value());+ lhs->is_delayed(false); lhs = lhs->perform(this);+ }+ }+ if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {+ std::string value(str->value());+ const char* start = value.c_str();+ if (Prelexer::sequence < Prelexer::number >(start) != 0) {+ rhs = SASS_MEMORY_NEW(ctx.mem, Textual, rhs->pstate(), Textual::DIMENSION, str->value());+ rhs->is_delayed(false); rhs = rhs->perform(this);+ }+ }+ }++ To_Value to_value(ctx, ctx.mem);+ Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));+ Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));+ 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 SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(),+ v_l->to_string() + " " + sep + " " + v_r->to_string());+ }+ }++ // ToDo: throw error in op functions+ // ToDo: then catch and re-throw them+ ParserState pstate(b->pstate());+ int precision = (int)ctx.c_options->precision;+ bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED;+ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {+ const Number* l_n = dynamic_cast<const Number*>(lhs);+ const Number* r_n = dynamic_cast<const Number*>(rhs);+ return op_numbers(ctx.mem, op_type, *l_n, *r_n, compressed, precision, &pstate);+ }+ if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {+ const Number* l_n = dynamic_cast<const Number*>(lhs);+ const Color* r_c = dynamic_cast<const Color*>(rhs);+ return op_number_color(ctx.mem, op_type, *l_n, *r_c, compressed, precision, &pstate);+ }+ if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {+ const Color* l_c = dynamic_cast<const Color*>(lhs);+ const Number* r_n = dynamic_cast<const Number*>(rhs);+ return op_color_number(ctx.mem, op_type, *l_c, *r_n, compressed, precision, &pstate);+ }+ if (l_type == Expression::COLOR && r_type == Expression::COLOR) {+ const Color* l_c = dynamic_cast<const Color*>(lhs);+ const Color* r_c = dynamic_cast<const Color*>(rhs);+ return op_colors(ctx.mem, op_type, *l_c, *r_c, compressed, precision, &pstate);+ }++ To_Value to_value(ctx, ctx.mem);+ Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));+ Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));+ Value* ex = op_strings(ctx.mem, op_type, *v_l, *v_r, compressed, precision, &pstate);+ if (String_Constant* str = dynamic_cast<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 = SASS_MEMORY_NEW(ctx.mem, Boolean, u->pstate(), (bool)*operand);+ result->value(!result->value());+ return result;+ }+ else if (operand->concrete_type() == Expression::NUMBER) {+ Number* result = SASS_MEMORY_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(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), ""));+ }+ else u->operand(operand);+ String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ u->pstate(),+ u->perform(&to_string));+ return result;+ }+ // unreachable+ return u;+ }++ Expression* Eval::operator()(Function_Call* c)+ {+ if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {+ std::ostringstream stm;+ stm << "Stack depth exceeded max of " << Constants::MaxCallStack;+ error(stm.str(), c->pstate(), backtrace());+ }+ std::string name(Util::normalize_underscores(c->name()));+ std::string full_name(name + "[f]");+ Arguments* args = c->arguments();+ if (full_name != "if[f]") {+ args = static_cast<Arguments*>(args->perform(this));+ }++ Env* env = environment();+ if (!env->has(full_name)) {+ if (!env->has("*[f]")) {+ // just pass it through as a literal+ Function_Call* lit = SASS_MEMORY_NEW(ctx.mem, Function_Call,+ c->pstate(),+ c->name(),+ args);+ To_String to_string(&ctx);+ if (args->has_named_arguments()) {+ error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());+ }+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ c->pstate(),+ lit->perform(&to_string));+ } else {+ // call generic function+ full_name = "*[f]";+ }+ }++ Definition* def = static_cast<Definition*>((*env)[full_name]);++ if (def->is_overload_stub()) {+ std::stringstream ss;+ ss << full_name+ << args->length();+ full_name = ss.str();+ std::string resolved_name(full_name);+ if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());+ def = static_cast<Definition*>((*env)[resolved_name]);+ }++ Expression* result = c;+ Block* body = def->block();+ Native_Function func = def->native_function();+ Sass_Function_Entry c_function = def->c_function();++ Parameters* params = def->parameters();+ Env fn_env(def->environment());+ exp.env_stack.push_back(&fn_env);++ if (func || body) {+ bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);+ Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+ exp.backtrace_stack.push_back(&here);+ // if it's user-defined, eval the body+ if (body) result = body->perform(this);+ // if it's native, invoke the underlying CPP function+ else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace());+ if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate());+ exp.backtrace_stack.pop_back();+ }++ // else if it's a user-defined c function+ // convert call into C-API compatible form+ else if (c_function) {+ Sass_Function_Fn c_func = sass_function_get_function(c_function);+ if (full_name == "*[f]") {+ String_Quoted *str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, c->pstate(), c->name());+ Arguments* new_args = SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate());+ *new_args << SASS_MEMORY_NEW(ctx.mem, Argument, c->pstate(), str);+ *new_args += args;+ args = new_args;+ }++ // populates env with default values for params+ std::string ff(c->name());+ bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);++ Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");+ exp.backtrace_stack.push_back(&here);++ To_C to_c;+ union Sass_Value* c_args = sass_make_list(params[0].length(), SASS_COMMA);+ for(size_t i = 0; i < params[0].length(); i++) {+ std::string key = params[0][i]->name();+ AST_Node* node = fn_env.get_local(key);+ Expression* arg = static_cast<Expression*>(node);+ sass_list_set_value(c_args, i, arg->perform(&to_c));+ }+ union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);+ 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(ctx.mem, c_val, ctx, backtrace(), c->pstate());++ exp.backtrace_stack.pop_back();+ sass_delete_value(c_args);+ if (c_val != c_args)+ sass_delete_value(c_val);+ }++ // link back to function definition+ // only do this for custom functions+ if (result->pstate().file == std::string::npos)+ result->pstate(c->pstate());++ result->is_delayed(result->concrete_type() == Expression::STRING);+ if (!result->is_delayed()) result = result->perform(this);+ exp.env_stack.pop_back();+ 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 = SASS_MEMORY_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);+ std::string name(v->name());+ Expression* value = 0;+ Env* env = environment();+ if (env->has(name)) value = static_cast<Expression*>((*env)[name]);+ else error("Undefined variable: \"" + v->name() + "\".", v->pstate());+ // std::cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << std::endl;+ if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();++ // behave according to as ruby sass (add leading zero)+ if (value->concrete_type() == Expression::NUMBER) {+ value = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str);+ } else if (auto str = dynamic_cast<String_Constant*>(value)) {+ value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->perform(&to_string));+ }+ }+ else if (value->concrete_type() == Expression::LIST) {+ value = SASS_MEMORY_NEW(ctx.mem, List, *static_cast<List*>(value));+ }+ else if (value->concrete_type() == Expression::MAP) {+ value = SASS_MEMORY_NEW(ctx.mem, Map, *static_cast<Map*>(value));+ }+ else if (value->concrete_type() == Expression::BOOLEAN) {+ value = SASS_MEMORY_NEW(ctx.mem, Boolean, *static_cast<Boolean*>(value));+ }+ else if (value->concrete_type() == Expression::COLOR) {+ value = SASS_MEMORY_NEW(ctx.mem, Color, *static_cast<Color*>(value));+ }+ else if (value->concrete_type() == Expression::NULL_VAL) {+ value = SASS_MEMORY_NEW(ctx.mem, Null, value->pstate());+ }+ else if (value->concrete_type() == Expression::SELECTOR) {+ value = value->perform(this); // ->perform(&listize);+ }++ // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::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 std::string& text = t->value();+ size_t num_pos = text.find_first_not_of(" \n\r\t");+ if (num_pos == std::string::npos) num_pos = text.length();+ size_t unit_pos = text.find_first_not_of("-+0123456789.", num_pos);+ if (unit_pos == std::string::npos) unit_pos = text.length();+ const std::string& num = text.substr(num_pos, unit_pos - num_pos);++ switch (t->type())+ {+ case Textual::NUMBER:+ result = SASS_MEMORY_NEW(ctx.mem, Number,+ t->pstate(),+ sass_atof(num.c_str()),+ "",+ zero);+ break;+ case Textual::PERCENTAGE:+ result = SASS_MEMORY_NEW(ctx.mem, Number,+ t->pstate(),+ sass_atof(num.c_str()),+ "%",+ zero);+ break;+ case Textual::DIMENSION:+ result = SASS_MEMORY_NEW(ctx.mem, Number,+ t->pstate(),+ sass_atof(num.c_str()),+ Token(number(text.c_str())),+ zero);+ break;+ case Textual::HEX: {+ if (t->value().substr(0, 1) != "#") {+ result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, t->pstate(), t->value());+ break;+ }+ std::string hext(t->value().substr(1)); // chop off the '#'+ if (hext.length() == 6) {+ std::string r(hext.substr(0,2));+ std::string g(hext.substr(2,2));+ std::string b(hext.substr(4,2));+ result = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Color,+ t->pstate(),+ static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),+ static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),+ static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),+ 1, false,+ t->value());+ }+ } break;+ }+ return result;+ }++ Expression* Eval::operator()(Number* n)+ {+ return n;+ }++ Expression* Eval::operator()(Boolean* b)+ {+ return b;+ }++ char is_quoted(std::string str)+ {+ size_t len = str.length();+ if (len < 2) return 0;+ if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {+ return str[0];+ }+ else {+ return 0;+ }+ }++ std::string Eval::interpolation(Expression* s, bool into_quotes) {+ Env* env = environment();+ if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {+ if (str_quoted->quote_mark()) {+ if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {+ return evacuate_escapes(str_quoted->value());+ } else {+ return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));+ }+ } else {+ return evacuate_escapes(str_quoted->value());+ }+ } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {+ if (into_quotes && !str_constant->is_interpolant()) return str_constant->value();+ return evacuate_escapes(str_constant->value());+ } else if (dynamic_cast<Parent_Selector*>(s)) {+ To_String to_string(&ctx);+ Expression* sel = s->perform(this);+ return evacuate_quotes(sel ? sel->perform(&to_string) : "");++ } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {+ // To_String to_string(&ctx);+ // return evacuate_quotes(str_schema->perform(&to_string));++ std::string res = "";+ for(auto i : str_schema->elements())+ res += (interpolation(i));+ //ToDo: do this in one step+ auto esc = evacuate_escapes(res);+ auto unq = unquote(esc);+ if (unq == esc) {+ return string_to_output(res);+ } else {+ return evacuate_quotes(unq);+ }+ } else if (List* list = dynamic_cast<List*>(s)) {+ std::string acc = ""; // ToDo: different output styles+ std::string sep = list->separator() == SASS_COMMA ? "," : " ";+ if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " ";+ bool initial = false;+ for(auto item : list->elements()) {+ if (item->concrete_type() != Expression::NULL_VAL) {+ if (initial) acc += sep;+ acc += interpolation(item);+ initial = true;+ }+ }+ return evacuate_quotes(acc);+ } else if (Variable* var = dynamic_cast<Variable*>(s)) {+ std::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 (dynamic_cast<Binary_Expression*>(s)) {+ Expression* ex = s->perform(this);+ // avoid recursive calls if same object gets returned+ // since we will call interpolate again for the result+ if (ex == s) {+ To_String to_string(&ctx);+ return evacuate_quotes(s->perform(&to_string));+ }+ return evacuate_quotes(interpolation(ex));+ } else if (dynamic_cast<Function_Call*>(s)) {+ Expression* ex = s->perform(this);+ return evacuate_quotes(unquote(interpolation(ex)));+ } else if (dynamic_cast<Unary_Expression*>(s)) {+ Expression* ex = s->perform(this);+ return evacuate_quotes(interpolation(ex));+ } else if (dynamic_cast<Map*>(s)) {+ To_String to_string(&ctx);+ std::string dbg(s->perform(&to_string));+ error(dbg + " isn't a valid CSS value.", s->pstate());+ return dbg;+ } else {+ To_String to_string(&ctx);+ return evacuate_quotes(s->perform(&to_string));+ }+ }++ Expression* Eval::operator()(String_Schema* s)+ {+ std::string acc;+ bool into_quotes = false;+ size_t L = s->length();+ if (L > 1) {+ if (String_Constant* l = dynamic_cast<String_Constant*>((*s)[0])) {+ if (String_Constant* r = dynamic_cast<String_Constant*>((*s)[L - 1])) {+ if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;+ if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;+ }+ }+ }+ for (size_t i = 0; i < L; ++i) {+ // really a very special fix, but this is the logic I got from+ // analyzing the ruby sass behavior and it actually seems to work+ // https://github.com/sass/libsass/issues/1333+ if (i == 0 && L > 1 && dynamic_cast<Function_Call*>((*s)[i])) {+ Expression* ex = (*s)[i]->perform(this);+ if (auto sq = dynamic_cast<String_Quoted*>(ex)) {+ if (sq->is_delayed() && ! s->has_interpolants()) {+ acc += string_escape(quote(sq->value(), sq->quote_mark()));+ } else {+ acc += interpolation((*s)[i], into_quotes);+ }+ } else if (ex) {+ acc += interpolation((*s)[i], into_quotes);+ }+ } else if ((*s)[i]) {+ acc += interpolation((*s)[i], into_quotes);+ }+ }+ String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), acc);+ if (!str->quote_mark()) {+ str->value(string_unescape(str->value()));+ } else if (str->quote_mark()) {+ str->quote_mark('*');+ }+ str->is_delayed(true);+ return str;+ }++ Expression* Eval::operator()(String_Constant* s)+ {+ if (!s->is_delayed() && name_to_color(s->value())) {+ Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *name_to_color(s->value()));+ c->pstate(s->pstate());+ c->disp(s->value());+ return c;+ }+ return s;+ }++ Expression* Eval::operator()(String_Quoted* s)+ {+ return s;+ }++ Expression* Eval::operator()(Supports_Operator* c)+ {+ Expression* left = c->left()->perform(this);+ Expression* right = c->right()->perform(this);+ Supports_Operator* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Operator,+ c->pstate(),+ static_cast<Supports_Condition*>(left),+ static_cast<Supports_Condition*>(right),+ c->operand());+ return cc;+ }++ Expression* Eval::operator()(Supports_Negation* c)+ {+ Expression* condition = c->condition()->perform(this);+ Supports_Negation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Negation,+ c->pstate(),+ static_cast<Supports_Condition*>(condition));+ return cc;+ }++ Expression* Eval::operator()(Supports_Declaration* c)+ {+ Expression* feature = c->feature()->perform(this);+ Expression* value = c->value()->perform(this);+ Supports_Declaration* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,+ c->pstate(),+ feature,+ value);+ return cc;+ }++ Expression* Eval::operator()(Supports_Interpolation* c)+ {+ Expression* value = c->value()->perform(this);+ Supports_Interpolation* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation,+ c->pstate(),+ value);+ 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 = SASS_MEMORY_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 = SASS_MEMORY_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);+ if (feature && dynamic_cast<String_Quoted*>(feature)) {+ feature = SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ feature->pstate(),+ dynamic_cast<String_Quoted*>(feature)->value());+ }+ Expression* value = e->value();+ value = (value ? value->perform(this) : 0);+ if (value && dynamic_cast<String_Quoted*>(value)) {+ value = SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ value->pstate(),+ dynamic_cast<String_Quoted*>(value)->value());+ }+ return SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List,+ val->pstate(),+ 0,+ SASS_COMMA,+ true);+ *wrapper << val;+ val = wrapper;+ }+ }+ return SASS_MEMORY_NEW(ctx.mem, Argument,+ a->pstate(),+ val,+ a->name(),+ is_rest_argument,+ is_keyword_argument);+ }++ Expression* Eval::operator()(Arguments* a)+ {+ Arguments* aa = SASS_MEMORY_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;+ }++ inline Expression* Eval::fallback_impl(AST_Node* n)+ {+ return static_cast<Expression*>(n);+ }++ // All the binary helpers.++ bool Eval::eq(Expression* lhs, Expression* rhs)+ {+ // use compare operator from ast node+ return lhs && rhs && *lhs == *rhs;+ }++ bool Eval::lt(Expression* lhs, Expression* rhs)+ {+ Number* l = dynamic_cast<Number*>(lhs);+ Number* r = dynamic_cast<Number*>(rhs);+ if (!l) error("may only compare numbers", lhs->pstate());+ if (!r) error("may only compare numbers", rhs->pstate());+ // use compare operator from ast node+ return *l < *r;+ }++ Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, bool compressed, int precision, ParserState* pstate)+ {+ double lv = l.value();+ double rv = r.value();+ if (op == Sass_OP::DIV && !rv) {+ return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), "Infinity");+ }+ if (op == Sass_OP::MOD && !rv) {+ error("division by zero", pstate ? *pstate : r.pstate());+ }++ Number tmp(r);+ bool strict = op != Sass_OP::MUL && op != Sass_OP::DIV;+ tmp.normalize(l.find_convertible_unit(), strict);+ std::string l_unit(l.unit());+ std::string r_unit(tmp.unit());+ if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&+ (op == Sass_OP::ADD || op == Sass_OP::SUB)) {+ error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", pstate ? *pstate : r.pstate());+ }+ Number* v = SASS_MEMORY_NEW(mem, Number, l);+ v->pstate(pstate ? *pstate : l.pstate());+ if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {+ v->numerator_units() = r.numerator_units();+ v->denominator_units() = r.denominator_units();+ }++ if (op == Sass_OP::MUL) {+ v->value(ops[op](lv, rv));+ 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 == Sass_OP::DIV) {+ v->value(ops[op](lv, rv));+ 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]);+ }+ } else {+ v->value(ops[op](lv, tmp.value()));+ }+ v->normalize();+ return v;+ }++ Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, bool compressed, int precision, ParserState* pstate)+ {+ Color r(rh);+ r.disp("");+ double lv = l.value();+ switch (op) {+ case Sass_OP::ADD:+ case Sass_OP::MUL: {+ return SASS_MEMORY_NEW(mem, Color,+ pstate ? *pstate : l.pstate(),+ ops[op](lv, r.r()),+ ops[op](lv, r.g()),+ ops[op](lv, r.b()),+ r.a());+ } break;+ case Sass_OP::SUB:+ case Sass_OP::DIV: {+ std::string sep(op == Sass_OP::SUB ? "-" : "/");+ std::string color(r.to_string(compressed||!r.sixtuplet(), precision));+ return SASS_MEMORY_NEW(mem, String_Quoted,+ pstate ? *pstate : l.pstate(),+ l.to_string(compressed, precision)+ + sep+ + color);+ } break;+ case Sass_OP::MOD: {+ error("cannot divide a number by a color", pstate ? *pstate : r.pstate());+ } break;+ default: break; // caller should ensure that we don't get here+ }+ // unreachable+ return SASS_MEMORY_NEW(mem, Color, rh);+ }++ Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, bool compressed, int precision, ParserState* pstate)+ {+ double rv = r.value();+ if (op == Sass_OP::DIV && !rv) error("division by zero", pstate ? *pstate : r.pstate());+ return SASS_MEMORY_NEW(mem, Color,+ pstate ? *pstate : l.pstate(),+ ops[op](l.r(), rv),+ ops[op](l.g(), rv),+ ops[op](l.b(), rv),+ l.a());+ }++ Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, bool compressed, int precision, ParserState* pstate)+ {+ if (l.a() != r.a()) {+ error("alpha channels must be equal when combining colors", pstate ? *pstate : r.pstate());+ }+ if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {+ error("division by zero", pstate ? *pstate : r.pstate());+ }+ return SASS_MEMORY_NEW(mem, Color,+ pstate ? *pstate : l.pstate(),+ ops[op](l.r(), r.r()),+ ops[op](l.g(), r.g()),+ ops[op](l.b(), r.b()),+ l.a());+ }++ Value* Eval::op_strings(Memory_Manager& mem, enum Sass_OP op, Value& lhs, Value& rhs, bool compressed, int precision, ParserState* pstate)+ {+ Expression::Concrete_Type ltype = lhs.concrete_type();+ Expression::Concrete_Type rtype = rhs.concrete_type();++ String_Quoted* lqstr = dynamic_cast<String_Quoted*>(&lhs);+ String_Quoted* rqstr = dynamic_cast<String_Quoted*>(&rhs);++ std::string lstr(lqstr ? lqstr->value() : lhs.to_string(compressed, precision));+ std::string rstr(rqstr ? rqstr->value() : rhs.to_string(compressed, precision));++ bool l_str_quoted = ((Sass::String*)&lhs) && ((Sass::String*)&lhs)->sass_fix_1291();+ bool r_str_quoted = ((Sass::String*)&rhs) && ((Sass::String*)&rhs)->sass_fix_1291();+ bool l_str_color = ltype == Expression::STRING && name_to_color(lstr) && !l_str_quoted;+ bool r_str_color = rtype == Expression::STRING && name_to_color(rstr) && !r_str_quoted;++ if (l_str_color && r_str_color) {+ const Color* c_l = name_to_color(lstr);+ const Color* c_r = name_to_color(rstr);+ return op_colors(mem, op,*c_l, *c_r, compressed, precision);+ }+ else if (l_str_color && rtype == Expression::COLOR) {+ const Color* c_l = name_to_color(lstr);+ const Color* c_r = dynamic_cast<const Color*>(&rhs);+ return op_colors(mem, op, *c_l, *c_r, compressed, precision);+ }+ else if (ltype == Expression::COLOR && r_str_color) {+ const Color* c_l = dynamic_cast<const Color*>(&lhs);+ const Color* c_r = name_to_color(rstr);+ return op_colors(mem, op, *c_l, *c_r, compressed, precision);+ }+ else if (l_str_color && rtype == Expression::NUMBER) {+ const Color* c_l = name_to_color(lstr);+ const Number* n_r = dynamic_cast<const Number*>(&rhs);+ return op_color_number(mem, op, *c_l, *n_r, compressed, precision);+ }+ else if (ltype == Expression::NUMBER && r_str_color) {+ const Number* n_l = dynamic_cast<const Number*>(&lhs);+ const Color* c_r = name_to_color(rstr);+ return op_number_color(mem, op, *n_l, *c_r, compressed, precision);+ }+ if (op == Sass_OP::MUL) error("invalid operands for multiplication", lhs.pstate());+ if (op == Sass_OP::MOD) error("invalid operands for modulo", lhs.pstate());+ std::string sep;+ switch (op) {+ case Sass_OP::SUB: sep = "-"; break;+ case Sass_OP::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\".", rhs.pstate());++ if (ltype == Expression::NUMBER && sep == "/" && rtype == Expression::STRING)+ {+ return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(),+ lhs.to_string() + sep + rhs.to_string());+ }++ if ( (ltype == Expression::STRING || sep == "") &&+ (sep != "/" || !rqstr || !rqstr->quote_mark())+ ) {+ char quote_mark = 0;+ std::string unq(unquote(lstr + sep + rstr, "e_mark, true));+ if (quote_mark && quote_mark != '*') {+ return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), quote_mark + unq + quote_mark);+ }+ return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr);+ }+ return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + quote(rstr));+ }++ Expression* cval_to_astnode(Memory_Manager& mem, union 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 = SASS_MEMORY_NEW(mem, Boolean, pstate, !!sass_boolean_get_value(v));+ } break;+ case SASS_NUMBER: {+ e = SASS_MEMORY_NEW(mem, Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));+ } break;+ case SASS_COLOR: {+ e = SASS_MEMORY_NEW(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: {+ if (sass_string_is_quoted(v))+ e = SASS_MEMORY_NEW(mem, String_Quoted, pstate, sass_string_get_value(v));+ else {+ e = SASS_MEMORY_NEW(mem, String_Constant, pstate, sass_string_get_value(v));+ }+ } break;+ case SASS_LIST: {+ List* l = SASS_MEMORY_NEW(mem, List, pstate, sass_list_get_length(v), sass_list_get_separator(v));+ for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {+ *l << cval_to_astnode(mem, sass_list_get_value(v, i), ctx, backtrace, pstate);+ }+ e = l;+ } break;+ case SASS_MAP: {+ Map* m = SASS_MEMORY_NEW(mem, Map, pstate);+ for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {+ *m << std::make_pair(+ cval_to_astnode(mem, sass_map_get_key(v, i), ctx, backtrace, pstate),+ cval_to_astnode(mem, sass_map_get_value(v, i), ctx, backtrace, pstate));+ }+ e = m;+ } break;+ case SASS_NULL: {+ e = SASS_MEMORY_NEW(mem, Null, pstate);+ } break;+ case SASS_ERROR: {+ error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);+ } break;+ case SASS_WARNING: {+ error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);+ } break;+ }+ return e;+ }++ Selector_List* Eval::operator()(Selector_List* s)+ {+ std::vector<Selector_List*> rv;+ Selector_List* sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, s->pstate());+ sl->media_block(s->media_block());+ for (size_t i = 0, iL = s->length(); i < iL; ++i) {+ rv.push_back(operator()((*s)[i]));+ }++ // we should actually permutate parent first+ // but here we have permutated the selector first+ size_t round = 0;+ while (round != std::string::npos) {+ bool abort = true;+ for (size_t i = 0, iL = rv.size(); i < iL; ++i) {+ if (rv[i]->length() > round) {+ *sl << (*rv[i])[round];+ abort = false;+ }+ }+ if (abort) {+ round = std::string::npos;+ } else {+ ++ round;+ }++ }+ return sl;+ }+++ Selector_List* Eval::operator()(Complex_Selector* s)+ {+ return s->parentize(selector(), ctx);++ }++ Attribute_Selector* Eval::operator()(Attribute_Selector* s)+ {+ String* attr = s->value();+ if (attr) { attr = static_cast<String*>(attr->perform(this)); }+ Attribute_Selector* ss = SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, *s);+ ss->value(attr);+ return ss;+ }++ Selector_List* Eval::operator()(Selector_Schema* s)+ {+ To_String to_string;+ // the parser will look for a brace to end the selector+ std::string result_str(s->contents()->perform(this)->perform(&to_string));+ result_str = unquote(Util::rtrim(result_str)) + "{";+ Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());+ return operator()(p.parse_selector_list(exp.block_stack.back()->is_root()));+ }++ Expression* Eval::operator()(Parent_Selector* p)+ {+ Selector_List* pr = selector();+ if (pr) {+ exp.selector_stack.pop_back();+ pr = operator()(pr);+ exp.selector_stack.push_back(pr);+ return pr;+ } else {+ return SASS_MEMORY_NEW(ctx.mem, Null, p->pstate());+ }+ }++}
+ libsass/src/eval.hpp view
@@ -0,0 +1,107 @@+#ifndef SASS_EVAL_H+#define SASS_EVAL_H++#include "context.hpp"+#include "listize.hpp"+#include "operation.hpp"++namespace Sass {++ class Expand;+ class Context;+ class Listize;++ class Eval : public Operation_CRTP<Expression*, Eval> {++ private:+ Expression* fallback_impl(AST_Node* n);++ public:+ Expand& exp;+ Context& ctx;+ Listize listize;+ Eval(Expand& exp);+ virtual ~Eval();++ Env* environment();+ Context& context();+ Selector_List* selector();+ Backtrace* backtrace();++ 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_Quoted*);+ Expression* operator()(String_Constant*);+ // Expression* operator()(Selector_List*);+ Expression* operator()(Media_Query*);+ Expression* operator()(Media_Query_Expression*);+ Expression* operator()(At_Root_Expression*);+ Expression* operator()(Supports_Operator*);+ Expression* operator()(Supports_Negation*);+ Expression* operator()(Supports_Declaration*);+ Expression* operator()(Supports_Interpolation*);+ Expression* operator()(Null*);+ Expression* operator()(Argument*);+ Expression* operator()(Arguments*);+ Expression* operator()(Comment*);++ // these will return selectors+ Selector_List* operator()(Selector_List*);+ Selector_List* operator()(Complex_Selector*);+ Attribute_Selector* operator()(Attribute_Selector*);+ // they don't have any specific implementatio (yet)+ Type_Selector* operator()(Type_Selector* s) { return s; };+ Pseudo_Selector* operator()(Pseudo_Selector* s) { return s; };+ Wrapped_Selector* operator()(Wrapped_Selector* s) { return s; };+ Selector_Qualifier* operator()(Selector_Qualifier* s) { return s; };+ Selector_Placeholder* operator()(Selector_Placeholder* s) { return s; };+ // actual evaluated selectors+ Selector_List* operator()(Selector_Schema*);+ Expression* operator()(Parent_Selector*);++ template <typename U>+ Expression* fallback(U x) { return fallback_impl(x); }++ // -- only need to define two comparisons, and the rest can be implemented in terms of them+ static bool eq(Expression*, Expression*);+ static bool lt(Expression*, Expression*);+ // -- arithmetic on the combinations that matter+ static Value* op_numbers(Memory_Manager&, enum Sass_OP, const Number&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+ static Value* op_number_color(Memory_Manager&, enum Sass_OP, const Number&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+ static Value* op_color_number(Memory_Manager&, enum Sass_OP, const Color&, const Number&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+ static Value* op_colors(Memory_Manager&, enum Sass_OP, const Color&, const Color&, bool compressed = false, int precision = 5, ParserState* pstate = 0);+ static Value* op_strings(Memory_Manager&, enum Sass_OP, Value&, Value&, bool compressed = false, int precision = 5, ParserState* pstate = 0);++ private:+ std::string interpolation(Expression* s, bool into_quotes = false);++ };++ Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate = ParserState("[AST]"));++}++#endif
+ libsass/src/expand.cpp view
@@ -0,0 +1,689 @@+#ifdef _MSC_VER+#pragma warning(disable : 4503)+#endif++#include <iostream>+#include <typeinfo>++#include "expand.hpp"+#include "bind.hpp"+#include "eval.hpp"+#include "to_string.hpp"+#include "backtrace.hpp"+#include "context.hpp"+#include "parser.hpp"++namespace Sass {++ Expand::Expand(Context& ctx, Env* env, Backtrace* bt)+ : ctx(ctx),+ eval(Eval(*this)),+ env_stack(std::vector<Env*>()),+ block_stack(std::vector<Block*>()),+ property_stack(std::vector<String*>()),+ selector_stack(std::vector<Selector_List*>()),+ backtrace_stack(std::vector<Backtrace*>()),+ in_keyframes(false)+ {+ env_stack.push_back(0);+ env_stack.push_back(env);+ block_stack.push_back(0);+ // import_stack.push_back(0);+ property_stack.push_back(0);+ selector_stack.push_back(0);+ backtrace_stack.push_back(0);+ backtrace_stack.push_back(bt);+ }++ Context& Expand::context()+ {+ return ctx;+ }++ Env* Expand::environment()+ {+ if (env_stack.size() > 0)+ return env_stack.back();+ return 0;+ }++ Selector_List* Expand::selector()+ {+ if (selector_stack.size() > 0)+ return selector_stack.back();+ return 0;+ }++ Backtrace* Expand::backtrace()+ {+ if (backtrace_stack.size() > 0)+ return backtrace_stack.back();+ return 0;+ }++ // blocks create new variable scopes+ Statement* Expand::operator()(Block* b)+ {+ // create new local environment+ // set the current env as parent+ Env env(environment());+ // copy the block object (add items later)+ Block* bb = SASS_MEMORY_NEW(ctx.mem, Block,+ b->pstate(),+ b->length(),+ b->is_root());+ // setup block and env stack+ this->block_stack.push_back(bb);+ this->env_stack.push_back(&env);+ // operate on block+ this->append_block(b);+ // revert block and env stack+ this->block_stack.pop_back();+ this->env_stack.pop_back();+ // return copy+ return bb;+ }++ Statement* Expand::operator()(Ruleset* r)+ {+ // reset when leaving scope++ if (in_keyframes) {+ Keyframe_Rule* k = SASS_MEMORY_NEW(ctx.mem, Keyframe_Rule, r->pstate(), r->block()->perform(this)->block());+ if (r->selector()) {+ selector_stack.push_back(0);+ k->selector(static_cast<Selector_List*>(r->selector()->perform(&eval)));+ selector_stack.pop_back();+ }+ return k;+ }++ // do some special checks for the base level rules+ if (r->is_root()) {+ if (Selector_List* selector_list = dynamic_cast<Selector_List*>(r->selector())) {+ for (Complex_Selector* complex_selector : selector_list->elements()) {+ Complex_Selector* tail = complex_selector;+ while (tail) {+ if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) {+ if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others+ To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+ error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace());+ }+ tail = tail->tail();+ }+ }+ }+ }++ Expression* ex = r->selector()->perform(&eval);+ Selector_List* sel = dynamic_cast<Selector_List*>(ex);+ if (sel == 0) throw std::runtime_error("Expanded null selector");++ selector_stack.push_back(sel);+ Block* blk = r->block()->perform(this)->block();+ Ruleset* rr = SASS_MEMORY_NEW(ctx.mem, Ruleset,+ r->pstate(),+ sel,+ blk);+ selector_stack.pop_back();+ rr->tabs(r->tabs());++ return rr;+ }++ Statement* Expand::operator()(Propset* p)+ {+ property_stack.push_back(p->property_fragment());+ Block* expanded_block = p->block()->perform(this)->block();++ for (size_t i = 0, L = expanded_block->length(); i < L; ++i) {+ Statement* stm = (*expanded_block)[i];+ if (Declaration* dec = static_cast<Declaration*>(stm)) {+ String_Schema* combined_prop = SASS_MEMORY_NEW(ctx.mem, String_Schema, p->pstate());+ if (!property_stack.empty()) {+ *combined_prop << property_stack.back()->perform(&eval)+ << SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ p->pstate(), "-")+ << dec->property(); // TODO: eval the prop into a string constant+ }+ else {+ *combined_prop << dec->property();+ }+ dec->property(combined_prop);+ *block_stack.back() << 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()(Supports_Block* f)+ {+ Expression* condition = f->condition()->perform(&eval);+ Supports_Block* ff = SASS_MEMORY_NEW(ctx.mem, Supports_Block,+ f->pstate(),+ static_cast<Supports_Condition*>(condition),+ f->block()->perform(this)->block());+ return ff;+ }++ Statement* Expand::operator()(Media_Block* m)+ {+ To_String to_string(&ctx);+ Expression* mq = m->media_queries()->perform(&eval);+ mq = Parser::from_c_str(mq->perform(&to_string).c_str(), ctx, mq->pstate()).parse_media_queries();+ Media_Block* mm = SASS_MEMORY_NEW(ctx.mem, Media_Block,+ m->pstate(),+ static_cast<List*>(mq),+ m->block()->perform(this)->block(),+ 0);+ mm->tabs(m->tabs());+ return mm;+ }++ Statement* Expand::operator()(At_Root_Block* a)+ {+ Block* ab = a->block();+ // if (ab) ab->is_root(true);+ Expression* ae = a->expression();+ if (ae) ae = ae->perform(&eval);+ else ae = SASS_MEMORY_NEW(ctx.mem, At_Root_Expression, a->pstate());+ Block* bb = ab ? ab->perform(this)->block() : 0;+ At_Root_Block* aa = SASS_MEMORY_NEW(ctx.mem, At_Root_Block,+ a->pstate(),+ bb,+ static_cast<At_Root_Expression*>(ae));+ // aa->block()->is_root(true);+ return aa;+ }++ Statement* Expand::operator()(At_Rule* a)+ {+ LOCAL_FLAG(in_keyframes, a->is_keyframes());+ Block* ab = a->block();+ Selector* as = a->selector();+ Expression* av = a->value();+ selector_stack.push_back(0);+ if (av) av = av->perform(&eval);+ if (as) as = dynamic_cast<Selector*>(as->perform(&eval));+ selector_stack.pop_back();+ Block* bb = ab ? ab->perform(this)->block() : 0;+ At_Rule* aa = SASS_MEMORY_NEW(ctx.mem, At_Rule,+ a->pstate(),+ a->keyword(),+ as,+ bb,+ av);+ return aa;+ }++ Statement* Expand::operator()(Declaration* d)+ {+ String* old_p = d->property();+ String* new_p = static_cast<String*>(old_p->perform(&eval));+ Expression* value = d->value()->perform(&eval);+ if (!value || (value->is_invisible() && !d->is_important())) return 0;+ Declaration* decl = SASS_MEMORY_NEW(ctx.mem, Declaration,+ d->pstate(),+ new_p,+ value,+ d->is_important());+ decl->tabs(d->tabs());+ return decl;+ }++ Statement* Expand::operator()(Assignment* a)+ {+ Env* env = environment();+ std::string var(a->variable());+ if (a->is_global()) {+ if (a->is_default()) {+ if (env->has_global(var)) {+ Expression* e = dynamic_cast<Expression*>(env->get_global(var));+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(&eval));+ }+ }+ else {+ env->set_global(var, a->value()->perform(&eval));+ }+ }+ else {+ env->set_global(var, a->value()->perform(&eval));+ }+ }+ else if (a->is_default()) {+ if (env->has_lexical(var)) {+ auto cur = env;+ while (cur && cur->is_lexical()) {+ if (cur->has_local(var)) {+ if (AST_Node* node = cur->get_local(var)) {+ Expression* e = dynamic_cast<Expression*>(node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ cur->set_local(var, a->value()->perform(&eval));+ }+ }+ else {+ throw std::runtime_error("Env not in sync");+ }+ return 0;+ }+ cur = cur->parent();+ }+ throw std::runtime_error("Env not in sync");+ }+ else if (env->has_global(var)) {+ if (AST_Node* node = env->get_global(var)) {+ Expression* e = dynamic_cast<Expression*>(node);+ if (!e || e->concrete_type() == Expression::NULL_VAL) {+ env->set_global(var, a->value()->perform(&eval));+ }+ }+ }+ else if (env->is_lexical()) {+ env->set_local(var, a->value()->perform(&eval));+ }+ else {+ env->set_local(var, a->value()->perform(&eval));+ }+ }+ else {+ env->set_lexical(var, a->value()->perform(&eval));+ }+ return 0;+ }++ Statement* Expand::operator()(Import* imp)+ {+ Import* result = SASS_MEMORY_NEW(ctx.mem, Import, imp->pstate());+ if (imp->media_queries() && imp->media_queries()->size()) {+ Expression* ex = imp->media_queries()->perform(&eval);+ result->media_queries(dynamic_cast<List*>(ex));+ }+ for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {+ result->urls().push_back(imp->urls()[i]->perform(&eval));+ }+ // all resources have been dropped for Input_Stubs+ // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {}+ return result;+ }++ Statement* Expand::operator()(Import_Stub* i)+ {+ // we don't seem to need that actually afterall+ Sass_Import_Entry import = sass_make_import(+ i->imp_path().c_str(),+ i->abs_path().c_str(),+ 0, 0+ );+ ctx.import_stack.push_back(import);+ const std::string& abs_path(i->resource().abs_path);+ append_block(ctx.sheets.at(abs_path).root);+ sass_delete_import(ctx.import_stack.back());+ ctx.import_stack.pop_back();+ return 0;+ }++ Statement* Expand::operator()(Warning* w)+ {+ // eval handles this too, because warnings may occur in functions+ w->perform(&eval);+ return 0;+ }++ Statement* Expand::operator()(Error* e)+ {+ // eval handles this too, because errors may occur in functions+ e->perform(&eval);+ return 0;+ }++ Statement* Expand::operator()(Debug* d)+ {+ // eval handles this too, because warnings may occur in functions+ d->perform(&eval);+ return 0;+ }++ Statement* Expand::operator()(Comment* c)+ {+ // TODO: eval the text, once we're parsing/storing it as a String_Schema+ return SASS_MEMORY_NEW(ctx.mem, Comment, c->pstate(), static_cast<String*>(c->text()->perform(&eval)), c->is_important());+ }++ Statement* Expand::operator()(If* i)+ {+ if (*i->predicate()->perform(&eval)) {+ append_block(i->block());+ }+ 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)+ {+ std::string variable(f->variable());+ Expression* low = f->lower_bound()->perform(&eval);+ if (low->concrete_type() != Expression::NUMBER) {+ error("lower bound of `@for` directive must be numeric", low->pstate(), backtrace());+ }+ Expression* high = f->upper_bound()->perform(&eval);+ if (high->concrete_type() != Expression::NUMBER) {+ error("upper bound of `@for` directive must be numeric", high->pstate(), backtrace());+ }+ 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()) {+ std::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+ Env* env = environment();+ Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());+ AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;+ env->set_local(variable, it);+ 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)+ {+ std::vector<std::string> variables(e->variables());+ Expression* expr = e->list()->perform(&eval);+ 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 = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);+ *list << expr;+ }+ else {+ list = static_cast<List*>(expr);+ }+ // remember variables and then reset them+ Env* env = environment();+ std::vector<AST_Node*> old_vars(variables.size());+ for (size_t i = 0, L = variables.size(); i < L; ++i) {+ old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;+ env->set_local(variables[i], 0);+ }+ Block* body = e->block();++ if (map) {+ for (auto key : map->keys()) {+ Expression* k = key->perform(&eval);+ Expression* v = map->at(key)->perform(&eval);++ if (variables.size() == 1) {+ List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_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 {+ bool arglist = list->is_arglist();+ for (size_t i = 0, L = list->length(); i < L; ++i) {+ Expression* e = (*list)[i];+ // unwrap value if the expression is an argument+ if (Argument* arg = dynamic_cast<Argument*>(e)) e = arg->value();+ // check if we got passed a list of args (investigate)+ if (List* scalars = dynamic_cast<List*>(e)) {+ if (variables.size() == 1) {+ Expression* var = scalars;+ if (arglist) var = (*scalars)[0];+ env->set_local(variables[0], var);+ } else {+ for (size_t j = 0, K = variables.size(); j < K; ++j) {+ Expression* res = j >= scalars->length()+ ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())+ : (*scalars)[j]->perform(&eval);+ env->set_local(variables[j], res);+ }+ }+ } else {+ if (variables.size() > 0) {+ env->set_local(variables[0], e);+ for (size_t j = 1, K = variables.size(); j < K; ++j) {+ Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());+ env->set_local(variables[j], res);+ }+ }+ }+ 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)) {+ 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 = dynamic_cast<Selector_List*>(selector());+ if (!extender) return 0;+ selector_stack.push_back(0);++ if (Selector_List* selector_list = dynamic_cast<Selector_List*>(e->selector())) {+ for (Complex_Selector* complex_selector : selector_list->elements()) {+ Complex_Selector* tail = complex_selector;+ while (tail) {+ if (tail->head()) for (Simple_Selector* header : tail->head()->elements()) {+ if (dynamic_cast<Parent_Selector*>(header) == NULL) continue; // skip all others+ To_String to_string(&ctx); std::string sel_str(complex_selector->perform(&to_string));+ error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace());+ }+ tail = tail->tail();+ }+ }+ }++ Selector_List* contextualized = dynamic_cast<Selector_List*>(e->selector()->perform(&eval));+ if (contextualized == NULL) return 0;+ for (auto complex_sel : contextualized->elements()) {+ Complex_Selector* c = complex_sel;+ if (!c->head() || c->tail()) {+ To_String to_string(&ctx); std::string sel_str(contextualized->perform(&to_string));+ error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace());+ }+ Compound_Selector* placeholder = c->head();+ placeholder->is_optional(e->selector()->is_optional());+ for (size_t i = 0, L = extender->length(); i < L; ++i) {+ Complex_Selector* sel = (*extender)[i];+ if (!(sel->head() && sel->head()->length() > 0 &&+ dynamic_cast<Parent_Selector*>((*sel->head())[0])))+ {+ Compound_Selector* hh = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, (*extender)[i]->pstate());+ hh->media_block((*extender)[i]->media_block());+ Complex_Selector* ssel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, (*extender)[i]->pstate());+ ssel->media_block((*extender)[i]->media_block());+ if (sel->has_line_feed()) ssel->has_line_feed(true);+ Parent_Selector* ps = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, (*extender)[i]->pstate());+ ps->media_block((*extender)[i]->media_block());+ *hh << ps;+ ssel->tail(sel);+ ssel->head(hh);+ sel = ssel;+ }+ // if (c->has_line_feed()) sel->has_line_feed(true);+ ctx.subset_map.put(placeholder->to_str_vec(), std::make_pair(sel, placeholder));+ }+ }++ selector_stack.pop_back();++ return 0;+ }++ Statement* Expand::operator()(Definition* d)+ {+ Env* env = environment();+ Definition* dd = SASS_MEMORY_NEW(ctx.mem, Definition, *d);+ env->local_frame()[d->name() ++ (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;++ if (d->type() == Definition::FUNCTION && (+ d->name() == "calc" ||+ d->name() == "element" ||+ d->name() == "expression" ||+ d->name() == "url"+ )) {+ deprecated(+ "Naming a function \"" + d->name() + "\" is disallowed",+ "This name conflicts with an existing CSS function with special parse rules.",+ d->pstate()+ );+ }+++ // set the static link so we can have lexical scoping+ dd->environment(env);+ return 0;+ }++ Statement* Expand::operator()(Mixin_Call* c)+ {+ Env* env = environment();+ std::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();++ if (c->block() && c->name() != "@content" && !body->has_content()) {+ error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace());+ }+ Arguments* args = static_cast<Arguments*>(c->arguments()+ ->perform(&eval));+ Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`");+ backtrace_stack.push_back(&new_bt);+ Env new_env(def->environment());+ env_stack.push_back(&new_env);+ if (c->block()) {+ // represent mixin content blocks as thunks/closures+ Definition* thunk = SASS_MEMORY_NEW(ctx.mem, Definition,+ c->pstate(),+ "@content",+ SASS_MEMORY_NEW(ctx.mem, Parameters, c->pstate()),+ c->block(),+ Definition::MIXIN);+ thunk->environment(env);+ new_env.local_frame()["@content[m]"] = thunk;+ }++ bind(std::string("Mixin"), c->name(), params, args, &ctx, &new_env, &eval);+ append_block(body);+ backtrace_stack.pop_back();+ env_stack.pop_back();+ return 0;+ }++ Statement* Expand::operator()(Content* c)+ {+ Env* env = environment();+ // convert @content directives into mixin calls to the underlying thunk+ if (!env->has("@content[m]")) return 0;+ if (block_stack.back()->is_root()) {+ selector_stack.push_back(0);+ }+ Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call,+ c->pstate(),+ "@content",+ SASS_MEMORY_NEW(ctx.mem, Arguments, c->pstate()));+ Statement* stm = call->perform(this);+ if (block_stack.back()->is_root()) {+ selector_stack.pop_back();+ }+ return stm;+ }++ // produce an error if something is not implemented+ inline Statement* Expand::fallback_impl(AST_Node* n)+ {+ std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();+ String_Quoted* msg = SASS_MEMORY_NEW(ctx.mem, String_Quoted, ParserState("[WARN]"), err);+ error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace());+ return SASS_MEMORY_NEW(ctx.mem, Warning, ParserState("[WARN]"), msg);+ }++ // process and add to last block on stack+ inline void Expand::append_block(Block* b)+ {+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* ith = (*b)[i]->perform(this);+ if (ith) *block_stack.back() << ith;+ }+ }++}
+ libsass/src/expand.hpp view
@@ -0,0 +1,79 @@+#ifndef SASS_EXPAND_H+#define SASS_EXPAND_H++#include <vector>++#include "ast.hpp"+#include "eval.hpp"+#include "operation.hpp"+#include "environment.hpp"++namespace Sass {++ class Listize;+ class Context;+ class Eval;+ typedef Environment<AST_Node*> Env;+ struct Backtrace;++ class Expand : public Operation_CRTP<Statement*, Expand> {+ public:++ Env* environment();+ Context& context();+ Selector_List* selector();+ Backtrace* backtrace();++ Context& ctx;+ Eval eval;++ // it's easier to work with vectors+ std::vector<Env*> env_stack;+ std::vector<Block*> block_stack;+ std::vector<String*> property_stack;+ std::vector<Selector_List*> selector_stack;+ std::vector<Backtrace*>backtrace_stack;+ bool in_keyframes;++ Statement* fallback_impl(AST_Node* n);++ public:+ Expand(Context&, Env*, Backtrace*);+ virtual ~Expand() { }++ using Operation<Statement*>::operator();++ Statement* operator()(Block*);+ Statement* operator()(Ruleset*);+ Statement* operator()(Propset*);+ Statement* operator()(Media_Block*);+ Statement* operator()(Supports_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/src/extend.cpp view
@@ -0,0 +1,2069 @@+#ifdef _MSC_VER+#pragma warning(disable : 4503)+#endif++#include "extend.hpp"+#include "context.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>+#include <set>+++/*+ 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 subset_map 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 std::pair<Complex_Selector*, Compound_Selector*> ExtensionPair;+ typedef std::vector<ExtensionPair> SubsetMapEntries;++#ifdef DEBUG++ // TODO: move the ast specific ostream operators into ast.hpp/ast.cpp+ std::ostream& operator<<(std::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;+ case Complex_Selector::REFERENCE: os << "\"/\""; break;+ }++ return os;+ }+++ std::ostream& operator<<(std::ostream& os, Compound_Selector& compoundSelector) {+ To_String to_string;+ for (size_t i = 0, L = compoundSelector.length(); i < L; ++i) {+ if (i > 0) os << ", ";+ os << compoundSelector[i]->perform(&to_string);+ }+ return os;+ }++ std::ostream& operator<<(std::ostream& os, Simple_Selector& simpleSelector) {+ To_String to_string;+ os << simpleSelector.perform(&to_string);+ return os;+ }++ // Print a string representation of a Compound_Selector+ static void printSimpleSelector(Simple_Selector* pSimpleSelector, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ std::cerr << message;+ }++ if (pSimpleSelector) {+ std::cerr << "[" << *pSimpleSelector << "]";+ } else {+ std::cerr << "NULL";+ }++ if (newline) {+ std::cerr << std::endl;+ }+ }++ // Print a string representation of a Compound_Selector+ typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+ typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection;+++ // Print a string representation of a Compound_Selector+ static void printCompoundSelector(Compound_Selector* pCompoundSelector, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ std::cerr << message;+ }++ if (pCompoundSelector) {+ std::cerr << "[" << *pCompoundSelector << "]";+ } else {+ std::cerr << "NULL";+ }++ if (newline) {+ std::cerr << std::endl;+ }+ }+++ std::ostream& operator<<(std::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) {+ std::cerr << message;+ }++ if (pComplexSelector) {+ std::cerr << *pComplexSelector;+ } else {+ std::cerr << "NULL";+ }++ if (newline) {+ std::cerr << std::endl;+ }+ }++ static void printSelsNewSeqPairCollection(SelsNewSeqPairCollection& collection, const char* message=NULL, bool newline=true) {+ To_String to_string;++ if (message) {+ std::cerr << message;+ }+ bool first = true;+ std::cerr << "[";+ for(SelsNewSeqPair& pair : collection) {+ if (first) {+ first = false;+ } else {+ std::cerr << ", ";+ }+ std::cerr << "[";+ Compound_Selector* pSels = pair.first;+ Complex_Selector* pNewSelector = pair.second;+ std::cerr << "[" << *pSels << "], ";+ printComplexSelector(pNewSelector, NULL, false);+ }+ std::cerr << "]";++ if (newline) {+ std::cerr << std::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) {+ std::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 std::deque<std::string> SourceStrings;+ SourceStrings sourceStrings;+ for (SourcesSet::iterator iterator = sources.begin(), iteratorEnd = sources.end(); iterator != iteratorEnd; ++iterator) {+ Complex_Selector* pSource = *iterator;+ std::stringstream sstream;+ sstream << complexSelectorToNode(pSource, ctx);+ sourceStrings.push_back(sstream.str());+ }++ // Sort to get consistent output+ std::sort(sourceStrings.begin(), sourceStrings.end());++ std::cerr << "SourcesSet[";+ for (SourceStrings::iterator iterator = sourceStrings.begin(), iteratorEnd = sourceStrings.end(); iterator != iteratorEnd; ++iterator) {+ std::string source = *iterator;+ if (iterator != sourceStrings.begin()) {+ std::cerr << ", ";+ }+ std::cerr << source;+ }+ std::cerr << "]";++ if (newline) {+ std::cerr << std::endl;+ }+ }+++ std::ostream& operator<<(std::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) {+ std::cerr << *pExtComplexSelector;+ } else {+ std::cerr << "NULL";+ }++ os << " -> ";++ if (pExtCompoundSelector) {+ std::cerr << *pExtCompoundSelector;+ } else {+ std::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(), std::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] = std::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, bool isReplace) {+ // 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();+ tempResult.got_line_feed = seqs1.got_line_feed;++ 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 = isReplace ? pSeq1->specificity() : 0;+ SourcesSet sources = pSeq1->sources();++ DEBUG_PRINTLN(TRIM, "TRIM SEQ1: " << seq1)+ DEBUG_EXEC(TRIM, printSourcesSet(sources, ctx, "TRIM SOURCES: "))++ for (SourcesSet::iterator sourcesSetIterator = sources.begin(), sourcesSetIteratorEnd = sources.end(); sourcesSetIterator != sourcesSetIteratorEnd; ++sourcesSetIterator) {+ const Complex_Selector* const pCurrentSelector = *sourcesSetIterator;+ maxSpecificity = std::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)}+ if (seq.collection()->size() == 0) return false;+ 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 (seq1.collection()->size() && !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);++ seq1.collection()->push_back(sel1);+ seq1.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+ */+ Node Extend::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);++ if (groupSeq1.collection()->size()) groupSeq1.collection()->pop_front();+ if (groupSeq2.collection()->size()) 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 = Extend::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& subset_map,+ std::set<Compound_Selector> seen, bool isReplace, bool isOriginal);++++ /*+ 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& subset_map,+ std::set<Compound_Selector> seen, bool isReplace) {++ DEBUG_EXEC(EXTEND_COMPOUND, printCompoundSelector(pSelector, "EXTEND COMPOUND: "))+ // TODO: Ruby has another loop here to skip certain members?++ Node extendedSelectors = Node::createCollection();+ // extendedSelectors.got_line_feed = true;++ To_String to_string;++ SubsetMapEntries entries = subset_map.get_v(pSelector->to_str_vec());++ typedef std::vector<std::pair<Complex_Selector, std::vector<ExtensionPair> > > GroupedByToAResult;++ GroupByToAFunctor<Complex_Selector> extPairKeyFunctor;+ GroupedByToAResult arr;+ group_by_to_a(entries, extPairKeyFunctor, arr);++ typedef std::pair<Compound_Selector*, Complex_Selector*> SelsNewSeqPair;+ typedef std::vector<SelsNewSeqPair> SelsNewSeqPairCollection;+++ SelsNewSeqPairCollection holder;+++ for (GroupedByToAResult::iterator groupedIter = arr.begin(), groupedIterEnd = arr.end(); groupedIter != groupedIterEnd; groupedIter++) {+ std::pair<Complex_Selector, std::vector<ExtensionPair> >& groupedPair = *groupedIter;++ Complex_Selector& seq = groupedPair.first;+ std::vector<ExtensionPair>& group = groupedPair.second;++ DEBUG_EXEC(EXTEND_COMPOUND, printComplexSelector(&seq, "SEQ: "))+++ Compound_Selector* pSels = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pSelector->pstate());+ for (std::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?+ // RUBY: self_without_sel = Sass::Util.array_minus(members, sels)+ 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->last()->head();+ Compound_Selector* pUnifiedSelector = NULL;++ if (!pInnermostCompoundSelector) {+ pInnermostCompoundSelector = SASS_MEMORY_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: "))++ // RUBY: next unless 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); // ->first();++ Complex_Selector* pNewInnerMost = SASS_MEMORY_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()) pNewInnerMost->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: "))++ // RUBY: new_seq.add_sources!(sources + [seq])+ 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: "))+++ if (pSels->has_line_feed()) pNewSelector->has_line_feed(true);;++ holder.push_back(std::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;+++ // RUBY??: next [] if seen.include?(sels)+ if (seen.find(*pSels) != seen.end()) {+ continue;+ }+++ std::set<Compound_Selector> recurseSeen(seen);+ recurseSeen.insert(*pSels);+++ DEBUG_PRINTLN(EXTEND_COMPOUND, "RECURSING DO EXTEND: " << complexSelectorToNode(pNewSelector, ctx))+ Node recurseExtendedSelectors = extendComplexSelector(pNewSelector, ctx, subset_map, recurseSeen, isReplace, false); // !:isOriginal++ 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& subset_map) {++ bool hasExtension = false;++ Complex_Selector* pIter = pComplexSelector;++ while (!hasExtension && pIter) {+ Compound_Selector* pHead = pIter->head();++ if (pHead) {+ SubsetMapEntries entries = subset_map.get_v(pHead->to_str_vec());+ for (ExtensionPair ext : entries) {+ // check if both selectors have the same media block parent+ if (ext.first->media_block() == pComplexSelector->media_block()) continue;+ To_String to_string(&ctx);+ if (ext.second->media_block() == 0) continue;+ if (pComplexSelector->media_block() &&+ ext.second->media_block()->media_queries() &&+ pComplexSelector->media_block()->media_queries()+ ) {+ std::string query_left(ext.second->media_block()->media_queries()->perform(&to_string));+ std::string query_right(pComplexSelector->media_block()->media_queries()->perform(&to_string));+ if (query_left == query_right) continue;+ }++ // fail if one goes across media block boundaries+ std::stringstream err;+ std::string cwd(Sass::File::get_cwd());+ ParserState pstate(ext.second->pstate());+ std::string rel_path(Sass::File::abs2rel(pstate.path, cwd, cwd));+ err << "You may not @extend an outer selector from within @media.\n";+ err << "You may only @extend selectors within the same directive.\n";+ err << "From \"@extend " << ext.second->perform(&to_string) << "\"";+ err << " 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+ std::stringstream err;+ To_String to_string(&ctx);+ std::string cwd(Sass::File::get_cwd());+ std::string sel1(pComplexSelector->perform(&to_string));+ Compound_Selector* pExtendSelector = 0;+ for (auto i : subset_map.values()) {+ if (i.first == pComplexSelector) {+ pExtendSelector = i.second;+ break;+ }+ }+ if (!pExtendSelector || !pExtendSelector->is_optional()) {+ std::string sel2(pExtendSelector ? pExtendSelector->perform(&to_string) : "[unknown]");+ err << "\"" << sel1 << "\" failed to @extend \"" << sel2 << "\"\n";+ err << "The selector \"" << sel2 << "\" was not found.\n";+ err << "Use \"@extend " << sel2 << " !optional\" if the extend should be able to fail.";+ error(err.str(), pExtendSelector ? pExtendSelector->pstate() : pComplexSelector->pstate());+ }+ */+ }++ return hasExtension;+ }+++ /*+ 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& subset_map,+ std::set<Compound_Selector> seen, bool isReplace, bool isOriginal) {++ 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+ // RUBY: next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)+ 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();++ // RUBY: extended = sseq_or_op.do_extend(extends, parent_directives, replace, seen)+ Node extended = extendCompoundSelector(pCompoundSelector, ctx, subset_map, seen, isReplace);+ if (sseqOrOp.got_line_feed) extended.got_line_feed = true;+ 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();++ // RUBY: extended.first.add_sources!([self]) if original && !has_placeholder?+ if (isOriginal && !pComplexSelector->has_placeholder()) {+ SourcesSet srcset;+ srcset.insert(pComplexSelector);+ pJustCurrentCompoundSelector->addSources(srcset, ctx);+ DEBUG_PRINTLN(EXTEND_COMPLEX, "ADD SOURCES: " << *pComplexSelector)+ }++ 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) {+ if (sseqOrOp.got_line_feed) pJustCurrentCompoundSelector->has_line_feed(sseqOrOp.got_line_feed);+ 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);+ weaved.got_line_feed = path.got_line_feed;+ weaves.collection()->push_back(weaved);+ }++ DEBUG_PRINTLN(EXTEND_COMPLEX, "WEAVES: " << weaves)++++ // Ruby Equivalent: trim+ Node trimmed = trim(weaves, ctx, isReplace);++ 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.+ */+ Selector_List* Extend::extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething) {++ To_String to_string(&ctx);++ Selector_List* pNewSelectors = SASS_MEMORY_NEW(ctx.mem, Selector_List, pSelectorList->pstate(), pSelectorList->length());++ extendedSomething = false;++ 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, subset_map)) {+ *pNewSelectors << pSelector;+ continue;+ }++ extendedSomething = true;++ std::set<Compound_Selector> seen;++ Node extendedSelectors = extendComplexSelector(pSelector, ctx, subset_map, seen, isReplace, true);+ if (!pSelector->has_placeholder()) {+ if (!extendedSelectors.contains(complexSelectorToNode(pSelector, ctx), true /*simpleSelectorOrderDependent*/)) {+ *pNewSelectors << pSelector;+ }+ }++ for (NodeDeque::iterator iterator = extendedSelectors.collection()->begin(), iteratorBegin = extendedSelectors.collection()->begin(), iteratorEnd = extendedSelectors.collection()->end(); iterator != iteratorEnd; ++iterator) {+ // When it is a replace, skip the first one, unless there is only one+ if(isReplace && iterator == iteratorBegin && extendedSelectors.collection()->size() > 1 ) continue;++ 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& subset_map) {+ 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 = Extend::extendSelectorList(static_cast<Selector_List*>(pObject->selector()), ctx, subset_map, false, 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))+ pNewSelectorList->remove_parent_selectors();+ pObject->selector(pNewSelectorList);+ } 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()(Supports_Block* pFeatureBlock)+ {+ pFeatureBlock->block()->perform(this);+ }++ void Extend::operator()(Media_Block* pMediaBlock)+ {+ pMediaBlock->block()->perform(this);+ }++ void Extend::operator()(At_Rule* a)+ {+ // Selector_List* ls = dynamic_cast<Selector_List*>(a->selector());+ // selector_stack.push_back(ls);+ if (a->block()) a->block()->perform(this);+ // exp.selector_stack.pop_back();+ }+}
+ libsass/src/extend.hpp view
@@ -0,0 +1,44 @@+#ifndef SASS_EXTEND_H+#define SASS_EXTEND_H++#include <string>++#include "ast.hpp"+#include "operation.hpp"+#include "subset_map.hpp"++namespace Sass {++ class Context;+ class Node;++ typedef Subset_Map<std::string, std::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:+ static Node subweave(Node& one, Node& two, Context& ctx);+ static Selector_List* extendSelectorList(Selector_List* pSelectorList, Context& ctx, ExtensionSubsetMap& subset_map, bool isReplace, bool& extendedSomething);+ Extend(Context&, ExtensionSubsetMap&);+ virtual ~Extend() { }++ using Operation<void>::operator();++ void operator()(Block*);+ void operator()(Ruleset*);+ void operator()(Supports_Block*);+ void operator()(Media_Block*);+ void operator()(At_Rule*);++ template <typename U>+ void fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/src/file.cpp view
@@ -0,0 +1,407 @@+#ifdef _WIN32+#ifdef __MINGW32__+#ifndef off64_t+#define off64_t _off64_t /* Workaround for http://sourceforge.net/p/mingw/bugs/2024/ */+#endif+#endif+#include <direct.h>+#define getcwd _getcwd+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)+#else+#include <unistd.h>+#endif+#ifdef _MSC_VER+#define NOMINMAX+#endif++#include <iostream>+#include <fstream>+#include <cctype>+#include <vector>+#include <algorithm>+#include <sys/stat.h>+#include "file.hpp"+#include "context.hpp"+#include "prelexer.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 {++ // return the current directory+ // always with forward slashes+ std::string get_cwd()+ {+ const size_t wd_len = 1024;+ char wd[wd_len];+ std::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 std::string& path)+ {+ #ifdef _WIN32+ std::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 std::string& path)+ {+ #ifdef _WIN32+ if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true;+ #endif+ size_t i = 0;+ // check if we have a protocol+ if (path[i] && Prelexer::is_alpha(path[i])) {+ // skip over all alphanumeric characters+ while (path[i] && Prelexer::is_alnum(path[i])) ++i;+ i = i && path[i] == ':' ? i + 1 : 0;+ }+ return path[i] == '/';+ }++ // helper function to find the last directory seperator+ inline size_t find_last_folder_separator(const std::string& path, size_t limit = std::string::npos)+ {+ size_t pos = std::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 = std::string::npos;+ #endif+ if (pos_p != std::string::npos && pos_w != std::string::npos) {+ pos = std::max(pos_p, pos_w);+ }+ else if (pos_p != std::string::npos) {+ pos = pos_p;+ }+ else {+ pos = pos_w;+ }+ return pos;+ }++ // return only the directory part of path+ std::string dir_name(const std::string& path)+ {+ size_t pos = find_last_folder_separator(path);+ if (pos == std::string::npos) return "";+ else return path.substr(0, pos+1);+ }++ // return only the filename part of path+ std::string base_name(const std::string& path)+ {+ size_t pos = find_last_folder_separator(path);+ if (pos == std::string::npos) return path;+ else return path.substr(pos+1);+ }++ // do a logical clean up of the path+ // no physical check on the filesystem+ std::string make_canonical_path (std::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)) != std::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);+++ size_t proto = 0;+ // check if we have a protocol+ if (path[proto] && Prelexer::is_alpha(path[proto])) {+ // skip over all alphanumeric characters+ while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+ // then skip over the mandatory colon+ if (proto && path[proto] == ':') ++ proto;+ }++ // then skip over start slashes+ while (path[proto++] == '/') {}++ pos = proto; // collapse multiple delimiters into a single one+ while((pos = path.find("//", pos)) != std::string::npos) path.erase(pos, 1);++ return path;++ }++ // join two path segments cleanly together+ // but only if right side is not absolute yet+ std::string join_paths(std::string l, std::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 == std::string::npos ? pos : pos + 1);+ }++ return l + r;+ }++ std::string path_for_console(const std::string& rel_path, const std::string& abs_path, const std::string& orig_path)+ {+ // magic algorith goes here!!++ // if the file is outside this directory show the absolute path+ if (rel_path.substr(0, 3) == "../") {+ return orig_path;+ }+ // this seems to work most of the time+ return abs_path == orig_path ? abs_path : rel_path;+ }++ // create an absolute path by resolving relative paths with cwd+ std::string rel2abs(const std::string& path, const std::string& base, const std::string& cwd)+ {+ return make_canonical_path(join_paths(join_paths(cwd, base), 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+ std::string abs2rel(const std::string& path, const std::string& base, const std::string& cwd)+ {++ std::string abs_path = rel2abs(path, cwd);+ std::string abs_base = rel2abs(base, cwd);++ size_t proto = 0;+ // check if we have a protocol+ if (path[proto] && Prelexer::is_alpha(path[proto])) {+ // skip over all alphanumeric characters+ while (path[proto] && Prelexer::is_alnum(path[proto++])) {}+ // then skip over the mandatory colon+ if (proto && path[proto] == ':') ++ proto;+ }++ // distinguish between windows absolute paths and valid protocols+ // we assume that protocols must at least have two chars to be valid+ if (proto && path[proto++] == '/' && proto > 3) return path;++ #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 (abs_base[0] != abs_path[0]) return abs_path;+ #endif++ std::string stripped_uri = "";+ std::string stripped_base = "";++ size_t index = 0;+ size_t minSize = std::min(abs_path.size(), abs_base.size());+ for (size_t i = 0; i < minSize; ++i) {+ #ifdef FS_CASE_SENSITIVE+ if (abs_path[i] != abs_base[i]) break;+ #else+ // compare the charactes in a case insensitive manner+ // windows fs is only case insensitive in ascii ranges+ if (tolower(abs_path[i]) != tolower(abs_base[i])) break;+ #endif+ if (abs_path[i] == '/') index = i + 1;+ }+ for (size_t i = index; i < abs_path.size(); ++i) {+ stripped_uri += abs_path[i];+ }+ for (size_t i = index; i < abs_base.size(); ++i) {+ stripped_base += abs_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;+ }+ }++ std::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+ std::vector<Include> resolve_includes(const std::string& root, const std::string& file)+ {+ std::string filename = join_paths(root, file);+ // supported extensions+ const std::vector<std::string> exts = {+ ".scss", ".sass", ".css"+ };+ // split the filename+ std::string base(dir_name(file));+ std::string name(base_name(file));+ std::vector<Include> includes;+ // create full path (maybe relative)+ std::string rel_path(join_paths(base, name));+ std::string abs_path(join_paths(root, rel_path));+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ // next test variation with underscore+ rel_path = join_paths(base, "_" + name);+ abs_path = join_paths(root, rel_path);+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ // next test exts plus underscore+ for(auto ext : exts) {+ rel_path = join_paths(base, "_" + name + ext);+ abs_path = join_paths(root, rel_path);+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ }+ // next test plain name with exts+ for(auto ext : exts) {+ rel_path = join_paths(base, name + ext);+ abs_path = join_paths(root, rel_path);+ if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });+ }+ // nothing found+ return includes;+ }++ // helper function to resolve a filename+ std::string find_file(const std::string& file, const std::vector<std::string> paths)+ {+ // search in every include path for a match+ for (size_t i = 0, S = paths.size(); i < S; ++i)+ {+ std::vector<Include> resolved(resolve_includes(paths[i], file));+ if (resolved.size()) return resolved[0].abs_path;+ }+ // nothing found+ return std::string("");+ }++ // inc paths can be directly passed from C code+ std::string find_file(const std::string& file, const char* paths[])+ {+ if (paths == 0) return std::string("");+ std::vector<std::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 std::string& path)+ {+ #ifdef _WIN32+ BYTE* pBuffer;+ DWORD dwBytes;+ // windows unicode filepaths are encoded in utf16+ std::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;+ std::ifstream file(path.c_str(), std::ios::in | std::ios::binary | std::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, std::ios::beg);+ file.read(contents, size);+ contents[size] = '\0';+ file.close();+ }+ #endif+ std::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/src/file.hpp view
@@ -0,0 +1,123 @@+#ifndef SASS_FILE_H+#define SASS_FILE_H++#include <string>+#include <vector>++namespace Sass {++ class Block;+ class Context;++ namespace File {++ // return the current directory+ // always with forward slashes+ std::string get_cwd();++ // test if path exists and is a file+ bool file_exists(const std::string& file);++ // return if given path is absolute+ // works with *nix and windows paths+ bool is_absolute_path(const std::string& path);++ // return only the directory part of path+ std::string dir_name(const std::string& path);++ // return only the filename part of path+ std::string base_name(const std::string&);++ // do a locigal clean up of the path+ // no physical check on the filesystem+ std::string make_canonical_path (std::string path);++ // join two path segments cleanly together+ // but only if right side is not absolute yet+ std::string join_paths(std::string root, std::string name);++ // if the relative path is outside of the cwd we want want to+ // show the absolute path in console messages+ std::string path_for_console(const std::string& rel_path, const std::string& abs_path, const std::string& orig_path);++ // create an absolute path by resolving relative paths with cwd+ std::string rel2abs(const std::string& path, const std::string& base = ".", const std::string& cwd = get_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+ std::string abs2rel(const std::string& path, const std::string& base = ".", const std::string& cwd = get_cwd());++ // helper function to resolve a filename+ std::string find_file(const std::string& file, const std::vector<std::string> paths);+ // inc paths can be directly passed from C code+ std::string find_file(const std::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 std::string& file);++ }++ // requested import+ class Importer {+ public:+ // requested import path+ std::string imp_path;+ // parent context path+ std::string ctx_path;+ // base derived from context path+ // this really just acts as a cache+ std::string base_path;+ public:+ Importer(std::string imp_path, std::string ctx_path)+ : imp_path(File::make_canonical_path(imp_path)),+ ctx_path(File::make_canonical_path(ctx_path)),+ base_path(File::dir_name(ctx_path))+ { }+ };++ // a resolved include (final import)+ class Include : public Importer {+ public:+ // resolved absolute path+ std::string abs_path;+ public:+ Include(const Importer& imp, std::string abs_path)+ : Importer(imp), abs_path(abs_path)+ { }+ };++ // a loaded resource+ class Resource {+ public:+ // the file contents+ char* contents;+ // conected sourcemap+ char* srcmap;+ public:+ Resource(char* contents, char* srcmap)+ : contents(contents), srcmap(srcmap)+ { }+ };++ // parsed stylesheet from loaded resource+ class StyleSheet : public Resource {+ public:+ // parsed root block+ Block* root;+ public:+ StyleSheet(const Resource& res, Block* root)+ : Resource(res), root(root)+ { }+ };++ namespace File {++ std::vector<Include> resolve_includes(const std::string& root, const std::string& file);++ }++}++#endif
+ libsass/src/functions.cpp view
@@ -0,0 +1,1923 @@+#include "functions.hpp"+#include "ast.hpp"+#include "context.hpp"+#include "backtrace.hpp"+#include "parser.hpp"+#include "constants.hpp"+#include "to_string.hpp"+#include "inspect.hpp"+#include "extend.hpp"+#include "eval.hpp"+#include "util.hpp"+#include "expand.hpp"+#include "utf8_string.hpp"+#include "sass/base.h"+#include "utf8.h"++#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>();+ std::string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters* params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(ctx.mem, Definition,+ ParserState("[built-in function]"),+ sig,+ name,+ params,+ func,+ false);+ }++ Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)+ {+ using namespace Prelexer;++ 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 >+ > >();+ std::string name(Util::normalize_underscores(sig_parser.lexed));+ Parameters* params = sig_parser.parse_parameters();+ return SASS_MEMORY_NEW(ctx.mem, Definition,+ ParserState("[c function]"),+ sig,+ name,+ params,+ c_func,+ false, true);+ }++ std::string function_name(Signature sig)+ {+ std::string str(sig);+ return str.substr(0, str.find('('));+ }++ namespace Functions {++ inline void handle_utf8_error (const ParserState& pstate, Backtrace* backtrace)+ {+ try {+ throw;+ }+ catch (utf8::invalid_code_point) {+ std::string msg("utf8::invalid_code_point");+ error(msg, pstate, backtrace);+ }+ catch (utf8::not_enough_room) {+ std::string msg("utf8::not_enough_room");+ error(msg, pstate, backtrace);+ }+ catch (utf8::invalid_utf8) {+ std::string msg("utf8::invalid_utf8");+ error(msg, pstate, backtrace);+ }+ catch (...) { throw; }+ }++ template <typename T>+ T* get_arg(const std::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) {+ std::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 std::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 SASS_MEMORY_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 std::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)) {+ std::stringstream msg;+ msg << "argument `" << argname << "` of `" << sig << "` must be between ";+ msg << lo << " and " << hi;+ error(msg.str(), pstate, backtrace);+ }+ return val;+ }++ #define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, backtrace, ctx)++ template <typename T>+ T* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);++ template <>+ Selector_List* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ To_String to_string(&ctx, false);+ Expression* exp = ARG(argname, Expression);+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << argname << ": null is not a valid selector: it must be a string,\n";+ msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";+ error(msg.str(), pstate);+ }+ std::string exp_src = exp->perform(&to_string) + "{";+ return Parser::parse_selector(exp_src.c_str(), ctx);+ }++ template <>+ Complex_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ To_String to_string(&ctx, false);+ Expression* exp = ARG(argname, Expression);+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << argname << ": null is not a valid selector: it must be a string,\n";+ msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";+ error(msg.str(), pstate);+ }+ std::string exp_src = exp->perform(&to_string) + "{";+ Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);+ return (sel_list->length() > 0) ? sel_list->first() : 0;+ }++ template <>+ Compound_Selector* get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {+ To_String to_string(&ctx, false);+ Expression* exp = ARG(argname, Expression);+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << argname << ": null is not a string for `" << function_name(sig) << "'";+ error(msg.str(), pstate);+ }+ std::string exp_src = exp->perform(&to_string) + "{";+ Selector_List* sel_list = Parser::parse_selector(exp_src.c_str(), ctx);+ return (sel_list->length() > 0) ? sel_list->first()->tail()->head() : 0;+ }++ #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 std::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 std::mt19937 rand(static_cast<unsigned int>(GetSeed()));++ // features+ static std::set<std::string> features {+ "global-variable-shadowing",+ "extend-selector-pseudoclass",+ "at-error",+ "units-level-3"+ };++ ////////////////+ // RGB FUNCTIONS+ ////////////////++ inline double color_num(Number* n) {+ if (n->unit() == "%") {+ return std::min(std::max(n->value() * 255 / 100.0, 0.0), 255.0);+ } else {+ return std::min(std::max(n->value(), 0.0), 255.0);+ }+ }++ inline double alpha_num(Number* n) {+ if (n->unit() == "%") {+ return std::min(std::max(n->value(), 0.0), 100.0);+ } else {+ return std::min(std::max(n->value(), 0.0), 1.0);+ }+ }++ Signature rgb_sig = "rgb($red, $green, $blue)";+ BUILT_IN(rgb)+ {+ return SASS_MEMORY_NEW(ctx.mem, Color,+ pstate,+ color_num(ARG("$red", Number)),+ color_num(ARG("$green", Number)),+ color_num(ARG("$blue", Number)));+ }++ Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";+ BUILT_IN(rgba_4)+ {+ return SASS_MEMORY_NEW(ctx.mem, Color,+ pstate,+ color_num(ARG("$red", Number)),+ color_num(ARG("$green", Number)),+ color_num(ARG("$blue", Number)),+ alpha_num(ARG("$alpha", Number)));+ }++ Signature rgba_2_sig = "rgba($color, $alpha)";+ BUILT_IN(rgba_2)+ {+ Color* c_arg = ARG("$color", Color);+ Color* new_c = SASS_MEMORY_NEW(ctx.mem, Color, *c_arg);+ new_c->a(alpha_num(ARG("$alpha", Number)));+ new_c->disp("");+ return new_c;+ }++ Signature red_sig = "red($color)";+ BUILT_IN(red)+ { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->r()); }++ Signature green_sig = "green($color)";+ BUILT_IN(green)+ { return SASS_MEMORY_NEW(ctx.mem, Number, pstate, ARG("$color", Color)->g()); }++ Signature blue_sig = "blue($color)";+ BUILT_IN(blue)+ { return SASS_MEMORY_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 SASS_MEMORY_NEW(ctx.mem, Color,+ pstate,+ Sass::round(w1*color1->r() + w2*color2->r()),+ Sass::round(w1*color1->g() + w2*color2->g()),+ Sass::round(w1*color1->b() + w2*color2->b()),+ 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 delta = 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 = delta / (max + min);+ else s = delta / (2.0 - max - min);++ if (r == max) h = (g - b) / delta + (g < b ? 6 : 0);+ else if (g == max) h = (b - r) / delta + 2;+ else if (b == max) h = (r - g) / delta + 4;+ }++ HSL hsl_struct;+ hsl_struct.h = h / 6 * 360;+ 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) {+ while (h < 0) h += 1;+ while (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;++ if (l < 0) l = 0;+ if (s < 0) s = 0;+ if (l > 1) l = 1;+ if (s > 1) s = 1;+ while (h < 0) h += 1;+ while (h > 1) h -= 1;++ // 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.0)-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 SASS_MEMORY_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(),+ ARG("$saturation", Number)->value(),+ ARG("$lightness", Number)->value(),+ 1.0,+ ctx,+ pstate);+ }++ Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";+ BUILT_IN(hsla)+ {+ return hsla_impl(ARG("$hue", Number)->value(),+ ARG("$saturation", Number)->value(),+ ARG("$lightness", Number)->value(),+ ARG("$alpha", Number)->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 SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, 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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, 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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "invert(" + amount->perform(&to_string) + ")");+ }++ Color* rgb_color = ARG("$color", Color);+ return SASS_MEMORY_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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, 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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "opacity(" + amount->perform(&to_string) + ")");+ }++ return SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_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) {+ double rr = r ? ARGR("$red", Number, -255, 255)->value() : 0;+ double gg = g ? ARGR("$green", Number, -255, 255)->value() : 0;+ double bb = b ? ARGR("$blue", Number, -255, 255)->value() : 0;+ double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0;+ return SASS_MEMORY_NEW(ctx.mem, Color,+ pstate,+ color->r() + rr,+ color->g() + gg,+ color->b() + bb,+ color->a() + aa);+ }+ if (hsl) {+ HSL hsl_struct = rgb_to_hsl(color->r(), color->g(), color->b());+ double ss = s ? ARGR("$saturation", Number, -100, 100)->value() : 0;+ double ll = l ? ARGR("$lightness", Number, -100, 100)->value() : 0;+ double aa = a ? ARGR("$alpha", Number, -1, 1)->value() : 0;+ return hsla_impl(hsl_struct.h + (h ? h->value() : 0),+ hsl_struct.s + ss,+ hsl_struct.l + ll,+ color->a() + aa,+ ctx,+ pstate);+ }+ if (a) {+ return SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_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 SASS_MEMORY_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;++ std::stringstream ss;+ ss << '#' << std::setw(2) << std::setfill('0');+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g));+ ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b));++ std::string result(ss.str());+ for (size_t i = 0, L = result.length(); i < L; ++i) {+ result[i] = std::toupper(result[i]);+ }+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, result);+ }++ ///////////////////+ // STRING FUNCTIONS+ ///////////////////++ Signature unquote_sig = "unquote($string)";+ BUILT_IN(sass_unquote)+ {+ AST_Node* arg = env["$string"];+ if (String_Quoted* string_quoted = dynamic_cast<String_Quoted*>(arg)) {+ String_Constant* result = SASS_MEMORY_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;+ }+ else if (dynamic_cast<String_Constant*>(arg)) {+ return (Expression*) arg;+ }+ else {+ To_String to_string(&ctx, false, true);+ std::string val(arg->perform(&to_string));+ val = dynamic_cast<Null*>(arg) ? "null" : val;++ deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);+ return (Expression*) arg;+ }+ }++ Signature quote_sig = "quote($string)";+ BUILT_IN(sass_quote)+ {+ To_String to_string(&ctx);+ AST_Node* arg = env["$string"];+ std::string str(quote(arg->perform(&to_string), String_Constant::double_quote()));+ String_Quoted* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ result->is_delayed(true);+ return result;+ }+++ Signature str_length_sig = "str-length($string)";+ BUILT_IN(str_length)+ {+ size_t len = std::string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ len = UTF_8::code_point_count(s->value(), 0, s->value().size());++ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, backtrace); }+ // return something even if we had an error (-1)+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)len);+ }++ Signature str_insert_sig = "str-insert($string, $insert, $index)";+ BUILT_IN(str_insert)+ {+ std::string str;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ str = s->value();+ str = unquote(str);+ String_Constant* i = ARG("$insert", String_Constant);+ std::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);+ }+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, backtrace); }+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ }++ Signature str_index_sig = "str-index($string, $substring)";+ BUILT_IN(str_index)+ {+ size_t index = std::string::npos;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ String_Constant* t = ARG("$substring", String_Constant);+ std::string str = s->value();+ str = unquote(str);+ std::string substr = t->value();+ substr = unquote(substr);++ size_t c_index = str.find(substr);+ if(c_index == std::string::npos) {+ return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ }+ index = UTF_8::code_point_count(str, 0, c_index) + 1;+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, backtrace); }+ // return something even if we had an error (-1)+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)index);+ }++ Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";+ BUILT_IN(str_slice)+ {+ std::string newstr;+ try {+ String_Constant* s = ARG("$string", String_Constant);+ double start_at = ARG("$start-at", Number)->value();+ double end_at = ARG("$end-at", Number)->value();++ std::string str = unquote(s->value());++ size_t size = utf8::distance(str.begin(), str.end());+ if (end_at <= size * -1.0) { end_at += size; }+ if (end_at < 0) { end_at += size + 1; }+ if (end_at > size) { end_at = (double)size; }+ if (start_at < 0) { start_at += size + 1; }+ else if (start_at == 0) { ++ start_at; }++ if (start_at <= end_at)+ {+ std::string::iterator start = str.begin();+ utf8::advance(start, start_at - 1, str.end());+ std::string::iterator end = start;+ utf8::advance(end, end_at - start_at + 1, str.end());+ newstr = std::string(start, end);+ }+ if (String_Quoted* ss = dynamic_cast<String_Quoted*>(s)) {+ if(ss->quote_mark()) newstr = quote(newstr);+ }+ }+ // handle any invalid utf8 errors+ // other errors will be re-thrown+ catch (...) { handle_utf8_error(pstate, backtrace); }+ return SASS_MEMORY_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);+ std::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)) {+ String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+ cpy->value(str);+ return cpy;+ } else {+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, str);+ }+ }++ Signature to_lower_case_sig = "to-lower-case($string)";+ BUILT_IN(to_lower_case)+ {+ String_Constant* s = ARG("$string", String_Constant);+ std::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)) {+ String_Quoted* cpy = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *ss);+ cpy->value(str);+ return cpy;+ } else {+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, 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 `" + std::string(sig) + "` must be unitless", pstate);+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, n->value() * 100, "%");+ }++ Signature round_sig = "round($number)";+ BUILT_IN(round)+ {+ Number* n = ARG("$number", Number);+ Number* r = SASS_MEMORY_NEW(ctx.mem, Number, *n);+ r->pstate(pstate);+ r->value(Sass::round(r->value()));+ return r;+ }++ Signature ceil_sig = "ceil($number)";+ BUILT_IN(ceil)+ {+ Number* n = ARG("$number", Number);+ Number* r = SASS_MEMORY_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 = SASS_MEMORY_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 = SASS_MEMORY_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 (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+ if (least) {+ if (Eval::lt(xi, least)) 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 (!xi) error("`" + std::string(sig) + "` only takes numeric arguments", pstate);+ if (greatest) {+ if (Eval::lt(greatest, xi)) greatest = xi;+ } else greatest = xi;+ }+ return greatest;+ }++ Signature random_sig = "random($limit:false)";+ BUILT_IN(random)+ {+ AST_Node* arg = env["$limit"];+ Value* v = dynamic_cast<Value*>(arg);+ Number* l = dynamic_cast<Number*>(arg);+ Boolean* b = dynamic_cast<Boolean*>(arg);+ if (l) {+ double v = l->value();+ if (v < 1) {+ stringstream err;+ err << "$limit " << v << " must be greater than or equal to 1 for `random`";+ error(err.str(), pstate);+ }+ bool eq_int = std::fabs(trunc(v) - v) < NUMBER_EPSILON;+ if (!eq_int) {+ stringstream err;+ err << "Expected $limit to be an integer but got `" << v << "` for `random`";+ error(err.str(), pstate);+ }+ std::uniform_real_distribution<> distributor(1, v + 1);+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)distributed);+ }+ else if (b) {+ std::uniform_real_distribution<> distributor(0, 1);+ double distributed = static_cast<double>(distributor(rand));+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, distributed);+ } else if (v) {+ throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v);+ } else {+ throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number");+ }+ return 0;+ }++ /////////////////+ // LIST FUNCTIONS+ /////////////////++ Signature length_sig = "length($list)";+ BUILT_IN(length)+ {+ if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)sl->length());+ }+ Expression* v = ARG("$list", Expression);+ if (v->concrete_type() == Expression::MAP) {+ Map* map = dynamic_cast<Map*>(env["$list"]);+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(map ? map->length() : 1));+ }+ if (v->concrete_type() == Expression::SELECTOR) {+ if (Compound_Selector* h = dynamic_cast<Compound_Selector*>(v)) {+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)h->length());+ } else if (Selector_List* ls = dynamic_cast<Selector_List*>(v)) {+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)ls->length());+ } else {+ return SASS_MEMORY_NEW(ctx.mem, Number, pstate, 1);+ }+ }++ List* list = dynamic_cast<List*>(env["$list"]);+ return SASS_MEMORY_NEW(ctx.mem, Number,+ pstate,+ (double)(list ? list->size() : 1));+ }++ Signature nth_sig = "nth($list, $n)";+ BUILT_IN(nth)+ {+ Number* n = ARG("$n", Number);+ Map* m = dynamic_cast<Map*>(env["$list"]);+ if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {+ size_t len = m ? m->length() : sl->length();+ bool empty = m ? m->empty() : sl->empty();+ if (empty) error("argument `$list` of `" + std::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 `" + std::string(sig) + "`", pstate);+ // return (*sl)[static_cast<int>(index)];+ Listize listize(ctx);+ return (*sl)[static_cast<int>(index)]->perform(&listize);+ }+ List* l = dynamic_cast<List*>(env["$list"]);+ if (n->value() == 0) error("argument `$n` of `" + std::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 = SASS_MEMORY_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 `" + std::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 `" + std::string(sig) + "`", pstate);++ if (m) {+ l = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l << ARG("$list", Expression);+ }+ if (l->empty()) error("argument `$list` of `" + std::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 `" + std::string(sig) + "`", pstate);+ List* result = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l << ARG("$list", Expression);+ }+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(ctx.mem, Number, pstate, (double)(i+1));+ }+ return SASS_MEMORY_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);+ enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);+ if (!l1) {+ l1 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l1 << ARG("$list1", Expression);+ sep_val = (l2 ? l2->separator() : SASS_SPACE);+ }+ if (!l2) {+ l2 = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l2 << ARG("$list2", Expression);+ }+ size_t len = l1->length() + l2->length();+ std::string sep_str = unquote(sep->value());+ if (sep_str == "space") sep_val = SASS_SPACE;+ else if (sep_str == "comma") sep_val = SASS_COMMA;+ else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ List* result = SASS_MEMORY_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);+ if (Selector_List* sl = dynamic_cast<Selector_List*>(env["$list"])) {+ Listize listize(ctx);+ l = dynamic_cast<List*>(sl->perform(&listize));+ }+ String_Constant* sep = ARG("$separator", String_Constant);+ if (!l) {+ l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l << ARG("$list", Expression);+ }+ List* result = SASS_MEMORY_NEW(ctx.mem, List, pstate, l->length() + 1, l->separator());+ std::string sep_str(unquote(sep->value()));+ if (sep_str == "space") result->separator(SASS_SPACE);+ else if (sep_str == "comma") result->separator(SASS_COMMA);+ else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);+ *result += l;+ bool is_arglist = l->is_arglist();+ result->is_arglist(is_arglist);+ if (is_arglist) {+ *result << SASS_MEMORY_NEW(ctx.mem, Argument,+ v->pstate(),+ v,+ "",+ false,+ false);++ } else {+ *result << v;+ }+ return result;+ }++ Signature zip_sig = "zip($lists...)";+ BUILT_IN(zip)+ {+ List* arglist = SASS_MEMORY_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 = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List, pstate, shortest, SASS_COMMA);+ size_t L = arglist->length();+ for (size_t i = 0; i < shortest; ++i) {+ List* zipper = SASS_MEMORY_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 list_separator_sig = "list_separator($list)";+ BUILT_IN(list_separator)+ {+ List* l = dynamic_cast<List*>(env["$list"]);+ if (!l) {+ l = SASS_MEMORY_NEW(ctx.mem, List, pstate, 1);+ *l << ARG("$list", Expression);+ }+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ pstate,+ l->separator() == SASS_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 SASS_MEMORY_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 SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_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 = SASS_MEMORY_NEW(ctx.mem, List, pstate, m->length(), SASS_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 = SASS_MEMORY_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 = SASS_MEMORY_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 = Eval::eq(key, arglist->value_at_index(j));+ }+ if (!remove) *result << std::make_pair(key, m->at(key));+ }+ return result;+ }++ Signature keywords_sig = "keywords($args)";+ BUILT_IN(keywords)+ {+ List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));+ Map* result = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+ for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {+ std::string name = std::string(((Argument*)(*arglist)[i])->name());+ name = name.erase(0, 1); // sanitize name (remove dollar sign)+ *result << std::make_pair(SASS_MEMORY_NEW(ctx.mem, String_Quoted,+ pstate, 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);+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, v->type());+ }++ Signature unit_sig = "unit($number)";+ BUILT_IN(unit)+ { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, quote(ARG("$number", Number)->unit(), '"')); }++ Signature unitless_sig = "unitless($number)";+ BUILT_IN(unitless)+ { return SASS_MEMORY_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 SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ Number tmp_n2(*n2);+ tmp_n2.normalize(n1->find_convertible_unit());+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, n1->unit() == tmp_n2.unit());+ }++ Signature variable_exists_sig = "variable-exists($name)";+ BUILT_IN(variable_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has("$"+s)) {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ }+ }++ Signature global_variable_exists_sig = "global-variable-exists($name)";+ BUILT_IN(global_variable_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global("$"+s)) {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ }+ }++ Signature function_exists_sig = "function-exists($name)";+ BUILT_IN(function_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global(s+"[f]")) {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ }+ }++ Signature mixin_exists_sig = "mixin-exists($name)";+ BUILT_IN(mixin_exists)+ {+ std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));++ if(d_env.has_global(s+"[m]")) {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ else {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ }+ }++ Signature feature_exists_sig = "feature-exists($name)";+ BUILT_IN(feature_exists)+ {+ std::string s = unquote(ARG("$name", String_Constant)->value());++ if(features.find(s) == features.end()) {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false);+ }+ else {+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true);+ }+ }++ Signature call_sig = "call($name, $args...)";+ BUILT_IN(call)+ {+ std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));+ List* arglist = SASS_MEMORY_NEW(ctx.mem, List, *ARG("$args", List));++ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);+ // std::string full_name(name + "[f]");+ // Definition* def = d_env.has(full_name) ? static_cast<Definition*>((d_env)[full_name]) : 0;+ // Parameters* params = def ? def->parameters() : 0;+ // size_t param_size = params ? params->length() : 0;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression* expr = arglist->value_at_index(i);+ // if (params && params->has_rest_parameter()) {+ // Parameter* p = param_size > i ? (*params)[i] : 0;+ // List* list = dynamic_cast<List*>(expr);+ // if (list && p && !p->is_rest_parameter()) expr = (*list)[0];+ // }+ if (arglist->is_arglist()) {+ Argument* arg = dynamic_cast<Argument*>((*arglist)[i]);+ *args << SASS_MEMORY_NEW(ctx.mem, Argument,+ pstate,+ expr,+ arg ? arg->name() : "",+ arg ? arg->is_rest_argument() : false,+ arg ? arg->is_keyword_argument() : false);+ } else {+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, pstate, expr);+ }+ }+ Function_Call* func = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, name, args);+ Expand expand(ctx, &d_env, backtrace);+ return func->perform(&expand.eval);++ }++ ////////////////////+ // BOOLEAN FUNCTIONS+ ////////////////////++ Signature not_sig = "not($value)";+ BUILT_IN(sass_not)+ { return SASS_MEMORY_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)+ {+ Expand expand(ctx, &d_env, backtrace);+ bool is_true = !ARG("$condition", Expression)->perform(&expand.eval)->is_false();+ if (is_true) {+ return ARG("$if-true", Expression)->perform(&expand.eval);+ }+ else {+ return ARG("$if-false", Expression)->perform(&expand.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 SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "null");+ } else if (v->concrete_type() == Expression::BOOLEAN && *v == 0) {+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, "false");+ } else if (v->concrete_type() == Expression::STRING) {+ return v;+ } else {+ bool parentheses = v->concrete_type() == Expression::MAP ||+ v->concrete_type() == Expression::LIST;+ Sass_Output_Style old_style;+ old_style = ctx.c_options->output_style;+ ctx.c_options->output_style = SASS_STYLE_NESTED;+ To_String to_string(&ctx, false);+ std::string inspect = v->perform(&to_string);+ if (inspect.empty() && parentheses) inspect = "()";+ ctx.c_options->output_style = old_style;+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, inspect);+ }+ // return v;+ }+ Signature selector_nest_sig = "selector-nest($selectors...)";+ BUILT_IN(selector_nest)+ {+ To_String to_string(&ctx, false);+ List* arglist = ARG("$selectors", List);++ // Not enough parameters+ if( arglist->length() == 0 )+ error("$selectors: At least one selector must be passed", pstate);++ // Parse args into vector of selectors+ std::vector<Selector_List*> parsedSelectors;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << "$selectors: null is not a valid selector: it must be a string,\n";+ msg << "a list of strings, or a list of lists of strings for 'selector-nest'";+ error(msg.str(), pstate);+ }+ std::string exp_src = exp->perform(&to_string) + "{";+ Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);+ parsedSelectors.push_back(sel);+ }++ // Nothing to do+ if( parsedSelectors.empty() ) {+ return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ }++ // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.+ std::vector<Selector_List*>::iterator itr = parsedSelectors.begin();+ Selector_List* result = *itr;+ ++itr;++ for(;itr != parsedSelectors.end(); ++itr) {+ Selector_List* child = *itr;+ std::vector<Complex_Selector*> exploded;+ Selector_List* rv = child->parentize(result, ctx);+ for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) {+ exploded.push_back((*rv)[m]);+ }+ result->elements(exploded);+ }++ Listize listize(ctx);+ return result->perform(&listize);+ }++ Signature selector_append_sig = "selector-append($selectors...)";+ BUILT_IN(selector_append)+ {+ To_String to_string;+ List* arglist = ARG("$selectors", List);++ // Not enough parameters+ if( arglist->length() == 0 )+ error("$selectors: At least one selector must be passed", pstate);++ // Parse args into vector of selectors+ std::vector<Selector_List*> parsedSelectors;+ for (size_t i = 0, L = arglist->length(); i < L; ++i) {+ Expression* exp = dynamic_cast<Expression*>(arglist->value_at_index(i));+ if (exp->concrete_type() == Expression::NULL_VAL) {+ std::stringstream msg;+ msg << "$selectors: null is not a valid selector: it must be a string,\n";+ msg << "a list of strings, or a list of lists of strings for 'selector-append'";+ error(msg.str(), pstate);+ }+ std::string exp_src = exp->perform(&to_string) + "{";+ Selector_List* sel = Parser::parse_selector(exp_src.c_str(), ctx);+ parsedSelectors.push_back(sel);+ }++ // Nothing to do+ if( parsedSelectors.empty() ) {+ return SASS_MEMORY_NEW(ctx.mem, Null, pstate);+ }++ // Set the first element as the `result`, keep appending to as we go down the parsedSelector vector.+ std::vector<Selector_List*>::iterator itr = parsedSelectors.begin();+ Selector_List* result = *itr;+ ++itr;++ for(;itr != parsedSelectors.end(); ++itr) {+ Selector_List* child = *itr;+ std::vector<Complex_Selector*> newElements;++ // For every COMPLEX_SELECTOR in `result`+ // For every COMPLEX_SELECTOR in `child`+ // let parentSeqClone equal a copy of result->elements[i]+ // let childSeq equal child->elements[j]+ // Append all of childSeq head elements into parentSeqClone+ // Set the innermost tail of parentSeqClone, to childSeq's tail+ // Replace result->elements with newElements+ for (size_t i = 0, resultLen = result->length(); i < resultLen; ++i) {+ for (size_t j = 0, childLen = child->length(); j < childLen; ++j) {+ Complex_Selector* parentSeqClone = (*result)[i]->cloneFully(ctx);+ Complex_Selector* childSeq = (*child)[j];+ Complex_Selector* base = childSeq->tail();++ // Must be a simple sequence+ if( childSeq->combinator() != Complex_Selector::Combinator::ANCESTOR_OF ) {+ std::string msg("Can't append `");+ msg += childSeq->perform(&to_string);+ msg += "` to `";+ msg += parentSeqClone->perform(&to_string);;+ msg += "`";+ error(msg, pstate, backtrace);+ }++ // Cannot be a Universal selector+ Type_Selector* pType = dynamic_cast<Type_Selector*>(base->head()->first());+ if(pType && pType->name() == "*") {+ std::string msg("Can't append `");+ msg += childSeq->perform(&to_string);+ msg += "` to `";+ msg += parentSeqClone->perform(&to_string);;+ msg += "`";+ error(msg, pstate, backtrace);+ }++ // TODO: Add check for namespace stuff++ // append any selectors in childSeq's head+ *(parentSeqClone->innermost()->head()) += (base->head());++ // Set parentSeqClone new tail+ parentSeqClone->innermost()->tail( base->tail() );++ newElements.push_back(parentSeqClone);+ }+ }++ result->elements(newElements);+ }++ Listize listize(ctx);+ return result->perform(&listize);+ }++ Signature selector_unify_sig = "selector-unify($selector1, $selector2)";+ BUILT_IN(selector_unify)+ {+ Selector_List* selector1 = ARGSEL("$selector1", Selector_List, p_contextualize);+ Selector_List* selector2 = ARGSEL("$selector2", Selector_List, p_contextualize);++ Selector_List* result = selector1->unify_with(selector2, ctx);+ Listize listize(ctx);+ return result->perform(&listize);+ }++ Signature simple_selectors_sig = "simple-selectors($selector)";+ BUILT_IN(simple_selectors)+ {+ Compound_Selector* sel = ARGSEL("$selector", Compound_Selector, p_contextualize);++ To_String to_string;+ List* l = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);++ for (size_t i = 0, L = sel->length(); i < L; ++i) {+ Simple_Selector* ss = (*sel)[i];+ std::string ss_string = ss->perform(&to_string) ;++ *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, ss->pstate(), ss_string);+ }++ return l;+ }++ Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";+ BUILT_IN(selector_extend)+ {+ Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize);+ Selector_List* extendee = ARGSEL("$extendee", Selector_List, p_contextualize);+ Selector_List* extender = ARGSEL("$extender", Selector_List, p_contextualize);++ ExtensionSubsetMap subset_map;+ extender->populate_extends(extendee, ctx, subset_map);++ bool extendedSomething;+ Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, false, extendedSomething);++ Listize listize(ctx);+ return result->perform(&listize);+ }++ Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";+ BUILT_IN(selector_replace)+ {+ Selector_List* selector = ARGSEL("$selector", Selector_List, p_contextualize);+ Selector_List* original = ARGSEL("$original", Selector_List, p_contextualize);+ Selector_List* replacement = ARGSEL("$replacement", Selector_List, p_contextualize);++ ExtensionSubsetMap subset_map;+ replacement->populate_extends(original, ctx, subset_map);++ bool extendedSomething;+ Selector_List* result = Extend::extendSelectorList(selector, ctx, subset_map, true, extendedSomething);++ Listize listize(ctx);+ return result->perform(&listize);+ }++ Signature selector_parse_sig = "selector-parse($selector)";+ BUILT_IN(selector_parse)+ {+ Selector_List* sel = ARGSEL("$selector", Selector_List, p_contextualize);++ Listize listize(ctx);+ return sel->perform(&listize);+ }++ Signature is_superselector_sig = "is-superselector($super, $sub)";+ BUILT_IN(is_superselector)+ {+ To_String to_string(&ctx, false);+ Selector_List* sel_sup = ARGSEL("$super", Selector_List, p_contextualize);+ Selector_List* sel_sub = ARGSEL("$sub", Selector_List, p_contextualize);+ bool result = sel_sup->is_superselector_of(sel_sub);+ return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, result);+ }++ Signature unique_id_sig = "unique-id()";+ BUILT_IN(unique_id)+ {+ std::stringstream ss;+ std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8+ uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));+ ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed;+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, ss.str());+ }++ }+}
+ libsass/src/functions.hpp view
@@ -0,0 +1,197 @@+#ifndef SASS_FUNCTIONS_H+#define SASS_FUNCTIONS_H++#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);++ std::string function_name(Signature);++ 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 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;+ extern Signature selector_nest_sig;+ extern Signature selector_append_sig;+ extern Signature selector_extend_sig;+ extern Signature selector_replace_sig;+ extern Signature selector_unify_sig;+ extern Signature is_superselector_sig;+ extern Signature simple_selectors_sig;+ extern Signature selector_parse_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(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);+ BUILT_IN(selector_nest);+ BUILT_IN(selector_append);+ BUILT_IN(selector_extend);+ BUILT_IN(selector_replace);+ BUILT_IN(selector_unify);+ BUILT_IN(is_superselector);+ BUILT_IN(simple_selectors);+ BUILT_IN(selector_parse);+ }+}++#endif
+ libsass/src/inspect.cpp view
@@ -0,0 +1,851 @@+#include <cmath>+#include <string>+#include <iostream>+#include <iomanip>+#include <stdint.h>+#include <stdint.h>++#include "ast.hpp"+#include "inspect.hpp"+#include "context.hpp"+#include "listize.hpp"+#include "color_maps.hpp"+#include "utf8/checked.h"++namespace Sass {++ 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() == SASS_STYLE_NESTED) indentation += block->tabs();+ for (size_t i = 0, L = block->length(); i < L; ++i) {+ (*block)[i]->perform(this);+ }+ if (output_style() == SASS_STYLE_NESTED) indentation -= block->tabs();+ if (!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()(Supports_Block* feature_block)+ {+ append_indentation();+ append_token("@supports", feature_block);+ append_mandatory_space();+ feature_block->condition()->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();+ bool was_wrapped = in_wrapped;+ in_wrapped = true;+ at_rule->selector()->perform(this);+ in_wrapped = was_wrapped;+ }+ if (at_rule->value()) {+ append_mandatory_space();+ at_rule->value()->perform(this);+ }+ 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;+ bool was_decl = in_declaration;+ in_declaration = true;+ if (output_style() == SASS_STYLE_NESTED)+ indentation += dec->tabs();+ append_indentation();+ dec->property()->perform(this);+ append_colon_separator();++ if (dec->value()->concrete_type() == Expression::SELECTOR) {+ Listize listize(*ctx);+ dec->value()->perform(&listize)->perform(this);+ } else {+ dec->value()->perform(this);+ }++ if (dec->is_important()) {+ append_optional_space();+ append_string("!important");+ }+ append_delimiter();+ if (output_style() == SASS_STYLE_NESTED)+ indentation -= dec->tabs();+ in_declaration = was_decl;+ }++ 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);+ if (import->media_queries()) {+ append_mandatory_space();+ import->media_queries()->perform(this);+ }+ append_delimiter();+ for (size_t i = 1, S = import->urls().size(); i < S; ++i) {+ 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);+ if (import->media_queries()) {+ append_mandatory_space();+ import->media_queries()->perform(this);+ }+ append_delimiter();+ }+ }+ }++ void Inspect::operator()(Import_Stub* import)+ {+ append_indentation();+ append_token("@import", import);+ append_mandatory_space();+ append_string(import->imp_path());+ 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->block()->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 (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)+ {+ std::string sep(list->separator() == SASS_SPACE ? " " : ",");+ if ((output_style() != SASS_STYLE_COMPRESSED || in_debug) && sep == ",") sep += " ";+ else if (in_media_block && sep != " ") sep += " "; // verified+ if (list->empty()) return;+ bool items_output = false;++ bool was_space_array = in_space_array;+ bool was_comma_array = in_comma_array;+ if (!in_declaration && (+ (list->separator() == SASS_SPACE && in_space_array) ||+ (list->separator() == SASS_COMMA && in_comma_array)+ )) {+ append_string("(");+ }++ if (list->separator() == SASS_SPACE) in_space_array = true;+ else if (list->separator() == SASS_COMMA) in_comma_array = true;++ for (size_t i = 0, L = list->size(); 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_comma_array = was_comma_array;+ in_space_array = was_space_array;+ if (!in_declaration && (+ (list->separator() == SASS_SPACE && in_space_array) ||+ (list->separator() == SASS_COMMA && in_comma_array)+ )) {+ append_string(")");+ }++ }++ void Inspect::operator()(Binary_Expression* expr)+ {+ expr->left()->perform(this);+ switch (expr->type()) {+ case Sass_OP::AND: append_string(" and "); break;+ case Sass_OP::OR: append_string(" or "); break;+ case Sass_OP::EQ: append_string(" == "); break;+ case Sass_OP::NEQ: append_string(" != "); break;+ case Sass_OP::GT: append_string(" > "); break;+ case Sass_OP::GTE: append_string(" >= "); break;+ case Sass_OP::LT: append_string(" < "); break;+ case Sass_OP::LTE: append_string(" <= "); break;+ case Sass_OP::ADD: append_string(" + "); break;+ case Sass_OP::SUB: append_string(" - "); break;+ case Sass_OP::MUL: append_string(" * "); break;+ case Sass_OP::DIV: append_string(in_media_block ? " / " : "/"); break;+ case Sass_OP::MOD: append_string(" % "); break;+ 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)+ {+ // use values to_string facility+ bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;+ std::string res(n->to_string(compressed, (int)ctx->c_options->precision));+ // output the final token+ append_token(res, n);+ }++ void Inspect::operator()(Color* c)+ {+ // use values to_string facility+ bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;+ std::string res(c->to_string(compressed, (int)ctx->c_options->precision));+ // output the final token+ append_token(res, c);+ }++ void Inspect::operator()(Boolean* b)+ {+ // use values to_string facility+ bool compressed = ctx->output_style() == SASS_STYLE_COMPRESSED;+ std::string res(b->to_string(compressed, (int)ctx->c_options->precision));+ // output the final token+ append_token(res, b);+ }++ 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)+ {+ // get options from optional? context+ int precision = ctx ? (int)ctx->c_options->precision : 5;+ bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;+ // use values to_string facility+ std::string res(s->to_string(compressed, precision));+ // output the final token+ append_token(res, s);+ }++ void Inspect::operator()(String_Quoted* s)+ {+ // get options from optional? context+ int precision = ctx ? (int)ctx->c_options->precision : 5;+ bool compressed = ctx ? ctx->output_style() == SASS_STYLE_COMPRESSED : false;+ // use values to_string facility+ std::string res(s->to_string(compressed, precision));+ // output the final token+ append_token(res, s);+ }+ void Inspect::operator()(Supports_Operator* so)+ {++ if (so->needs_parens(so->left())) append_string("(");+ so->left()->perform(this);+ if (so->needs_parens(so->left())) append_string(")");++ if (so->operand() == Supports_Operator::AND) {+ append_mandatory_space();+ append_token("and", so);+ append_mandatory_space();+ } else if (so->operand() == Supports_Operator::OR) {+ append_mandatory_space();+ append_token("or", so);+ append_mandatory_space();+ }++ if (so->needs_parens(so->right())) append_string("(");+ so->right()->perform(this);+ if (so->needs_parens(so->right())) append_string(")");+ }++ void Inspect::operator()(Supports_Negation* sn)+ {+ append_token("not", sn);+ append_mandatory_space();+ if (sn->needs_parens(sn->condition())) append_string("(");+ sn->condition()->perform(this);+ if (sn->needs_parens(sn->condition())) append_string(")");+ }++ void Inspect::operator()(Supports_Declaration* sd)+ {+ append_string("(");+ sd->feature()->perform(this);+ append_string(": ");+ sd->value()->perform(this);+ append_string(")");+ }++ void Inspect::operator()(Supports_Interpolation* sd)+ {+ sd->value()->perform(this);+ }++ 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)+ {+ // use values to_string facility+ bool compressed = output_style() == SASS_STYLE_COMPRESSED;+ std::string res(n->to_string(compressed, (int)ctx->c_options->precision));+ // output the final token+ append_token(res, n);+ }++ // 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()(Parent_Selector* p)+ {+ 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->ns_name(), s);+ }++ void Inspect::operator()(Selector_Qualifier* s)+ {+ append_token(s->ns_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->ns_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->ns_name(), s);+ if (s->expression()) {+ append_string("(");+ s->expression()->perform(this);+ append_string(")");+ }+ }++ void Inspect::operator()(Wrapped_Selector* s)+ {+ bool was = in_wrapped;+ in_wrapped = true;+ append_token(s->name(), s);+ append_string("(");+ bool was_comma_array = in_comma_array;+ in_comma_array = false;+ s->selector()->perform(this);+ in_comma_array = was_comma_array;+ 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()) {+ if (output_style() != SASS_STYLE_COMPACT) {+ 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 (c->has_line_feed()) {+ if (!(c->has_parent_ref())) {+ append_optional_linefeed();+ append_indentation();+ }+ }++ if (head && head->length() != 0) head->perform(this);+ bool is_empty = !head || head->length() == 0 || head->is_empty_reference();+ bool is_tail = head && !head->is_empty_reference() && tail;+ if (output_style() == SASS_STYLE_COMPRESSED && comb != Complex_Selector::ANCESTOR_OF) scheduled_space = 0;++ 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::REFERENCE:+ append_mandatory_space();+ append_string("/");+ c->reference()->perform(this);+ append_string("/");+ append_mandatory_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);+ if (!tail && c->has_line_break()) {+ if (output_style() == SASS_STYLE_COMPACT) {+ append_mandatory_space();+ }+ }+ }++ void Inspect::operator()(Selector_List* g)+ {+ if (g->empty()) return;++ bool was_comma_array = in_comma_array;+ if (!in_declaration && in_comma_array) {+ append_string("(");+ }++ if (in_declaration) in_comma_array = true;++ for (size_t i = 0, L = g->length(); i < L; ++i) {+ if (!in_wrapped && i == 0) append_indentation();+ if ((*g)[i] == 0) continue;+ (*g)[i]->perform(this);+ if (i < L - 1) {+ scheduled_space = 0;+ append_comma_separator();+ }+ }++ in_comma_array = was_comma_array;+ if (!in_declaration && in_comma_array) {+ append_string(")");+ }++ }++ void Inspect::fallback_impl(AST_Node* n)+ {+ }++}
+ libsass/src/inspect.hpp view
@@ -0,0 +1,98 @@+#ifndef SASS_INSPECT_H+#define SASS_INSPECT_H++#include "position.hpp"+#include "operation.hpp"+#include "emitter.hpp"++namespace Sass {+ class Context;++ 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()(Supports_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()(Custom_Warning*);+ // virtual void operator()(Custom_Error*);+ 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()(Supports_Operator*);+ virtual void operator()(Supports_Negation*);+ virtual void operator()(Supports_Declaration*);+ virtual void operator()(Supports_Interpolation*);+ 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_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/src/json.cpp view
@@ -0,0 +1,1398 @@+/*+ 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.+*/++#ifdef _MSC_VER+#define _CRT_SECURE_NO_WARNINGS+#define _CRT_NONSTDC_NO_DEPRECATE+#endif++#include "json.hpp"++#include <assert.h>+#include <stdint.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>++#if defined(_MSC_VER) && _MSC_VER < 1900+#include <stdarg.h>+#ifdef snprintf+#undef snprintf+#endif+extern "C" int snprintf(char *, size_t, const char *, ...);+#endif++#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, (int)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.+ *+ * We use uint32_t Type for Unicode codepoints.+ * We need our own because wchar_t might be 16 bits.+ */++/*+ * 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, uint32_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 = ((uint32_t)c[0] & 0x1F) << 6 |+ ((uint32_t)c[1] & 0x3F);+ return 2;+ } else if (c[0] <= 0xEF) {+ /* E0..EF */+ *out = ((uint32_t)c[0] & 0xF) << 12 |+ ((uint32_t)c[1] & 0x3F) << 6 |+ ((uint32_t)c[2] & 0x3F);+ return 3;+ } else {+ /* F0..F4 (unless input is invalid) */+ *out = ((uint32_t)c[0] & 0x7) << 18 |+ ((uint32_t)c[1] & 0x3F) << 12 |+ ((uint32_t)c[2] & 0x3F) << 6 |+ ((uint32_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(uint32_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, uint32_t *unicode)+{+ if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {+ *unicode = 0x10000 + ((((uint32_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(uint32_t unicode, uint16_t *uc, uint16_t *lc)+{+ uint32_t n;++ assert(unicode >= 0x10000 && unicode <= 0x10FFFF);++ n = unicode - 0x10000;+ *uc = ((n >> 10) & 0x3FF) | 0xD800;+ *lc = (n & 0x3FF) | 0xDC00;+}++static bool is_space (const char *c);+static bool is_digit (const char *c);+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 = NULL;+ 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 = NULL;++ 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 = NULL;+ JsonNode *value = NULL;++ 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 = { 0, 0, 0 };+ 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;+ uint32_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;+}++bool is_space(const char *c) {+ return ((*c) == '\t' || (*c) == '\n' || (*c) == '\r' || (*c) == ' ');+}++bool is_digit(const char *c){+ return ((*c) >= '0' && (*c) <= '9');+}++/*+ * 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/src/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/src/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/src/lexer.cpp view
@@ -0,0 +1,169 @@+#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+ //####################################++ bool is_alpha(const char& chr)+ {+ return unsigned(chr - 'A') <= 'Z' - 'A' ||+ unsigned(chr - 'a') <= 'z' - 'a';+ }++ bool is_space(const char& chr)+ {+ // adapted the technique from is_alpha+ return chr == ' ' || unsigned(chr - '\t') <= '\r' - '\t';+ }++ bool is_digit(const char& chr)+ {+ // adapted the technique from is_alpha+ return unsigned(chr - '0') <= '9' - '0';+ }++ 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';+ }++ bool is_punct(const char& chr)+ {+ // locale independent+ return chr == '.';+ }++ bool is_alnum(const char& chr)+ {+ return is_alpha(chr) || is_digit(chr);+ }++ // check if char is outside ascii range+ bool is_unicode(const char& chr)+ {+ // check for unicode range+ return unsigned(chr) > 127;+ }++ // check if char is outside ascii range+ // but with specific ranges (copied from Ruby Sass)+ bool is_nonascii(const char& chr)+ {+ return (+ (unsigned(chr) > 127 && unsigned(chr) < 55296) ||+ (unsigned(chr) > 57343 && unsigned(chr) < 65534) ||+ (unsigned(chr) > 65535 && unsigned(chr) < 1114111)+ );+ }++ // check if char is within a reduced ascii range+ // valid in a uri (copied from Ruby Sass)+ bool is_uri_character(const char& chr)+ {+ return unsigned(chr) > 41 && unsigned(chr) < 127;+ }++ // check if char is within a reduced ascii range+ // valid for escaping (copied from Ruby Sass)+ bool is_escapable_character(const char& chr)+ {+ return unsigned(chr) > 31 && unsigned(chr) < 127;+ }++ // Match word character (look ahead)+ 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* nonascii(const char* src) { return is_nonascii(*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; }+ const char* uri_character(const char* src) { return is_uri_character(*src) ? src + 1 : 0; }+ const char* escapable_character(const char* src) { return is_escapable_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;+ }++ // Assert end_of_file boundary (/\z/)+ // This is a zero-width positive lookahead+ const char* end_of_file(const char* src)+ {+ // end of file or unix linefeed return here+ return *src == 0 ? src : 0;+ }++ }+}
+ libsass/src/lexer.hpp view
@@ -0,0 +1,285 @@+#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+ bool is_space(const char& src);+ bool is_alpha(const char& src);+ bool is_punct(const char& src);+ bool is_digit(const char& src);+ bool is_alnum(const char& src);+ bool is_xdigit(const char& src);+ bool is_unicode(const char& src);+ bool is_nonascii(const char& src);+ bool is_character(const char& src);+ bool is_uri_character(const char& src);+ bool escapable_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* nonascii(const char* src);+ const char* character(const char* src);+ const char* uri_character(const char* src);+ const char* escapable_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);++ // Assert end_of_file boundary (/\z/)+ const char* end_of_file(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: /(?:x)/+ template <char chr>+ const char* exactly(const char* src) {+ return *src == chr ? src + 1 : 0;+ }++ // Match the full string literal.+ // Regex equivalent: /(?:literal)/+ 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;+ }+ // did the matcher finish?+ return *pre == 0 ? src : 0;+ }+++ // Match the full string literal.+ // Regex equivalent: /(?:literal)/i+ // only define lower case alpha chars+ template <const char* str>+ const char* insensitive(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+32 == *pre)) {+ ++src, ++pre;+ }+ // did the matcher finish?+ return *pre == 0 ? src : 0;+ }++ // 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 <const prelexer mx>+ const char* alternatives(const char* src) {+ const char* rslt;+ if ((rslt = mx(src))) return rslt;+ return 0;+ }+ template <const prelexer mx1, const prelexer mx2, const prelexer... mxs>+ const char* alternatives(const char* src) {+ const char* rslt;+ if ((rslt = mx1(src))) return rslt;+ return alternatives<mx2, mxs...>(src);+ }++ // Tries supplied matchers in order.+ // Succeeds if all of them succeeds.+ // Regex equivalent: /(?:FOO)(?:BAR)/+ template <const prelexer mx1>+ const char* sequence(const char* src) {+ const char* rslt = src;+ if (!(rslt = mx1(rslt))) return 0;+ return rslt;+ }+ template <const prelexer mx1, const prelexer mx2, const prelexer... mxs>+ const char* sequence(const char* src) {+ const char* rslt = src;+ if (!(rslt = mx1(rslt))) return 0;+ return sequence<mx2, mxs...>(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/i+ template <const char* str>+ const char* keyword(const char* src) {+ return sequence <+ insensitive < str >,+ word_boundary+ >(src);+ }++ // 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/src/listize.cpp view
@@ -0,0 +1,87 @@+#include <iostream>+#include <typeinfo>+#include <string>++#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 = SASS_MEMORY_NEW(ctx.mem, List, sel->pstate(), sel->length(), SASS_COMMA);+ for (size_t i = 0, L = sel->length(); i < L; ++i) {+ if (!(*sel)[i]) continue;+ *l << (*sel)[i]->perform(this);+ }+ return l;+ }++ Expression* Listize::operator()(Compound_Selector* sel)+ {+ To_String to_string;+ std::string str;+ for (size_t i = 0, L = sel->length(); i < L; ++i) {+ Expression* e = (*sel)[i]->perform(this);+ if (e) str += e->perform(&to_string);+ }+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), str);+ }++ Expression* Listize::operator()(Complex_Selector* sel)+ {+ List* l = SASS_MEMORY_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;+ }++ To_String to_string;+ std::string reference = ! sel->reference() ? ""+ : sel->reference()->perform(&to_string);+ switch(sel->combinator())+ {+ case Complex_Selector::PARENT_OF:+ *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), ">");+ break;+ case Complex_Selector::ADJACENT_TO:+ *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "+");+ break;+ case Complex_Selector::REFERENCE:+ *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, sel->pstate(), "/" + reference + "/");+ break;+ case Complex_Selector::PRECEDES:+ *l << SASS_MEMORY_NEW(ctx.mem, String_Quoted, 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::fallback_impl(AST_Node* n)+ {+ return dynamic_cast<Expression*>(n);+ }++}
+ libsass/src/listize.hpp view
@@ -0,0 +1,39 @@+#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 {++ 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*);++ template <typename U>+ Expression* fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/src/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/src/memory_manager.cpp view
@@ -0,0 +1,76 @@+#include "ast.hpp"+#include "memory_manager.hpp"++namespace Sass {++ Memory_Manager::Memory_Manager(size_t size)+ : nodes(std::vector<Memory_Object*>())+ {+ size_t init = size;+ if (init < 8) init = 8;+ // reserve some space+ nodes.reserve(init);+ }++ Memory_Manager::~Memory_Manager()+ {+ // release memory for all controlled nodes+ // avoid calling erase for every single node+ for (size_t i = 0, S = nodes.size(); i < S; ++i) {+ deallocate(nodes[i]);+ }+ // just in case+ nodes.clear();+ }++ Memory_Object* Memory_Manager::add(Memory_Object* np)+ {+ // object has been initialized+ // it can be "deleted" from now on+ np->refcount = 1;+ return np;+ }++ bool Memory_Manager::has(Memory_Object* np)+ {+ // check if the pointer is controlled under our pool+ return find(nodes.begin(), nodes.end(), np) != nodes.end();+ }++ Memory_Object* Memory_Manager::allocate(size_t size)+ {+ // allocate requested memory+ void* heap = malloc(size);+ // init internal refcount status to zero+ (static_cast<Memory_Object*>(heap))->refcount = 0;+ // add the memory under our management+ nodes.push_back(static_cast<Memory_Object*>(heap));+ // cast object to its initial type+ return static_cast<Memory_Object*>(heap);+ }++ void Memory_Manager::deallocate(Memory_Object* np)+ {+ // only call destructor if initialized+ if (np->refcount) np->~Memory_Object();+ // always free the memory+ free(np);+ }++ void Memory_Manager::remove(Memory_Object* np)+ {+ // remove node from pool (no longer active)+ nodes.erase(find(nodes.begin(), nodes.end(), np));+ // you are now in control of the memory+ }++ void Memory_Manager::destroy(Memory_Object* np)+ {+ // remove from pool+ remove(np);+ // release memory+ deallocate(np);+ }++}+
+ libsass/src/memory_manager.hpp view
@@ -0,0 +1,48 @@+#ifndef SASS_MEMORY_MANAGER_H+#define SASS_MEMORY_MANAGER_H++#include <vector>++namespace Sass {++ class Memory_Object {+ friend class Memory_Manager;+ long refcount;+ public:+ Memory_Object() { refcount = 0; };+ virtual ~Memory_Object() {};+ };++ /////////////////////////////////////////////////////////////////////////////+ // 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.+ /////////////////////////////////////////////////////////////////////////////+ class Memory_Manager {+ std::vector<Memory_Object*> nodes;++ public:+ Memory_Manager(size_t size = 0);+ ~Memory_Manager();++ bool has(Memory_Object* np);+ Memory_Object* allocate(size_t size);+ void deallocate(Memory_Object* np);+ void remove(Memory_Object* np);+ void destroy(Memory_Object* np);+ Memory_Object* add(Memory_Object* np);++ };+}++///////////////////////////////////////////////////////////////////////////////+// Use macros for the allocation task, since overloading operator `new`+// has been proven to be flaky under certain compilers (see comment below).+///////////////////////////////////////////////////////////////////////////////++#define SASS_MEMORY_NEW(mgr, Class, ...) \+ (static_cast<Class*>(mgr.add(new (mgr.allocate(sizeof(Class))) Class(__VA_ARGS__)))) \++#endif
+ libsass/src/node.cpp view
@@ -0,0 +1,322 @@+#include <vector>++#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);++ Node n(SELECTOR, Complex_Selector::ANCESTOR_OF, pStripped, null /*pCollection*/);+ if (pSelector) n.got_line_feed = pSelector->has_line_feed();+ return n;+ }+++ Node Node::createCollection() {+ NodeDequePtr pEmptyCollection = std::make_shared<NodeDeque>();+ return Node(COLLECTION, Complex_Selector::ANCESTOR_OF, NULL /*pSelector*/, pEmptyCollection);+ }+++ Node Node::createCollection(const NodeDeque& values) {+ NodeDequePtr pShallowCopiedCollection = std::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 = std::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));+ }+ }++ Node n(mType, mCombinator, mpSelector ? mpSelector->clone(ctx) : NULL, pNewCollection);+ n.got_line_feed = got_line_feed;+ return n;+ }+++ 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+ std::ostream& operator<<(std::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;+ case Complex_Selector::REFERENCE: 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();+ node.got_line_feed = pToConvert->has_line_feed();+ bool has_lf = pToConvert->has_line_feed();++ // unwrap the selector from parent ref+ if (pToConvert->head() && pToConvert->head()->has_parent_ref()) {+ Complex_Selector* tail = pToConvert->tail();+ if (tail) tail->has_line_feed(pToConvert->has_line_feed());+ pToConvert = tail;+ }++ while (pToConvert) {++ bool empty_parent_ref = pToConvert->head() && pToConvert->head()->is_empty_reference();++ if (pToConvert->head() == NULL || empty_parent_ref) {+ }++ // the first Complex_Selector may contain a dummy head pointer, skip it.+ if (pToConvert->head() != NULL && !empty_parent_ref) {+ node.collection()->push_back(Node::createSelector(pToConvert, ctx));+ if (has_lf) node.collection()->back().got_line_feed = has_lf;+ has_lf = false;+ }++ if (pToConvert->combinator() != Complex_Selector::ANCESTOR_OF) {+ node.collection()->push_back(Node::createCombinator(pToConvert->combinator()));+ if (has_lf) node.collection()->back().got_line_feed = has_lf;+ has_lf = false;+ }++ if (pToConvert && empty_parent_ref && pToConvert->tail()) {+ // pToConvert->tail()->has_line_feed(pToConvert->has_line_feed());+ }++ 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();++ std::string noPath("");+ Position noPosition(-1, -1, -1);+ Complex_Selector* pFirst = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL);++ Complex_Selector* pCurrent = pFirst;++ if (toConvert.isSelector()) pFirst->has_line_feed(toConvert.got_line_feed);+ if (toConvert.isCombinator()) pFirst->has_line_feed(toConvert.got_line_feed);++ 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()+ // if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);+ pCurrent = pCurrent->tail();+ } else if (child.isCombinator()) {+ pCurrent->combinator(child.combinator());+ if (child.got_line_feed) pCurrent->has_line_feed(child.got_line_feed);++ // 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(SASS_MEMORY_NEW(ctx.mem, Complex_Selector, ParserState("[NODE]"), Complex_Selector::ANCESTOR_OF, NULL, NULL));+ if (nextNode.got_line_feed) pCurrent->tail()->has_line_feed(nextNode.got_line_feed);+ 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 = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, ParserState("[NODE]"), 1);+ Parent_Selector* selectorRef = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, ParserState("[NODE]"));+ fakeHead->elements().push_back(selectorRef);+ if (toConvert.got_line_feed) pFirst->has_line_feed(toConvert.got_line_feed);+ // pFirst->has_line_feed(pFirst->has_line_feed() || pFirst->tail()->has_line_feed() || toConvert.got_line_feed);+ pFirst->head(fakeHead);+ return pFirst;+ }++ // A very naive trim function, which removes duplicates in a node+ // This is only used in Complex_Selector::unify_with for now, may need modifications to fit other needs+ Node Node::naiveTrim(Node& seqses, Context& ctx) {++ std::vector<Node*> res;+ std::vector<Complex_Selector*> known;++ NodeDeque::reverse_iterator seqsesIter = seqses.collection()->rbegin(),+ seqsesIterEnd = seqses.collection()->rend();++ for (; seqsesIter != seqsesIterEnd; ++seqsesIter)+ {+ Node& seqs1 = *seqsesIter;+ if( seqs1.isSelector() ) {+ Complex_Selector* sel = seqs1.selector();+ std::vector<Complex_Selector*>::iterator it;+ bool found = false;+ for (it = known.begin(); it != known.end(); ++it) {+ if (**it == *sel) { found = true; break; }+ }+ if( !found ) {+ known.push_back(seqs1.selector());+ res.push_back(&seqs1);+ }+ } else {+ res.push_back(&seqs1);+ }+ }++ Node result = Node::createCollection();++ for (size_t i = res.size() - 1; i != std::string::npos; --i) {+ result.collection()->push_back(*res[i]);+ }++ return result;+ }+}
+ libsass/src/node.hpp view
@@ -0,0 +1,120 @@+#ifndef SASS_NODE_H+#define SASS_NODE_H++#include <deque>+#include <memory>++#include "ast.hpp"+++namespace Sass {+++++ 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 std::deque<Node> NodeDeque;+ typedef std::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();+ static Node naiveTrim(Node& seqses, Context& ctx);++ 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+ std::ostream& operator<<(std::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/src/operation.hpp view
@@ -0,0 +1,172 @@+#ifndef SASS_OPERATION_H+#define SASS_OPERATION_H++#include "ast_fwd_decl.hpp"++namespace Sass {++ 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()(Supports_Block* x) = 0;+ virtual T operator()(Media_Block* x) = 0;+ virtual T operator()(At_Root_Block* x) = 0;+ virtual T operator()(At_Rule* x) = 0;+ 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()(Custom_Warning* x) = 0;+ virtual T operator()(Custom_Error* 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_Quoted* x) = 0;+ virtual T operator()(String_Constant* x) = 0;+ virtual T operator()(Supports_Condition* x) = 0;+ virtual T operator()(Supports_Operator* x) = 0;+ virtual T operator()(Supports_Negation* x) = 0;+ virtual T operator()(Supports_Declaration* x) = 0;+ virtual T operator()(Supports_Interpolation* 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_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()(Supports_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()(Custom_Warning* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Custom_Error* 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()(String_Quoted* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Supports_Condition* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Supports_Operator* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Supports_Negation* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Supports_Declaration* x) { return static_cast<D*>(this)->fallback(x); }+ virtual T operator()(Supports_Interpolation* 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_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/src/output.cpp view
@@ -0,0 +1,393 @@+#include "ast.hpp"+#include "output.hpp"+#include "to_string.hpp"++namespace Sass {++ Output::Output(Context* ctx)+ : Inspect(Emitter(ctx)),+ charset(""),+ top_nodes(0)+ {}++ Output::~Output() { }++ void Output::fallback_impl(AST_Node* n)+ {+ return n->perform(this);+ }++ void Output::operator()(Number* n)+ {+ // use values to_string facility+ To_String to_string(ctx);+ std::string res = n->perform(&to_string);+ // check for a valid unit here+ // includes result for reporting+ if (n->numerator_units().size() > 1 ||+ n->denominator_units().size() > 0 ||+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('/') != std::string::npos) ||+ (n->numerator_units().size() && n->numerator_units()[0].find_first_of('*') != std::string::npos)+ ) {+ error(res + " isn't a valid CSS value.", n->pstate());+ }+ // output the final token+ append_token(res, n);+ }++ void Output::operator()(Import* imp)+ {+ top_nodes.push_back(imp);+ }++ void Output::operator()(Map* m)+ {+ To_String to_string(ctx);+ std::string dbg(m->perform(&to_string));+ error(dbg + " isn't a valid CSS value.", m->pstate());+ }++ OutputBuffer Output::get_buffer(void)+ {++ Emitter emitter(ctx);+ Inspect inspect(emitter);++ size_t size_nodes = top_nodes.size();+ for (size_t i = 0; i < size_nodes; i++) {+ top_nodes[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() != SASS_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);+ std::string txt = c->text()->perform(&to_string);+ // if (indentation && txt == "/**/") return;+ bool important = c->is_important();+ if (output_style() != SASS_STYLE_COMPRESSED || important) {+ if (buffer().size() == 0) {+ top_nodes.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() == SASS_STYLE_NESTED) indentation += r->tabs();+ if (ctx && ctx->c_options->source_comments) {+ std::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());+ std::string val(valConst->value());+ if (auto qstr = dynamic_cast<String_Quoted*>(valConst)) {+ if (!qstr->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() == SASS_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()(Supports_Block* f)+ {+ if (f->is_invisible()) return;++ Supports_Condition* c = f->condition();+ 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() == SASS_STYLE_NESTED) indentation += f->tabs();+ append_indentation();+ append_token("@supports", f);+ append_mandatory_space();+ c->perform(this);+ append_scope_opener();++ if (b->has_non_hoistable()) {+ // JMA - hoisted, output the non-hoistable in a nested block, followed by the hoistable+ 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() == SASS_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() == SASS_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();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ if ((*b)[i]) (*b)[i]->perform(this);+ if (i < L - 1) append_special_linefeed();+ }++ if (output_style() == SASS_STYLE_NESTED) indentation -= m->tabs();+ append_scope_closer();+ }++ void Output::operator()(At_Rule* a)+ {+ std::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;+ }+ 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();++ bool format = kwd != "@font-face";;++ 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 && format) 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 && format) 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)+ {+ std::string value(s->value());+ if (s->can_compress_whitespace() && output_style() == SASS_STYLE_COMPRESSED) {+ value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());+ }+ if (!in_comment) {+ append_token(string_to_output(value), s);+ } else {+ append_token(value, s);+ }+ }++}
+ libsass/src/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;++ // 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:+ std::string charset;+ std::vector<AST_Node*> top_nodes;++ public:+ OutputBuffer get_buffer(void);++ virtual void operator()(Map*);+ virtual void operator()(Ruleset*);+ // virtual void operator()(Propset*);+ virtual void operator()(Supports_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()(Number*);+ virtual void operator()(String_Quoted*);+ virtual void operator()(String_Constant*);++ void fallback_impl(AST_Node* n);++ };++}++#endif
+ libsass/src/parser.cpp view
@@ -0,0 +1,2436 @@+#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 "color_maps.hpp"+#include "sass/functions.h"+#include "error_handling.hpp"++#include <typeinfo>+#include <tuple>++namespace Sass {+ using namespace Constants;+ using namespace Prelexer;++ 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 = SASS_MEMORY_NEW(ctx.mem, Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate)+ {+ Parser p(ctx, pstate);+ p.source = beg;+ p.position = p.source;+ p.end = end;+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ Selector_List* Parser::parse_selector(const char* src, Context& ctx, ParserState pstate)+ {+ Parser p = Parser::from_c_str(src, ctx, pstate);+ // ToDo: ruby sass errors on parent references+ // ToDo: remap the source-map entries somehow+ return p.parse_selector_list(false);+ }++ bool Parser::peek_newline(const char* start)+ {+ return peek_linefeed(start ? start : position)+ && ! peek_css<exactly<'{'>>(start);+ }++ 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 = SASS_MEMORY_NEW(ctx.mem, Block, pstate);+ p.block_stack.push_back(root);+ root->is_root(true);+ return p;+ }++ /* main entry point to parse root block */+ Block* Parser::parse()+ {+ bool is_root = false;+ Block* root = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, true);+ read_bom();++ // custom headers+ if (ctx.resources.size() == 1) {+ is_root = true;+ ctx.apply_custom_headers(root, path, pstate);+ }++ block_stack.push_back(root);+ /* bool rv = */ parse_block_nodes(is_root);+ block_stack.pop_back();++ // update for end position+ root->update_pstate(pstate);++ if (position != end) {+ css_error("Invalid CSS", " after ", ": expected selector or at-rule, was ");+ }++ return root;+ }+++ // convenience function for block parsing+ // will create a new block ad-hoc for you+ // this is the base block parsing function+ Block* Parser::parse_css_block(bool is_root)+ {++ // parse comments before block+ // lex < optional_css_comments >();++ // lex mandatory opener or error out+ if (!lex_css < exactly<'{'> >()) {+ css_error("Invalid CSS", " after ", ": expected \"{\", was ");+ }+ // create new block and push to the selector stack+ Block* block = SASS_MEMORY_NEW(ctx.mem, Block, pstate, 0, is_root);+ block_stack.push_back(block);++ if (!parse_block_nodes()) css_error("Invalid CSS", " after ", ": expected \"}\", was ");;++ if (!lex_css < exactly<'}'> >()) {+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");+ }++ // update for end position+ block->update_pstate(pstate);++ // parse comments after block+ // lex < optional_css_comments >();++ block_stack.pop_back();++ return block;+ }++ // convenience function for block parsing+ // will create a new block ad-hoc for you+ // also updates the `in_at_root` flag+ Block* Parser::parse_block(bool is_root)+ {+ LOCAL_FLAG(in_at_root, is_root);+ return parse_css_block(is_root);+ }++ // the main block parsing function+ // parses stuff between `{` and `}`+ bool Parser::parse_block_nodes(bool is_root)+ {++ // loop until end of string+ while (position < end) {++ // we should be able to refactor this+ parse_block_comments();+ lex < css_whitespace >();++ if (lex < exactly<';'> >()) continue;+ if (peek < end_of_file >()) return true;+ if (peek < exactly<'}'> >()) return true;++ if (parse_block_node(is_root)) continue;++ parse_block_comments();++ if (lex_css < exactly<';'> >()) continue;+ if (peek_css < end_of_file >()) return true;+ if (peek_css < exactly<'}'> >()) return true;++ // illegal sass+ return false;+ }+ // return success+ return true;+ }++ // parser for a single node in a block+ // semicolons must be lexed beforehand+ bool Parser::parse_block_node(bool is_root) {++ Block* block = block_stack.back();++ while (lex< block_comment >()) {+ bool is_important = lexed.begin[2] == '!';+ String* contents = parse_interpolated_chunk(lexed);+ (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);+ }++ // throw away white-space+ // includes line comments+ lex < css_whitespace >();++ Lookahead lookahead_result;++ // also parse block comments++ // first parse everything that is allowed in functions+ if (lex < variable >(true)) { (*block) << parse_assignment(); }+ else if (lex < kwd_err >(true)) { (*block) << parse_error(); }+ else if (lex < kwd_dbg >(true)) { (*block) << parse_debug(); }+ else if (lex < kwd_warn >(true)) { (*block) << parse_warning(); }+ else if (lex < kwd_if_directive >(true)) { (*block) << parse_if_directive(); }+ else if (lex < kwd_for_directive >(true)) { (*block) << parse_for_directive(); }+ else if (lex < kwd_each_directive >(true)) { (*block) << parse_each_directive(); }+ else if (lex < kwd_while_directive >(true)) { (*block) << parse_while_directive(); }+ else if (lex < kwd_return_directive >(true)) { (*block) << parse_return_directive(); }++ // abort if we are in function context and have nothing parsed yet+ else if (stack.back() == function_def) {+ error("Functions can only contain variable declarations and control directives", pstate);+ }++ // parse imports to process later+ else if (lex < kwd_import >(true)) {+ if (stack.back() == mixin_def || stack.back() == function_def) {+ error("Import directives may not be used within control directives or mixins.", pstate);+ }+ Import* imp = parse_import();+ // if it is a url, we only add the statement+ if (!imp->urls().empty()) (*block) << imp;+ // process all resources now (add Import_Stub nodes)+ for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {+ (*block) << SASS_MEMORY_NEW(ctx.mem, Import_Stub, pstate, imp->incs()[i]);+ }+ }++ else if (lex < kwd_extend >(true)) {+ if (block->is_root()) {+ error("Extend directives may only be used within rules.", pstate);+ }++ Lookahead lookahead = lookahead_for_include(position);+ if (!lookahead.found) css_error("Invalid CSS", " after ", ": expected selector, was ");+ Selector* target;+ if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found);+ else target = parse_selector_list(true);+ (*block) << SASS_MEMORY_NEW(ctx.mem, Extension, pstate, target);+ }++ // selector may contain interpolations which need delayed evaluation+ else if (!(lookahead_result = lookahead_for_selector(position)).error)+ { (*block) << parse_ruleset(lookahead_result, is_root); }++ // parse multiple specific keyword directives+ else if (lex < kwd_media >(true)) { (*block) << parse_media_block(); }+ else if (lex < kwd_at_root >(true)) { (*block) << parse_at_root_block(); }+ else if (lex < kwd_include_directive >(true)) { (*block) << parse_include_directive(); }+ else if (lex < kwd_content_directive >(true)) { (*block) << parse_content_directive(); }+ else if (lex < kwd_supports_directive >(true)) { (*block) << parse_supports_directive(); }+ else if (lex < kwd_mixin >(true)) { (*block) << parse_definition(Definition::MIXIN); }+ else if (lex < kwd_function >(true)) { (*block) << parse_definition(Definition::FUNCTION); }++ // ignore the @charset directive for now+ else if (lex< kwd_charset_directive >(true)) { parse_charset_directive(); }++ // generic at keyword (keep last)+ else if (lex< at_keyword >(true)) { (*block) << parse_at_rule(); }++ else if (block->is_root()) {+ lex< css_whitespace >();+ if (position >= end) return true;+ css_error("Invalid CSS", " after ", ": expected 1 selector or at-rule, was ");+ }+ // parse a declaration+ else+ {+ // ToDo: how does it handle parse errors?+ // maybe we are expected to parse something?+ Declaration* decl = parse_declaration();+ decl->tabs(indentation);+ (*block) << decl;+ // maybe we have a "sub-block"+ if (peek< exactly<'{'> >()) {+ if (decl->is_indented()) ++ indentation;+ // parse a propset that rides on the declaration's property+ (*block) << SASS_MEMORY_NEW(ctx.mem, Propset, pstate, decl->property(), parse_block());+ if (decl->is_indented()) -- indentation;+ }+ }+ // something matched+ return true;+ }+ // EO parse_block_nodes++ // parse imports inside the+ Import* Parser::parse_import()+ {+ Import* imp = SASS_MEMORY_NEW(ctx.mem, Import, pstate);+ std::vector<std::pair<std::string,Function_Call*>> to_import;+ bool first = true;+ do {+ while (lex< block_comment >());+ if (lex< quoted_string >()) {+ if (!ctx.call_importers(unquote(std::string(lexed)), path, pstate, imp))+ {+ // push single file import+ // import_single_file(imp, lexed);+ to_import.push_back(std::pair<std::string,Function_Call*>(std::string(lexed), 0));+ }+ }+ else if (lex< uri_prefix >()) {+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);+ Function_Call* result = SASS_MEMORY_NEW(ctx.mem, Function_Call, pstate, "url", args);+ if (lex< quoted_string >()) {+ Expression* the_url = parse_string();+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);+ }+ else if (lex < uri_value >(false)) { // don't skip comments+ String* the_url = parse_interpolated_chunk(lexed);+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);+ }+ else if (peek < skip_over_scopes < exactly < '(' >, exactly < ')' > > >(position)) {+ Expression* the_url = parse_list(); // parse_interpolated_chunk(lexed);+ *args << SASS_MEMORY_NEW(ctx.mem, Argument, the_url->pstate(), the_url);+ }+ else {+ error("malformed URL", pstate);+ }+ if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);+ to_import.push_back(std::pair<std::string, Function_Call*>("", 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<','> >());++ if (!peek_css<alternatives<exactly<';'>,end_of_file>>()) {+ List* media_queries = parse_media_queries();+ imp->media_queries(media_queries);+ }++ for(auto location : to_import) {+ if (location.second) {+ imp->urls().push_back(location.second);+ } else {+ ctx.import_url(imp, location.first, path);+ }+ }++ return imp;+ }++ Definition* Parser::parse_definition(Definition::Type which_type)+ {+ std::string which_str(lexed);+ if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);+ std::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 (which_type == Definition::MIXIN) stack.push_back(mixin_def);+ else stack.push_back(function_def);+ Block* body = parse_block();+ stack.pop_back();+ Definition* def = SASS_MEMORY_NEW(ctx.mem, Definition, source_position_of_def, name, params, body, which_type);+ return def;+ }++ Parameters* Parser::parse_parameters()+ {+ std::string name(lexed);+ Position position = after_token;+ Parameters* params = SASS_MEMORY_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 >();+ std::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 = SASS_MEMORY_NEW(ctx.mem, Parameter, pos, name, val, is_rest);+ return p;+ }++ Arguments* Parser::parse_arguments()+ {+ std::string name(lexed);+ Position position = after_token;+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, pstate);+ if (lex_css< exactly<'('> >()) {+ // if there's anything there at all+ if (!peek_css< exactly<')'> >()) {+ do (*args) << parse_argument();+ 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()+ {+ if (peek_css< sequence < exactly< hash_lbrace >, exactly< rbrace > > >()) {+ position += 2;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }++ Argument* arg;+ if (peek_css< sequence < variable, optional_css_comments, exactly<':'> > >()) {+ lex_css< variable >();+ std::string name(Util::normalize_underscores(lexed));+ ParserState p = pstate;+ lex_css< exactly<':'> >();+ Expression* val = parse_space_list();+ val->is_delayed(false);+ arg = SASS_MEMORY_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 = SASS_MEMORY_NEW(ctx.mem, Argument, pstate, val, "", is_arglist, is_keyword);+ }+ return arg;+ }++ Assignment* Parser::parse_assignment()+ {+ std::string name(Util::normalize_underscores(lexed));+ ParserState var_source_position = pstate;+ if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);+ Expression* val;+ Lookahead lookahead = lookahead_for_value(position);+ if (lookahead.has_interpolants && lookahead.found) {+ val = parse_value_schema(lookahead.found);+ } else {+ val = parse_list();+ }+ val->is_delayed(false);+ bool is_default = false;+ bool is_global = false;+ while (peek< alternatives < default_flag, global_flag > >()) {+ if (lex< default_flag >()) is_default = true;+ else if (lex< global_flag >()) is_global = true;+ }+ Assignment* var = SASS_MEMORY_NEW(ctx.mem, Assignment, var_source_position, name, val, is_default, is_global);+ return var;+ }++ // a ruleset connects a selector and a block+ Ruleset* Parser::parse_ruleset(Lookahead lookahead, bool is_root)+ {+ // make sure to move up the the last position+ lex < optional_css_whitespace >(false, true);+ // create the connector object (add parts later)+ Ruleset* ruleset = SASS_MEMORY_NEW(ctx.mem, Ruleset, pstate);+ // parse selector static or as schema to be evaluated later+ if (lookahead.parsable) ruleset->selector(parse_selector_list(is_root));+ else ruleset->selector(parse_selector_schema(lookahead.found));+ // then parse the inner block+ ruleset->block(parse_block());+ // update for end position+ ruleset->update_pstate(pstate);+ // inherit is_root from parent block+ // need this info for sanity checks+ ruleset->is_root(is_root);+ // return AST Node+ return ruleset;+ }++ // parse a selector schema that will be evaluated in the eval stage+ // uses a string schema internally to do the actual schema handling+ // in the eval stage we will be re-parse it into an actual selector+ Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector)+ {+ // move up to the start+ lex< optional_spaces >();+ const char* i = position;+ // selector schema re-uses string schema implementation+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);+ // the selector schema is pretty much just a wrapper for the string schema+ Selector_Schema* selector_schema = SASS_MEMORY_NEW(ctx.mem, Selector_Schema, pstate, schema);+ selector_schema->media_block(last_media_block);++ // process until end+ 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) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));+ // check if the interpolation only contains white-space (error out)+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ // skip over all nested inner interpolations up to our own delimiter+ const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, end_of_selector);+ // pass inner expression to the parser to resolve nested interpolations+ Expression* interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();+ // set status on the list expression+ interpolant->is_interpolant(true);+ // add to the string schema+ (*schema) << interpolant;+ // advance position+ i = j;+ }+ // no more interpolants have been found+ // add the last segment if there is one+ else {+ // make sure to add the last bits of the string up to the end (if any)+ if (i < end_of_selector) (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, end_of_selector));+ // exit loop+ i = end_of_selector;+ }+ }+ // EO until eos++ // update position+ position = i;++ // update for end position+ selector_schema->update_pstate(pstate);++ // return parsed result+ return selector_schema;+ }+ // EO parse_selector_schema++ void Parser::parse_charset_directive()+ {+ lex <+ sequence <+ quoted_string,+ optional_spaces,+ exactly <';'>+ >+ >();+ }++ // called after parsing `kwd_include_directive`+ Mixin_Call* Parser::parse_include_directive()+ {+ // lex identifier into `lexed` var+ lex_identifier(); // may error out+ // normalize underscores to hyphens+ std::string name(Util::normalize_underscores(lexed));+ // create the initial mixin call object+ Mixin_Call* call = SASS_MEMORY_NEW(ctx.mem, Mixin_Call, pstate, name, 0, 0);+ // parse mandatory arguments+ call->arguments(parse_arguments());+ // parse optional block+ if (peek < exactly <'{'> >()) {+ call->block(parse_block());+ }+ // return ast node+ return call;+ }+ // EO parse_include_directive++ // parse a list of complex selectors+ // this is the main entry point for most+ Selector_List* Parser::parse_selector_list(bool in_root)+ {+ bool reloop = true;+ bool had_linefeed = false;+ Complex_Selector* sel = 0;+ To_String to_string(&ctx);+ Selector_List* group = SASS_MEMORY_NEW(ctx.mem, Selector_List, pstate);+ group->media_block(last_media_block);++ do {+ reloop = false;++ had_linefeed = had_linefeed || peek_newline();++ if (peek_css< class_char < selector_list_delims > >())+ break; // in case there are superfluous commas at the end+++ // now parse the complex selector+ sel = parse_complex_selector(in_root);++ if (!sel) return group;++ sel->has_line_feed(had_linefeed);++ had_linefeed = false;++ while (peek_css< exactly<','> >())+ {+ lex< css_comments >(false);+ // consume everything up and including the comma speparator+ reloop = lex< exactly<','> >() != 0;+ // remember line break (also between some commas)+ had_linefeed = had_linefeed || peek_newline();+ // remember line break (also between some commas)+ }+ (*group) << sel;+ }+ while (reloop);+ while (lex_css< kwd_optional >()) {+ group->is_optional(true);+ }+ // update for end position+ group->update_pstate(pstate);+ if (sel) sel->last()->has_line_break(false);+ return group;+ }+ // EO parse_selector_list++ // a complex selector combines a compound selector with another+ // complex selector, with one of four combinator operations.+ // the compound selector (head) is optional, since the combinator+ // can come first in the whole selector sequence (like `> DIV').+ Complex_Selector* Parser::parse_complex_selector(bool in_root)+ {++ String* reference = 0;+ lex < block_comment >();+ // parse the left hand side+ Compound_Selector* lhs = 0;+ // special case if it starts with combinator ([+~>])+ if (!peek_css< class_char < selector_combinator_ops > >()) {+ // parse the left hand side+ lhs = parse_compound_selector();+ }++ // check for end of file condition+ if (peek < end_of_file >()) return 0;++ // parse combinator between lhs and rhs+ Complex_Selector::Combinator combinator;+ if (lex< exactly<'+'> >()) combinator = Complex_Selector::ADJACENT_TO;+ else if (lex< exactly<'~'> >()) combinator = Complex_Selector::PRECEDES;+ else if (lex< exactly<'>'> >()) combinator = Complex_Selector::PARENT_OF;+ else if (lex< sequence < exactly<'/'>, negate < exactly < '*' > > > >()) {+ // comments are allowed, but not spaces?+ combinator = Complex_Selector::REFERENCE;+ if (!lex < re_reference_combinator >()) return 0;+ reference = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ if (!lex < exactly < '/' > >()) return 0; // ToDo: error msg?+ }+ else /* if (lex< zero >()) */ combinator = Complex_Selector::ANCESTOR_OF;++ if (!lhs && combinator == Complex_Selector::ANCESTOR_OF) return 0;++ // lex < block_comment >();+ // source position of a complex selector points to the combinator+ // ToDo: make sure we update pstate for ancestor of (lex < zero >());+ Complex_Selector* sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, combinator, lhs);+ sel->media_block(last_media_block);++ if (combinator == Complex_Selector::REFERENCE) sel->reference(reference);+ // has linfeed after combinator?+ sel->has_line_break(peek_newline());+ // sel->has_line_feed(has_line_feed);++ // check if we got the abort condition (ToDo: optimize)+ if (!peek_css< class_char < complex_selector_delims > >()) {+ // parse next selector in sequence+ sel->tail(parse_complex_selector(true));+ if (sel->tail()) {+ // ToDo: move this logic below into tail setter+ if (sel->tail()->has_reference()) sel->has_reference(true);+ if (sel->tail()->has_placeholder()) sel->has_placeholder(true);+ }+ }++ // add a parent selector if we are not in a root+ // also skip adding parent ref if we only have refs+ if (!sel->has_reference() && !in_at_root && !in_root) {+ // create the objects to wrap parent selector reference+ Parent_Selector* parent = SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);+ parent->media_block(last_media_block);+ Compound_Selector* head = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);+ head->media_block(last_media_block);+ // add simple selector+ (*head) << parent;+ // selector may not have any head yet+ if (!sel->head()) { sel->head(head); }+ // otherwise we need to create a new complex selector and set the old one as its tail+ else {+ sel = SASS_MEMORY_NEW(ctx.mem, Complex_Selector, pstate, Complex_Selector::ANCESTOR_OF, head, sel);+ sel->media_block(last_media_block);+ }+ // peek for linefeed and remember result on head+ // if (peek_newline()) head->has_line_break(true);+ }++ // complex selector+ return sel;+ }+ // EO parse_complex_selector++ // parse one compound selector, which is basically+ // a list of simple selectors (directly adjancent)+ // lex them exactly (without skipping white-space)+ Compound_Selector* Parser::parse_compound_selector()+ {+ // init an empty compound selector wrapper+ Compound_Selector* seq = SASS_MEMORY_NEW(ctx.mem, Compound_Selector, pstate);+ seq->media_block(last_media_block);++ // skip initial white-space+ lex< css_whitespace >();++ // parse list+ while (true)+ {+ // remove all block comments (don't skip white-space)+ lex< delimited_by< slash_star, star_slash, false > >(false);+ // parse functional+ if (peek < re_pseudo_selector >())+ {+ (*seq) << parse_simple_selector();+ }+ // parse parent selector+ else if (lex< exactly<'&'> >(false))+ {+ // this produces a linefeed!?+ seq->has_parent_reference(true);+ (*seq) << SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate);+ }+ // parse type selector+ else if (lex< re_type_selector >(false))+ {+ (*seq) << SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);+ }+ // peek for abort conditions+ else if (peek< spaces >()) break;+ else if (peek< end_of_file >()) { break; }+ else if (peek_css < class_char < selector_combinator_ops > >()) break;+ else if (peek_css < class_char < complex_selector_delims > >()) break;+ // otherwise parse another simple selector+ else {+ Simple_Selector* sel = parse_simple_selector();+ if (!sel) return 0;+ (*seq) << sel;+ }+ }++ if (seq && !peek_css<exactly<'{'>>()) {+ seq->has_line_break(peek_newline());+ }++ // EO while true+ return seq;++ }+ // EO parse_compound_selector++ Simple_Selector* Parser::parse_simple_selector()+ {+ lex < css_comments >(false);+ if (lex< alternatives < id_name, class_name > >()) {+ return SASS_MEMORY_NEW(ctx.mem, Selector_Qualifier, pstate, lexed);+ }+ else if (lex< quoted_string >()) {+ return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, unquote(lexed));+ }+ else if (lex< alternatives < variable, number, static_reference_combinator > >()) {+ return SASS_MEMORY_NEW(ctx.mem, Type_Selector, pstate, lexed);+ }+ else if (peek< pseudo_not >()) {+ return parse_negated_selector();+ }+ else if (peek< re_pseudo_selector >()) {+ return parse_pseudo_selector();+ }+ else if (peek< exactly<':'> >()) {+ return parse_pseudo_selector();+ }+ else if (lex < exactly<'['> >()) {+ return parse_attribute_selector();+ }+ else if (lex< placeholder >()) {+ Selector_Placeholder* sel = SASS_MEMORY_NEW(ctx.mem, Selector_Placeholder, pstate, lexed);+ sel->media_block(last_media_block);+ return sel;+ }+ // failed+ return 0;+ }++ Wrapped_Selector* Parser::parse_negated_selector()+ {+ lex< pseudo_not >();+ std::string name(lexed);+ ParserState nsource_position = pstate;+ Selector* negated = parse_selector_list(true);+ if (!lex< exactly<')'> >()) {+ error("negated selector is missing ')'", pstate);+ }+ name.erase(name.size() - 1);+ return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, nsource_position, name, negated);+ }++ // a pseudo selector often starts with one or two colons+ // it can contain more selectors inside parantheses+ Simple_Selector* Parser::parse_pseudo_selector() {+ if (lex< sequence<+ optional < pseudo_prefix >,+ // we keep the space within the name, strange enough+ // ToDo: refactor output to schedule the space for it+ // or do we really want to keep the real white-space?+ sequence< identifier, optional < block_comment >, exactly<'('> >+ > >())+ {++ std::string name(lexed);+ name.erase(name.size() - 1);+ ParserState p = pstate;++ // specially parse static stuff+ // ToDo: really everything static?+ if (peek_css <+ sequence <+ alternatives <+ static_value,+ binomial+ >,+ optional_css_whitespace,+ exactly<')'>+ >+ >()+ ) {+ lex_css< alternatives < static_value, binomial > >();+ String_Constant* expr = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ if (expr && lex_css< exactly<')'> >()) {+ expr->can_compress_whitespace(true);+ return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, p, name, expr);+ }+ }+ else if (Selector* wrapped = parse_selector_list(true)) {+ if (wrapped && lex_css< exactly<')'> >()) {+ return SASS_MEMORY_NEW(ctx.mem, Wrapped_Selector, p, name, wrapped);+ }+ }++ }+ // EO if pseudo selector++ else if (lex < sequence< optional < pseudo_prefix >, identifier > >()) {+ return SASS_MEMORY_NEW(ctx.mem, Pseudo_Selector, pstate, lexed);+ }+ else if(lex < pseudo_prefix >()) {+ css_error("Invalid CSS", " after ", ": expected pseudoclass or pseudoelement, was ");+ }++ css_error("Invalid CSS", " after ", ": expected \")\", was ");++ // unreachable statement+ return 0;+ }++ Attribute_Selector* Parser::parse_attribute_selector()+ {+ ParserState p = pstate;+ if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);+ std::string name(lexed);+ if (lex_css< alternatives < exactly<']'>, exactly<'/'> > >()) return SASS_MEMORY_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);+ }+ std::string matcher(lexed);++ String* value = 0;+ if (lex_css< identifier >()) {+ value = SASS_MEMORY_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< alternatives < exactly<']'>, exactly<'/'> > >()) error("unterminated attribute selector for " + name, pstate);+ return SASS_MEMORY_NEW(ctx.mem, Attribute_Selector, p, name, matcher, value);+ }++ /* parse block comment and add to block */+ void Parser::parse_block_comments()+ {+ Block* block = block_stack.back();+ while (lex< block_comment >()) {+ bool is_important = lexed.begin[2] == '!';+ String* contents = parse_interpolated_chunk(lexed);+ (*block) << SASS_MEMORY_NEW(ctx.mem, Comment, pstate, contents, is_important);+ }+ }++ Declaration* Parser::parse_declaration() {+ String* prop = 0;+ if (lex< sequence< optional< exactly<'*'> >, identifier_schema > >()) {+ prop = parse_identifier_schema();+ }+ else if (lex< sequence< optional< exactly<'*'> >, identifier, zero_plus< block_comment > > >()) {+ prop = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ prop->is_delayed(true);+ }+ else {+ css_error("Invalid CSS", " after ", ": expected \"}\", was ");+ }+ bool is_indented = true;+ const std::string property(lexed);+ if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);+ lex < css_comments >(false);+ if (peek_css< exactly<';'> >()) error("style declaration must contain a value", pstate);+ if (peek_css< exactly<'{'> >()) is_indented = false; // don't indent if value is empty+ if (peek_css< static_value >()) {+ return SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, parse_static_value()/*, lex<kwd_important>()*/);+ }+ else {+ Expression* value;+ Lookahead lookahead = lookahead_for_value(position);+ if (lookahead.found) {+ if (lookahead.has_interpolants) {+ value = parse_value_schema(lookahead.found);+ } else {+ value = parse_list();+ }+ }+ else {+ value = parse_list();+ if (List* list = dynamic_cast<List*>(value)) {+ if (list->length() == 0 && !peek< exactly <'{'> >()) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ }+ }+ lex < css_comments >(false);+ auto decl = SASS_MEMORY_NEW(ctx.mem, Declaration, prop->pstate(), prop, value/*, lex<kwd_important>()*/);+ decl->is_indented(is_indented);+ return decl;+ }+ }++ // 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()+ {+ Expression* key = parse_list();+ Map* map = SASS_MEMORY_NEW(ctx.mem, Map, pstate, 1);+ if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {+ if (!str->quote_mark() && !str->is_delayed()) {+ if (const Color* col = name_to_color(str->value())) {+ Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);+ c->pstate(str->pstate());+ c->disp(str->value());+ key = c;+ }+ }+ }++ // 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) << std::make_pair(key, value);++ while (lex_css< exactly<','> >())+ {+ // allow trailing commas - #495+ if (peek_css< exactly<')'> >(position))+ { break; }++ Expression* key = parse_list();+ if (String_Quoted* str = dynamic_cast<String_Quoted*>(key)) {+ if (!str->quote_mark() && !str->is_delayed()) {+ if (const Color* col = name_to_color(str->value())) {+ Color* c = SASS_MEMORY_NEW(ctx.mem, Color, *col);+ c->pstate(str->pstate());+ c->disp(str->value());+ key = c;+ }+ }+ }++ if (!(lex< exactly<':'> >()))+ { error("invalid syntax", pstate); }++ Expression* value = parse_space_list();++ (*map) << std::make_pair(key, value);+ }++ ParserState ps = map->pstate();+ ps.offset = pstate - ps + pstate.offset;+ map->pstate(ps);++ return map;+ }++ // parse list returns either a space separated list,+ // a comma separated list or any bare expression found.+ // so to speak: we unwrap items from lists if possible here!+ Expression* Parser::parse_list()+ {+ // parse list is relly just an alias+ return parse_comma_list();+ }++ // will return singletons unwrapped+ Expression* Parser::parse_comma_list()+ {+ // check if we have an empty list+ // return the empty list as such+ if (peek_css< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position))+ { return SASS_MEMORY_NEW(ctx.mem, List, pstate, 0); }++ // now try to parse a space list+ Expression* list = parse_space_list();+ // if it's a singleton, return it (don't wrap it)+ if (!peek_css< exactly<','> >(position)) return list;++ // if we got so far, we actually do have a comma list+ List* comma_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_COMMA);+ // wrap the first expression+ (*comma_list) << list;++ while (lex_css< exactly<','> >())+ {+ // check for abort condition+ if (peek_css< alternatives <+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position)+ ) { break; }+ // otherwise add another expression+ (*comma_list) << parse_space_list();+ }+ // return the list+ return comma_list;+ }+ // EO parse_comma_list++ // will return singletons unwrapped+ Expression* Parser::parse_space_list()+ {+ Expression* disj1 = parse_disjunction();+ // if it's a singleton, return it (don't wrap it)+ if (peek_css< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<','>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position)+ ) { return disj1; }++ List* space_list = SASS_MEMORY_NEW(ctx.mem, List, pstate, 2, SASS_SPACE);+ (*space_list) << disj1;++ while (!(peek_css< alternatives <+ // exactly<'!'>,+ exactly<';'>,+ exactly<'}'>,+ exactly<'{'>,+ exactly<')'>,+ exactly<','>,+ exactly<':'>,+ exactly<ellipsis>,+ default_flag,+ global_flag+ > >(position)) && peek_css< optional_css_whitespace >() != end+ ) {+ // the space is parsed implicitly?+ (*space_list) << parse_disjunction();+ }+ // return the list+ return space_list;+ }+ // EO parse_space_list++ // parse logical OR operation+ Expression* Parser::parse_disjunction()+ {+ // parse the left hand side conjunction+ Expression* conj = parse_conjunction();+ // parse multiple right hand sides+ std::vector<Expression*> operands;+ while (lex_css< kwd_or >())+ operands.push_back(parse_conjunction());+ // if it's a singleton, return it directly+ if (operands.size() == 0) return conj;+ // fold all operands into one binary expression+ return fold_operands(conj, operands, Sass_OP::OR);+ }+ // EO parse_disjunction++ // parse logical AND operation+ Expression* Parser::parse_conjunction()+ {+ // parse the left hand side relation+ Expression* rel = parse_relation();+ // parse multiple right hand sides+ std::vector<Expression*> operands;+ while (lex_css< kwd_and >())+ operands.push_back(parse_relation());+ // if it's a singleton, return it directly+ if (operands.size() == 0) return rel;+ // fold all operands into one binary expression+ return fold_operands(rel, operands, Sass_OP::AND);+ }+ // EO parse_conjunction++ // parse comparison operations+ Expression* Parser::parse_relation()+ {+ // parse the left hand side expression+ Expression* lhs = parse_expression();+ // if it's a singleton, return it (don't wrap it)+ if (!(peek< alternatives <+ kwd_eq,+ kwd_neq,+ kwd_gte,+ kwd_gt,+ kwd_lte,+ kwd_lt+ > >(position)))+ { return lhs; }+ // parse the operator+ enum Sass_OP op+ = lex<kwd_eq>() ? Sass_OP::EQ+ : lex<kwd_neq>() ? Sass_OP::NEQ+ : lex<kwd_gte>() ? Sass_OP::GTE+ : lex<kwd_lte>() ? Sass_OP::LTE+ : lex<kwd_gt>() ? Sass_OP::GT+ : lex<kwd_lt>() ? Sass_OP::LT+ // we checked the possibilites on top of fn+ : Sass_OP::EQ;+ // parse the right hand side expression+ Expression* rhs = parse_expression();+ // return binary expression with a left and a right hand side+ return SASS_MEMORY_NEW(ctx.mem, Binary_Expression, lhs->pstate(), op, lhs, rhs);+ }+ // parse_relation++ // parse expression valid for operations+ // called from parse_relation+ // called from parse_for_directive+ // called from parse_media_expression+ // parse addition and subtraction operations+ Expression* Parser::parse_expression()+ {+ Expression* lhs = parse_operators();+ // if it's a singleton, return it (don't wrap it)+ if (!(peek< exactly<'+'> >(position) ||+ // condition is a bit misterious, but some combinations should not be counted as operations+ (peek< no_spaces >(position) && peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< space > > >(position)) ||+ (peek< sequence< negate< unsigned_number >, exactly<'-'>, negate< unsigned_number > > >(position))) ||+ peek< identifier >(position))+ { return lhs; }++ std::vector<Expression*> operands;+ std::vector<Sass_OP> operators;+ while (lex< exactly<'+'> >() || lex< sequence< negate< digit >, exactly<'-'> > >()) {+ operators.push_back(lexed.to_string() == "+" ? Sass_OP::ADD : Sass_OP::SUB);+ operands.push_back(parse_operators());+ }++ if (operands.size() == 0) return lhs;+ return fold_operands(lhs, operands, operators);+ }++ // parse addition and subtraction operations+ Expression* Parser::parse_operators()+ {+ Expression* factor = parse_factor();+ // Special case: Ruby sass never tries to modulo if the lhs contains an interpolant+ if (peek_css< exactly<'%'> >() && factor->concrete_type() == Expression::STRING) {+ String_Schema* ss = dynamic_cast<String_Schema*>(factor);+ if (ss && ss->has_interpolants()) return factor;+ }+ // if it's a singleton, return it (don't wrap it)+ if (!peek_css< class_char< static_ops > >()) return factor;+ // parse more factors and operators+ std::vector<Expression*> operands; // factors+ std::vector<enum Sass_OP> operators; // ops+ // lex operations to apply to lhs+ while (lex_css< class_char< static_ops > >()) {+ switch(*lexed.begin) {+ case '*': operators.push_back(Sass_OP::MUL); break;+ case '/': operators.push_back(Sass_OP::DIV); break;+ case '%': operators.push_back(Sass_OP::MOD); break;+ default: throw std::runtime_error("unknown static op parsed"); break;+ }+ operands.push_back(parse_factor());+ }+ // operands and operators to binary expression+ return fold_operands(factor, operands, operators);+ }+ // EO parse_operators+++ // called from parse_operators+ // called from parse_value_schema+ Expression* Parser::parse_factor()+ {+ lex < css_comments >(false);+ if (lex_css< exactly<'('> >()) {+ // parse_map may return a list+ Expression* value = parse_map();+ // lex the expected closing parenthesis+ if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);+ // expression can be evaluated+ value->is_delayed(false);+ // make sure wrapped lists and division expressions are non-delayed within parentheses+ if (value->concrete_type() == Expression::LIST) {+ List* l = static_cast<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() == Sass_OP::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 (lex < functional_schema >()) {+ return parse_function_call_schema();+ }+ else if (lex< identifier_schema >()) {+ String* string = parse_identifier_schema();+ if (String_Schema* schema = dynamic_cast<String_Schema*>(string)) {+ if (lex < exactly < '(' > >()) {+ *schema << parse_list();+ lex < exactly < ')' > >();+ }+ }+ return string;+ }+ else if (peek< sequence< uri_prefix, W, real_uri_value > >()) {+ return parse_url_function_string();+ }+ else if (peek< re_functional >()) {+ return parse_function_call();+ }+ else if (lex< exactly<'+'> >()) {+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::PLUS, parse_factor());+ }+ else if (lex< exactly<'-'> >()) {+ return SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_factor());+ }+ else if (lex< sequence< kwd_not > >()) {+ return SASS_MEMORY_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 SASS_MEMORY_NEW(ctx.mem, Unary_Expression, pstate, Unary_Expression::MINUS, parse_value());+ }+ else {+ return parse_value();+ }+ }++ // parse one value for a list+ Expression* Parser::parse_value()+ {+ lex< css_comments >(false);+ if (lex< ampersand >())+ {+ return SASS_MEMORY_NEW(ctx.mem, Parent_Selector, pstate); }++ if (lex< kwd_important >())+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, "!important"); }++ if (const char* stop = peek< value_schema >())+ { return parse_value_schema(stop); }++ // string may be interpolated+ if (lex< quoted_string >())+ { return parse_string(); }++ if (lex< kwd_true >())+ { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, true); }++ if (lex< kwd_false >())+ { return SASS_MEMORY_NEW(ctx.mem, Boolean, pstate, false); }++ if (lex< kwd_null >())+ { return SASS_MEMORY_NEW(ctx.mem, Null, pstate); }++ if (lex< identifier >()) {+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ }++ if (lex< percentage >())+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed); }++ // match hex number first because 0x000 looks like a number followed by an indentifier+ if (lex< sequence < alternatives< hex, hex0 >, negate < exactly<'-'> > > >())+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed); }++ if (lex< sequence < exactly <'#'>, identifier > >())+ { return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed); }++ // also handle the 10em- foo special case+ if (lex< sequence< dimension, optional< sequence< exactly<'-'>, negate< digit > > > > >())+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed); }++ if (lex< sequence< static_component, one_plus< strict_identifier > > >())+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }++ if (lex< number >())+ { return SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed); }++ if (lex< variable >())+ { return SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed)); }++ // Special case handling for `%` proceeding an interpolant.+ if (lex< sequence< exactly<'%'>, optional< percentage > > >())+ { return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed); }++ error("error reading values after " + lexed.to_string(), pstate);++ // 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 = SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::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 = SASS_MEMORY_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) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p));+ }+ // we need to skip anything inside strings+ // create a new target in parser/prelexer+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ 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) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, chunk.end));+ break;+ }+ ++ i;+ }++ return schema;+ }++ String_Constant* Parser::parse_static_expression()+ {+ if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {+ return parse_static_value();+ }+ return 0;+ }++ String_Constant* Parser::parse_static_value()+ {+ lex< static_value >();+ Token str(lexed);+ --str.end;+ --position;++ String_Constant* str_node = SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, str.time_wspace());+ str_node->is_delayed(true);+ return str_node;+ }++ String* Parser::parse_string()+ {+ return parse_interpolated_chunk(Token(lexed));+ }++ 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) {+ return SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, std::string(str.begin, str.end));+ }++ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);+ while (i < str.end) {+ p = find_first_in_interval< exactly<hash_lbrace> >(i, str.end);+ if (p) {+ if (i < p) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, p)); // accumulate the preceding segment if it's nonempty+ }+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p+2;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ 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) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(i, str.end));+ }+ break;+ }+ }+ return schema;+ }++ String* Parser::parse_ie_keyword_arg()+ {+ String_Schema* kwd_arg = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate, 3);+ if (lex< variable >()) {+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, Util::normalize_underscores(lexed));+ } else {+ lex< alternatives< identifier_schema, identifier > >();+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ }+ lex< exactly<'='> >();+ *kwd_arg << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ if (peek< variable >()) *kwd_arg << parse_list();+ else if (lex< number >()) *kwd_arg << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, Util::normalize_decimals(lexed));+ else if (peek < ie_keyword_arg_value >()) { *kwd_arg << parse_list(); }+ return kwd_arg;+ }++ String_Schema* Parser::parse_value_schema(const char* stop)+ {+ // initialize the string schema object to add tokens+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);++ if (peek<exactly<'}'>>()) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }++ const char* e = 0;+ size_t num_items = 0;+ while (position < stop) {+ // parse space between tokens+ if (lex< spaces >() && num_items) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");+ }+ if ((e = peek< re_functional >()) && e < stop) {+ (*schema) << parse_function_call();+ }+ // lex an interpolant /#{...}/+ else if (lex< exactly < hash_lbrace > >()) {+ // Try to lex static expression first+ if (peek< exactly< rbrace > >()) {+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ if (lex< re_static_expression >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ } else {+ (*schema) << parse_list();+ }+ // ToDo: no error check here?+ lex < exactly < rbrace > >();+ }+ // lex some string constants+ else if (lex< alternatives < exactly<'%'>, exactly < '-' >, identifier > >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);+ if (*position == '"' || *position == '\'') {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");+ }+ }+ // lex a quoted string+ else if (lex< quoted_string >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed, '"');+ if (*position == '"' || *position == '\'' || alpha(position)) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");+ }+ }+ // lex (normalized) variable+ else if (lex< variable >()) {+ std::string name(Util::normalize_underscores(lexed));+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Variable, pstate, name);+ }+ // lex percentage value+ else if (lex< percentage >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::PERCENTAGE, lexed);+ }+ // lex dimension value+ else if (lex< dimension >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::DIMENSION, lexed);+ }+ // lex number value+ else if (lex< number >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::NUMBER, lexed);+ }+ // lex hex color value+ else if (lex< sequence < hex, negate < exactly < '-' > > > >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, Textual, pstate, Textual::HEX, lexed);+ }+ else if (lex< sequence < exactly <'#'>, identifier > >()) {+ (*schema) << SASS_MEMORY_NEW(ctx.mem, String_Quoted, pstate, lexed);+ }+ // lex a value in parentheses+ else if (peek< parenthese_scope >()) {+ (*schema) << parse_factor();+ }+ else {+ return schema;+ }+ ++num_items;+ }+ return schema;+ }++ // this parses interpolation outside other strings+ // means the result must not be quoted again later+ String* Parser::parse_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 SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, std::string(id.begin, id.end));+ }++ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);+ while (i < id.end) {+ p = find_first_in_interval< exactly<hash_lbrace> >(i, id.end);+ if (p) {+ if (i < p) {+ // accumulate the preceding segment if it's nonempty+ const char* o = position; position = i;+ *schema << parse_value_schema(p);+ position = o;+ }+ // we need to skip anything inside strings+ // create a new target in parser/prelexer+ if (peek < sequence < optional_spaces, exactly<rbrace> > >(p+2)) { position = p;+ css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");+ }+ 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) {+ const char* o = position; position = i;+ *schema << parse_value_schema(id.end);+ position = o;+ }+ break;+ }+ }+ return schema;+ }++ // calc functions should preserve arguments+ Function_Call* Parser::parse_calc_function()+ {+ lex< identifier >();+ std::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< skip_over_scopes <+ exactly < '(' >,+ exactly < ')' >+ > >();++ Argument* arg = SASS_MEMORY_NEW(ctx.mem, Argument, arg_pos, parse_interpolated_chunk(Token(arg_beg, arg_end)));+ Arguments* args = SASS_MEMORY_NEW(ctx.mem, Arguments, arg_pos);+ *args << arg;+ return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, name, args);+ }++ String* Parser::parse_url_function_string()+ {+ const char* p = position;++ lex< uri_prefix >();+ std::string prefix = lexed;++ lex< real_uri_value >(false);+ std::string uri = lexed;++ if (peek< exactly< hash_lbrace > >()) {+ const char* pp = position;+ // TODO: error checking for unclosed interpolants+ while (peek< exactly< hash_lbrace > >(pp)) {+ pp = sequence< interpolant, real_uri_value >(pp);+ }+ position = peek< real_uri_suffix >(pp);+ return parse_interpolated_chunk(Token(p, position));+ } else {+ lex< real_uri_suffix >();+ std::string res = prefix + Util::rtrim(uri) + lexed.to_string();+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, res);+ }++ }++ Function_Call* Parser::parse_function_call()+ {+ lex< identifier >();+ std::string name(lexed);++ ParserState call_pos = pstate;+ Arguments* args = parse_arguments();+ return SASS_MEMORY_NEW(ctx.mem, Function_Call, call_pos, 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 = SASS_MEMORY_NEW(ctx.mem, Function_Call_Schema, source_position_of_call, name, parse_arguments());+ return the_call;+ }++ Content* Parser::parse_content_directive()+ {+ if (stack.back() != mixin_def) {+ error("@content may only be used within a mixin", pstate);+ }+ return SASS_MEMORY_NEW(ctx.mem, Content, pstate);+ }++ If* Parser::parse_if_directive(bool else_if)+ {+ ParserState if_source_position = pstate;+ Expression* predicate = parse_list();+ predicate->is_delayed(false);+ Block* block = parse_block();+ Block* alternative = 0;++ // only throw away comment if we parse a case+ // we want all other comments to be parsed+ if (lex_css< elseif_directive >()) {+ alternative = SASS_MEMORY_NEW(ctx.mem, Block, pstate);+ (*alternative) << parse_if_directive(true);+ }+ else if (lex_css< kwd_else_directive >()) {+ alternative = parse_block();+ }+ return SASS_MEMORY_NEW(ctx.mem, If, if_source_position, predicate, block, alternative);+ }++ For* Parser::parse_for_directive()+ {+ ParserState for_source_position = pstate;+ lex_variable();+ std::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);+ Block* body = parse_block();+ return SASS_MEMORY_NEW(ctx.mem, For, for_source_position, var, lower_bound, upper_bound, body, inclusive);+ }++ // helper to parse a var token+ Token Parser::lex_variable()+ {+ // peek for dollar sign first+ if (!peek< exactly <'$'> >()) {+ css_error("Invalid CSS", " after ", ": expected \"$\", was ");+ }+ // we expect a simple identfier as the call name+ if (!lex< sequence < exactly <'$'>, identifier > >()) {+ lex< exactly <'$'> >(); // move pstate and position up+ css_error("Invalid CSS", " after ", ": expected identifier, was ");+ }+ // return object+ return token;+ }+ // helper to parse identifier+ Token Parser::lex_identifier()+ {+ // we expect a simple identfier as the call name+ if (!lex< identifier >()) { // ToDo: pstate wrong?+ css_error("Invalid CSS", " after ", ": expected identifier, was ");+ }+ // return object+ return token;+ }++ Each* Parser::parse_each_directive()+ {+ ParserState each_source_position = pstate;+ std::vector<std::string> vars;+ lex_variable();+ 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);+ }+ }+ Block* body = parse_block();+ return SASS_MEMORY_NEW(ctx.mem, Each, each_source_position, vars, list, body);+ }++ // called after parsing `kwd_while_directive`+ While* Parser::parse_while_directive()+ {+ // create the initial while call object+ While* call = SASS_MEMORY_NEW(ctx.mem, While, pstate, 0, 0);+ // parse mandatory predicate+ Expression* predicate = parse_list();+ predicate->is_delayed(false);+ call->predicate(predicate);+ // parse mandatory block+ call->block(parse_block());+ // return ast node+ return call;+ }++ // EO parse_while_directive+ Media_Block* Parser::parse_media_block()+ {+ Media_Block* media_block = SASS_MEMORY_NEW(ctx.mem, Media_Block, pstate, 0, 0);+ media_block->media_queries(parse_media_queries());++ Media_Block* prev_media_block = last_media_block;+ last_media_block = media_block;+ media_block->block(parse_css_block());+ last_media_block = prev_media_block;++ return media_block;+ }++ List* Parser::parse_media_queries()+ {+ List* media_queries = SASS_MEMORY_NEW(ctx.mem, List, pstate, 0, SASS_COMMA);+ if (!peek_css < exactly <'{'> >()) (*media_queries) << parse_media_query();+ while (lex_css < exactly <','> >()) (*media_queries) << parse_media_query();+ return media_queries;+ }++ // Expression* Parser::parse_media_query()+ Media_Query* Parser::parse_media_query()+ {+ Media_Query* media_query = SASS_MEMORY_NEW(ctx.mem, Media_Query, pstate);++ lex < css_comments >(false);+ if (lex < kwd_not >()) media_query->is_negated(true);+ else if (lex < kwd_only >()) media_query->is_restricted(true);++ lex < css_comments >(false);+ if (lex < 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_css < kwd_and >()) (*media_query) << parse_media_expression();+ if (lex < identifier_schema >()) {+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);+ *schema << media_query->media_type();+ *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, " ");+ *schema << parse_identifier_schema();+ media_query->media_type(schema);+ }+ while (lex_css < kwd_and >()) (*media_query) << parse_media_expression();+ return media_query;+ }++ Media_Query_Expression* Parser::parse_media_expression()+ {+ if (lex < identifier_schema >()) {+ String* ss = parse_identifier_schema();+ return SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, pstate, ss, 0, true);+ }+ if (!lex_css< exactly<'('> >()) {+ error("media query expression must begin with '('", pstate);+ }+ Expression* feature = 0;+ if (peek_css< 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 SASS_MEMORY_NEW(ctx.mem, Media_Query_Expression, feature->pstate(), feature, expression);+ }++ // lexed after `kwd_supports_directive`+ // these are very similar to media blocks+ Supports_Block* Parser::parse_supports_directive()+ {+ Supports_Condition* cond = parse_supports_condition();+ // create the ast node object for the support queries+ Supports_Block* query = SASS_MEMORY_NEW(ctx.mem, Supports_Block, pstate, cond);+ // additional block is mandatory+ // parse inner block+ query->block(parse_block());+ // return ast node+ return query;+ }++ // parse one query operation+ // may encounter nested queries+ Supports_Condition* Parser::parse_supports_condition()+ {+ lex < css_whitespace >();+ Supports_Condition* cond = parse_supports_negation();+ if (!cond) cond = parse_supports_operator();+ if (!cond) cond = parse_supports_interpolation();+ return cond;+ }++ Supports_Condition* Parser::parse_supports_negation()+ {+ if (!lex < kwd_not >()) return 0;++ Supports_Condition* cond = parse_supports_condition_in_parens();+ return SASS_MEMORY_NEW(ctx.mem, Supports_Negation, pstate, cond);+ }++ Supports_Condition* Parser::parse_supports_operator()+ {+ Supports_Condition* cond = parse_supports_condition_in_parens();+ if (!cond) return 0;++ while (true) {+ Supports_Operator::Operand op = Supports_Operator::OR;+ if (lex < kwd_and >()) { op = Supports_Operator::AND; }+ else if(!lex < kwd_or >()) { break; }++ lex < css_whitespace >();+ Supports_Condition* right = parse_supports_condition_in_parens();++ // Supports_Condition* cc = SASS_MEMORY_NEW(ctx.mem, Supports_Condition, *static_cast<Supports_Condition*>(cond));+ cond = SASS_MEMORY_NEW(ctx.mem, Supports_Operator, pstate, cond, right, op);+ }+ return cond;+ }++ Supports_Condition* Parser::parse_supports_interpolation()+ {+ if (!lex < interpolant >()) return 0;++ String* interp = parse_interpolated_chunk(lexed);+ if (!interp) return 0;++ return SASS_MEMORY_NEW(ctx.mem, Supports_Interpolation, pstate, interp);+ }++ // TODO: This needs some major work. Although feature conditions+ // look like declarations their semantics differ siginificantly+ Supports_Condition* Parser::parse_supports_declaration()+ {+ Supports_Condition* cond = 0;+ // parse something declaration like+ Declaration* declaration = parse_declaration();+ if (!declaration) error("@supports condition expected declaration", pstate);+ cond = SASS_MEMORY_NEW(ctx.mem, Supports_Declaration,+ declaration->pstate(),+ declaration->property(),+ declaration->value());+ // ToDo: maybe we need an additional error condition?+ return cond;+ }++ Supports_Condition* Parser::parse_supports_condition_in_parens()+ {+ Supports_Condition* interp = parse_supports_interpolation();+ if (interp != 0) return interp;++ if (!lex < exactly <'('> >()) return 0;+ lex < css_whitespace >();++ Supports_Condition* cond = parse_supports_condition();+ if (cond != 0) {+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);+ } else {+ cond = parse_supports_declaration();+ if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);+ }+ lex < css_whitespace >();+ return cond;+ }++ At_Root_Block* Parser::parse_at_root_block()+ {+ ParserState at_source_position = pstate;+ Block* body = 0;+ At_Root_Expression* expr = 0;+ Lookahead lookahead_result;+ LOCAL_FLAG(in_at_root, true);+ if (lex< exactly<'('> >()) {+ expr = parse_at_root_expression();+ }+ if (peek < exactly<'{'> >()) {+ body = parse_block(true);+ }+ else if ((lookahead_result = lookahead_for_selector(position)).found) {+ Ruleset* r = parse_ruleset(lookahead_result, false);+ body = SASS_MEMORY_NEW(ctx.mem, Block, r->pstate(), 1, true);+ *body << r;+ }+ At_Root_Block* at_root = SASS_MEMORY_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()+ {+ 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 = SASS_MEMORY_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 = SASS_MEMORY_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()+ {+ std::string kwd(lexed);++ if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);++ At_Rule* at_rule = SASS_MEMORY_NEW(ctx.mem, At_Rule, pstate, kwd);+ Lookahead lookahead = lookahead_for_include(position);+ if (lookahead.found && !lookahead.has_interpolants) {+ at_rule->selector(parse_selector_list(true));+ }++ lex < css_comments >(false);++ if (lex < static_property >()) {+ at_rule->value(parse_interpolated_chunk(Token(lexed)));+ } else if (!(peek < alternatives < exactly<'{'>, exactly<'}'>, exactly<';'> > >())) {+ at_rule->value(parse_list());+ }++ lex < css_comments >(false);++ if (peek< exactly<'{'> >()) {+ at_rule->block(parse_block());+ }++ return at_rule;+ }++ Warning* Parser::parse_warning()+ {+ return SASS_MEMORY_NEW(ctx.mem, Warning, pstate, parse_list());+ }++ Error* Parser::parse_error()+ {+ return SASS_MEMORY_NEW(ctx.mem, Error, pstate, parse_list());+ }++ Debug* Parser::parse_debug()+ {+ return SASS_MEMORY_NEW(ctx.mem, Debug, pstate, parse_list());+ }++ Return* Parser::parse_return_directive()+ {+ // check that we do not have an empty list (ToDo: check if we got all cases)+ if (peek_css < alternatives < exactly < ';' >, exactly < '}' >, end_of_file > >())+ { css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was "); }+ return SASS_MEMORY_NEW(ctx.mem, Return, pstate, parse_list());+ }++ Lookahead Parser::lookahead_for_selector(const char* start)+ {+ // init result struct+ Lookahead rv = Lookahead();+ // get start position+ const char* p = start ? start : position;+ // match in one big "regex"+ rv.error = p;+ if (const char* q =+ peek <+ one_plus <+ alternatives <+ // consume whitespace and comments+ spaces, block_comment, line_comment,+ // match `/deep/` selector (pass-trough)+ // there is no functionality for it yet+ schema_reference_combinator,+ // match selector ops /[*&%,()\[\]]/+ class_char < selector_lookahead_ops >,+ // match selector combinators /[>+~]/+ class_char < selector_combinator_ops >,+ // match attribute compare operators+ alternatives <+ exact_match, class_match, dash_match,+ prefix_match, suffix_match, substring_match+ >,+ // main selector match+ sequence <+ // allow namespace prefix+ optional < namespace_schema >,+ // modifiers prefixes+ alternatives <+ sequence <+ exactly <'#'>,+ // not for interpolation+ negate < exactly <'{'> >+ >,+ // class match+ exactly <'.'>,+ // single or double colon+ optional < pseudo_prefix >+ >,+ // accept hypens in token+ one_plus < sequence <+ // can start with hyphens+ zero_plus < exactly<'-'> >,+ // now the main token+ alternatives <+ kwd_optional,+ exactly <'*'>,+ quoted_string,+ interpolant,+ identifier,+ percentage,+ dimension,+ variable,+ alnum+ >+ > >,+ // can also end with hyphens+ zero_plus < exactly<'-'> >+ >+ >+ >+ >(p)+ ) {+ while (p < q) {+ // did we have interpolations?+ if (*p == '#' && *(p+1) == '{') {+ rv.has_interpolants = true;+ p = q; break;+ }+ ++ p;+ }+ // store anyway }+++ // ToDo: remove+ rv.error = q;+ rv.position = q;+ // check expected opening bracket+ // only after successfull matching+ if (peek < exactly<'{'> >(q)) rv.found = q;+ // else if (peek < exactly<';'> >(q)) rv.found = q;+ // else if (peek < exactly<'}'> >(q)) rv.found = q;+ if (rv.found || *p == 0) rv.error = 0;+ }++ rv.parsable = ! rv.has_interpolants;++ // return result+ return rv;++ }+ // EO lookahead_for_selector++ // used in parse_block_nodes and parse_at_rule+ // ToDo: actual usage is still not really clear to me?+ Lookahead Parser::lookahead_for_include(const char* start)+ {+ // we actually just lookahead for a selector+ Lookahead rv = lookahead_for_selector(start);+ // but the "found" rules are different+ if (const char* p = rv.position) {+ // check for additional abort condition+ if (peek < exactly<';'> >(p)) rv.found = p;+ else if (peek < exactly<'}'> >(p)) rv.found = p;+ }+ // return result+ return rv;+ }+ // EO lookahead_for_include++ // look ahead for a token with interpolation in it+ // we mostly use the result if there is an interpolation+ // everything that passes here gets parsed as one schema+ // meaning it will not be parsed as a space separated list+ Lookahead Parser::lookahead_for_value(const char* start)+ {+ // init result struct+ Lookahead rv = Lookahead();+ // get start position+ const char* p = start ? start : position;+ // match in one big "regex"+ if (const char* q =+ peek <+ non_greedy <+ alternatives <+ // consume whitespace+ block_comment, spaces,+ // main tokens+ interpolant,+ identifier,+ variable,+ // issue #442+ sequence <+ parenthese_scope,+ interpolant+ >+ >,+ sequence <+ optional_spaces,+ alternatives <+ exactly<'{'>,+ exactly<'}'>,+ exactly<';'>+ >+ >+ >+ >(p)+ ) {+ if (p == q) return rv;+ while (p < q) {+ // did we have interpolations?+ if (*p == '#' && *(p+1) == '{') {+ rv.has_interpolants = true;+ p = q; break;+ }+ ++ p;+ }+ // store anyway+ // ToDo: remove+ rv.position = q;+ // check expected opening bracket+ // only after successfull matching+ if (peek < exactly<'{'> >(q)) rv.found = q;+ else if (peek < exactly<';'> >(q)) rv.found = q;+ else if (peek < exactly<'}'> >(q)) rv.found = q;+ }++ // return result+ return rv;+ }+ // EO lookahead_for_value++ void Parser::read_bom()+ {+ size_t skip = 0;+ std::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, std::vector<Expression*>& operands, enum Sass_OP op)+ {+ for (size_t i = 0, S = operands.size(); i < S; ++i) {+ base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, pstate, op, base, operands[i]);+ Binary_Expression* b = static_cast<Binary_Expression*>(base);+ if (op == Sass_OP::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, std::vector<Expression*>& operands, std::vector<enum Sass_OP>& ops)+ {+ for (size_t i = 0, S = operands.size(); i < S; ++i) {+ base = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, base->pstate(), ops[i], base, operands[i]);+ Binary_Expression* b = static_cast<Binary_Expression*>(base);+ if (ops[i] == Sass_OP::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(std::string msg, Position pos)+ {+ throw Exception::InvalidSass(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 std::string& msg, const std::string& prefix, const std::string& middle)+ {+ int max_len = 18;+ const char* pos = peek < optional_spaces >();++ const char* last_pos(pos - 1);+ // backup position to last significant char+ while ((!*last_pos || Prelexer::is_space(*last_pos)) && last_pos > source) -- last_pos;++ bool ellipsis_left = false;+ const char* pos_left(last_pos + 1);+ const char* end_left(last_pos + 1);+ while (pos_left > source) {+ if (end_left - pos_left >= max_len) {+ ellipsis_left = true;+ break;+ }++ const char* prev = pos_left - 1;+ if (*prev == '\r') break;+ if (*prev == '\n') break;+ pos_left = prev;+ }+ if (pos_left < source) {+ pos_left = source;+ }++ bool ellipsis_right = false;+ const char* end_right(pos);+ const char* pos_right(pos);+ while (end_right <= end) {+ if (end_right - pos_right > max_len) {+ ellipsis_right = true;+ break;+ }+ if (*end_right == '\r') break;+ if (*end_right == '\n') break;+ ++ end_right;+ }+ if (end_right > end) end_right = end;++ std::string left(pos_left, end_left);+ std::string right(pos_right, end_right);+ if (ellipsis_left) left = ellipsis + left.substr(left.size() - 15);+ if (ellipsis_right) right = right.substr(right.size() - 15) + ellipsis;+ // now pass new message to the more generic error function+ error(msg + prefix + quote(left) + middle + quote(right), pstate);+ }++}
+ libsass/src/parser.hpp view
@@ -0,0 +1,311 @@+#ifndef SASS_PARSER_H+#define SASS_PARSER_H++#include <string>+#include <vector>++#include "ast.hpp"+#include "position.hpp"+#include "context.hpp"+#include "position.hpp"+#include "prelexer.hpp"++struct Lookahead {+ const char* found;+ const char* error;+ const char* position;+ bool parsable;+ bool has_interpolants;+};++namespace Sass {++ class Parser : public ParserState {+ public:++ enum Syntactic_Context { nothing, mixin_def, function_def };++ Context& ctx;+ std::vector<Block*> block_stack;+ std::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(pstate), indentation(0)+ { in_at_root = false; stack.push_back(nothing); }++ // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));+ static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));+ static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"));+ static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"));+ // special static parsers to convert strings into certain selectors+ static Selector_List* parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"));++#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::prelexer mx>+ const char* sneak(const char* start = 0)+ {+ using namespace Prelexer;++ // 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::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::prelexer mx>+ const char* lex(bool lazy = true, bool force = false)+ {++ // 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);++ // maybe we want to update the parser state anyway?+ if (force == false) {+ // 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 the parse results)+ 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::prelexer mx>+ const char* lex_css()+ {+ // copy old token+ Token prev = lexed;+ // store previous pointer+ const char* oldpos = position;+ Position bt = before_token;+ Position at = after_token;+ ParserState op = pstate;+ // throw away comments+ // update srcmap position+ lex < Prelexer::css_comments >();+ // now lex a new token+ const char* pos = lex< mx >();+ // maybe restore prev state+ if (pos == 0) {+ pstate = op;+ lexed = prev;+ position = oldpos;+ after_token = at;+ before_token = bt;+ }+ // return match+ return pos;+ }++ // all block comments will be skipped and thrown away+ template <Prelexer::prelexer mx>+ const char* peek_css(const char* start = 0)+ {+ // now peek a token (skip comments first)+ return peek< mx >(peek < Prelexer::css_comments >(start));+ }++#ifdef __clang__++#pragma clang diagnostic pop++#endif++ void error(std::string msg, Position pos);+ // generate message with given and expected sample+ // text before and in the middle are configurable+ void css_error(const std::string& msg,+ const std::string& prefix = " after ",+ const std::string& middle = ", was: ");+ void read_bom();++ Block* parse();+ Import* parse_import();+ Definition* parse_definition(Definition::Type which_type);+ Parameters* parse_parameters();+ Parameter* parse_parameter();+ Mixin_Call* parse_include_directive();+ Arguments* parse_arguments();+ Argument* parse_argument();+ Assignment* parse_assignment();+ // Propset* parse_propset();+ Ruleset* parse_ruleset(Lookahead lookahead, bool is_root = false);+ Selector_Schema* parse_selector_schema(const char* end_of_selector);+ Selector_List* parse_selector_list(bool at_root = false);+ Complex_Selector* parse_complex_selector(bool in_root = true);+ Compound_Selector* parse_compound_selector();+ Simple_Selector* parse_simple_selector();+ Wrapped_Selector* parse_negated_selector();+ Simple_Selector* parse_pseudo_selector();+ Attribute_Selector* parse_attribute_selector();+ Block* parse_block(bool is_root = false);+ Block* parse_css_block(bool is_root = false);+ bool parse_block_nodes(bool is_root = false);+ bool parse_block_node(bool is_root = false);++ 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_operators();+ Expression* parse_factor();+ Expression* parse_value2();+ Expression* parse_value();+ Function_Call* parse_calc_function();+ Function_Call* parse_function_call();+ Function_Call_Schema* parse_function_call_schema();+ String* parse_url_function_string();+ String* parse_interpolated_chunk(Token, bool constant = false);+ String* parse_string();+ String_Constant* parse_static_expression();+ // String_Constant* parse_static_property();+ 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();+ Return* parse_return_directive();+ Content* parse_content_directive();+ void parse_charset_directive();+ Media_Block* parse_media_block();+ List* parse_media_queries();+ Media_Query* parse_media_query();+ Media_Query_Expression* parse_media_expression();+ Supports_Block* parse_supports_directive();+ Supports_Condition* parse_supports_condition();+ Supports_Condition* parse_supports_negation();+ Supports_Condition* parse_supports_operator();+ Supports_Condition* parse_supports_interpolation();+ Supports_Condition* parse_supports_declaration();+ Supports_Condition* parse_supports_condition_in_parens();+ 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();++ // these will throw errors+ Token lex_variable();+ Token lex_identifier();++ void parse_block_comments();++ Lookahead lookahead_for_value(const char* start = 0);+ Lookahead lookahead_for_selector(const char* start = 0);+ Lookahead lookahead_for_include(const char* start = 0);++ Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, Sass_OP op);+ Expression* fold_operands(Expression* base, std::vector<Expression*>& operands, std::vector<Sass_OP>& ops);++ void throw_syntax_error(std::string message, size_t ln = 0);+ void throw_read_error(std::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/src/paths.hpp view
@@ -0,0 +1,71 @@+#ifndef SASS_PATHS_H+#define SASS_PATHS_H++#include <string>+#include <vector>+#include <sstream>+++template<typename T>+std::string vector_to_string(std::vector<T> v)+{+ std::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 {+++ template<typename T>+ std::vector<std::vector<T> > paths(std::vector<std::vector<T> > strata, size_t from_end = 0)+ {+ if (strata.empty()) {+ return std::vector<std::vector<T> >();+ }++ size_t end = strata.size() - from_end;+ if (end <= 1) {+ std::vector<std::vector<T> > starting_points;+ starting_points.reserve(strata[0].size());+ for (size_t i = 0, S = strata[0].size(); i < S; ++i) {+ std::vector<T> starting_point;+ starting_point.push_back(strata[0][i]);+ starting_points.push_back(starting_point);+ }+ return starting_points;+ }++ std::vector<std::vector<T> > up_to_here = paths(strata, from_end + 1);+ std::vector<T> here = strata[end-1];++ std::vector<std::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) {+ std::vector<T> branch = up_to_here[i];+ branch.push_back(here[j]);+ branches.push_back(branch);+ }+ }++ return branches;+ }++}++#endif
+ libsass/src/plugins.cpp view
@@ -0,0 +1,165 @@+#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"++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 = std::string(our_version).find('.', 0);+ if (pos != std::string::npos) pos = std::string(our_version).find('.', pos + 1);++ // if we do not have two dots we fallback to compare complete string+ if (pos == std::string::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 std::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)+ std::cerr << "failed loading 'libsass_support' in <" << path << ">" << std::endl;+ if (const char* dlsym_error = dlerror()) std::cerr << dlsym_error << std::endl;+ CLOSE_LIB(plugin);+ }+ }+ else+ {+ // print debug message to stderr (should not happen)+ std::cerr << "failed loading plugin <" << path << ">" << std::endl;+ if (const char* dlopen_error = dlerror()) std::cerr << dlopen_error << std::endl;+ }++ return false;++ }++ size_t Plugins::load_plugins(const std::string& path)+ {++ // count plugins+ size_t loaded = 0;++ #ifdef _WIN32++ try+ {++ // use wchar (utf16)+ WIN32_FIND_DATAW data;+ // trailing slash is guaranteed+ std::string globsrch(path + "*.dll");+ // convert to wide chars (utf16) for system call+ std::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)+ std::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?+ std::cerr << "filename in plugin path has invalid utf8?" << std::endl;+ }+ }+ }+ catch (utf8::invalid_utf8)+ {+ // report the error to the console (should not happen)+ // implementors should make sure to provide valid utf8+ std::cerr << "plugin path contains invalid utf8" << std::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/src/plugins.hpp view
@@ -0,0 +1,57 @@+#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 {+++ class Plugins {++ public: // c-tor+ Plugins(void);+ ~Plugins(void);++ public: // methods+ // load one specific plugin+ bool load_plugin(const std::string& path);+ // load all plugins from a directory+ size_t load_plugins(const std::string& path);++ public: // public accessors+ const std::vector<Sass_Importer_Entry> get_headers(void) { return headers; }+ const std::vector<Sass_Importer_Entry> get_importers(void) { return importers; }+ const std::vector<Sass_Function_Entry> get_functions(void) { return functions; }++ private: // private vars+ std::vector<Sass_Importer_Entry> headers;+ std::vector<Sass_Importer_Entry> importers;+ std::vector<Sass_Function_Entry> functions;++ };++}++#endif
+ libsass/src/position.cpp view
@@ -0,0 +1,162 @@+#include "position.hpp"++namespace Sass {+++ Offset::Offset(const char* string)+ : line(0), column(0)+ {+ *this = inc(string, string + strlen(string));+ }++ Offset::Offset(const std::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(const char* path, const char* src, const size_t file)+ : Position(file, 0, 0), path(path), src(src), offset(0, 0), token() { }++ ParserState::ParserState(const char* path, const char* src, const Position& position, Offset offset)+ : Position(position), path(path), src(src), offset(offset), token() { }++ ParserState::ParserState(const char* path, const char* src, const Token& token, const 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/src/position.hpp view
@@ -0,0 +1,123 @@+#ifndef SASS_POSITION_H+#define SASS_POSITION_H++#include <string>+#include <cstring>+// #include <iostream>++namespace Sass {+++ class Offset {++ public: // c-tor+ Offset(const char* string);+ Offset(const std::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 std::ostream& operator<<(std::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 std::ostream& operator<<(std::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; }+ std::string ws_before() const { return std::string(prefix, begin); }+ std::string to_string() const { return std::string(begin, end); }+ std::string time_wspace() const {+ std::string str(to_string());+ std::string whitespaces(" \t\f\v\n\r");+ return str.erase(str.find_last_not_of(whitespaces)+1);+ }++ operator bool() { return begin && end && begin >= end; }+ operator std::string() { return to_string(); }++ bool operator==(Token t) { return to_string() == t.to_string(); }+ };++ class ParserState : public Position {++ public: // c-tor+ ParserState(const char* path, const char* src = 0, const size_t file = std::string::npos);+ ParserState(const char* path, const char* src, const Position& position, Offset offset = Offset(0, 0));+ ParserState(const char* path, const char* src, const Token& token, const Position& position, Offset offset = Offset(0, 0));++ public: // down casts+ Offset off() { return *this; }+ Position pos() { return *this; }+ ParserState pstate() { return *this; }++ public:+ const char* path;+ const char* src;+ Offset offset;+ Token token;++ };++}++#endif
+ libsass/src/prelexer.cpp view
@@ -0,0 +1,1141 @@+#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<+ 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<'\\'>,+ alternatives <+ minmax_range<+ 1, 3, xdigit+ >,+ any_char+ >,+ optional <+ exactly <' '>+ >+ >(src);+ }++ // Match identifier start+ const char* identifier_alpha(const char* src)+ {+ return alternatives<+ unicode_seq,+ alpha,+ unicode,+ exactly<'-'>,+ exactly<'_'>,+ NONASCII,+ ESCAPE,+ escape_seq+ >(src);+ }++ // Match identifier after start+ const char* identifier_alnum(const char* src)+ {+ return alternatives<+ unicode_seq,+ alnum,+ unicode,+ exactly<'-'>,+ exactly<'_'>,+ NONASCII,+ ESCAPE,+ escape_seq+ >(src);+ }++ // Match CSS identifiers.+ const char* strict_identifier(const char* src)+ {+ return sequence<+ one_plus < strict_identifier_alpha >,+ zero_plus < strict_identifier_alnum >+ // word_boundary not needed+ >(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* strict_identifier_alpha(const char* src)+ {+ return alternatives <+ alpha,+ unicode,+ escape_seq,+ exactly<'_'>+ >(src);+ }++ const char* strict_identifier_alnum(const char* src)+ {+ return alternatives <+ alnum,+ unicode,+ escape_seq,+ exactly<'_'>+ >(src);+ }++ // Match CSS unit identifier.+ const char* unit_identifier(const char* src)+ {+ return sequence <+ optional < exactly <'-'> >,+ strict_identifier_alpha,+ zero_plus < alternatives<+ strict_identifier_alnum,+ sequence <+ one_plus < exactly<'-'> >,+ strict_identifier_alpha+ >+ > >+ >(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) {++ return sequence <+ one_plus <+ sequence <+ zero_plus <+ alternatives <+ identifier,+ exactly <'-'>+ >+ >,+ interpolant,+ zero_plus <+ alternatives <+ digits,+ identifier,+ quoted_string,+ exactly<'+'>,+ 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 escapes+ sequence <+ exactly < '\\' >,+ re_linebreak+ >,+ escape_seq,+ unicode_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 escapes+ sequence <+ exactly < '\\' >,+ re_linebreak+ >,+ escape_seq,+ unicode_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* re_reference_combinator(const char* src) {+ return sequence <+ optional <+ sequence <+ zero_plus <+ exactly <'-'>+ >,+ identifier,+ exactly <'|'>+ >+ >,+ zero_plus <+ exactly <'-'>+ >,+ identifier+ >(src);+ }++ const char* static_reference_combinator(const char* src) {+ return sequence <+ exactly <'/'>,+ re_reference_combinator,+ exactly <'/'>+ >(src);+ }++ const char* schema_reference_combinator(const char* src) {+ return sequence <+ exactly <'/'>,+ optional <+ sequence <+ css_ip_identifier,+ exactly <'|'>+ >+ >,+ css_ip_identifier,+ exactly <'/'>+ > (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_directive(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_directive(const char* src) {+ return word<include_kwd>(src);+ }++ const char* kwd_content_directive(const char* src) {+ return word<content_kwd>(src);+ }++ const char* kwd_charset_directive(const char* src) {+ return word<charset_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_comments,+ 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);+ }++ const char* css_identifier(const char* src) {+ return sequence <+ zero_plus <+ exactly <'-'>+ >,+ identifier+ >(src);+ }++ const char* css_ip_identifier(const char* src) {+ return sequence <+ zero_plus <+ exactly <'-'>+ >,+ alternatives <+ identifier,+ interpolant+ >+ >(src);+ }++ // Match CSS type selectors+ const char* namespace_prefix(const char* src) {+ return sequence <+ optional <+ alternatives <+ exactly <'*'>,+ css_identifier+ >+ >,+ exactly <'|'>,+ negate <+ exactly <'='>+ >+ >(src);+ }++ // Match CSS type selectors+ const char* namespace_schema(const char* src) {+ return sequence <+ optional <+ alternatives <+ exactly <'*'>,+ css_ip_identifier+ >+ >,+ exactly<'|'>,+ negate <+ exactly <'='>+ >+ >(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_schema>, 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_schema>, 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 < sequence <+ optional_css_whitespace, sign,+ optional_css_whitespace, 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, unit_identifier >(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+ sequence <+ negate <+ exactly < '$' >+ >,+ zero_plus <+ alternatives <+ alnum,+ interpolant,+ 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* kwd_important(const char* src) {+ return sequence< exactly<'!'>,+ optional_css_whitespace,+ word<important_kwd> >(src);+ }+ // Match CSS "!optional" keyword.+ const char* kwd_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 <+ one_plus <+ sequence <+ zero_plus <+ alternatives <+ identifier,+ exactly <'-'>+ >+ >,+ one_plus <+ sequence <+ interpolant,+ alternatives <+ digits,+ identifier,+ exactly<'+'>,+ exactly<'-'>+ >+ >+ >+ >+ >,+ negate <+ exactly <'%'>+ >,+ lookahead <+ exactly <'('>+ >+ > (src);+ }++ const char* re_nothing(const char* src) {+ return src;+ }++ const char* re_functional(const char* src) {+ return sequence< identifier, optional < block_comment >, exactly<'('> >(src);+ }+ const char* re_pseudo_selector(const char* src) {+ return sequence< identifier, optional < block_comment >, 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_only(const char* src) {+ return keyword < only_kwd >(src);+ }+ const char* kwd_and(const char* src) {+ return keyword < and_kwd >(src);+ }+ const char* kwd_or(const char* src) {+ return keyword < or_kwd >(src);+ }+ const char* kwd_not(const char* src) {+ return keyword < 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>, exactly<'('>, skip_over_scopes< exactly<'('>, exactly<')'> > >(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_property(const char* src) {+ return alternatives <+ variable,+ identifier_schema,+ identifier+ >(src);+ }+ const char* ie_keyword_arg_value(const char* src) {+ return alternatives <+ variable,+ identifier_schema,+ identifier,+ quoted_string,+ number,+ hexa,+ sequence <+ exactly < '(' >,+ skip_over_scopes <+ exactly < '(' >,+ exactly < ')' >+ >+ >+ >(src);+ }++ const char* ie_keyword_arg(const char* src) {+ return sequence <+ ie_keyword_arg_property,+ optional_css_whitespace,+ exactly<'='>,+ optional_css_whitespace,+ ie_keyword_arg_value+ >(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);+ }*/++ const char* H(const char* src) {+ return std::isxdigit(*src) ? src+1 : 0;+ }++ const char* W(const char* src) {+ return zero_plus< alternatives<+ space,+ exactly< '\t' >,+ exactly< '\r' >,+ exactly< '\n' >,+ exactly< '\f' >+ > >(src);+ }++ const char* UUNICODE(const char* src) {+ return sequence< exactly<'\\'>,+ between<H, 1, 6>,+ optional< W >+ >(src);+ }++ const char* NONASCII(const char* src) {+ return nonascii(src);+ }++ const char* ESCAPE(const char* src) {+ return alternatives<+ UUNICODE,+ sequence<+ exactly<'\\'>,+ alternatives<+ NONASCII,+ escapable_character+ >+ >+ >(src);+ }+++ // const char* real_uri_prefix(const char* src) {+ // return alternatives<+ // exactly< url_kwd >,+ // exactly< url_prefix_kwd >+ // >(src);+ // }++ const char* real_uri_suffix(const char* src) {+ return sequence< W, exactly< ')' > >(src);+ }++ const char* real_uri_value(const char* src) {+ return+ sequence<+ non_greedy<+ alternatives<+ class_char< real_uri_chars >,+ uri_character,+ NONASCII,+ ESCAPE+ >,+ alternatives<+ real_uri_suffix,+ exactly< hash_lbrace >+ >+ >+ >+ (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* unicode_seq(const char* src) {+ return sequence <+ alternatives <+ exactly< 'U' >,+ exactly< 'u' >+ >,+ exactly< '+' >,+ padded_token <+ 6, xdigit,+ exactly < '?' >+ >+ >(src);+ }++ const char* static_component(const char* src) {+ return alternatives< identifier,+ static_string,+ percentage,+ hex,+ exactly<'|'>,+ // exactly<'+'>,+ sequence < number, unit_identifier >,+ number,+ sequence< exactly<'!'>, word<important_kwd> >+ >(src);+ }++ const char* static_property(const char* src) {+ return+ sequence <+ zero_plus<+ sequence <+ optional_css_comments,+ alternatives <+ exactly<','>,+ exactly<'('>,+ exactly<')'>,+ kwd_optional,+ quoted_string,+ interpolant,+ identifier,+ percentage,+ dimension,+ variable,+ alnum,+ sequence <+ exactly <'\\'>,+ any_char+ >+ >+ >+ >,+ lookahead <+ sequence <+ optional_css_comments,+ alternatives <+ exactly <';'>,+ exactly <'}'>,+ end_of_file+ >+ >+ >+ >(src);+ }++ const char* static_value(const char* src) {+ return sequence< sequence<+ static_component,+ zero_plus< identifier >+ >,+ zero_plus < sequence<+ alternatives<+ sequence< optional_spaces, alternatives<+ exactly < '/' >,+ exactly < ',' >,+ exactly < ' ' >+ >, optional_spaces >,+ spaces+ >,+ static_component+ > >,+ zero_plus < spaces >,+ alternatives< exactly<';'>, exactly<'}'> >+ >(src);+ }++ const char* parenthese_scope(const char* src) {+ return sequence <+ exactly < '(' >,+ skip_over_scopes <+ exactly < '(' >,+ exactly < ')' >+ >+ >(src);+ }++ const char* type_selector(const char* src) {+ return sequence< optional<namespace_schema>, identifier>(src);+ }+ const char* re_type_selector(const char* src) {+ return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src);+ }+ const char* re_type_selector2(const char* src) {+ return alternatives< type_selector, universal, quoted_string, dimension, percentage, number, identifier_alnums >(src);+ }+ const char* re_static_expression(const char* src) {+ return sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number >(src);+ }++ template <size_t size, prelexer mx, prelexer pad>+ const char* padded_token(const char* src)+ {+ size_t got = 0;+ const char* pos = src;+ while (got < size) {+ if (!mx(pos)) break;+ ++ pos; ++ got;+ }+ while (got < size) {+ if (!pad(pos)) break;+ ++ pos; ++ got;+ }+ return got ? pos : 0;+ }++ template <size_t min, size_t max, prelexer mx>+ const char* minmax_range(const char* src)+ {+ size_t got = 0;+ const char* pos = src;+ while (got < max) {+ if (!mx(pos)) break;+ ++ pos; ++ got;+ }+ if (got < min) return 0;+ if (got > max) return 0;+ return pos;+ }++ }+}
+ libsass/src/prelexer.hpp view
@@ -0,0 +1,416 @@+#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_only(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) {++ 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 (const char* pos = start(src)) {+ ++ level; // increase counter+ src = pos - 1; // advance position+ }++ // 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;+ // advance position+ src = final - 1;+ }++ // next+ ++ src;+ }++ return 0;+ }++ // skip to a skip delimited by parentheses+ // uses smart `skip_over_scopes` internally+ const char* parenthese_scope(const char* src);++ // 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) {+ return skip_over_scopes<start, stop>(src, 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* strict_identifier(const char* src);+ const char* strict_identifier_alpha(const char* src);+ const char* strict_identifier_alnum(const char* src);+ // Match a CSS unit identifier.+ const char* unit_identifier(const char* src);+ // const char* strict_identifier_alnums(const char* src);+ // Match reference selector.+ const char* re_reference_combinator(const char* src);+ const char* static_reference_combinator(const char* src);+ const char* schema_reference_combinator(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_directive(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_directive(const char* src);+ const char* kwd_content_directive(const char* src);+ const char* kwd_charset_directive(const char* src);+ const char* kwd_extend(const char* src);++ const char* unicode_seq(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* re_nothing(const char* src);+ const char* re_type_selector2(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);++ const char* re_type_selector(const char* src);+ const char* re_static_expression(const char* src);++ // identifier that can start with hyphens+ const char* css_identifier(const char* src);+ const char* css_ip_identifier(const char* src);++ // Match CSS type selectors+ const char* namespace_schema(const char* src);+ 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* kwd_important(const char* src);+ // Match CSS "!optional" keyword.+ const char* kwd_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* re_functional(const char* src);+ const char* re_pseudo_selector(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);+ const char* ie_keyword_arg_value(const char* src);+ const char* ie_keyword_arg_property(const char* src);++ // match url()+ const char* H(const char* src);+ const char* W(const char* src);+ // `UNICODE` makes VS sad+ const char* UUNICODE(const char* src);+ const char* NONASCII(const char* src);+ const char* ESCAPE(const char* src);+ const char* real_uri_suffix(const char* src);+ // const char* real_uri_prefix(const char* src);+ const char* real_uri_value(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_property(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;+ }++ template <size_t size, prelexer mx, prelexer pad>+ const char* padded_token(const char* src);++ template <size_t min, size_t max, prelexer mx>+ const char* minmax_range(const char* src);++ }+}++#endif
+ libsass/src/remove_placeholders.cpp view
@@ -0,0 +1,55 @@+#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)+ { }++ 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) {+ // Create a new selector group without placeholders+ Selector_List* sl = static_cast<Selector_List*>(r->selector());++ if (sl) {+ Selector_List* new_sl = SASS_MEMORY_NEW(ctx.mem, Selector_List, sl->pstate());++ for (size_t i = 0, L = sl->length(); i < L; ++i) {+ if (!(*sl)[i]->contains_placeholder()) {+ *new_sl << (*sl)[i];+ }+ }++ // Set the new placeholder selector list+ r->selector(new_sl);+ }++ // Iterate into child blocks+ Block* b = r->block();++ for (size_t i = 0, L = b->length(); i < L; ++i) {+ if ((*b)[i]) (*b)[i]->perform(this);+ }+ }++ void Remove_Placeholders::operator()(Media_Block* m) {+ Block* b = m->block();+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ if ((*b)[i]) (*b)[i]->perform(this);+ }+ }++ void Remove_Placeholders::operator()(At_Rule* a) {+ if (a->block()) a->block()->perform(this);+ }++}
+ libsass/src/remove_placeholders.hpp view
@@ -0,0 +1,37 @@+#ifndef SASS_REMOVE_PLACEHOLDERS_H+#define SASS_REMOVE_PLACEHOLDERS_H++#pragma once++#include "ast.hpp"+#include "operation.hpp"++namespace Sass {+++ 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 U>+ void fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/src/sass.cpp view
@@ -0,0 +1,41 @@+#include <cstdlib>+#include <cstring>+#include <vector>+#include <sstream>++#include "sass.h"+#include "file.hpp"+#include "util.hpp"++extern "C" {+ using namespace Sass;++ // caller must free the returned memory+ char* ADDCALL sass_string_quote (const char *str, const char quote_mark)+ {+ std::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)+ {+ std::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[])+ {+ std::string resolved(File::find_file(file, paths));+ return sass_strdup(resolved.c_str());+ }++ // Get compiled libsass version+ const char* ADDCALL libsass_version(void)+ {+ return LIBSASS_VERSION;+ }++}
+ libsass/src/sass2scss.cpp view
@@ -0,0 +1,845 @@+/**+ * sass2scss+ * Licensed under the MIT License+ * Copyright (c) Marcel Greter+ */++#ifdef _MSC_VER+#define _CRT_SECURE_NO_WARNINGS+#define _CRT_NONSTDC_NO_DEPRECATE+#endif++// 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"++// 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 std::string closer (const converter& converter)+ {+ return PRETTIFY(converter) == 0 ? " }" :+ PRETTIFY(converter) <= 1 ? " }" :+ "\n" + INDENT(converter) + "}";+ }++ // pretty printer helper function+ static std::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 (std::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 != std::string::npos) sel.erase(pos, std::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 (std::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 == std::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 == std::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 (std::string& sass)+ {++ size_t col_pos = 0;+ bool apoed = false;+ bool quoted = false;+ bool comment = false;+ size_t brackets = 0;++ while (col_pos != std::string::npos)+ {++ // process all interesting chars+ col_pos = sass.find_first_of("\"\'/\\*()", col_pos);++ // assertion for valid result+ if (col_pos != std::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 std::string removeMultilineComment (std::string &sass)+ {++ std::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 != std::string::npos)+ {++ // process all interesting chars+ col_pos = sass.find_first_of("\"\'/\\*", col_pos);++ // assertion for valid result+ if (col_pos != std::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+ std::string rtrim(const std::string &sass)+ {+ std::string trimmed = sass;+ size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");+ if (pos_ws != std::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+ std::string flush (std::string& sass, converter& converter)+ {++ // return flushed+ std::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 == std::string::npos) return scss;++ // get the linefeeds from the string+ std::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 != std::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 == std::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 != std::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+ std::string process (std::string& sass, converter& converter)+ {++ // resulting string+ std::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 == std::string::npos)+ {+ // just add complete whitespace+ converter.whitespace += sass + "\n";+ }+ // have meaningfull first char+ else+ {++ // extract and store indentation string+ std::string indent = sass.substr(0, pos_left);++ // check if current line starts a comment+ std::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 != std::string::npos)+ {+ // get the possible pseudo selector+ std::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 != std::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 != std::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 == std::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 != std::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) != "@include" &&+ sass.substr(pos_left, 8) != "@content"+ ) {++ // probably a selector anyway+ converter.selector = true;+ // try to find first colon in the current line+ size_t pos_colon = sass.find_first_of(":", pos_left);+ // assertion that we have a colon+ if (pos_colon != std::string::npos)+ {+ // it is not a selector if we have a space after a colon+ if (sass[pos_colon+1] == ' ') converter.selector = false;+ if (sass[pos_colon+1] == ' ') converter.selector = false;+ }++ }++ // 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 != std::string::npos)+ {++ // get the last meaningfull char+ std::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+ std::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 std::string& sass, const int options)+ {++ // local variables+ std::string line;+ std::string scss = "";+ std::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+ std::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/src/sass_context.cpp view
@@ -0,0 +1,708 @@+#include <cstring>+#include <stdexcept>+#include <sstream>+#include <string>+#include <vector>++#include "sass.h"+#include "file.hpp"+#include "json.hpp"+#include "util.hpp"+#include "context.hpp"+#include "sass_context.hpp"+#include "ast_fwd_decl.hpp"+#include "error_handling.hpp"++#define LFEED "\n"++extern "C" {+ using namespace Sass;++ 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, def) \+ type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } \+ void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \+ { free(options->option); options->option = option || def ? sass_strdup(option ? option : def) : 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; }++ static int handle_errors(Sass_Context* c_ctx) {+ try {+ throw;+ }+ catch (Exception::Base& e) {+ std::stringstream msg_stream;+ std::string cwd(Sass::File::get_cwd());+ std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));++ std::string msg_prefix("Error: ");+ bool got_newline = false;+ msg_stream << msg_prefix;+ const char* msg = e.what();+ while(msg && *msg) {+ if (*msg == '\r') {+ got_newline = true;+ } else if (*msg == '\n') {+ got_newline = true;+ } else if (got_newline) {+ msg_stream << std::string(msg_prefix.size(), ' ');+ got_newline = false;+ }+ msg_stream << *msg;+ ++ msg;+ }+ if (!got_newline) msg_stream << "\n";+ msg_stream << std::string(msg_prefix.size(), ' ');+ msg_stream << " on line " << e.pstate.line+1 << " of " << rel_path << "\n";++ // now create the code trace (ToDo: maybe have util functions?)+ if (e.pstate.line != std::string::npos && e.pstate.column != std::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 << ">> " << std::string(line_beg + move_in, line_end - shorten) << "\n";+ msg_stream << " " << std::string(e.pstate.column - move_in, '-') << "^\n";+ }++ 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));+ json_append_member(json_err, "line", json_mknumber((double)(e.pstate.line+1)));+ json_append_member(json_err, "column", json_mknumber((double)(e.pstate.column+1)));+ json_append_member(json_err, "message", json_mkstring(e.what()));+ json_append_member(json_err, "formatted", json_mkstring(msg_stream.str().c_str()));++ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = sass_strdup(e.what());+ c_ctx->error_status = 1;+ c_ctx->error_file = sass_strdup(e.pstate.path);+ 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 (std::bad_alloc& ba) {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Unable to allocate memory: " << ba.what() << std::endl;+ json_append_member(json_err, "status", json_mknumber(2));+ json_append_member(json_err, "message", json_mkstring(ba.what()));+ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = sass_strdup(ba.what());+ c_ctx->error_status = 2;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (std::exception& e) {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Error: " << e.what() << std::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 = sass_strdup(e.what());+ c_ctx->error_status = 3;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (std::string& e) {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Error: " << e << std::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 = sass_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 (const char* e) {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Error: " << e << std::endl;+ json_append_member(json_err, "status", json_mknumber(4));+ json_append_member(json_err, "message", json_mkstring(e));+ c_ctx->error_json = json_stringify(json_err, " ");;+ c_ctx->error_message = sass_strdup(msg_stream.str().c_str());+ c_ctx->error_text = sass_strdup(e);+ c_ctx->error_status = 4;+ c_ctx->output_string = 0;+ c_ctx->source_map_string = 0;+ json_delete(json_err);+ }+ catch (...) {+ std::stringstream msg_stream;+ JsonNode* json_err = json_mkobject();+ msg_stream << "Unknown error occurred" << std::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 = sass_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* cpp_ctx) throw()+ {+ try {++ // 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(std::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(std::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;+ }++ // 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 = std::string::npos;+ c_ctx->error_column = std::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+ std::string input_path = safe_str(c_ctx->input_path);+ std::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+ bool skip = c_ctx->type == SASS_CONTEXT_DATA;++ // dispatch parse call+ root = cpp_ctx->parse();+ // abort on errors+ if (!root) return 0;++ // skip all prefixed files? (ToDo: check srcmap)+ // IMO source-maps should point to headers already+ // therefore don't skip it for now. re-enable or+ // remove completely once this is tested+ size_t headers = cpp_ctx->head_imports;++ // copy the included files on to the context (dont forget to free later)+ if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL)+ throw(std::bad_alloc());++ // 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* cpp_ctx)+ {++ // prepare sass compiler with context and options+ Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx);++ 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) { std::cerr << "Error allocating memory for options" << std::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) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; }+ ctx->type = SASS_CONTEXT_FILE;+ init_options(ctx);+ try {+ if (input_path == 0) { throw(std::runtime_error("File context created without an input path")); }+ if (*input_path == 0) { throw(std::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) { std::cerr << "Error allocating memory for data context" << std::endl; return 0; }+ ctx->type = SASS_CONTEXT_DATA;+ init_options(ctx);+ try {+ if (source_string == 0) { throw(std::runtime_error("Data context created without a source string")); }+ if (*source_string == 0) { throw(std::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_data_compiler (struct Sass_Data_Context* data_ctx)+ {+ if (data_ctx == 0) return 0;+ Context* cpp_ctx = new Data_Context(data_ctx);+ return sass_prepare_context(data_ctx, cpp_ctx);+ }++ struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx)+ {+ if (file_ctx == 0) return 0;+ Context* cpp_ctx = new File_Context(file_ctx);+ return sass_prepare_context(file_ctx, cpp_ctx);+ }++ int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx)+ {+ if (data_ctx == 0) return 1;+ if (data_ctx->error_status)+ return data_ctx->error_status;+ try {+ if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string")); }+ if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); }+ }+ catch (...) { return handle_errors(data_ctx) | 1; }+ Context* cpp_ctx = new Data_Context(data_ctx);+ return sass_compile_context(data_ctx, cpp_ctx);+ }++ int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx)+ {+ if (file_ctx == 0) return 1;+ if (file_ctx->error_status)+ return file_ctx->error_status;+ try {+ if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path")); }+ if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path")); }+ }+ catch (...) { return handle_errors(file_ctx) | 1; }+ Context* cpp_ctx = new File_Context(file_ctx);+ return sass_compile_context(file_ctx, cpp_ctx);+ }++ 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 = compiler->cpp_ctx;+ Block* root = compiler->root;+ // compile the parsed root block+ try { compiler->c_ctx->output_string = cpp_ctx->render(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->render_srcmap();+ // 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->plugin_path) free(ctx->plugin_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 = compiler->cpp_ctx;+ if (cpp_ctx) delete(cpp_ctx);+ compiler->cpp_ctx = 0;+ free(compiler);+ }++ // Deallocate all associated memory with file context+ void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx)+ {+ // clear the context and free it+ sass_clear_context(ctx); free(ctx);+ }+ // Deallocate all associated memory with data context+ void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx)+ {+ // clean the source string if it was not passed+ // we reset this member once we start parsing+ if (ctx->source_string) free(ctx->source_string);+ // clear the context and free it+ 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; }+ struct Sass_Options* ADDCALL sass_compiler_get_options(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, 0);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, plugin_path, 0);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, include_path, 0);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0);+ IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0);++ // 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/src/sass_context.hpp view
@@ -0,0 +1,156 @@+#ifndef SASS_SASS_CONTEXT_H+#define SASS_SASS_CONTEXT_H++#include "sass.h"+#include "context.hpp"+#include "ast_fwd_decl.hpp"++// Input behaviours+enum Sass_Input_Style {+ SASS_CONTEXT_NULL,+ SASS_CONTEXT_FILE,+ SASS_CONTEXT_DATA,+ SASS_CONTEXT_FOLDER+};++// simple linked list+struct string_list {+ string_list* next;+ char* string;+};++// sass config options structure+struct Sass_Options {++ // Precision for fractional numbers+ int precision;++ // Output style for the generated css code+ // A value from above SASS_STYLE_* constants+ enum Sass_Output_Style output_style;++ // Emit comments in the generated CSS indicating+ // the corresponding source line.+ bool source_comments;++ // 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;+ char* srcmap_string;++};++// link c and cpp context+struct Sass_Compiler {+ // progress status+ Sass_Compiler_State state;+ // original c context+ Sass_Context* c_ctx;+ // Sass::Context+ Sass::Context* cpp_ctx;+ // Sass::Block+ Sass::Block* root;+};++#endif
+ libsass/src/sass_functions.cpp view
@@ -0,0 +1,144 @@+#include <cstring>+#include "util.hpp"+#include "context.hpp"+#include "sass/functions.h"+#include "sass_functions.hpp"++extern "C" {+ using namespace Sass;++ 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; }++ 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));+ }++ Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; }+ void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; }++ // 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* imp_path, const char* abs_path, char* source, char* srcmap)+ {+ Sass_Import* v = (Sass_Import*) calloc(1, sizeof(Sass_Import));+ if (v == 0) return 0;+ v->imp_path = imp_path ? sass_strdup(imp_path) : 0;+ v->abs_path = abs_path ? sass_strdup(abs_path) : 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 ? sass_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->imp_path);+ free(import->abs_path);+ free(import->source);+ free(import->srcmap);+ free(import->error);+ free(import);+ }++ // Getter for import entry+ const char* ADDCALL sass_import_get_imp_path(Sass_Import_Entry entry) { return entry->imp_path; }+ const char* ADDCALL sass_import_get_abs_path(Sass_Import_Entry entry) { return entry->abs_path; }+ 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/src/sass_functions.hpp view
@@ -0,0 +1,32 @@+#ifndef SASS_SASS_FUNCTIONS_H+#define SASS_SASS_FUNCTIONS_H++#include "sass.h"++// Struct to hold custom function callback+struct Sass_Function {+ const char* signature;+ Sass_Function_Fn function;+ void* cookie;+};++// External import entry+struct Sass_Import {+ char* imp_path; // path as found in the import statement+ char *abs_path; // path after importer has resolved it+ char* source;+ char* srcmap;+ // error handling+ char* error;+ size_t line;+ size_t column;+};++// Struct to hold importer callback+struct Sass_Importer {+ Sass_Importer_Fn importer;+ double priority;+ void* cookie;+};++#endif
+ libsass/src/sass_interface.cpp view
@@ -0,0 +1,216 @@+#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/base.h"+#include "sass/interface.h"++#define LFEED "\n"++extern "C" {++ sass_context* sass_new_context()+ { return (sass_context*) calloc(1, sizeof(sass_context)); }++ 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);++ Sass::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);++ Sass::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)+ {+ Sass::free_string_array(ctx->included_files);+ free(ctx);+ }++ int sass_compile(sass_context* c_ctx)+ {+ using namespace Sass;+ try {+ std::string input_path = safe_str(c_ctx->input_path);+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ std::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;+ }+ Data_Context cpp_ctx(+ (Sass_Data_Context*) 0+ );+ 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;+ }+ }+ Block* root = cpp_ctx.parse();+ c_ctx->output_string = cpp_ctx.render(root);+ c_ctx->source_map_string = cpp_ctx.render_srcmap();+ c_ctx->error_message = 0;+ c_ctx->error_status = 0;++ if (copy_strings(cpp_ctx.get_included_files(true), &c_ctx->included_files, 1) == NULL)+ throw(std::bad_alloc());+ }+ catch (Exception::InvalidSass& e) {+ std::stringstream msg_stream;+ msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::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::bad_alloc& ba) {+ std::stringstream msg_stream;+ msg_stream << "Unable to allocate memory: " << ba.what() << std::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) {+ std::stringstream msg_stream;+ msg_stream << "Error: " << e.what() << std::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::string& e) {+ std::stringstream msg_stream;+ msg_stream << "Error: " << e << std::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+ std::stringstream msg_stream;+ msg_stream << "Unknown error occurred" << std::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 {+ std::string input_path = safe_str(c_ctx->input_path);+ int lastindex = static_cast<int>(input_path.find_last_of("."));+ std::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;+ }+ File_Context cpp_ctx(+ (Sass_File_Context*) 0+ );+ 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;+ }+ }+ Block* root = cpp_ctx.parse();+ c_ctx->output_string = cpp_ctx.render(root);+ c_ctx->source_map_string = cpp_ctx.render_srcmap();+ c_ctx->error_message = 0;+ c_ctx->error_status = 0;++ if (copy_strings(cpp_ctx.get_included_files(false), &c_ctx->included_files) == NULL)+ throw(std::bad_alloc());+ }+ catch (Exception::InvalidSass& e) {+ std::stringstream msg_stream;+ msg_stream << e.pstate.path << ":" << e.pstate.line << ": " << e.what() << std::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::bad_alloc& ba) {+ std::stringstream msg_stream;+ msg_stream << "Unable to allocate memory: " << ba.what() << std::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) {+ std::stringstream msg_stream;+ msg_stream << "Error: " << e.what() << std::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::string& e) {+ std::stringstream msg_stream;+ msg_stream << "Error: " << e << std::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+ std::stringstream msg_stream;+ msg_stream << "Unknown error occurred" << std::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/src/sass_util.cpp view
@@ -0,0 +1,150 @@+#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.got_line_feed = arr.got_line_feed;+ 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(Node& arr, Context& ctx, int n) {+ if (n != -1 && n == 0) {+ return arr;+ }++ Node flattened = Node::createCollection();+ if (arr.got_line_feed) flattened.got_line_feed = true;++ for (NodeDeque::iterator iter = arr.collection()->begin(), iterEnd = arr.collection()->end();+ iter != iterEnd; iter++) {+ Node& e = *iter;++ // e has the lf set+ if (e.isCollection()) {++ // e.collection().got_line_feed = e.got_line_feed;+ Node recurseFlattened = flatten(e, ctx, n - 1);++ if(e.got_line_feed) {+ flattened.got_line_feed = e.got_line_feed;+ recurseFlattened.got_line_feed = e.got_line_feed;+ }++ for(auto i : (*recurseFlattened.collection())) {+ if (recurseFlattened.got_line_feed) {++ i.got_line_feed = true;+ }+ flattened.collection()->push_back(i);+ }++ } else {+ flattened.collection()->push_back(e);+ }+ }++ return flattened;+ }+}
+ libsass/src/sass_util.hpp view
@@ -0,0 +1,256 @@+#ifndef SASS_SASS_UTIL_H+#define SASS_SASS_UTIL_H++#include "ast.hpp"+#include "node.hpp"+#include "debug.hpp"++namespace Sass {+++++ /*+ 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 std::vector<std::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(), std::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] = std::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(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(std::vector<EnumType>& enumeration, KeyFunctorType& keyFunc, std::vector<std::pair<KeyType, std::vector<EnumType> > >& arr /*out*/) {++ std::map<unsigned int, KeyType> order;++ std::map<KeyType, std::vector<EnumType> > grouped;++ for (typename std::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(std::make_pair((unsigned int)order.size(), key));++ std::vector<EnumType> newCollection;+ newCollection.push_back(e);+ grouped.insert(std::make_pair(key, newCollection));+ } else {+ std::vector<EnumType>& collection = grouped.at(key);+ collection.push_back(e);+ }+ }++ for (unsigned int index = 0; index < order.size(); index++) {+ KeyType& key = order.at(index);+ std::vector<EnumType>& values = grouped.at(key);++ std::pair<KeyType, std::vector<EnumType> > grouping = std::make_pair(key, values);++ arr.push_back(grouping);+ }+ }+++}++#endif
+ libsass/src/sass_values.cpp view
@@ -0,0 +1,351 @@+#include <cstdlib>+#include <cstring>+#include "util.hpp"+#include "eval.hpp"+#include "values.hpp"+#include "sass/values.h"+#include "sass_values.hpp"++extern "C" {+ using namespace Sass;++ // 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; }+ bool ADDCALL sass_string_is_quoted(const union Sass_Value* v) { return v->string.quoted; }+ void ADDCALL sass_string_set_quoted(union Sass_Value* v, bool quoted) { v->string.quoted = quoted; }++ // 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)+ {+ union 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)+ {+ union 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)+ {+ union 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)+ {+ union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->string.quoted = false;+ 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_qstring(const char* val)+ {+ union Sass_Value* v = (Sass_Value*) calloc(1, sizeof(Sass_Value));+ if (v == 0) return 0;+ v->string.quoted = true;+ 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)+ {+ union 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)+ {+ union 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)+ {+ union 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)+ {+ union 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)+ {+ union 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_string_is_quoted(val) ? sass_make_qstring(val->string.value) : 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;++ }++ union Sass_Value* ADDCALL sass_value_stringify (const union Sass_Value* v, bool compressed, int precision)+ {+ Memory_Manager mem;+ Value* val = sass_value_to_ast_node(mem, v);+ std::string str(val->to_string(compressed, precision));+ return sass_make_qstring(str.c_str());+ }++ union Sass_Value* ADDCALL sass_value_op (enum Sass_OP op, const union Sass_Value* a, const union Sass_Value* b)+ {++ Sass::Value* rv = 0;+ Memory_Manager mem;++ try {++ Value* lhs = sass_value_to_ast_node(mem, a);+ Value* rhs = sass_value_to_ast_node(mem, b);++ // see if it's a relational expression+ switch(op) {+ case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs));+ case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs));+ case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs) && !Eval::eq(lhs, rhs));+ case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs));+ case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs));+ case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs) || Eval::eq(lhs, rhs));+ default: break;+ }++ if (sass_value_is_number(a) && sass_value_is_number(b)) {+ const Number* l_n = dynamic_cast<const Number*>(lhs);+ const Number* r_n = dynamic_cast<const Number*>(rhs);+ rv = Eval::op_numbers(mem, op, *l_n, *r_n);+ }+ else if (sass_value_is_number(a) && sass_value_is_color(a)) {+ const Number* l_n = dynamic_cast<const Number*>(lhs);+ const Color* r_c = dynamic_cast<const Color*>(rhs);+ rv = Eval::op_number_color(mem, op, *l_n, *r_c);+ }+ else if (sass_value_is_color(a) && sass_value_is_number(b)) {+ const Color* l_c = dynamic_cast<const Color*>(lhs);+ const Number* r_n = dynamic_cast<const Number*>(rhs);+ rv = Eval::op_color_number(mem, op, *l_c, *r_n);+ }+ else if (sass_value_is_color(a) && sass_value_is_color(b)) {+ const Color* l_c = dynamic_cast<const Color*>(lhs);+ const Color* r_c = dynamic_cast<const Color*>(rhs);+ rv = Eval::op_colors(mem, op, *l_c, *r_c);+ }+ else /* convert other stuff to string and apply operation */ {+ Value* l_v = dynamic_cast<Value*>(lhs);+ Value* r_v = dynamic_cast<Value*>(rhs);+ rv = Eval::op_strings(mem, op, *l_v, *r_v);+ }++ // ToDo: maybe we should should return null value?+ if (!rv) return sass_make_error("invalid return value");++ // convert result back to ast node+ return ast_node_to_sass_value(rv);++ }++ // simply pass the error message back to the caller for now+ catch (Exception::InvalidSass& e) { return sass_make_error(e.what()); }+ catch (std::bad_alloc&) { return sass_make_error("memory exhausted"); }+ catch (std::exception& e) { return sass_make_error(e.what()); }+ catch (std::string& e) { return sass_make_error(e.c_str()); }+ catch (const char* e) { return sass_make_error(e); }+ catch (...) { return sass_make_error("unknown"); }++ return 0;++ }++}
+ libsass/src/sass_values.hpp view
@@ -0,0 +1,81 @@+#ifndef SASS_SASS_VALUES_H+#define SASS_SASS_VALUES_H++#include "sass.h"++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;+ bool quoted;+ 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;+};++#endif
+ libsass/src/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 {+ SourceMap::SourceMap() : current_position(0, 0, 0), file("stdin") { }+ SourceMap::SourceMap(const std::string& file) : current_position(0, 0, 0), file(file) { }++ std::string SourceMap::render_srcmap(Context &ctx) {++ const bool include_sources = ctx.c_options->source_map_contents;+ const std::vector<std::string> links = ctx.srcmap_links;+ const std::vector<Resource>& sources(ctx.resources);++ 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 = links[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);++ if (include_sources) {+ JsonNode *json_contents = json_mkarray();+ for (size_t i = 0; i < source_index.size(); ++i) {+ const Resource& resource(sources[source_index[i]]);+ JsonNode *json_content = json_mkstring(resource.contents);+ json_append_element(json_contents, json_content);+ }+ if (json_contents->children.head)+ json_append_member(json_srcmap, "sourcesContent", json_contents);+ }++ std::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");+ std::string result = std::string(str);+ free(str);+ json_delete(json_srcmap);+ return result;+ }++ std::string SourceMap::serialize_mappings() {+ std::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(std::runtime_error("prepend sourcemap has illegal line"));+ }+ if (mapping.generated_position.line == size.line) {+ if (mapping.generated_position.column > size.column) {+ throw(std::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/src/source_map.hpp view
@@ -0,0 +1,62 @@+#ifndef SASS_SOURCE_MAP_H+#define SASS_SOURCE_MAP_H++#include <string>+#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 {++ class Context;+ class OutputBuffer;++ class SourceMap {++ public:+ std::vector<size_t> source_index;+ SourceMap();+ SourceMap(const std::string& file);++ 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);++ std::string render_srcmap(Context &ctx);+ ParserState remap(const ParserState& pstate);++ private:++ std::string serialize_mappings();++ std::vector<Mapping> mappings;+ Position current_position;+public:+ std::string file;+private:+ Base64VLQ base64vlq;+ };++ class OutputBuffer {+ public:+ OutputBuffer(void)+ : buffer(""),+ smap()+ { }+ public:+ std::string buffer;+ SourceMap smap;+ };++}++#endif
+ libsass/src/subset_map.hpp view
@@ -0,0 +1,145 @@+#ifndef SASS_SUBSET_MAP_H+#define SASS_SUBSET_MAP_H++#include <map>+#include <set>+#include <vector>+#include <algorithm>+#include <iterator>+++// #include <iostream>+// #include <sstream>+// template<typename T>+// std::string vector_to_string(std::vector<T> v)+// {+// std::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>+// std::string set_to_string(set<T> v)+// {+// std::stringstream buffer;+// buffer << "[";+// typename std::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 {++ 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:+ std::vector<V> values_;+ std::map<K, std::vector<triple<std::vector<K>, std::set<K>, size_t> > > hash_;+ public:+ void put(const std::vector<K>& s, const V& value);+ std::vector<std::pair<V, std::vector<K> > > get_kv(const std::vector<K>& s);+ std::vector<V> get_v(const std::vector<K>& s);+ bool empty() { return values_.empty(); }+ void clear() { values_.clear(); hash_.clear(); }+ const std::vector<V> values(void) { return values_; }+ };++ template<typename K, typename V>+ void Subset_Map<K, V>::put(const std::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);+ std::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>+ std::vector<std::pair<V, std::vector<K> > > Subset_Map<K, V>::get_kv(const std::vector<K>& s)+ {+ std::vector<K> sorted = s;+ sort(sorted.begin(), sorted.end());+ std::vector<std::pair<size_t, std::vector<K> > > indices;+ for (size_t i = 0, S = s.size(); i < S; ++i) {+ if (!hash_.count(s[i])) {+ continue;+ }+ std::vector<triple<std::vector<K>, std::set<K>, size_t> > subsets = hash_[s[i]];+ // std::cerr << "length of subsets: " << subsets.size() << std::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())) {+ // std::cout << vector_to_string(s) << " doesn't include " << set_to_string(subsets[j].second) << std::endl;+ continue;+ }+ indices.push_back(std::make_pair(subsets[j].third, subsets[j].first));+ // std::cerr << "pushed " << subsets[j].third << " and " << vector_to_string(subsets[j].first) << " onto indices" << std::endl;+ }+ }+ sort(indices.begin(), indices.end());+ typename std::vector<std::pair<size_t, std::vector<K> > >::iterator indices_end = unique(indices.begin(), indices.end());+ indices.resize(distance(indices.begin(), indices_end));++ std::vector<std::pair<V, std::vector<K> > > results;+ for (size_t i = 0, S = indices.size(); i < S; ++i) {+ results.push_back(std::make_pair(values_[indices[i].first], indices[i].second));+ }+ return results;+ }++ template<typename K, typename V>+ std::vector<V> Subset_Map<K, V>::get_v(const std::vector<K>& s)+ {+ std::vector<std::pair<V, std::vector<K> > > kvs = get_kv(s);+ std::vector<V> results;+ for (size_t i = 0, S = kvs.size(); i < S; ++i) results.push_back(kvs[i].first);+ return results;+ }++}++#endif
+ libsass/src/support/libsass.pc.in view
@@ -0,0 +1,11 @@+prefix=@prefix@+exec_prefix=@exec_prefix@+libdir=@libdir@+includedir=@includedir@++Name: libsass+URL: https://github.com/sass/libsass+Description: A C implementation of a Sass compiler+Version: @VERSION@+Libs: -L${libdir} -lsass+Cflags: -I${includedir}
+ libsass/src/to_c.cpp view
@@ -0,0 +1,73 @@+#include "to_c.hpp"+#include "ast.hpp"++namespace Sass {++ union Sass_Value* To_C::fallback_impl(AST_Node* n)+ { return sass_make_error("unknown type for C-API"); }++ union Sass_Value* To_C::operator()(Boolean* b)+ { return sass_make_boolean(b->value()); }++ union Sass_Value* To_C::operator()(Number* n)+ { return sass_make_number(n->value(), n->unit().c_str()); }++ union Sass_Value* To_C::operator()(Custom_Warning* w)+ { return sass_make_warning(w->message().c_str()); }++ union Sass_Value* To_C::operator()(Custom_Error* e)+ { return sass_make_error(e->message().c_str()); }++ union Sass_Value* To_C::operator()(Color* c)+ { return sass_make_color(c->r(), c->g(), c->b(), c->a()); }++ union Sass_Value* To_C::operator()(String_Constant* s)+ {+ if (s->quote_mark()) {+ return sass_make_qstring(s->value().c_str());+ } else {+ return sass_make_string(s->value().c_str());+ }+ }++ union Sass_Value* To_C::operator()(String_Quoted* s)+ { return sass_make_qstring(s->value().c_str()); }++ union Sass_Value* To_C::operator()(List* l)+ {+ union Sass_Value* v = sass_make_list(l->length(), l->separator());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ sass_list_set_value(v, i, (*l)[i]->perform(this));+ }+ return v;+ }++ union Sass_Value* To_C::operator()(Map* m)+ {+ union 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;+ }++ union Sass_Value* To_C::operator()(Arguments* a)+ {+ union 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;+ }++ union Sass_Value* To_C::operator()(Argument* a)+ { return a->value()->perform(this); }++ // not strictly necessary because of the fallback+ union Sass_Value* To_C::operator()(Null* n)+ { return sass_make_null(); }++};
+ libsass/src/to_c.hpp view
@@ -0,0 +1,41 @@+#ifndef SASS_TO_C_H+#define SASS_TO_C_H++#include "ast_fwd_decl.hpp"+#include "operation.hpp"+#include "sass/values.h"++namespace Sass {++ class To_C : public Operation_CRTP<union Sass_Value*, To_C> {+ // import all the class-specific methods and override as desired+ using Operation<union Sass_Value*>::operator();+ // override this to define a catch-all+ union Sass_Value* fallback_impl(AST_Node* n);++ public:++ To_C() { }+ virtual ~To_C() { }++ union Sass_Value* operator()(Boolean*);+ union Sass_Value* operator()(Number*);+ union Sass_Value* operator()(Color*);+ union Sass_Value* operator()(String_Constant*);+ union Sass_Value* operator()(String_Quoted*);+ union Sass_Value* operator()(Custom_Warning*);+ union Sass_Value* operator()(Custom_Error*);+ union Sass_Value* operator()(List*);+ union Sass_Value* operator()(Map*);+ union Sass_Value* operator()(Null*);+ union Sass_Value* operator()(Arguments*);+ union Sass_Value* operator()(Argument*);++ // dispatch to fallback implementation+ union Sass_Value* fallback(AST_Node* x)+ { return fallback_impl(x); }+ };++}++#endif
+ libsass/src/to_string.cpp view
@@ -0,0 +1,48 @@+#include <cmath>+#include <sstream>+#include <iomanip>+#include <iostream>++#include "ast.hpp"+#include "inspect.hpp"+#include "context.hpp"+#include "to_string.hpp"++namespace Sass {++ To_String::To_String(Context* ctx, bool in_declaration, bool in_debug)+ : ctx(ctx), in_declaration(in_declaration), in_debug(in_debug) { }+ To_String::~To_String() { }++ inline std::string To_String::fallback_impl(AST_Node* n)+ {+ Emitter emitter(ctx);+ Inspect i(emitter);+ i.in_declaration = in_declaration;+ i.in_debug = in_debug;+ if (n) n->perform(&i);+ return i.get_buffer();+ }++ inline std::string To_String::operator()(String_Schema* s)+ {+ std::string acc("");+ for (size_t i = 0, L = s->length(); i < L; ++i) {+ acc += s->elements()[i]->perform(this);+ }+ return acc;+ }++ inline std::string To_String::operator()(String_Quoted* s)+ {+ return s->value();+ }++ inline std::string To_String::operator()(String_Constant* s)+ {+ return s->value();+ }++ inline std::string To_String::operator()(Null* n)+ { return in_debug ? "null" : ""; }+}
+ libsass/src/to_string.hpp view
@@ -0,0 +1,38 @@+#ifndef SASS_TO_STRING_H+#define SASS_TO_STRING_H++#include <string>++#include "operation.hpp"++namespace Sass {++ class Context;+ class Null;++ class To_String : public Operation_CRTP<std::string, To_String> {+ // import all the class-specific methods and override as desired+ using Operation<std::string>::operator();+ // override this to define a catch-all+ std::string fallback_impl(AST_Node* n);++ Context* ctx;+ bool in_declaration;+ bool in_debug;++ public:++ To_String(Context* ctx = 0, bool in_declaration = true, bool in_debug = false);+ virtual ~To_String();++ std::string operator()(Null* n);+ std::string operator()(String_Schema*);+ std::string operator()(String_Quoted*);+ std::string operator()(String_Constant*);++ template <typename U>+ std::string fallback(U n) { return fallback_impl(n); }+ };+}++#endif
+ libsass/src/to_value.cpp view
@@ -0,0 +1,109 @@+#include "ast.hpp"+#include "to_value.hpp"+#include "to_string.hpp"++namespace Sass {++ Value* To_Value::fallback_impl(AST_Node* n)+ {+ // throw a runtime error if this happens+ // we want a well defined set of possible nodes+ throw std::runtime_error("invalid node for to_value");+ // mute warning+ return 0;+ }++ // Custom_Error is a valid value+ Value* To_Value::operator()(Custom_Error* e)+ {+ return e;+ }++ // Custom_Warning is a valid value+ Value* To_Value::operator()(Custom_Warning* w)+ {+ return w;+ }++ // Boolean is a valid value+ Value* To_Value::operator()(Boolean* b)+ {+ return b;+ }++ // Number is a valid value+ Value* To_Value::operator()(Number* n)+ {+ return n;+ }++ // Color is a valid value+ Value* To_Value::operator()(Color* c)+ {+ return c;+ }++ // String_Constant is a valid value+ Value* To_Value::operator()(String_Constant* s)+ {+ return s;+ }++ // String_Quoted is a valid value+ Value* To_Value::operator()(String_Quoted* s)+ {+ return s;+ }++ // List is a valid value+ Value* To_Value::operator()(List* l)+ {+ List* ll = SASS_MEMORY_NEW(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);+ }+ return ll;+ }++ // Map is a valid value+ Value* To_Value::operator()(Map* m)+ {+ return m;+ }++ // Null is a valid value+ Value* To_Value::operator()(Null* n)+ {+ return n;+ }++ // Argument returns its value+ Value* To_Value::operator()(Argument* arg)+ {+ if (!arg->name().empty()) return 0;+ return arg->value()->perform(this);+ }++ // Selector_List is converted to a string+ Value* To_Value::operator()(Selector_List* s)+ {+ To_String to_string(&ctx);+ return SASS_MEMORY_NEW(mem, String_Quoted,+ s->pstate(),+ s->perform(&to_string));+ }++ // Binary_Expression is converted to a string+ Value* To_Value::operator()(Binary_Expression* s)+ {+ To_String to_string(&ctx);+ return SASS_MEMORY_NEW(mem, String_Quoted,+ s->pstate(),+ s->perform(&to_string));+ }++};
+ libsass/src/to_value.hpp view
@@ -0,0 +1,50 @@+#ifndef SASS_TO_VALUE_H+#define SASS_TO_VALUE_H++#include "operation.hpp"+#include "sass/values.h"+#include "ast_fwd_decl.hpp"++namespace Sass {++ class To_Value : public Operation_CRTP<Value*, To_Value> {++ Value* fallback_impl(AST_Node* n);++ private:++ Context& ctx;+ Memory_Manager& mem;++ public:++ To_Value(Context& ctx, Memory_Manager& mem)+ : ctx(ctx), mem(mem)+ { }+ virtual ~To_Value() { }+ using Operation<Value*>::operator();++ Value* operator()(Argument*);+ Value* operator()(Boolean*);+ Value* operator()(Number*);+ Value* operator()(Color*);+ Value* operator()(String_Constant*);+ Value* operator()(String_Quoted*);+ Value* operator()(Custom_Warning*);+ Value* operator()(Custom_Error*);+ Value* operator()(List*);+ Value* operator()(Map*);+ Value* operator()(Null*);++ // convert to string via `To_String`+ Value* operator()(Selector_List*);+ Value* operator()(Binary_Expression*);++ // fallback throws error+ template <typename U>+ Value* fallback(U x) { return fallback_impl(x); }+ };++}++#endif
+ libsass/src/units.cpp view
@@ -0,0 +1,160 @@+#include <stdexcept>+#include "units.hpp"++#ifdef IN+#undef IN+#endif++namespace Sass {++ /* the conversion matrix can be readed the following way */+ /* if you go down, the factor is for the numerator (multiply) */+ /* if you go right, the factor is for the denominator (divide) */+ /* and yes, we actually use both, not sure why, but why not!? */++ const double size_conversion_factors[6][6] =+ {+ /* in cm pc mm pt px */+ /* in */ { 1, 2.54, 6, 25.4, 72, 96, },+ /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54 },+ /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0 },+ /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4 },+ /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0 },+ /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1, }+ };++ const double angle_conversion_factors[4][4] =+ {+ /* deg grad rad turn */+ /* deg */ { 1, 40.0/36.0, PI/180.0, 1.0/360.0 },+ /* grad */ { 36.0/40.0, 1, PI/200.0, 1.0/400.0 },+ /* rad */ { 180.0/PI, 200.0/PI, 1, 0.5/PI },+ /* turn */ { 360.0, 400.0, 2.0*PI, 1 }+ };++ const double time_conversion_factors[2][2] =+ {+ /* s ms */+ /* s */ { 1, 1000.0 },+ /* ms */ { 1/1000.0, 1 }+ };+ const double frequency_conversion_factors[2][2] =+ {+ /* Hz kHz */+ /* Hz */ { 1, 1/1000.0 },+ /* kHz */ { 1000.0, 1 }+ };+ const double resolution_conversion_factors[3][3] =+ {+ /* dpi dpcm dppx */+ /* dpi */ { 1, 2.54, 96 },+ /* dpcm */ { 1/2.54, 1, 96/2.54 },+ /* dppx */ { 1/96.0, 2.54/96, 1 }+ };++ SassUnitType get_unit_type(SassUnit unit)+ {+ switch (unit & 0xFF00)+ {+ case SassUnitType::LENGTH: return SassUnitType::LENGTH; break;+ case SassUnitType::ANGLE: return SassUnitType::ANGLE; break;+ case SassUnitType::TIME: return SassUnitType::TIME; break;+ case SassUnitType::FREQUENCY: return SassUnitType::FREQUENCY; break;+ case SassUnitType::RESOLUTION: return SassUnitType::RESOLUTION; break;+ default: return SassUnitType::INCOMMENSURABLE; break;+ }+ };++ SassUnit string_to_unit(const std::string& s)+ {+ // size units+ if (s == "px") return SassUnit::PX;+ else if (s == "pt") return SassUnit::PT;+ else if (s == "pc") return SassUnit::PC;+ else if (s == "mm") return SassUnit::MM;+ else if (s == "cm") return SassUnit::CM;+ else if (s == "in") return SassUnit::IN;+ // angle units+ else if (s == "deg") return SassUnit::DEG;+ else if (s == "grad") return SassUnit::GRAD;+ else if (s == "rad") return SassUnit::RAD;+ else if (s == "turn") return SassUnit::TURN;+ // time units+ else if (s == "s") return SassUnit::SEC;+ else if (s == "ms") return SassUnit::MSEC;+ // frequency units+ else if (s == "Hz") return SassUnit::HERTZ;+ else if (s == "kHz") return SassUnit::KHERTZ;+ // resolutions units+ else if (s == "dpi") return SassUnit::DPI;+ else if (s == "dpcm") return SassUnit::DPCM;+ else if (s == "dppx") return SassUnit::DPPX;+ // for unknown units+ else return SassUnit::UNKNOWN;+ }++ const char* unit_to_string(SassUnit unit)+ {+ switch (unit) {+ // size units+ case SassUnit::PX: return "px"; break;+ case SassUnit::PT: return "pt"; break;+ case SassUnit::PC: return "pc"; break;+ case SassUnit::MM: return "mm"; break;+ case SassUnit::CM: return "cm"; break;+ case SassUnit::IN: return "in"; break;+ // angle units+ case SassUnit::DEG: return "deg"; break;+ case SassUnit::GRAD: return "grad"; break;+ case SassUnit::RAD: return "rad"; break;+ case SassUnit::TURN: return "turn"; break;+ // time units+ case SassUnit::SEC: return "s"; break;+ case SassUnit::MSEC: return "ms"; break;+ // frequency units+ case SassUnit::HERTZ: return "Hz"; break;+ case SassUnit::KHERTZ: return "kHz"; break;+ // resolutions units+ case SassUnit::DPI: return "dpi"; break;+ case SassUnit::DPCM: return "dpcm"; break;+ case SassUnit::DPPX: return "dppx"; break;+ // for unknown units+ default: return ""; break;+ }+ }++ // throws incompatibleUnits exceptions+ double conversion_factor(const std::string& s1, const std::string& s2, bool strict)+ {+ // assert for same units+ if (s1 == s2) return 1;+ // get unit enum from string+ SassUnit u1 = string_to_unit(s1);+ SassUnit u2 = string_to_unit(s2);+ // query unit group types+ SassUnitType t1 = get_unit_type(u1);+ SassUnitType t2 = get_unit_type(u2);+ // get absolute offset+ // used for array acces+ size_t i1 = u1 - t1;+ size_t i2 = u2 - t2;+ // error if units are not of the same group+ // don't error for multiplication and division+ if (strict && t1 != t2) throw incompatibleUnits(u1, u2);+ // only process known units+ if (u1 != UNKNOWN && u2 != UNKNOWN) {+ switch (t1) {+ case SassUnitType::LENGTH: return size_conversion_factors[i1][i2]; break;+ case SassUnitType::ANGLE: return angle_conversion_factors[i1][i2]; break;+ case SassUnitType::TIME: return time_conversion_factors[i1][i2]; break;+ case SassUnitType::FREQUENCY: return frequency_conversion_factors[i1][i2]; break;+ case SassUnitType::RESOLUTION: return resolution_conversion_factors[i1][i2]; break;+ // ToDo: should we throw error here?+ case SassUnitType::INCOMMENSURABLE: return 0; break;+ }+ }+ // fallback+ return 1;+ }++}
+ libsass/src/units.hpp view
@@ -0,0 +1,88 @@+#ifndef SASS_UNITS_H+#define SASS_UNITS_H++#include <cmath>+#include <string>+#include <sstream>++namespace Sass {++ const double PI = acos(-1);++ enum SassUnitType {+ LENGTH = 0x000,+ ANGLE = 0x100,+ TIME = 0x200,+ FREQUENCY = 0x300,+ RESOLUTION = 0x400,+ INCOMMENSURABLE = 0x500+ };++ enum SassUnit {++ // size units+ IN = SassUnitType::LENGTH,+ CM,+ PC,+ MM,+ PT,+ PX,++ // angle units+ DEG = ANGLE,+ GRAD,+ RAD,+ TURN,++ // time units+ SEC = TIME,+ MSEC,++ // frequency units+ HERTZ = FREQUENCY,+ KHERTZ,++ // resolutions units+ DPI = RESOLUTION,+ DPCM,+ DPPX,++ // for unknown units+ UNKNOWN = INCOMMENSURABLE++ };++ extern const double size_conversion_factors[6][6];+ extern const double angle_conversion_factors[4][4];+ extern const double time_conversion_factors[2][2];+ extern const double frequency_conversion_factors[2][2];+ extern const double resolution_conversion_factors[3][3];++ enum SassUnit string_to_unit(const std::string&);+ const char* unit_to_string(SassUnit unit);+ enum SassUnitType get_unit_type(SassUnit unit);+ // throws incompatibleUnits exceptions+ double conversion_factor(const std::string&, const std::string&, bool = true);++ class incompatibleUnits: public std::exception+ {+ public:+ const char* msg;+ incompatibleUnits(SassUnit a, SassUnit b)+ : exception()+ {+ std::stringstream ss;+ ss << "Incompatible units: ";+ ss << "'" << unit_to_string(a) << "' and ";+ ss << "'" << unit_to_string(b) << "'";+ msg = ss.str().c_str();+ }+ virtual const char* what() const throw()+ {+ return msg;+ }+ };++}++#endif
+ libsass/src/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/src/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/src/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/src/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/src/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/src/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 std::string& str, size_t start, size_t end);+ size_t code_point_count(const std::string& str);++ // function that will return the byte offset of a code point in a+ size_t offset_at_position(const std::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 std::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+ std::string convert_from_utf16(const std::wstring& wstr);+ std::wstring convert_to_utf16(const std::string& str);+ #endif++ }+}++#endif
+ libsass/src/util.cpp view
@@ -0,0 +1,780 @@+#include "sass.h"+#include "ast.hpp"+#include "util.hpp"+#include "lexer.hpp"+#include "prelexer.hpp"+#include "constants.hpp"+#include "utf8/checked.h"++#include <cmath>+#include <stdint.h>++namespace Sass {++ #define out_of_memory() do { \+ std::cerr << "Out of memory.\n"; \+ exit(EXIT_FAILURE); \+ } while (0)++ double round(double val)+ {+ // work around some compiler issue+ // cygwin has it not defined in std+ using namespace std;++ // This was later repatched in 3.4.20+ // which is as yet unreleased.+ // https://github.com/sass/sass/commit/4e3e1d5684cc29073a507578fc977434ff488c93+ if (fmod(val, 1) - 0.5 > -0.00001) return std::ceil(val);+ return ::round(val);++ // Use this version once sass-spec is at 3.4.20+ // if (fmod(val, 1) - 0.5 > -0.00001) return ::round(val);+ // return value > 0 ? std::ceil(val) : std::floor(val);+ }++ /* Sadly, sass_strdup is not portable. */+ 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);+ }++ // helper for safe access to c_ctx+ const char* safe_str (const char* str, const char* alt) {+ return str == NULL ? alt : str;+ }++ void free_string_array(char ** arr) {+ if(!arr)+ return;++ char **it = arr;+ while (it && (*it)) {+ free(*it);+ ++it;+ }++ free(arr);+ }++ char **copy_strings(const std::vector<std::string>& strings, char*** array, int skip) {+ int num = static_cast<int>(strings.size()) - skip;+ char** arr = (char**) calloc(num + 1, sizeof(char*));+ if (arr == 0)+ return *array = (char **)NULL;++ for(int i = 0; i < num; i++) {+ arr[i] = (char*) malloc(sizeof(char) * (strings[i + skip].size() + 1));+ if (arr[i] == 0) {+ free_string_array(arr);+ return *array = (char **)NULL;+ }+ std::copy(strings[i + skip].begin(), strings[i + skip].end(), arr[i]);+ arr[i][strings[i + skip].size()] = '\0';+ }++ arr[num] = 0;+ return *array = arr;+ }++ std::string string_eval_escapes(const std::string& s)+ {++ std::string out("");+ bool esc = false;+ for (size_t i = 0, L = s.length(); i < L; ++i) {+ if(s[i] == '\\' && esc == false) {+ esc = true;++ // escape length+ size_t len = 1;++ // parse as many sequence chars as possible+ // ToDo: Check if ruby aborts after possible max+ while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;++ // hex string?+ if (len > 1) {++ // convert the extracted hex string to code point value+ // ToDo: Maybe we could do this without creating a substring+ uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);++ if (cp == 0) cp = 0xFFFD;++ // assert invalid code points+ if (cp >= 1) {++ // use a very simple approach to convert via utf8 lib+ // maybe there is a more elegant way; maybe we shoud+ // convert the whole output from string to a stream!?+ // allocate memory for utf8 char and convert to utf8+ unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);+ for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]);++ // skip some more chars?+ i += len - 1; esc = false;+ if (cp == 10) out += ' ';++ }++ }++ }+ else {+ out += s[i];+ esc = false;+ }+ }+ return out;++ }++ // double escape every escape sequences+ // escape unescaped quotes and backslashes+ std::string string_escape(const std::string& str)+ {+ std::string out("");+ for (auto i : str) {+ // escape some characters+ if (i == '"') out += '\\';+ if (i == '\'') out += '\\';+ if (i == '\\') out += '\\';+ out += i;+ }+ return out;+ }++ // unescape every escape sequence+ // only removes unescaped backslashes+ std::string string_unescape(const std::string& str)+ {+ std::string out("");+ bool esc = false;+ for (auto i : str) {+ if (esc || i != '\\') {+ esc = false;+ out += i;+ } else {+ esc = true;+ }+ }+ // open escape sequence at end+ // maybe it should thow an error+ if (esc) { out += '\\'; }+ return out;+ }++ // read css string (handle multiline DELIM)+ std::string read_css_string(const std::string& str)+ {+ std::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+ std::string evacuate_quotes(const std::string& str)+ {+ std::string out("");+ bool esc = false;+ for (auto i : str) {+ if (!esc) {+ // ignore next character+ if (i == '\\') esc = true;+ // evacuate unescaped quotes+ else if (i == '"') out += '\\';+ else if (i == '\'') out += '\\';+ }+ // get escaped char now+ else { esc = false; }+ // remove nothing+ out += i;+ }+ return out;+ }++ // double escape all escape sequences+ // keep unescaped quotes and backslashes+ std::string evacuate_escapes(const std::string& str)+ {+ std::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+ std::string string_to_output(const std::string& str)+ {+ std::string out("");+ bool lf = false;+ for (auto i : str) {+ if (i == 10) {+ out += ' ';+ lf = true;+ } else if (!(lf && isspace(i))) {+ out += i;+ lf = false;+ }+ }+ return out;+ }++ std::string comment_to_string(const std::string& text)+ {+ std::string str = "";+ size_t has = 0;+ char prev = 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 += ' ';+ if (prev == '*' && i == '/') str += "*/";+ else str += i;+ }+ } else if (i == '\n') {+ clean = true;+ } else if (i == '\r') {+ clean = true;+ } else {+ str += i;+ }+ prev = i;+ }+ if (has) return str;+ else return text;+ }++ std::string normalize_wspace(const std::string& str)+ {+ bool ws = false;+ bool esc = false;+ std::string text = "";+ for(const char& i : str) {+ if (!esc && i == '\\') {+ esc = true;+ ws = false;+ text += i;+ } else if (esc) {+ esc = false;+ ws = false;+ text += i;+ } else if (+ i == ' ' ||+ i == '\r' ||+ i == '\n' ||+ i == ' '+ ) {+ // only add one space+ if (!ws) text += ' ';+ ws = true;+ } else {+ ws = false;+ text += i;+ }+ }+ if (esc) text += '\\';+ return text;+ }++ // find best quote_mark by detecting if the string contains any single+ // or double quotes. When a single quote is found, we not we want a double+ // quote as quote_mark. Otherwise we check if the string cotains any double+ // 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;+ }++ std::string unquote(const std::string& s, char* qd, bool keep_utf8_sequences)+ {++ // 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;++ std::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 (keep_utf8_sequences) {+ unq.push_back(s[i]);+ } else if (len > 1) {++ // convert the extracted hex string to code point value+ // ToDo: Maybe we could do this without creating a substring+ uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), NULL, 16);++ if (s[i + len] == ' ') ++ len;++ // 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;++ }++ std::string quote(const std::string& s, char q, bool keep_linefeed_whitespace)+ {++ // autodetect with fallback to given quote+ q = detect_best_quotemark(s.c_str(), q);++ // return an empty quoted string+ if (s.empty()) return std::string(2, q ? q : '"');++ std::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 == '\n') {+ quoted.push_back('\\');+ quoted.push_back('a');+ // we hope we can remove this flag once we figure out+ // why ruby sass has these different output behaviors+ if (keep_linefeed_whitespace)+ quoted.push_back(' ');+ } 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)+ {+ using namespace Prelexer;+ using namespace Constants;+ return sequence <+ zero_plus <+ alternatives <+ exactly <' '>,+ exactly <'\t'>,+ line_comment,+ block_comment,+ delimited_by <+ slash_star,+ star_slash,+ false+ >+ >+ >,+ re_linebreak+ >(start) != 0;+ }++ namespace Util {+ using std::string;++ std::string rtrim(const std::string &str) {+ std::string trimmed = str;+ size_t pos_ws = trimmed.find_last_not_of(" \t\n\v\f\r");+ if (pos_ws != std::string::npos)+ { trimmed.erase(pos_ws + 1); }+ else { trimmed.clear(); }+ return trimmed;+ }++ std::string normalize_underscores(const std::string& str) {+ std::string normalized = str;+ for(size_t i = 0, L = normalized.length(); i < L; ++i) {+ if(normalized[i] == '_') {+ normalized[i] = '-';+ }+ }+ return normalized;+ }++ std::string normalize_decimals(const std::string& str) {+ std::string prefix = "0";+ std::string normalized = str;++ return normalized[0] == '.' ? normalized.insert(0, prefix) : normalized;+ }++ // compress a color sixtuplet if possible+ // input: "#CC9900" -> output: "#C90"+ std::string normalize_sixtuplet(const std::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 std::string("#" + col.substr(1, 1)+ + col.substr(3, 1)+ + col.substr(5, 1));+ } else {+ return std::string(col);+ }+ }++ bool isPrintable(Ruleset* r, Sass_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<At_Rule*>(stm)) {+ return true;+ } else 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)) {+ // keep for uncompressed+ if (style != SASS_STYLE_COMPRESSED) {+ hasDeclarations = true;+ }+ // output style compressed+ if (c->is_important()) {+ hasDeclarations = c->is_important();+ }+ } else if (Declaration* d = dynamic_cast<Declaration*>(stm)) {+ return isPrintable(d, style);+ } else {+ hasDeclarations = true;+ }++ if (hasDeclarations || hasPrintableChildBlocks) {+ return true;+ }+ }++ return false;+ }++ bool isPrintable(String_Constant* s, Sass_Output_Style style)+ {+ return ! s->value().empty();+ }++ bool isPrintable(String_Quoted* s, Sass_Output_Style style)+ {+ return true;+ }++ bool isPrintable(Declaration* d, Sass_Output_Style style)+ {+ Expression* val = d->value();+ if (String_Quoted* sq = dynamic_cast<String_Quoted*>(val)) return isPrintable(sq, style);+ if (String_Constant* sc = dynamic_cast<String_Constant*>(val)) return isPrintable(sc, style);+ return true;+ }++ bool isPrintable(Supports_Block* f, Sass_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()) {+ // 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, Sass_Output_Style style)+ {+ if (m == 0) return false;+ Block* b = m->block();+ if (b == 0) return false;+ for (size_t i = 0, L = b->length(); i < L; ++i) {+ Statement* stm = (*b)[i];+ if (typeid(*stm) == typeid(At_Rule)) return true;+ if (typeid(*stm) == typeid(Declaration)) return true;+ if (Has_Block* child = dynamic_cast<Has_Block*>(stm)) {+ if (isPrintable(child->block(), style)) return true;+ }+ }+ return false;+ }++ bool isPrintable(Block* b, Sass_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)) {+ Comment* c = (Comment*) stm;+ // keep for uncompressed+ if (style != SASS_STYLE_COMPRESSED) {+ return true;+ }+ // output style compressed+ if (c->is_important()) {+ return true;+ }+ }+ else if (typeid(*stm) == typeid(Ruleset)) {+ Ruleset* r = (Ruleset*) stm;+ if (isPrintable(r, style)) {+ return true;+ }+ }+ else if (typeid(*stm) == typeid(Supports_Block)) {+ Supports_Block* f = (Supports_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;+ }++ std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep)+ {+ switch (vec.size())+ {+ case 0:+ return std::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/src/util.hpp view
@@ -0,0 +1,61 @@+#ifndef SASS_UTIL_H+#define SASS_UTIL_H++#include <vector>+#include <string>+#include <assert.h>+#include "sass/base.h"+#include "ast_fwd_decl.hpp"++#define SASS_ASSERT(cond, msg) assert(cond && msg)++namespace Sass {++ double round(double val);+ char* sass_strdup(const char* str);+ double sass_atof(const char* str);+ const char* safe_str(const char *, const char* = "");+ void free_string_array(char **);+ char **copy_strings(const std::vector<std::string>&, char ***, int = 0);+ std::string string_escape(const std::string& str);+ std::string string_unescape(const std::string& str);+ std::string string_eval_escapes(const std::string& str);+ std::string read_css_string(const std::string& str);+ std::string evacuate_quotes(const std::string& str);+ std::string evacuate_escapes(const std::string& str);+ std::string string_to_output(const std::string& str);+ std::string comment_to_string(const std::string& text);+ std::string normalize_wspace(const std::string& str);++ std::string quote(const std::string&, char q = 0, bool keep_linefeed_whitespace = false);+ std::string unquote(const std::string&, char* q = 0, bool keep_utf8_sequences = false);+ 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 {++ std::string rtrim(const std::string& str);++ std::string normalize_underscores(const std::string& str);+ std::string normalize_decimals(const std::string& str);+ std::string normalize_sixtuplet(const std::string& col);++ std::string vecJoin(const std::vector<std::string>& vec, const std::string& sep);+ bool containsAnyPrintableStatements(Block* b);++ bool isPrintable(Ruleset* r, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(Supports_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(Media_Block* r, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(Block* b, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(String_Constant* s, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(String_Quoted* s, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isPrintable(Declaration* d, Sass_Output_Style style = SASS_STYLE_NESTED);+ bool isAscii(const char chr);++ }+}+#endif
+ libsass/src/values.cpp view
@@ -0,0 +1,137 @@+#include "sass.h"+#include "values.hpp"++#include <stdint.h>++namespace Sass {++ // convert value from C++ side to C-API+ union Sass_Value* ast_node_to_sass_value (const Expression* val)+ {+ if (val->concrete_type() == Expression::NUMBER)+ {+ const Number* res = dynamic_cast<const Number*>(val);+ return sass_make_number(res->value(), res->unit().c_str());+ }+ else if (val->concrete_type() == Expression::COLOR)+ {+ const Color* col = dynamic_cast<const Color*>(val);+ return sass_make_color(col->r(), col->g(), col->b(), col->a());+ }+ else if (val->concrete_type() == Expression::LIST)+ {+ const List* l = dynamic_cast<const List*>(val);+ union Sass_Value* list = sass_make_list(l->size(), l->separator());+ for (size_t i = 0, L = l->length(); i < L; ++i) {+ auto val = ast_node_to_sass_value((*l)[i]);+ sass_list_set_value(list, i, val);+ }+ return list;+ }+ else if (val->concrete_type() == Expression::MAP)+ {+ const Map* m = dynamic_cast<const Map*>(val);+ union Sass_Value* map = sass_make_map(m->length());+ size_t i = 0; for (auto key : m->keys()) {+ sass_map_set_key(map, i, ast_node_to_sass_value(key));+ sass_map_set_value(map, i, ast_node_to_sass_value(m->at(key)));+ ++ i;+ }+ return map;+ }+ else if (val->concrete_type() == Expression::NULL_VAL)+ {+ return sass_make_null();+ }+ else if (val->concrete_type() == Expression::BOOLEAN)+ {+ const Boolean* res = dynamic_cast<const Boolean*>(val);+ return sass_make_boolean(res->value());+ }+ else if (val->concrete_type() == Expression::STRING)+ {+ if (const String_Quoted* qstr = dynamic_cast<const String_Quoted*>(val))+ {+ return sass_make_qstring(qstr->value().c_str());+ }+ else if (const String_Constant* cstr = dynamic_cast<const String_Constant*>(val))+ {+ return sass_make_string(cstr->value().c_str());+ }+ }+ return sass_make_error("unknown sass value type");+ }++ // convert value from C-API to C++ side+ Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val)+ {+ switch (sass_value_get_tag(val)) {+ case SASS_NUMBER:+ return SASS_MEMORY_NEW(mem, Number,+ ParserState("[C-VALUE]"),+ sass_number_get_value(val),+ sass_number_get_unit(val));+ break;+ case SASS_BOOLEAN:+ return SASS_MEMORY_NEW(mem, Boolean,+ ParserState("[C-VALUE]"),+ sass_boolean_get_value(val));+ break;+ case SASS_COLOR:+ return SASS_MEMORY_NEW(mem, Color,+ ParserState("[C-VALUE]"),+ sass_color_get_r(val),+ sass_color_get_g(val),+ sass_color_get_b(val),+ sass_color_get_a(val));+ break;+ case SASS_STRING:+ if (sass_string_is_quoted(val)) {+ return SASS_MEMORY_NEW(mem, String_Quoted,+ ParserState("[C-VALUE]"),+ sass_string_get_value(val));+ } else {+ return SASS_MEMORY_NEW(mem, String_Constant,+ ParserState("[C-VALUE]"),+ sass_string_get_value(val));+ }+ break;+ case SASS_LIST: {+ List* l = SASS_MEMORY_NEW(mem, List,+ ParserState("[C-VALUE]"),+ sass_list_get_length(val),+ sass_list_get_separator(val));+ for (size_t i = 0, L = sass_list_get_length(val); i < L; ++i) {+ *l << sass_value_to_ast_node(mem, sass_list_get_value(val, i));+ }+ return l;+ }+ break;+ case SASS_MAP: {+ Map* m = SASS_MEMORY_NEW(mem, Map, ParserState("[C-VALUE]"));+ for (size_t i = 0, L = sass_map_get_length(val); i < L; ++i) {+ *m << std::make_pair(+ sass_value_to_ast_node(mem, sass_map_get_key(val, i)),+ sass_value_to_ast_node(mem, sass_map_get_value(val, i)));+ }+ return m;+ }+ break;+ case SASS_NULL:+ return SASS_MEMORY_NEW(mem, Null, ParserState("[C-VALUE]"));+ break;+ case SASS_ERROR:+ return SASS_MEMORY_NEW(mem, Custom_Error,+ ParserState("[C-VALUE]"),+ sass_error_get_message(val));+ break;+ case SASS_WARNING:+ return SASS_MEMORY_NEW(mem, Custom_Warning,+ ParserState("[C-VALUE]"),+ sass_warning_get_message(val));+ break;+ }+ return 0;+ }++}
+ libsass/src/values.hpp view
@@ -0,0 +1,12 @@+#ifndef SASS_VALUES_H+#define SASS_VALUES_H++#include "ast.hpp"++namespace Sass {++ union Sass_Value* ast_node_to_sass_value (const Expression* val);+ Value* sass_value_to_ast_node (Memory_Manager& mem, const union Sass_Value* val);++}+#endif
− libsass/subset_map.hpp
@@ -1,147 +0,0 @@-#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
@@ -1,61 +0,0 @@-#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
@@ -1,44 +0,0 @@-#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
@@ -1,34 +0,0 @@-#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, bool in_declaration)- : ctx(ctx), in_declaration(in_declaration) { }- To_String::~To_String() { }-- inline string To_String::fallback_impl(AST_Node* n)- {- Emitter emitter(ctx);- Inspect i(emitter);- i.in_declaration = in_declaration;- 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
@@ -1,35 +0,0 @@-#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;- bool in_declaration;-- public:- To_String(Context* ctx = 0, bool in_declaration = true);- 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
@@ -1,155 +0,0 @@-#include <stdexcept>-#include "units.hpp"--namespace Sass {-- /* the conversion matrix can be readed the following way */- /* if you go down, the factor is for the numerator (multiply) */- /* if you go right, the factor is for the denominator (divide) */- /* and yes, we actually use both, not sure why, but why not!? */-- const double size_conversion_factors[6][6] =- {- /* in cm pc mm pt px */- /* in */ { 1, 2.54, 6, 25.4, 72, 96, },- /* cm */ { 1.0/2.54, 1, 6.0/2.54, 10, 72.0/2.54, 96.0/2.54 },- /* pc */ { 1.0/6.0, 2.54/6.0, 1, 25.4/6.0, 72.0/6.0, 96.0/6.0 },- /* mm */ { 1.0/25.4, 1.0/10.0, 6.0/25.4, 1, 72.0/25.4, 96.0/25.4 },- /* pt */ { 1.0/72.0, 2.54/72.0, 6.0/72.0, 25.4/72.0, 1, 96.0/72.0 },- /* px */ { 1.0/96.0, 2.54/96.0, 6.0/96.0, 25.4/96.0, 72.0/96.0, 1, }- };-- const double angle_conversion_factors[4][4] =- {- /* deg grad rad turn */- /* deg */ { 1, 40.0/36.0, PI/180.0, 1.0/360.0 },- /* grad */ { 36.0/40.0, 1, PI/200.0, 1.0/400.0 },- /* rad */ { 180.0/PI, 200.0/PI, 1, 0.5/PI },- /* turn */ { 360.0, 400.0, 2.0*PI, 1 }- };-- const double time_conversion_factors[2][2] =- {- /* s ms */- /* s */ { 1, 1000.0 },- /* ms */ { 1/1000.0, 1 }- };- const double frequency_conversion_factors[2][2] =- {- /* Hz kHz */- /* Hz */ { 1, 1/1000.0 },- /* kHz */ { 1000.0, 1 }- };- const double resolution_conversion_factors[3][3] =- {- /* dpi dpcm dppx */- /* dpi */ { 1, 2.54, 96 },- /* dpcm */ { 1/2.54, 1, 96/2.54 },- /* dppx */ { 1/96.0, 2.54/96, 1 }- };-- SassUnitType get_unit_type(SassUnit unit)- {- switch (unit & 0xFF00)- {- case SIZE: return SIZE; break;- case ANGLE: return ANGLE; break;- case TIME: return TIME; break;- case FREQUENCY: return FREQUENCY; break;- case RESOLUTION: return RESOLUTION; break;- default: return INCOMMENSURABLE; break;- }- };-- SassUnit string_to_unit(const string& s)- {- // size units- if (s == "px") return PX;- else if (s == "pt") return PT;- else if (s == "pc") return PC;- else if (s == "mm") return MM;- else if (s == "cm") return CM;- else if (s == "in") return IN;- // angle units- else if (s == "deg") return DEG;- else if (s == "grad") return GRAD;- else if (s == "rad") return RAD;- else if (s == "turn") return TURN;- // time units- else if (s == "s") return SEC;- else if (s == "ms") return MSEC;- // frequency units- else if (s == "Hz") return HERTZ;- else if (s == "kHz") return KHERTZ;- // resolutions units- else if (s == "dpi") return DPI;- else if (s == "dpcm") return DPCM;- else if (s == "dppx") return DPPX;- // for unknown units- else return UNKNOWN;- }-- const char* unit_to_string(SassUnit unit)- {- switch (unit) {- // size units- case PX: return "px"; break;- case PT: return "pt"; break;- case PC: return "pc"; break;- case MM: return "mm"; break;- case CM: return "cm"; break;- case IN: return "in"; break;- // angle units- case DEG: return "deg"; break;- case GRAD: return "grad"; break;- case RAD: return "rad"; break;- case TURN: return "turn"; break;- // time units- case SEC: return "s"; break;- case MSEC: return "ms"; break;- // frequency units- case HERTZ: return "Hz"; break;- case KHERTZ: return "kHz"; break;- // resolutions units- case DPI: return "dpi"; break;- case DPCM: return "dpcm"; break;- case DPPX: return "dppx"; break;- // for unknown units- default: return ""; break;;- }- }-- // throws incompatibleUnits exceptions- double conversion_factor(const string& s1, const string& s2)- {- // assert for same units- if (s1 == s2) return 1;- // get unit enum from string- SassUnit u1 = string_to_unit(s1);- SassUnit u2 = string_to_unit(s2);- // query unit group types- SassUnitType t1 = get_unit_type(u1);- SassUnitType t2 = get_unit_type(u2);- // get absolute offset- // used for array acces- size_t i1 = u1 - t1;- size_t i2 = u2 - t2;- // error if units are not of the same group- if (t1 != t2) throw incompatibleUnits(u1, u2);- // only process known units- if (u1 != UNKNOWN && u2 != UNKNOWN) {- switch (t1) {- case SIZE: return size_conversion_factors[i1][i2]; break;- case ANGLE: return angle_conversion_factors[i1][i2]; break;- case TIME: return time_conversion_factors[i1][i2]; break;- case FREQUENCY: return frequency_conversion_factors[i1][i2]; break;- case RESOLUTION: return resolution_conversion_factors[i1][i2]; break;- // ToDo: should we throw error here?- case INCOMMENSURABLE: return 0; break;- }- }- // fallback- return 1;- }--}
− libsass/units.hpp
@@ -1,89 +0,0 @@-#ifndef SASS_UNITS_H-#define SASS_UNITS_H--#include <cmath>-#include <string>-#include <sstream>--namespace Sass {- using namespace std;-- const double PI = acos(-1);-- enum SassUnitType {- SIZE = 0x000,- ANGLE = 0x100,- TIME = 0x200,- FREQUENCY = 0x300,- RESOLUTION = 0x400,- INCOMMENSURABLE = 0x500- };-- enum SassUnit {-- // size units- IN = SIZE,- CM,- PC,- MM,- PT,- PX,-- // angle units- DEG = ANGLE,- GRAD,- RAD,- TURN,-- // time units- SEC = TIME,- MSEC,-- // frequency units- HERTZ = FREQUENCY,- KHERTZ,-- // resolutions units- DPI = RESOLUTION,- DPCM,- DPPX,-- // for unknown units- UNKNOWN = INCOMMENSURABLE-- };-- extern const double size_conversion_factors[6][6];- extern const double angle_conversion_factors[4][4];- extern const double time_conversion_factors[2][2];- extern const double frequency_conversion_factors[2][2];- extern const double resolution_conversion_factors[3][3];-- SassUnit string_to_unit(const string&);- const char* unit_to_string(SassUnit unit);- SassUnitType get_unit_type(SassUnit unit);- // throws incompatibleUnits exceptions- double conversion_factor(const string&, const string&);-- class incompatibleUnits: public exception- {- public:- const char* msg;- incompatibleUnits(SassUnit a, SassUnit b)- : exception()- {- stringstream ss;- ss << "Incompatible units: ";- ss << "'" << unit_to_string(a) << "' and ";- ss << "'" << unit_to_string(b) << "'";- msg = ss.str().c_str();- };- virtual const char* what() const throw()- {- return msg;- }- };--}--#endif
− libsass/utf8.h
@@ -1,34 +0,0 @@-// Copyright 2006 Nemanja Trifunovic--/*-Permission is hereby granted, free of charge, to any person or organization-obtaining a copy of the software and accompanying documentation covered by-this license (the "Software") to use, reproduce, display, distribute,-execute, and transmit the Software, and to prepare derivative works of the-Software, and to permit third-parties to whom the Software is furnished to-do so, all subject to the following:--The copyright notices in the Software and this entire statement, including-the above license grant, this restriction and the following disclaimer,-must be included in all copies of the Software, in whole or in part, and-all derivative works of the Software, unless such copies or derivative-works are solely in the form of machine-executable object code generated by-a source language processor.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER-DEALINGS IN THE SOFTWARE.-*/---#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731-#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731--#include "utf8/checked.h"-#include "utf8/unchecked.h"--#endif // header guard
− libsass/utf8/checked.h
@@ -1,327 +0,0 @@-// Copyright 2006 Nemanja Trifunovic--/*-Permission is hereby granted, free of charge, to any person or organization-obtaining a copy of the software and accompanying documentation covered by-this license (the "Software") to use, reproduce, display, distribute,-execute, and transmit the Software, and to prepare derivative works of the-Software, and to permit third-parties to whom the Software is furnished to-do so, all subject to the following:--The copyright notices in the Software and this entire statement, including-the above license grant, this restriction and the following disclaimer,-must be included in all copies of the Software, in whole or in part, and-all derivative works of the Software, unless such copies or derivative-works are solely in the form of machine-executable object code generated by-a source language processor.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER-DEALINGS IN THE SOFTWARE.-*/---#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731-#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731--#include "core.h"-#include <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
@@ -1,329 +0,0 @@-// Copyright 2006 Nemanja Trifunovic--/*-Permission is hereby granted, free of charge, to any person or organization-obtaining a copy of the software and accompanying documentation covered by-this license (the "Software") to use, reproduce, display, distribute,-execute, and transmit the Software, and to prepare derivative works of the-Software, and to permit third-parties to whom the Software is furnished to-do so, all subject to the following:--The copyright notices in the Software and this entire statement, including-the above license grant, this restriction and the following disclaimer,-must be included in all copies of the Software, in whole or in part, and-all derivative works of the Software, unless such copies or derivative-works are solely in the form of machine-executable object code generated by-a source language processor.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER-DEALINGS IN THE SOFTWARE.-*/---#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731-#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731--#include <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
@@ -1,228 +0,0 @@-// Copyright 2006 Nemanja Trifunovic--/*-Permission is hereby granted, free of charge, to any person or organization-obtaining a copy of the software and accompanying documentation covered by-this license (the "Software") to use, reproduce, display, distribute,-execute, and transmit the Software, and to prepare derivative works of the-Software, and to permit third-parties to whom the Software is furnished to-do so, all subject to the following:--The copyright notices in the Software and this entire statement, including-the above license grant, this restriction and the following disclaimer,-must be included in all copies of the Software, in whole or in part, and-all derivative works of the Software, unless such copies or derivative-works are solely in the form of machine-executable object code generated by-a source language processor.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER-DEALINGS IN THE SOFTWARE.-*/---#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731-#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731--#include "core.h"--namespace utf8-{- namespace unchecked - {- template <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
@@ -1,97 +0,0 @@-#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
@@ -1,37 +0,0 @@-#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
@@ -1,704 +0,0 @@-#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);- }-- string string_eval_escapes(const string& s)- {-- string out("");- bool esc = false;- for (size_t i = 0, L = s.length(); i < L; ++i) {- if(s[i] == '\\' && esc == false) {- esc = true;-- // escape length- size_t len = 1;-- // parse as many sequence chars as possible- // ToDo: Check if ruby aborts after possible max- while (i + len < L && s[i + len] && isxdigit(s[i + len])) ++ len;-- // hex string?- if (len > 1) {-- // convert the extracted hex string to code point value- // ToDo: Maybe we could do this without creating a substring- uint32_t cp = strtol(s.substr (i + 1, len - 1).c_str(), nullptr, 16);-- if (cp == 0) cp = 0xFFFD;-- // assert invalid code points- if (cp >= 1) {-- // use a very simple approach to convert via utf8 lib- // maybe there is a more elegant way; maybe we shoud- // convert the whole output from string to a stream!?- // allocate memory for utf8 char and convert to utf8- unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);- for(size_t m = 0; u[m] && m < 5; m++) out.push_back(u[m]);-- // skip some more chars?- i += len - 1; esc = false;- if (cp == 10) out += ' ';-- }-- }-- }- else {- out += s[i];- esc = false;- }- }- return out;-- }-- // double escape every escape sequences- // escape unescaped quotes and backslashes- 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;- char prev = 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 += ' ';- if (prev == '*' && i == '/') str += "*/";- else str += i;- }- } else if (i == '\n') {- clean = true;- } else if (i == '\r') {- clean = true;- } else {- str += i;- }- prev = i;- }- if (has) return str;- else return text;- }-- string normalize_wspace(const string& str)- {- bool ws = false;- bool esc = false;- string text = "";- for(const char& i : str) {- if (!esc && i == '\\') {- esc = true;- ws = false;- text += i;- } else if (esc) {- esc = false;- ws = false;- text += i;- } else if (- i == ' ' ||- i == '\r' ||- i == '\n' ||- i == ' '- ) {- // only add one space- if (!ws) text += ' ';- ws = true;- } else {- ws = false;- text += i;- }- }- if (esc) text += '\\';- return text;- }-- // find best quote_mark by detecting if the string contains any single- // or double quotes. When a single quote is found, we not we want a double- // quote as quote_mark. Otherwise we check if the string cotains any double- // 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, bool keep_linefeed_whitespace)- {-- // 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');- // we hope we can remove this flag once we figure out- // why ruby sass has these different output behaviors- if (keep_linefeed_whitespace)- quoted.push_back(' ');- } 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 if (Declaration* d = dynamic_cast<Declaration*>(stm)) {- return isPrintable(d, style);- } else {- hasDeclarations = true;- }-- if (hasDeclarations || hasPrintableChildBlocks) {- return true;- }- }-- return false;- }-- bool isPrintable(String_Constant* s, Output_Style style)- {- return ! s->value().empty();- }-- bool isPrintable(String_Quoted* s, Output_Style style)- {- return true;- }-- bool isPrintable(Declaration* d, Output_Style style)- {- Expression* val = d->value();- if (String_Quoted* sq = dynamic_cast<String_Quoted*>(val)) return isPrintable(sq, style);- if (String_Constant* sc = dynamic_cast<String_Constant*>(val)) return isPrintable(sc, style);- return true;- }-- bool isPrintable(Expression* e, Output_Style style) {- return isPrintable(e, style);- }-- 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
@@ -1,54 +0,0 @@-#ifndef SASS_UTIL_H-#define SASS_UTIL_H--#include <cstdio>-#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 string_eval_escapes(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, bool keep_linefeed_whitespace = false);- 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 isPrintable(String_Constant* s, Output_Style style = NESTED);- bool isPrintable(String_Quoted* s, Output_Style style = NESTED);- bool isPrintable(Declaration* d, Output_Style style = NESTED);- bool isPrintable(Expression* e, Output_Style style = NESTED);- bool isAscii(const char chr);-- }-}-#endif
tests/Main.hs view
@@ -35,9 +35,19 @@ #ifndef EXTERNAL_LIBSASS it "should report correct version" $ do str <- peekCString libsass_version- str `shouldBe` "3.2.4-18-g3672"+ str `shouldBe` "3.3.2" #endif it "should support quoted strings" $ withCString "sample" $ \cstr -> do str <- sass_make_qstring cstr sass_string_is_quoted str `shouldReturn` True++ it "should correctly combine two SassValues" $ withCString "" $ \unit -> do+ val1 <- sass_make_number 1.0 unit+ val2 <- sass_make_number 2.0 unit+ val3 <- sass_value_op (fromIntegral $ fromEnum SassAdd) val1 val2+ result <- sass_number_get_value val3+ sass_delete_value val1+ sass_delete_value val2+ sass_delete_value val3+ result `shouldBe` 3.0