packages feed

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 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_, &quote_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_, &quote_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, &quote_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