1
0
mirror of https://github.com/bitwarden/help synced 2025-12-27 05:33:14 +00:00

Promote to Master (#748)

* initial commit

* adding quotes for the array error

* Create Gemfile

* Create Gemfile.lock

* add .nvmrc and .node-version

* removed /article from URL

* update links to work with netlify

* more fixed links

* link fixes

* update bad links

* Update netlify.toml

toml test for redirects

* article redirect

* link fixes

* Update index.html

* Update netlify.toml

* Update _config.yml

* Update netlify.toml

* Update netlify.toml

* Update netlify.toml

* Update netlify.toml

* Update netlify.toml

* add article back into URL for launch

* Update netlify.toml

* Update netlify.toml

* add order to categories front matter

* Update netlify.toml

* update

* sidemenu update

* Revert "sidemenu update"

This reverts commit 5441c3d35c.

* update order prop

* Navbar updates per Gary and compiler warnings

* font/style tweaks

* Update sidebar.html

* Stage Release Documentation (#739)

* initial drafts

* rewrite Custom Fields article to prioritize new context-menu option & better organize ancillary information

* edit

* edit

* Custom Field Context Menu & CAPTCHA item in release notes

* SSO relink event

* update rn

* small edits

* improve release notes titles

* fix side menu

* Edits courtest of mportune!

* update order

* link fixes

* link cleanup

* image updates and a link

* fix trailing slash

Co-authored-by: DanHillesheim <79476558+DanHillesheim@users.noreply.github.com>
This commit is contained in:
fred_the_tech_writer
2021-09-21 13:21:11 -04:00
committed by GitHub
parent 63f78e8979
commit 906e2ca0dd
3304 changed files with 386714 additions and 8864 deletions

View File

@@ -0,0 +1 @@
3.6.4

View File

@@ -0,0 +1,60 @@
#include <cstring>
#include <iostream>
#include <stdint.h>
#include <sass.h>
// gcc: g++ -shared plugin.cpp -o plugin.so -fPIC -Llib -lsass
// mingw: g++ -shared plugin.cpp -o plugin.dll -Llib -lsass
extern "C" const char* ADDCALL libsass_get_version() {
return libsass_version();
}
union Sass_Value* custom_function(const union Sass_Value* s_args, Sass_Function_Entry cb, struct Sass_Compiler* comp)
{
// get context/option struct associated with this compiler
struct Sass_Context* ctx = sass_compiler_get_context(comp);
struct Sass_Options* opts = sass_compiler_get_options(comp);
// get the cookie from function descriptor
void* cookie = sass_function_get_cookie(cb);
// we actually abuse the void* to store an "int"
return sass_make_number((intptr_t)cookie, "px");
}
extern "C" Sass_Function_List ADDCALL libsass_load_functions()
{
// allocate a custom function caller
Sass_Function_Entry c_func =
sass_make_function("foo()", custom_function, (void*)42);
// create list of all custom functions
Sass_Function_List fn_list = sass_make_function_list(1);
// put the only function in this plugin to the list
sass_function_set_list_entry(fn_list, 0, c_func);
// return the list
return fn_list;
}
Sass_Import_List custom_importer(const char* cur_path, Sass_Importer_Entry cb, struct Sass_Compiler* comp)
{
// get the cookie from importer descriptor
void* cookie = sass_importer_get_cookie(cb);
// create a list to hold our import entries
Sass_Import_List incs = sass_make_import_list(1);
// create our only import entry (route path back)
incs[0] = sass_make_import_entry(cur_path, 0, 0);
// return imports
return incs;
}
extern "C" Sass_Importer_List ADDCALL libsass_load_importers()
{
// allocate a custom function caller
Sass_Importer_Entry c_imp =
sass_make_importer(custom_importer, - 99, (void*)42);
// create list of all custom functions
Sass_Importer_List imp_list = sass_make_importer_list(1);
// put the only function in this plugin to the list
sass_importer_set_list_entry(imp_list, 0, c_imp);
// return the list
return imp_list;
}

View File

@@ -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

View File

@@ -0,0 +1,97 @@
#ifndef SASS_BASE_H
#define SASS_BASE_H
// #define DEBUG
// #define DEBUG_SHARED_PTR
#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
// Work around lack of `noexcept` keyword support in VS2013
#if defined(_MSC_VER) && (_MSC_VER <= 1800) && !defined(_ALLOW_KEYWORD_MACROS)
#define _ALLOW_KEYWORD_MACROS 1
#define noexcept throw( )
#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,
// only used internaly
SASS_STYLE_INSPECT,
SASS_STYLE_TO_SASS,
SASS_STYLE_TO_CSS
};
// to allocate buffer to be filled
ADDAPI void* ADDCALL sass_alloc_memory(size_t size);
// to allocate a buffer from existing string
ADDAPI char* ADDCALL sass_copy_c_string(const char* str);
// to free overtaken memory when done
ADDAPI void ADDCALL sass_free_memory(void* ptr);
// 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);
// Implemented sass language version
// Hardcoded version 3.4 for time being
ADDAPI const char* ADDCALL libsass_version(void);
// Get compiled libsass language
ADDAPI const char* ADDCALL libsass_language_version(void);
#ifdef __cplusplus
} // __cplusplus defined.
#endif
#endif

View File

@@ -0,0 +1,174 @@
#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
// Useful 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);
ADDAPI void ADDCALL sass_delete_options(struct Sass_Options* options);
// 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_source_map_file_urls (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_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_source_map_file_urls (struct Sass_Options* options, bool source_map_file_urls);
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);
// Getters for options include path array
ADDAPI size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options);
ADDAPI const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i);
// Plugin paths to load dynamic libraries work the same
ADDAPI size_t ADDCALL sass_option_get_plugin_path_size(struct Sass_Options* options);
ADDAPI const char* ADDCALL sass_option_get_plugin_path(struct Sass_Options* options, size_t i);
// 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_error_src (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);
ADDAPI size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler);
ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler);
ADDAPI Sass_Callee_Entry ADDCALL sass_compiler_get_callee_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);
// Resolve a file via the given include paths in the sass option struct
// find_file looks for the exact file name while find_include does a regular sass include
ADDAPI char* ADDCALL sass_find_file (const char* path, struct Sass_Options* opt);
ADDAPI char* ADDCALL sass_find_include (const char* path, struct Sass_Options* opt);
// Resolve a file relative to last import or include paths in the sass option struct
// find_file looks for the exact file name while find_include does a regular sass include
ADDAPI char* ADDCALL sass_compiler_find_file (const char* path, struct Sass_Compiler* compiler);
ADDAPI char* ADDCALL sass_compiler_find_include (const char* path, struct Sass_Compiler* compiler);
#ifdef __cplusplus
} // __cplusplus defined.
#endif
#endif

View File

@@ -0,0 +1,139 @@
#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_Env;
struct Sass_Callee;
struct Sass_Import;
struct Sass_Options;
struct Sass_Compiler;
struct Sass_Importer;
struct Sass_Function;
// Typedef helpers for callee lists
typedef struct Sass_Env (*Sass_Env_Frame);
// Typedef helpers for callee lists
typedef struct Sass_Callee (*Sass_Callee_Entry);
// 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);
// Type of function calls
enum Sass_Callee_Type {
SASS_CALLEE_MIXIN,
SASS_CALLEE_FUNCTION,
SASS_CALLEE_C_FUNCTION,
};
// 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);
ADDAPI void ADDCALL sass_delete_importer_list (Sass_Importer_List list);
// 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 callee entry
ADDAPI const char* ADDCALL sass_callee_get_name (Sass_Callee_Entry);
ADDAPI const char* ADDCALL sass_callee_get_path (Sass_Callee_Entry);
ADDAPI size_t ADDCALL sass_callee_get_line (Sass_Callee_Entry);
ADDAPI size_t ADDCALL sass_callee_get_column (Sass_Callee_Entry);
ADDAPI enum Sass_Callee_Type ADDCALL sass_callee_get_type (Sass_Callee_Entry);
ADDAPI Sass_Env_Frame ADDCALL sass_callee_get_env (Sass_Callee_Entry);
// Getters and Setters for environments (lexical, local and global)
ADDAPI union Sass_Value* ADDCALL sass_env_get_lexical (Sass_Env_Frame, const char*);
ADDAPI void ADDCALL sass_env_set_lexical (Sass_Env_Frame, const char*, union Sass_Value*);
ADDAPI union Sass_Value* ADDCALL sass_env_get_local (Sass_Env_Frame, const char*);
ADDAPI void ADDCALL sass_env_set_local (Sass_Env_Frame, const char*, union Sass_Value*);
ADDAPI union Sass_Value* ADDCALL sass_env_get_global (Sass_Env_Frame, const char*);
ADDAPI void ADDCALL sass_env_set_global (Sass_Env_Frame, const char*, union Sass_Value*);
// 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);
ADDAPI void ADDCALL sass_delete_function (Sass_Function_Entry entry);
ADDAPI void ADDCALL sass_delete_function_list (Sass_Function_List list);
// 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

View File

@@ -0,0 +1,145 @@
#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,
// only used internally to represent a hash map before evaluation
// otherwise we would be too early to check for duplicate keys
SASS_HASH
};
// 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
};
// 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, bool is_bracketed);
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);
// 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);
// 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);
// 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);
ADDAPI bool ADDCALL sass_list_get_is_bracketed (const union Sass_Value* v);
ADDAPI void ADDCALL sass_list_set_is_bracketed (union Sass_Value* v, bool 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);
#ifdef __cplusplus
} // __cplusplus defined.
#endif
#endif

View File

@@ -0,0 +1,12 @@
#ifndef SASS_VERSION_H
#define SASS_VERSION_H
#ifndef LIBSASS_VERSION
#define LIBSASS_VERSION "[NA]"
#endif
#ifndef LIBSASS_LANGUAGE_VERSION
#define LIBSASS_LANGUAGE_VERSION "3.5"
#endif
#endif

View File

@@ -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.1.1"
#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

View File

@@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
//-----------------------------------------------------------------------------
// LibSass only needs MurmurHash2, so we made this header only
//-----------------------------------------------------------------------------
#ifndef _MURMURHASH2_H_
#define _MURMURHASH2_H_
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER) && (_MSC_VER < 1600)
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
// Other compilers
#else // defined(_MSC_VER)
#include <stdint.h>
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
inline uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = seed ^ len;
// Mix 4 bytes at a time into the hash
const unsigned char * data = (const unsigned char *)key;
while(len >= 4)
{
uint32_t k = *(uint32_t*)data;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch(len)
{
case 3:
h ^= data[2] << 16;
/* fall through */
case 2:
h ^= data[1] << 8;
/* fall through */
case 1:
h ^= data[0];
h *= m;
};
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
//-----------------------------------------------------------------------------
#endif // _MURMURHASH2_H_

View File

@@ -0,0 +1,953 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
static Null sass_null(SourceSpan("null"));
const char* sass_op_to_name(enum Sass_OP op) {
switch (op) {
case AND: return "and";
case OR: return "or";
case EQ: return "eq";
case NEQ: return "neq";
case GT: return "gt";
case GTE: return "gte";
case LT: return "lt";
case LTE: return "lte";
case ADD: return "plus";
case SUB: return "minus";
case MUL: return "times";
case DIV: return "div";
case MOD: return "mod";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
}
const char* sass_op_separator(enum Sass_OP op) {
switch (op) {
case AND: return "&&";
case OR: return "||";
case EQ: return "==";
case NEQ: return "!=";
case GT: return ">";
case GTE: return ">=";
case LT: return "<";
case LTE: return "<=";
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
case DIV: return "/";
case MOD: return "%";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void AST_Node::update_pstate(const SourceSpan& pstate)
{
pstate_.offset += pstate.position - pstate_.position + pstate.offset;
}
sass::string AST_Node::to_string(Sass_Inspect_Options opt) const
{
Sass_Output_Options out(opt);
Emitter emitter(out);
Inspect i(emitter);
i.in_declaration = true;
// ToDo: inspect should be const
const_cast<AST_Node*>(this)->perform(&i);
return i.get_buffer();
}
sass::string AST_Node::to_css(Sass_Inspect_Options opt) const
{
opt.output_style = TO_CSS;
Sass_Output_Options out(opt);
Emitter emitter(out);
Inspect i(emitter);
i.in_declaration = true;
// ToDo: inspect should be const
const_cast<AST_Node*>(this)->perform(&i);
return i.get_buffer();
}
sass::string AST_Node::to_string() const
{
return to_string({ NESTED, 5 });
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Statement::Statement(SourceSpan pstate, Type st, size_t t)
: AST_Node(pstate), statement_type_(st), tabs_(t), group_end_(false)
{ }
Statement::Statement(const Statement* ptr)
: AST_Node(ptr),
statement_type_(ptr->statement_type_),
tabs_(ptr->tabs_),
group_end_(ptr->group_end_)
{ }
bool Statement::bubbles()
{
return false;
}
bool Statement::has_content()
{
return statement_type_ == CONTENT;
}
bool Statement::is_invisible() const
{
return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Block::Block(SourceSpan pstate, size_t s, bool r)
: Statement(pstate),
Vectorized<Statement_Obj>(s),
is_root_(r)
{ }
Block::Block(const Block* ptr)
: Statement(ptr),
Vectorized<Statement_Obj>(*ptr),
is_root_(ptr->is_root_)
{ }
bool Block::isInvisible() const
{
for (auto& item : elements()) {
if (!item->is_invisible()) return false;
}
return true;
}
bool Block::has_content()
{
for (size_t i = 0, L = elements().size(); i < L; ++i) {
if (elements()[i]->has_content()) return true;
}
return Statement::has_content();
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
ParentStatement::ParentStatement(SourceSpan pstate, Block_Obj b)
: Statement(pstate), block_(b)
{ }
ParentStatement::ParentStatement(const ParentStatement* ptr)
: Statement(ptr), block_(ptr->block_)
{ }
bool ParentStatement::has_content()
{
return (block_ && block_->has_content()) || Statement::has_content();
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
StyleRule::StyleRule(SourceSpan pstate, SelectorListObj s, Block_Obj b)
: ParentStatement(pstate, b), selector_(s), schema_(), is_root_(false)
{ statement_type(RULESET); }
StyleRule::StyleRule(const StyleRule* ptr)
: ParentStatement(ptr),
selector_(ptr->selector_),
schema_(ptr->schema_),
is_root_(ptr->is_root_)
{ statement_type(RULESET); }
bool StyleRule::is_invisible() const {
if (const SelectorList * sl = Cast<SelectorList>(selector())) {
for (size_t i = 0, L = sl->length(); i < L; i += 1)
if (!(*sl)[i]->isInvisible()) return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Bubble::Bubble(SourceSpan pstate, Statement_Obj n, Statement_Obj g, size_t t)
: Statement(pstate, Statement::BUBBLE, t), node_(n), group_end_(g == nullptr)
{ }
Bubble::Bubble(const Bubble* ptr)
: Statement(ptr),
node_(ptr->node_),
group_end_(ptr->group_end_)
{ }
bool Bubble::bubbles()
{
return true;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Trace::Trace(SourceSpan pstate, sass::string n, Block_Obj b, char type)
: ParentStatement(pstate, b), type_(type), name_(n)
{ }
Trace::Trace(const Trace* ptr)
: ParentStatement(ptr),
type_(ptr->type_),
name_(ptr->name_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
AtRule::AtRule(SourceSpan pstate, sass::string kwd, SelectorListObj sel, Block_Obj b, ExpressionObj val)
: ParentStatement(pstate, b), keyword_(kwd), selector_(sel), value_(val) // set value manually if needed
{ statement_type(DIRECTIVE); }
AtRule::AtRule(const AtRule* ptr)
: ParentStatement(ptr),
keyword_(ptr->keyword_),
selector_(ptr->selector_),
value_(ptr->value_) // set value manually if needed
{ statement_type(DIRECTIVE); }
bool AtRule::bubbles() { return is_keyframes() || is_media(); }
bool AtRule::is_media() {
return keyword_.compare("@-webkit-media") == 0 ||
keyword_.compare("@-moz-media") == 0 ||
keyword_.compare("@-o-media") == 0 ||
keyword_.compare("@media") == 0;
}
bool AtRule::is_keyframes() {
return keyword_.compare("@-webkit-keyframes") == 0 ||
keyword_.compare("@-moz-keyframes") == 0 ||
keyword_.compare("@-o-keyframes") == 0 ||
keyword_.compare("@keyframes") == 0;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Keyframe_Rule::Keyframe_Rule(SourceSpan pstate, Block_Obj b)
: ParentStatement(pstate, b), name_()
{ statement_type(KEYFRAMERULE); }
Keyframe_Rule::Keyframe_Rule(const Keyframe_Rule* ptr)
: ParentStatement(ptr), name_(ptr->name_)
{ statement_type(KEYFRAMERULE); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Declaration::Declaration(SourceSpan pstate, String_Obj prop, ExpressionObj val, bool i, bool c, Block_Obj b)
: ParentStatement(pstate, b), property_(prop), value_(val), is_important_(i), is_custom_property_(c), is_indented_(false)
{ statement_type(DECLARATION); }
Declaration::Declaration(const Declaration* ptr)
: ParentStatement(ptr),
property_(ptr->property_),
value_(ptr->value_),
is_important_(ptr->is_important_),
is_custom_property_(ptr->is_custom_property_),
is_indented_(ptr->is_indented_)
{ statement_type(DECLARATION); }
bool Declaration::is_invisible() const
{
if (is_custom_property()) return false;
return !(value_ && !Cast<Null>(value_));
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Assignment::Assignment(SourceSpan pstate, sass::string var, ExpressionObj val, bool is_default, bool is_global)
: Statement(pstate), variable_(var), value_(val), is_default_(is_default), is_global_(is_global)
{ statement_type(ASSIGNMENT); }
Assignment::Assignment(const Assignment* ptr)
: Statement(ptr),
variable_(ptr->variable_),
value_(ptr->value_),
is_default_(ptr->is_default_),
is_global_(ptr->is_global_)
{ statement_type(ASSIGNMENT); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Import::Import(SourceSpan pstate)
: Statement(pstate),
urls_(sass::vector<ExpressionObj>()),
incs_(sass::vector<Include>()),
import_queries_()
{ statement_type(IMPORT); }
Import::Import(const Import* ptr)
: Statement(ptr),
urls_(ptr->urls_),
incs_(ptr->incs_),
import_queries_(ptr->import_queries_)
{ statement_type(IMPORT); }
sass::vector<Include>& Import::incs() { return incs_; }
sass::vector<ExpressionObj>& Import::urls() { return urls_; }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Import_Stub::Import_Stub(SourceSpan pstate, Include res)
: Statement(pstate), resource_(res)
{ statement_type(IMPORT_STUB); }
Import_Stub::Import_Stub(const Import_Stub* ptr)
: Statement(ptr), resource_(ptr->resource_)
{ statement_type(IMPORT_STUB); }
Include Import_Stub::resource() { return resource_; };
sass::string Import_Stub::imp_path() { return resource_.imp_path; };
sass::string Import_Stub::abs_path() { return resource_.abs_path; };
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
WarningRule::WarningRule(SourceSpan pstate, ExpressionObj msg)
: Statement(pstate), message_(msg)
{ statement_type(WARNING); }
WarningRule::WarningRule(const WarningRule* ptr)
: Statement(ptr), message_(ptr->message_)
{ statement_type(WARNING); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
ErrorRule::ErrorRule(SourceSpan pstate, ExpressionObj msg)
: Statement(pstate), message_(msg)
{ statement_type(ERROR); }
ErrorRule::ErrorRule(const ErrorRule* ptr)
: Statement(ptr), message_(ptr->message_)
{ statement_type(ERROR); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
DebugRule::DebugRule(SourceSpan pstate, ExpressionObj val)
: Statement(pstate), value_(val)
{ statement_type(DEBUGSTMT); }
DebugRule::DebugRule(const DebugRule* ptr)
: Statement(ptr), value_(ptr->value_)
{ statement_type(DEBUGSTMT); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Comment::Comment(SourceSpan pstate, String_Obj txt, bool is_important)
: Statement(pstate), text_(txt), is_important_(is_important)
{ statement_type(COMMENT); }
Comment::Comment(const Comment* ptr)
: Statement(ptr),
text_(ptr->text_),
is_important_(ptr->is_important_)
{ statement_type(COMMENT); }
bool Comment::is_invisible() const
{
return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
If::If(SourceSpan pstate, ExpressionObj pred, Block_Obj con, Block_Obj alt)
: ParentStatement(pstate, con), predicate_(pred), alternative_(alt)
{ statement_type(IF); }
If::If(const If* ptr)
: ParentStatement(ptr),
predicate_(ptr->predicate_),
alternative_(ptr->alternative_)
{ statement_type(IF); }
bool If::has_content()
{
return ParentStatement::has_content() || (alternative_ && alternative_->has_content());
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
ForRule::ForRule(SourceSpan pstate,
sass::string var, ExpressionObj lo, ExpressionObj hi, Block_Obj b, bool inc)
: ParentStatement(pstate, b),
variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc)
{ statement_type(FOR); }
ForRule::ForRule(const ForRule* ptr)
: ParentStatement(ptr),
variable_(ptr->variable_),
lower_bound_(ptr->lower_bound_),
upper_bound_(ptr->upper_bound_),
is_inclusive_(ptr->is_inclusive_)
{ statement_type(FOR); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
EachRule::EachRule(SourceSpan pstate, sass::vector<sass::string> vars, ExpressionObj lst, Block_Obj b)
: ParentStatement(pstate, b), variables_(vars), list_(lst)
{ statement_type(EACH); }
EachRule::EachRule(const EachRule* ptr)
: ParentStatement(ptr), variables_(ptr->variables_), list_(ptr->list_)
{ statement_type(EACH); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
WhileRule::WhileRule(SourceSpan pstate, ExpressionObj pred, Block_Obj b)
: ParentStatement(pstate, b), predicate_(pred)
{ statement_type(WHILE); }
WhileRule::WhileRule(const WhileRule* ptr)
: ParentStatement(ptr), predicate_(ptr->predicate_)
{ statement_type(WHILE); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Return::Return(SourceSpan pstate, ExpressionObj val)
: Statement(pstate), value_(val)
{ statement_type(RETURN); }
Return::Return(const Return* ptr)
: Statement(ptr), value_(ptr->value_)
{ statement_type(RETURN); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
ExtendRule::ExtendRule(SourceSpan pstate, SelectorListObj s)
: Statement(pstate), isOptional_(false), selector_(s), schema_()
{ statement_type(EXTEND); }
ExtendRule::ExtendRule(SourceSpan pstate, Selector_Schema_Obj s)
: Statement(pstate), isOptional_(false), selector_(), schema_(s)
{
statement_type(EXTEND);
}
ExtendRule::ExtendRule(const ExtendRule* ptr)
: Statement(ptr),
isOptional_(ptr->isOptional_),
selector_(ptr->selector_),
schema_(ptr->schema_)
{ statement_type(EXTEND); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Definition::Definition(const Definition* ptr)
: ParentStatement(ptr),
name_(ptr->name_),
parameters_(ptr->parameters_),
environment_(ptr->environment_),
type_(ptr->type_),
native_function_(ptr->native_function_),
c_function_(ptr->c_function_),
cookie_(ptr->cookie_),
is_overload_stub_(ptr->is_overload_stub_),
signature_(ptr->signature_)
{ }
Definition::Definition(SourceSpan pstate,
sass::string n,
Parameters_Obj params,
Block_Obj b,
Type t)
: ParentStatement(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::Definition(SourceSpan pstate,
Signature sig,
sass::string n,
Parameters_Obj params,
Native_Function func_ptr,
bool overload_stub)
: ParentStatement(pstate, {}),
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::Definition(SourceSpan pstate,
Signature sig,
sass::string n,
Parameters_Obj params,
Sass_Function_Entry c_func)
: ParentStatement(pstate, {}),
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)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Mixin_Call::Mixin_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Parameters_Obj b_params, Block_Obj b)
: ParentStatement(pstate, b), name_(n), arguments_(args), block_parameters_(b_params)
{ }
Mixin_Call::Mixin_Call(const Mixin_Call* ptr)
: ParentStatement(ptr),
name_(ptr->name_),
arguments_(ptr->arguments_),
block_parameters_(ptr->block_parameters_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Content::Content(SourceSpan pstate, Arguments_Obj args)
: Statement(pstate),
arguments_(args)
{ statement_type(CONTENT); }
Content::Content(const Content* ptr)
: Statement(ptr),
arguments_(ptr->arguments_)
{ statement_type(CONTENT); }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Expression::Expression(SourceSpan pstate, bool d, bool e, bool i, Type ct)
: AST_Node(pstate),
is_delayed_(d),
is_expanded_(e),
is_interpolant_(i),
concrete_type_(ct)
{ }
Expression::Expression(const Expression* ptr)
: AST_Node(ptr),
is_delayed_(ptr->is_delayed_),
is_expanded_(ptr->is_expanded_),
is_interpolant_(ptr->is_interpolant_),
concrete_type_(ptr->concrete_type_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Unary_Expression::Unary_Expression(SourceSpan pstate, Type t, ExpressionObj o)
: Expression(pstate), optype_(t), operand_(o), hash_(0)
{ }
Unary_Expression::Unary_Expression(const Unary_Expression* ptr)
: Expression(ptr),
optype_(ptr->optype_),
operand_(ptr->operand_),
hash_(ptr->hash_)
{ }
const sass::string Unary_Expression::type_name() {
switch (optype_) {
case PLUS: return "plus";
case MINUS: return "minus";
case SLASH: return "slash";
case NOT: return "not";
default: return "invalid";
}
}
bool Unary_Expression::operator==(const Expression& rhs) const
{
try
{
const Unary_Expression* m = Cast<Unary_Expression>(&rhs);
if (m == 0) return false;
return type() == m->type() &&
*operand() == *m->operand();
}
catch (std::bad_cast&)
{
return false;
}
catch (...) { throw; }
}
size_t Unary_Expression::hash() const
{
if (hash_ == 0) {
hash_ = std::hash<size_t>()(optype_);
hash_combine(hash_, operand()->hash());
};
return hash_;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Argument::Argument(SourceSpan pstate, ExpressionObj val, sass::string n, bool rest, bool keyword)
: Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)
{
if (!name_.empty() && is_rest_argument_) {
coreError("variable-length argument may not be passed by name", pstate_);
}
}
Argument::Argument(const Argument* ptr)
: Expression(ptr),
value_(ptr->value_),
name_(ptr->name_),
is_rest_argument_(ptr->is_rest_argument_),
is_keyword_argument_(ptr->is_keyword_argument_),
hash_(ptr->hash_)
{
if (!name_.empty() && is_rest_argument_) {
coreError("variable-length argument may not be passed by name", pstate_);
}
}
void Argument::set_delayed(bool delayed)
{
if (value_) value_->set_delayed(delayed);
is_delayed(delayed);
}
bool Argument::operator==(const Expression& rhs) const
{
try
{
const Argument* m = Cast<Argument>(&rhs);
if (!(m && name() == m->name())) return false;
return *value() == *m->value();
}
catch (std::bad_cast&)
{
return false;
}
catch (...) { throw; }
}
size_t Argument::hash() const
{
if (hash_ == 0) {
hash_ = std::hash<sass::string>()(name());
hash_combine(hash_, value()->hash());
}
return hash_;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Arguments::Arguments(SourceSpan pstate)
: Expression(pstate),
Vectorized<Argument_Obj>(),
has_named_arguments_(false),
has_rest_argument_(false),
has_keyword_argument_(false)
{ }
Arguments::Arguments(const Arguments* ptr)
: Expression(ptr),
Vectorized<Argument_Obj>(*ptr),
has_named_arguments_(ptr->has_named_arguments_),
has_rest_argument_(ptr->has_rest_argument_),
has_keyword_argument_(ptr->has_keyword_argument_)
{ }
void Arguments::set_delayed(bool delayed)
{
for (Argument_Obj arg : elements()) {
if (arg) arg->set_delayed(delayed);
}
is_delayed(delayed);
}
Argument_Obj Arguments::get_rest_argument()
{
if (this->has_rest_argument()) {
for (Argument_Obj arg : this->elements()) {
if (arg->is_rest_argument()) {
return arg;
}
}
}
return {};
}
Argument_Obj Arguments::get_keyword_argument()
{
if (this->has_keyword_argument()) {
for (Argument_Obj arg : this->elements()) {
if (arg->is_keyword_argument()) {
return arg;
}
}
}
return {};
}
void Arguments::adjust_after_pushing(Argument_Obj a)
{
if (!a->name().empty()) {
if (has_keyword_argument()) {
coreError("named arguments must precede variable-length argument", a->pstate());
}
has_named_arguments(true);
}
else if (a->is_rest_argument()) {
if (has_rest_argument()) {
coreError("functions and mixins may only be called with one variable-length argument", a->pstate());
}
if (has_keyword_argument_) {
coreError("only keyword arguments may follow variable arguments", a->pstate());
}
has_rest_argument(true);
}
else if (a->is_keyword_argument()) {
if (has_keyword_argument()) {
coreError("functions and mixins may only be called with one keyword argument", a->pstate());
}
has_keyword_argument(true);
}
else {
if (has_rest_argument()) {
coreError("ordinal arguments must precede variable-length arguments", a->pstate());
}
if (has_named_arguments()) {
coreError("ordinal arguments must precede named arguments", a->pstate());
}
}
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Media_Query::Media_Query(SourceSpan pstate, String_Obj t, size_t s, bool n, bool r)
: Expression(pstate), Vectorized<Media_Query_ExpressionObj>(s),
media_type_(t), is_negated_(n), is_restricted_(r)
{ }
Media_Query::Media_Query(const Media_Query* ptr)
: Expression(ptr),
Vectorized<Media_Query_ExpressionObj>(*ptr),
media_type_(ptr->media_type_),
is_negated_(ptr->is_negated_),
is_restricted_(ptr->is_restricted_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Media_Query_Expression::Media_Query_Expression(SourceSpan pstate,
ExpressionObj f, ExpressionObj v, bool i)
: Expression(pstate), feature_(f), value_(v), is_interpolated_(i)
{ }
Media_Query_Expression::Media_Query_Expression(const Media_Query_Expression* ptr)
: Expression(ptr),
feature_(ptr->feature_),
value_(ptr->value_),
is_interpolated_(ptr->is_interpolated_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
At_Root_Query::At_Root_Query(SourceSpan pstate, ExpressionObj f, ExpressionObj v, bool i)
: Expression(pstate), feature_(f), value_(v)
{ }
At_Root_Query::At_Root_Query(const At_Root_Query* ptr)
: Expression(ptr),
feature_(ptr->feature_),
value_(ptr->value_)
{ }
bool At_Root_Query::exclude(sass::string str)
{
bool with = feature() && unquote(feature()->to_string()).compare("with") == 0;
List* l = static_cast<List*>(value().ptr());
sass::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]->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]->to_string());
if (v.compare("all") == 0 || v == str) return true;
}
return false;
}
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
AtRootRule::AtRootRule(SourceSpan pstate, Block_Obj b, At_Root_Query_Obj e)
: ParentStatement(pstate, b), expression_(e)
{ statement_type(ATROOT); }
AtRootRule::AtRootRule(const AtRootRule* ptr)
: ParentStatement(ptr), expression_(ptr->expression_)
{ statement_type(ATROOT); }
bool AtRootRule::bubbles() {
return true;
}
bool AtRootRule::exclude_node(Statement_Obj s) {
if (expression() == nullptr)
{
return s->statement_type() == Statement::RULESET;
}
if (s->statement_type() == Statement::DIRECTIVE)
{
if (AtRuleObj dir = Cast<AtRule>(s))
{
sass::string keyword(dir->keyword());
if (keyword.length() > 0) keyword.erase(0, 1);
return expression()->exclude(keyword);
}
}
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 (AtRuleObj dir = Cast<AtRule>(s))
{
if (dir->is_keyframes()) return expression()->exclude("keyframes");
}
return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Parameter::Parameter(SourceSpan pstate, sass::string n, ExpressionObj def, bool rest)
: AST_Node(pstate), name_(n), default_value_(def), is_rest_parameter_(rest)
{ }
Parameter::Parameter(const Parameter* ptr)
: AST_Node(ptr),
name_(ptr->name_),
default_value_(ptr->default_value_),
is_rest_parameter_(ptr->is_rest_parameter_)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Parameters::Parameters(SourceSpan pstate)
: AST_Node(pstate),
Vectorized<Parameter_Obj>(),
has_optional_parameters_(false),
has_rest_parameter_(false)
{ }
Parameters::Parameters(const Parameters* ptr)
: AST_Node(ptr),
Vectorized<Parameter_Obj>(*ptr),
has_optional_parameters_(ptr->has_optional_parameters_),
has_rest_parameter_(ptr->has_rest_parameter_)
{ }
void Parameters::adjust_after_pushing(Parameter_Obj p)
{
if (p->default_value()) {
if (has_rest_parameter()) {
coreError("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()) {
coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate());
}
has_rest_parameter(true);
}
else {
if (has_rest_parameter()) {
coreError("required parameters must precede variable-length parameters", p->pstate());
}
if (has_optional_parameters()) {
coreError("required parameters must precede optional parameters", p->pstate());
}
}
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// If you forget to add a class here you will get
// undefined reference to `vtable for Sass::Class'
IMPLEMENT_AST_OPERATORS(StyleRule);
IMPLEMENT_AST_OPERATORS(MediaRule);
IMPLEMENT_AST_OPERATORS(CssMediaRule);
IMPLEMENT_AST_OPERATORS(CssMediaQuery);
IMPLEMENT_AST_OPERATORS(Import);
IMPLEMENT_AST_OPERATORS(Import_Stub);
IMPLEMENT_AST_OPERATORS(AtRule);
IMPLEMENT_AST_OPERATORS(AtRootRule);
IMPLEMENT_AST_OPERATORS(WhileRule);
IMPLEMENT_AST_OPERATORS(EachRule);
IMPLEMENT_AST_OPERATORS(ForRule);
IMPLEMENT_AST_OPERATORS(If);
IMPLEMENT_AST_OPERATORS(Mixin_Call);
IMPLEMENT_AST_OPERATORS(ExtendRule);
IMPLEMENT_AST_OPERATORS(Media_Query);
IMPLEMENT_AST_OPERATORS(Media_Query_Expression);
IMPLEMENT_AST_OPERATORS(DebugRule);
IMPLEMENT_AST_OPERATORS(ErrorRule);
IMPLEMENT_AST_OPERATORS(WarningRule);
IMPLEMENT_AST_OPERATORS(Assignment);
IMPLEMENT_AST_OPERATORS(Return);
IMPLEMENT_AST_OPERATORS(At_Root_Query);
IMPLEMENT_AST_OPERATORS(Comment);
IMPLEMENT_AST_OPERATORS(Parameters);
IMPLEMENT_AST_OPERATORS(Parameter);
IMPLEMENT_AST_OPERATORS(Arguments);
IMPLEMENT_AST_OPERATORS(Argument);
IMPLEMENT_AST_OPERATORS(Unary_Expression);
IMPLEMENT_AST_OPERATORS(Block);
IMPLEMENT_AST_OPERATORS(Content);
IMPLEMENT_AST_OPERATORS(Trace);
IMPLEMENT_AST_OPERATORS(Keyframe_Rule);
IMPLEMENT_AST_OPERATORS(Bubble);
IMPLEMENT_AST_OPERATORS(Definition);
IMPLEMENT_AST_OPERATORS(Declaration);
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast2c.hpp"
#include "ast.hpp"
namespace Sass {
union Sass_Value* AST2C::operator()(Boolean* b)
{ return sass_make_boolean(b->value()); }
union Sass_Value* AST2C::operator()(Number* n)
{ return sass_make_number(n->value(), n->unit().c_str()); }
union Sass_Value* AST2C::operator()(Custom_Warning* w)
{ return sass_make_warning(w->message().c_str()); }
union Sass_Value* AST2C::operator()(Custom_Error* e)
{ return sass_make_error(e->message().c_str()); }
union Sass_Value* AST2C::operator()(Color_RGBA* c)
{ return sass_make_color(c->r(), c->g(), c->b(), c->a()); }
union Sass_Value* AST2C::operator()(Color_HSLA* c)
{
Color_RGBA_Obj rgba = c->copyAsRGBA();
return operator()(rgba.ptr());
}
union Sass_Value* AST2C::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* AST2C::operator()(String_Quoted* s)
{ return sass_make_qstring(s->value().c_str()); }
union Sass_Value* AST2C::operator()(List* l)
{
union Sass_Value* v = sass_make_list(l->length(), l->separator(), l->is_bracketed());
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* AST2C::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* AST2C::operator()(Arguments* a)
{
union Sass_Value* v = sass_make_list(a->length(), SASS_COMMA, false);
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* AST2C::operator()(Argument* a)
{ return a->value()->perform(this); }
// not strictly necessary because of the fallback
union Sass_Value* AST2C::operator()(Null* n)
{ return sass_make_null(); }
};

View File

@@ -0,0 +1,39 @@
#ifndef SASS_AST2C_H
#define SASS_AST2C_H
#include "ast_fwd_decl.hpp"
#include "operation.hpp"
#include "sass/values.h"
namespace Sass {
class AST2C : public Operation_CRTP<union Sass_Value*, AST2C> {
public:
AST2C() { }
~AST2C() { }
union Sass_Value* operator()(Boolean*);
union Sass_Value* operator()(Number*);
union Sass_Value* operator()(Color_RGBA*);
union Sass_Value* operator()(Color_HSLA*);
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*);
// return sass error if type is not supported
union Sass_Value* fallback(AST_Node* x)
{ return sass_make_error("unknown type for C-API"); }
};
}
#endif

View File

@@ -0,0 +1,140 @@
#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;
}
void reset()
{
*(this->var) = this->orig;
}
~LocalOption() {
*(this->var) = this->orig;
}
};
#define LOCAL_FLAG(name,opt) LocalOption<bool> flag_##name(name, opt)
#define LOCAL_COUNT(name,opt) LocalOption<size_t> cnt_##name(name, opt)
#define NESTING_GUARD(name) \
LocalOption<size_t> cnt_##name(name, name + 1); \
if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \
#define ADD_PROPERTY(type, name)\
protected:\
type name##_;\
public:\
type name() const { return name##_; }\
type name(type name##__) { return name##_ = name##__; }\
private:
#define HASH_PROPERTY(type, name)\
protected:\
type name##_;\
public:\
type name() const { return name##_; }\
type name(type name##__) { hash_ = 0; return name##_ = name##__; }\
private:
#define ADD_CONSTREF(type, name) \
protected: \
type name##_; \
public: \
const type& name() const { return name##_; } \
void name(type name##__) { name##_ = name##__; } \
private:
#define HASH_CONSTREF(type, name) \
protected: \
type name##_; \
public: \
const type& name() const { return name##_; } \
void name(type name##__) { hash_ = 0; name##_ = name##__; } \
private:
#ifdef DEBUG_SHARED_PTR
#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \
virtual klass* copy(sass::string, size_t) const = 0; \
virtual klass* clone(sass::string, size_t) const = 0; \
#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass* copy(sass::string, size_t) const override = 0; \
virtual klass* clone(sass::string, size_t) const override = 0; \
#define ATTACH_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass* copy(sass::string, size_t) const override; \
virtual klass* clone(sass::string, size_t) const override; \
#else
#define ATTACH_ABSTRACT_AST_OPERATIONS(klass) \
virtual klass* copy() const = 0; \
virtual klass* clone() const = 0; \
#define ATTACH_VIRTUAL_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass* copy() const override = 0; \
virtual klass* clone() const override = 0; \
#define ATTACH_AST_OPERATIONS(klass) \
klass(const klass* ptr); \
virtual klass* copy() const override; \
virtual klass* clone() const override; \
#endif
#define ATTACH_VIRTUAL_CMP_OPERATIONS(klass) \
virtual bool operator==(const klass& rhs) const = 0; \
virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \
#define ATTACH_CMP_OPERATIONS(klass) \
virtual bool operator==(const klass& rhs) const; \
virtual bool operator!=(const klass& rhs) const { return !(*this == rhs); }; \
#ifdef DEBUG_SHARED_PTR
#define IMPLEMENT_AST_OPERATORS(klass) \
klass* klass::copy(sass::string file, size_t line) const { \
klass* cpy = SASS_MEMORY_NEW(klass, this); \
cpy->trace(file, line); \
return cpy; \
} \
klass* klass::clone(sass::string file, size_t line) const { \
klass* cpy = copy(file, line); \
cpy->cloneChildren(); \
return cpy; \
} \
#else
#define IMPLEMENT_AST_OPERATORS(klass) \
klass* klass::copy() const { \
return SASS_MEMORY_NEW(klass, this); \
} \
klass* klass::clone() const { \
klass* cpy = copy(); \
cpy->cloneChildren(); \
return cpy; \
} \
#endif
#endif

View File

@@ -0,0 +1,31 @@
#include "ast.hpp"
namespace Sass {
#define IMPLEMENT_BASE_CAST(T) \
template<> \
T* Cast(AST_Node* ptr) { \
return dynamic_cast<T*>(ptr); \
}; \
\
template<> \
const T* Cast(const AST_Node* ptr) { \
return dynamic_cast<const T*>(ptr); \
}; \
IMPLEMENT_BASE_CAST(AST_Node)
IMPLEMENT_BASE_CAST(Expression)
IMPLEMENT_BASE_CAST(Statement)
IMPLEMENT_BASE_CAST(ParentStatement)
IMPLEMENT_BASE_CAST(PreValue)
IMPLEMENT_BASE_CAST(Value)
IMPLEMENT_BASE_CAST(Color)
IMPLEMENT_BASE_CAST(List)
IMPLEMENT_BASE_CAST(String)
IMPLEMENT_BASE_CAST(String_Constant)
IMPLEMENT_BASE_CAST(SupportsCondition)
IMPLEMENT_BASE_CAST(Selector)
IMPLEMENT_BASE_CAST(SelectorComponent)
IMPLEMENT_BASE_CAST(SimpleSelector)
}

View File

@@ -0,0 +1,274 @@
#ifndef SASS_AST_FWD_DECL_H
#define SASS_AST_FWD_DECL_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "memory.hpp"
#include "sass/functions.h"
/////////////////////////////////////////////
// Forward declarations for the AST visitors.
/////////////////////////////////////////////
namespace Sass {
class SourceData;
class SourceFile;
class SynthFile;
class ItplFile;
class AST_Node;
class ParentStatement;
class SimpleSelector;
class Parent_Reference;
class PreValue;
class Block;
class Expression;
class Statement;
class Value;
class Declaration;
class StyleRule;
class Bubble;
class Trace;
class MediaRule;
class CssMediaRule;
class CssMediaQuery;
class SupportsRule;
class AtRule;
class Keyframe_Rule;
class AtRootRule;
class Assignment;
class Import;
class Import_Stub;
class WarningRule;
class ErrorRule;
class DebugRule;
class Comment;
class If;
class ForRule;
class EachRule;
class WhileRule;
class Return;
class Content;
class ExtendRule;
class Definition;
class List;
class Map;
class Function;
class Mixin_Call;
class Binary_Expression;
class Unary_Expression;
class Function_Call;
class Custom_Warning;
class Custom_Error;
class Variable;
class Number;
class Color;
class Color_RGBA;
class Color_HSLA;
class Boolean;
class String;
class Null;
class String_Schema;
class String_Constant;
class String_Quoted;
class Media_Query;
class Media_Query_Expression;
class SupportsCondition;
class SupportsOperation;
class SupportsNegation;
class SupportsDeclaration;
class Supports_Interpolation;
class At_Root_Query;
class Parameter;
class Parameters;
class Argument;
class Arguments;
class Selector;
class Selector_Schema;
class PlaceholderSelector;
class TypeSelector;
class ClassSelector;
class IDSelector;
class AttributeSelector;
class PseudoSelector;
class SelectorComponent;
class SelectorCombinator;
class CompoundSelector;
class ComplexSelector;
class SelectorList;
// common classes
class Context;
class Expand;
class Eval;
class Extension;
// declare classes that are instances of memory nodes
// Note: also add a mapping without underscore
// ToDo: move to camelCase vars in the future
#define IMPL_MEM_OBJ(type) \
typedef SharedImpl<type> type##Obj; \
typedef SharedImpl<type> type##_Obj; \
IMPL_MEM_OBJ(SourceData);
IMPL_MEM_OBJ(SourceFile);
IMPL_MEM_OBJ(SynthFile);
IMPL_MEM_OBJ(ItplFile);
IMPL_MEM_OBJ(AST_Node);
IMPL_MEM_OBJ(Statement);
IMPL_MEM_OBJ(Block);
IMPL_MEM_OBJ(StyleRule);
IMPL_MEM_OBJ(Bubble);
IMPL_MEM_OBJ(Trace);
IMPL_MEM_OBJ(MediaRule);
IMPL_MEM_OBJ(CssMediaRule);
IMPL_MEM_OBJ(CssMediaQuery);
IMPL_MEM_OBJ(SupportsRule);
IMPL_MEM_OBJ(AtRule);
IMPL_MEM_OBJ(Keyframe_Rule);
IMPL_MEM_OBJ(AtRootRule);
IMPL_MEM_OBJ(Declaration);
IMPL_MEM_OBJ(Assignment);
IMPL_MEM_OBJ(Import);
IMPL_MEM_OBJ(Import_Stub);
IMPL_MEM_OBJ(WarningRule);
IMPL_MEM_OBJ(ErrorRule);
IMPL_MEM_OBJ(DebugRule);
IMPL_MEM_OBJ(Comment);
IMPL_MEM_OBJ(PreValue);
IMPL_MEM_OBJ(ParentStatement);
IMPL_MEM_OBJ(If);
IMPL_MEM_OBJ(ForRule);
IMPL_MEM_OBJ(EachRule);
IMPL_MEM_OBJ(WhileRule);
IMPL_MEM_OBJ(Return);
IMPL_MEM_OBJ(Content);
IMPL_MEM_OBJ(ExtendRule);
IMPL_MEM_OBJ(Definition);
IMPL_MEM_OBJ(Mixin_Call);
IMPL_MEM_OBJ(Value);
IMPL_MEM_OBJ(Expression);
IMPL_MEM_OBJ(List);
IMPL_MEM_OBJ(Map);
IMPL_MEM_OBJ(Function);
IMPL_MEM_OBJ(Binary_Expression);
IMPL_MEM_OBJ(Unary_Expression);
IMPL_MEM_OBJ(Function_Call);
IMPL_MEM_OBJ(Custom_Warning);
IMPL_MEM_OBJ(Custom_Error);
IMPL_MEM_OBJ(Variable);
IMPL_MEM_OBJ(Number);
IMPL_MEM_OBJ(Color);
IMPL_MEM_OBJ(Color_RGBA);
IMPL_MEM_OBJ(Color_HSLA);
IMPL_MEM_OBJ(Boolean);
IMPL_MEM_OBJ(String_Schema);
IMPL_MEM_OBJ(String);
IMPL_MEM_OBJ(String_Constant);
IMPL_MEM_OBJ(String_Quoted);
IMPL_MEM_OBJ(Media_Query);
IMPL_MEM_OBJ(Media_Query_Expression);
IMPL_MEM_OBJ(SupportsCondition);
IMPL_MEM_OBJ(SupportsOperation);
IMPL_MEM_OBJ(SupportsNegation);
IMPL_MEM_OBJ(SupportsDeclaration);
IMPL_MEM_OBJ(Supports_Interpolation);
IMPL_MEM_OBJ(At_Root_Query);
IMPL_MEM_OBJ(Null);
IMPL_MEM_OBJ(Parent_Reference);
IMPL_MEM_OBJ(Parameter);
IMPL_MEM_OBJ(Parameters);
IMPL_MEM_OBJ(Argument);
IMPL_MEM_OBJ(Arguments);
IMPL_MEM_OBJ(Selector);
IMPL_MEM_OBJ(Selector_Schema);
IMPL_MEM_OBJ(SimpleSelector);
IMPL_MEM_OBJ(PlaceholderSelector);
IMPL_MEM_OBJ(TypeSelector);
IMPL_MEM_OBJ(ClassSelector);
IMPL_MEM_OBJ(IDSelector);
IMPL_MEM_OBJ(AttributeSelector);
IMPL_MEM_OBJ(PseudoSelector);
IMPL_MEM_OBJ(SelectorComponent);
IMPL_MEM_OBJ(SelectorCombinator);
IMPL_MEM_OBJ(CompoundSelector);
IMPL_MEM_OBJ(ComplexSelector);
IMPL_MEM_OBJ(SelectorList);
// ###########################################################################
// some often used typedefs
// ###########################################################################
typedef sass::vector<Block*> BlockStack;
typedef sass::vector<Sass_Callee> CalleeStack;
typedef sass::vector<AST_Node_Obj> CallStack;
typedef sass::vector<CssMediaRuleObj> MediaStack;
typedef sass::vector<SelectorListObj> SelectorStack;
typedef sass::vector<Sass_Import_Entry> ImporterStack;
// only to switch implementations for testing
#define environment_map std::map
// ###########################################################################
// explicit type conversion functions
// ###########################################################################
template<class T>
T* Cast(AST_Node* ptr);
template<class T>
const T* Cast(const AST_Node* ptr);
// sometimes you know the class you want to cast to is final
// in this case a simple typeid check is faster and safe to use
#define DECLARE_BASE_CAST(T) \
template<> T* Cast(AST_Node* ptr); \
template<> const T* Cast(const AST_Node* ptr); \
// ###########################################################################
// implement specialization for final classes
// ###########################################################################
DECLARE_BASE_CAST(AST_Node)
DECLARE_BASE_CAST(Expression)
DECLARE_BASE_CAST(Statement)
DECLARE_BASE_CAST(ParentStatement)
DECLARE_BASE_CAST(PreValue)
DECLARE_BASE_CAST(Value)
DECLARE_BASE_CAST(List)
DECLARE_BASE_CAST(Color)
DECLARE_BASE_CAST(String)
DECLARE_BASE_CAST(String_Constant)
DECLARE_BASE_CAST(SupportsCondition)
DECLARE_BASE_CAST(Selector)
DECLARE_BASE_CAST(SimpleSelector)
DECLARE_BASE_CAST(SelectorComponent)
}
#endif

View File

@@ -0,0 +1,292 @@
#ifndef SASS_AST_HELPERS_H
#define SASS_AST_HELPERS_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <algorithm>
#include <functional>
#include "util_string.hpp"
namespace Sass {
// ###########################################################################
// ###########################################################################
// easier to search with name
const bool DELAYED = true;
// ToDo: should this really be hardcoded
// Note: most methods follow precision option
const double NUMBER_EPSILON = 1e-12;
// macro to test if numbers are equal within a small error margin
#define NEAR_EQUAL(lhs, rhs) std::fabs(lhs - rhs) < NUMBER_EPSILON
// ###########################################################################
// We define various functions and functors here.
// Functions satisfy the BinaryPredicate requirement
// Functors are structs used for e.g. unordered_map
// ###########################################################################
// ###########################################################################
// Implement compare and hashing operations for raw pointers
// ###########################################################################
template <class T>
size_t PtrHashFn(const T* ptr) {
return std::hash<std::size_t>()((size_t)ptr);
}
struct PtrHash {
template <class T>
size_t operator() (const T* ptr) const {
return PtrHashFn(ptr);
}
};
template <class T>
bool PtrEqualityFn(const T* lhs, const T* rhs) {
return lhs == rhs; // compare raw pointers
}
struct PtrEquality {
template <class T>
bool operator() (const T* lhs, const T* rhs) const {
return PtrEqualityFn<T>(lhs, rhs);
}
};
// ###########################################################################
// Implement compare and hashing operations for AST Nodes
// ###########################################################################
// TODO: get rid of funtions and use ObjEquality<T>
template <class T>
// Hash the raw pointer instead of object
size_t ObjPtrHashFn(const T& obj) {
return PtrHashFn(obj.ptr());
}
struct ObjPtrHash {
template <class T>
// Hash the raw pointer instead of object
size_t operator() (const T& obj) const {
return ObjPtrHashFn(obj);
}
};
template <class T>
// Hash the object and its content
size_t ObjHashFn(const T& obj) {
return obj ? obj->hash() : 0;
}
struct ObjHash {
template <class T>
// Hash the object and its content
size_t operator() (const T& obj) const {
return ObjHashFn(obj);
}
};
template <class T>
// Hash the object behind pointer
size_t PtrObjHashFn(const T* obj) {
return obj ? obj->hash() : 0;
}
struct PtrObjHash {
template <class T>
// Hash the object behind pointer
size_t operator() (const T* obj) const {
return PtrObjHashFn(obj);
}
};
template <class T>
// Compare raw pointers to the object
bool ObjPtrEqualityFn(const T& lhs, const T& rhs) {
return PtrEqualityFn(lhs.ptr(), rhs.ptr());
}
struct ObjPtrEquality {
template <class T>
// Compare raw pointers to the object
bool operator() (const T& lhs, const T& rhs) const {
return ObjPtrEqualityFn<T>(lhs, rhs);
}
};
template <class T>
// Compare the objects behind the pointers
bool PtrObjEqualityFn(const T* lhs, const T* rhs) {
if (lhs == nullptr) return rhs == nullptr;
else if (rhs == nullptr) return false;
else return *lhs == *rhs;
}
struct PtrObjEquality {
template <class T>
// Compare the objects behind the pointers
bool operator() (const T* lhs, const T* rhs) const {
return PtrObjEqualityFn<T>(lhs, rhs);
}
};
template <class T>
// Compare the objects and its contents
bool ObjEqualityFn(const T& lhs, const T& rhs) {
return PtrObjEqualityFn(lhs.ptr(), rhs.ptr());
}
struct ObjEquality {
template <class T>
// Compare the objects and its contents
bool operator() (const T& lhs, const T& rhs) const {
return ObjEqualityFn<T>(lhs, rhs);
}
};
// ###########################################################################
// Implement ordering operations for AST Nodes
// ###########################################################################
template <class T>
// Compare the objects behind pointers
bool PtrObjLessThanFn(const T* lhs, const T* rhs) {
if (lhs == nullptr) return rhs != nullptr;
else if (rhs == nullptr) return false;
else return *lhs < *rhs;
}
struct PtrObjLessThan {
template <class T>
// Compare the objects behind pointers
bool operator() (const T* lhs, const T* rhs) const {
return PtrObjLessThanFn<T>(lhs, rhs);
}
};
template <class T>
// Compare the objects and its content
bool ObjLessThanFn(const T& lhs, const T& rhs) {
return PtrObjLessThanFn(lhs.ptr(), rhs.ptr());
};
struct ObjLessThan {
template <class T>
// Compare the objects and its content
bool operator() (const T& lhs, const T& rhs) const {
return ObjLessThanFn<T>(lhs, rhs);
}
};
// ###########################################################################
// Some STL helper functions
// ###########################################################################
// Check if all elements are equal
template <class X, class Y,
typename XT = typename X::value_type,
typename YT = typename Y::value_type>
bool ListEquality(const X& lhs, const Y& rhs,
bool(*cmp)(const XT*, const YT*))
{
return lhs.size() == rhs.size() &&
std::equal(lhs.begin(), lhs.end(),
rhs.begin(), cmp);
}
// Return if Vector is empty
template <class T>
bool listIsEmpty(T* cnt) {
return cnt && cnt->empty();
}
// Erase items from vector that match predicate
template<class T, class UnaryPredicate>
void listEraseItemIf(T& vec, UnaryPredicate* predicate)
{
vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end());
}
// Check that every item in `lhs` is also in `rhs`
// Note: this works by comparing the raw pointers
template <typename T>
bool listIsSubsetOrEqual(const T& lhs, const T& rhs) {
for (const auto& item : lhs) {
if (std::find(rhs.begin(), rhs.end(), item) == rhs.end())
return false;
}
return true;
}
// ##########################################################################
// Returns whether [name] is the name of a pseudo-element
// that can be written with pseudo-class syntax (CSS2 vs CSS3):
// `:before`, `:after`, `:first-line`, or `:first-letter`
// ##########################################################################
inline bool isFakePseudoElement(const sass::string& name)
{
return Util::equalsLiteral("after", name)
|| Util::equalsLiteral("before", name)
|| Util::equalsLiteral("first-line", name)
|| Util::equalsLiteral("first-letter", name);
}
// ##########################################################################
// Names of pseudo selectors that take selectors as arguments,
// and that are subselectors of their arguments.
// For example, `.foo` is a superselector of `:matches(.foo)`.
// ##########################################################################
inline bool isSubselectorPseudo(const sass::string& norm)
{
return Util::equalsLiteral("any", norm)
|| Util::equalsLiteral("matches", norm)
|| Util::equalsLiteral("nth-child", norm)
|| Util::equalsLiteral("nth-last-child", norm);
}
// EO isSubselectorPseudo
// ###########################################################################
// Pseudo-class selectors that take unadorned selectors as arguments.
// ###########################################################################
inline bool isSelectorPseudoClass(const sass::string& test)
{
return Util::equalsLiteral("not", test)
|| Util::equalsLiteral("matches", test)
|| Util::equalsLiteral("current", test)
|| Util::equalsLiteral("any", test)
|| Util::equalsLiteral("has", test)
|| Util::equalsLiteral("host", test)
|| Util::equalsLiteral("host-context", test);
}
// EO isSelectorPseudoClass
// ###########################################################################
// Pseudo-element selectors that take unadorned selectors as arguments.
// ###########################################################################
inline bool isSelectorPseudoElement(const sass::string& test)
{
return Util::equalsLiteral("slotted", test);
}
// EO isSelectorPseudoElement
// ###########################################################################
// Pseudo-element selectors that has binominals
// ###########################################################################
inline bool isSelectorPseudoBinominal(const sass::string& test)
{
return Util::equalsLiteral("nth-child", test)
|| Util::equalsLiteral("nth-last-child", test);
}
// isSelectorPseudoBinominal
// ###########################################################################
// ###########################################################################
}
#endif

View File

@@ -0,0 +1,396 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast_selectors.hpp"
namespace Sass {
/*#########################################################################*/
// Compare against base class on right hand side
// try to find the most specialized implementation
/*#########################################################################*/
// Selector lists can be compared to comma lists
bool SelectorList::operator== (const Expression& rhs) const
{
if (auto l = Cast<List>(&rhs)) { return *this == *l; }
if (auto s = Cast<Selector>(&rhs)) { return *this == *s; }
if (Cast<String>(&rhs) || Cast<Null>(&rhs)) { return false; }
throw std::runtime_error("invalid selector base classes to compare");
}
// Selector lists can be compared to comma lists
bool SelectorList::operator== (const Selector& rhs) const
{
if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }
if (auto list = Cast<List>(&rhs)) { return *this == *list; }
throw std::runtime_error("invalid selector base classes to compare");
}
bool ComplexSelector::operator== (const Selector& rhs) const
{
if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<ComplexSelector>(&rhs)) { return *sel == *this; }
if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }
throw std::runtime_error("invalid selector base classes to compare");
}
bool SelectorCombinator::operator== (const Selector& rhs) const
{
if (auto cpx = Cast<SelectorCombinator>(&rhs)) { return *this == *cpx; }
return false;
}
bool CompoundSelector::operator== (const Selector& rhs) const
{
if (auto sel = Cast<SimpleSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }
throw std::runtime_error("invalid selector base classes to compare");
}
bool SimpleSelector::operator== (const Selector& rhs) const
{
if (auto sel = Cast<SelectorList>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<ComplexSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<CompoundSelector>(&rhs)) { return *this == *sel; }
if (auto sel = Cast<SimpleSelector>(&rhs)) return *this == *sel;
throw std::runtime_error("invalid selector base classes to compare");
}
/*#########################################################################*/
/*#########################################################################*/
bool SelectorList::operator== (const SelectorList& rhs) const
{
if (&rhs == this) return true;
if (rhs.length() != length()) return false;
std::unordered_set<const ComplexSelector*, PtrObjHash, PtrObjEquality> lhs_set;
lhs_set.reserve(length());
for (const ComplexSelectorObj& element : elements()) {
lhs_set.insert(element.ptr());
}
for (const ComplexSelectorObj& element : rhs.elements()) {
if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;
}
return true;
}
/*#########################################################################*/
// Compare SelectorList against all other selector types
/*#########################################################################*/
bool SelectorList::operator== (const ComplexSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (length() != 1) return false;
// Compare simple selectors
return *get(0) == rhs;
}
bool SelectorList::operator== (const CompoundSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (length() != 1) return false;
// Compare simple selectors
return *get(0) == rhs;
}
bool SelectorList::operator== (const SimpleSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (length() != 1) return false;
// Compare simple selectors
return *get(0) == rhs;
}
/*#########################################################################*/
// Compare ComplexSelector against itself
/*#########################################################################*/
bool ComplexSelector::operator== (const ComplexSelector& rhs) const
{
size_t len = length();
size_t rlen = rhs.length();
if (len != rlen) return false;
for (size_t i = 0; i < len; i += 1) {
if (*get(i) != *rhs.get(i)) return false;
}
return true;
}
/*#########################################################################*/
// Compare ComplexSelector against all other selector types
/*#########################################################################*/
bool ComplexSelector::operator== (const SelectorList& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare complex selector
return *this == *rhs.get(0);
}
bool ComplexSelector::operator== (const CompoundSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (length() != 1) return false;
// Compare compound selector
return *get(0) == rhs;
}
bool ComplexSelector::operator== (const SimpleSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (length() != 1) return false;
// Compare simple selectors
return *get(0) == rhs;
}
/*#########################################################################*/
// Compare SelectorCombinator against itself
/*#########################################################################*/
bool SelectorCombinator::operator==(const SelectorCombinator& rhs) const
{
return combinator() == rhs.combinator();
}
/*#########################################################################*/
// Compare SelectorCombinator against SelectorComponent
/*#########################################################################*/
bool SelectorCombinator::operator==(const SelectorComponent& rhs) const
{
if (const SelectorCombinator * sel = rhs.getCombinator()) {
return *this == *sel;
}
return false;
}
bool CompoundSelector::operator==(const SelectorComponent& rhs) const
{
if (const CompoundSelector * sel = rhs.getCompound()) {
return *this == *sel;
}
return false;
}
/*#########################################################################*/
// Compare CompoundSelector against itself
/*#########################################################################*/
// ToDo: Verifiy implementation
/*#########################################################################*/
bool CompoundSelector::operator== (const CompoundSelector& rhs) const
{
// std::cerr << "comp vs comp\n";
if (&rhs == this) return true;
if (rhs.length() != length()) return false;
std::unordered_set<const SimpleSelector*, PtrObjHash, PtrObjEquality> lhs_set;
lhs_set.reserve(length());
for (const SimpleSelectorObj& element : elements()) {
lhs_set.insert(element.ptr());
}
// there is no break?!
for (const SimpleSelectorObj& element : rhs.elements()) {
if (lhs_set.find(element.ptr()) == lhs_set.end()) return false;
}
return true;
}
/*#########################################################################*/
// Compare CompoundSelector against all other selector types
/*#########################################################################*/
bool CompoundSelector::operator== (const SelectorList& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare complex selector
return *this == *rhs.get(0);
}
bool CompoundSelector::operator== (const ComplexSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare compound selector
return *this == *rhs.get(0);
}
bool CompoundSelector::operator== (const SimpleSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return false;
// Must have exactly one item
size_t rlen = length();
if (rlen > 1) return false;
if (rlen == 0) return true;
// Compare simple selectors
return *get(0) < rhs;
}
/*#########################################################################*/
// Compare SimpleSelector against itself (upcast from abstract base)
/*#########################################################################*/
// DOES NOT EXIST FOR ABSTRACT BASE CLASS
/*#########################################################################*/
// Compare SimpleSelector against all other selector types
/*#########################################################################*/
bool SimpleSelector::operator== (const SelectorList& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare complex selector
return *this == *rhs.get(0);
}
bool SimpleSelector::operator== (const ComplexSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return true;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare compound selector
return *this == *rhs.get(0);
}
bool SimpleSelector::operator== (const CompoundSelector& rhs) const
{
// If both are empty they are equal
if (empty() && rhs.empty()) return false;
// Must have exactly one item
if (rhs.length() != 1) return false;
// Compare simple selector
return *this == *rhs.get(0);
}
/*#########################################################################*/
/*#########################################################################*/
bool IDSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<IDSelector>(&rhs);
return sel ? *this == *sel : false;
}
bool TypeSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<TypeSelector>(&rhs);
return sel ? *this == *sel : false;
}
bool ClassSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<ClassSelector>(&rhs);
return sel ? *this == *sel : false;
}
bool PseudoSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<PseudoSelector>(&rhs);
return sel ? *this == *sel : false;
}
bool AttributeSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<AttributeSelector>(&rhs);
return sel ? *this == *sel : false;
}
bool PlaceholderSelector::operator== (const SimpleSelector& rhs) const
{
auto sel = Cast<PlaceholderSelector>(&rhs);
return sel ? *this == *sel : false;
}
/*#########################################################################*/
/*#########################################################################*/
bool IDSelector::operator== (const IDSelector& rhs) const
{
// ID has no namespacing
return name() == rhs.name();
}
bool TypeSelector::operator== (const TypeSelector& rhs) const
{
return is_ns_eq(rhs) && name() == rhs.name();
}
bool ClassSelector::operator== (const ClassSelector& rhs) const
{
// Class has no namespacing
return name() == rhs.name();
}
bool PlaceholderSelector::operator== (const PlaceholderSelector& rhs) const
{
// Placeholder has no namespacing
return name() == rhs.name();
}
bool AttributeSelector::operator== (const AttributeSelector& rhs) const
{
// smaller return, equal go on, bigger abort
if (is_ns_eq(rhs)) {
if (name() != rhs.name()) return false;
if (matcher() != rhs.matcher()) return false;
if (modifier() != rhs.modifier()) return false;
const String* lhs_val = value();
const String* rhs_val = rhs.value();
return PtrObjEquality()(lhs_val, rhs_val);
}
else { return false; }
}
bool PseudoSelector::operator== (const PseudoSelector& rhs) const
{
if (is_ns_eq(rhs)) {
if (name() != rhs.name()) return false;
if (isElement() != rhs.isElement()) return false;
const String* lhs_arg = argument();
const String* rhs_arg = rhs.argument();
if (!PtrObjEquality()(lhs_arg, rhs_arg)) return false;
const SelectorList* lhs_sel = selector();
const SelectorList* rhs_sel = rhs.selector();
return PtrObjEquality()(lhs_sel, rhs_sel);
}
else { return false; }
}
/*#########################################################################*/
/*#########################################################################*/
}

View File

@@ -0,0 +1,539 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "util_string.hpp"
namespace Sass {
// ##########################################################################
// To compare/debug against libsass you can use debugger.hpp:
// c++: std::cerr << "result " << debug_vec(compound) << "\n";
// dart: stderr.writeln("result " + compound.toString());
// ##########################################################################
// ##########################################################################
// Returns whether [list1] is a superselector of [list2].
// That is, whether [list1] matches every element that
// [list2] matches, as well as possibly additional elements.
// ##########################################################################
bool listIsSuperslector(
const sass::vector<ComplexSelectorObj>& list1,
const sass::vector<ComplexSelectorObj>& list2);
// ##########################################################################
// Returns whether [complex1] is a superselector of [complex2].
// That is, whether [complex1] matches every element that
// [complex2] matches, as well as possibly additional elements.
// ##########################################################################
bool complexIsSuperselector(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2);
// ##########################################################################
// Returns all pseudo selectors in [compound] that have
// a selector argument, and that have the given [name].
// ##########################################################################
sass::vector<PseudoSelectorObj> selectorPseudoNamed(
CompoundSelectorObj compound, sass::string name)
{
sass::vector<PseudoSelectorObj> rv;
for (SimpleSelectorObj sel : compound->elements()) {
if (PseudoSelectorObj pseudo = Cast<PseudoSelector>(sel)) {
if (pseudo->isClass() && pseudo->selector()) {
if (sel->name() == name) {
rv.push_back(sel);
}
}
}
}
return rv;
}
// EO selectorPseudoNamed
// ##########################################################################
// Returns whether [simple1] is a superselector of [simple2].
// That is, whether [simple1] matches every element that
// [simple2] matches, as well as possibly additional elements.
// ##########################################################################
bool simpleIsSuperselector(
const SimpleSelectorObj& simple1,
const SimpleSelectorObj& simple2)
{
// If they are equal they are superselectors
if (ObjEqualityFn(simple1, simple2)) {
return true;
}
// Some selector pseudoclasses can match normal selectors.
if (const PseudoSelector* pseudo = Cast<PseudoSelector>(simple2)) {
if (pseudo->selector() && isSubselectorPseudo(pseudo->normalized())) {
for (auto complex : pseudo->selector()->elements()) {
// Make sure we have exacly one items
if (complex->length() != 1) {
return false;
}
// That items must be a compound selector
if (auto compound = Cast<CompoundSelector>(complex->at(0))) {
// It must contain the lhs simple selector
if (!compound->contains(simple1)) {
return false;
}
}
}
return true;
}
}
return false;
}
// EO simpleIsSuperselector
// ##########################################################################
// Returns whether [simple] is a superselector of [compound].
// That is, whether [simple] matches every element that
// [compound] matches, as well as possibly additional elements.
// ##########################################################################
bool simpleIsSuperselectorOfCompound(
const SimpleSelectorObj& simple,
const CompoundSelectorObj& compound)
{
for (SimpleSelectorObj simple2 : compound->elements()) {
if (simpleIsSuperselector(simple, simple2)) {
return true;
}
}
return false;
}
// EO simpleIsSuperselectorOfCompound
// ##########################################################################
// ##########################################################################
bool typeIsSuperselectorOfCompound(
const TypeSelectorObj& type,
const CompoundSelectorObj& compound)
{
for (const SimpleSelectorObj& simple : compound->elements()) {
if (const TypeSelectorObj& rhs = Cast<TypeSelector>(simple)) {
if (*type != *rhs) return true;
}
}
return false;
}
// EO typeIsSuperselectorOfCompound
// ##########################################################################
// ##########################################################################
bool idIsSuperselectorOfCompound(
const IDSelectorObj& id,
const CompoundSelectorObj& compound)
{
for (const SimpleSelectorObj& simple : compound->elements()) {
if (const IDSelectorObj& rhs = Cast<IDSelector>(simple)) {
if (*id != *rhs) return true;
}
}
return false;
}
// EO idIsSuperselectorOfCompound
// ##########################################################################
// ##########################################################################
bool pseudoIsSuperselectorOfPseudo(
const PseudoSelectorObj& pseudo1,
const PseudoSelectorObj& pseudo2,
const ComplexSelectorObj& parent
)
{
if (!pseudo2->selector()) return false;
if (pseudo1->name() == pseudo2->name()) {
SelectorListObj list = pseudo2->selector();
return listIsSuperslector(list->elements(), { parent });
}
return false;
}
// EO pseudoIsSuperselectorOfPseudo
// ##########################################################################
// ##########################################################################
bool pseudoNotIsSuperselectorOfCompound(
const PseudoSelectorObj& pseudo1,
const CompoundSelectorObj& compound2,
const ComplexSelectorObj& parent)
{
for (const SimpleSelectorObj& simple2 : compound2->elements()) {
if (const TypeSelectorObj& type2 = Cast<TypeSelector>(simple2)) {
if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {
if (typeIsSuperselectorOfCompound(type2, compound1)) return true;
}
}
else if (const IDSelectorObj& id2 = Cast<IDSelector>(simple2)) {
if (const CompoundSelectorObj& compound1 = Cast<CompoundSelector>(parent->last())) {
if (idIsSuperselectorOfCompound(id2, compound1)) return true;
}
}
else if (const PseudoSelectorObj& pseudo2 = Cast<PseudoSelector>(simple2)) {
if (pseudoIsSuperselectorOfPseudo(pseudo1, pseudo2, parent)) return true;
}
}
return false;
}
// pseudoNotIsSuperselectorOfCompound
// ##########################################################################
// Returns whether [pseudo1] is a superselector of [compound2].
// That is, whether [pseudo1] matches every element that [compound2]
// matches, as well as possibly additional elements. This assumes that
// [pseudo1]'s `selector` argument is not `null`. If [parents] is passed,
// it represents the parents of [compound2]. This is relevant for pseudo
// selectors with selector arguments, where we may need to know if the
// parent selectors in the selector argument match [parents].
// ##########################################################################
bool selectorPseudoIsSuperselector(
const PseudoSelectorObj& pseudo1,
const CompoundSelectorObj& compound2,
// ToDo: is this really the most convenient way to do this?
sass::vector<SelectorComponentObj>::const_iterator parents_from,
sass::vector<SelectorComponentObj>::const_iterator parents_to)
{
// ToDo: move normalization function
sass::string name(Util::unvendor(pseudo1->name()));
if (name == "matches" || name == "any") {
sass::vector<PseudoSelectorObj> pseudos =
selectorPseudoNamed(compound2, pseudo1->name());
SelectorListObj selector1 = pseudo1->selector();
for (PseudoSelectorObj pseudo2 : pseudos) {
SelectorListObj selector = pseudo2->selector();
if (selector1->isSuperselectorOf(selector)) {
return true;
}
}
for (ComplexSelectorObj complex1 : selector1->elements()) {
sass::vector<SelectorComponentObj> parents;
for (auto cur = parents_from; cur != parents_to; cur++) {
parents.push_back(*cur);
}
parents.push_back(compound2);
if (complexIsSuperselector(complex1->elements(), parents)) {
return true;
}
}
}
else if (name == "has" || name == "host" || name == "host-context" || name == "slotted") {
sass::vector<PseudoSelectorObj> pseudos =
selectorPseudoNamed(compound2, pseudo1->name());
SelectorListObj selector1 = pseudo1->selector();
for (PseudoSelectorObj pseudo2 : pseudos) {
SelectorListObj selector = pseudo2->selector();
if (selector1->isSuperselectorOf(selector)) {
return true;
}
}
}
else if (name == "not") {
for (ComplexSelectorObj complex : pseudo1->selector()->elements()) {
if (!pseudoNotIsSuperselectorOfCompound(pseudo1, compound2, complex)) return false;
}
return true;
}
else if (name == "current") {
sass::vector<PseudoSelectorObj> pseudos =
selectorPseudoNamed(compound2, "current");
for (PseudoSelectorObj pseudo2 : pseudos) {
if (ObjEqualityFn(pseudo1, pseudo2)) return true;
}
}
else if (name == "nth-child" || name == "nth-last-child") {
for (auto simple2 : compound2->elements()) {
if (PseudoSelectorObj pseudo2 = simple2->getPseudoSelector()) {
if (pseudo1->name() != pseudo2->name()) continue;
if (!ObjEqualityFn(pseudo1->argument(), pseudo2->argument())) continue;
if (pseudo1->selector()->isSuperselectorOf(pseudo2->selector())) return true;
}
}
return false;
}
return false;
}
// EO selectorPseudoIsSuperselector
// ##########################################################################
// Returns whether [compound1] is a superselector of [compound2].
// That is, whether [compound1] matches every element that [compound2]
// matches, as well as possibly additional elements. If [parents] is
// passed, it represents the parents of [compound2]. This is relevant
// for pseudo selectors with selector arguments, where we may need to
// know if the parent selectors in the selector argument match [parents].
// ##########################################################################
bool compoundIsSuperselector(
const CompoundSelectorObj& compound1,
const CompoundSelectorObj& compound2,
// ToDo: is this really the most convenient way to do this?
const sass::vector<SelectorComponentObj>::const_iterator parents_from,
const sass::vector<SelectorComponentObj>::const_iterator parents_to)
{
// Every selector in [compound1.components] must have
// a matching selector in [compound2.components].
for (SimpleSelectorObj simple1 : compound1->elements()) {
PseudoSelectorObj pseudo1 = Cast<PseudoSelector>(simple1);
if (pseudo1 && pseudo1->selector()) {
if (!selectorPseudoIsSuperselector(pseudo1, compound2, parents_from, parents_to)) {
return false;
}
}
else if (!simpleIsSuperselectorOfCompound(simple1, compound2)) {
return false;
}
}
// [compound1] can't be a superselector of a selector
// with pseudo-elements that [compound2] doesn't share.
for (SimpleSelectorObj simple2 : compound2->elements()) {
PseudoSelectorObj pseudo2 = Cast<PseudoSelector>(simple2);
if (pseudo2 && pseudo2->isElement()) {
if (!simpleIsSuperselectorOfCompound(pseudo2, compound1)) {
return false;
}
}
}
return true;
}
// EO compoundIsSuperselector
// ##########################################################################
// Returns whether [compound1] is a superselector of [compound2].
// That is, whether [compound1] matches every element that [compound2]
// matches, as well as possibly additional elements. If [parents] is
// passed, it represents the parents of [compound2]. This is relevant
// for pseudo selectors with selector arguments, where we may need to
// know if the parent selectors in the selector argument match [parents].
// ##########################################################################
bool compoundIsSuperselector(
const CompoundSelectorObj& compound1,
const CompoundSelectorObj& compound2,
const sass::vector<SelectorComponentObj>& parents)
{
return compoundIsSuperselector(
compound1, compound2,
parents.begin(), parents.end()
);
}
// EO compoundIsSuperselector
// ##########################################################################
// Returns whether [complex1] is a superselector of [complex2].
// That is, whether [complex1] matches every element that
// [complex2] matches, as well as possibly additional elements.
// ##########################################################################
bool complexIsSuperselector(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2)
{
// Selectors with trailing operators are neither superselectors nor subselectors.
if (!complex1.empty() && Cast<SelectorCombinator>(complex1.back())) return false;
if (!complex2.empty() && Cast<SelectorCombinator>(complex2.back())) return false;
size_t i1 = 0, i2 = 0;
while (true) {
size_t remaining1 = complex1.size() - i1;
size_t remaining2 = complex2.size() - i2;
if (remaining1 == 0 || remaining2 == 0) {
return false;
}
// More complex selectors are never
// superselectors of less complex ones.
if (remaining1 > remaining2) {
return false;
}
// Selectors with leading operators are
// neither superselectors nor subselectors.
if (Cast<SelectorCombinator>(complex1[i1])) {
return false;
}
if (Cast<SelectorCombinator>(complex2[i2])) {
return false;
}
CompoundSelectorObj compound1 = Cast<CompoundSelector>(complex1[i1]);
CompoundSelectorObj compound2 = Cast<CompoundSelector>(complex2.back());
if (remaining1 == 1) {
sass::vector<SelectorComponentObj>::const_iterator parents_to = complex2.end();
sass::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();
std::advance(parents_from, i2 + 1); // equivalent to dart `.skip(i2 + 1)`
bool rv = compoundIsSuperselector(compound1, compound2, parents_from, parents_to);
sass::vector<SelectorComponentObj> pp;
sass::vector<SelectorComponentObj>::const_iterator end = parents_to;
sass::vector<SelectorComponentObj>::const_iterator beg = parents_from;
while (beg != end) {
pp.push_back(*beg);
beg++;
}
return rv;
}
// Find the first index where `complex2.sublist(i2, afterSuperselector)`
// is a subselector of [compound1]. We stop before the superselector
// would encompass all of [complex2] because we know [complex1] has
// more than one element, and consuming all of [complex2] wouldn't
// leave anything for the rest of [complex1] to match.
size_t afterSuperselector = i2 + 1;
for (; afterSuperselector < complex2.size(); afterSuperselector++) {
SelectorComponentObj component2 = complex2[afterSuperselector - 1];
if (CompoundSelectorObj compound2 = Cast<CompoundSelector>(component2)) {
sass::vector<SelectorComponentObj>::const_iterator parents_to = complex2.begin();
sass::vector<SelectorComponentObj>::const_iterator parents_from = complex2.begin();
// complex2.take(afterSuperselector - 1).skip(i2 + 1)
std::advance(parents_from, i2 + 1); // equivalent to dart `.skip`
std::advance(parents_to, afterSuperselector); // equivalent to dart `.take`
if (compoundIsSuperselector(compound1, compound2, parents_from, parents_to)) {
break;
}
}
}
if (afterSuperselector == complex2.size()) {
return false;
}
SelectorComponentObj component1 = complex1[i1 + 1],
component2 = complex2[afterSuperselector];
SelectorCombinatorObj combinator1 = Cast<SelectorCombinator>(component1);
SelectorCombinatorObj combinator2 = Cast<SelectorCombinator>(component2);
if (!combinator1.isNull()) {
if (combinator2.isNull()) {
return false;
}
// `.a ~ .b` is a superselector of `.a + .b`,
// but otherwise the combinators must match.
if (combinator1->isGeneralCombinator()) {
if (combinator2->isChildCombinator()) {
return false;
}
}
else if (*combinator1 != *combinator2) {
return false;
}
// `.foo > .baz` is not a superselector of `.foo > .bar > .baz` or
// `.foo > .bar .baz`, despite the fact that `.baz` is a superselector of
// `.bar > .baz` and `.bar .baz`. Same goes for `+` and `~`.
if (remaining1 == 3 && remaining2 > 3) {
return false;
}
i1 += 2; i2 = afterSuperselector + 1;
}
else if (!combinator2.isNull()) {
if (!combinator2->isChildCombinator()) {
return false;
}
i1 += 1; i2 = afterSuperselector + 1;
}
else {
i1 += 1; i2 = afterSuperselector;
}
}
return false;
}
// EO complexIsSuperselector
// ##########################################################################
// Like [complexIsSuperselector], but compares [complex1]
// and [complex2] as though they shared an implicit base
// [SimpleSelector]. For example, `B` is not normally a
// superselector of `B A`, since it doesn't match elements
// that match `A`. However, it *is* a parent superselector,
// since `B X` is a superselector of `B A X`.
// ##########################################################################
bool complexIsParentSuperselector(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2)
{
// Try some simple heuristics to see if we can avoid allocations.
if (complex1.empty() && complex2.empty()) return false;
if (Cast<SelectorCombinator>(complex1.front())) return false;
if (Cast<SelectorCombinator>(complex2.front())) return false;
if (complex1.size() > complex2.size()) return false;
// TODO(nweiz): There's got to be a way to do this without a bunch of extra allocations...
sass::vector<SelectorComponentObj> cplx1(complex1);
sass::vector<SelectorComponentObj> cplx2(complex2);
CompoundSelectorObj base = SASS_MEMORY_NEW(CompoundSelector, "[tmp]");
cplx1.push_back(base); cplx2.push_back(base);
return complexIsSuperselector(cplx1, cplx2);
}
// EO complexIsParentSuperselector
// ##########################################################################
// Returns whether [list] has a superselector for [complex].
// That is, whether an item in [list] matches every element that
// [complex] matches, as well as possibly additional elements.
// ##########################################################################
bool listHasSuperslectorForComplex(
sass::vector<ComplexSelectorObj> list,
ComplexSelectorObj complex)
{
// Return true if every [complex] selector on [list2]
// is a super selector of the full selector [list1].
for (ComplexSelectorObj lhs : list) {
if (complexIsSuperselector(lhs->elements(), complex->elements())) {
return true;
}
}
return false;
}
// listIsSuperslectorOfComplex
// ##########################################################################
// Returns whether [list1] is a superselector of [list2].
// That is, whether [list1] matches every element that
// [list2] matches, as well as possibly additional elements.
// ##########################################################################
bool listIsSuperslector(
const sass::vector<ComplexSelectorObj>& list1,
const sass::vector<ComplexSelectorObj>& list2)
{
// Return true if every [complex] selector on [list2]
// is a super selector of the full selector [list1].
for (ComplexSelectorObj complex : list2) {
if (!listHasSuperslectorForComplex(list1, complex)) {
return false;
}
}
return true;
}
// EO listIsSuperslector
// ##########################################################################
// Implement selector methods (dispatch to functions)
// ##########################################################################
bool SelectorList::isSuperselectorOf(const SelectorList* sub) const
{
return listIsSuperslector(elements(), sub->elements());
}
bool ComplexSelector::isSuperselectorOf(const ComplexSelector* sub) const
{
return complexIsSuperselector(elements(), sub->elements());
}
// ##########################################################################
// ##########################################################################
}

View File

@@ -0,0 +1,275 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
// ##########################################################################
// Returns the contents of a [SelectorList] that matches only
// elements that are matched by both [complex1] and [complex2].
// If no such list can be produced, returns `null`.
// ##########################################################################
// ToDo: fine-tune API to avoid unnecessary wrapper allocations
// ##########################################################################
sass::vector<sass::vector<SelectorComponentObj>> unifyComplex(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes)
{
SASS_ASSERT(!complexes.empty(), "Can't unify empty list");
if (complexes.size() == 1) return complexes;
CompoundSelectorObj unifiedBase = SASS_MEMORY_NEW(CompoundSelector, SourceSpan("[phony]"));
for (auto complex : complexes) {
SelectorComponentObj base = complex.back();
if (CompoundSelector * comp = base->getCompound()) {
if (unifiedBase->empty()) {
unifiedBase->concat(comp);
}
else {
for (SimpleSelectorObj simple : comp->elements()) {
unifiedBase = simple->unifyWith(unifiedBase);
if (unifiedBase.isNull()) return {};
}
}
}
else {
return {};
}
}
sass::vector<sass::vector<SelectorComponentObj>> complexesWithoutBases;
for (size_t i = 0; i < complexes.size(); i += 1) {
sass::vector<SelectorComponentObj> sel = complexes[i];
sel.pop_back(); // remove last item (base) from the list
complexesWithoutBases.push_back(std::move(sel));
}
complexesWithoutBases.back().push_back(unifiedBase);
return weave(complexesWithoutBases);
}
// EO unifyComplex
// ##########################################################################
// Returns a [CompoundSelector] that matches only elements
// that are matched by both [compound1] and [compound2].
// If no such selector can be produced, returns `null`.
// ##########################################################################
CompoundSelector* CompoundSelector::unifyWith(CompoundSelector* rhs)
{
if (empty()) return rhs;
CompoundSelectorObj unified = SASS_MEMORY_COPY(rhs);
for (const SimpleSelectorObj& sel : elements()) {
unified = sel->unifyWith(unified);
if (unified.isNull()) break;
}
return unified.detach();
}
// EO CompoundSelector::unifyWith(CompoundSelector*)
// ##########################################################################
// Returns the compoments of a [CompoundSelector] that matches only elements
// matched by both this and [compound]. By default, this just returns a copy
// of [compound] with this selector added to the end, or returns the original
// array if this selector already exists in it. Returns `null` if unification
// is impossible—for example, if there are multiple ID selectors.
// ##########################################################################
// This is implemented in `selector/simple.dart` as `SimpleSelector::unify`
// ##########################################################################
CompoundSelector* SimpleSelector::unifyWith(CompoundSelector* rhs)
{
if (rhs->length() == 1) {
if (rhs->get(0)->is_universal()) {
CompoundSelector* this_compound = SASS_MEMORY_NEW(CompoundSelector, pstate());
this_compound->append(SASS_MEMORY_COPY(this));
CompoundSelector* unified = rhs->get(0)->unifyWith(this_compound);
if (unified == nullptr || unified != this_compound) delete this_compound;
return unified;
}
}
for (const SimpleSelectorObj& sel : rhs->elements()) {
if (*this == *sel) {
return rhs;
}
}
CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, rhs->pstate());
bool addedThis = false;
for (auto simple : rhs->elements()) {
// Make sure pseudo selectors always come last.
if (!addedThis && simple->getPseudoSelector()) {
result->append(this);
addedThis = true;
}
result->append(simple);
}
if (!addedThis) {
result->append(this);
}
return result.detach();
}
// EO SimpleSelector::unifyWith(CompoundSelector*)
// ##########################################################################
// This is implemented in `selector/type.dart` as `PseudoSelector::unify`
// ##########################################################################
CompoundSelector* TypeSelector::unifyWith(CompoundSelector* rhs)
{
if (rhs->empty()) {
rhs->append(this);
return rhs;
}
TypeSelector* type = Cast<TypeSelector>(rhs->at(0));
if (type != nullptr) {
SimpleSelector* unified = unifyWith(type);
if (unified == nullptr) {
return nullptr;
}
rhs->elements()[0] = unified;
}
else if (!is_universal() || (has_ns_ && ns_ != "*")) {
rhs->insert(rhs->begin(), this);
}
return rhs;
}
// ##########################################################################
// This is implemented in `selector/id.dart` as `PseudoSelector::unify`
// ##########################################################################
CompoundSelector* IDSelector::unifyWith(CompoundSelector* rhs)
{
for (const SimpleSelector* sel : rhs->elements()) {
if (const IDSelector* id_sel = Cast<IDSelector>(sel)) {
if (id_sel->name() != name()) return nullptr;
}
}
return SimpleSelector::unifyWith(rhs);
}
// ##########################################################################
// This is implemented in `selector/pseudo.dart` as `PseudoSelector::unify`
// ##########################################################################
CompoundSelector* PseudoSelector::unifyWith(CompoundSelector* compound)
{
if (compound->length() == 1 && compound->first()->is_universal()) {
// std::cerr << "implement universal pseudo\n";
}
for (const SimpleSelectorObj& sel : compound->elements()) {
if (*this == *sel) {
return compound;
}
}
CompoundSelectorObj result = SASS_MEMORY_NEW(CompoundSelector, compound->pstate());
bool addedThis = false;
for (auto simple : compound->elements()) {
// Make sure pseudo selectors always come last.
if (PseudoSelectorObj pseudo = simple->getPseudoSelector()) {
if (pseudo->isElement()) {
// A given compound selector may only contain one pseudo element. If
// [compound] has a different one than [this], unification fails.
if (isElement()) {
return {};
}
// Otherwise, this is a pseudo selector and
// should come before pseduo elements.
result->append(this);
addedThis = true;
}
}
result->append(simple);
}
if (!addedThis) {
result->append(this);
}
return result.detach();
}
// EO PseudoSelector::unifyWith(CompoundSelector*
// ##########################################################################
// This is implemented in `extend/functions.dart` as `unifyUniversalAndElement`
// Returns a [SimpleSelector] that matches only elements that are matched by
// both [selector1] and [selector2], which must both be either [UniversalSelector]s
// or [TypeSelector]s. If no such selector can be produced, returns `null`.
// Note: libsass handles universal selector directly within the type selector
// ##########################################################################
SimpleSelector* TypeSelector::unifyWith(const SimpleSelector* rhs)
{
bool rhs_ns = false;
if (!(is_ns_eq(*rhs) || rhs->is_universal_ns())) {
if (!is_universal_ns()) {
return nullptr;
}
rhs_ns = true;
}
bool rhs_name = false;
if (!(name_ == rhs->name() || rhs->is_universal())) {
if (!(is_universal())) {
return nullptr;
}
rhs_name = true;
}
if (rhs_ns) {
ns(rhs->ns());
has_ns(rhs->has_ns());
}
if (rhs_name) name(rhs->name());
return this;
}
// EO TypeSelector::unifyWith(const SimpleSelector*)
// ##########################################################################
// Unify two complex selectors. Internally calls `unifyComplex`
// and then wraps the result in newly create ComplexSelectors.
// ##########################################################################
SelectorList* ComplexSelector::unifyWith(ComplexSelector* rhs)
{
SelectorListObj list = SASS_MEMORY_NEW(SelectorList, pstate());
sass::vector<sass::vector<SelectorComponentObj>> rv =
unifyComplex({ elements(), rhs->elements() });
for (sass::vector<SelectorComponentObj> items : rv) {
ComplexSelectorObj sel = SASS_MEMORY_NEW(ComplexSelector, pstate());
sel->elements() = std::move(items);
list->append(sel);
}
return list.detach();
}
// EO ComplexSelector::unifyWith(ComplexSelector*)
// ##########################################################################
// only called from the sass function `selector-unify`
// ##########################################################################
SelectorList* SelectorList::unifyWith(SelectorList* rhs)
{
SelectorList* slist = SASS_MEMORY_NEW(SelectorList, pstate());
// Unify all of children with RHS's children,
// storing the results in `unified_complex_selectors`
for (ComplexSelectorObj& seq1 : elements()) {
for (ComplexSelectorObj& seq2 : rhs->elements()) {
if (SelectorListObj unified = seq1->unifyWith(seq2)) {
std::move(unified->begin(), unified->end(),
std::inserter(slist->elements(), slist->end()));
}
}
}
return slist;
}
// EO SelectorList::unifyWith(SelectorList*)
// ##########################################################################
// ##########################################################################
}

View File

@@ -0,0 +1,616 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "permutate.hpp"
#include "dart_helpers.hpp"
namespace Sass {
// ##########################################################################
// Returns whether or not [compound] contains a `::root` selector.
// ##########################################################################
bool hasRoot(const CompoundSelector* compound)
{
// Libsass does not yet know the root selector
return false;
}
// EO hasRoot
// ##########################################################################
// Returns whether a [CompoundSelector] may contain only
// one simple selector of the same type as [simple].
// ##########################################################################
bool isUnique(const SimpleSelector* simple)
{
if (Cast<IDSelector>(simple)) return true;
if (const PseudoSelector * pseudo = Cast<PseudoSelector>(simple)) {
if (pseudo->is_pseudo_element()) return true;
}
return false;
}
// EO isUnique
// ##########################################################################
// Returns whether [complex1] and [complex2] need to be unified to
// produce a valid combined selector. This is necessary when both
// selectors contain the same unique simple selector, such as an ID.
// ##########################################################################
bool mustUnify(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2)
{
sass::vector<const SimpleSelector*> uniqueSelectors1;
for (const SelectorComponent* component : complex1) {
if (const CompoundSelector * compound = component->getCompound()) {
for (const SimpleSelector* sel : compound->elements()) {
if (isUnique(sel)) {
uniqueSelectors1.push_back(sel);
}
}
}
}
if (uniqueSelectors1.empty()) return false;
// ToDo: unsure if this is correct
for (const SelectorComponent* component : complex2) {
if (const CompoundSelector * compound = component->getCompound()) {
for (const SimpleSelector* sel : compound->elements()) {
if (isUnique(sel)) {
for (auto check : uniqueSelectors1) {
if (*check == *sel) return true;
}
}
}
}
}
return false;
}
// EO isUnique
// ##########################################################################
// Helper function used by `weaveParents`
// ##########################################################################
bool cmpGroups(
const sass::vector<SelectorComponentObj>& group1,
const sass::vector<SelectorComponentObj>& group2,
sass::vector<SelectorComponentObj>& select)
{
if (group1.size() == group2.size() && std::equal(group1.begin(), group1.end(), group2.begin(), PtrObjEqualityFn<SelectorComponent>)) {
select = group1;
return true;
}
if (!Cast<CompoundSelector>(group1.front())) {
select = {};
return false;
}
if (!Cast<CompoundSelector>(group2.front())) {
select = {};
return false;
}
if (complexIsParentSuperselector(group1, group2)) {
select = group2;
return true;
}
if (complexIsParentSuperselector(group2, group1)) {
select = group1;
return true;
}
if (!mustUnify(group1, group2)) {
select = {};
return false;
}
sass::vector<sass::vector<SelectorComponentObj>> unified
= unifyComplex({ group1, group2 });
if (unified.empty()) return false;
if (unified.size() > 1) return false;
select = unified.front();
return true;
}
// EO cmpGroups
// ##########################################################################
// Helper function used by `weaveParents`
// ##########################################################################
template <class T>
bool checkForEmptyChild(const T& item) {
return item.empty();
}
// EO checkForEmptyChild
// ##########################################################################
// Helper function used by `weaveParents`
// ##########################################################################
bool cmpChunkForEmptySequence(
const sass::vector<sass::vector<SelectorComponentObj>>& seq,
const sass::vector<SelectorComponentObj>& group)
{
return seq.empty();
}
// EO cmpChunkForEmptySequence
// ##########################################################################
// Helper function used by `weaveParents`
// ##########################################################################
bool cmpChunkForParentSuperselector(
const sass::vector<sass::vector<SelectorComponentObj>>& seq,
const sass::vector<SelectorComponentObj>& group)
{
return seq.empty() || complexIsParentSuperselector(seq.front(), group);
}
// EO cmpChunkForParentSuperselector
// ##########################################################################
// Returns all orderings of initial subseqeuences of [queue1] and [queue2].
// The [done] callback is used to determine the extent of the initial
// subsequences. It's called with each queue until it returns `true`.
// Destructively removes the initial subsequences of [queue1] and [queue2].
// 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 queues would then contain `(D E)` and `(3 4 5)`.
// ##########################################################################
template <class T>
sass::vector<sass::vector<T>> getChunks(
sass::vector<T>& queue1, sass::vector<T>& queue2,
const T& group, bool(*done)(const sass::vector<T>&, const T&)
) {
sass::vector<T> chunk1;
while (!done(queue1, group)) {
chunk1.push_back(queue1.front());
queue1.erase(queue1.begin());
}
sass::vector<T> chunk2;
while (!done(queue2, group)) {
chunk2.push_back(queue2.front());
queue2.erase(queue2.begin());
}
if (chunk1.empty() && chunk2.empty()) return {};
else if (chunk1.empty()) return { chunk2 };
else if (chunk2.empty()) return { chunk1 };
sass::vector<T> choice1(chunk1), choice2(chunk2);
std::move(std::begin(chunk2), std::end(chunk2),
std::inserter(choice1, std::end(choice1)));
std::move(std::begin(chunk1), std::end(chunk1),
std::inserter(choice2, std::end(choice2)));
return { choice1, choice2 };
}
// EO getChunks
// ##########################################################################
// If the first element of [queue] has a `::root`
// selector, removes and returns that element.
// ##########################################################################
CompoundSelectorObj getFirstIfRoot(sass::vector<SelectorComponentObj>& queue) {
if (queue.empty()) return {};
SelectorComponent* first = queue.front();
if (CompoundSelector* sel = Cast<CompoundSelector>(first)) {
if (!hasRoot(sel)) return {};
queue.erase(queue.begin());
return sel;
}
return {};
}
// EO getFirstIfRoot
// ##########################################################################
// Returns [complex], grouped into sub-lists such that no sub-list
// contains two adjacent [ComplexSelector]s. For example,
// `(A B > C D + E ~ > G)` is grouped into `[(A) (B > C) (D + E ~ > G)]`.
// ##########################################################################
sass::vector<sass::vector<SelectorComponentObj>> groupSelectors(
const sass::vector<SelectorComponentObj>& components)
{
bool lastWasCompound = false;
sass::vector<SelectorComponentObj> group;
sass::vector<sass::vector<SelectorComponentObj>> groups;
for (size_t i = 0; i < components.size(); i += 1) {
if (CompoundSelector* compound = components[i]->getCompound()) {
if (lastWasCompound) {
groups.push_back(group);
group.clear();
}
group.push_back(compound);
lastWasCompound = true;
}
else if (SelectorCombinator* combinator = components[i]->getCombinator()) {
group.push_back(combinator);
lastWasCompound = false;
}
}
if (!group.empty()) {
groups.push_back(group);
}
return groups;
}
// EO groupSelectors
// ##########################################################################
// Extracts leading [Combinator]s from [components1] and [components2]
// and merges them together into a single list of combinators.
// If there are no combinators to be merged, returns an empty list.
// If the combinators can't be merged, returns `null`.
// ##########################################################################
bool mergeInitialCombinators(
sass::vector<SelectorComponentObj>& components1,
sass::vector<SelectorComponentObj>& components2,
sass::vector<SelectorComponentObj>& result)
{
sass::vector<SelectorComponentObj> combinators1;
while (!components1.empty() && Cast<SelectorCombinator>(components1.front())) {
SelectorCombinatorObj front = Cast<SelectorCombinator>(components1.front());
components1.erase(components1.begin());
combinators1.push_back(front);
}
sass::vector<SelectorComponentObj> combinators2;
while (!components2.empty() && Cast<SelectorCombinator>(components2.front())) {
SelectorCombinatorObj front = Cast<SelectorCombinator>(components2.front());
components2.erase(components2.begin());
combinators2.push_back(front);
}
// If neither sequence of combinators is a subsequence
// of the other, they cannot be merged successfully.
sass::vector<SelectorComponentObj> LCS = lcs<SelectorComponentObj>(combinators1, combinators2);
if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {
result = combinators2;
return true;
}
if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {
result = combinators1;
return true;
}
return false;
}
// EO mergeInitialCombinators
// ##########################################################################
// Extracts trailing [Combinator]s, and the selectors to which they apply,
// from [components1] and [components2] and merges them together into a
// single list. If there are no combinators to be merged, returns an
// empty list. If the sequences can't be merged, returns `null`.
// ##########################################################################
bool mergeFinalCombinators(
sass::vector<SelectorComponentObj>& components1,
sass::vector<SelectorComponentObj>& components2,
sass::vector<sass::vector<sass::vector<SelectorComponentObj>>>& result)
{
if (components1.empty() || !Cast<SelectorCombinator>(components1.back())) {
if (components2.empty() || !Cast<SelectorCombinator>(components2.back())) {
return true;
}
}
sass::vector<SelectorComponentObj> combinators1;
while (!components1.empty() && Cast<SelectorCombinator>(components1.back())) {
SelectorCombinatorObj back = Cast<SelectorCombinator>(components1.back());
components1.erase(components1.end() - 1);
combinators1.push_back(back);
}
sass::vector<SelectorComponentObj> combinators2;
while (!components2.empty() && Cast<SelectorCombinator>(components2.back())) {
SelectorCombinatorObj back = Cast<SelectorCombinator>(components2.back());
components2.erase(components2.end() - 1);
combinators2.push_back(back);
}
// reverse now as we used push_back (faster than new alloc)
std::reverse(combinators1.begin(), combinators1.end());
std::reverse(combinators2.begin(), combinators2.end());
if (combinators1.size() > 1 || combinators2.size() > 1) {
// If there are multiple combinators, something hacky's going on. If one
// is a supersequence of the other, use that, otherwise give up.
auto LCS = lcs<SelectorComponentObj>(combinators1, combinators2);
if (ListEquality(LCS, combinators1, PtrObjEqualityFn<SelectorComponent>)) {
result.push_back({ combinators2 });
}
else if (ListEquality(LCS, combinators2, PtrObjEqualityFn<SelectorComponent>)) {
result.push_back({ combinators1 });
}
else {
return false;
}
return true;
}
// This code looks complicated, but it's actually just a bunch of special
// cases for interactions between different combinators.
SelectorCombinatorObj combinator1, combinator2;
if (!combinators1.empty()) combinator1 = combinators1.back();
if (!combinators2.empty()) combinator2 = combinators2.back();
if (!combinator1.isNull() && !combinator2.isNull()) {
CompoundSelector* compound1 = Cast<CompoundSelector>(components1.back());
CompoundSelector* compound2 = Cast<CompoundSelector>(components2.back());
components1.pop_back();
components2.pop_back();
if (combinator1->isGeneralCombinator() && combinator2->isGeneralCombinator()) {
if (compound1->isSuperselectorOf(compound2)) {
result.push_back({ { compound2, combinator2 } });
}
else if (compound2->isSuperselectorOf(compound1)) {
result.push_back({ { compound1, combinator1 } });
}
else {
sass::vector<sass::vector<SelectorComponentObj>> choices;
choices.push_back({ compound1, combinator1, compound2, combinator2 });
choices.push_back({ compound2, combinator2, compound1, combinator1 });
if (CompoundSelector* unified = compound1->unifyWith(compound2)) {
choices.push_back({ unified, combinator1 });
}
result.push_back(choices);
}
}
else if ((combinator1->isGeneralCombinator() && combinator2->isAdjacentCombinator()) ||
(combinator1->isAdjacentCombinator() && combinator2->isGeneralCombinator())) {
CompoundSelector* followingSiblingSelector = combinator1->isGeneralCombinator() ? compound1 : compound2;
CompoundSelector* nextSiblingSelector = combinator1->isGeneralCombinator() ? compound2 : compound1;
SelectorCombinator* followingSiblingCombinator = combinator1->isGeneralCombinator() ? combinator1 : combinator2;
SelectorCombinator* nextSiblingCombinator = combinator1->isGeneralCombinator() ? combinator2 : combinator1;
if (followingSiblingSelector->isSuperselectorOf(nextSiblingSelector)) {
result.push_back({ { nextSiblingSelector, nextSiblingCombinator } });
}
else {
CompoundSelectorObj unified = compound1->unifyWith(compound2);
sass::vector<sass::vector<SelectorComponentObj>> items;
if (!unified.isNull()) {
items.push_back({
unified, nextSiblingCombinator
});
}
items.insert(items.begin(), {
followingSiblingSelector,
followingSiblingCombinator,
nextSiblingSelector,
nextSiblingCombinator,
});
result.push_back(items);
}
}
else if (combinator1->isChildCombinator() && (combinator2->isAdjacentCombinator() || combinator2->isGeneralCombinator())) {
result.push_back({ { compound2, combinator2 } });
components1.push_back(compound1);
components1.push_back(combinator1);
}
else if (combinator2->isChildCombinator() && (combinator1->isAdjacentCombinator() || combinator1->isGeneralCombinator())) {
result.push_back({ { compound1, combinator1 } });
components2.push_back(compound2);
components2.push_back(combinator2);
}
else if (*combinator1 == *combinator2) {
CompoundSelectorObj unified = compound1->unifyWith(compound2);
if (unified.isNull()) return false;
result.push_back({ { unified, combinator1 } });
}
else {
return false;
}
return mergeFinalCombinators(components1, components2, result);
}
else if (!combinator1.isNull()) {
if (combinator1->isChildCombinator() && !components2.empty()) {
const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());
const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());
if (back1 && back2 && back2->isSuperselectorOf(back1)) {
components2.pop_back();
}
}
result.push_back({ { components1.back(), combinator1 } });
components1.pop_back();
return mergeFinalCombinators(components1, components2, result);
}
if (combinator2->isChildCombinator() && !components1.empty()) {
const CompoundSelector* back1 = Cast<CompoundSelector>(components1.back());
const CompoundSelector* back2 = Cast<CompoundSelector>(components2.back());
if (back1 && back2 && back1->isSuperselectorOf(back2)) {
components1.pop_back();
}
}
result.push_back({ { components2.back(), combinator2 } });
components2.pop_back();
return mergeFinalCombinators(components1, components2, result);
}
// EO mergeFinalCombinators
// ##########################################################################
// Expands "parenthesized selectors" in [complexes]. That is, if
// we have `.A .B {@extend .C}` and `.D .C {...}`, this conceptually
// expands into `.D .C, .D (.A .B)`, and this function translates
// `.D (.A .B)` into `.D .A .B, .A .D .B`. For thoroughness, `.A.D .B`
// would also be required, but including merged selectors results in
// exponential output for very little gain. The selector `.D (.A .B)`
// is represented as the list `[[.D], [.A, .B]]`.
// ##########################################################################
sass::vector<sass::vector<SelectorComponentObj>> weave(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes) {
sass::vector<sass::vector<SelectorComponentObj>> prefixes;
prefixes.push_back(complexes.at(0));
for (size_t i = 1; i < complexes.size(); i += 1) {
if (complexes[i].empty()) {
continue;
}
const sass::vector<SelectorComponentObj>& complex = complexes[i];
SelectorComponent* target = complex.back();
if (complex.size() == 1) {
for (auto& prefix : prefixes) {
prefix.push_back(target);
}
continue;
}
sass::vector<SelectorComponentObj> parents(complex);
parents.pop_back();
sass::vector<sass::vector<SelectorComponentObj>> newPrefixes;
for (sass::vector<SelectorComponentObj> prefix : prefixes) {
sass::vector<sass::vector<SelectorComponentObj>>
parentPrefixes = weaveParents(prefix, parents);
if (parentPrefixes.empty()) continue;
for (auto& parentPrefix : parentPrefixes) {
parentPrefix.push_back(target);
newPrefixes.push_back(parentPrefix);
}
}
prefixes = newPrefixes;
}
return prefixes;
}
// EO weave
// ##########################################################################
// Interweaves [parents1] and [parents2] as parents of the same target
// selector. Returns all possible orderings of the selectors in the
// inputs (including using unification) that maintain the relative
// ordering of the input. For example, given `.foo .bar` and `.baz .bang`,
// this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,
// `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
// and so on until `.baz .bang .foo .bar`. Semantically, for selectors A
// and B, this returns all selectors `AB_i` such that the union over all i
// of elements matched by `AB_i X` is identical to the intersection of all
// elements matched by `A X` and all elements matched by `B X`. Some `AB_i`
// are elided to reduce the size of the output.
// ##########################################################################
sass::vector<sass::vector<SelectorComponentObj>> weaveParents(
sass::vector<SelectorComponentObj> queue1,
sass::vector<SelectorComponentObj> queue2)
{
sass::vector<SelectorComponentObj> leads;
sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> trails;
if (!mergeInitialCombinators(queue1, queue2, leads)) return {};
if (!mergeFinalCombinators(queue1, queue2, trails)) return {};
// list comes out in reverse order for performance
std::reverse(trails.begin(), trails.end());
// Make sure there's at most one `:root` in the output.
// Note: does not yet do anything in libsass (no root selector)
CompoundSelectorObj root1 = getFirstIfRoot(queue1);
CompoundSelectorObj root2 = getFirstIfRoot(queue2);
if (!root1.isNull() && !root2.isNull()) {
CompoundSelectorObj root = root1->unifyWith(root2);
if (root.isNull()) return {}; // null
queue1.insert(queue1.begin(), root);
queue2.insert(queue2.begin(), root);
}
else if (!root1.isNull()) {
queue2.insert(queue2.begin(), root1);
}
else if (!root2.isNull()) {
queue1.insert(queue1.begin(), root2);
}
// group into sub-lists so no sub-list contains two adjacent ComplexSelectors.
sass::vector<sass::vector<SelectorComponentObj>> groups1 = groupSelectors(queue1);
sass::vector<sass::vector<SelectorComponentObj>> groups2 = groupSelectors(queue2);
// The main array to store our choices that will be permutated
sass::vector<sass::vector<sass::vector<SelectorComponentObj>>> choices;
// append initial combinators
choices.push_back({ leads });
sass::vector<sass::vector<SelectorComponentObj>> LCS =
lcs<sass::vector<SelectorComponentObj>>(groups1, groups2, cmpGroups);
for (auto group : LCS) {
// Create junks from groups1 and groups2
sass::vector<sass::vector<sass::vector<SelectorComponentObj>>>
chunks = getChunks<sass::vector<SelectorComponentObj>>(
groups1, groups2, group, cmpChunkForParentSuperselector);
// Create expanded array by flattening chunks2 inner
sass::vector<sass::vector<SelectorComponentObj>>
expanded = flattenInner(chunks);
// Prepare data structures
choices.push_back(expanded);
choices.push_back({ group });
if (!groups1.empty()) {
groups1.erase(groups1.begin());
}
if (!groups2.empty()) {
groups2.erase(groups2.begin());
}
}
// Create junks from groups1 and groups2
sass::vector<sass::vector<sass::vector<SelectorComponentObj>>>
chunks = getChunks<sass::vector<SelectorComponentObj>>(
groups1, groups2, {}, cmpChunkForEmptySequence);
// Append chunks with inner arrays flattened
choices.emplace_back(flattenInner(chunks));
// append all trailing selectors to choices
std::move(std::begin(trails), std::end(trails),
std::inserter(choices, std::end(choices)));
// move all non empty items to the front, then erase the trailing ones
choices.erase(std::remove_if(choices.begin(), choices.end(), checkForEmptyChild
<sass::vector<sass::vector<SelectorComponentObj>>>), choices.end());
// permutate all possible paths through selectors
sass::vector<sass::vector<SelectorComponentObj>>
results = flattenInner(permutate(choices));
return results;
}
// EO weaveParents
// ##########################################################################
// ##########################################################################
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,522 @@
#ifndef SASS_AST_SEL_H
#define SASS_AST_SEL_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
/////////////////////////////////////////////////////////////////////////
// Some helper functions
/////////////////////////////////////////////////////////////////////////
bool compoundIsSuperselector(
const CompoundSelectorObj& compound1,
const CompoundSelectorObj& compound2,
const sass::vector<SelectorComponentObj>& parents);
bool complexIsParentSuperselector(
const sass::vector<SelectorComponentObj>& complex1,
const sass::vector<SelectorComponentObj>& complex2);
sass::vector<sass::vector<SelectorComponentObj>> weave(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes);
sass::vector<sass::vector<SelectorComponentObj>> weaveParents(
sass::vector<SelectorComponentObj> parents1,
sass::vector<SelectorComponentObj> parents2);
sass::vector<SimpleSelectorObj> unifyCompound(
const sass::vector<SimpleSelectorObj>& compound1,
const sass::vector<SimpleSelectorObj>& compound2);
sass::vector<sass::vector<SelectorComponentObj>> unifyComplex(
const sass::vector<sass::vector<SelectorComponentObj>>& complexes);
/////////////////////////////////////////
// Abstract base class for CSS selectors.
/////////////////////////////////////////
class Selector : public Expression {
protected:
mutable size_t hash_;
public:
Selector(SourceSpan pstate);
virtual ~Selector() = 0;
size_t hash() const override = 0;
virtual bool has_real_parent_ref() const;
// you should reset this to null on containers
virtual unsigned long specificity() const = 0;
// by default we return the regular specificity
// you must override this for all containers
virtual size_t maxSpecificity() const { return specificity(); }
virtual size_t minSpecificity() const { return specificity(); }
// dispatch to correct handlers
ATTACH_VIRTUAL_CMP_OPERATIONS(Selector)
ATTACH_VIRTUAL_AST_OPERATIONS(Selector)
};
inline Selector::~Selector() { }
/////////////////////////////////////////////////////////////////////////
// Interpolated selectors -- the interpolated String will be expanded and
// re-parsed into a normal selector class.
/////////////////////////////////////////////////////////////////////////
class Selector_Schema final : public AST_Node {
ADD_PROPERTY(String_Schema_Obj, contents)
ADD_PROPERTY(bool, connect_parent);
// store computed hash
mutable size_t hash_;
public:
Selector_Schema(SourceSpan pstate, String_Obj c);
bool has_real_parent_ref() const;
// selector schema is not yet a final selector, so we do not
// have a specificity for it yet. We need to
virtual unsigned long specificity() const;
size_t hash() const override;
ATTACH_AST_OPERATIONS(Selector_Schema)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////
// Abstract base class for simple selectors.
////////////////////////////////////////////
class SimpleSelector : public Selector {
public:
enum Simple_Type {
ID_SEL,
TYPE_SEL,
CLASS_SEL,
PSEUDO_SEL,
ATTRIBUTE_SEL,
PLACEHOLDER_SEL,
};
public:
HASH_CONSTREF(sass::string, ns)
HASH_CONSTREF(sass::string, name)
ADD_PROPERTY(Simple_Type, simple_type)
HASH_PROPERTY(bool, has_ns)
public:
SimpleSelector(SourceSpan pstate, sass::string n = "");
// ordering within parent (peudos go last)
virtual int getSortOrder() const = 0;
virtual sass::string ns_name() const;
size_t hash() const override;
virtual bool empty() const;
// namespace compare functions
bool is_ns_eq(const SimpleSelector& r) const;
// namespace query functions
bool is_universal_ns() const;
bool is_empty_ns() const;
bool has_empty_ns() const;
bool has_qualified_ns() const;
// name query functions
bool is_universal() const;
virtual bool has_placeholder();
virtual ~SimpleSelector() = 0;
virtual CompoundSelector* unifyWith(CompoundSelector*);
/* helper function for syntax sugar */
virtual IDSelector* getIdSelector() { return NULL; }
virtual TypeSelector* getTypeSelector() { return NULL; }
virtual PseudoSelector* getPseudoSelector() { return NULL; }
ComplexSelectorObj wrapInComplex();
CompoundSelectorObj wrapInCompound();
virtual bool isInvisible() const { return false; }
virtual bool is_pseudo_element() const;
virtual bool has_real_parent_ref() const override;
bool operator==(const Selector& rhs) const final override;
virtual bool operator==(const SelectorList& rhs) const;
virtual bool operator==(const ComplexSelector& rhs) const;
virtual bool operator==(const CompoundSelector& rhs) const;
ATTACH_VIRTUAL_CMP_OPERATIONS(SimpleSelector);
ATTACH_VIRTUAL_AST_OPERATIONS(SimpleSelector);
ATTACH_CRTP_PERFORM_METHODS();
};
inline SimpleSelector::~SimpleSelector() { }
/////////////////////////////////////////////////////////////////////////
// Placeholder selectors (e.g., "%foo") for use in extend-only selectors.
/////////////////////////////////////////////////////////////////////////
class PlaceholderSelector final : public SimpleSelector {
public:
PlaceholderSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 0; }
bool isInvisible() const override { return true; }
virtual unsigned long specificity() const override;
virtual bool has_placeholder() override;
bool operator==(const SimpleSelector& rhs) const override;
ATTACH_CMP_OPERATIONS(PlaceholderSelector)
ATTACH_AST_OPERATIONS(PlaceholderSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
/////////////////////////////////////////////////////////////////////
// Type selectors (and the universal selector) -- e.g., div, span, *.
/////////////////////////////////////////////////////////////////////
class TypeSelector final : public SimpleSelector {
public:
TypeSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 1; }
virtual unsigned long specificity() const override;
SimpleSelector* unifyWith(const SimpleSelector*);
CompoundSelector* unifyWith(CompoundSelector*) override;
TypeSelector* getTypeSelector() override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(TypeSelector)
ATTACH_AST_OPERATIONS(TypeSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////
// Class selectors -- i.e., .foo.
////////////////////////////////////////////////
class ClassSelector final : public SimpleSelector {
public:
ClassSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 3; }
virtual unsigned long specificity() const override;
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(ClassSelector)
ATTACH_AST_OPERATIONS(ClassSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////
// ID selectors -- i.e., #foo.
////////////////////////////////////////////////
class IDSelector final : public SimpleSelector {
public:
IDSelector(SourceSpan pstate, sass::string n);
int getSortOrder() const override final { return 2; }
virtual unsigned long specificity() const override;
CompoundSelector* unifyWith(CompoundSelector*) override;
IDSelector* getIdSelector() final override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(IDSelector)
ATTACH_AST_OPERATIONS(IDSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////////////////////
// Attribute selectors -- e.g., [src*=".jpg"], etc.
///////////////////////////////////////////////////
class AttributeSelector final : public SimpleSelector {
ADD_CONSTREF(sass::string, matcher)
// this cannot be changed to obj atm!!!!!!????!!!!!!!
ADD_PROPERTY(String_Obj, value) // might be interpolated
ADD_PROPERTY(char, modifier);
public:
AttributeSelector(SourceSpan pstate, sass::string n, sass::string m, String_Obj v, char o = 0);
int getSortOrder() const override final { return 4; }
size_t hash() const override;
virtual unsigned long specificity() const override;
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(AttributeSelector)
ATTACH_AST_OPERATIONS(AttributeSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////////////////////////////////////
// Pseudo selectors -- e.g., :first-child, :nth-of-type(...), etc.
//////////////////////////////////////////////////////////////////
// Pseudo Selector cannot have any namespace?
class PseudoSelector final : public SimpleSelector {
ADD_PROPERTY(sass::string, normalized)
ADD_PROPERTY(String_Obj, argument)
ADD_PROPERTY(SelectorListObj, selector)
ADD_PROPERTY(bool, isSyntacticClass)
ADD_PROPERTY(bool, isClass)
public:
PseudoSelector(SourceSpan pstate, sass::string n, bool element = false);
int getSortOrder() const override final { return 5; }
virtual bool is_pseudo_element() const override;
size_t hash() const override;
bool empty() const override;
bool has_real_parent_ref() const override;
// Whether this is a pseudo-element selector.
// This is `true` if and only if [isClass] is `false`.
bool isElement() const { return !isClass(); }
// Whether this is syntactically a pseudo-element selector.
// This is `true` if and only if [isSyntacticClass] is `false`.
bool isSyntacticElement() const { return !isSyntacticClass(); }
virtual unsigned long specificity() const override;
PseudoSelectorObj withSelector(SelectorListObj selector);
CompoundSelector* unifyWith(CompoundSelector*) override;
PseudoSelector* getPseudoSelector() final override { return this; }
bool operator==(const SimpleSelector& rhs) const final override;
ATTACH_CMP_OPERATIONS(PseudoSelector)
ATTACH_AST_OPERATIONS(PseudoSelector)
void cloneChildren() override;
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// Complex Selectors are the most important class of selectors.
// A Selector List consists of Complex Selectors (separated by comma)
// Complex Selectors are itself a list of Compounds and Combinators
// Between each item there is an implicit ancestor of combinator
////////////////////////////////////////////////////////////////////////////
class ComplexSelector final : public Selector, public Vectorized<SelectorComponentObj> {
ADD_PROPERTY(bool, chroots)
// line break before list separator
ADD_PROPERTY(bool, hasPreLineFeed)
public:
ComplexSelector(SourceSpan pstate);
// Returns true if the first components
// is a compound selector and fullfills
// a few other criteria.
bool isInvisible() const;
size_t hash() const override;
void cloneChildren() override;
bool has_placeholder() const;
bool has_real_parent_ref() const override;
SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual unsigned long specificity() const override;
SelectorList* unifyWith(ComplexSelector* rhs);
bool isSuperselectorOf(const ComplexSelector* sub) const;
SelectorListObj wrapInList();
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorList& rhs) const;
bool operator==(const CompoundSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
ATTACH_CMP_OPERATIONS(ComplexSelector)
ATTACH_AST_OPERATIONS(ComplexSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// Base class for complex selector components
////////////////////////////////////////////////////////////////////////////
class SelectorComponent : public Selector {
// line break after list separator
ADD_PROPERTY(bool, hasPostLineBreak)
public:
SelectorComponent(SourceSpan pstate, bool postLineBreak = false);
size_t hash() const override = 0;
void cloneChildren() override;
// By default we consider instances not empty
virtual bool empty() const { return false; }
virtual bool has_placeholder() const = 0;
bool has_real_parent_ref() const override = 0;
ComplexSelector* wrapInComplex();
size_t maxSpecificity() const override { return 0; }
size_t minSpecificity() const override { return 0; }
virtual bool isCompound() const { return false; };
virtual bool isCombinator() const { return false; };
/* helper function for syntax sugar */
virtual CompoundSelector* getCompound() { return NULL; }
virtual SelectorCombinator* getCombinator() { return NULL; }
virtual const CompoundSelector* getCompound() const { return NULL; }
virtual const SelectorCombinator* getCombinator() const { return NULL; }
virtual unsigned long specificity() const override;
bool operator==(const Selector& rhs) const override = 0;
ATTACH_VIRTUAL_CMP_OPERATIONS(SelectorComponent);
ATTACH_VIRTUAL_AST_OPERATIONS(SelectorComponent);
};
////////////////////////////////////////////////////////////////////////////
// A specific combinator between compound selectors
////////////////////////////////////////////////////////////////////////////
class SelectorCombinator final : public SelectorComponent {
public:
// Enumerate all possible selector combinators. There is some
// discrepancy with dart-sass. Opted to name them as in CSS33
enum Combinator { CHILD /* > */, GENERAL /* ~ */, ADJACENT /* + */};
private:
// Store the type of this combinator
HASH_CONSTREF(Combinator, combinator)
public:
SelectorCombinator(SourceSpan pstate, Combinator combinator, bool postLineBreak = false);
bool has_real_parent_ref() const override { return false; }
bool has_placeholder() const override { return false; }
/* helper function for syntax sugar */
SelectorCombinator* getCombinator() final override { return this; }
const SelectorCombinator* getCombinator() const final override { return this; }
// Query type of combinator
bool isCombinator() const override { return true; };
// Matches the right-hand selector if it's a direct child of the left-
// hand selector in the DOM tree. Dart-sass also calls this `child`
// https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator
bool isChildCombinator() const { return combinator_ == CHILD; } // >
// Matches the right-hand selector if it comes after the left-hand
// selector in the DOM tree. Dart-sass class this `followingSibling`
// https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator
bool isGeneralCombinator() const { return combinator_ == GENERAL; } // ~
// Matches the right-hand selector if it's immediately adjacent to the
// left-hand selector in the DOM tree. Dart-sass calls this `nextSibling`
// https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator
bool isAdjacentCombinator() const { return combinator_ == ADJACENT; } // +
size_t maxSpecificity() const override { return 0; }
size_t minSpecificity() const override { return 0; }
size_t hash() const override {
return std::hash<int>()(combinator_);
}
void cloneChildren() override;
virtual unsigned long specificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorComponent& rhs) const override;
ATTACH_CMP_OPERATIONS(SelectorCombinator)
ATTACH_AST_OPERATIONS(SelectorCombinator)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////////
// A compound selector consists of multiple simple selectors
////////////////////////////////////////////////////////////////////////////
class CompoundSelector final : public SelectorComponent, public Vectorized<SimpleSelectorObj> {
ADD_PROPERTY(bool, hasRealParent)
ADD_PROPERTY(bool, extended)
public:
CompoundSelector(SourceSpan pstate, bool postLineBreak = false);
// Returns true if this compound selector
// fullfills various criteria.
bool isInvisible() const;
bool empty() const override {
return Vectorized::empty();
}
size_t hash() const override;
CompoundSelector* unifyWith(CompoundSelector* rhs);
/* helper function for syntax sugar */
CompoundSelector* getCompound() final override { return this; }
const CompoundSelector* getCompound() const final override { return this; }
bool isSuperselectorOf(const CompoundSelector* sub, sass::string wrapped = "") const;
void cloneChildren() override;
bool has_real_parent_ref() const override;
bool has_placeholder() const override;
sass::vector<ComplexSelectorObj> resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool isCompound() const override { return true; };
virtual unsigned long specificity() const override;
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const SelectorComponent& rhs) const override;
bool operator==(const SelectorList& rhs) const;
bool operator==(const ComplexSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
void sortChildren();
ATTACH_CMP_OPERATIONS(CompoundSelector)
ATTACH_AST_OPERATIONS(CompoundSelector)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////
// Comma-separated selector groups.
///////////////////////////////////
class SelectorList final : public Selector, public Vectorized<ComplexSelectorObj> {
private:
// maybe we have optional flag
// ToDo: should be at ExtendRule?
ADD_PROPERTY(bool, is_optional)
public:
SelectorList(SourceSpan pstate, size_t s = 0);
sass::string type() const override { return "list"; }
size_t hash() const override;
SelectorList* unifyWith(SelectorList*);
// Returns true if all complex selectors
// can have real parents, meaning every
// first component does allow for it
bool isInvisible() const;
void cloneChildren() override;
bool has_real_parent_ref() const override;
SelectorList* resolve_parent_refs(SelectorStack pstack, Backtraces& traces, bool implicit_parent = true);
virtual unsigned long specificity() const override;
bool isSuperselectorOf(const SelectorList* sub) const;
size_t maxSpecificity() const override;
size_t minSpecificity() const override;
bool operator==(const Selector& rhs) const override;
bool operator==(const ComplexSelector& rhs) const;
bool operator==(const CompoundSelector& rhs) const;
bool operator==(const SimpleSelector& rhs) const;
// Selector Lists can be compared to comma lists
bool operator==(const Expression& rhs) const override;
ATTACH_CMP_OPERATIONS(SelectorList)
ATTACH_AST_OPERATIONS(SelectorList)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////
// The Sass `@extend` directive.
////////////////////////////////
class ExtendRule final : public Statement {
ADD_PROPERTY(bool, isOptional)
// This should be a simple selector only!
ADD_PROPERTY(SelectorListObj, selector)
ADD_PROPERTY(Selector_Schema_Obj, schema)
public:
ExtendRule(SourceSpan pstate, SelectorListObj s);
ExtendRule(SourceSpan pstate, Selector_Schema_Obj s);
ATTACH_AST_OPERATIONS(ExtendRule)
ATTACH_CRTP_PERFORM_METHODS()
};
}
#endif

View File

@@ -0,0 +1,114 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
SupportsRule::SupportsRule(SourceSpan pstate, SupportsConditionObj condition, Block_Obj block)
: ParentStatement(pstate, block), condition_(condition)
{ statement_type(SUPPORTS); }
SupportsRule::SupportsRule(const SupportsRule* ptr)
: ParentStatement(ptr), condition_(ptr->condition_)
{ statement_type(SUPPORTS); }
bool SupportsRule::bubbles() { return true; }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
SupportsCondition::SupportsCondition(SourceSpan pstate)
: Expression(pstate)
{ }
SupportsCondition::SupportsCondition(const SupportsCondition* ptr)
: Expression(ptr)
{ }
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
SupportsOperation::SupportsOperation(SourceSpan pstate, SupportsConditionObj l, SupportsConditionObj r, Operand o)
: SupportsCondition(pstate), left_(l), right_(r), operand_(o)
{ }
SupportsOperation::SupportsOperation(const SupportsOperation* ptr)
: SupportsCondition(ptr),
left_(ptr->left_),
right_(ptr->right_),
operand_(ptr->operand_)
{ }
bool SupportsOperation::needs_parens(SupportsConditionObj cond) const
{
if (SupportsOperationObj op = Cast<SupportsOperation>(cond)) {
return op->operand() != operand();
}
return Cast<SupportsNegation>(cond) != NULL;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
SupportsNegation::SupportsNegation(SourceSpan pstate, SupportsConditionObj c)
: SupportsCondition(pstate), condition_(c)
{ }
SupportsNegation::SupportsNegation(const SupportsNegation* ptr)
: SupportsCondition(ptr), condition_(ptr->condition_)
{ }
bool SupportsNegation::needs_parens(SupportsConditionObj cond) const
{
return Cast<SupportsNegation>(cond) ||
Cast<SupportsOperation>(cond);
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
SupportsDeclaration::SupportsDeclaration(SourceSpan pstate, ExpressionObj f, ExpressionObj v)
: SupportsCondition(pstate), feature_(f), value_(v)
{ }
SupportsDeclaration::SupportsDeclaration(const SupportsDeclaration* ptr)
: SupportsCondition(ptr),
feature_(ptr->feature_),
value_(ptr->value_)
{ }
bool SupportsDeclaration::needs_parens(SupportsConditionObj cond) const
{
return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
Supports_Interpolation::Supports_Interpolation(SourceSpan pstate, ExpressionObj v)
: SupportsCondition(pstate), value_(v)
{ }
Supports_Interpolation::Supports_Interpolation(const Supports_Interpolation* ptr)
: SupportsCondition(ptr),
value_(ptr->value_)
{ }
bool Supports_Interpolation::needs_parens(SupportsConditionObj cond) const
{
return false;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
IMPLEMENT_AST_OPERATORS(SupportsRule);
IMPLEMENT_AST_OPERATORS(SupportsCondition);
IMPLEMENT_AST_OPERATORS(SupportsOperation);
IMPLEMENT_AST_OPERATORS(SupportsNegation);
IMPLEMENT_AST_OPERATORS(SupportsDeclaration);
IMPLEMENT_AST_OPERATORS(Supports_Interpolation);
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
}

View File

@@ -0,0 +1,121 @@
#ifndef SASS_AST_SUPPORTS_H
#define SASS_AST_SUPPORTS_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <set>
#include <deque>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <typeinfo>
#include <algorithm>
#include "sass/base.h"
#include "ast_fwd_decl.hpp"
#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 "source_map.hpp"
#include "fn_utils.hpp"
#include "sass.h"
namespace Sass {
////////////////////
// `@supports` rule.
////////////////////
class SupportsRule : public ParentStatement {
ADD_PROPERTY(SupportsConditionObj, condition)
public:
SupportsRule(SourceSpan pstate, SupportsConditionObj condition, Block_Obj block = {});
bool bubbles() override;
ATTACH_AST_OPERATIONS(SupportsRule)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////////////////////////
// The abstract superclass of all Supports conditions.
//////////////////////////////////////////////////////
class SupportsCondition : public Expression {
public:
SupportsCondition(SourceSpan pstate);
virtual bool needs_parens(SupportsConditionObj cond) const { return false; }
ATTACH_AST_OPERATIONS(SupportsCondition)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////
// An operator condition (e.g. `CONDITION1 and CONDITION2`).
////////////////////////////////////////////////////////////
class SupportsOperation : public SupportsCondition {
public:
enum Operand { AND, OR };
private:
ADD_PROPERTY(SupportsConditionObj, left);
ADD_PROPERTY(SupportsConditionObj, right);
ADD_PROPERTY(Operand, operand);
public:
SupportsOperation(SourceSpan pstate, SupportsConditionObj l, SupportsConditionObj r, Operand o);
virtual bool needs_parens(SupportsConditionObj cond) const override;
ATTACH_AST_OPERATIONS(SupportsOperation)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////////////
// A negation condition (`not CONDITION`).
//////////////////////////////////////////
class SupportsNegation : public SupportsCondition {
private:
ADD_PROPERTY(SupportsConditionObj, condition);
public:
SupportsNegation(SourceSpan pstate, SupportsConditionObj c);
virtual bool needs_parens(SupportsConditionObj cond) const override;
ATTACH_AST_OPERATIONS(SupportsNegation)
ATTACH_CRTP_PERFORM_METHODS()
};
/////////////////////////////////////////////////////
// A declaration condition (e.g. `(feature: value)`).
/////////////////////////////////////////////////////
class SupportsDeclaration : public SupportsCondition {
private:
ADD_PROPERTY(ExpressionObj, feature);
ADD_PROPERTY(ExpressionObj, value);
public:
SupportsDeclaration(SourceSpan pstate, ExpressionObj f, ExpressionObj v);
virtual bool needs_parens(SupportsConditionObj cond) const override;
ATTACH_AST_OPERATIONS(SupportsDeclaration)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////////////////
// An interpolation condition (e.g. `#{$var}`).
///////////////////////////////////////////////
class Supports_Interpolation : public SupportsCondition {
private:
ADD_PROPERTY(ExpressionObj, value);
public:
Supports_Interpolation(SourceSpan pstate, ExpressionObj v);
virtual bool needs_parens(SupportsConditionObj cond) const override;
ATTACH_AST_OPERATIONS(Supports_Interpolation)
ATTACH_CRTP_PERFORM_METHODS()
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,498 @@
#ifndef SASS_AST_VALUES_H
#define SASS_AST_VALUES_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
namespace Sass {
//////////////////////////////////////////////////////////////////////
// Still just an expression, but with a to_string method
//////////////////////////////////////////////////////////////////////
class PreValue : public Expression {
public:
PreValue(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);
ATTACH_VIRTUAL_AST_OPERATIONS(PreValue);
virtual ~PreValue() { }
};
//////////////////////////////////////////////////////////////////////
// base class for values that support operations
//////////////////////////////////////////////////////////////////////
class Value : public PreValue {
public:
Value(SourceSpan pstate, bool d = false, bool e = false, bool i = false, Type ct = NONE);
// Some obects are not meant to be compared
// ToDo: maybe fallback to pointer comparison?
virtual bool operator< (const Expression& rhs) const override = 0;
virtual bool operator== (const Expression& rhs) const override = 0;
// We can give some reasonable implementations by using
// inverst operators on the specialized implementations
virtual bool operator> (const Expression& rhs) const {
return rhs < *this;
}
virtual bool operator!= (const Expression& rhs) const {
return !(*this == rhs);
}
ATTACH_VIRTUAL_AST_OPERATIONS(Value);
};
///////////////////////////////////////////////////////////////////////
// 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<ExpressionObj> {
void adjust_after_pushing(ExpressionObj e) override { is_expanded(false); }
private:
ADD_PROPERTY(enum Sass_Separator, separator)
ADD_PROPERTY(bool, is_arglist)
ADD_PROPERTY(bool, is_bracketed)
ADD_PROPERTY(bool, from_selector)
public:
List(SourceSpan pstate, size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false);
sass::string type() const override { return is_arglist_ ? "arglist" : "list"; }
static sass::string type_name() { return "list"; }
const char* sep_string(bool compressed = false) const {
return separator() == SASS_SPACE ?
" " : (compressed ? "," : ", ");
}
bool is_invisible() const override { return empty() && !is_bracketed(); }
ExpressionObj value_at_index(size_t i);
virtual size_t hash() const override;
virtual size_t size() const;
virtual void set_delayed(bool delayed) override;
virtual bool operator< (const Expression& rhs) const override;
virtual bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(List)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////////////////////////////////////////////////////
// Key value paris.
///////////////////////////////////////////////////////////////////////
class Map : public Value, public Hashed<ExpressionObj, ExpressionObj, Map_Obj> {
void adjust_after_pushing(std::pair<ExpressionObj, ExpressionObj> p) override { is_expanded(false); }
public:
Map(SourceSpan pstate, size_t size = 0);
sass::string type() const override { return "map"; }
static sass::string type_name() { return "map"; }
bool is_invisible() const override { return empty(); }
List_Obj to_list(SourceSpan& pstate);
virtual size_t hash() const override;
virtual bool operator< (const Expression& rhs) const override;
virtual bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Map)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////////////////////////////////////////////
// Binary expressions. Represents logical, relational, and arithmetic
// operations. Templatized to avoid large switch statements and repetitive
// subclassing.
//////////////////////////////////////////////////////////////////////////
class Binary_Expression : public PreValue {
private:
HASH_PROPERTY(Operand, op)
HASH_PROPERTY(ExpressionObj, left)
HASH_PROPERTY(ExpressionObj, right)
mutable size_t hash_;
public:
Binary_Expression(SourceSpan pstate,
Operand op, ExpressionObj lhs, ExpressionObj rhs);
const sass::string type_name();
const sass::string separator();
bool is_left_interpolant(void) const override;
bool is_right_interpolant(void) const override;
bool has_interpolant() const override;
virtual void set_delayed(bool delayed) override;
virtual bool operator< (const Expression& rhs) const override;
virtual bool operator==(const Expression& rhs) const override;
virtual size_t hash() const override;
enum Sass_OP optype() const { return op_.operand; }
ATTACH_AST_OPERATIONS(Binary_Expression)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////
// Function reference.
////////////////////////////////////////////////////
class Function final : public Value {
public:
ADD_PROPERTY(Definition_Obj, definition)
ADD_PROPERTY(bool, is_css)
public:
Function(SourceSpan pstate, Definition_Obj def, bool css);
sass::string type() const override { return "function"; }
static sass::string type_name() { return "function"; }
bool is_invisible() const override { return true; }
sass::string name();
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Function)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////
// Function calls.
//////////////////
class Function_Call final : public PreValue {
HASH_CONSTREF(String_Obj, sname)
HASH_PROPERTY(Arguments_Obj, arguments)
HASH_PROPERTY(Function_Obj, func)
ADD_PROPERTY(bool, via_call)
ADD_PROPERTY(void*, cookie)
mutable size_t hash_;
public:
Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, void* cookie);
Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args, Function_Obj func);
Function_Call(SourceSpan pstate, sass::string n, Arguments_Obj args);
Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, void* cookie);
Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args, Function_Obj func);
Function_Call(SourceSpan pstate, String_Obj n, Arguments_Obj args);
sass::string name() const;
bool is_css();
bool operator==(const Expression& rhs) const override;
size_t hash() const override;
ATTACH_AST_OPERATIONS(Function_Call)
ATTACH_CRTP_PERFORM_METHODS()
};
///////////////////////
// Variable references.
///////////////////////
class Variable final : public PreValue {
ADD_CONSTREF(sass::string, name)
public:
Variable(SourceSpan pstate, sass::string n);
virtual bool operator==(const Expression& rhs) const override;
virtual size_t hash() const override;
ATTACH_AST_OPERATIONS(Variable)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////
// Numbers, percentages, dimensions, and colors.
////////////////////////////////////////////////
class Number final : public Value, public Units {
HASH_PROPERTY(double, value)
ADD_PROPERTY(bool, zero)
mutable size_t hash_;
public:
Number(SourceSpan pstate, double val, sass::string u = "", bool zero = true);
bool zero() { return zero_; }
sass::string type() const override { return "number"; }
static sass::string type_name() { return "number"; }
// cancel out unnecessary units
// result will be in input units
void reduce();
// normalize units to defaults
// needed to compare two numbers
void normalize();
size_t hash() const override;
bool operator< (const Number& rhs) const;
bool operator== (const Number& rhs) const;
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Number)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////
// Colors.
//////////
class Color : public Value {
ADD_CONSTREF(sass::string, disp)
HASH_PROPERTY(double, a)
protected:
mutable size_t hash_;
public:
Color(SourceSpan pstate, double a = 1, const sass::string disp = "");
sass::string type() const override { return "color"; }
static sass::string type_name() { return "color"; }
virtual size_t hash() const override = 0;
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
virtual Color_RGBA* copyAsRGBA() const = 0;
virtual Color_RGBA* toRGBA() = 0;
virtual Color_HSLA* copyAsHSLA() const = 0;
virtual Color_HSLA* toHSLA() = 0;
ATTACH_VIRTUAL_AST_OPERATIONS(Color)
};
//////////
// Colors.
//////////
class Color_RGBA final : public Color {
HASH_PROPERTY(double, r)
HASH_PROPERTY(double, g)
HASH_PROPERTY(double, b)
public:
Color_RGBA(SourceSpan pstate, double r, double g, double b, double a = 1, const sass::string disp = "");
sass::string type() const override { return "color"; }
static sass::string type_name() { return "color"; }
size_t hash() const override;
Color_RGBA* copyAsRGBA() const override;
Color_RGBA* toRGBA() override { return this; }
Color_HSLA* copyAsHSLA() const override;
Color_HSLA* toHSLA() override { return copyAsHSLA(); }
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Color_RGBA)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////
// Colors.
//////////
class Color_HSLA final : public Color {
HASH_PROPERTY(double, h)
HASH_PROPERTY(double, s)
HASH_PROPERTY(double, l)
public:
Color_HSLA(SourceSpan pstate, double h, double s, double l, double a = 1, const sass::string disp = "");
sass::string type() const override { return "color"; }
static sass::string type_name() { return "color"; }
size_t hash() const override;
Color_RGBA* copyAsRGBA() const override;
Color_RGBA* toRGBA() override { return copyAsRGBA(); }
Color_HSLA* copyAsHSLA() const override;
Color_HSLA* toHSLA() override { return this; }
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Color_HSLA)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////
// Errors from Sass_Values.
//////////////////////////////
class Custom_Error final : public Value {
ADD_CONSTREF(sass::string, message)
public:
Custom_Error(SourceSpan pstate, sass::string msg);
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Custom_Error)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////
// Warnings from Sass_Values.
//////////////////////////////
class Custom_Warning final : public Value {
ADD_CONSTREF(sass::string, message)
public:
Custom_Warning(SourceSpan pstate, sass::string msg);
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Custom_Warning)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////
// Booleans.
////////////
class Boolean final : public Value {
HASH_PROPERTY(bool, value)
mutable size_t hash_;
public:
Boolean(SourceSpan pstate, bool val);
operator bool() override { return value_; }
sass::string type() const override { return "bool"; }
static sass::string type_name() { return "bool"; }
size_t hash() const override;
bool is_false() override { return !value_; }
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Boolean)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////////////////////
// Abstract base class for Sass string values. Includes interpolated and
// "flat" strings.
////////////////////////////////////////////////////////////////////////
class String : public Value {
public:
String(SourceSpan pstate, bool delayed = false);
static sass::string type_name() { return "string"; }
virtual ~String() = 0;
virtual void rtrim() = 0;
virtual bool operator<(const Expression& rhs) const override {
return this->to_string() < rhs.to_string();
};
virtual bool operator==(const Expression& rhs) const override {
return this->to_string() == rhs.to_string();
};
ATTACH_VIRTUAL_AST_OPERATIONS(String);
ATTACH_CRTP_PERFORM_METHODS()
};
inline String::~String() { };
///////////////////////////////////////////////////////////////////////
// Interpolated strings. Meant to be reduced to flat strings during the
// evaluation phase.
///////////////////////////////////////////////////////////////////////
class String_Schema final : public String, public Vectorized<PreValueObj> {
ADD_PROPERTY(bool, css)
mutable size_t hash_;
public:
String_Schema(SourceSpan pstate, size_t size = 0, bool css = true);
sass::string type() const override { return "string"; }
static sass::string type_name() { return "string"; }
bool is_left_interpolant(void) const override;
bool is_right_interpolant(void) const override;
bool has_interpolants();
void rtrim() override;
size_t hash() const override;
virtual void set_delayed(bool delayed) override;
bool operator< (const Expression& rhs) const override;
bool operator==(const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(String_Schema)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////
// Flat strings -- the lowest level of raw textual data.
////////////////////////////////////////////////////////
class String_Constant : public String {
ADD_PROPERTY(char, quote_mark)
HASH_CONSTREF(sass::string, value)
protected:
mutable size_t hash_;
public:
String_Constant(SourceSpan pstate, sass::string val, bool css = true);
String_Constant(SourceSpan pstate, const char* beg, bool css = true);
String_Constant(SourceSpan pstate, const char* beg, const char* end, bool css = true);
String_Constant(SourceSpan pstate, const Token& tok, bool css = true);
sass::string type() const override { return "string"; }
static sass::string type_name() { return "string"; }
bool is_invisible() const override;
virtual void rtrim() override;
size_t hash() const override;
bool operator< (const Expression& rhs) const override;
bool operator==(const Expression& rhs) const override;
// quotes are forced on inspection
virtual sass::string inspect() const override;
ATTACH_AST_OPERATIONS(String_Constant)
ATTACH_CRTP_PERFORM_METHODS()
};
////////////////////////////////////////////////////////
// Possibly quoted string (unquote on instantiation)
////////////////////////////////////////////////////////
class String_Quoted final : public String_Constant {
public:
String_Quoted(SourceSpan pstate, sass::string val, char q = 0,
bool keep_utf8_escapes = false, bool skip_unquoting = false,
bool strict_unquoting = true, bool css = true);
bool operator< (const Expression& rhs) const override;
bool operator==(const Expression& rhs) const override;
// quotes are forced on inspection
sass::string inspect() const override;
ATTACH_AST_OPERATIONS(String_Quoted)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////
// The null value.
//////////////////
class Null final : public Value {
public:
Null(SourceSpan pstate);
sass::string type() const override { return "null"; }
static sass::string type_name() { return "null"; }
bool is_invisible() const override { return true; }
operator bool() override { return false; }
bool is_false() override { return true; }
size_t hash() const override;
bool operator< (const Expression& rhs) const override;
bool operator== (const Expression& rhs) const override;
ATTACH_AST_OPERATIONS(Null)
ATTACH_CRTP_PERFORM_METHODS()
};
//////////////////////////////////
// The Parent Reference Expression.
//////////////////////////////////
class Parent_Reference final : public Value {
public:
Parent_Reference(SourceSpan pstate);
sass::string type() const override { return "parent"; }
static sass::string type_name() { return "parent"; }
bool operator< (const Expression& rhs) const override {
return false; // they are always equal
}
bool operator==(const Expression& rhs) const override {
return true; // they are always equal
};
ATTACH_AST_OPERATIONS(Parent_Reference)
ATTACH_CRTP_PERFORM_METHODS()
};
}
#endif

View File

@@ -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 */

View File

@@ -0,0 +1,79 @@
// :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)
{
base64_init_encodestate(&_state);
}
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

View File

@@ -0,0 +1,50 @@
#include "backtrace.hpp"
namespace Sass {
const sass::string traces_to_string(Backtraces traces, sass::string indent) {
sass::ostream ss;
sass::string cwd(File::get_cwd());
bool first = true;
size_t i_beg = traces.size() - 1;
size_t i_end = sass::string::npos;
for (size_t i = i_beg; i != i_end; i --) {
const Backtrace& trace = traces[i];
// make path relative to the current directory
sass::string rel_path(File::abs2rel(trace.pstate.getPath(), cwd, cwd));
// skip functions on error cases (unsure why ruby sass does this)
// if (trace.caller.substr(0, 6) == ", in f") continue;
if (first) {
ss << indent;
ss << "on line ";
ss << trace.pstate.getLine();
ss << ":";
ss << trace.pstate.getColumn();
ss << " of " << rel_path;
// ss << trace.caller;
first = false;
} else {
ss << trace.caller;
ss << std::endl;
ss << indent;
ss << "from line ";
ss << trace.pstate.getLine();
ss << ":";
ss << trace.pstate.getColumn();
ss << " of " << rel_path;
}
}
ss << std::endl;
return ss.str();
}
};

View File

@@ -0,0 +1,29 @@
#ifndef SASS_BACKTRACE_H
#define SASS_BACKTRACE_H
#include <vector>
#include <sstream>
#include "file.hpp"
#include "position.hpp"
namespace Sass {
struct Backtrace {
SourceSpan pstate;
sass::string caller;
Backtrace(SourceSpan pstate, sass::string c = "")
: pstate(pstate),
caller(c)
{ }
};
typedef sass::vector<Backtrace> Backtraces;
const sass::string traces_to_string(Backtraces traces, sass::string indent = "\t");
}
#endif

View File

@@ -0,0 +1,47 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "base64vlq.hpp"
namespace Sass {
sass::string Base64VLQ::encode(const int number) const
{
sass::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;
}

View File

@@ -0,0 +1,30 @@
#ifndef SASS_BASE64VLQ_H
#define SASS_BASE64VLQ_H
#include <string>
namespace Sass {
class Base64VLQ {
public:
sass::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

View File

@@ -0,0 +1,312 @@
#include "sass.hpp"
#include "bind.hpp"
#include "ast.hpp"
#include "backtrace.hpp"
#include "context.hpp"
#include "expand.hpp"
#include "eval.hpp"
#include <map>
#include <iostream>
#include <sstream>
namespace Sass {
void bind(sass::string type, sass::string name, Parameters_Obj ps, Arguments_Obj as, Env* env, Eval* eval, Backtraces& traces)
{
sass::string callee(type + " " + name);
std::map<sass::string, Parameter_Obj> param_map;
List_Obj varargs = SASS_MEMORY_NEW(List, as->pstate());
varargs->is_arglist(true); // enable keyword size handling
for (size_t i = 0, L = as->length(); i < L; ++i) {
if (auto str = Cast<String_Quoted>((*as)[i]->value())) {
// force optional quotes (only if needed)
if (str->quote_mark()) {
str->quote_mark('*');
}
}
}
// 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_Obj p = ps->at(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_Obj a = as->at(ia);
if (ip >= LP) {
// skip empty rest arguments
if (a->is_rest_argument()) {
if (List_Obj l = Cast<List>(a->value())) {
if (l->length() == 0) {
++ ia; continue;
}
}
}
sass::ostream msg;
msg << "wrong number of arguments (" << LA << " for " << LP << ")";
msg << " for `" << name << "'";
return error(msg.str(), as->pstate(), traces);
}
Parameter_Obj p = ps->at(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_Obj rest = Cast<List>(a->value())) {
// create a new list object for wrapped items
List* arglist = SASS_MEMORY_NEW(List,
p->pstate(),
0,
rest->separator(),
true);
// wrap each item from list as an argument
for (ExpressionObj item : rest->elements()) {
if (Argument_Obj arg = Cast<Argument>(item)) {
arglist->append(SASS_MEMORY_COPY(arg)); // copy
} else {
arglist->append(SASS_MEMORY_NEW(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(List, p->pstate(), 0, SASS_COMMA, true);
env->local_frame()[p->name()] = arglist;
Map_Obj argmap = Cast<Map>(a->value());
for (auto key : argmap->keys()) {
if (String_Constant_Obj str = Cast<String_Constant>(key)) {
sass::string param = unquote(str->value());
arglist->append(SASS_MEMORY_NEW(Argument,
key->pstate(),
argmap->at(key),
"$" + param,
false,
false));
} else {
traces.push_back(Backtrace(key->pstate()));
throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a);
}
}
} else {
// create a new list object for wrapped items
List_Obj arglist = SASS_MEMORY_NEW(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_Obj ls = Cast<List>(a->value());
// skip any list completely if empty
if (ls && ls->empty() && a->is_rest_argument()) continue;
ExpressionObj value = a->value();
if (Argument_Obj arg = Cast<Argument>(value)) {
arglist->append(arg);
}
// check if we have rest argument
else if (a->is_rest_argument()) {
// preserve the list separator from rest args
if (List_Obj rest = Cast<List>(a->value())) {
arglist->separator(rest->separator());
for (size_t i = 0, L = rest->length(); i < L; ++i) {
ExpressionObj obj = rest->value_at_index(i);
arglist->append(SASS_MEMORY_NEW(Argument,
obj->pstate(),
obj,
"",
false,
false));
}
}
// no more arguments
break;
}
// wrap all other value types into Argument
else {
arglist->append(SASS_MEMORY_NEW(Argument,
a->pstate(),
a->value(),
a->name(),
false,
false));
}
}
// assign new arglist to environment
env->local_frame()[p->name()] = arglist;
}
// consumed parameter
++ip;
// no more parameters
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_Obj arglist = Cast<List>(a->value());
if (!arglist) {
if (ExpressionObj arg = Cast<Expression>(a->value())) {
arglist = SASS_MEMORY_NEW(List, a->pstate(), 1);
arglist->append(arg);
}
}
// empty rest arg - treat all args as default values
if (!arglist || !arglist->length()) {
break;
} else {
if (arglist->length() > LP - ip && !ps->has_rest_parameter()) {
size_t arg_count = (arglist->length() + LA - 1);
sass::ostream 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());
while (arglist->length() > LP - ip) {
arglist->elements().erase(arglist->elements().end() - 1);
}
}
}
// otherwise move one of the rest args into the param, converting to argument if necessary
ExpressionObj obj = arglist->at(0);
if (!(a = Cast<Argument>(obj))) {
Expression* a_to_convert = obj;
a = SASS_MEMORY_NEW(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_Obj argmap = Cast<Map>(a->value());
for (auto key : argmap->keys()) {
String_Constant* val = Cast<String_Constant>(key);
if (val == NULL) {
traces.push_back(Backtrace(key->pstate()));
throw Exception::InvalidVarKwdType(key->pstate(), traces, key->inspect(), a);
}
sass::string param = "$" + unquote(val->value());
if (!param_map.count(param)) {
sass::ostream msg;
msg << callee << " has no parameter named " << param;
error(msg.str(), a->pstate(), traces);
}
env->local_frame()[param] = argmap->at(key);
}
++ia;
continue;
} else {
++ia;
}
if (a->name().empty()) {
if (env->has_local(p->name())) {
sass::ostream msg;
msg << "parameter " << p->name()
<< " provided more than once in call to " << callee;
error(msg.str(), a->pstate(), traces);
}
// 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())) {
if (ps->has_rest_parameter()) {
varargs->append(a);
} else {
sass::ostream msg;
msg << callee << " has no parameter named " << a->name();
error(msg.str(), a->pstate(), traces);
}
}
if (param_map[a->name()]) {
if (param_map[a->name()]->is_rest_parameter()) {
sass::ostream msg;
msg << "argument " << a->name() << " of " << callee
<< "cannot be used as named argument";
error(msg.str(), a->pstate(), traces);
}
}
if (env->has_local(a->name())) {
sass::ostream msg;
msg << "parameter " << p->name()
<< "provided more than once in call to " << callee;
error(msg.str(), a->pstate(), traces);
}
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) {
Parameter_Obj leftover = ps->at(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()] = varargs;
}
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
throw Exception::MissingArgument(as->pstate(), traces, name, leftover->name(), type);
}
}
}
return;
}
}

View File

@@ -0,0 +1,15 @@
#ifndef SASS_BIND_H
#define SASS_BIND_H
#include <string>
#include "backtrace.hpp"
#include "environment.hpp"
#include "ast_fwd_decl.hpp"
namespace Sass {
void bind(sass::string type, sass::string name, Parameters_Obj, Arguments_Obj, Env*, Eval*, Backtraces& traces);
}
#endif

View File

@@ -0,0 +1,64 @@
#include "ast.hpp"
#include "units.hpp"
#include "position.hpp"
#include "backtrace.hpp"
#include "sass/values.h"
#include "ast_fwd_decl.hpp"
#include "error_handling.hpp"
namespace Sass {
Value* c2ast(union Sass_Value* v, Backtraces traces, SourceSpan pstate)
{
using std::strlen;
using std::strcpy;
Value* e = NULL;
switch (sass_value_get_tag(v)) {
case SASS_BOOLEAN: {
e = SASS_MEMORY_NEW(Boolean, pstate, !!sass_boolean_get_value(v));
} break;
case SASS_NUMBER: {
e = SASS_MEMORY_NEW(Number, pstate, sass_number_get_value(v), sass_number_get_unit(v));
} break;
case SASS_COLOR: {
e = SASS_MEMORY_NEW(Color_RGBA, 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(String_Quoted, pstate, sass_string_get_value(v));
else {
e = SASS_MEMORY_NEW(String_Constant, pstate, sass_string_get_value(v));
}
} break;
case SASS_LIST: {
List* l = SASS_MEMORY_NEW(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->append(c2ast(sass_list_get_value(v, i), traces, pstate));
}
l->is_bracketed(sass_list_get_is_bracketed(v));
e = l;
} break;
case SASS_MAP: {
Map* m = SASS_MEMORY_NEW(Map, pstate);
for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {
*m << std::make_pair(
c2ast(sass_map_get_key(v, i), traces, pstate),
c2ast(sass_map_get_value(v, i), traces, pstate));
}
e = m;
} break;
case SASS_NULL: {
e = SASS_MEMORY_NEW(Null, pstate);
} break;
case SASS_ERROR: {
error("Error in C function: " + sass::string(sass_error_get_message(v)), pstate, traces);
} break;
case SASS_WARNING: {
error("Warning in C function: " + sass::string(sass_warning_get_message(v)), pstate, traces);
} break;
default: break;
}
return e;
}
}

View File

@@ -0,0 +1,14 @@
#ifndef SASS_C2AST_H
#define SASS_C2AST_H
#include "position.hpp"
#include "backtrace.hpp"
#include "ast_fwd_decl.hpp"
namespace Sass {
Value* c2ast(union Sass_Value* v, Backtraces traces, SourceSpan pstate);
}
#endif

View File

@@ -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

View File

@@ -0,0 +1,106 @@
/*
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;
/* fall through */
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;
/* fall through */
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);
}

View File

@@ -0,0 +1,393 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "check_nesting.hpp"
namespace Sass {
CheckNesting::CheckNesting()
: parents(sass::vector<Statement*>()),
traces(sass::vector<Backtrace>()),
parent(0), current_mixin_definition(0)
{ }
void error(AST_Node* node, Backtraces traces, sass::string msg) {
traces.push_back(Backtrace(node->pstate()));
throw Exception::InvalidSass(node->pstate(), traces, msg);
}
Statement* CheckNesting::visit_children(Statement* parent)
{
Statement* old_parent = this->parent;
if (AtRootRule* root = Cast<AtRootRule>(parent)) {
sass::vector<Statement*> old_parents = this->parents;
sass::vector<Statement*> new_parents;
for (size_t i = 0, L = this->parents.size(); i < L; i++) {
Statement* p = this->parents.at(i);
if (!root->exclude_node(p)) {
new_parents.push_back(p);
}
}
this->parents = new_parents;
for (size_t i = this->parents.size(); i > 0; i--) {
Statement* p = 0;
Statement* gp = 0;
if (i > 0) p = this->parents.at(i - 1);
if (i > 1) gp = this->parents.at(i - 2);
if (!this->is_transparent_parent(p, gp)) {
this->parent = p;
break;
}
}
AtRootRule* ar = Cast<AtRootRule>(parent);
Block* ret = ar->block();
if (ret != NULL) {
for (auto n : ret->elements()) {
n->perform(this);
}
}
this->parent = old_parent;
this->parents = old_parents;
return ret;
}
if (!this->is_transparent_parent(parent, old_parent)) {
this->parent = parent;
}
this->parents.push_back(parent);
Block* b = Cast<Block>(parent);
if (Trace* trace = Cast<Trace>(parent)) {
if (trace->type() == 'i') {
this->traces.push_back(Backtrace(trace->pstate()));
}
}
if (!b) {
if (ParentStatement* bb = Cast<ParentStatement>(parent)) {
b = bb->block();
}
}
if (b) {
for (auto n : b->elements()) {
n->perform(this);
}
}
this->parent = old_parent;
this->parents.pop_back();
if (Trace* trace = Cast<Trace>(parent)) {
if (trace->type() == 'i') {
this->traces.pop_back();
}
}
return b;
}
Statement* CheckNesting::operator()(Block* b)
{
return this->visit_children(b);
}
Statement* CheckNesting::operator()(Definition* n)
{
if (!this->should_visit(n)) return NULL;
if (!is_mixin(n)) {
visit_children(n);
return n;
}
Definition* old_mixin_definition = this->current_mixin_definition;
this->current_mixin_definition = n;
visit_children(n);
this->current_mixin_definition = old_mixin_definition;
return n;
}
Statement* CheckNesting::operator()(If* i)
{
this->visit_children(i);
if (Block* b = Cast<Block>(i->alternative())) {
for (auto n : b->elements()) n->perform(this);
}
return i;
}
bool CheckNesting::should_visit(Statement* node)
{
if (!this->parent) return true;
if (Cast<Content>(node))
{ this->invalid_content_parent(this->parent, node); }
if (is_charset(node))
{ this->invalid_charset_parent(this->parent, node); }
if (Cast<ExtendRule>(node))
{ this->invalid_extend_parent(this->parent, node); }
// if (Cast<Import>(node))
// { this->invalid_import_parent(this->parent); }
if (this->is_mixin(node))
{ this->invalid_mixin_definition_parent(this->parent, node); }
if (this->is_function(node))
{ this->invalid_function_parent(this->parent, node); }
if (this->is_function(this->parent))
{ this->invalid_function_child(node); }
if (Declaration* d = Cast<Declaration>(node))
{
this->invalid_prop_parent(this->parent, node);
this->invalid_value_child(d->value());
}
if (Cast<Declaration>(this->parent))
{ this->invalid_prop_child(node); }
if (Cast<Return>(node))
{ this->invalid_return_parent(this->parent, node); }
return true;
}
void CheckNesting::invalid_content_parent(Statement* parent, AST_Node* node)
{
if (!this->current_mixin_definition) {
error(node, traces, "@content may only be used within a mixin.");
}
}
void CheckNesting::invalid_charset_parent(Statement* parent, AST_Node* node)
{
if (!(
is_root_node(parent)
)) {
error(node, traces, "@charset may only be used at the root of a document.");
}
}
void CheckNesting::invalid_extend_parent(Statement* parent, AST_Node* node)
{
if (!(
Cast<StyleRule>(parent) ||
Cast<Mixin_Call>(parent) ||
is_mixin(parent)
)) {
error(node, traces, "Extend directives may only be used within rules.");
}
}
// void CheckNesting::invalid_import_parent(Statement* parent, AST_Node* node)
// {
// for (auto pp : this->parents) {
// if (
// Cast<EachRule>(pp) ||
// Cast<ForRule>(pp) ||
// Cast<If>(pp) ||
// Cast<WhileRule>(pp) ||
// Cast<Trace>(pp) ||
// Cast<Mixin_Call>(pp) ||
// is_mixin(pp)
// ) {
// error(node, traces, "Import directives may not be defined within control directives or other mixins.");
// }
// }
// if (this->is_root_node(parent)) {
// return;
// }
// if (false/*n.css_import?*/) {
// error(node, traces, "CSS import directives may only be used at the root of a document.");
// }
// }
void CheckNesting::invalid_mixin_definition_parent(Statement* parent, AST_Node* node)
{
for (Statement* pp : this->parents) {
if (
Cast<EachRule>(pp) ||
Cast<ForRule>(pp) ||
Cast<If>(pp) ||
Cast<WhileRule>(pp) ||
Cast<Trace>(pp) ||
Cast<Mixin_Call>(pp) ||
is_mixin(pp)
) {
error(node, traces, "Mixins may not be defined within control directives or other mixins.");
}
}
}
void CheckNesting::invalid_function_parent(Statement* parent, AST_Node* node)
{
for (Statement* pp : this->parents) {
if (
Cast<EachRule>(pp) ||
Cast<ForRule>(pp) ||
Cast<If>(pp) ||
Cast<WhileRule>(pp) ||
Cast<Trace>(pp) ||
Cast<Mixin_Call>(pp) ||
is_mixin(pp)
) {
error(node, traces, "Functions may not be defined within control directives or other mixins.");
}
}
}
void CheckNesting::invalid_function_child(Statement* child)
{
if (!(
Cast<EachRule>(child) ||
Cast<ForRule>(child) ||
Cast<If>(child) ||
Cast<WhileRule>(child) ||
Cast<Trace>(child) ||
Cast<Comment>(child) ||
Cast<DebugRule>(child) ||
Cast<Return>(child) ||
Cast<Variable>(child) ||
// Ruby Sass doesn't distinguish variables and assignments
Cast<Assignment>(child) ||
Cast<WarningRule>(child) ||
Cast<ErrorRule>(child)
)) {
error(child, traces, "Functions can only contain variable declarations and control directives.");
}
}
void CheckNesting::invalid_prop_child(Statement* child)
{
if (!(
Cast<EachRule>(child) ||
Cast<ForRule>(child) ||
Cast<If>(child) ||
Cast<WhileRule>(child) ||
Cast<Trace>(child) ||
Cast<Comment>(child) ||
Cast<Declaration>(child) ||
Cast<Mixin_Call>(child)
)) {
error(child, traces, "Illegal nesting: Only properties may be nested beneath properties.");
}
}
void CheckNesting::invalid_prop_parent(Statement* parent, AST_Node* node)
{
if (!(
is_mixin(parent) ||
is_directive_node(parent) ||
Cast<StyleRule>(parent) ||
Cast<Keyframe_Rule>(parent) ||
Cast<Declaration>(parent) ||
Cast<Mixin_Call>(parent)
)) {
error(node, traces, "Properties are only allowed within rules, directives, mixin includes, or other properties.");
}
}
void CheckNesting::invalid_value_child(AST_Node* d)
{
if (Map* m = Cast<Map>(d)) {
traces.push_back(Backtrace(m->pstate()));
throw Exception::InvalidValue(traces, *m);
}
if (Number* n = Cast<Number>(d)) {
if (!n->is_valid_css_unit()) {
traces.push_back(Backtrace(n->pstate()));
throw Exception::InvalidValue(traces, *n);
}
}
// error(dbg + " isn't a valid CSS value.", m->pstate(),);
}
void CheckNesting::invalid_return_parent(Statement* parent, AST_Node* node)
{
if (!this->is_function(parent)) {
error(node, traces, "@return may only be used within a function.");
}
}
bool CheckNesting::is_transparent_parent(Statement* parent, Statement* grandparent)
{
bool parent_bubbles = parent && parent->bubbles();
bool valid_bubble_node = parent_bubbles &&
!is_root_node(grandparent) &&
!is_at_root_node(grandparent);
return Cast<Import>(parent) ||
Cast<EachRule>(parent) ||
Cast<ForRule>(parent) ||
Cast<If>(parent) ||
Cast<WhileRule>(parent) ||
Cast<Trace>(parent) ||
valid_bubble_node;
}
bool CheckNesting::is_charset(Statement* n)
{
AtRule* d = Cast<AtRule>(n);
return d && d->keyword() == "charset";
}
bool CheckNesting::is_mixin(Statement* n)
{
Definition* def = Cast<Definition>(n);
return def && def->type() == Definition::MIXIN;
}
bool CheckNesting::is_function(Statement* n)
{
Definition* def = Cast<Definition>(n);
return def && def->type() == Definition::FUNCTION;
}
bool CheckNesting::is_root_node(Statement* n)
{
if (Cast<StyleRule>(n)) return false;
Block* b = Cast<Block>(n);
return b && b->is_root();
}
bool CheckNesting::is_at_root_node(Statement* n)
{
return Cast<AtRootRule>(n) != NULL;
}
bool CheckNesting::is_directive_node(Statement* n)
{
return Cast<AtRule>(n) ||
Cast<Import>(n) ||
Cast<MediaRule>(n) ||
Cast<CssMediaRule>(n) ||
Cast<SupportsRule>(n);
}
}

View File

@@ -0,0 +1,70 @@
#ifndef SASS_CHECK_NESTING_H
#define SASS_CHECK_NESTING_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "operation.hpp"
#include <vector>
namespace Sass {
class CheckNesting : public Operation_CRTP<Statement*, CheckNesting> {
sass::vector<Statement*> parents;
Backtraces traces;
Statement* parent;
Definition* current_mixin_definition;
Statement* before(Statement*);
Statement* visit_children(Statement*);
public:
CheckNesting();
~CheckNesting() { }
Statement* operator()(Block*);
Statement* operator()(Definition*);
Statement* operator()(If*);
template <typename U>
Statement* fallback(U x) {
Statement* s = Cast<Statement>(x);
if (s && this->should_visit(s)) {
Block* b1 = Cast<Block>(s);
ParentStatement* b2 = Cast<ParentStatement>(s);
if (b1 || b2) return visit_children(s);
}
return s;
}
private:
void invalid_content_parent(Statement*, AST_Node*);
void invalid_charset_parent(Statement*, AST_Node*);
void invalid_extend_parent(Statement*, AST_Node*);
// void invalid_import_parent(Statement*);
void invalid_mixin_definition_parent(Statement*, AST_Node*);
void invalid_function_parent(Statement*, AST_Node*);
void invalid_function_child(Statement*);
void invalid_prop_child(Statement*);
void invalid_prop_parent(Statement*, AST_Node*);
void invalid_return_parent(Statement*, AST_Node*);
void invalid_value_child(AST_Node*);
bool is_transparent_parent(Statement*, Statement*);
bool should_visit(Statement*);
bool is_charset(Statement*);
bool is_mixin(Statement*);
bool is_function(Statement*);
bool is_root_node(Statement*);
bool is_at_root_node(Statement*);
bool is_directive_node(Statement*);
};
}
#endif

View File

@@ -0,0 +1,652 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "color_maps.hpp"
#include "util_string.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 SourceSpan color_table("[COLOR TABLE]");
const Color_RGBA aliceblue(color_table, 240, 248, 255, 1);
const Color_RGBA antiquewhite(color_table, 250, 235, 215, 1);
const Color_RGBA cyan(color_table, 0, 255, 255, 1);
const Color_RGBA aqua(color_table, 0, 255, 255, 1);
const Color_RGBA aquamarine(color_table, 127, 255, 212, 1);
const Color_RGBA azure(color_table, 240, 255, 255, 1);
const Color_RGBA beige(color_table, 245, 245, 220, 1);
const Color_RGBA bisque(color_table, 255, 228, 196, 1);
const Color_RGBA black(color_table, 0, 0, 0, 1);
const Color_RGBA blanchedalmond(color_table, 255, 235, 205, 1);
const Color_RGBA blue(color_table, 0, 0, 255, 1);
const Color_RGBA blueviolet(color_table, 138, 43, 226, 1);
const Color_RGBA brown(color_table, 165, 42, 42, 1);
const Color_RGBA burlywood(color_table, 222, 184, 135, 1);
const Color_RGBA cadetblue(color_table, 95, 158, 160, 1);
const Color_RGBA chartreuse(color_table, 127, 255, 0, 1);
const Color_RGBA chocolate(color_table, 210, 105, 30, 1);
const Color_RGBA coral(color_table, 255, 127, 80, 1);
const Color_RGBA cornflowerblue(color_table, 100, 149, 237, 1);
const Color_RGBA cornsilk(color_table, 255, 248, 220, 1);
const Color_RGBA crimson(color_table, 220, 20, 60, 1);
const Color_RGBA darkblue(color_table, 0, 0, 139, 1);
const Color_RGBA darkcyan(color_table, 0, 139, 139, 1);
const Color_RGBA darkgoldenrod(color_table, 184, 134, 11, 1);
const Color_RGBA darkgray(color_table, 169, 169, 169, 1);
const Color_RGBA darkgrey(color_table, 169, 169, 169, 1);
const Color_RGBA darkgreen(color_table, 0, 100, 0, 1);
const Color_RGBA darkkhaki(color_table, 189, 183, 107, 1);
const Color_RGBA darkmagenta(color_table, 139, 0, 139, 1);
const Color_RGBA darkolivegreen(color_table, 85, 107, 47, 1);
const Color_RGBA darkorange(color_table, 255, 140, 0, 1);
const Color_RGBA darkorchid(color_table, 153, 50, 204, 1);
const Color_RGBA darkred(color_table, 139, 0, 0, 1);
const Color_RGBA darksalmon(color_table, 233, 150, 122, 1);
const Color_RGBA darkseagreen(color_table, 143, 188, 143, 1);
const Color_RGBA darkslateblue(color_table, 72, 61, 139, 1);
const Color_RGBA darkslategray(color_table, 47, 79, 79, 1);
const Color_RGBA darkslategrey(color_table, 47, 79, 79, 1);
const Color_RGBA darkturquoise(color_table, 0, 206, 209, 1);
const Color_RGBA darkviolet(color_table, 148, 0, 211, 1);
const Color_RGBA deeppink(color_table, 255, 20, 147, 1);
const Color_RGBA deepskyblue(color_table, 0, 191, 255, 1);
const Color_RGBA dimgray(color_table, 105, 105, 105, 1);
const Color_RGBA dimgrey(color_table, 105, 105, 105, 1);
const Color_RGBA dodgerblue(color_table, 30, 144, 255, 1);
const Color_RGBA firebrick(color_table, 178, 34, 34, 1);
const Color_RGBA floralwhite(color_table, 255, 250, 240, 1);
const Color_RGBA forestgreen(color_table, 34, 139, 34, 1);
const Color_RGBA magenta(color_table, 255, 0, 255, 1);
const Color_RGBA fuchsia(color_table, 255, 0, 255, 1);
const Color_RGBA gainsboro(color_table, 220, 220, 220, 1);
const Color_RGBA ghostwhite(color_table, 248, 248, 255, 1);
const Color_RGBA gold(color_table, 255, 215, 0, 1);
const Color_RGBA goldenrod(color_table, 218, 165, 32, 1);
const Color_RGBA gray(color_table, 128, 128, 128, 1);
const Color_RGBA grey(color_table, 128, 128, 128, 1);
const Color_RGBA green(color_table, 0, 128, 0, 1);
const Color_RGBA greenyellow(color_table, 173, 255, 47, 1);
const Color_RGBA honeydew(color_table, 240, 255, 240, 1);
const Color_RGBA hotpink(color_table, 255, 105, 180, 1);
const Color_RGBA indianred(color_table, 205, 92, 92, 1);
const Color_RGBA indigo(color_table, 75, 0, 130, 1);
const Color_RGBA ivory(color_table, 255, 255, 240, 1);
const Color_RGBA khaki(color_table, 240, 230, 140, 1);
const Color_RGBA lavender(color_table, 230, 230, 250, 1);
const Color_RGBA lavenderblush(color_table, 255, 240, 245, 1);
const Color_RGBA lawngreen(color_table, 124, 252, 0, 1);
const Color_RGBA lemonchiffon(color_table, 255, 250, 205, 1);
const Color_RGBA lightblue(color_table, 173, 216, 230, 1);
const Color_RGBA lightcoral(color_table, 240, 128, 128, 1);
const Color_RGBA lightcyan(color_table, 224, 255, 255, 1);
const Color_RGBA lightgoldenrodyellow(color_table, 250, 250, 210, 1);
const Color_RGBA lightgray(color_table, 211, 211, 211, 1);
const Color_RGBA lightgrey(color_table, 211, 211, 211, 1);
const Color_RGBA lightgreen(color_table, 144, 238, 144, 1);
const Color_RGBA lightpink(color_table, 255, 182, 193, 1);
const Color_RGBA lightsalmon(color_table, 255, 160, 122, 1);
const Color_RGBA lightseagreen(color_table, 32, 178, 170, 1);
const Color_RGBA lightskyblue(color_table, 135, 206, 250, 1);
const Color_RGBA lightslategray(color_table, 119, 136, 153, 1);
const Color_RGBA lightslategrey(color_table, 119, 136, 153, 1);
const Color_RGBA lightsteelblue(color_table, 176, 196, 222, 1);
const Color_RGBA lightyellow(color_table, 255, 255, 224, 1);
const Color_RGBA lime(color_table, 0, 255, 0, 1);
const Color_RGBA limegreen(color_table, 50, 205, 50, 1);
const Color_RGBA linen(color_table, 250, 240, 230, 1);
const Color_RGBA maroon(color_table, 128, 0, 0, 1);
const Color_RGBA mediumaquamarine(color_table, 102, 205, 170, 1);
const Color_RGBA mediumblue(color_table, 0, 0, 205, 1);
const Color_RGBA mediumorchid(color_table, 186, 85, 211, 1);
const Color_RGBA mediumpurple(color_table, 147, 112, 219, 1);
const Color_RGBA mediumseagreen(color_table, 60, 179, 113, 1);
const Color_RGBA mediumslateblue(color_table, 123, 104, 238, 1);
const Color_RGBA mediumspringgreen(color_table, 0, 250, 154, 1);
const Color_RGBA mediumturquoise(color_table, 72, 209, 204, 1);
const Color_RGBA mediumvioletred(color_table, 199, 21, 133, 1);
const Color_RGBA midnightblue(color_table, 25, 25, 112, 1);
const Color_RGBA mintcream(color_table, 245, 255, 250, 1);
const Color_RGBA mistyrose(color_table, 255, 228, 225, 1);
const Color_RGBA moccasin(color_table, 255, 228, 181, 1);
const Color_RGBA navajowhite(color_table, 255, 222, 173, 1);
const Color_RGBA navy(color_table, 0, 0, 128, 1);
const Color_RGBA oldlace(color_table, 253, 245, 230, 1);
const Color_RGBA olive(color_table, 128, 128, 0, 1);
const Color_RGBA olivedrab(color_table, 107, 142, 35, 1);
const Color_RGBA orange(color_table, 255, 165, 0, 1);
const Color_RGBA orangered(color_table, 255, 69, 0, 1);
const Color_RGBA orchid(color_table, 218, 112, 214, 1);
const Color_RGBA palegoldenrod(color_table, 238, 232, 170, 1);
const Color_RGBA palegreen(color_table, 152, 251, 152, 1);
const Color_RGBA paleturquoise(color_table, 175, 238, 238, 1);
const Color_RGBA palevioletred(color_table, 219, 112, 147, 1);
const Color_RGBA papayawhip(color_table, 255, 239, 213, 1);
const Color_RGBA peachpuff(color_table, 255, 218, 185, 1);
const Color_RGBA peru(color_table, 205, 133, 63, 1);
const Color_RGBA pink(color_table, 255, 192, 203, 1);
const Color_RGBA plum(color_table, 221, 160, 221, 1);
const Color_RGBA powderblue(color_table, 176, 224, 230, 1);
const Color_RGBA purple(color_table, 128, 0, 128, 1);
const Color_RGBA red(color_table, 255, 0, 0, 1);
const Color_RGBA rosybrown(color_table, 188, 143, 143, 1);
const Color_RGBA royalblue(color_table, 65, 105, 225, 1);
const Color_RGBA saddlebrown(color_table, 139, 69, 19, 1);
const Color_RGBA salmon(color_table, 250, 128, 114, 1);
const Color_RGBA sandybrown(color_table, 244, 164, 96, 1);
const Color_RGBA seagreen(color_table, 46, 139, 87, 1);
const Color_RGBA seashell(color_table, 255, 245, 238, 1);
const Color_RGBA sienna(color_table, 160, 82, 45, 1);
const Color_RGBA silver(color_table, 192, 192, 192, 1);
const Color_RGBA skyblue(color_table, 135, 206, 235, 1);
const Color_RGBA slateblue(color_table, 106, 90, 205, 1);
const Color_RGBA slategray(color_table, 112, 128, 144, 1);
const Color_RGBA slategrey(color_table, 112, 128, 144, 1);
const Color_RGBA snow(color_table, 255, 250, 250, 1);
const Color_RGBA springgreen(color_table, 0, 255, 127, 1);
const Color_RGBA steelblue(color_table, 70, 130, 180, 1);
const Color_RGBA tan(color_table, 210, 180, 140, 1);
const Color_RGBA teal(color_table, 0, 128, 128, 1);
const Color_RGBA thistle(color_table, 216, 191, 216, 1);
const Color_RGBA tomato(color_table, 255, 99, 71, 1);
const Color_RGBA turquoise(color_table, 64, 224, 208, 1);
const Color_RGBA violet(color_table, 238, 130, 238, 1);
const Color_RGBA wheat(color_table, 245, 222, 179, 1);
const Color_RGBA white(color_table, 255, 255, 255, 1);
const Color_RGBA whitesmoke(color_table, 245, 245, 245, 1);
const Color_RGBA yellow(color_table, 255, 255, 0, 1);
const Color_RGBA yellowgreen(color_table, 154, 205, 50, 1);
const Color_RGBA rebeccapurple(color_table, 102, 51, 153, 1);
const Color_RGBA transparent(color_table, 0, 0, 0, 0);
}
static const auto* const colors_to_names = new std::unordered_map<int, const char*> {
{ 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 }
};
static const auto *const names_to_colors = new std::unordered_map<sass::string, const Color_RGBA*>
{
{ 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_RGBA* name_to_color(const char* key)
{
return name_to_color(sass::string(key));
}
const Color_RGBA* name_to_color(const sass::string& key)
{
// case insensitive lookup. See #2462
sass::string lower = key;
Util::ascii_str_tolower(&lower);
auto p = names_to_colors->find(lower);
if (p != names_to_colors->end()) {
return p->second;
}
return nullptr;
}
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 nullptr;
}
const char* color_to_name(const double key)
{
return color_to_name((int)key);
}
const char* color_to_name(const Color_RGBA& c)
{
double key = c.r() * 0x10000
+ c.g() * 0x100
+ c.b();
return color_to_name(key);
}
}

View File

@@ -0,0 +1,323 @@
#ifndef SASS_COLOR_MAPS_H
#define SASS_COLOR_MAPS_H
#include <map>
#include "ast.hpp"
namespace Sass {
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_RGBA aliceblue;
extern const Color_RGBA antiquewhite;
extern const Color_RGBA cyan;
extern const Color_RGBA aqua;
extern const Color_RGBA aquamarine;
extern const Color_RGBA azure;
extern const Color_RGBA beige;
extern const Color_RGBA bisque;
extern const Color_RGBA black;
extern const Color_RGBA blanchedalmond;
extern const Color_RGBA blue;
extern const Color_RGBA blueviolet;
extern const Color_RGBA brown;
extern const Color_RGBA burlywood;
extern const Color_RGBA cadetblue;
extern const Color_RGBA chartreuse;
extern const Color_RGBA chocolate;
extern const Color_RGBA coral;
extern const Color_RGBA cornflowerblue;
extern const Color_RGBA cornsilk;
extern const Color_RGBA crimson;
extern const Color_RGBA darkblue;
extern const Color_RGBA darkcyan;
extern const Color_RGBA darkgoldenrod;
extern const Color_RGBA darkgray;
extern const Color_RGBA darkgrey;
extern const Color_RGBA darkgreen;
extern const Color_RGBA darkkhaki;
extern const Color_RGBA darkmagenta;
extern const Color_RGBA darkolivegreen;
extern const Color_RGBA darkorange;
extern const Color_RGBA darkorchid;
extern const Color_RGBA darkred;
extern const Color_RGBA darksalmon;
extern const Color_RGBA darkseagreen;
extern const Color_RGBA darkslateblue;
extern const Color_RGBA darkslategray;
extern const Color_RGBA darkslategrey;
extern const Color_RGBA darkturquoise;
extern const Color_RGBA darkviolet;
extern const Color_RGBA deeppink;
extern const Color_RGBA deepskyblue;
extern const Color_RGBA dimgray;
extern const Color_RGBA dimgrey;
extern const Color_RGBA dodgerblue;
extern const Color_RGBA firebrick;
extern const Color_RGBA floralwhite;
extern const Color_RGBA forestgreen;
extern const Color_RGBA magenta;
extern const Color_RGBA fuchsia;
extern const Color_RGBA gainsboro;
extern const Color_RGBA ghostwhite;
extern const Color_RGBA gold;
extern const Color_RGBA goldenrod;
extern const Color_RGBA gray;
extern const Color_RGBA grey;
extern const Color_RGBA green;
extern const Color_RGBA greenyellow;
extern const Color_RGBA honeydew;
extern const Color_RGBA hotpink;
extern const Color_RGBA indianred;
extern const Color_RGBA indigo;
extern const Color_RGBA ivory;
extern const Color_RGBA khaki;
extern const Color_RGBA lavender;
extern const Color_RGBA lavenderblush;
extern const Color_RGBA lawngreen;
extern const Color_RGBA lemonchiffon;
extern const Color_RGBA lightblue;
extern const Color_RGBA lightcoral;
extern const Color_RGBA lightcyan;
extern const Color_RGBA lightgoldenrodyellow;
extern const Color_RGBA lightgray;
extern const Color_RGBA lightgrey;
extern const Color_RGBA lightgreen;
extern const Color_RGBA lightpink;
extern const Color_RGBA lightsalmon;
extern const Color_RGBA lightseagreen;
extern const Color_RGBA lightskyblue;
extern const Color_RGBA lightslategray;
extern const Color_RGBA lightslategrey;
extern const Color_RGBA lightsteelblue;
extern const Color_RGBA lightyellow;
extern const Color_RGBA lime;
extern const Color_RGBA limegreen;
extern const Color_RGBA linen;
extern const Color_RGBA maroon;
extern const Color_RGBA mediumaquamarine;
extern const Color_RGBA mediumblue;
extern const Color_RGBA mediumorchid;
extern const Color_RGBA mediumpurple;
extern const Color_RGBA mediumseagreen;
extern const Color_RGBA mediumslateblue;
extern const Color_RGBA mediumspringgreen;
extern const Color_RGBA mediumturquoise;
extern const Color_RGBA mediumvioletred;
extern const Color_RGBA midnightblue;
extern const Color_RGBA mintcream;
extern const Color_RGBA mistyrose;
extern const Color_RGBA moccasin;
extern const Color_RGBA navajowhite;
extern const Color_RGBA navy;
extern const Color_RGBA oldlace;
extern const Color_RGBA olive;
extern const Color_RGBA olivedrab;
extern const Color_RGBA orange;
extern const Color_RGBA orangered;
extern const Color_RGBA orchid;
extern const Color_RGBA palegoldenrod;
extern const Color_RGBA palegreen;
extern const Color_RGBA paleturquoise;
extern const Color_RGBA palevioletred;
extern const Color_RGBA papayawhip;
extern const Color_RGBA peachpuff;
extern const Color_RGBA peru;
extern const Color_RGBA pink;
extern const Color_RGBA plum;
extern const Color_RGBA powderblue;
extern const Color_RGBA purple;
extern const Color_RGBA red;
extern const Color_RGBA rosybrown;
extern const Color_RGBA royalblue;
extern const Color_RGBA saddlebrown;
extern const Color_RGBA salmon;
extern const Color_RGBA sandybrown;
extern const Color_RGBA seagreen;
extern const Color_RGBA seashell;
extern const Color_RGBA sienna;
extern const Color_RGBA silver;
extern const Color_RGBA skyblue;
extern const Color_RGBA slateblue;
extern const Color_RGBA slategray;
extern const Color_RGBA slategrey;
extern const Color_RGBA snow;
extern const Color_RGBA springgreen;
extern const Color_RGBA steelblue;
extern const Color_RGBA tan;
extern const Color_RGBA teal;
extern const Color_RGBA thistle;
extern const Color_RGBA tomato;
extern const Color_RGBA turquoise;
extern const Color_RGBA violet;
extern const Color_RGBA wheat;
extern const Color_RGBA white;
extern const Color_RGBA whitesmoke;
extern const Color_RGBA yellow;
extern const Color_RGBA yellowgreen;
extern const Color_RGBA rebeccapurple;
extern const Color_RGBA transparent;
}
const Color_RGBA* name_to_color(const char*);
const Color_RGBA* name_to_color(const sass::string&);
const char* color_to_name(const int);
const char* color_to_name(const Color_RGBA&);
const char* color_to_name(const double);
}
#endif

View File

@@ -0,0 +1,199 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#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 = 0;
extern const unsigned long Specificity_Element = 1;
extern const unsigned long Specificity_Base = 1000;
extern const unsigned long Specificity_Class = 1000;
extern const unsigned long Specificity_Attr = 1000;
extern const unsigned long Specificity_Pseudo = 1000;
extern const unsigned long Specificity_ID = 1000000;
extern const int UnificationOrder_Element = 1;
extern const int UnificationOrder_Id = 2;
extern const int UnificationOrder_Class = 2;
extern const int UnificationOrder_Attribute = 3;
extern const int UnificationOrder_PseudoClass = 4;
extern const int UnificationOrder_Wrapped = 5;
extern const int UnificationOrder_PseudoElement = 6;
extern const int UnificationOrder_Placeholder = 7;
// 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 of_kwd[] = "of";
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_fn_kwd[] = "rgb(";
extern const char url_fn_kwd[] = "url(";
extern const char url_kwd[] = "url";
// extern const char url_prefix_fn_kwd[] = "url-prefix(";
extern const char important_kwd[] = "important";
extern const char pseudo_not_fn_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_fn_kwd[] = "calc";
extern const char almost_any_value_class[] = "\"'#!;{}";
// 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";
// definition keywords
extern const char using_kwd[] = "using";
// 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 op_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[] = "#%&";
extern const char selector_combinator_child[] = ">";
extern const char selector_combinator_general[] = "~";
extern const char selector_combinator_adjacent[] = "+";
// 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 };
}
}

View File

@@ -0,0 +1,200 @@
#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_Element;
extern const unsigned long Specificity_Base;
extern const unsigned long Specificity_Class;
extern const unsigned long Specificity_Attr;
extern const unsigned long Specificity_Pseudo;
extern const unsigned long Specificity_ID;
// Selector unification order;
extern const int UnificationOrder_Element;
extern const int UnificationOrder_Id;
extern const int UnificationOrder_Class;
extern const int UnificationOrder_Attribute;
extern const int UnificationOrder_PseudoClass;
extern const int UnificationOrder_Wrapped;
extern const int UnificationOrder_PseudoElement;
extern const int UnificationOrder_Placeholder;
// 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 of_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_fn_kwd[];
extern const char url_fn_kwd[];
extern const char url_kwd[];
// extern const char url_prefix_fn_kwd[];
extern const char important_kwd[];
extern const char pseudo_not_fn_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_fn_kwd[];
// char classes for "regular expressions"
extern const char almost_any_value_class[];
// 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[];
// definition keywords
extern const char using_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 op_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[];
// constants for selector combinators
extern const char selector_combinator_child[];
extern const char selector_combinator_general[];
extern const char selector_combinator_adjacent[];
// 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

View File

@@ -0,0 +1,863 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "remove_placeholders.hpp"
#include "sass_functions.hpp"
#include "check_nesting.hpp"
#include "fn_selectors.hpp"
#include "fn_strings.hpp"
#include "fn_numbers.hpp"
#include "fn_colors.hpp"
#include "fn_miscs.hpp"
#include "fn_lists.hpp"
#include "fn_maps.hpp"
#include "context.hpp"
#include "expand.hpp"
#include "parser.hpp"
#include "cssize.hpp"
#include "source.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 sass::string safe_input(const char* in_path)
{
if (in_path == nullptr || in_path[0] == '\0') return "stdin";
return in_path;
}
static sass::string safe_output(const char* out_path, sass::string input_path)
{
if (out_path == nullptr || out_path[0] == '\0') {
if (input_path.empty()) return "stdout";
return input_path.substr(0, input_path.find_last_of(".")) + ".css";
}
return out_path;
}
Context::Context(struct Sass_Context& c_ctx)
: CWD(File::get_cwd()),
c_options(c_ctx),
entry_path(""),
head_imports(0),
plugins(),
emitter(c_options),
ast_gc(),
strings(),
resources(),
sheets(),
import_stack(),
callee_stack(),
traces(),
extender(Extender::NORMAL, traces),
c_compiler(NULL),
c_headers (sass::vector<Sass_Importer_Entry>()),
c_importers (sass::vector<Sass_Importer_Entry>()),
c_functions (sass::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, "")))
{
// Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.
// If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.
// include_paths.push_back(CWD);
// collect more paths from different options
collect_include_paths(c_options.include_path);
collect_include_paths(c_options.include_paths);
collect_plugin_paths(c_options.plugin_path);
collect_plugin_paths(c_options.plugin_paths);
// load plugins and register custom behaviors
for(auto plug : plugin_paths) plugins.load_plugins(plug);
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 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();
sheets.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) {
sass::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);
}
sass::string path(beg);
if (!path.empty()) {
if (*path.rbegin() != '/') path += '/';
include_paths.push_back(path);
}
}
}
void Context::collect_include_paths(string_list* paths_array)
{
while (paths_array)
{
collect_include_paths(paths_array->string);
paths_array = paths_array->next;
}
}
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) {
sass::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);
}
sass::string path(beg);
if (!path.empty()) {
if (*path.rbegin() != '/') path += '/';
plugin_paths.push_back(path);
}
}
}
void Context::collect_plugin_paths(string_list* paths_array)
{
while (paths_array)
{
collect_plugin_paths(paths_array->string);
paths_array = paths_array->next;
}
}
// resolve the imp_path in base_path or include_paths
// looks for alternatives and returns a list from one directory
sass::vector<Include> Context::find_includes(const Importer& import)
{
// make sure we resolve against an absolute path
sass::string base_path(rel2abs(import.base_path));
// first try to resolve the load path relative to the base path
sass::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
sass::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;
SourceFileObj source = SASS_MEMORY_NEW(SourceFile,
inc.abs_path.c_str(), contents, idx);
// create the initial parser state from resource
SourceSpan pstate(source);
// check existing import stack for possible recursion
for (size_t i = 0; i < import_stack.size() - 2; ++i) {
auto parent = import_stack[i];
if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
sass::string cwd(File::get_cwd());
// make path relative to the current directory
sass::string stack("An @import loop has been found:");
for (size_t n = 1; n < i + 2; ++n) {
stack += "\n " + sass::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) +
" imports " + sass::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd));
}
// implement error throw directly until we
// decided how to handle full stack traces
throw Exception::InvalidSyntax(pstate, traces, stack);
// error(stack, prstate ? *prstate : pstate, import_stack);
}
}
// create a parser instance from the given c_str buffer
Parser p(source, *this, traces);
// do not yet dispose these buffers
sass_import_take_source(import);
sass_import_take_srcmap(import);
// then parse the root block
Block_Obj 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 sass::string, StyleSheet>
ast_pair(inc.abs_path, { res, root });
// register resulting resource
sheets.insert(ast_pair);
}
// 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, SourceSpan& prstate)
{
traces.push_back(Backtrace(prstate));
register_resource(inc, res);
traces.pop_back();
}
// Add a new import to the context (called from `import_url`)
Include Context::load_import(const Importer& imp, SourceSpan pstate)
{
// search for valid imports (ie. partials) on the filesystem
// this may return more than one valid result (ambiguous imp_path)
const sass::vector<Include> resolved(find_includes(imp));
// error nicely on ambiguous imp_path
if (resolved.size() > 1) {
sass::ostream 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, traces);
}
// process the resolved entry
else if (resolved.size() == 1) {
bool use_cache = c_importers.size() == 0;
// use cache for the resource loading
if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0];
// try to read the content of the resolved file entry
// the memory buffer returned must be freed by us!
if (char* contents = read_file(resolved[0].abs_path)) {
// register the newly resolved file resource
register_resource(resolved[0], { contents, 0 }, pstate);
// return resolved entry
return resolved[0];
}
}
// nothing found
return { imp, "" };
}
void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) {
SourceSpan pstate(imp->pstate());
sass::string imp_path(unquote(load_path));
sass::string protocol("file");
using namespace Prelexer;
if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
protocol = sass::string(imp_path.c_str(), proto - 3);
// if (protocol.compare("file") && true) { }
}
// add urls (protocol other than file) and urls without protocol to `urls` member
// ToDo: if ctx_path is already a file resource, we should not add it here?
if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
imp->urls().push_back(SASS_MEMORY_NEW(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(String_Constant, pstate, unquote(load_path));
Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);
Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);
loc_args->append(loc_arg);
Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("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 + ".", pstate, traces);
}
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 sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::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_ent : importers) {
// int priority = sass_importer_get_priority(importer);
Sass_Importer_Fn fn = sass_importer_get_function(importer_ent);
// skip importer if it returns NULL
if (Sass_Import_List includes =
fn(load_path.c_str(), importer_ent, 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
sass::string uniq_path = load_path;
if (!only_one && count) {
sass::ostream 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_ent = *it_includes;
char* source = sass_import_take_source(include_ent);
char* srcmap = sass_import_take_srcmap(include_ent);
size_t line = sass_import_get_error_line(include_ent);
size_t column = sass_import_get_error_column(include_ent);
const char *abs_path = sass_import_get_abs_path(include_ent);
// 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_ent)) {
if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate);
if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces);
else { error(err_message, { pstate.source, { line, column } }, traces); }
}
// 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)
sass::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 }, pstate);
}
// 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&, sass::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_Obj 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 embedded 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_copy_c_string(emitted.buffer.c_str());
}
void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate)
{
// create a custom import to resolve headers
Import_Obj imp = SASS_MEMORY_NEW(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->append(imp);
// process all other resources (add Import_Stub nodes)
for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
}
}
Block_Obj File_Context::parse()
{
// check if entry file is given
if (input_path.empty()) return {};
// create absolute path from input filename
// ToDo: this should be resolved via custom importers
sass::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 std::runtime_error(
"File to read not found or unreadable: "
+ std::string(input_path.c_str()));
// 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_Obj Data_Context::parse()
{
// check if source string is given
if (!source_c_str) return {};
// 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
sass::string abs_path(rel2abs(entry_path));
char* abs_path_c_str = sass_copy_c_string(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_Obj Context::compile()
{
// abort if there is no data
if (resources.size() == 0) return {};
// get root block from the first style sheet
Block_Obj root = sheets.at(entry_path).root;
// abort on invalid root
if (root.isNull()) return {};
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
// create crtp visitor objects
Expand expand(*this, &global);
Cssize cssize(*this);
CheckNesting check_nesting;
// check nesting in all files
for (auto sheet : sheets) {
auto styles = sheet.second;
check_nesting(styles.root);
}
// expand and eval the tree
root = expand(root);
Extension unsatisfied;
// check that all extends were used
if (extender.checkForUnsatisfiedExtends(unsatisfied)) {
throw Exception::UnsatisfiedExtend(traces, unsatisfied);
}
// check nesting
check_nesting(root);
// merge and bubble certain rules
root = cssize(root);
// clean up by removing empty placeholders
// ToDo: maybe we can do this somewhere else?
Remove_Placeholders remove_placeholders;
root->perform(&remove_placeholders);
// return processed tree
return root;
}
// EO compile
sass::string Context::format_embedded_source_map()
{
sass::string map = emitter.render_srcmap(*this);
sass::istream is( map.c_str() );
sass::ostream buffer;
base64::encoder E;
E.encode(is, buffer);
sass::string url = "data:application/json;base64," + buffer.str();
url.erase(url.size() - 1);
return "/*# sourceMappingURL=" + url + " */";
}
sass::string Context::format_source_mapping_url(const sass::string& file)
{
sass::string url = abs2rel(file, output_path, CWD);
return "/*# sourceMappingURL=" + url + " */";
}
char* Context::render_srcmap()
{
if (source_map_file == "") return 0;
sass::string map = emitter.render_srcmap(*this);
return sass_copy_c_string(map.c_str());
}
// for data context we want to start after "stdin"
// we probably always want to skip the header includes?
sass::vector<sass::string> Context::get_included_files(bool skip, size_t headers)
{
// create a copy of the vector for manipulations
sass::vector<sass::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);
sass::ostream ss;
ss << def->name() << "[f]" << arity;
def->environment(env);
(*env)[ss.str()] = def;
}
void register_overload_stub(Context& ctx, sass::string name, Env* env)
{
Definition* stub = SASS_MEMORY_NEW(Definition,
SourceSpan{ "[built-in function]" },
nullptr,
name,
Parameters_Obj{},
nullptr,
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);
register_function(ctx, is_bracketed_sig, is_bracketed, 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);
register_function(ctx, content_exists_sig, content_exists, env);
register_function(ctx, get_function_sig, get_function, 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;
}
}

View File

@@ -0,0 +1,140 @@
#ifndef SASS_CONTEXT_H
#define SASS_CONTEXT_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#define BUFFERSIZE 255
#include "b64/encode.h"
#include "sass_context.hpp"
#include "stylesheet.hpp"
#include "plugins.hpp"
#include "output.hpp"
namespace Sass {
class Context {
public:
void import_url (Import* imp, sass::string load_path, const sass::string& ctx_path);
bool call_headers(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp)
{ return call_loader(load_path, ctx_path, pstate, imp, c_headers, false); };
bool call_importers(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp)
{ return call_loader(load_path, ctx_path, pstate, imp, c_importers, true); };
private:
bool call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector<Sass_Importer_Entry> importers, bool only_one = true);
public:
const sass::string CWD;
struct Sass_Options& c_options;
sass::string entry_path;
size_t head_imports;
Plugins plugins;
Output emitter;
// generic ast node garbage container
// used to avoid possible circular refs
CallStack ast_gc;
// resources add under our control
// these are guaranteed to be freed
sass::vector<char*> strings;
sass::vector<Resource> resources;
std::map<const sass::string, StyleSheet> sheets;
ImporterStack import_stack;
sass::vector<Sass_Callee> callee_stack;
sass::vector<Backtrace> traces;
Extender extender;
struct Sass_Compiler* c_compiler;
// absolute paths to includes
sass::vector<sass::string> included_files;
// relative includes for sourcemap
sass::vector<sass::string> srcmap_links;
// vectors above have same size
sass::vector<sass::string> plugin_paths; // relative paths to load plugins
sass::vector<sass::string> include_paths; // lookup paths for includes
void apply_custom_headers(Block_Obj root, const char* path, SourceSpan pstate);
sass::vector<Sass_Importer_Entry> c_headers;
sass::vector<Sass_Importer_Entry> c_importers;
sass::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 sass::string indent; // String to be used for indentation
const sass::string linefeed; // String to be used for line feeds
const sass::string input_path; // for relative paths in src-map
const sass::string output_path; // for relative paths to the output
const sass::string source_map_file; // path to source map file (enables feature)
const sass::string source_map_root; // path for sourceRoot property (pass-through)
virtual ~Context();
Context(struct Sass_Context&);
virtual Block_Obj parse() = 0;
virtual Block_Obj compile();
virtual char* render(Block_Obj root);
virtual char* render_srcmap();
void register_resource(const Include&, const Resource&);
void register_resource(const Include&, const Resource&, SourceSpan&);
sass::vector<Include> find_includes(const Importer& import);
Include load_import(const Importer&, SourceSpan pstate);
Sass_Output_Style output_style() { return c_options.output_style; };
sass::vector<sass::string> get_included_files(bool skip = false, size_t headers = 0);
private:
void collect_plugin_paths(const char* paths_str);
void collect_plugin_paths(string_list* paths_array);
void collect_include_paths(const char* paths_str);
void collect_include_paths(string_list* paths_array);
sass::string format_embedded_source_map();
sass::string format_source_mapping_url(const sass::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(sass::string name, Env* env);
public:
const sass::string& cwd() { return CWD; };
};
class File_Context : public Context {
public:
File_Context(struct Sass_File_Context& ctx)
: Context(ctx)
{ }
virtual ~File_Context();
virtual Block_Obj 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_Obj parse();
};
}
#endif

View File

@@ -0,0 +1,521 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <iostream>
#include <typeinfo>
#include <vector>
#include "cssize.hpp"
#include "context.hpp"
namespace Sass {
Cssize::Cssize(Context& ctx)
: traces(ctx.traces),
block_stack(BlockStack()),
p_stack(sass::vector<Statement*>())
{ }
Statement* Cssize::parent()
{
return p_stack.size() ? p_stack.back() : block_stack.front();
}
Block* Cssize::operator()(Block* b)
{
Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root());
// bb->tabs(b->tabs());
block_stack.push_back(bb);
append_block(b, bb);
block_stack.pop_back();
return bb.detach();
}
Statement* Cssize::operator()(Trace* t)
{
traces.push_back(Backtrace(t->pstate()));
auto result = t->block()->perform(this);
traces.pop_back();
return result;
}
Statement* Cssize::operator()(Declaration* d)
{
String_Obj property = Cast<String>(d->property());
if (Declaration* dd = Cast<Declaration>(parent())) {
String_Obj parent_property = Cast<String>(dd->property());
property = SASS_MEMORY_NEW(String_Constant,
d->property()->pstate(),
parent_property->to_string() + "-" + property->to_string());
if (!dd->value()) {
d->tabs(dd->tabs() + 1);
}
}
Declaration_Obj dd = SASS_MEMORY_NEW(Declaration,
d->pstate(),
property,
d->value(),
d->is_important(),
d->is_custom_property());
dd->is_indented(d->is_indented());
dd->tabs(d->tabs());
p_stack.push_back(dd);
Block_Obj bb = d->block() ? operator()(d->block()) : NULL;
p_stack.pop_back();
if (bb && bb->length()) {
if (dd->value() && !dd->value()->is_invisible()) {
bb->unshift(dd);
}
return bb.detach();
}
else if (dd->value() && !dd->value()->is_invisible()) {
return dd.detach();
}
return 0;
}
Statement* Cssize::operator()(AtRule* r)
{
if (!r->block() || !r->block()->length()) return r;
if (parent()->statement_type() == Statement::RULESET)
{
return r->is_keyframes() ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r);
}
p_stack.push_back(r);
AtRuleObj rr = SASS_MEMORY_NEW(AtRule,
r->pstate(),
r->keyword(),
r->selector(),
r->block() ? operator()(r->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_Obj s = r->block()->at(i);
if (s->statement_type() != Statement::BUBBLE) directive_exists = true;
else {
Bubble_Obj s_obj = Cast<Bubble>(s);
s = s_obj->node();
if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;
else directive_exists = (Cast<AtRule>(s)->keyword() == rr->keyword());
}
}
Block* result = SASS_MEMORY_NEW(Block, rr->pstate());
if (!(directive_exists || rr->is_keyframes()))
{
AtRule* empty_node = Cast<AtRule>(rr);
empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate()));
result->append(empty_node);
}
Block_Obj db = rr->block();
if (db.isNull()) db = SASS_MEMORY_NEW(Block, rr->pstate());
Block_Obj ss = debubble(db, rr);
for (size_t i = 0, L = ss->length(); i < L; ++i) {
result->append(ss->at(i));
}
return result;
}
Statement* Cssize::operator()(Keyframe_Rule* r)
{
if (!r->block() || !r->block()->length()) return r;
Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule,
r->pstate(),
operator()(r->block()));
if (!r->name().isNull()) rr->name(r->name());
return debubble(rr->block(), rr);
}
Statement* Cssize::operator()(StyleRule* r)
{
p_stack.push_back(r);
// this can return a string schema
// string schema is not a statement!
// r->block() is already a string schema
// and that is coming from propset expand
Block* bb = operator()(r->block());
// this should protect us (at least a bit) from our mess
// fixing this properly is harder that it should be ...
if (Cast<Statement>(bb) == NULL) {
error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
}
StyleRuleObj rr = SASS_MEMORY_NEW(StyleRule,
r->pstate(),
r->selector(),
bb);
rr->is_root(r->is_root());
// rr->tabs(r->block()->tabs());
p_stack.pop_back();
if (!rr->block()) {
error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
}
Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());
Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());
for (size_t i = 0, L = rr->block()->length(); i < L; i++)
{
Statement* s = rr->block()->at(i);
if (bubblable(s)) rules->append(s);
if (!bubblable(s)) props->append(s);
}
if (props->length())
{
Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate());
pb->concat(props);
rr->block(pb);
for (size_t i = 0, L = rules->length(); i < L; i++)
{
Statement* stm = rules->at(i);
stm->tabs(stm->tabs() + 1);
}
rules->unshift(rr);
}
Block* ptr = rules;
rules = debubble(rules);
void* lp = ptr;
void* rp = rules;
if (lp != rp) {
Block_Obj obj = ptr;
}
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()(CssMediaRule* m)
{
if (parent()->statement_type() == Statement::RULESET)
{
return bubble(m);
}
if (parent()->statement_type() == Statement::MEDIA)
{
return SASS_MEMORY_NEW(Bubble, m->pstate(), m);
}
p_stack.push_back(m);
CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());
mm->concat(m->elements());
mm->block(operator()(m->block()));
mm->tabs(m->tabs());
p_stack.pop_back();
return debubble(mm->block(), mm);
}
Statement* Cssize::operator()(SupportsRule* m)
{
if (!m->block()->length())
{ return m; }
if (parent()->statement_type() == Statement::RULESET)
{ return bubble(m); }
p_stack.push_back(m);
SupportsRuleObj mm = SASS_MEMORY_NEW(SupportsRule,
m->pstate(),
m->condition(),
operator()(m->block()));
mm->tabs(m->tabs());
p_stack.pop_back();
return debubble(mm->block(), mm);
}
Statement* Cssize::operator()(AtRootRule* 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 && m->block())
{
Block* bb = operator()(m->block());
for (size_t i = 0, L = bb->length(); i < L; ++i) {
// (bb->elements())[i]->tabs(m->tabs());
Statement_Obj stm = bb->at(i);
if (bubblable(stm)) stm->tabs(stm->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(Bubble, m->pstate(), m);
}
return bubble(m);
}
Statement* Cssize::bubble(AtRule* m)
{
Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());
ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent()));
new_rule->block(bb);
new_rule->tabs(this->parent()->tabs());
new_rule->block()->concat(m->block());
Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate());
wrapper_block->append(new_rule);
AtRuleObj mm = SASS_MEMORY_NEW(AtRule,
m->pstate(),
m->keyword(),
m->selector(),
wrapper_block);
if (m->value()) mm->value(m->value());
Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
return bubble;
}
Statement* Cssize::bubble(AtRootRule* m)
{
if (!m || !m->block()) return NULL;
Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());
ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent()));
Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
if (new_rule) {
new_rule->block(bb);
new_rule->tabs(this->parent()->tabs());
new_rule->block()->concat(m->block());
wrapper_block->append(new_rule);
}
AtRootRule* mm = SASS_MEMORY_NEW(AtRootRule,
m->pstate(),
wrapper_block,
m->expression());
Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
return bubble;
}
Statement* Cssize::bubble(SupportsRule* m)
{
StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent()));
Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());
StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule,
parent->pstate(),
parent->selector(),
bb);
new_rule->tabs(parent->tabs());
new_rule->block()->concat(m->block());
Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
wrapper_block->append(new_rule);
SupportsRule* mm = SASS_MEMORY_NEW(SupportsRule,
m->pstate(),
m->condition(),
wrapper_block);
mm->tabs(m->tabs());
Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
return bubble;
}
Statement* Cssize::bubble(CssMediaRule* m)
{
StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent()));
Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());
StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule,
parent->pstate(),
parent->selector(),
bb);
new_rule->tabs(parent->tabs());
new_rule->block()->concat(m->block());
Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
wrapper_block->append(new_rule);
CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule,
m->pstate(),
wrapper_block);
mm->concat(m->elements());
mm->tabs(m->tabs());
return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
}
bool Cssize::bubblable(Statement* s)
{
return Cast<StyleRule>(s) || (s && s->bubbles());
}
Block* Cssize::flatten(const Block* b)
{
Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());
for (size_t i = 0, L = b->length(); i < L; ++i) {
Statement* ss = b->at(i);
if (const Block* bb = Cast<Block>(ss)) {
Block_Obj bs = flatten(bb);
for (size_t j = 0, K = bs->length(); j < K; ++j) {
result->append(bs->at(j));
}
}
else {
result->append(ss);
}
}
return result;
}
sass::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block* b)
{
sass::vector<std::pair<bool, Block_Obj>> results;
for (size_t i = 0, L = b->length(); i < L; ++i) {
Statement_Obj value = b->at(i);
bool key = Cast<Bubble>(value) != NULL;
if (!results.empty() && results.back().first == key)
{
Block_Obj wrapper_block = results.back().second;
wrapper_block->append(value);
}
else
{
Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());
wrapper_block->append(value);
results.push_back(std::make_pair(key, wrapper_block));
}
}
return results;
}
Block* Cssize::debubble(Block* children, Statement* parent)
{
ParentStatementObj previous_parent;
sass::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children);
Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate());
for (size_t i = 0, L = baz.size(); i < L; ++i) {
bool is_bubble = baz[i].first;
Block_Obj slice = baz[i].second;
if (!is_bubble) {
if (!parent) {
result->append(slice);
}
else if (previous_parent) {
previous_parent->block()->concat(slice);
}
else {
previous_parent = SASS_MEMORY_COPY(parent);
previous_parent->block(slice);
previous_parent->tabs(parent->tabs());
result->append(previous_parent);
}
continue;
}
for (size_t j = 0, K = slice->length(); j < K; ++j)
{
Statement_Obj ss;
Statement_Obj stm = slice->at(j);
// this has to go now here (too bad)
Bubble_Obj node = Cast<Bubble>(stm);
CssMediaRule* rule1 = NULL;
CssMediaRule* rule2 = NULL;
if (parent) rule1 = Cast<CssMediaRule>(parent);
if (node) rule2 = Cast<CssMediaRule>(node->node());
if (rule1 || rule2) {
ss = node->node();
}
ss = node->node();
if (!ss) {
continue;
}
ss->tabs(ss->tabs() + node->tabs());
ss->group_end(node->group_end());
Block_Obj bb = SASS_MEMORY_NEW(Block,
children->pstate(),
children->length(),
children->is_root());
auto evaled = ss->perform(this);
if (evaled) bb->append(evaled);
Block_Obj wrapper_block = SASS_MEMORY_NEW(Block,
children->pstate(),
children->length(),
children->is_root());
Block* wrapper = flatten(bb);
wrapper_block->append(wrapper);
if (wrapper->length()) {
previous_parent = {};
}
if (wrapper_block) {
result->append(wrapper_block);
}
}
}
return flatten(result);
}
void Cssize::append_block(Block* b, Block* cur)
{
for (size_t i = 0, L = b->length(); i < L; ++i) {
Statement_Obj ith = b->at(i)->perform(this);
if (Block_Obj bb = Cast<Block>(ith)) {
for (size_t j = 0, K = bb->length(); j < K; ++j) {
cur->append(bb->at(j));
}
}
else if (ith) {
cur->append(ith);
}
}
}
}

View File

@@ -0,0 +1,71 @@
#ifndef SASS_CSSIZE_H
#define SASS_CSSIZE_H
#include "ast.hpp"
#include "context.hpp"
#include "operation.hpp"
#include "environment.hpp"
namespace Sass {
struct Backtrace;
class Cssize : public Operation_CRTP<Statement*, Cssize> {
Backtraces& traces;
BlockStack block_stack;
sass::vector<Statement*> p_stack;
public:
Cssize(Context&);
~Cssize() { }
Block* operator()(Block*);
Statement* operator()(StyleRule*);
// Statement* operator()(Bubble*);
Statement* operator()(CssMediaRule*);
Statement* operator()(SupportsRule*);
Statement* operator()(AtRootRule*);
Statement* operator()(AtRule*);
Statement* operator()(Keyframe_Rule*);
Statement* operator()(Trace*);
Statement* operator()(Declaration*);
// Statement* operator()(Assignment*);
// Statement* operator()(Import*);
// Statement* operator()(Import_Stub*);
// Statement* operator()(WarningRule*);
// Statement* operator()(Error*);
// Statement* operator()(Comment*);
// Statement* operator()(If*);
// Statement* operator()(ForRule*);
// Statement* operator()(EachRule*);
// Statement* operator()(WhileRule*);
// Statement* operator()(Return*);
// Statement* operator()(ExtendRule*);
// Statement* operator()(Definition*);
// Statement* operator()(Mixin_Call*);
// Statement* operator()(Content*);
Statement* operator()(Null*);
Statement* parent();
sass::vector<std::pair<bool, Block_Obj>> slice_by_bubble(Block*);
Statement* bubble(AtRule*);
Statement* bubble(AtRootRule*);
Statement* bubble(CssMediaRule*);
Statement* bubble(SupportsRule*);
Block* debubble(Block* children, Statement* parent = 0);
Block* flatten(const Block*);
bool bubblable(Statement*);
// generic fallback
template <typename U>
Statement* fallback(U x)
{ return Cast<Statement>(x); }
void append_block(Block*, Block*);
};
}
#endif

View File

@@ -0,0 +1,199 @@
#ifndef SASS_DART_HELPERS_H
#define SASS_DART_HELPERS_H
#include <vector>
#include <utility>
#include <iterator>
#include <functional>
namespace Sass {
// ##########################################################################
// Flatten `vector<vector<T>>` to `vector<T>`
// ##########################################################################
template <class T>
T flatten(const sass::vector<T>& all)
{
T flattened;
for (const auto& sub : all) {
std::copy(std::begin(sub), std::end(sub),
std::back_inserter(flattened));
}
return flattened;
}
// ##########################################################################
// Expands each element of this Iterable into zero or more elements.
// Calls a function on every element and ads all results to flat array
// ##########################################################################
// Equivalent to dart `cnt.any`
// Pass additional closure variables to `fn`
template <class T, class U, typename ...Args>
T expand(const T& cnt, U fn, Args... args) {
T flattened;
for (const auto& sub : cnt) {
auto rv = fn(sub, args...);
flattened.insert(flattened.end(),
rv.begin(), rv.end());
}
return flattened;
}
// ##########################################################################
// ##########################################################################
template <class T>
T flattenInner(const sass::vector<T>& vec)
{
T outer;
for (const auto& sub : vec) {
outer.emplace_back(std::move(flatten(sub)));
}
return outer;
}
// EO flattenInner
// ##########################################################################
// Equivalent to dart `cnt.any`
// Pass additional closure variables to `fn`
// ##########################################################################
template <class T, class U, typename ...Args>
bool hasAny(const T& cnt, U fn, Args... args) {
for (const auto& sub : cnt) {
if (fn(sub, args...)) {
return true;
}
}
return false;
}
// EO hasAny
// ##########################################################################
// Equivalent to dart `cnt.take(len).any`
// Pass additional closure variables to `fn`
// ##########################################################################
template <class T, class U, typename ...Args>
bool hasSubAny(const T& cnt, size_t len, U fn, Args... args) {
for (size_t i = 0; i < len; i++) {
if (fn(cnt[i], args...)) {
return true;
}
}
return false;
}
// ##########################################################################
// Default predicate for lcs algorithm
// ##########################################################################
template <class T>
inline bool lcsIdentityCmp(const T& X, const T& Y, T& result)
{
// Assert equality
if (!ObjEqualityFn(X, Y)) {
return false;
}
// Store in reference
result = X;
// Return success
return true;
}
// EO lcsIdentityCmp
// ##########################################################################
// Longest common subsequence with predicate
// ##########################################################################
template <class T>
sass::vector<T> lcs(
const sass::vector<T>& X, const sass::vector<T>& Y,
bool(*select)(const T&, const T&, T&) = lcsIdentityCmp<T>)
{
std::size_t m = X.size(), mm = X.size() + 1;
std::size_t n = Y.size(), nn = Y.size() + 1;
if (m == 0) return {};
if (n == 0) return {};
// MSVC does not support variable-length arrays
// To circumvent, allocate one array on the heap
// Then use a macro to access via double index
// e.g. `size_t L[m][n]` is supported by gcc
size_t* len = new size_t[mm * nn + 1];
bool* acc = new bool[mm * nn + 1];
T* res = new T[mm * nn + 1];
#define LEN(x, y) len[(x) * nn + (y)]
#define ACC(x, y) acc[(x) * nn + (y)]
#define RES(x, y) res[(x) * nn + (y)]
/* Following steps build L[m+1][n+1] in bottom up fashion. Note
that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] */
for (size_t i = 0; i <= m; i++) {
for (size_t j = 0; j <= n; j++) {
if (i == 0 || j == 0)
LEN(i, j) = 0;
else {
ACC(i - 1, j - 1) = select(X[i - 1], Y[j - 1], RES(i - 1, j - 1));
if (ACC(i - 1, j - 1))
LEN(i, j) = LEN(i - 1, j - 1) + 1;
else
LEN(i, j) = std::max(LEN(i - 1, j), LEN(i, j - 1));
}
}
}
// Following code is used to print LCS
sass::vector<T> lcs;
std::size_t index = LEN(m, n);
lcs.reserve(index);
// Start from the right-most-bottom-most corner
// and one by one store objects in lcs[]
std::size_t i = m, j = n;
while (i > 0 && j > 0) {
// If current objects in X[] and Y are same,
// then current object is part of LCS
if (ACC(i - 1, j - 1))
{
// Put the stored object in result
// Note: we push instead of unshift
// Note: reverse the vector later
// ToDo: is deque more performant?
lcs.push_back(RES(i - 1, j - 1));
// reduce values of i, j and index
i -= 1; j -= 1; index -= 1;
}
// If not same, then find the larger of two and
// go in the direction of larger value
else if (LEN(i - 1, j) > LEN(i, j - 1)) {
i--;
}
else {
j--;
}
}
// reverse now as we used push_back
std::reverse(lcs.begin(), lcs.end());
// Delete temp memory on heap
delete[] len;
delete[] acc;
delete[] res;
#undef LEN
#undef ACC
#undef RES
return lcs;
}
// EO lcs
// ##########################################################################
// ##########################################################################
}
#endif

View File

@@ -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

View File

@@ -0,0 +1,963 @@
#ifndef SASS_DEBUGGER_H
#define SASS_DEBUGGER_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <queue>
#include <vector>
#include <string>
#include <sstream>
#include "ast.hpp"
#include "ast_fwd_decl.hpp"
#include "extension.hpp"
#include "ordered_map.hpp"
using namespace Sass;
inline void debug_ast(AST_Node* node, sass::string ind = "", Env* env = 0);
inline sass::string debug_vec(const AST_Node* node) {
if (node == NULL) return "null";
else return node->to_string();
}
inline sass::string debug_dude(sass::vector<sass::vector<int>> vec) {
sass::sstream out;
out << "{";
bool joinOut = false;
for (auto ct : vec) {
if (joinOut) out << ", ";
joinOut = true;
out << "{";
bool joinIn = false;
for (auto nr : ct) {
if (joinIn) out << ", ";
joinIn = true;
out << nr;
}
out << "}";
}
out << "}";
return out.str();
}
inline sass::string debug_vec(sass::string& str) {
return str;
}
inline sass::string debug_vec(Extension& ext) {
sass::sstream out;
out << debug_vec(ext.extender);
out << " {@extend ";
out << debug_vec(ext.target);
if (ext.isOptional) {
out << " !optional";
}
out << "}";
return out.str();
}
template <class T>
inline sass::string debug_vec(sass::vector<T> vec) {
sass::sstream out;
out << "[";
for (size_t i = 0; i < vec.size(); i += 1) {
if (i > 0) out << ", ";
out << debug_vec(vec[i]);
}
out << "]";
return out.str();
}
template <class T>
inline sass::string debug_vec(std::queue<T> vec) {
sass::sstream out;
out << "{";
for (size_t i = 0; i < vec.size(); i += 1) {
if (i > 0) out << ", ";
out << debug_vec(vec[i]);
}
out << "}";
return out.str();
}
template <class T, class U, class O>
inline sass::string debug_vec(std::map<T, U, O> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(it->first) // string (key)
<< ": "
<< debug_vec(it->second); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_vec(const ordered_map<T, U, O, V>& vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(*it); // string (key)
// << debug_vec(it->second); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_vec(std::unordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(it->first) // string (key)
<< ": "
<< debug_vec(it->second); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_keys(std::unordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(it->first); // string (key)
joinit = true;
}
out << "}";
return out.str();
}
inline sass::string debug_vec(ExtListSelSet& vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(*it); // string (key)
joinit = true;
}
out << "}";
return out.str();
}
/*
template <class T, class U, class O, class V>
inline sass::string debug_values(tsl::ordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(const_cast<U&>(it->second)); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_vec(tsl::ordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(it->first) // string (key)
<< ": "
<< debug_vec(const_cast<U&>(it->second)); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_vals(tsl::ordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(const_cast<U&>(it->second)); // string's value
joinit = true;
}
out << "}";
return out.str();
}
template <class T, class U, class O, class V>
inline sass::string debug_keys(tsl::ordered_map<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto it = vec.begin(); it != vec.end(); it++)
{
if (joinit) out << ", ";
out << debug_vec(it->first);
joinit = true;
}
out << "}";
return out.str();
}
*/
template <class T, class U>
inline sass::string debug_vec(std::set<T, U> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto item : vec) {
if (joinit) out << ", ";
out << debug_vec(item);
joinit = true;
}
out << "}";
return out.str();
}
/*
template <class T, class U, class O, class V>
inline sass::string debug_vec(tsl::ordered_set<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto item : vec) {
if (joinit) out << ", ";
out << debug_vec(item);
joinit = true;
}
out << "}";
return out.str();
}
*/
template <class T, class U, class O, class V>
inline sass::string debug_vec(std::unordered_set<T, U, O, V> vec) {
sass::sstream out;
out << "{";
bool joinit = false;
for (auto item : vec) {
if (joinit) out << ", ";
out << debug_vec(item);
joinit = true;
}
out << "}";
return out.str();
}
inline sass::string debug_bool(bool val) {
return val ? "true" : "false";
}
inline sass::string debug_vec(ExtSmplSelSet* node) {
if (node == NULL) return "null";
else return debug_vec(*node);
}
inline void debug_ast(const AST_Node* node, sass::string ind = "", Env* env = 0) {
debug_ast(const_cast<AST_Node*>(node), ind, env);
}
inline sass::string str_replace(sass::string str, const sass::string& oldStr, const sass::string& newStr)
{
size_t pos = 0;
while((pos = str.find(oldStr, pos)) != sass::string::npos)
{
str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
return str;
}
inline sass::string prettyprint(const sass::string& str) {
sass::string clean = str_replace(str, "\n", "\\n");
clean = str_replace(clean, " ", "\\t");
clean = str_replace(clean, "\r", "\\r");
return clean;
}
inline sass::string longToHex(long long t) {
sass::sstream is;
is << std::hex << t;
return is.str();
}
inline sass::string pstate_source_position(AST_Node* node)
{
sass::sstream str;
Offset start(node->pstate().position);
Offset end(start + node->pstate().offset);
size_t file = node->pstate().getSrcId();
str << (file == sass::string::npos ? 99999999 : file)
<< "@[" << start.line << ":" << start.column << "]"
<< "-[" << end.line << ":" << end.column << "]";
#ifdef DEBUG_SHARED_PTR
str << "x" << node->getRefCount() << ""
<< " " << node->getDbgFile()
<< "@" << node->getDbgLine();
#endif
return str.str();
}
inline void debug_ast(AST_Node* node, sass::string ind, Env* env)
{
if (node == 0) return;
if (ind == "") std::cerr << "####################################################################\n";
if (Cast<Bubble>(node)) {
Bubble* bubble = Cast<Bubble>(node);
std::cerr << ind << "Bubble " << bubble;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << bubble->tabs();
std::cerr << std::endl;
debug_ast(bubble->node(), ind + " ", env);
} else if (Cast<Trace>(node)) {
Trace* trace = Cast<Trace>(node);
std::cerr << ind << "Trace " << trace;
std::cerr << " (" << pstate_source_position(node) << ")"
<< " [name:" << trace->name() << ", type: " << trace->type() << "]"
<< std::endl;
debug_ast(trace->block(), ind + " ", env);
} else if (Cast<AtRootRule>(node)) {
AtRootRule* root_block = Cast<AtRootRule>(node);
std::cerr << ind << "AtRootRule " << 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 (Cast<SelectorList>(node)) {
SelectorList* selector = Cast<SelectorList>(node);
std::cerr << ind << "SelectorList " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");
std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");
std::cerr << (selector->has_real_parent_ref() ? " [real-parent]": " -");
std::cerr << std::endl;
for(const ComplexSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<ComplexSelector>(node)) {
ComplexSelector* selector = Cast<ComplexSelector>(node);
std::cerr << ind << "ComplexSelector " << selector
<< " (" << pstate_source_position(node) << ")"
<< " <" << selector->hash() << ">"
<< " [" << (selector->chroots() ? "CHROOT" : "CONNECT") << "]"
<< " [length:" << longToHex(selector->length()) << "]"
<< " [weight:" << longToHex(selector->specificity()) << "]"
<< (selector->is_invisible() ? " [is_invisible]" : " -")
<< (selector->isInvisible() ? " [isInvisible]" : " -")
<< (selector->hasPreLineFeed() ? " [hasPreLineFeed]" : " -")
// << (selector->is_invisible() ? " [INVISIBLE]": " -")
// << (selector->has_placeholder() ? " [PLACEHOLDER]": " -")
// << (selector->is_optional() ? " [is_optional]": " -")
<< (selector->has_real_parent_ref() ? " [real parent]": " -")
// << (selector->has_line_feed() ? " [line-feed]": " -")
// << (selector->has_line_break() ? " [line-break]": " -")
<< " -- \n";
for(const SelectorComponentObj& i : selector->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<SelectorCombinator>(node)) {
SelectorCombinator* selector = Cast<SelectorCombinator>(node);
std::cerr << ind << "SelectorCombinator " << selector
<< " (" << pstate_source_position(node) << ")"
<< " <" << selector->hash() << ">"
<< " [weight:" << longToHex(selector->specificity()) << "]"
<< (selector->has_real_parent_ref() ? " [real parent]": " -")
<< " -- ";
sass::string del;
switch (selector->combinator()) {
case SelectorCombinator::CHILD: del = ">"; break;
case SelectorCombinator::GENERAL: del = "~"; break;
case SelectorCombinator::ADJACENT: del = "+"; break;
}
std::cerr << "[" << del << "]" << "\n";
} else if (Cast<CompoundSelector>(node)) {
CompoundSelector* selector = Cast<CompoundSelector>(node);
std::cerr << ind << "CompoundSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << (selector->hasRealParent() ? " [REAL PARENT]" : "") << ">";
std::cerr << " [weight:" << longToHex(selector->specificity()) << "]";
std::cerr << (selector->hasPostLineBreak() ? " [hasPostLineBreak]" : " -");
std::cerr << (selector->is_invisible() ? " [is_invisible]" : " -");
std::cerr << (selector->isInvisible() ? " [isInvisible]" : " -");
std::cerr << "\n";
for(const SimpleSelector_Obj& i : selector->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Parent_Reference>(node)) {
Parent_Reference* selector = Cast<Parent_Reference>(node);
std::cerr << ind << "Parent_Reference " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << std::endl;
} else if (Cast<PseudoSelector>(node)) {
PseudoSelector* selector = Cast<PseudoSelector>(node);
std::cerr << ind << "PseudoSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << " <<" << selector->ns_name() << ">>";
std::cerr << (selector->isClass() ? " [isClass]": " -");
std::cerr << (selector->isSyntacticClass() ? " [isSyntacticClass]": " -");
std::cerr << std::endl;
debug_ast(selector->argument(), ind + " <= ", env);
debug_ast(selector->selector(), ind + " || ", env);
} else if (Cast<AttributeSelector>(node)) {
AttributeSelector* selector = Cast<AttributeSelector>(node);
std::cerr << ind << "AttributeSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << " <<" << selector->ns_name() << ">>";
std::cerr << std::endl;
debug_ast(selector->value(), ind + "[" + selector->matcher() + "] ", env);
} else if (Cast<ClassSelector>(node)) {
ClassSelector* selector = Cast<ClassSelector>(node);
std::cerr << ind << "ClassSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << " <<" << selector->ns_name() << ">>";
std::cerr << std::endl;
} else if (Cast<IDSelector>(node)) {
IDSelector* selector = Cast<IDSelector>(node);
std::cerr << ind << "IDSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << " <<" << selector->ns_name() << ">>";
std::cerr << std::endl;
} else if (Cast<TypeSelector>(node)) {
TypeSelector* selector = Cast<TypeSelector>(node);
std::cerr << ind << "TypeSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " <" << selector->hash() << ">";
std::cerr << " <<" << selector->ns_name() << ">>";
std::cerr << std::endl;
} else if (Cast<PlaceholderSelector>(node)) {
PlaceholderSelector* selector = Cast<PlaceholderSelector>(node);
std::cerr << ind << "PlaceholderSelector [" << selector->ns_name() << "] " << selector;
std::cerr << " (" << pstate_source_position(selector) << ")"
<< " <" << selector->hash() << ">"
<< (selector->isInvisible() ? " [isInvisible]" : " -")
<< std::endl;
} else if (Cast<SimpleSelector>(node)) {
SimpleSelector* selector = Cast<SimpleSelector>(node);
std::cerr << ind << "SimpleSelector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")";
} else if (Cast<Selector_Schema>(node)) {
Selector_Schema* selector = Cast<Selector_Schema>(node);
std::cerr << ind << "Selector_Schema " << selector;
std::cerr << " (" << pstate_source_position(node) << ")"
<< (selector->connect_parent() ? " [connect-parent]": " -")
<< std::endl;
debug_ast(selector->contents(), ind + " ");
// for(auto i : selector->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Selector>(node)) {
Selector* selector = Cast<Selector>(node);
std::cerr << ind << "Selector " << selector;
std::cerr << " (" << pstate_source_position(node) << ")"
<< std::endl;
} else if (Cast<Media_Query_Expression>(node)) {
Media_Query_Expression* block = 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 (Cast<Media_Query>(node)) {
Media_Query* block = 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(const auto& i : block->elements()) { debug_ast(i, ind + " ", env); }
}
else if (Cast<MediaRule>(node)) {
MediaRule* rule = Cast<MediaRule>(node);
std::cerr << ind << "MediaRule " << rule;
std::cerr << " (" << pstate_source_position(rule) << ")";
std::cerr << " " << rule->tabs() << std::endl;
debug_ast(rule->schema(), ind + " =@ ");
debug_ast(rule->block(), ind + " ");
}
else if (Cast<CssMediaRule>(node)) {
CssMediaRule* rule = Cast<CssMediaRule>(node);
std::cerr << ind << "CssMediaRule " << rule;
std::cerr << " (" << pstate_source_position(rule) << ")";
std::cerr << " " << rule->tabs() << std::endl;
for (auto item : rule->elements()) {
debug_ast(item, ind + " == ");
}
debug_ast(rule->block(), ind + " ");
}
else if (Cast<CssMediaQuery>(node)) {
CssMediaQuery* query = Cast<CssMediaQuery>(node);
std::cerr << ind << "CssMediaQuery " << query;
std::cerr << " (" << pstate_source_position(query) << ")";
std::cerr << " [" << (query->modifier()) << "] ";
std::cerr << " [" << (query->type()) << "] ";
std::cerr << " " << debug_vec(query->features());
std::cerr << std::endl;
} else if (Cast<SupportsRule>(node)) {
SupportsRule* block = Cast<SupportsRule>(node);
std::cerr << ind << "SupportsRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->condition(), ind + " =@ ");
debug_ast(block->block(), ind + " <>");
} else if (Cast<SupportsOperation>(node)) {
SupportsOperation* block = Cast<SupportsOperation>(node);
std::cerr << ind << "SupportsOperation " << block;
std::cerr << " (" << pstate_source_position(node) << ")"
<< std::endl;
debug_ast(block->left(), ind + " left) ");
debug_ast(block->right(), ind + " right) ");
} else if (Cast<SupportsNegation>(node)) {
SupportsNegation* block = Cast<SupportsNegation>(node);
std::cerr << ind << "SupportsNegation " << block;
std::cerr << " (" << pstate_source_position(node) << ")"
<< std::endl;
debug_ast(block->condition(), ind + " condition) ");
} else if (Cast<At_Root_Query>(node)) {
At_Root_Query* block = Cast<At_Root_Query>(node);
std::cerr << ind << "At_Root_Query " << block;
std::cerr << " (" << pstate_source_position(node) << ")"
<< std::endl;
debug_ast(block->feature(), ind + " feature) ");
debug_ast(block->value(), ind + " value) ");
} else if (Cast<SupportsDeclaration>(node)) {
SupportsDeclaration* block = Cast<SupportsDeclaration>(node);
std::cerr << ind << "SupportsDeclaration " << block;
std::cerr << " (" << pstate_source_position(node) << ")"
<< std::endl;
debug_ast(block->feature(), ind + " feature) ");
debug_ast(block->value(), ind + " value) ");
} else if (Cast<Block>(node)) {
Block* root_block = Cast<Block>(node);
std::cerr << ind << "Block " << root_block;
std::cerr << " (" << pstate_source_position(node) << ")";
if (root_block->is_root()) std::cerr << " [root]";
if (root_block->isInvisible()) std::cerr << " [isInvisible]";
std::cerr << " " << root_block->tabs() << std::endl;
for(const Statement_Obj& i : root_block->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<WarningRule>(node)) {
WarningRule* block = Cast<WarningRule>(node);
std::cerr << ind << "WarningRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->message(), ind + " : ");
} else if (Cast<ErrorRule>(node)) {
ErrorRule* block = Cast<ErrorRule>(node);
std::cerr << ind << "ErrorRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
} else if (Cast<DebugRule>(node)) {
DebugRule* block = Cast<DebugRule>(node);
std::cerr << ind << "DebugRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->value(), ind + " ");
} else if (Cast<Comment>(node)) {
Comment* block = Cast<Comment>(node);
std::cerr << ind << "Comment " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->text(), ind + "// ", env);
} else if (Cast<If>(node)) {
If* block = 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 (Cast<Return>(node)) {
Return* block = Cast<Return>(node);
std::cerr << ind << "Return " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs();
std::cerr << " [" << block->value()->to_string() << "]" << std::endl;
} else if (Cast<ExtendRule>(node)) {
ExtendRule* block = Cast<ExtendRule>(node);
std::cerr << ind << "ExtendRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->selector(), ind + "-> ", env);
} else if (Cast<Content>(node)) {
Content* block = Cast<Content>(node);
std::cerr << ind << "Content " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->arguments(), ind + " args: ", env);
} else if (Cast<Import_Stub>(node)) {
Import_Stub* block = Cast<Import_Stub>(node);
std::cerr << ind << "Import_Stub " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << block->imp_path() << "] ";
std::cerr << " " << block->tabs() << std::endl;
} else if (Cast<Import>(node)) {
Import* block = Cast<Import>(node);
std::cerr << ind << "Import " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
// sass::vector<sass::string> files_;
for (auto imp : block->urls()) debug_ast(imp, ind + "@: ", env);
debug_ast(block->import_queries(), ind + "@@ ");
} else if (Cast<Assignment>(node)) {
Assignment* block = 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 (Cast<Declaration>(node)) {
Declaration* block = Cast<Declaration>(node);
std::cerr << ind << "Declaration " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [is_custom_property: " << block->is_custom_property() << "] ";
std::cerr << " " << block->tabs() << std::endl;
debug_ast(block->property(), ind + " prop: ", env);
debug_ast(block->value(), ind + " value: ", env);
debug_ast(block->block(), ind + " ", env);
} else if (Cast<Keyframe_Rule>(node)) {
Keyframe_Rule* ParentStatement = Cast<Keyframe_Rule>(node);
std::cerr << ind << "Keyframe_Rule " << ParentStatement;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << ParentStatement->tabs() << std::endl;
if (ParentStatement->name()) debug_ast(ParentStatement->name(), ind + "@");
if (ParentStatement->block()) for(const Statement_Obj& i : ParentStatement->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<AtRule>(node)) {
AtRule* block = Cast<AtRule>(node);
std::cerr << ind << "AtRule " << 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(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<EachRule>(node)) {
EachRule* block = Cast<EachRule>(node);
std::cerr << ind << "EachRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<ForRule>(node)) {
ForRule* block = Cast<ForRule>(node);
std::cerr << ind << "ForRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<WhileRule>(node)) {
WhileRule* block = Cast<WhileRule>(node);
std::cerr << ind << "WhileRule " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << block->tabs() << std::endl;
if (block->block()) for(const Statement_Obj& i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Definition>(node)) {
Definition* block = 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()) debug_ast(block->block(), ind + " ", env);
} else if (Cast<Mixin_Call>(node)) {
Mixin_Call* block = Cast<Mixin_Call>(node);
std::cerr << ind << "Mixin_Call " << block << " " << block->tabs();
std::cerr << " (" << pstate_source_position(block) << ")";
std::cerr << " [" << block->name() << "]";
std::cerr << " [has_content: " << block->has_content() << "] " << std::endl;
debug_ast(block->arguments(), ind + " args: ", env);
debug_ast(block->block_parameters(), ind + " block_params: ", env);
if (block->block()) debug_ast(block->block(), ind + " ", env);
} else if (StyleRule* ruleset = Cast<StyleRule>(node)) {
std::cerr << ind << "StyleRule " << ruleset;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [indent: " << ruleset->tabs() << "]";
std::cerr << (ruleset->is_invisible() ? " [INVISIBLE]" : "");
std::cerr << (ruleset->is_root() ? " [root]" : "");
std::cerr << std::endl;
debug_ast(ruleset->selector(), ind + ">");
debug_ast(ruleset->block(), ind + " ");
} else if (Cast<Block>(node)) {
Block* block = Cast<Block>(node);
std::cerr << ind << "Block " << block;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << (block->is_invisible() ? " [INVISIBLE]" : "");
std::cerr << " [indent: " << block->tabs() << "]" << std::endl;
for(const Statement_Obj& i : block->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Variable>(node)) {
Variable* expression = Cast<Variable>(node);
std::cerr << ind << "Variable " << expression;
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << expression->name() << "]" << std::endl;
sass::string name(expression->name());
if (env && env->has(name)) debug_ast(Cast<Expression>((*env)[name]), ind + " -> ", env);
} else if (Cast<Function_Call>(node)) {
Function_Call* expression = Cast<Function_Call>(node);
std::cerr << ind << "Function_Call " << expression;
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << expression->name() << "]";
if (expression->is_delayed()) std::cerr << " [delayed]";
if (expression->is_interpolant()) std::cerr << " [interpolant]";
if (expression->is_css()) std::cerr << " [css]";
std::cerr << std::endl;
debug_ast(expression->arguments(), ind + " args: ", env);
debug_ast(expression->func(), ind + " func: ", env);
} else if (Cast<Function>(node)) {
Function* expression = Cast<Function>(node);
std::cerr << ind << "Function " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
if (expression->is_css()) std::cerr << " [css]";
std::cerr << std::endl;
debug_ast(expression->definition(), ind + " definition: ", env);
} else if (Cast<Arguments>(node)) {
Arguments* expression = 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(const Argument_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Argument>(node)) {
Argument* expression = Cast<Argument>(node);
std::cerr << ind << "Argument " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << expression->value().ptr() << "]";
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 (Cast<Parameters>(node)) {
Parameters* expression = 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(const Parameter_Obj& i : expression->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Parameter>(node)) {
Parameter* expression = 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().ptr() << "] ";
std::cerr << " [rest: " << expression->is_rest_parameter() << "] " << std::endl;
} else if (Cast<Unary_Expression>(node)) {
Unary_Expression* expression = Cast<Unary_Expression>(node);
std::cerr << ind << "Unary_Expression " << expression;
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " [delayed: " << expression->is_delayed() << "] ";
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << expression->type() << "]" << std::endl;
debug_ast(expression->operand(), ind + " operand: ", env);
} else if (Cast<Binary_Expression>(node)) {
Binary_Expression* expression = Cast<Binary_Expression>(node);
std::cerr << ind << "Binary_Expression " << expression;
if (expression->is_interpolant()) std::cerr << " [is interpolant] ";
if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";
if (expression->is_right_interpolant()) std::cerr << " [right interpolant] ";
std::cerr << " [delayed: " << expression->is_delayed() << "] ";
std::cerr << " [ws_before: " << expression->op().ws_before << "] ";
std::cerr << " [ws_after: " << expression->op().ws_after << "] ";
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [" << expression->type_name() << "]" << std::endl;
debug_ast(expression->left(), ind + " left: ", env);
debug_ast(expression->right(), ind + " right: ", env);
} else if (Cast<Map>(node)) {
Map* expression = Cast<Map>(node);
std::cerr << ind << "Map " << expression;
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [Hashed]" << std::endl;
for (const auto& i : expression->elements()) {
debug_ast(i.first, ind + " key: ");
debug_ast(i.second, ind + " val: ");
}
} else if (Cast<List>(node)) {
List* expression = Cast<List>(node);
std::cerr << ind << "List " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " (" << expression->length() << ") " <<
(expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map " : "Space ") <<
" [delayed: " << expression->is_delayed() << "] " <<
" [interpolant: " << expression->is_interpolant() << "] " <<
" [listized: " << expression->from_selector() << "] " <<
" [arglist: " << expression->is_arglist() << "] " <<
" [bracketed: " << expression->is_bracketed() << "] " <<
" [expanded: " << expression->is_expanded() << "] " <<
" [hash: " << expression->hash() << "] " <<
std::endl;
for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Boolean>(node)) {
Boolean* expression = Cast<Boolean>(node);
std::cerr << ind << "Boolean " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " [" << expression->value() << "]" << std::endl;
} else if (Cast<Color_RGBA>(node)) {
Color_RGBA* expression = Cast<Color_RGBA>(node);
std::cerr << ind << "Color " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [name: " << expression->disp() << "] ";
std::cerr << " [delayed: " << expression->is_delayed() << "] ";
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " rgba[" << expression->r() << ":" << expression->g() << ":" << expression->b() << "@" << expression->a() << "]" << std::endl;
} else if (Cast<Color_HSLA>(node)) {
Color_HSLA* expression = Cast<Color_HSLA>(node);
std::cerr << ind << "Color " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [name: " << expression->disp() << "] ";
std::cerr << " [delayed: " << expression->is_delayed() << "] ";
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " hsla[" << expression->h() << ":" << expression->s() << ":" << expression->l() << "@" << expression->a() << "]" << std::endl;
} else if (Cast<Number>(node)) {
Number* expression = Cast<Number>(node);
std::cerr << ind << "Number " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [delayed: " << expression->is_delayed() << "] ";
std::cerr << " [interpolant: " << expression->is_interpolant() << "] ";
std::cerr << " [" << expression->value() << expression->unit() << "]" <<
" [hash: " << expression->hash() << "] " <<
std::endl;
} else if (Cast<Null>(node)) {
Null* expression = Cast<Null>(node);
std::cerr << ind << "Null " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " [interpolant: " << expression->is_interpolant() << "] "
// " [hash: " << expression->hash() << "] "
<< std::endl;
} else if (Cast<String_Quoted>(node)) {
String_Quoted* expression = 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->is_interpolant()) std::cerr << " [interpolant]";
if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]";
std::cerr << std::endl;
} else if (Cast<String_Constant>(node)) {
String_Constant* expression = 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->is_interpolant()) std::cerr << " [interpolant]";
std::cerr << std::endl;
} else if (Cast<String_Schema>(node)) {
String_Schema* expression = Cast<String_Schema>(node);
std::cerr << ind << "String_Schema " << expression;
std::cerr << " (" << pstate_source_position(expression) << ")";
std::cerr << " " << expression->concrete_type();
std::cerr << " (" << pstate_source_position(node) << ")";
if (expression->css()) std::cerr << " [css]";
if (expression->is_delayed()) std::cerr << " [delayed]";
if (expression->is_interpolant()) std::cerr << " [is interpolant]";
if (expression->has_interpolant()) std::cerr << " [has interpolant]";
if (expression->is_left_interpolant()) std::cerr << " [left interpolant] ";
if (expression->is_right_interpolant()) std::cerr << " [right interpolant] ";
std::cerr << std::endl;
for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<String>(node)) {
String* expression = Cast<String>(node);
std::cerr << ind << "String " << expression;
std::cerr << " " << expression->concrete_type();
std::cerr << " (" << pstate_source_position(node) << ")";
if (expression->is_interpolant()) std::cerr << " [interpolant]";
std::cerr << std::endl;
} else if (Cast<Expression>(node)) {
Expression* expression = Cast<Expression>(node);
std::cerr << ind << "Expression " << expression;
std::cerr << " (" << pstate_source_position(node) << ")";
switch (expression->concrete_type()) {
case Expression::Type::NONE: std::cerr << " [NONE]"; break;
case Expression::Type::BOOLEAN: std::cerr << " [BOOLEAN]"; break;
case Expression::Type::NUMBER: std::cerr << " [NUMBER]"; break;
case Expression::Type::COLOR: std::cerr << " [COLOR]"; break;
case Expression::Type::STRING: std::cerr << " [STRING]"; break;
case Expression::Type::LIST: std::cerr << " [LIST]"; break;
case Expression::Type::MAP: std::cerr << " [MAP]"; break;
case Expression::Type::SELECTOR: std::cerr << " [SELECTOR]"; break;
case Expression::Type::NULL_VAL: std::cerr << " [NULL_VAL]"; break;
case Expression::Type::C_WARNING: std::cerr << " [C_WARNING]"; break;
case Expression::Type::C_ERROR: std::cerr << " [C_ERROR]"; break;
case Expression::Type::FUNCTION: std::cerr << " [FUNCTION]"; break;
case Expression::Type::NUM_TYPES: std::cerr << " [NUM_TYPES]"; break;
case Expression::Type::VARIABLE: std::cerr << " [VARIABLE]"; break;
case Expression::Type::FUNCTION_VAL: std::cerr << " [FUNCTION_VAL]"; break;
case Expression::Type::PARENT: std::cerr << " [PARENT]"; break;
}
std::cerr << std::endl;
} else if (Cast<ParentStatement>(node)) {
ParentStatement* parent = Cast<ParentStatement>(node);
std::cerr << ind << "ParentStatement " << parent;
std::cerr << " (" << pstate_source_position(node) << ")";
std::cerr << " " << parent->tabs() << std::endl;
if (parent->block()) for(const Statement_Obj& i : parent->block()->elements()) { debug_ast(i, ind + " ", env); }
} else if (Cast<Statement>(node)) {
Statement* statement = 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_ast(const AST_Node* node, sass::string ind = "", Env* env = 0)
{
debug_ast(const_cast<AST_Node*>(node), ind, env);
}
*/
#endif // SASS_DEBUGGER

View File

@@ -0,0 +1,297 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "emitter.hpp"
#include "util_string.hpp"
#include "util.hpp"
namespace Sass {
Emitter::Emitter(struct Sass_Output_Options& opt)
: wbuf(),
opt(opt),
indentation(0),
scheduled_space(0),
scheduled_linefeed(0),
scheduled_delimiter(false),
scheduled_crutch(0),
scheduled_mapping(0),
in_custom_property(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
sass::string Emitter::get_buffer(void)
{
return wbuf.buffer;
}
Sass_Output_Style Emitter::output_style(void) const
{
return opt.output_style;
}
// PROXY METHODS FOR SOURCE MAPS
void Emitter::add_source_index(size_t idx)
{ wbuf.smap.source_index.push_back(idx); }
sass::string Emitter::render_srcmap(Context &ctx)
{ return wbuf.smap.render_srcmap(ctx); }
void Emitter::set_filename(const sass::string& str)
{ wbuf.smap.file = str; }
void Emitter::schedule_mapping(const AST_Node* node)
{ scheduled_mapping = node; }
void Emitter::add_open_mapping(const AST_Node* node)
{ wbuf.smap.add_open_mapping(node); }
void Emitter::add_close_mapping(const AST_Node* node)
{ wbuf.smap.add_close_mapping(node); }
SourceSpan Emitter::remap(const SourceSpan& pstate)
{ return wbuf.smap.remap(pstate); }
// MAIN BUFFER MANIPULATION
// add outstanding delimiter
void Emitter::finalize(bool final)
{
scheduled_space = 0;
if (output_style() == SASS_STYLE_COMPRESSED)
if (final) scheduled_delimiter = false;
if (scheduled_linefeed)
scheduled_linefeed = 1;
flush_schedules();
}
// flush scheduled space/linefeed
void Emitter::flush_schedules(void)
{
// check the schedule
if (scheduled_linefeed) {
sass::string linefeeds = "";
for (size_t i = 0; i < scheduled_linefeed; i++)
linefeeds += opt.linefeed;
scheduled_space = 0;
scheduled_linefeed = 0;
append_string(linefeeds);
} else if (scheduled_space) {
sass::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 sass::string& text)
{
// do not adjust mappings for utf8 bom
// seems they are not counted in any UA
if (text.compare("\xEF\xBB\xBF") != 0) {
wbuf.smap.prepend(Offset(text));
}
wbuf.buffer = text + wbuf.buffer;
}
char Emitter::last_char()
{
return wbuf.buffer.back();
}
// append a single char to the buffer
void Emitter::append_char(const char chr)
{
// write space/lf
flush_schedules();
// add to buffer
wbuf.buffer += chr;
// account for data in source-maps
wbuf.smap.append(Offset(chr));
}
// append some text or token to the buffer
void Emitter::append_string(const sass::string& text)
{
// write space/lf
flush_schedules();
if (in_comment) {
sass::string out = Util::normalize_newlines(text);
if (output_style() == COMPACT) {
out = comment_to_compact_string(out);
}
wbuf.smap.append(Offset(out));
wbuf.buffer += std::move(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 sass::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 sass::string& text, const AST_Node* node)
{
flush_schedules();
add_open_mapping(node);
// hotfix for browser issues
// this is pretty ugly indeed
if (scheduled_crutch) {
add_open_mapping(scheduled_crutch);
scheduled_crutch = 0;
}
append_string(text);
add_close_mapping(node);
}
// HELPER METHODS
void Emitter::append_indentation()
{
if (output_style() == COMPRESSED) return;
if (output_style() == COMPACT) return;
if (in_declaration && in_comma_array) return;
if (scheduled_linefeed && indentation)
scheduled_linefeed = 1;
sass::string indent = "";
for (size_t i = 0; i < indentation; i++)
indent += opt.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(":");
if (!in_custom_property) append_optional_space();
}
void Emitter::append_mandatory_space()
{
scheduled_space = 1;
}
void Emitter::append_optional_space()
{
if ((output_style() != COMPRESSED) && buffer().size()) {
unsigned char lst = buffer().at(buffer().length() - 1);
if (!isspace(lst) || scheduled_delimiter) {
if (last_char() != '(') {
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(opt.indent);
}
}
void Emitter::append_optional_linefeed()
{
if (in_declaration && in_comma_array) return;
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)
{
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() == 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;
}
}

View File

@@ -0,0 +1,101 @@
#ifndef SASS_EMITTER_H
#define SASS_EMITTER_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "sass/base.h"
#include "source_map.hpp"
#include "ast_fwd_decl.hpp"
namespace Sass {
class Context;
class Emitter {
public:
Emitter(struct Sass_Output_Options& opt);
virtual ~Emitter() { }
protected:
OutputBuffer wbuf;
public:
const sass::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 sass::string& str);
void add_open_mapping(const AST_Node* node);
void add_close_mapping(const AST_Node* node);
void schedule_mapping(const AST_Node* node);
sass::string render_srcmap(Context &ctx);
SourceSpan remap(const SourceSpan& pstate);
public:
struct Sass_Output_Options& opt;
size_t indentation;
size_t scheduled_space;
size_t scheduled_linefeed;
bool scheduled_delimiter;
const AST_Node* scheduled_crutch;
const AST_Node* scheduled_mapping;
public:
// output strings different in custom css properties
bool in_custom_property;
// 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 sass::string
sass::string get_buffer(void);
// flush scheduled space/linefeed
Sass_Output_Style output_style(void) const;
// add outstanding linefeed
void finalize(bool final = true);
// flush scheduled space/linefeed
void flush_schedules(void);
// prepend some text or token to the buffer
void prepend_string(const sass::string& text);
void prepend_output(const OutputBuffer& out);
// append some text or token to the buffer
void append_string(const sass::string& text);
// append a single character to buffer
void append_char(const char chr);
// append some white-space only text
void append_wspace(const sass::string& text);
// append some text or token to the buffer
// this adds source-mappings for node start and end
void append_token(const sass::string& text, const AST_Node* node);
// query last appended character
char last_char();
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

View File

@@ -0,0 +1,260 @@
#include "sass.hpp"
#include "ast.hpp"
#include "environment.hpp"
namespace Sass {
template <typename T>
Environment<T>::Environment(bool is_shadow)
: local_frame_(environment_map<sass::string, T>()),
parent_(0), is_shadow_(false)
{ }
template <typename T>
Environment<T>::Environment(Environment<T>* env, bool is_shadow)
: local_frame_(environment_map<sass::string, T>()),
parent_(env), is_shadow_(is_shadow)
{ }
template <typename T>
Environment<T>::Environment(Environment<T>& env, bool is_shadow)
: local_frame_(environment_map<sass::string, T>()),
parent_(&env), is_shadow_(is_shadow)
{ }
// 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>
environment_map<sass::string, T>& Environment<T>::local_frame() {
return local_frame_;
}
template <typename T>
bool Environment<T>::has_local(const sass::string& key) const
{ return local_frame_.find(key) != local_frame_.end(); }
template <typename T> EnvResult
Environment<T>::find_local(const sass::string& key)
{
auto end = local_frame_.end();
auto it = local_frame_.find(key);
return EnvResult(it, it != end);
}
template <typename T>
T& Environment<T>::get_local(const sass::string& key)
{ return local_frame_[key]; }
template <typename T>
void Environment<T>::set_local(const sass::string& key, const T& val)
{
local_frame_[key] = val;
}
template <typename T>
void Environment<T>::set_local(const sass::string& key, T&& val)
{
local_frame_[key] = val;
}
template <typename T>
void Environment<T>::del_local(const sass::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 sass::string& key)
{ return global_env()->has(key); }
template <typename T>
T& Environment<T>::get_global(const sass::string& key)
{ return (*global_env())[key]; }
template <typename T>
void Environment<T>::set_global(const sass::string& key, const T& val)
{
global_env()->local_frame_[key] = val;
}
template <typename T>
void Environment<T>::set_global(const sass::string& key, T&& val)
{
global_env()->local_frame_[key] = val;
}
template <typename T>
void Environment<T>::del_global(const sass::string& key)
{ global_env()->local_frame_.erase(key); }
template <typename T>
Environment<T>* Environment<T>::lexical_env(const sass::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 sass::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 sass::string& key, const T& val)
{
Environment<T>* cur = this;
bool shadow = false;
while ((cur && cur->is_lexical()) || shadow) {
EnvResult rv(cur->find_local(key));
if (rv.found) {
rv.it->second = val;
return;
}
shadow = cur->is_shadow();
cur = cur->parent_;
}
set_local(key, val);
}
// this one moves the value
template <typename T>
void Environment<T>::set_lexical(const sass::string& key, T&& val)
{
Environment<T>* cur = this;
bool shadow = false;
while ((cur && cur->is_lexical()) || shadow) {
EnvResult rv(cur->find_local(key));
if (rv.found) {
rv.it->second = val;
return;
}
shadow = cur->is_shadow();
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 sass::string& key) const
{
auto cur = this;
while (cur) {
if (cur->has_local(key)) {
return true;
}
cur = cur->parent_;
}
return false;
}
// look on the full stack for key
// include all scopes available
template <typename T> EnvResult
Environment<T>::find(const sass::string& key)
{
auto cur = this;
while (true) {
EnvResult rv(cur->find_local(key));
if (rv.found) return rv;
cur = cur->parent_;
if (!cur) return rv;
}
};
// use array access for getter and setter functions
template <typename T>
T& Environment<T>::get(const sass::string& key)
{
auto cur = this;
while (cur) {
if (cur->has_local(key)) {
return cur->get_local(key);
}
cur = cur->parent_;
}
return get_local(key);
}
// use array access for getter and setter functions
template <typename T>
T& Environment<T>::operator[](const sass::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(sass::string prefix)
{
size_t indent = 0;
if (parent_) indent = parent_->print(prefix) + 1;
std::cerr << prefix << sass::string(indent, ' ') << "== " << this << std::endl;
for (typename environment_map<sass::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 << sass::string(indent, ' ') << i->first << " " << i->second;
if (Value* val = Cast<Value>(i->second))
{ std::cerr << " : " << val->to_string(); }
std::cerr << std::endl;
}
}
return indent ;
}
#endif
*/
// compile implementation for AST_Node
template class Environment<AST_Node_Obj>;
}

View File

@@ -0,0 +1,124 @@
#ifndef SASS_ENVIRONMENT_H
#define SASS_ENVIRONMENT_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <map>
#include <string>
#include "ast_fwd_decl.hpp"
#include "ast_def_macros.hpp"
namespace Sass {
// this defeats the whole purpose of environment being templatable!!
typedef environment_map<sass::string, AST_Node_Obj>::iterator EnvIter;
class EnvResult {
public:
EnvIter it;
bool found;
public:
EnvResult(EnvIter it, bool found)
: it(it), found(found) {}
};
template <typename T>
class Environment {
// TODO: test with map
environment_map<sass::string, T> local_frame_;
ADD_PROPERTY(Environment*, parent)
ADD_PROPERTY(bool, is_shadow)
public:
Environment(bool is_shadow = false);
Environment(Environment* env, bool is_shadow = false);
Environment(Environment& env, bool is_shadow = false);
// link parent to create a stack
void link(Environment& env);
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
environment_map<sass::string, T>& local_frame();
bool has_local(const sass::string& key) const;
EnvResult find_local(const sass::string& key);
T& get_local(const sass::string& key);
// set variable on the current frame
void set_local(const sass::string& key, const T& val);
void set_local(const sass::string& key, T&& val);
void del_local(const sass::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 sass::string& key);
bool has_global(const sass::string& key);
T& get_global(const sass::string& key);
// set a variable on the global frame
void set_global(const sass::string& key, const T& val);
void set_global(const sass::string& key, T&& val);
void del_global(const sass::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 sass::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 sass::string& key, T&& val);
void set_lexical(const sass::string& key, const T& val);
// look on the full stack for key
// include all scopes available
bool has(const sass::string& key) const;
// look on the full stack for key
// include all scopes available
T& get(const sass::string& key);
// look on the full stack for key
// include all scopes available
EnvResult find(const sass::string& key);
// use array access for getter and setter functions
T& operator[](const sass::string& key);
#ifdef DEBUG
size_t print(sass::string prefix = "");
#endif
};
// define typedef for our use case
typedef Environment<AST_Node_Obj> Env;
typedef sass::vector<Env*> EnvStack;
}
#endif

View File

@@ -0,0 +1,233 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "prelexer.hpp"
#include "backtrace.hpp"
#include "error_handling.hpp"
#include <iostream>
namespace Sass {
namespace Exception {
Base::Base(SourceSpan pstate, sass::string msg, Backtraces traces)
: std::runtime_error(msg.c_str()), msg(msg),
prefix("Error"), pstate(pstate), traces(traces)
{ }
InvalidSass::InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg)
: Base(pstate, msg, traces)
{ }
InvalidParent::InvalidParent(Selector* parent, Backtraces traces, Selector* selector)
: Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector)
{
msg = "Invalid parent selector for "
"\"" + selector->to_string(Sass_Inspect_Options()) + "\": "
"\"" + parent->to_string(Sass_Inspect_Options()) + "\"";
}
InvalidVarKwdType::InvalidVarKwdType(SourceSpan pstate, Backtraces traces, sass::string name, const Argument* arg)
: Base(pstate, def_msg, traces), name(name), arg(arg)
{
msg = "Variable keyword argument map must have string keys.\n" +
name + " is not a string in " + arg->to_string() + ".";
}
InvalidArgumentType::InvalidArgumentType(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string type, const Value* value)
: Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value)
{
msg = arg + ": \"";
if (value) msg += value->to_string(Sass_Inspect_Options());
msg += "\" is not a " + type + " for `" + fn + "'";
}
MissingArgument::MissingArgument(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string fntype)
: Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype)
{
msg = fntype + " " + fn + " is missing argument " + arg + ".";
}
InvalidSyntax::InvalidSyntax(SourceSpan pstate, Backtraces traces, sass::string msg)
: Base(pstate, msg, traces)
{ }
NestingLimitError::NestingLimitError(SourceSpan pstate, Backtraces traces, sass::string msg)
: Base(pstate, msg, traces)
{ }
DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org)
: Base(org.pstate(), def_msg, traces), dup(dup), org(org)
{
msg = "Duplicate key " + dup.get_duplicate_key()->inspect() + " in map (" + org.inspect() + ").";
}
TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const sass::string type)
: Base(var.pstate(), def_msg, traces), var(var), type(type)
{
msg = var.to_string() + " is not an " + type + ".";
}
InvalidValue::InvalidValue(Backtraces traces, const Expression& val)
: Base(val.pstate(), def_msg, traces), val(val)
{
msg = val.to_string() + " isn't a valid CSS value.";
}
StackError::StackError(Backtraces traces, const AST_Node& node)
: Base(node.pstate(), def_msg, traces), node(node)
{
msg = "stack level too deep";
}
IncompatibleUnits::IncompatibleUnits(const Units& lhs, const Units& rhs)
{
msg = "Incompatible units: '" + rhs.unit() + "' and '" + lhs.unit() + "'.";
}
IncompatibleUnits::IncompatibleUnits(const UnitType lhs, const UnitType rhs)
{
msg = sass::string("Incompatible units: '") + unit_to_string(rhs) + "' and '" + unit_to_string(lhs) + "'.";
}
AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op)
: OperationError(), lhs(lhs), rhs(rhs), op(op)
{
msg = "Alpha channels must be equal: " +
lhs->to_string({ NESTED, 5 }) +
" " + sass_op_to_name(op) + " " +
rhs->to_string({ NESTED, 5 }) + ".";
}
ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)
: OperationError(), lhs(lhs), rhs(rhs)
{
msg = "divided by 0";
}
UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op)
: OperationError(), lhs(lhs), rhs(rhs), op(op)
{
msg = def_op_msg + ": \"" +
lhs->to_string({ NESTED, 5 }) +
" " + sass_op_to_name(op) + " " +
rhs->to_string({ TO_SASS, 5 }) +
"\".";
}
InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op)
: UndefinedOperation(lhs, rhs, op)
{
msg = def_op_null_msg + ": \"" + lhs->inspect() + " " + sass_op_to_name(op) + " " + rhs->inspect() + "\".";
}
SassValueError::SassValueError(Backtraces traces, SourceSpan pstate, OperationError& err)
: Base(pstate, err.what(), traces)
{
msg = err.what();
prefix = err.errtype();
}
TopLevelParent::TopLevelParent(Backtraces traces, SourceSpan pstate)
: Base(pstate, "Top-level selectors may not contain the parent selector \"&\".", traces)
{
}
UnsatisfiedExtend::UnsatisfiedExtend(Backtraces traces, Extension extension)
: Base(extension.target->pstate(), "The target selector was not found.\n"
"Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)
{
}
ExtendAcrossMedia::ExtendAcrossMedia(Backtraces traces, Extension extension)
: Base(extension.target->pstate(), "You may not @extend selectors across media queries.\n"
"Use \"@extend " + extension.target->to_string() + " !optional\" to avoid this error.", traces)
{
}
}
void warn(sass::string msg, SourceSpan pstate)
{
std::cerr << "Warning: " << msg << std::endl;
}
void warning(sass::string msg, SourceSpan pstate)
{
sass::string cwd(Sass::File::get_cwd());
sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd));
sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd));
sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath()));
std::cerr << "WARNING on line " << pstate.getLine() << ", column " << pstate.getColumn() << " of " << output_path << ":" << std::endl;
std::cerr << msg << std::endl << std::endl;
}
void warn(sass::string msg, SourceSpan pstate, Backtrace* bt)
{
warn(msg, pstate);
}
void deprecated_function(sass::string msg, SourceSpan pstate)
{
sass::string cwd(Sass::File::get_cwd());
sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd));
sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd));
sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath()));
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.getLine() << " of " << output_path << std::endl;
}
void deprecated(sass::string msg, sass::string msg2, bool with_column, SourceSpan pstate)
{
sass::string cwd(Sass::File::get_cwd());
sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd));
sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd));
sass::string output_path(Sass::File::path_for_console(rel_path, pstate.getPath(), pstate.getPath()));
std::cerr << "DEPRECATION WARNING on line " << pstate.getLine();
// if (with_column) std::cerr << ", column " << pstate.column + pstate.offset.column + 1;
if (output_path.length()) std::cerr << " of " << output_path;
std::cerr << ":" << std::endl;
std::cerr << msg << std::endl;
if (msg2.length()) std::cerr << msg2 << std::endl;
std::cerr << std::endl;
}
void deprecated_bind(sass::string msg, SourceSpan pstate)
{
sass::string cwd(Sass::File::get_cwd());
sass::string abs_path(Sass::File::rel2abs(pstate.getPath(), cwd, cwd));
sass::string rel_path(Sass::File::abs2rel(pstate.getPath(), cwd, cwd));
sass::string output_path(Sass::File::path_for_console(rel_path, abs_path, pstate.getPath()));
std::cerr << "WARNING: " << msg << std::endl;
std::cerr << " on line " << pstate.getLine() << " of " << output_path << std::endl;
std::cerr << "This will be an error in future versions of Sass." << std::endl;
}
// should be replaced with error with backtraces
void coreError(sass::string msg, SourceSpan pstate)
{
Backtraces traces;
throw Exception::InvalidSyntax(pstate, traces, msg);
}
void error(sass::string msg, SourceSpan pstate, Backtraces& traces)
{
traces.push_back(Backtrace(pstate));
throw Exception::InvalidSyntax(pstate, traces, msg);
}
}

View File

@@ -0,0 +1,239 @@
#ifndef SASS_ERROR_HANDLING_H
#define SASS_ERROR_HANDLING_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <string>
#include <sstream>
#include <stdexcept>
#include "units.hpp"
#include "position.hpp"
#include "backtrace.hpp"
#include "ast_fwd_decl.hpp"
#include "sass/functions.h"
namespace Sass {
struct Backtrace;
namespace Exception {
const sass::string def_msg = "Invalid sass detected";
const sass::string def_op_msg = "Undefined operation";
const sass::string def_op_null_msg = "Invalid null operation";
const sass::string def_nesting_limit = "Code too deeply nested";
class Base : public std::runtime_error {
protected:
sass::string msg;
sass::string prefix;
public:
SourceSpan pstate;
Backtraces traces;
public:
Base(SourceSpan pstate, sass::string msg, Backtraces traces);
virtual const char* errtype() const { return prefix.c_str(); }
virtual const char* what() const throw() { return msg.c_str(); }
virtual ~Base() throw() {};
};
class InvalidSass : public Base {
public:
InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg);
virtual ~InvalidSass() throw() {};
};
class InvalidParent : public Base {
protected:
Selector* parent;
Selector* selector;
public:
InvalidParent(Selector* parent, Backtraces traces, Selector* selector);
virtual ~InvalidParent() throw() {};
};
class MissingArgument : public Base {
protected:
sass::string fn;
sass::string arg;
sass::string fntype;
public:
MissingArgument(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string fntype);
virtual ~MissingArgument() throw() {};
};
class InvalidArgumentType : public Base {
protected:
sass::string fn;
sass::string arg;
sass::string type;
const Value* value;
public:
InvalidArgumentType(SourceSpan pstate, Backtraces traces, sass::string fn, sass::string arg, sass::string type, const Value* value = 0);
virtual ~InvalidArgumentType() throw() {};
};
class InvalidVarKwdType : public Base {
protected:
sass::string name;
const Argument* arg;
public:
InvalidVarKwdType(SourceSpan pstate, Backtraces traces, sass::string name, const Argument* arg = 0);
virtual ~InvalidVarKwdType() throw() {};
};
class InvalidSyntax : public Base {
public:
InvalidSyntax(SourceSpan pstate, Backtraces traces, sass::string msg);
virtual ~InvalidSyntax() throw() {};
};
class NestingLimitError : public Base {
public:
NestingLimitError(SourceSpan pstate, Backtraces traces, sass::string msg = def_nesting_limit);
virtual ~NestingLimitError() throw() {};
};
class DuplicateKeyError : public Base {
protected:
const Map& dup;
const Expression& org;
public:
DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org);
virtual const char* errtype() const { return "Error"; }
virtual ~DuplicateKeyError() throw() {};
};
class TypeMismatch : public Base {
protected:
const Expression& var;
const sass::string type;
public:
TypeMismatch(Backtraces traces, const Expression& var, const sass::string type);
virtual const char* errtype() const { return "Error"; }
virtual ~TypeMismatch() throw() {};
};
class InvalidValue : public Base {
protected:
const Expression& val;
public:
InvalidValue(Backtraces traces, const Expression& val);
virtual const char* errtype() const { return "Error"; }
virtual ~InvalidValue() throw() {};
};
class StackError : public Base {
protected:
const AST_Node& node;
public:
StackError(Backtraces traces, const AST_Node& node);
virtual const char* errtype() const { return "SystemStackError"; }
virtual ~StackError() throw() {};
};
/* common virtual base class (has no pstate or trace) */
class OperationError : public std::runtime_error {
protected:
sass::string msg;
public:
OperationError(sass::string msg = def_op_msg)
: std::runtime_error(msg.c_str()), msg(msg)
{};
public:
virtual const char* errtype() const { return "Error"; }
virtual const char* what() const throw() { return msg.c_str(); }
virtual ~OperationError() throw() {};
};
class ZeroDivisionError : public OperationError {
protected:
const Expression& lhs;
const Expression& rhs;
public:
ZeroDivisionError(const Expression& lhs, const Expression& rhs);
virtual const char* errtype() const { return "ZeroDivisionError"; }
virtual ~ZeroDivisionError() throw() {};
};
class IncompatibleUnits : public OperationError {
protected:
// const Sass::UnitType lhs;
// const Sass::UnitType rhs;
public:
IncompatibleUnits(const Units& lhs, const Units& rhs);
IncompatibleUnits(const UnitType lhs, const UnitType rhs);
virtual ~IncompatibleUnits() throw() {};
};
class UndefinedOperation : public OperationError {
protected:
const Expression* lhs;
const Expression* rhs;
const Sass_OP op;
public:
UndefinedOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op);
// virtual const char* errtype() const { return "Error"; }
virtual ~UndefinedOperation() throw() {};
};
class InvalidNullOperation : public UndefinedOperation {
public:
InvalidNullOperation(const Expression* lhs, const Expression* rhs, enum Sass_OP op);
virtual ~InvalidNullOperation() throw() {};
};
class AlphaChannelsNotEqual : public OperationError {
protected:
const Expression* lhs;
const Expression* rhs;
const Sass_OP op;
public:
AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, enum Sass_OP op);
// virtual const char* errtype() const { return "Error"; }
virtual ~AlphaChannelsNotEqual() throw() {};
};
class SassValueError : public Base {
public:
SassValueError(Backtraces traces, SourceSpan pstate, OperationError& err);
virtual ~SassValueError() throw() {};
};
class TopLevelParent : public Base {
public:
TopLevelParent(Backtraces traces, SourceSpan pstate);
virtual ~TopLevelParent() throw() {};
};
class UnsatisfiedExtend : public Base {
public:
UnsatisfiedExtend(Backtraces traces, Extension extension);
virtual ~UnsatisfiedExtend() throw() {};
};
class ExtendAcrossMedia : public Base {
public:
ExtendAcrossMedia(Backtraces traces, Extension extension);
virtual ~ExtendAcrossMedia() throw() {};
};
}
void warn(sass::string msg, SourceSpan pstate);
void warn(sass::string msg, SourceSpan pstate, Backtrace* bt);
void warning(sass::string msg, SourceSpan pstate);
void deprecated_function(sass::string msg, SourceSpan pstate);
void deprecated(sass::string msg, sass::string msg2, bool with_column, SourceSpan pstate);
void deprecated_bind(sass::string msg, SourceSpan pstate);
// void deprecated(sass::string msg, SourceSpan pstate, Backtrace* bt);
void coreError(sass::string msg, SourceSpan pstate);
void error(sass::string msg, SourceSpan pstate, Backtraces& traces);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
#ifndef SASS_EVAL_H
#define SASS_EVAL_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast.hpp"
#include "context.hpp"
#include "listize.hpp"
#include "operation.hpp"
#include "environment.hpp"
namespace Sass {
class Expand;
class Context;
class Eval : public Operation_CRTP<Expression*, Eval> {
public:
Expand& exp;
Context& ctx;
Backtraces& traces;
Eval(Expand& exp);
~Eval();
bool force;
bool is_in_comment;
bool is_in_selector_schema;
Boolean_Obj bool_true;
Boolean_Obj bool_false;
Env* environment();
EnvStack& env_stack();
const sass::string cwd();
CalleeStack& callee_stack();
struct Sass_Inspect_Options& options();
struct Sass_Compiler* compiler();
// for evaluating function bodies
Expression* operator()(Block*);
Expression* operator()(Assignment*);
Expression* operator()(If*);
Expression* operator()(ForRule*);
Expression* operator()(EachRule*);
Expression* operator()(WhileRule*);
Expression* operator()(Return*);
Expression* operator()(WarningRule*);
Expression* operator()(ErrorRule*);
Expression* operator()(DebugRule*);
Expression* operator()(List*);
Expression* operator()(Map*);
Expression* operator()(Binary_Expression*);
Expression* operator()(Unary_Expression*);
Expression* operator()(Function_Call*);
Expression* operator()(Variable*);
Expression* operator()(Number*);
Expression* operator()(Color_RGBA*);
Expression* operator()(Color_HSLA*);
Expression* operator()(Boolean*);
Expression* operator()(String_Schema*);
Expression* operator()(String_Quoted*);
Expression* operator()(String_Constant*);
Media_Query* operator()(Media_Query*);
Expression* operator()(Media_Query_Expression*);
Expression* operator()(At_Root_Query*);
Expression* operator()(SupportsOperation*);
Expression* operator()(SupportsNegation*);
Expression* operator()(SupportsDeclaration*);
Expression* operator()(Supports_Interpolation*);
Expression* operator()(Null*);
Expression* operator()(Argument*);
Expression* operator()(Arguments*);
Expression* operator()(Comment*);
// these will return selectors
SelectorList* operator()(SelectorList*);
SelectorList* operator()(ComplexSelector*);
CompoundSelector* operator()(CompoundSelector*);
SelectorComponent* operator()(SelectorComponent*);
SimpleSelector* operator()(SimpleSelector* s);
PseudoSelector* operator()(PseudoSelector* s);
// they don't have any specific implementation (yet)
IDSelector* operator()(IDSelector* s) { return s; };
ClassSelector* operator()(ClassSelector* s) { return s; };
TypeSelector* operator()(TypeSelector* s) { return s; };
AttributeSelector* operator()(AttributeSelector* s) { return s; };
PlaceholderSelector* operator()(PlaceholderSelector* s) { return s; };
// actual evaluated selectors
SelectorList* operator()(Selector_Schema*);
Expression* operator()(Parent_Reference*);
// generic fallback
template <typename U>
Expression* fallback(U x)
{ return Cast<Expression>(x); }
private:
void interpolation(Context& ctx, sass::string& res, ExpressionObj ex, bool into_quotes, bool was_itpl = false);
};
}
#endif

View File

@@ -0,0 +1,75 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "expand.hpp"
#include "eval.hpp"
#include "ast.hpp"
namespace Sass {
SelectorList* Eval::operator()(SelectorList* s)
{
sass::vector<SelectorListObj> rv;
SelectorListObj sl = SASS_MEMORY_NEW(SelectorList, s->pstate());
for (size_t i = 0, iL = s->length(); i < iL; ++i) {
rv.push_back(operator()(s->get(i)));
}
// we should actually permutate parent first
// but here we have permutated the selector first
size_t round = 0;
while (round != sass::string::npos) {
bool abort = true;
for (size_t i = 0, iL = rv.size(); i < iL; ++i) {
if (rv[i]->length() > round) {
sl->append((*rv[i])[round]);
abort = false;
}
}
if (abort) {
round = sass::string::npos;
}
else {
++round;
}
}
return sl.detach();
}
SelectorComponent* Eval::operator()(SelectorComponent* s)
{
return {};
}
SelectorList* Eval::operator()(ComplexSelector* s)
{
bool implicit_parent = !exp.old_at_root_without_rule;
if (is_in_selector_schema) exp.pushNullSelector();
SelectorListObj other = s->resolve_parent_refs(
exp.getOriginalStack(), traces, implicit_parent);
if (is_in_selector_schema) exp.popNullSelector();
for (size_t i = 0; i < other->length(); i++) {
ComplexSelectorObj sel = other->at(i);
for (size_t n = 0; n < sel->length(); n++) {
if (CompoundSelectorObj comp = Cast<CompoundSelector>(sel->at(n))) {
sel->at(n) = operator()(comp);
}
}
}
return other.detach();
}
CompoundSelector* Eval::operator()(CompoundSelector* s)
{
for (size_t i = 0; i < s->length(); i++) {
SimpleSelector* ss = s->at(i);
// skip parents here (called via resolve_parent_refs)
s->at(i) = Cast<SimpleSelector>(ss->perform(this));
}
return s;
}
}

View File

@@ -0,0 +1,875 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <iostream>
#include <typeinfo>
#include "ast.hpp"
#include "expand.hpp"
#include "bind.hpp"
#include "eval.hpp"
#include "backtrace.hpp"
#include "context.hpp"
#include "parser.hpp"
#include "sass_functions.hpp"
#include "error_handling.hpp"
namespace Sass {
// simple endless recursion protection
const size_t maxRecursion = 500;
Expand::Expand(Context& ctx, Env* env, SelectorStack* stack, SelectorStack* originals)
: ctx(ctx),
traces(ctx.traces),
eval(Eval(*this)),
recursions(0),
in_keyframes(false),
at_root_without_rule(false),
old_at_root_without_rule(false),
env_stack(),
block_stack(),
call_stack(),
selector_stack(),
originalStack(),
mediaStack()
{
env_stack.push_back(nullptr);
env_stack.push_back(env);
block_stack.push_back(nullptr);
call_stack.push_back({});
if (stack == NULL) { pushToSelectorStack({}); }
else {
for (auto item : *stack) {
if (item.isNull()) pushToSelectorStack({});
else pushToSelectorStack(item);
}
}
if (originals == NULL) { pushToOriginalStack({}); }
else {
for (auto item : *stack) {
if (item.isNull()) pushToOriginalStack({});
else pushToOriginalStack(item);
}
}
mediaStack.push_back({});
}
Env* Expand::environment()
{
if (env_stack.size() > 0)
return env_stack.back();
return 0;
}
void Expand::pushNullSelector()
{
pushToSelectorStack({});
pushToOriginalStack({});
}
void Expand::popNullSelector()
{
popFromOriginalStack();
popFromSelectorStack();
}
SelectorStack Expand::getOriginalStack()
{
return originalStack;
}
SelectorStack Expand::getSelectorStack()
{
return selector_stack;
}
SelectorListObj& Expand::selector()
{
if (selector_stack.size() > 0) {
auto& sel = selector_stack.back();
if (sel.isNull()) return sel;
return sel;
}
// Avoid the need to return copies
// We always want an empty first item
selector_stack.push_back({});
return selector_stack.back();;
}
SelectorListObj& Expand::original()
{
if (originalStack.size() > 0) {
auto& sel = originalStack.back();
if (sel.isNull()) return sel;
return sel;
}
// Avoid the need to return copies
// We always want an empty first item
originalStack.push_back({});
return originalStack.back();
}
SelectorListObj Expand::popFromSelectorStack()
{
SelectorListObj last = selector_stack.back();
if (selector_stack.size() > 0)
selector_stack.pop_back();
if (last.isNull()) return {};
return last;
}
void Expand::pushToSelectorStack(SelectorListObj selector)
{
selector_stack.push_back(selector);
}
SelectorListObj Expand::popFromOriginalStack()
{
SelectorListObj last = originalStack.back();
if (originalStack.size() > 0)
originalStack.pop_back();
if (last.isNull()) return {};
return last;
}
void Expand::pushToOriginalStack(SelectorListObj selector)
{
originalStack.push_back(selector);
}
// blocks create new variable scopes
Block* 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_Obj bb = SASS_MEMORY_NEW(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 may throw up!
this->append_block(b);
// revert block and env stack
this->block_stack.pop_back();
this->env_stack.pop_back();
// return copy
return bb.detach();
}
Statement* Expand::operator()(StyleRule* r)
{
LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);
if (in_keyframes) {
Block* bb = operator()(r->block());
Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
if (r->schema()) {
pushNullSelector();
k->name(eval(r->schema()));
popNullSelector();
}
else if (r->selector()) {
if (SelectorListObj s = r->selector()) {
pushNullSelector();
k->name(eval(s));
popNullSelector();
}
}
return k.detach();
}
if (r->schema()) {
SelectorListObj sel = eval(r->schema());
r->selector(sel);
for (auto complex : sel->elements()) {
// ToDo: maybe we can get rid of chroots?
complex->chroots(complex->has_real_parent_ref());
}
}
// reset when leaving scope
LOCAL_FLAG(at_root_without_rule, false);
SelectorListObj evaled = eval(r->selector());
// do not connect parent again
Env env(environment());
if (block_stack.back()->is_root()) {
env_stack.push_back(&env);
}
Block_Obj blk;
pushToSelectorStack(evaled);
// The copy is needed for parent reference evaluation
// dart-sass stores it as `originalSelector` member
pushToOriginalStack(SASS_MEMORY_COPY(evaled));
ctx.extender.addSelector(evaled, mediaStack.back());
if (r->block()) blk = operator()(r->block());
popFromOriginalStack();
popFromSelectorStack();
StyleRule* rr = SASS_MEMORY_NEW(StyleRule,
r->pstate(),
evaled,
blk);
if (block_stack.back()->is_root()) {
env_stack.pop_back();
}
rr->is_root(r->is_root());
rr->tabs(r->tabs());
return rr;
}
Statement* Expand::operator()(SupportsRule* f)
{
ExpressionObj condition = f->condition()->perform(&eval);
SupportsRuleObj ff = SASS_MEMORY_NEW(SupportsRule,
f->pstate(),
Cast<SupportsCondition>(condition),
operator()(f->block()));
return ff.detach();
}
sass::vector<CssMediaQuery_Obj> Expand::mergeMediaQueries(
const sass::vector<CssMediaQuery_Obj>& lhs,
const sass::vector<CssMediaQuery_Obj>& rhs)
{
sass::vector<CssMediaQuery_Obj> queries;
for (CssMediaQuery_Obj query1 : lhs) {
for (CssMediaQuery_Obj query2 : rhs) {
CssMediaQuery_Obj result = query1->merge(query2);
if (result && !result->empty()) {
queries.push_back(result);
}
}
}
return queries;
}
Statement* Expand::operator()(MediaRule* m)
{
ExpressionObj mq = eval(m->schema());
sass::string str_mq(mq->to_css(ctx.c_options));
ItplFile* source = SASS_MEMORY_NEW(ItplFile,
str_mq.c_str(), m->pstate());
Parser parser(source, ctx, traces);
// Create a new CSS only representation of the media rule
CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());
sass::vector<CssMediaQuery_Obj> parsed = parser.parseCssMediaQueries();
if (mediaStack.size() && mediaStack.back()) {
auto& parent = mediaStack.back()->elements();
css->concat(mergeMediaQueries(parent, parsed));
}
else {
css->concat(parsed);
}
mediaStack.push_back(css);
css->block(operator()(m->block()));
mediaStack.pop_back();
return css.detach();
}
Statement* Expand::operator()(AtRootRule* a)
{
Block_Obj ab = a->block();
ExpressionObj ae = a->expression();
if (ae) ae = ae->perform(&eval);
else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());
LOCAL_FLAG(at_root_without_rule, Cast<At_Root_Query>(ae)->exclude("rule"));
LOCAL_FLAG(in_keyframes, false);
;
Block_Obj bb = ab ? operator()(ab) : NULL;
AtRootRuleObj aa = SASS_MEMORY_NEW(AtRootRule,
a->pstate(),
bb,
Cast<At_Root_Query>(ae));
return aa.detach();
}
Statement* Expand::operator()(AtRule* a)
{
LOCAL_FLAG(in_keyframes, a->is_keyframes());
Block* ab = a->block();
SelectorList* as = a->selector();
Expression* av = a->value();
pushNullSelector();
if (av) av = av->perform(&eval);
if (as) as = eval(as);
popNullSelector();
Block* bb = ab ? operator()(ab) : NULL;
AtRule* aa = SASS_MEMORY_NEW(AtRule,
a->pstate(),
a->keyword(),
as,
bb,
av);
return aa;
}
Statement* Expand::operator()(Declaration* d)
{
Block_Obj ab = d->block();
String_Obj old_p = d->property();
ExpressionObj prop = old_p->perform(&eval);
String_Obj new_p = Cast<String>(prop);
// we might get a color back
if (!new_p) {
sass::string str(prop->to_string(ctx.c_options));
new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);
}
ExpressionObj value = d->value();
if (value) value = value->perform(&eval);
Block_Obj bb = ab ? operator()(ab) : NULL;
if (!bb) {
if (!value || (value->is_invisible() && !d->is_important())) {
if (d->is_custom_property()) {
error("Custom property values may not be empty.", d->value()->pstate(), traces);
} else {
return nullptr;
}
}
}
Declaration* decl = SASS_MEMORY_NEW(Declaration,
d->pstate(),
new_p,
value,
d->is_important(),
d->is_custom_property(),
bb);
decl->tabs(d->tabs());
return decl;
}
Statement* Expand::operator()(Assignment* a)
{
Env* env = environment();
const sass::string& var(a->variable());
if (a->is_global()) {
if (!env->has_global(var)) {
deprecated(
"!global assignments won't be able to declare new variables in future versions.",
"Consider adding `" + var + ": null` at the top level.",
true, a->pstate());
}
if (a->is_default()) {
if (env->has_global(var)) {
ExpressionObj e = 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_Obj node = cur->get_local(var)) {
ExpressionObj e = 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_Obj node = env->get_global(var)) {
ExpressionObj e = 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_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());
if (imp->import_queries() && imp->import_queries()->size()) {
ExpressionObj ex = imp->import_queries()->perform(&eval);
result->import_queries(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.detach();
}
Statement* Expand::operator()(Import_Stub* i)
{
traces.push_back(Backtrace(i->pstate()));
// get parent node from call stack
AST_Node_Obj parent = call_stack.back();
if (Cast<Block>(parent) == NULL) {
error("Import directives may not be used within control directives or mixins.", i->pstate(), traces);
}
// 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);
Block_Obj trace_block = SASS_MEMORY_NEW(Block, i->pstate());
Trace_Obj trace = SASS_MEMORY_NEW(Trace, i->pstate(), i->imp_path(), trace_block, 'i');
block_stack.back()->append(trace);
block_stack.push_back(trace_block);
const sass::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();
block_stack.pop_back();
traces.pop_back();
return 0;
}
Statement* Expand::operator()(WarningRule* w)
{
// eval handles this too, because warnings may occur in functions
w->perform(&eval);
return 0;
}
Statement* Expand::operator()(ErrorRule* e)
{
// eval handles this too, because errors may occur in functions
e->perform(&eval);
return 0;
}
Statement* Expand::operator()(DebugRule* d)
{
// eval handles this too, because warnings may occur in functions
d->perform(&eval);
return 0;
}
Statement* Expand::operator()(Comment* c)
{
if (ctx.output_style() == COMPRESSED) {
// comments should not be evaluated in compact
// https://github.com/sass/libsass/issues/2359
if (!c->is_important()) return NULL;
}
eval.is_in_comment = true;
Comment* rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());
eval.is_in_comment = false;
// TODO: eval the text, once we're parsing/storing it as a String_Schema
return rv;
}
Statement* Expand::operator()(If* i)
{
Env env(environment(), true);
env_stack.push_back(&env);
call_stack.push_back(i);
ExpressionObj rv = i->predicate()->perform(&eval);
if (*rv) {
append_block(i->block());
}
else {
Block* alt = i->alternative();
if (alt) append_block(alt);
}
call_stack.pop_back();
env_stack.pop_back();
return 0;
}
// For does not create a new env scope
// But iteration vars are reset afterwards
Statement* Expand::operator()(ForRule* f)
{
sass::string variable(f->variable());
ExpressionObj low = f->lower_bound()->perform(&eval);
if (low->concrete_type() != Expression::NUMBER) {
traces.push_back(Backtrace(low->pstate()));
throw Exception::TypeMismatch(traces, *low, "integer");
}
ExpressionObj high = f->upper_bound()->perform(&eval);
if (high->concrete_type() != Expression::NUMBER) {
traces.push_back(Backtrace(high->pstate()));
throw Exception::TypeMismatch(traces, *high, "integer");
}
Number_Obj sass_start = Cast<Number>(low);
Number_Obj sass_end = Cast<Number>(high);
// check if units are valid for sequence
if (sass_start->unit() != sass_end->unit()) {
sass::ostream msg; msg << "Incompatible units: '"
<< sass_start->unit() << "' and '"
<< sass_end->unit() << "'.";
error(msg.str(), low->pstate(), traces);
}
double start = sass_start->value();
double end = sass_end->value();
// only create iterator once in this environment
Env env(environment(), true);
env_stack.push_back(&env);
call_stack.push_back(f);
Block* body = f->block();
if (start < end) {
if (f->is_inclusive()) ++end;
for (double i = start;
i < end;
++i) {
Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());
env.set_local(variable, it);
append_block(body);
}
} else {
if (f->is_inclusive()) --end;
for (double i = start;
i > end;
--i) {
Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());
env.set_local(variable, it);
append_block(body);
}
}
call_stack.pop_back();
env_stack.pop_back();
return 0;
}
// Eval does not create a new env scope
// But iteration vars are reset afterwards
Statement* Expand::operator()(EachRule* e)
{
sass::vector<sass::string> variables(e->variables());
ExpressionObj expr = e->list()->perform(&eval);
List_Obj list;
Map_Obj map;
if (expr->concrete_type() == Expression::MAP) {
map = Cast<Map>(expr);
}
else if (SelectorList * ls = Cast<SelectorList>(expr)) {
ExpressionObj rv = Listize::perform(ls);
list = Cast<List>(rv);
}
else if (expr->concrete_type() != Expression::LIST) {
list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
list->append(expr);
}
else {
list = Cast<List>(expr);
}
// remember variables and then reset them
Env env(environment(), true);
env_stack.push_back(&env);
call_stack.push_back(e);
Block* body = e->block();
if (map) {
for (auto key : map->keys()) {
ExpressionObj k = key->perform(&eval);
ExpressionObj v = map->at(key)->perform(&eval);
if (variables.size() == 1) {
List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
variable->append(k);
variable->append(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();
if (list->length() == 1 && Cast<SelectorList>(list)) {
list = Cast<List>(list);
}
for (size_t i = 0, L = list->length(); i < L; ++i) {
ExpressionObj item = list->at(i);
// unwrap value if the expression is an argument
if (Argument_Obj arg = Cast<Argument>(item)) item = arg->value();
// check if we got passed a list of args (investigate)
if (List_Obj scalars = Cast<List>(item)) {
if (variables.size() == 1) {
List_Obj 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) {
env.set_local(variables[j], j >= scalars->length()
? SASS_MEMORY_NEW(Null, expr->pstate())
: (*scalars)[j]->perform(&eval));
}
}
} else {
if (variables.size() > 0) {
env.set_local(variables.at(0), item);
for (size_t j = 1, K = variables.size(); j < K; ++j) {
ExpressionObj res = SASS_MEMORY_NEW(Null, expr->pstate());
env.set_local(variables[j], res);
}
}
}
append_block(body);
}
}
call_stack.pop_back();
env_stack.pop_back();
return 0;
}
Statement* Expand::operator()(WhileRule* w)
{
ExpressionObj pred = w->predicate();
Block* body = w->block();
Env env(environment(), true);
env_stack.push_back(&env);
call_stack.push_back(w);
ExpressionObj cond = pred->perform(&eval);
while (!cond->is_false()) {
append_block(body);
cond = pred->perform(&eval);
}
call_stack.pop_back();
env_stack.pop_back();
return 0;
}
Statement* Expand::operator()(Return* r)
{
error("@return may only be used within a function", r->pstate(), traces);
return 0;
}
Statement* Expand::operator()(ExtendRule* e)
{
// evaluate schema first
if (e->schema()) {
e->selector(eval(e->schema()));
e->isOptional(e->selector()->is_optional());
}
// evaluate the selector
e->selector(eval(e->selector()));
if (e->selector()) {
for (auto complex : e->selector()->elements()) {
if (complex->length() != 1) {
error("complex selectors may not be extended.", complex->pstate(), traces);
}
if (const CompoundSelector* compound = complex->first()->getCompound()) {
if (compound->length() != 1) {
sass::ostream sels; bool addComma = false;
sels << "Compound selectors may no longer be extended.\n";
sels << "Consider `@extend ";
for (auto sel : compound->elements()) {
if (addComma) sels << ", ";
sels << sel->to_sass();
addComma = true;
}
sels << "` instead.\n";
sels << "See http://bit.ly/ExtendCompound for details.";
warning(sels.str(), compound->pstate());
// Make this an error once deprecation is over
for (SimpleSelectorObj simple : compound->elements()) {
// Pass every selector we ever see to extender (to make them findable for extend)
ctx.extender.addExtension(selector(), simple, mediaStack.back(), e->isOptional());
}
}
else {
// Pass every selector we ever see to extender (to make them findable for extend)
ctx.extender.addExtension(selector(), compound->first(), mediaStack.back(), e->isOptional());
}
}
else {
error("complex selectors may not be extended.", complex->pstate(), traces);
}
}
}
return nullptr;
}
Statement* Expand::operator()(Definition* d)
{
Env* env = environment();
Definition_Obj dd = SASS_MEMORY_COPY(d);
env->local_frame()[d->name() +
(d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;
if (d->type() == Definition::FUNCTION && (
Prelexer::calc_fn_call(d->name().c_str()) ||
d->name() == "element" ||
d->name() == "expression" ||
d->name() == "url"
)) {
deprecated(
"Naming a function \"" + d->name() + "\" is disallowed and will be an error in future versions of Sass.",
"This name conflicts with an existing CSS function with special parse rules.",
false, d->pstate()
);
}
// set the static link so we can have lexical scoping
dd->environment(env);
return 0;
}
Statement* Expand::operator()(Mixin_Call* c)
{
if (recursions > maxRecursion) {
throw Exception::StackError(traces, *c);
}
recursions ++;
Env* env = environment();
sass::string full_name(c->name() + "[m]");
if (!env->has(full_name)) {
error("no mixin named " + c->name(), c->pstate(), traces);
}
Definition_Obj def = Cast<Definition>((*env)[full_name]);
Block_Obj body = def->block();
Parameters_Obj params = def->parameters();
if (c->block() && c->name() != "@content" && !body->has_content()) {
error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces);
}
ExpressionObj rv = c->arguments()->perform(&eval);
Arguments_Obj args = Cast<Arguments>(rv);
sass::string msg(", in mixin `" + c->name() + "`");
traces.push_back(Backtrace(c->pstate(), msg));
ctx.callee_stack.push_back({
c->name().c_str(),
c->pstate().getPath(),
c->pstate().getLine(),
c->pstate().getColumn(),
SASS_CALLEE_MIXIN,
{ env }
});
Env new_env(def->environment());
env_stack.push_back(&new_env);
if (c->block()) {
Parameters_Obj params = c->block_parameters();
if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate());
// represent mixin content blocks as thunks/closures
Definition_Obj thunk = SASS_MEMORY_NEW(Definition,
c->pstate(),
"@content",
params,
c->block(),
Definition::MIXIN);
thunk->environment(env);
new_env.local_frame()["@content[m]"] = thunk;
}
bind(sass::string("Mixin"), c->name(), params, args, &new_env, &eval, traces);
Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());
Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);
env->set_global("is_in_mixin", bool_true);
if (Block* pr = block_stack.back()) {
trace_block->is_root(pr->is_root());
}
block_stack.push_back(trace_block);
for (auto bb : body->elements()) {
if (StyleRule* r = Cast<StyleRule>(bb)) {
r->is_root(trace_block->is_root());
}
Statement_Obj ith = bb->perform(this);
if (ith) trace->block()->append(ith);
}
block_stack.pop_back();
env->del_global("is_in_mixin");
ctx.callee_stack.pop_back();
env_stack.pop_back();
traces.pop_back();
recursions --;
return trace.detach();
}
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;
Arguments_Obj args = c->arguments();
if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate());
Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,
c->pstate(),
"@content",
args);
Trace_Obj trace = Cast<Trace>(call->perform(this));
return trace.detach();
}
// process and add to last block on stack
inline void Expand::append_block(Block* b)
{
if (b->is_root()) call_stack.push_back(b);
for (size_t i = 0, L = b->length(); i < L; ++i) {
Statement* stm = b->at(i);
Statement_Obj ith = stm->perform(this);
if (ith) block_stack.back()->append(ith);
}
if (b->is_root()) call_stack.pop_back();
}
}

View File

@@ -0,0 +1,98 @@
#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;
struct Backtrace;
class Expand : public Operation_CRTP<Statement*, Expand> {
public:
Env* environment();
SelectorListObj& selector();
SelectorListObj& original();
SelectorListObj popFromSelectorStack();
SelectorStack getOriginalStack();
SelectorStack getSelectorStack();
void pushNullSelector();
void popNullSelector();
void pushToSelectorStack(SelectorListObj selector);
SelectorListObj popFromOriginalStack();
void pushToOriginalStack(SelectorListObj selector);
Context& ctx;
Backtraces& traces;
Eval eval;
size_t recursions;
bool in_keyframes;
bool at_root_without_rule;
bool old_at_root_without_rule;
// it's easier to work with vectors
EnvStack env_stack;
BlockStack block_stack;
CallStack call_stack;
private:
SelectorStack selector_stack;
public:
SelectorStack originalStack;
MediaStack mediaStack;
Boolean_Obj bool_true;
private:
sass::vector<CssMediaQuery_Obj> mergeMediaQueries(const sass::vector<CssMediaQuery_Obj>& lhs, const sass::vector<CssMediaQuery_Obj>& rhs);
public:
Expand(Context&, Env*, SelectorStack* stack = nullptr, SelectorStack* original = nullptr);
~Expand() { }
Block* operator()(Block*);
Statement* operator()(StyleRule*);
Statement* operator()(MediaRule*);
// Css StyleRule is already static
// Statement* operator()(CssMediaRule*);
Statement* operator()(SupportsRule*);
Statement* operator()(AtRootRule*);
Statement* operator()(AtRule*);
Statement* operator()(Declaration*);
Statement* operator()(Assignment*);
Statement* operator()(Import*);
Statement* operator()(Import_Stub*);
Statement* operator()(WarningRule*);
Statement* operator()(ErrorRule*);
Statement* operator()(DebugRule*);
Statement* operator()(Comment*);
Statement* operator()(If*);
Statement* operator()(ForRule*);
Statement* operator()(EachRule*);
Statement* operator()(WhileRule*);
Statement* operator()(Return*);
Statement* operator()(ExtendRule*);
Statement* operator()(Definition*);
Statement* operator()(Mixin_Call*);
Statement* operator()(Content*);
void append_block(Block*);
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,399 @@
#ifndef SASS_EXTENDER_H
#define SASS_EXTENDER_H
#include <set>
#include <map>
#include <string>
#include "ast_helpers.hpp"
#include "ast_fwd_decl.hpp"
#include "operation.hpp"
#include "extension.hpp"
#include "backtrace.hpp"
#include "ordered_map.hpp"
namespace Sass {
// ##########################################################################
// Different hash map types used by extender
// ##########################################################################
// This is special (ptrs!)
typedef std::unordered_set<
ComplexSelectorObj,
ObjPtrHash,
ObjPtrEquality
> ExtCplxSelSet;
typedef std::unordered_set<
SimpleSelectorObj,
ObjHash,
ObjEquality
> ExtSmplSelSet;
typedef std::unordered_set<
SelectorListObj,
ObjPtrHash,
ObjPtrEquality
> ExtListSelSet;
typedef std::unordered_map<
SimpleSelectorObj,
ExtListSelSet,
ObjHash,
ObjEquality
> ExtSelMap;
typedef ordered_map<
ComplexSelectorObj,
Extension,
ObjHash,
ObjEquality
> ExtSelExtMapEntry;
typedef std::unordered_map<
SimpleSelectorObj,
ExtSelExtMapEntry,
ObjHash,
ObjEquality
> ExtSelExtMap;
typedef std::unordered_map <
SimpleSelectorObj,
sass::vector<
Extension
>,
ObjHash,
ObjEquality
> ExtByExtMap;
class Extender : public Operation_CRTP<void, Extender> {
public:
enum ExtendMode { TARGETS, REPLACE, NORMAL, };
private:
// ##########################################################################
// The mode that controls this extender's behavior.
// ##########################################################################
ExtendMode mode;
// ##########################################################################
// Shared backtraces with context and expander. Needed the throw
// errors when e.g. extending across media query boundaries.
// ##########################################################################
Backtraces& traces;
// ##########################################################################
// A map from all simple selectors in the stylesheet to the rules that
// contain them.This is used to find which rules an `@extend` applies to.
// ##########################################################################
ExtSelMap selectors;
// ##########################################################################
// A map from all extended simple selectors
// to the sources of those extensions.
// ##########################################################################
ExtSelExtMap extensions;
// ##########################################################################
// A map from all simple selectors in extenders to
// the extensions that those extenders define.
// ##########################################################################
ExtByExtMap extensionsByExtender;
// ##########################################################################
// A map from CSS rules to the media query contexts they're defined in.
// This tracks the contexts in which each style rule is defined.
// If a rule is defined at the top level, it doesn't have an entry.
// ##########################################################################
ordered_map<
SelectorListObj,
CssMediaRuleObj,
ObjPtrHash,
ObjPtrEquality
> mediaContexts;
// ##########################################################################
// A map from [SimpleSelector]s to the specificity of their source selectors.
// This tracks the maximum specificity of the [ComplexSelector] that originally
// contained each [SimpleSelector]. This allows us to ensure we don't trim any
// selectors that need to exist to satisfy the [second law that of extend][].
// [second law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184
// ##########################################################################
std::unordered_map<
SimpleSelectorObj,
size_t,
ObjPtrHash,
ObjPtrEquality
> sourceSpecificity;
// ##########################################################################
// A set of [ComplexSelector]s that were originally part of their
// component [SelectorList]s, as opposed to being added by `@extend`.
// This allows us to ensure that we don't trim any selectors
// that need to exist to satisfy the [first law of extend][].
// ##########################################################################
ExtCplxSelSet originals;
public:
// Constructor without default [mode].
// [traces] are needed to throw errors.
Extender(Backtraces& traces);
// ##########################################################################
// Constructor with specific [mode].
// [traces] are needed to throw errors.
// ##########################################################################
Extender(ExtendMode mode, Backtraces& traces);
// ##########################################################################
// Empty desctructor
// ##########################################################################
~Extender() {};
// ##########################################################################
// Extends [selector] with [source] extender and [targets] extendees.
// This works as though `source {@extend target}` were written in the
// stylesheet, with the exception that [target] can contain compound
// selectors which must be extended as a unit.
// ##########################################################################
static SelectorListObj extend(
SelectorListObj& selector,
const SelectorListObj& source,
const SelectorListObj& target,
Backtraces& traces);
// ##########################################################################
// Returns a copy of [selector] with [targets] replaced by [source].
// ##########################################################################
static SelectorListObj replace(
SelectorListObj& selector,
const SelectorListObj& source,
const SelectorListObj& target,
Backtraces& traces);
// ##########################################################################
// Adds [selector] to this extender, with [selectorSpan] as the span covering
// the selector and [ruleSpan] as the span covering the entire style rule.
// Extends [selector] using any registered extensions, then returns an empty
// [ModifiableCssStyleRule] with the resulting selector. If any more relevant
// extensions are added, the returned rule is automatically updated.
// The [mediaContext] is the media query context in which the selector was
// defined, or `null` if it was defined at the top level of the document.
// ##########################################################################
void addSelector(
const SelectorListObj& selector,
const CssMediaRuleObj& mediaContext);
// ##########################################################################
// Registers the [SimpleSelector]s in [list]
// to point to [rule] in [selectors].
// ##########################################################################
void registerSelector(
const SelectorListObj& list,
const SelectorListObj& rule);
// ##########################################################################
// Adds an extension to this extender. The [extender] is the selector for the
// style rule in which the extension is defined, and [target] is the selector
// passed to `@extend`. The [extend] provides the extend span and indicates
// whether the extension is optional. The [mediaContext] defines the media query
// context in which the extension is defined. It can only extend selectors
// within the same context. A `null` context indicates no media queries.
// ##########################################################################
void addExtension(
const SelectorListObj& extender,
const SimpleSelectorObj& target,
const CssMediaRuleObj& mediaQueryContext,
bool is_optional = false);
// ##########################################################################
// The set of all simple selectors in style rules handled
// by this extender. This includes simple selectors that
// were added because of downstream extensions.
// ##########################################################################
ExtSmplSelSet getSimpleSelectors() const;
// ##########################################################################
// Check for extends that have not been satisfied.
// Returns true if any non-optional extension did not
// extend any selector. Updates the passed reference
// to point to that Extension for further analysis.
// ##########################################################################
bool checkForUnsatisfiedExtends(
Extension& unsatisfied) const;
private:
// ##########################################################################
// A helper function for [extend] and [replace].
// ##########################################################################
static SelectorListObj extendOrReplace(
SelectorListObj& selector,
const SelectorListObj& source,
const SelectorListObj& target,
const ExtendMode mode,
Backtraces& traces);
// ##########################################################################
// Returns an extension that combines [left] and [right]. Throws
// a [SassException] if [left] and [right] have incompatible
// media contexts. Throws an [ArgumentError] if [left]
// and [right] don't have the same extender and target.
// ##########################################################################
static Extension mergeExtension(
const Extension& lhs,
const Extension& rhs);
// ##########################################################################
// Extend [extensions] using [newExtensions].
// ##########################################################################
// Note: dart-sass throws an error in here
// ##########################################################################
void extendExistingStyleRules(
const ExtListSelSet& rules,
const ExtSelExtMap& newExtensions);
// ##########################################################################
// Extend [extensions] using [newExtensions]. Note that this does duplicate
// some work done by [_extendExistingStyleRules], but it's necessary to
// expand each extension's extender separately without reference to the full
// selector list, so that relevant results don't get trimmed too early.
// Returns `null` (Note: empty map) if there are no extensions to add.
// ##########################################################################
ExtSelExtMap extendExistingExtensions(
// Taking in a reference here makes MSVC debug stuck!?
const sass::vector<Extension>& extensions,
const ExtSelExtMap& newExtensions);
// ##########################################################################
// Extends [list] using [extensions].
// ##########################################################################
SelectorListObj extendList(
const SelectorListObj& list,
const ExtSelExtMap& extensions,
const CssMediaRuleObj& mediaContext);
// ##########################################################################
// Extends [complex] using [extensions], and
// returns the contents of a [SelectorList].
// ##########################################################################
sass::vector<ComplexSelectorObj> extendComplex(
// Taking in a reference here makes MSVC debug stuck!?
const ComplexSelectorObj& list,
const ExtSelExtMap& extensions,
const CssMediaRuleObj& mediaQueryContext);
// ##########################################################################
// Returns a one-off [Extension] whose
// extender is composed solely of [simple].
// ##########################################################################
Extension extensionForSimple(
const SimpleSelectorObj& simple) const;
// ##########################################################################
// Returns a one-off [Extension] whose extender is composed
// solely of a compound selector containing [simples].
// ##########################################################################
Extension extensionForCompound(
// Taking in a reference here makes MSVC debug stuck!?
const sass::vector<SimpleSelectorObj>& simples) const;
// ##########################################################################
// Extends [compound] using [extensions], and returns the
// contents of a [SelectorList]. The [inOriginal] parameter
// indicates whether this is in an original complex selector,
// meaning that [compound] should not be trimmed out.
// ##########################################################################
sass::vector<ComplexSelectorObj> extendCompound(
const CompoundSelectorObj& compound,
const ExtSelExtMap& extensions,
const CssMediaRuleObj& mediaQueryContext,
bool inOriginal = false);
// ##########################################################################
// Extends [simple] without extending the
// contents of any selector pseudos it contains.
// ##########################################################################
sass::vector<Extension> extendWithoutPseudo(
const SimpleSelectorObj& simple,
const ExtSelExtMap& extensions,
ExtSmplSelSet* targetsUsed) const;
// ##########################################################################
// Extends [simple] and also extending the
// contents of any selector pseudos it contains.
// ##########################################################################
sass::vector<sass::vector<Extension>> extendSimple(
const SimpleSelectorObj& simple,
const ExtSelExtMap& extensions,
const CssMediaRuleObj& mediaQueryContext,
ExtSmplSelSet* targetsUsed);
// ##########################################################################
// Inner loop helper for [extendPseudo] function
// ##########################################################################
static sass::vector<ComplexSelectorObj> extendPseudoComplex(
const ComplexSelectorObj& complex,
const PseudoSelectorObj& pseudo,
const CssMediaRuleObj& mediaQueryContext);
// ##########################################################################
// Extends [pseudo] using [extensions], and returns
// a list of resulting pseudo selectors.
// ##########################################################################
sass::vector<PseudoSelectorObj> extendPseudo(
const PseudoSelectorObj& pseudo,
const ExtSelExtMap& extensions,
const CssMediaRuleObj& mediaQueryContext);
// ##########################################################################
// Rotates the element in list from [start] (inclusive) to [end] (exclusive)
// one index higher, looping the final element back to [start].
// ##########################################################################
static void rotateSlice(
sass::vector<ComplexSelectorObj>& list,
size_t start, size_t end);
// ##########################################################################
// Removes elements from [selectors] if they're subselectors of other
// elements. The [isOriginal] callback indicates which selectors are
// original to the document, and thus should never be trimmed.
// ##########################################################################
sass::vector<ComplexSelectorObj> trim(
const sass::vector<ComplexSelectorObj>& selectors,
const ExtCplxSelSet& set) const;
// ##########################################################################
// Returns the maximum specificity of the given [simple] source selector.
// ##########################################################################
size_t maxSourceSpecificity(const SimpleSelectorObj& simple) const;
// ##########################################################################
// Returns the maximum specificity for sources that went into producing [compound].
// ##########################################################################
size_t maxSourceSpecificity(const CompoundSelectorObj& compound) const;
// ##########################################################################
// Helper function used as callbacks on lists
// ##########################################################################
static bool dontTrimComplex(
const ComplexSelector* complex2,
const ComplexSelector* complex1,
const size_t maxSpecificity);
// ##########################################################################
// Helper function used as callbacks on lists
// ##########################################################################
static bool hasExactlyOne(const ComplexSelectorObj& vec);
static bool hasMoreThanOne(const ComplexSelectorObj& vec);
};
}
#endif

View File

@@ -0,0 +1,43 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast_helpers.hpp"
#include "extension.hpp"
#include "ast.hpp"
namespace Sass {
// ##########################################################################
// Static function to create a copy with a new extender
// ##########################################################################
Extension Extension::withExtender(const ComplexSelectorObj& newExtender) const
{
Extension extension(newExtender);
extension.specificity = specificity;
extension.isOptional = isOptional;
extension.target = target;
return extension;
}
// ##########################################################################
// Asserts that the [mediaContext] for a selector is
// compatible with the query context for this extender.
// ##########################################################################
void Extension::assertCompatibleMediaContext(CssMediaRuleObj mediaQueryContext, Backtraces& traces) const
{
if (this->mediaContext.isNull()) return;
if (mediaQueryContext && ObjPtrEqualityFn(mediaContext->block(), mediaQueryContext->block())) return;
if (ObjEqualityFn<CssMediaRuleObj>(mediaQueryContext, mediaContext)) return;
throw Exception::ExtendAcrossMedia(traces, *this);
}
// ##########################################################################
// ##########################################################################
}

View File

@@ -0,0 +1,89 @@
#ifndef SASS_EXTENSION_H
#define SASS_EXTENSION_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <unordered_map>
#include <unordered_set>
#include "ast_fwd_decl.hpp"
#include "backtrace.hpp"
namespace Sass {
class Extension {
public:
// The selector in which the `@extend` appeared.
ComplexSelectorObj extender;
// The selector that's being extended.
// `null` for one-off extensions.
SimpleSelectorObj target;
// The minimum specificity required for any
// selector generated from this extender.
size_t specificity;
// Whether this extension is optional.
bool isOptional;
// Whether this is a one-off extender representing a selector that was
// originally in the document, rather than one defined with `@extend`.
bool isOriginal;
bool isSatisfied;
// The media query context to which this extend is restricted,
// or `null` if it can apply within any context.
CssMediaRuleObj mediaContext;
// Creates a one-off extension that's not intended to be modified over time.
// If [specificity] isn't passed, it defaults to `extender.maxSpecificity`.
Extension(ComplexSelectorObj extender) :
extender(extender),
target({}),
specificity(0),
isOptional(true),
isOriginal(false),
isSatisfied(false),
mediaContext({}) {
}
// Copy constructor
Extension(const Extension& extension) :
extender(extension.extender),
target(extension.target),
specificity(extension.specificity),
isOptional(extension.isOptional),
isOriginal(extension.isOriginal),
isSatisfied(extension.isSatisfied),
mediaContext(extension.mediaContext) {
}
// Default constructor
Extension() :
extender({}),
target({}),
specificity(0),
isOptional(false),
isOriginal(false),
isSatisfied(false),
mediaContext({}) {
}
// Asserts that the [mediaContext] for a selector is
// compatible with the query context for this extender.
void assertCompatibleMediaContext(CssMediaRuleObj mediaContext, Backtraces& traces) const;
Extension withExtender(const ComplexSelectorObj& newExtender) const;
};
}
#endif

View File

@@ -0,0 +1,531 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#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 S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#else
# include <unistd.h>
#endif
#include <cstdio>
#include <vector>
#include <algorithm>
#include <sys/stat.h>
#include "file.hpp"
#include "context.hpp"
#include "prelexer.hpp"
#include "utf8_string.hpp"
#include "sass_functions.hpp"
#include "error_handling.hpp"
#include "util.hpp"
#include "util_string.hpp"
#include "sass2scss.h"
#ifdef _WIN32
# include <windows.h>
# ifdef _MSC_VER
# include <codecvt>
inline static Sass::sass::string wstring_to_string(const std::wstring& wstr)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_converter;
return wchar_converter.to_bytes(wstr);
}
# else // mingw(/gcc) does not support C++11's codecvt yet.
inline static Sass::sass::string wstring_to_string(const std::wstring &wstr)
{
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
Sass::sass::string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
# endif
#endif
namespace Sass {
namespace File {
// return the current directory
// always with forward slashes
// always with trailing slash
sass::string get_cwd()
{
const size_t wd_len = 4096;
#ifndef _WIN32
char wd[wd_len];
char* pwd = getcwd(wd, wd_len);
// we should check error for more detailed info (e.g. ENOENT)
// http://man7.org/linux/man-pages/man2/getcwd.2.html#ERRORS
if (pwd == NULL) throw Exception::OperationError("cwd gone missing");
sass::string cwd = pwd;
#else
wchar_t wd[wd_len];
wchar_t* pwd = _wgetcwd(wd, wd_len);
if (pwd == NULL) throw Exception::OperationError("cwd gone missing");
sass::string cwd = wstring_to_string(pwd);
//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 sass::string& path)
{
#ifdef _WIN32
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16
sass::string abspath(join_paths(get_cwd(), path));
if (!(abspath[0] == '/' && abspath[1] == '/')) {
abspath = "//?/" + abspath;
}
std::wstring wpath(UTF_8::convert_to_utf16(abspath));
std::replace(wpath.begin(), wpath.end(), '/', '\\');
DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);
if (rv > 32767) throw Exception::OperationError("Path is too long");
if (rv == 0) throw Exception::OperationError("Path could not be resolved");
DWORD dwAttrib = GetFileAttributesW(resolved);
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 sass::string& path)
{
#ifdef _WIN32
if (path.length() >= 2 && Util::ascii_isalpha(path[0]) && path[1] == ':') return true;
#endif
size_t i = 0;
// check if we have a protocol
if (path[i] && Util::ascii_isalpha(static_cast<unsigned char>(path[i]))) {
// skip over all alphanumeric characters
while (path[i] && Util::ascii_isalnum(static_cast<unsigned char>(path[i]))) ++i;
i = i && path[i] == ':' ? i + 1 : 0;
}
return path[i] == '/';
}
// helper function to find the last directory separator
inline size_t find_last_folder_separator(const sass::string& path, size_t limit = sass::string::npos)
{
size_t pos;
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 = sass::string::npos;
#endif
if (pos_p != sass::string::npos && pos_w != sass::string::npos) {
pos = std::max(pos_p, pos_w);
}
else if (pos_p != sass::string::npos) {
pos = pos_p;
}
else {
pos = pos_w;
}
return pos;
}
// return only the directory part of path
sass::string dir_name(const sass::string& path)
{
size_t pos = find_last_folder_separator(path);
if (pos == sass::string::npos) return "";
else return path.substr(0, pos+1);
}
// return only the filename part of path
sass::string base_name(const sass::string& path)
{
size_t pos = find_last_folder_separator(path);
if (pos == sass::string::npos) return path;
else return path.substr(pos+1);
}
// do a logical clean up of the path
// no physical check on the filesystem
sass::string make_canonical_path (sass::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)) != sass::string::npos) path.erase(pos, 2);
// remove all leading and trailing self references
while(path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2);
while((pos = path.length()) > 1 && path[pos - 2] == '/' && path[pos - 1] == '.') path.erase(pos - 2);
size_t proto = 0;
// check if we have a protocol
if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) {
// skip over all alphanumeric characters
while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(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)) != sass::string::npos) path.erase(pos, 1);
return path;
}
// join two path segments cleanly together
// but only if right side is not absolute yet
sass::string join_paths(sass::string l, sass::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 += '/';
// this does a logical cleanup of the right hand path
// Note that this does collapse x/../y sections into y.
// This is by design. If /foo on your system is a symlink
// to /bar/baz, then /foo/../cd is actually /bar/cd,
// not /cd as a naive ../ removal would give you.
// will only work on leading double dot dirs on rhs
// therefore it is safe if lhs is already resolved cwd
while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {
size_t L = l.length(), pos = find_last_folder_separator(l, L - 2);
bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\');
bool is_self = pos + 3 == L && (l[pos+1] == '.');
if (!is_self && !is_slash) r = r.substr(3);
else if (pos == sass::string::npos) break;
l = l.substr(0, pos == sass::string::npos ? pos : pos + 1);
}
return l + r;
}
sass::string path_for_console(const sass::string& rel_path, const sass::string& abs_path, const sass::string& orig_path)
{
// magic algorithm 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
sass::string rel2abs(const sass::string& path, const sass::string& base, const sass::string& cwd)
{
sass::string rv = make_canonical_path(join_paths(join_paths(cwd + "/", base + "/"), path));
#ifdef _WIN32
// On windows we may get an absolute path without directory
// In that case we should prepend the directory from the root
if (rv[0] == '/' && rv[1] != '/') {
rv.insert(0, cwd, 0, 2);
}
#endif
return rv;
}
// create a path that is relative to the given base directory
// path and base will first be resolved against cwd to make them absolute
sass::string abs2rel(const sass::string& path, const sass::string& base, const sass::string& cwd)
{
sass::string abs_path = rel2abs(path, cwd);
sass::string abs_base = rel2abs(base, cwd);
size_t proto = 0;
// check if we have a protocol
if (path[proto] && Util::ascii_isalpha(static_cast<unsigned char>(path[proto]))) {
// skip over all alphanumeric characters
while (path[proto] && Util::ascii_isalnum(static_cast<unsigned char>(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
sass::string stripped_uri = "";
sass::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 (Util::ascii_tolower(static_cast<unsigned char>(abs_path[i])) !=
Util::ascii_tolower(static_cast<unsigned char>(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;
}
}
sass::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
// (5) given + _index.scss
// (6) given + _index.sass
sass::vector<Include> resolve_includes(const sass::string& root, const sass::string& file, const sass::vector<sass::string>& exts)
{
sass::string filename = join_paths(root, file);
// split the filename
sass::string base(dir_name(file));
sass::string name(base_name(file));
sass::vector<Include> includes;
// create full path (maybe relative)
sass::string rel_path(join_paths(base, name));
sass::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 });
}
// index files
if (includes.size() == 0) {
// ignore directories that look like @import'able filename
for(auto ext : exts) {
if (ends_with(name, ext)) return includes;
}
// next test underscore index exts
for(auto ext : exts) {
rel_path = join_paths(base, join_paths(name, "_index" + ext));
abs_path = join_paths(root, rel_path);
if (file_exists(abs_path)) includes.push_back({{ rel_path, root }, abs_path });
}
// next test plain index exts
for(auto ext : exts) {
rel_path = join_paths(base, join_paths(name, "index" + 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;
}
sass::vector<sass::string> find_files(const sass::string& file, const sass::vector<sass::string> paths)
{
sass::vector<sass::string> includes;
for (sass::string path : paths) {
sass::string abs_path(join_paths(path, file));
if (file_exists(abs_path)) includes.push_back(abs_path);
}
return includes;
}
sass::vector<sass::string> find_files(const sass::string& file, struct Sass_Compiler* compiler)
{
// get the last import entry to get current base directory
// struct Sass_Options* options = sass_compiler_get_options(compiler);
Sass_Import_Entry import = sass_compiler_get_last_import(compiler);
const sass::vector<sass::string>& incs = compiler->cpp_ctx->include_paths;
// create the vector with paths to lookup
sass::vector<sass::string> paths(1 + incs.size());
paths.push_back(dir_name(import->abs_path));
paths.insert(paths.end(), incs.begin(), incs.end());
// dispatch to find files in paths
return find_files(file, paths);
}
// helper function to search one file in all include paths
// this is normally not used internally by libsass (C-API sugar)
sass::string find_file(const sass::string& file, const sass::vector<sass::string> paths)
{
if (file.empty()) return file;
auto res = find_files(file, paths);
return res.empty() ? "" : res.front();
}
// helper function to resolve a filename
sass::string find_include(const sass::string& file, const sass::vector<sass::string> paths)
{
// search in every include path for a match
for (size_t i = 0, S = paths.size(); i < S; ++i)
{
sass::vector<Include> resolved(resolve_includes(paths[i], file));
if (resolved.size()) return resolved[0].abs_path;
}
// nothing found
return sass::string("");
}
// try to load the given filename
// returned memory must be freed
// will auto convert .sass files
char* read_file(const sass::string& path)
{
#ifdef _WIN32
BYTE* pBuffer;
DWORD dwBytes;
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16
sass::string abspath(join_paths(get_cwd(), path));
if (!(abspath[0] == '/' && abspath[1] == '/')) {
abspath = "//?/" + abspath;
}
std::wstring wpath(UTF_8::convert_to_utf16(abspath));
std::replace(wpath.begin(), wpath.end(), '/', '\\');
DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);
if (rv > 32767) throw Exception::OperationError("Path is too long");
if (rv == 0) throw Exception::OperationError("Path could not be resolved");
HANDLE hFile = CreateFileW(resolved, 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
// and another one for edge-cases in lexer
pBuffer = (BYTE*)malloc((dwFileLength+2)*sizeof(BYTE));
ReadFile(hFile, pBuffer, dwFileLength, &dwBytes, NULL);
pBuffer[dwFileLength+0] = '\0';
pBuffer[dwFileLength+1] = '\0';
CloseHandle(hFile);
// just convert from unsigned char*
char* contents = (char*) pBuffer;
#else
// Read the file using `<cstdio>` instead of `<fstream>` for better portability.
// The `<fstream>` header initializes `<locale>` and this buggy in GCC4/5 with static linking.
// See:
// https://www.spinics.net/lists/gcchelp/msg46851.html
// https://github.com/sass/sassc-ruby/issues/128
struct stat st;
if (stat(path.c_str(), &st) == -1 || S_ISDIR(st.st_mode)) return 0;
FILE* fd = std::fopen(path.c_str(), "rb");
if (fd == nullptr) return nullptr;
const std::size_t size = st.st_size;
char* contents = static_cast<char*>(malloc(st.st_size + 2 * sizeof(char)));
if (std::fread(static_cast<void*>(contents), 1, size, fd) != size) {
free(contents);
std::fclose(fd);
return nullptr;
}
if (std::fclose(fd) != 0) {
free(contents);
return nullptr;
}
contents[size] = '\0';
contents[size + 1] = '\0';
#endif
sass::string extension;
if (path.length() > 5) {
extension = path.substr(path.length() - 5, 5);
}
Util::ascii_str_tolower(&extension);
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;
}
}
// split a path string delimited by semicolons or colons (OS dependent)
sass::vector<sass::string> split_path_list(const char* str)
{
sass::vector<sass::string> paths;
if (str == NULL) return paths;
// find delimiter via prelexer (return zero at end)
const char* end = Prelexer::find_first<PATH_SEP>(str);
// search until null delimiter
while (end) {
// add path from current position to delimiter
paths.push_back(sass::string(str, end - str));
str = end + 1; // skip delimiter
end = Prelexer::find_first<PATH_SEP>(str);
}
// add path from current position to end
paths.push_back(sass::string(str));
// return back
return paths;
}
}
}

View File

@@ -0,0 +1,124 @@
#ifndef SASS_FILE_H
#define SASS_FILE_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <string>
#include <vector>
#include "sass/context.h"
#include "ast_fwd_decl.hpp"
namespace Sass {
namespace File {
// return the current directory
// always with forward slashes
sass::string get_cwd();
// test if path exists and is a file
bool file_exists(const sass::string& file);
// return if given path is absolute
// works with *nix and windows paths
bool is_absolute_path(const sass::string& path);
// return only the directory part of path
sass::string dir_name(const sass::string& path);
// return only the filename part of path
sass::string base_name(const sass::string&);
// do a locigal clean up of the path
// no physical check on the filesystem
sass::string make_canonical_path (sass::string path);
// join two path segments cleanly together
// but only if right side is not absolute yet
sass::string join_paths(sass::string root, sass::string name);
// if the relative path is outside of the cwd we want want to
// show the absolute path in console messages
sass::string path_for_console(const sass::string& rel_path, const sass::string& abs_path, const sass::string& orig_path);
// create an absolute path by resolving relative paths with cwd
sass::string rel2abs(const sass::string& path, const sass::string& base = ".", const sass::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
sass::string abs2rel(const sass::string& path, const sass::string& base = ".", const sass::string& cwd = get_cwd());
// helper function to resolve a filename
// searching without variations in all paths
sass::string find_file(const sass::string& file, struct Sass_Compiler* options);
sass::string find_file(const sass::string& file, const sass::vector<sass::string> paths);
// helper function to resolve a include filename
// this has the original resolve logic for sass include
sass::string find_include(const sass::string& file, const sass::vector<sass::string> paths);
// split a path string delimited by semicolons or colons (OS dependent)
sass::vector<sass::string> split_path_list(const char* paths);
// try to load the given filename
// returned memory must be freed
// will auto convert .sass files
char* read_file(const sass::string& file);
}
// requested import
class Importer {
public:
// requested import path
sass::string imp_path;
// parent context path
sass::string ctx_path;
// base derived from context path
// this really just acts as a cache
sass::string base_path;
public:
Importer(sass::string imp_path, sass::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
sass::string abs_path;
public:
Include(const Importer& imp, sass::string abs_path)
: Importer(imp), abs_path(abs_path)
{ }
};
// a loaded resource
class Resource {
public:
// the file contents
char* contents;
// connected sourcemap
char* srcmap;
public:
Resource(char* contents, char* srcmap)
: contents(contents), srcmap(srcmap)
{ }
};
namespace File {
sass::vector<Include> resolve_includes(const sass::string& root, const sass::string& file,
const sass::vector<sass::string>& exts = { ".scss", ".sass", ".css" });
}
}
#endif

View File

@@ -0,0 +1,596 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <iomanip>
#include "ast.hpp"
#include "fn_utils.hpp"
#include "fn_colors.hpp"
#include "util.hpp"
#include "util_string.hpp"
namespace Sass {
namespace Functions {
bool string_argument(AST_Node_Obj obj) {
String_Constant* s = Cast<String_Constant>(obj);
if (s == nullptr) return false;
const sass::string& str = s->value();
return starts_with(str, "calc(") ||
starts_with(str, "var(");
}
void hsla_alpha_percent_deprecation(const SourceSpan& pstate, const sass::string val)
{
sass::string msg("Passing a percentage as the alpha value to hsla() will be interpreted");
sass::string tail("differently in future versions of Sass. For now, use " + val + " instead.");
deprecated(msg, tail, false, pstate);
}
Signature rgb_sig = "rgb($red, $green, $blue)";
BUILT_IN(rgb)
{
if (
string_argument(env["$red"]) ||
string_argument(env["$green"]) ||
string_argument(env["$blue"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
+ env["$red"]->to_string()
+ ", "
+ env["$green"]->to_string()
+ ", "
+ env["$blue"]->to_string()
+ ")"
);
}
return SASS_MEMORY_NEW(Color_RGBA,
pstate,
COLOR_NUM("$red"),
COLOR_NUM("$green"),
COLOR_NUM("$blue"));
}
Signature rgba_4_sig = "rgba($red, $green, $blue, $alpha)";
BUILT_IN(rgba_4)
{
if (
string_argument(env["$red"]) ||
string_argument(env["$green"]) ||
string_argument(env["$blue"]) ||
string_argument(env["$alpha"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("
+ env["$red"]->to_string()
+ ", "
+ env["$green"]->to_string()
+ ", "
+ env["$blue"]->to_string()
+ ", "
+ env["$alpha"]->to_string()
+ ")"
);
}
return SASS_MEMORY_NEW(Color_RGBA,
pstate,
COLOR_NUM("$red"),
COLOR_NUM("$green"),
COLOR_NUM("$blue"),
ALPHA_NUM("$alpha"));
}
Signature rgba_2_sig = "rgba($color, $alpha)";
BUILT_IN(rgba_2)
{
if (
string_argument(env["$color"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "rgba("
+ env["$color"]->to_string()
+ ", "
+ env["$alpha"]->to_string()
+ ")"
);
}
Color_RGBA_Obj c_arg = ARG("$color", Color)->toRGBA();
if (
string_argument(env["$alpha"])
) {
sass::ostream strm;
strm << "rgba("
<< (int)c_arg->r() << ", "
<< (int)c_arg->g() << ", "
<< (int)c_arg->b() << ", "
<< env["$alpha"]->to_string()
<< ")";
return SASS_MEMORY_NEW(String_Constant, pstate, strm.str());
}
Color_RGBA_Obj new_c = SASS_MEMORY_COPY(c_arg);
new_c->a(ALPHA_NUM("$alpha"));
new_c->disp("");
return new_c.detach();
}
////////////////
// RGB FUNCTIONS
////////////////
Signature red_sig = "red($color)";
BUILT_IN(red)
{
Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
return SASS_MEMORY_NEW(Number, pstate, color->r());
}
Signature green_sig = "green($color)";
BUILT_IN(green)
{
Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
return SASS_MEMORY_NEW(Number, pstate, color->g());
}
Signature blue_sig = "blue($color)";
BUILT_IN(blue)
{
Color_RGBA_Obj color = ARG("$color", Color)->toRGBA();
return SASS_MEMORY_NEW(Number, pstate, color->b());
}
Color_RGBA* colormix(Context& ctx, SourceSpan& pstate, Color* color1, Color* color2, double weight) {
Color_RGBA_Obj c1 = color1->toRGBA();
Color_RGBA_Obj c2 = color2->toRGBA();
double p = weight/100;
double w = 2*p - 1;
double a = c1->a() - c2->a();
double w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0;
double w2 = 1 - w1;
return SASS_MEMORY_NEW(Color_RGBA,
pstate,
Sass::round(w1*c1->r() + w2*c2->r(), ctx.c_options.precision),
Sass::round(w1*c1->g() + w2*c2->g(), ctx.c_options.precision),
Sass::round(w1*c1->b() + w2*c2->b(), ctx.c_options.precision),
c1->a()*p + c2->a()*(1-p));
}
Signature mix_sig = "mix($color1, $color2, $weight: 50%)";
BUILT_IN(mix)
{
Color_Obj color1 = ARG("$color1", Color);
Color_Obj color2 = ARG("$color2", Color);
double weight = DARG_U_PRCT("$weight");
return colormix(ctx, pstate, color1, color2, weight);
}
////////////////
// HSL FUNCTIONS
////////////////
Signature hsl_sig = "hsl($hue, $saturation, $lightness)";
BUILT_IN(hsl)
{
if (
string_argument(env["$hue"]) ||
string_argument(env["$saturation"]) ||
string_argument(env["$lightness"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "hsl("
+ env["$hue"]->to_string()
+ ", "
+ env["$saturation"]->to_string()
+ ", "
+ env["$lightness"]->to_string()
+ ")"
);
}
return SASS_MEMORY_NEW(Color_HSLA,
pstate,
ARGVAL("$hue"),
ARGVAL("$saturation"),
ARGVAL("$lightness"),
1.0);
}
Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)";
BUILT_IN(hsla)
{
if (
string_argument(env["$hue"]) ||
string_argument(env["$saturation"]) ||
string_argument(env["$lightness"]) ||
string_argument(env["$alpha"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "hsla("
+ env["$hue"]->to_string()
+ ", "
+ env["$saturation"]->to_string()
+ ", "
+ env["$lightness"]->to_string()
+ ", "
+ env["$alpha"]->to_string()
+ ")"
);
}
Number* alpha = ARG("$alpha", Number);
if (alpha && alpha->unit() == "%") {
Number_Obj val = SASS_MEMORY_COPY(alpha);
val->numerators.clear(); // convert
val->value(val->value() / 100.0);
sass::string nr(val->to_string(ctx.c_options));
hsla_alpha_percent_deprecation(pstate, nr);
}
return SASS_MEMORY_NEW(Color_HSLA,
pstate,
ARGVAL("$hue"),
ARGVAL("$saturation"),
ARGVAL("$lightness"),
ARGVAL("$alpha"));
}
/////////////////////////////////////////////////////////////////////////
// Query functions
/////////////////////////////////////////////////////////////////////////
Signature hue_sig = "hue($color)";
BUILT_IN(hue)
{
Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
return SASS_MEMORY_NEW(Number, pstate, col->h(), "deg");
}
Signature saturation_sig = "saturation($color)";
BUILT_IN(saturation)
{
Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
return SASS_MEMORY_NEW(Number, pstate, col->s(), "%");
}
Signature lightness_sig = "lightness($color)";
BUILT_IN(lightness)
{
Color_HSLA_Obj col = ARG("$color", Color)->toHSLA();
return SASS_MEMORY_NEW(Number, pstate, col->l(), "%");
}
/////////////////////////////////////////////////////////////////////////
// HSL manipulation functions
/////////////////////////////////////////////////////////////////////////
Signature adjust_hue_sig = "adjust-hue($color, $degrees)";
BUILT_IN(adjust_hue)
{
Color* col = ARG("$color", Color);
double degrees = ARGVAL("$degrees");
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->h(absmod(copy->h() + degrees, 360.0));
return copy.detach();
}
Signature lighten_sig = "lighten($color, $amount)";
BUILT_IN(lighten)
{
Color* col = ARG("$color", Color);
double amount = DARG_U_PRCT("$amount");
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->l(clip(copy->l() + amount, 0.0, 100.0));
return copy.detach();
}
Signature darken_sig = "darken($color, $amount)";
BUILT_IN(darken)
{
Color* col = ARG("$color", Color);
double amount = DARG_U_PRCT("$amount");
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->l(clip(copy->l() - amount, 0.0, 100.0));
return copy.detach();
}
Signature saturate_sig = "saturate($color, $amount: false)";
BUILT_IN(saturate)
{
// CSS3 filter function overload: pass literal through directly
if (!Cast<Number>(env["$amount"])) {
return SASS_MEMORY_NEW(String_Quoted, pstate, "saturate(" + env["$color"]->to_string(ctx.c_options) + ")");
}
Color* col = ARG("$color", Color);
double amount = DARG_U_PRCT("$amount");
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->s(clip(copy->s() + amount, 0.0, 100.0));
return copy.detach();
}
Signature desaturate_sig = "desaturate($color, $amount)";
BUILT_IN(desaturate)
{
Color* col = ARG("$color", Color);
double amount = DARG_U_PRCT("$amount");
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->s(clip(copy->s() - amount, 0.0, 100.0));
return copy.detach();
}
Signature grayscale_sig = "grayscale($color)";
BUILT_IN(grayscale)
{
// CSS3 filter function overload: pass literal through directly
Number* amount = Cast<Number>(env["$color"]);
if (amount) {
return SASS_MEMORY_NEW(String_Quoted, pstate, "grayscale(" + amount->to_string(ctx.c_options) + ")");
}
Color* col = ARG("$color", Color);
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->s(0.0); // just reset saturation
return copy.detach();
}
/////////////////////////////////////////////////////////////////////////
// Misc manipulation functions
/////////////////////////////////////////////////////////////////////////
Signature complement_sig = "complement($color)";
BUILT_IN(complement)
{
Color* col = ARG("$color", Color);
Color_HSLA_Obj copy = col->copyAsHSLA();
copy->h(absmod(copy->h() - 180.0, 360.0));
return copy.detach();
}
Signature invert_sig = "invert($color, $weight: 100%)";
BUILT_IN(invert)
{
// CSS3 filter function overload: pass literal through directly
Number* amount = Cast<Number>(env["$color"]);
double weight = DARG_U_PRCT("$weight");
if (amount) {
// TODO: does not throw on 100% manually passed as value
if (weight < 100.0) {
error("Only one argument may be passed to the plain-CSS invert() function.", pstate, traces);
}
return SASS_MEMORY_NEW(String_Quoted, pstate, "invert(" + amount->to_string(ctx.c_options) + ")");
}
Color* col = ARG("$color", Color);
Color_RGBA_Obj inv = col->copyAsRGBA();
inv->r(clip(255.0 - inv->r(), 0.0, 255.0));
inv->g(clip(255.0 - inv->g(), 0.0, 255.0));
inv->b(clip(255.0 - inv->b(), 0.0, 255.0));
return colormix(ctx, pstate, inv, col, weight);
}
/////////////////////////////////////////////////////////////////////////
// Opacity functions
/////////////////////////////////////////////////////////////////////////
Signature alpha_sig = "alpha($color)";
Signature opacity_sig = "opacity($color)";
BUILT_IN(alpha)
{
String_Constant* ie_kwd = Cast<String_Constant>(env["$color"]);
if (ie_kwd) {
return SASS_MEMORY_NEW(String_Quoted, pstate, "alpha(" + ie_kwd->value() + ")");
}
// CSS3 filter function overload: pass literal through directly
Number* amount = Cast<Number>(env["$color"]);
if (amount) {
return SASS_MEMORY_NEW(String_Quoted, pstate, "opacity(" + amount->to_string(ctx.c_options) + ")");
}
return SASS_MEMORY_NEW(Number, pstate, ARG("$color", Color)->a());
}
Signature opacify_sig = "opacify($color, $amount)";
Signature fade_in_sig = "fade-in($color, $amount)";
BUILT_IN(opacify)
{
Color* col = ARG("$color", Color);
double amount = DARG_U_FACT("$amount");
Color_Obj copy = SASS_MEMORY_COPY(col);
copy->a(clip(col->a() + amount, 0.0, 1.0));
return copy.detach();
}
Signature transparentize_sig = "transparentize($color, $amount)";
Signature fade_out_sig = "fade-out($color, $amount)";
BUILT_IN(transparentize)
{
Color* col = ARG("$color", Color);
double amount = DARG_U_FACT("$amount");
Color_Obj copy = SASS_MEMORY_COPY(col);
copy->a(std::max(col->a() - amount, 0.0));
return copy.detach();
}
////////////////////////
// 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* col = ARG("$color", Color);
Number* r = Cast<Number>(env["$red"]);
Number* g = Cast<Number>(env["$green"]);
Number* b = Cast<Number>(env["$blue"]);
Number* h = Cast<Number>(env["$hue"]);
Number* s = Cast<Number>(env["$saturation"]);
Number* l = Cast<Number>(env["$lightness"]);
Number* a = Cast<Number>(env["$alpha"]);
bool rgb = r || g || b;
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);
}
else if (rgb) {
Color_RGBA_Obj c = col->copyAsRGBA();
if (r) c->r(c->r() + DARG_R_BYTE("$red"));
if (g) c->g(c->g() + DARG_R_BYTE("$green"));
if (b) c->b(c->b() + DARG_R_BYTE("$blue"));
if (a) c->a(c->a() + DARG_R_FACT("$alpha"));
return c.detach();
}
else if (hsl) {
Color_HSLA_Obj c = col->copyAsHSLA();
if (h) c->h(c->h() + absmod(h->value(), 360.0));
if (s) c->s(c->s() + DARG_R_PRCT("$saturation"));
if (l) c->l(c->l() + DARG_R_PRCT("$lightness"));
if (a) c->a(c->a() + DARG_R_FACT("$alpha"));
return c.detach();
}
else if (a) {
Color_Obj c = SASS_MEMORY_COPY(col);
c->a(c->a() + DARG_R_FACT("$alpha"));
c->a(clip(c->a(), 0.0, 1.0));
return c.detach();
}
error("not enough arguments for `adjust-color'", pstate, traces);
// unreachable
return col;
}
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* col = ARG("$color", Color);
Number* r = Cast<Number>(env["$red"]);
Number* g = Cast<Number>(env["$green"]);
Number* b = Cast<Number>(env["$blue"]);
Number* h = Cast<Number>(env["$hue"]);
Number* s = Cast<Number>(env["$saturation"]);
Number* l = Cast<Number>(env["$lightness"]);
Number* a = Cast<Number>(env["$alpha"]);
bool rgb = r || g || b;
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);
}
else if (rgb) {
Color_RGBA_Obj c = col->copyAsRGBA();
double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;
double gscale = (g ? DARG_R_PRCT("$green") : 0.0) / 100.0;
double bscale = (b ? DARG_R_PRCT("$blue") : 0.0) / 100.0;
double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;
if (rscale) c->r(c->r() + rscale * (rscale > 0.0 ? 255.0 - c->r() : c->r()));
if (gscale) c->g(c->g() + gscale * (gscale > 0.0 ? 255.0 - c->g() : c->g()));
if (bscale) c->b(c->b() + bscale * (bscale > 0.0 ? 255.0 - c->b() : c->b()));
if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
return c.detach();
}
else if (hsl) {
Color_HSLA_Obj c = col->copyAsHSLA();
double hscale = (h ? DARG_R_PRCT("$hue") : 0.0) / 100.0;
double sscale = (s ? DARG_R_PRCT("$saturation") : 0.0) / 100.0;
double lscale = (l ? DARG_R_PRCT("$lightness") : 0.0) / 100.0;
double ascale = (a ? DARG_R_PRCT("$alpha") : 0.0) / 100.0;
if (hscale) c->h(c->h() + hscale * (hscale > 0.0 ? 360.0 - c->h() : c->h()));
if (sscale) c->s(c->s() + sscale * (sscale > 0.0 ? 100.0 - c->s() : c->s()));
if (lscale) c->l(c->l() + lscale * (lscale > 0.0 ? 100.0 - c->l() : c->l()));
if (ascale) c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
return c.detach();
}
else if (a) {
Color_Obj c = SASS_MEMORY_COPY(col);
double ascale = DARG_R_PRCT("$alpha") / 100.0;
c->a(c->a() + ascale * (ascale > 0.0 ? 1.0 - c->a() : c->a()));
c->a(clip(c->a(), 0.0, 1.0));
return c.detach();
}
error("not enough arguments for `scale-color'", pstate, traces);
// unreachable
return col;
}
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* col = ARG("$color", Color);
Number* r = Cast<Number>(env["$red"]);
Number* g = Cast<Number>(env["$green"]);
Number* b = Cast<Number>(env["$blue"]);
Number* h = Cast<Number>(env["$hue"]);
Number* s = Cast<Number>(env["$saturation"]);
Number* l = Cast<Number>(env["$lightness"]);
Number* a = Cast<Number>(env["$alpha"]);
bool rgb = r || g || b;
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);
}
else if (rgb) {
Color_RGBA_Obj c = col->copyAsRGBA();
if (r) c->r(DARG_U_BYTE("$red"));
if (g) c->g(DARG_U_BYTE("$green"));
if (b) c->b(DARG_U_BYTE("$blue"));
if (a) c->a(DARG_U_FACT("$alpha"));
return c.detach();
}
else if (hsl) {
Color_HSLA_Obj c = col->copyAsHSLA();
if (h) c->h(absmod(h->value(), 360.0));
if (s) c->s(DARG_U_PRCT("$saturation"));
if (l) c->l(DARG_U_PRCT("$lightness"));
if (a) c->a(DARG_U_FACT("$alpha"));
return c.detach();
}
else if (a) {
Color_Obj c = SASS_MEMORY_COPY(col);
c->a(clip(DARG_U_FACT("$alpha"), 0.0, 1.0));
return c.detach();
}
error("not enough arguments for `change-color'", pstate, traces);
// unreachable
return col;
}
Signature ie_hex_str_sig = "ie-hex-str($color)";
BUILT_IN(ie_hex_str)
{
Color* col = ARG("$color", Color);
Color_RGBA_Obj c = col->toRGBA();
double r = clip(c->r(), 0.0, 255.0);
double g = clip(c->g(), 0.0, 255.0);
double b = clip(c->b(), 0.0, 255.0);
double a = clip(c->a(), 0.0, 1.0) * 255.0;
sass::ostream ss;
ss << '#' << std::setw(2) << std::setfill('0');
ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(a, ctx.c_options.precision));
ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(r, ctx.c_options.precision));
ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(g, ctx.c_options.precision));
ss << std::hex << std::setw(2) << static_cast<unsigned long>(Sass::round(b, ctx.c_options.precision));
sass::string result = ss.str();
Util::ascii_str_toupper(&result);
return SASS_MEMORY_NEW(String_Quoted, pstate, result);
}
}
}

View File

@@ -0,0 +1,85 @@
#ifndef SASS_FN_COLORS_H
#define SASS_FN_COLORS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
// macros for common ranges (u mean unsigned or upper, r for full range)
#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double
#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double
#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double
#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double
#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double
#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double
// macros for color related inputs (rbg and alpha/opacity values)
#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double
#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double
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;
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);
}
}
#endif

View File

@@ -0,0 +1,285 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "listize.hpp"
#include "operators.hpp"
#include "fn_utils.hpp"
#include "fn_lists.hpp"
namespace Sass {
namespace Functions {
/////////////////
// LIST FUNCTIONS
/////////////////
Signature keywords_sig = "keywords($args)";
BUILT_IN(keywords)
{
List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List)); // copy
Map_Obj result = SASS_MEMORY_NEW(Map, pstate, 1);
for (size_t i = arglist->size(), L = arglist->length(); i < L; ++i) {
ExpressionObj obj = arglist->at(i);
Argument_Obj arg = (Argument*) obj.ptr(); // XXX
sass::string name = sass::string(arg->name());
name = name.erase(0, 1); // sanitize name (remove dollar sign)
*result << std::make_pair(SASS_MEMORY_NEW(String_Quoted,
pstate, name),
arg->value());
}
return result.detach();
}
Signature length_sig = "length($list)";
BUILT_IN(length)
{
if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {
return SASS_MEMORY_NEW(Number, pstate, (double) sl->length());
}
Expression* v = ARG("$list", Expression);
if (v->concrete_type() == Expression::MAP) {
Map* map = Cast<Map>(env["$list"]);
return SASS_MEMORY_NEW(Number, pstate, (double)(map ? map->length() : 1));
}
if (v->concrete_type() == Expression::SELECTOR) {
if (CompoundSelector * h = Cast<CompoundSelector>(v)) {
return SASS_MEMORY_NEW(Number, pstate, (double)h->length());
} else if (SelectorList * ls = Cast<SelectorList>(v)) {
return SASS_MEMORY_NEW(Number, pstate, (double)ls->length());
} else {
return SASS_MEMORY_NEW(Number, pstate, 1);
}
}
List* list = Cast<List>(env["$list"]);
return SASS_MEMORY_NEW(Number,
pstate,
(double)(list ? list->size() : 1));
}
Signature nth_sig = "nth($list, $n)";
BUILT_IN(nth)
{
double nr = ARGVAL("$n");
Map* m = Cast<Map>(env["$list"]);
if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {
size_t len = m ? m->length() : sl->length();
bool empty = m ? m->empty() : sl->empty();
if (empty) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces);
double index = std::floor(nr < 0 ? len + nr : nr - 1);
if (index < 0 || index > len - 1) error("index out of bounds for `" + sass::string(sig) + "`", pstate, traces);
return Cast<Value>(Listize::perform(sl->get(static_cast<int>(index))));
}
List_Obj l = Cast<List>(env["$list"]);
if (nr == 0) error("argument `$n` of `" + sass::string(sig) + "` must be non-zero", pstate, traces);
// if the argument isn't a list, then wrap it in a singleton list
if (!m && !l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
l->append(ARG("$list", Expression));
}
size_t len = m ? m->length() : l->length();
bool empty = m ? m->empty() : l->empty();
if (empty) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces);
double index = std::floor(nr < 0 ? len + nr : nr - 1);
if (index < 0 || index > len - 1) error("index out of bounds for `" + sass::string(sig) + "`", pstate, traces);
if (m) {
l = SASS_MEMORY_NEW(List, pstate, 2);
l->append(m->keys()[static_cast<unsigned int>(index)]);
l->append(m->at(m->keys()[static_cast<unsigned int>(index)]));
return l.detach();
}
else {
ValueObj rv = l->value_at_index(static_cast<int>(index));
rv->set_delayed(false);
return rv.detach();
}
}
Signature set_nth_sig = "set-nth($list, $n, $value)";
BUILT_IN(set_nth)
{
Map_Obj m = Cast<Map>(env["$list"]);
List_Obj l = Cast<List>(env["$list"]);
Number_Obj n = ARG("$n", Number);
ExpressionObj v = ARG("$value", Expression);
if (!l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
l->append(ARG("$list", Expression));
}
if (m) {
l = m->to_list(pstate);
}
if (l->empty()) error("argument `$list` of `" + sass::string(sig) + "` must not be empty", pstate, traces);
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 `" + sass::string(sig) + "`", pstate, traces);
List* result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());
for (size_t i = 0, L = l->length(); i < L; ++i) {
result->append(((i == index) ? v : (*l)[i]));
}
return result;
}
Signature index_sig = "index($list, $value)";
BUILT_IN(index)
{
Map_Obj m = Cast<Map>(env["$list"]);
List_Obj l = Cast<List>(env["$list"]);
ExpressionObj v = ARG("$value", Expression);
if (!l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
l->append(ARG("$list", Expression));
}
if (m) {
l = m->to_list(pstate);
}
for (size_t i = 0, L = l->length(); i < L; ++i) {
if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));
}
return SASS_MEMORY_NEW(Null, pstate);
}
Signature join_sig = "join($list1, $list2, $separator: auto, $bracketed: auto)";
BUILT_IN(join)
{
Map_Obj m1 = Cast<Map>(env["$list1"]);
Map_Obj m2 = Cast<Map>(env["$list2"]);
List_Obj l1 = Cast<List>(env["$list1"]);
List_Obj l2 = Cast<List>(env["$list2"]);
String_Constant_Obj sep = ARG("$separator", String_Constant);
enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE);
Value* bracketed = ARG("$bracketed", Value);
bool is_bracketed = (l1 ? l1->is_bracketed() : false);
if (!l1) {
l1 = SASS_MEMORY_NEW(List, pstate, 1);
l1->append(ARG("$list1", Expression));
sep_val = (l2 ? l2->separator() : SASS_SPACE);
is_bracketed = (l2 ? l2->is_bracketed() : false);
}
if (!l2) {
l2 = SASS_MEMORY_NEW(List, pstate, 1);
l2->append(ARG("$list2", Expression));
}
if (m1) {
l1 = m1->to_list(pstate);
sep_val = SASS_COMMA;
}
if (m2) {
l2 = m2->to_list(pstate);
}
size_t len = l1->length() + l2->length();
sass::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 `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);
String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);
bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";
if (!bracketed_is_auto) {
is_bracketed = !bracketed->is_false();
}
List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed);
result->concat(l1);
result->concat(l2);
return result.detach();
}
Signature append_sig = "append($list, $val, $separator: auto)";
BUILT_IN(append)
{
Map_Obj m = Cast<Map>(env["$list"]);
List_Obj l = Cast<List>(env["$list"]);
ExpressionObj v = ARG("$val", Expression);
if (SelectorList * sl = Cast<SelectorList>(env["$list"])) {
l = Cast<List>(Listize::perform(sl));
}
String_Constant_Obj sep = ARG("$separator", String_Constant);
if (!l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
l->append(ARG("$list", Expression));
}
if (m) {
l = m->to_list(pstate);
}
List* result = SASS_MEMORY_COPY(l);
sass::string sep_str(unquote(sep->value()));
if (sep_str != "auto") { // check default first
if (sep_str == "space") result->separator(SASS_SPACE);
else if (sep_str == "comma") result->separator(SASS_COMMA);
else error("argument `$separator` of `" + sass::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);
}
if (l->is_arglist()) {
result->append(SASS_MEMORY_NEW(Argument,
v->pstate(),
v,
"",
false,
false));
} else {
result->append(v);
}
return result;
}
Signature zip_sig = "zip($lists...)";
BUILT_IN(zip)
{
List_Obj arglist = SASS_MEMORY_COPY(ARG("$lists", List));
size_t shortest = 0;
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
List_Obj ith = Cast<List>(arglist->value_at_index(i));
Map_Obj mith = Cast<Map>(arglist->value_at_index(i));
if (!ith) {
if (mith) {
ith = mith->to_list(pstate);
} else {
ith = SASS_MEMORY_NEW(List, pstate, 1);
ith->append(arglist->value_at_index(i));
}
if (arglist->is_arglist()) {
Argument_Obj arg = (Argument*)(arglist->at(i).ptr()); // XXX
arg->value(ith);
} else {
(*arglist)[i] = ith;
}
}
shortest = (i ? std::min(shortest, ith->length()) : ith->length());
}
List* zippers = SASS_MEMORY_NEW(List, pstate, shortest, SASS_COMMA);
size_t L = arglist->length();
for (size_t i = 0; i < shortest; ++i) {
List* zipper = SASS_MEMORY_NEW(List, pstate, L);
for (size_t j = 0; j < L; ++j) {
zipper->append(Cast<List>(arglist->value_at_index(j))->at(i));
}
zippers->append(zipper);
}
return zippers;
}
Signature list_separator_sig = "list_separator($list)";
BUILT_IN(list_separator)
{
List_Obj l = Cast<List>(env["$list"]);
if (!l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
l->append(ARG("$list", Expression));
}
return SASS_MEMORY_NEW(String_Quoted,
pstate,
l->separator() == SASS_COMMA ? "comma" : "space");
}
Signature is_bracketed_sig = "is-bracketed($list)";
BUILT_IN(is_bracketed)
{
ValueObj value = ARG("$list", Value);
List_Obj list = Cast<List>(value);
return SASS_MEMORY_NEW(Boolean, pstate, list && list->is_bracketed());
}
}
}

View File

@@ -0,0 +1,34 @@
#ifndef SASS_FN_LISTS_H
#define SASS_FN_LISTS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
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 is_bracketed_sig;
extern Signature keywords_sig;
BUILT_IN(length);
BUILT_IN(nth);
BUILT_IN(index);
BUILT_IN(join);
BUILT_IN(append);
BUILT_IN(zip);
BUILT_IN(list_separator);
BUILT_IN(is_bracketed);
BUILT_IN(keywords);
}
}
#endif

View File

@@ -0,0 +1,94 @@
#include "operators.hpp"
#include "fn_utils.hpp"
#include "fn_maps.hpp"
namespace Sass {
namespace Functions {
/////////////////
// MAP FUNCTIONS
/////////////////
Signature map_get_sig = "map-get($map, $key)";
BUILT_IN(map_get)
{
// leaks for "map-get((), foo)" if not Obj
// investigate why this is (unexpected)
Map_Obj m = ARGM("$map", Map);
ExpressionObj v = ARG("$key", Expression);
try {
ValueObj val = m->at(v);
if (!val) return SASS_MEMORY_NEW(Null, pstate);
val->set_delayed(false);
return val.detach();
} catch (const std::out_of_range&) {
return SASS_MEMORY_NEW(Null, pstate);
}
catch (...) { throw; }
}
Signature map_has_key_sig = "map-has-key($map, $key)";
BUILT_IN(map_has_key)
{
Map_Obj m = ARGM("$map", Map);
ExpressionObj v = ARG("$key", Expression);
return SASS_MEMORY_NEW(Boolean, pstate, m->has(v));
}
Signature map_keys_sig = "map-keys($map)";
BUILT_IN(map_keys)
{
Map_Obj m = ARGM("$map", Map);
List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);
for ( auto key : m->keys()) {
result->append(key);
}
return result;
}
Signature map_values_sig = "map-values($map)";
BUILT_IN(map_values)
{
Map_Obj m = ARGM("$map", Map);
List* result = SASS_MEMORY_NEW(List, pstate, m->length(), SASS_COMMA);
for ( auto key : m->keys()) {
result->append(m->at(key));
}
return result;
}
Signature map_merge_sig = "map-merge($map1, $map2)";
BUILT_IN(map_merge)
{
Map_Obj m1 = ARGM("$map1", Map);
Map_Obj m2 = ARGM("$map2", Map);
size_t len = m1->length() + m2->length();
Map* result = SASS_MEMORY_NEW(Map, pstate, len);
// concat not implemented for maps
*result += m1;
*result += m2;
return result;
}
Signature map_remove_sig = "map-remove($map, $keys...)";
BUILT_IN(map_remove)
{
bool remove;
Map_Obj m = ARGM("$map", Map);
List_Obj arglist = ARG("$keys", List);
Map* result = SASS_MEMORY_NEW(Map, pstate, 1);
for (auto key : m->keys()) {
remove = false;
for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {
remove = Operators::eq(key, arglist->value_at_index(j));
}
if (!remove) *result << std::make_pair(key, m->at(key));
}
return result;
}
}
}

View File

@@ -0,0 +1,30 @@
#ifndef SASS_FN_MAPS_H
#define SASS_FN_MAPS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
#define ARGM(argname, argtype) get_arg_m(argname, env, sig, pstate, traces)
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;
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);
}
}
#endif

View File

@@ -0,0 +1,244 @@
#include "ast.hpp"
#include "expand.hpp"
#include "fn_utils.hpp"
#include "fn_miscs.hpp"
#include "util_string.hpp"
namespace Sass {
namespace Functions {
//////////////////////////
// INTROSPECTION FUNCTIONS
//////////////////////////
Signature type_of_sig = "type-of($value)";
BUILT_IN(type_of)
{
Expression* v = ARG("$value", Expression);
return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());
}
Signature variable_exists_sig = "variable-exists($name)";
BUILT_IN(variable_exists)
{
sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
if(d_env.has("$"+s)) {
return SASS_MEMORY_NEW(Boolean, pstate, true);
}
else {
return SASS_MEMORY_NEW(Boolean, pstate, false);
}
}
Signature global_variable_exists_sig = "global-variable-exists($name)";
BUILT_IN(global_variable_exists)
{
sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
if(d_env.has_global("$"+s)) {
return SASS_MEMORY_NEW(Boolean, pstate, true);
}
else {
return SASS_MEMORY_NEW(Boolean, pstate, false);
}
}
Signature function_exists_sig = "function-exists($name)";
BUILT_IN(function_exists)
{
String_Constant* ss = Cast<String_Constant>(env["$name"]);
if (!ss) {
error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);
}
sass::string name = Util::normalize_underscores(unquote(ss->value()));
if(d_env.has(name+"[f]")) {
return SASS_MEMORY_NEW(Boolean, pstate, true);
}
else {
return SASS_MEMORY_NEW(Boolean, pstate, false);
}
}
Signature mixin_exists_sig = "mixin-exists($name)";
BUILT_IN(mixin_exists)
{
sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
if(d_env.has(s+"[m]")) {
return SASS_MEMORY_NEW(Boolean, pstate, true);
}
else {
return SASS_MEMORY_NEW(Boolean, pstate, false);
}
}
Signature feature_exists_sig = "feature-exists($feature)";
BUILT_IN(feature_exists)
{
sass::string s = unquote(ARG("$feature", String_Constant)->value());
static const auto *const features = new std::unordered_set<sass::string> {
"global-variable-shadowing",
"extend-selector-pseudoclass",
"at-error",
"units-level-3",
"custom-property"
};
return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end());
}
Signature call_sig = "call($function, $args...)";
BUILT_IN(call)
{
sass::string function;
Function* ff = Cast<Function>(env["$function"]);
String_Constant* ss = Cast<String_Constant>(env["$function"]);
if (ss) {
function = Util::normalize_underscores(unquote(ss->value()));
std::cerr << "DEPRECATION WARNING: ";
std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;
std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl;
std::cerr << std::endl;
} else if (ff) {
function = ff->name();
}
List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));
Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);
// sass::string full_name(name + "[f]");
// Definition* def = d_env.has(full_name) ? 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) {
ExpressionObj expr = arglist->value_at_index(i);
// if (params && params->has_rest_parameter()) {
// Parameter_Obj p = param_size > i ? (*params)[i] : 0;
// List* list = Cast<List>(expr);
// if (list && p && !p->is_rest_parameter()) expr = (*list)[0];
// }
if (arglist->is_arglist()) {
ExpressionObj obj = arglist->at(i);
Argument_Obj arg = (Argument*) obj.ptr(); // XXX
args->append(SASS_MEMORY_NEW(Argument,
pstate,
expr,
arg ? arg->name() : "",
arg ? arg->is_rest_argument() : false,
arg ? arg->is_keyword_argument() : false));
} else {
args->append(SASS_MEMORY_NEW(Argument, pstate, expr));
}
}
Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args);
Expand expand(ctx, &d_env, &selector_stack, &original_stack);
func->via_call(true); // calc invoke is allowed
if (ff) func->func(ff);
return Cast<PreValue>(func->perform(&expand.eval));
}
////////////////////
// BOOLEAN FUNCTIONS
////////////////////
Signature not_sig = "not($value)";
BUILT_IN(sass_not)
{
return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());
}
Signature if_sig = "if($condition, $if-true, $if-false)";
BUILT_IN(sass_if)
{
Expand expand(ctx, &d_env, &selector_stack, &original_stack);
ExpressionObj cond = ARG("$condition", Expression)->perform(&expand.eval);
bool is_true = !cond->is_false();
ExpressionObj res = ARG(is_true ? "$if-true" : "$if-false", Expression);
ValueObj qwe = Cast<Value>(res->perform(&expand.eval));
// res = res->perform(&expand.eval.val_eval);
qwe->set_delayed(false); // clone?
return qwe.detach();
}
//////////////////////////
// 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(String_Constant, pstate, "null");
} else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {
return SASS_MEMORY_NEW(String_Constant, pstate, "false");
} else if (v->concrete_type() == Expression::STRING) {
String_Constant *s = Cast<String_Constant>(v);
if (s->quote_mark()) {
return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark()));
} else {
return s;
}
} else {
// ToDo: fix to_sass for nested parentheses
Sass_Output_Style old_style;
old_style = ctx.c_options.output_style;
ctx.c_options.output_style = TO_SASS;
Emitter emitter(ctx.c_options);
Inspect i(emitter);
i.in_declaration = false;
v->perform(&i);
ctx.c_options.output_style = old_style;
return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());
}
}
Signature content_exists_sig = "content-exists()";
BUILT_IN(content_exists)
{
if (!d_env.has_global("is_in_mixin")) {
error("Cannot call content-exists() except within a mixin.", pstate, traces);
}
return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));
}
Signature get_function_sig = "get-function($name, $css: false)";
BUILT_IN(get_function)
{
String_Constant* ss = Cast<String_Constant>(env["$name"]);
if (!ss) {
error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);
}
sass::string name = Util::normalize_underscores(unquote(ss->value()));
sass::string full_name = name + "[f]";
Boolean_Obj css = ARG("$css", Boolean);
if (!css->is_false()) {
Definition* def = SASS_MEMORY_NEW(Definition,
pstate,
name,
SASS_MEMORY_NEW(Parameters, pstate),
SASS_MEMORY_NEW(Block, pstate, 0, false),
Definition::FUNCTION);
return SASS_MEMORY_NEW(Function, pstate, def, true);
}
if (!d_env.has_global(full_name)) {
error("Function not found: " + name, pstate, traces);
}
Definition* def = Cast<Definition>(d_env[full_name]);
return SASS_MEMORY_NEW(Function, pstate, def, false);
}
}
}

View File

@@ -0,0 +1,40 @@
#ifndef SASS_FN_MISCS_H
#define SASS_FN_MISCS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
extern Signature type_of_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 set_nth_sig;
extern Signature content_exists_sig;
extern Signature get_function_sig;
BUILT_IN(type_of);
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(set_nth);
BUILT_IN(content_exists);
BUILT_IN(get_function);
}
}
#endif

View File

@@ -0,0 +1,227 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <cstdint>
#include <cstdlib>
#include <cmath>
#include <random>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include "ast.hpp"
#include "units.hpp"
#include "fn_utils.hpp"
#include "fn_numbers.hpp"
#ifdef __MINGW32__
#include "windows.h"
#include "wincrypt.h"
#endif
namespace Sass {
namespace Functions {
#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
uint64_t GetSeed()
{
std::random_device rd;
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()));
///////////////////
// NUMBER FUNCTIONS
///////////////////
Signature percentage_sig = "percentage($number)";
BUILT_IN(percentage)
{
Number_Obj n = ARGN("$number");
if (!n->is_unitless()) error("argument $number of `" + sass::string(sig) + "` must be unitless", pstate, traces);
return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");
}
Signature round_sig = "round($number)";
BUILT_IN(round)
{
Number_Obj r = ARGN("$number");
r->value(Sass::round(r->value(), ctx.c_options.precision));
r->pstate(pstate);
return r.detach();
}
Signature ceil_sig = "ceil($number)";
BUILT_IN(ceil)
{
Number_Obj r = ARGN("$number");
r->value(std::ceil(r->value()));
r->pstate(pstate);
return r.detach();
}
Signature floor_sig = "floor($number)";
BUILT_IN(floor)
{
Number_Obj r = ARGN("$number");
r->value(std::floor(r->value()));
r->pstate(pstate);
return r.detach();
}
Signature abs_sig = "abs($number)";
BUILT_IN(abs)
{
Number_Obj r = ARGN("$number");
r->value(std::abs(r->value()));
r->pstate(pstate);
return r.detach();
}
Signature min_sig = "min($numbers...)";
BUILT_IN(min)
{
List* arglist = ARG("$numbers", List);
Number_Obj least;
size_t L = arglist->length();
if (L == 0) {
error("At least one argument must be passed.", pstate, traces);
}
for (size_t i = 0; i < L; ++i) {
ExpressionObj val = arglist->value_at_index(i);
Number_Obj xi = Cast<Number>(val);
if (!xi) {
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);
}
if (least) {
if (*xi < *least) least = xi;
} else least = xi;
}
return least.detach();
}
Signature max_sig = "max($numbers...)";
BUILT_IN(max)
{
List* arglist = ARG("$numbers", List);
Number_Obj greatest;
size_t L = arglist->length();
if (L == 0) {
error("At least one argument must be passed.", pstate, traces);
}
for (size_t i = 0; i < L; ++i) {
ExpressionObj val = arglist->value_at_index(i);
Number_Obj xi = Cast<Number>(val);
if (!xi) {
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);
}
if (greatest) {
if (*greatest < *xi) greatest = xi;
} else greatest = xi;
}
return greatest.detach();
}
Signature random_sig = "random($limit:false)";
BUILT_IN(random)
{
AST_Node_Obj arg = env["$limit"];
Value* v = Cast<Value>(arg);
Number* l = Cast<Number>(arg);
Boolean* b = Cast<Boolean>(arg);
if (l) {
double lv = l->value();
if (lv < 1) {
sass::ostream err;
err << "$limit " << lv << " must be greater than or equal to 1 for `random'";
error(err.str(), pstate, traces);
}
bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;
if (!eq_int) {
sass::ostream err;
err << "Expected $limit to be an integer but got " << lv << " for `random'";
error(err.str(), pstate, traces);
}
std::uniform_real_distribution<> distributor(1, lv + 1);
uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
return SASS_MEMORY_NEW(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(Number, pstate, distributed);
} else if (v) {
traces.push_back(Backtrace(pstate));
throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);
} else {
traces.push_back(Backtrace(pstate));
throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");
}
}
Signature unique_id_sig = "unique-id()";
BUILT_IN(unique_id)
{
sass::ostream 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(String_Quoted, pstate, ss.str());
}
Signature unit_sig = "unit($number)";
BUILT_IN(unit)
{
Number_Obj arg = ARGN("$number");
sass::string str(quote(arg->unit(), '"'));
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
Signature unitless_sig = "unitless($number)";
BUILT_IN(unitless)
{
Number_Obj arg = ARGN("$number");
bool unitless = arg->is_unitless();
return SASS_MEMORY_NEW(Boolean, pstate, unitless);
}
Signature comparable_sig = "comparable($number1, $number2)";
BUILT_IN(comparable)
{
Number_Obj n1 = ARGN("$number1");
Number_Obj n2 = ARGN("$number2");
if (n1->is_unitless() || n2->is_unitless()) {
return SASS_MEMORY_NEW(Boolean, pstate, true);
}
// normalize into main units
n1->normalize(); n2->normalize();
Units &lhs_unit = *n1, &rhs_unit = *n2;
bool is_comparable = (lhs_unit == rhs_unit);
return SASS_MEMORY_NEW(Boolean, pstate, is_comparable);
}
}
}

View File

@@ -0,0 +1,45 @@
#ifndef SASS_FN_NUMBERS_H
#define SASS_FN_NUMBERS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
// return a number object (copied since we want to have reduced units)
#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy
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 unique_id_sig;
extern Signature unit_sig;
extern Signature unitless_sig;
extern Signature comparable_sig;
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(unique_id);
BUILT_IN(unit);
BUILT_IN(unitless);
BUILT_IN(comparable);
}
}
#endif

View File

@@ -0,0 +1,205 @@
#include <numeric>
#include "parser.hpp"
#include "extender.hpp"
#include "listize.hpp"
#include "fn_utils.hpp"
#include "fn_selectors.hpp"
namespace Sass {
namespace Functions {
Signature selector_nest_sig = "selector-nest($selectors...)";
BUILT_IN(selector_nest)
{
List* arglist = ARG("$selectors", List);
// Not enough parameters
if (arglist->length() == 0) {
error(
"$selectors: At least one selector must be passed for `selector-nest'",
pstate, traces);
}
// Parse args into vector of selectors
SelectorStack parsedSelectors;
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
ExpressionObj exp = Cast<Expression>(arglist->value_at_index(i));
if (exp->concrete_type() == Expression::NULL_VAL) {
error(
"$selectors: null is not a valid selector: it must be a string,\n"
"a list of strings, or a list of lists of strings for 'selector-nest'",
pstate, traces);
}
if (String_Constant_Obj str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string(ctx.c_options);
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
SelectorListObj sel = Parser::parse_selector(source, ctx, traces);
parsedSelectors.push_back(sel);
}
// Nothing to do
if( parsedSelectors.empty() ) {
return SASS_MEMORY_NEW(Null, pstate);
}
// Set the first element as the `result`, keep
// appending to as we go down the parsedSelector vector.
SelectorStack::iterator itr = parsedSelectors.begin();
SelectorListObj& result = *itr;
++itr;
for(;itr != parsedSelectors.end(); ++itr) {
SelectorListObj& child = *itr;
original_stack.push_back(result);
SelectorListObj rv = child->resolve_parent_refs(original_stack, traces);
result->elements(rv->elements());
original_stack.pop_back();
}
return Cast<Value>(Listize::perform(result));
}
Signature selector_append_sig = "selector-append($selectors...)";
BUILT_IN(selector_append)
{
List* arglist = ARG("$selectors", List);
// Not enough parameters
if (arglist->empty()) {
error(
"$selectors: At least one selector must be "
"passed for `selector-append'",
pstate, traces);
}
// Parse args into vector of selectors
SelectorStack parsedSelectors;
parsedSelectors.push_back({});
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
Expression* exp = Cast<Expression>(arglist->value_at_index(i));
if (exp->concrete_type() == Expression::NULL_VAL) {
error(
"$selectors: null is not a valid selector: it must be a string,\n"
"a list of strings, or a list of lists of strings for 'selector-append'",
pstate, traces);
}
if (String_Constant* str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string();
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
SelectorListObj sel = Parser::parse_selector(source, ctx, traces, true);
for (auto& complex : sel->elements()) {
if (complex->empty()) {
complex->append(SASS_MEMORY_NEW(CompoundSelector, "[phony]"));
}
if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {
comp->hasRealParent(true);
complex->chroots(true);
}
}
if (parsedSelectors.size() > 1) {
if (!sel->has_real_parent_ref()) {
auto parent = parsedSelectors.back();
for (auto& complex : parent->elements()) {
if (CompoundSelector* comp = Cast<CompoundSelector>(complex->first())) {
comp->hasRealParent(false);
}
}
error("Can't append \"" + sel->to_string() + "\" to \"" +
parent->to_string() + "\" for `selector-append'",
pstate, traces);
}
// Build the resolved stack from the left. It's cheaper to directly
// calculate and update each resolved selcted from the left, than to
// recursively calculate them from the right side, as we would need
// to go through the whole stack depth to build the final results.
// E.g. 'a', 'b', 'x, y' => 'a' => 'a b' => 'a b x, a b y'
// vs 'a', 'b', 'x, y' => 'x' => 'b x' => 'a b x', 'y' ...
parsedSelectors.push_back(sel->resolve_parent_refs(parsedSelectors, traces, true));
}
else {
parsedSelectors.push_back(sel);
}
}
// Nothing to do
if( parsedSelectors.empty() ) {
return SASS_MEMORY_NEW(Null, pstate);
}
return Cast<Value>(Listize::perform(parsedSelectors.back()));
}
Signature selector_unify_sig = "selector-unify($selector1, $selector2)";
BUILT_IN(selector_unify)
{
SelectorListObj selector1 = ARGSELS("$selector1");
SelectorListObj selector2 = ARGSELS("$selector2");
SelectorListObj result = selector1->unifyWith(selector2);
return Cast<Value>(Listize::perform(result));
}
Signature simple_selectors_sig = "simple-selectors($selector)";
BUILT_IN(simple_selectors)
{
CompoundSelectorObj sel = ARGSEL("$selector");
List* l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);
for (size_t i = 0, L = sel->length(); i < L; ++i) {
const SimpleSelectorObj& ss = sel->get(i);
sass::string ss_string = ss->to_string() ;
l->append(SASS_MEMORY_NEW(String_Quoted, ss->pstate(), ss_string));
}
return l;
}
Signature selector_extend_sig = "selector-extend($selector, $extendee, $extender)";
BUILT_IN(selector_extend)
{
SelectorListObj selector = ARGSELS("$selector");
SelectorListObj target = ARGSELS("$extendee");
SelectorListObj source = ARGSELS("$extender");
SelectorListObj result = Extender::extend(selector, source, target, traces);
return Cast<Value>(Listize::perform(result));
}
Signature selector_replace_sig = "selector-replace($selector, $original, $replacement)";
BUILT_IN(selector_replace)
{
SelectorListObj selector = ARGSELS("$selector");
SelectorListObj target = ARGSELS("$original");
SelectorListObj source = ARGSELS("$replacement");
SelectorListObj result = Extender::replace(selector, source, target, traces);
return Cast<Value>(Listize::perform(result));
}
Signature selector_parse_sig = "selector-parse($selector)";
BUILT_IN(selector_parse)
{
SelectorListObj selector = ARGSELS("$selector");
return Cast<Value>(Listize::perform(selector));
}
Signature is_superselector_sig = "is-superselector($super, $sub)";
BUILT_IN(is_superselector)
{
SelectorListObj sel_sup = ARGSELS("$super");
SelectorListObj sel_sub = ARGSELS("$sub");
bool result = sel_sup->isSuperselectorOf(sel_sub);
return SASS_MEMORY_NEW(Boolean, pstate, result);
}
}
}

View File

@@ -0,0 +1,35 @@
#ifndef SASS_FN_SELECTORS_H
#define SASS_FN_SELECTORS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
#define ARGSEL(argname) get_arg_sel(argname, env, sig, pstate, traces, ctx)
#define ARGSELS(argname) get_arg_sels(argname, env, sig, pstate, traces, ctx)
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);
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;
}
}
#endif

View File

@@ -0,0 +1,268 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "utf8.h"
#include "ast.hpp"
#include "fn_utils.hpp"
#include "fn_strings.hpp"
#include "util_string.hpp"
namespace Sass {
namespace Functions {
void handle_utf8_error (const SourceSpan& pstate, Backtraces traces)
{
try {
throw;
}
catch (utf8::invalid_code_point&) {
sass::string msg("utf8::invalid_code_point");
error(msg, pstate, traces);
}
catch (utf8::not_enough_room&) {
sass::string msg("utf8::not_enough_room");
error(msg, pstate, traces);
}
catch (utf8::invalid_utf8&) {
sass::string msg("utf8::invalid_utf8");
error(msg, pstate, traces);
}
catch (...) { throw; }
}
///////////////////
// STRING FUNCTIONS
///////////////////
Signature unquote_sig = "unquote($string)";
BUILT_IN(sass_unquote)
{
AST_Node_Obj arg = env["$string"];
if (String_Quoted* string_quoted = Cast<String_Quoted>(arg)) {
String_Constant* result = SASS_MEMORY_NEW(String_Constant, pstate, string_quoted->value());
// remember if the string was quoted (color tokens)
result->is_delayed(true); // delay colors
return result;
}
else if (String_Constant* str = Cast<String_Constant>(arg)) {
return str;
}
else if (Value* ex = Cast<Value>(arg)) {
Sass_Output_Style oldstyle = ctx.c_options.output_style;
ctx.c_options.output_style = SASS_STYLE_NESTED;
sass::string val(arg->to_string(ctx.c_options));
val = Cast<Null>(arg) ? "null" : val;
ctx.c_options.output_style = oldstyle;
deprecated_function("Passing " + val + ", a non-string value, to unquote()", pstate);
return ex;
}
throw std::runtime_error("Invalid Data Type for unquote");
}
Signature quote_sig = "quote($string)";
BUILT_IN(sass_quote)
{
const String_Constant* s = ARG("$string", String_Constant);
String_Quoted *result = SASS_MEMORY_NEW(
String_Quoted, pstate, s->value(),
/*q=*/'\0', /*keep_utf8_escapes=*/false, /*skip_unquoting=*/true);
result->quote_mark('*');
return result;
}
Signature str_length_sig = "str-length($string)";
BUILT_IN(str_length)
{
size_t len = sass::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, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)len);
}
Signature str_insert_sig = "str-insert($string, $insert, $index)";
BUILT_IN(str_insert)
{
sass::string str;
try {
String_Constant* s = ARG("$string", String_Constant);
str = s->value();
String_Constant* i = ARG("$insert", String_Constant);
sass::string ins = i->value();
double index = ARGVAL("$index");
if (index != (int)index) {
sass::ostream strm;
strm << "$index: ";
strm << std::to_string(index);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
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 = 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, traces); }
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
Signature str_index_sig = "str-index($string, $substring)";
BUILT_IN(str_index)
{
size_t index = sass::string::npos;
try {
String_Constant* s = ARG("$string", String_Constant);
String_Constant* t = ARG("$substring", String_Constant);
sass::string str = s->value();
sass::string substr = t->value();
size_t c_index = str.find(substr);
if(c_index == sass::string::npos) {
return SASS_MEMORY_NEW(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, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)index);
}
Signature str_slice_sig = "str-slice($string, $start-at, $end-at:-1)";
BUILT_IN(str_slice)
{
sass::string newstr;
try {
String_Constant* s = ARG("$string", String_Constant);
double start_at = ARGVAL("$start-at");
double end_at = ARGVAL("$end-at");
if (start_at != (int)start_at) {
sass::ostream strm;
strm << "$start-at: ";
strm << std::to_string(start_at);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
String_Quoted* ss = Cast<String_Quoted>(s);
sass::string str(s->value());
size_t size = utf8::distance(str.begin(), str.end());
if (!Cast<Number>(env["$end-at"])) {
end_at = -1;
}
if (end_at != (int)end_at) {
sass::ostream strm;
strm << "$end-at: ";
strm << std::to_string(end_at);
strm << " is not an int";
error(strm.str(), pstate, traces);
}
if (end_at == 0 || (end_at + size) < 0) {
if (ss && ss->quote_mark()) newstr = quote("");
return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);
}
if (end_at < 0) {
end_at += size + 1;
if (end_at == 0) end_at = 1;
}
if (end_at > size) { end_at = (double)size; }
if (start_at < 0) {
start_at += size + 1;
if (start_at <= 0) start_at = 1;
}
else if (start_at == 0) { ++ start_at; }
if (start_at <= end_at)
{
sass::string::iterator start = str.begin();
utf8::advance(start, start_at - 1, str.end());
sass::string::iterator end = start;
utf8::advance(end, end_at - start_at + 1, str.end());
newstr = sass::string(start, end);
}
if (ss) {
if(ss->quote_mark()) newstr = quote(newstr);
}
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, traces); }
return SASS_MEMORY_NEW(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);
sass::string str = s->value();
Util::ascii_str_toupper(&str);
if (String_Quoted* ss = Cast<String_Quoted>(s)) {
String_Quoted* cpy = SASS_MEMORY_COPY(ss);
cpy->value(str);
return cpy;
} else {
return SASS_MEMORY_NEW(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);
sass::string str = s->value();
Util::ascii_str_tolower(&str);
if (String_Quoted* ss = Cast<String_Quoted>(s)) {
String_Quoted* cpy = SASS_MEMORY_COPY(ss);
cpy->value(str);
return cpy;
} else {
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
}
}
}

View File

@@ -0,0 +1,34 @@
#ifndef SASS_FN_STRINGS_H
#define SASS_FN_STRINGS_H
#include "fn_utils.hpp"
namespace Sass {
namespace Functions {
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 length_sig;
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(length);
}
}
#endif

View File

@@ -0,0 +1,158 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "parser.hpp"
#include "fn_utils.hpp"
#include "util_string.hpp"
namespace Sass {
Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)
{
SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[built-in function]", sig, std::string::npos);
Parser sig_parser(source, ctx, ctx.traces);
sig_parser.lex<Prelexer::identifier>();
sass::string name(Util::normalize_underscores(sig_parser.lexed));
Parameters_Obj params = sig_parser.parse_parameters();
return SASS_MEMORY_NEW(Definition,
SourceSpan(source),
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);
SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[c function]", sig, std::string::npos);
Parser sig_parser(source, ctx, ctx.traces);
// 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 >
> >();
sass::string name(Util::normalize_underscores(sig_parser.lexed));
Parameters_Obj params = sig_parser.parse_parameters();
return SASS_MEMORY_NEW(Definition,
SourceSpan(source),
sig,
name,
params,
c_func);
}
namespace Functions {
sass::string function_name(Signature sig)
{
sass::string str(sig);
return str.substr(0, str.find('('));
}
Map* get_arg_m(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces)
{
AST_Node* value = env[argname];
if (Map* map = Cast<Map>(value)) return map;
List* list = Cast<List>(value);
if (list && list->length() == 0) {
return SASS_MEMORY_NEW(Map, pstate, 0);
}
return get_arg<Map>(argname, env, sig, pstate, traces);
}
double get_arg_r(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, double lo, double hi)
{
Number* val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
double v = tmpnr.value();
if (!(lo <= v && v <= hi)) {
sass::ostream msg;
msg << "argument `" << argname << "` of `" << sig << "` must be between ";
msg << lo << " and " << hi;
error(msg.str(), pstate, traces);
}
return v;
}
Number* get_arg_n(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces)
{
Number* val = get_arg<Number>(argname, env, sig, pstate, traces);
val = SASS_MEMORY_COPY(val);
val->reduce();
return val;
}
double get_arg_val(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces)
{
Number* val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
return tmpnr.value();
}
double color_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces)
{
Number* val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
if (tmpnr.unit() == "%") {
return std::min(std::max(tmpnr.value() * 255 / 100.0, 0.0), 255.0);
} else {
return std::min(std::max(tmpnr.value(), 0.0), 255.0);
}
}
double alpha_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces) {
Number* val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
if (tmpnr.unit() == "%") {
return std::min(std::max(tmpnr.value(), 0.0), 100.0);
} else {
return std::min(std::max(tmpnr.value(), 0.0), 1.0);
}
}
SelectorListObj get_arg_sels(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx) {
ExpressionObj exp = ARG(argname, Expression);
if (exp->concrete_type() == Expression::NULL_VAL) {
sass::ostream 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(), exp->pstate(), traces);
}
if (String_Constant* str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string(ctx.c_options);
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
return Parser::parse_selector(source, ctx, traces, false);
}
CompoundSelectorObj get_arg_sel(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx) {
ExpressionObj exp = ARG(argname, Expression);
if (exp->concrete_type() == Expression::NULL_VAL) {
sass::ostream msg;
msg << argname << ": null is not a string for `" << function_name(sig) << "'";
error(msg.str(), exp->pstate(), traces);
}
if (String_Constant* str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
sass::string exp_src = exp->to_string(ctx.c_options);
ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate());
SelectorListObj sel_list = Parser::parse_selector(source, ctx, traces, false);
if (sel_list->length() == 0) return {};
return sel_list->first()->first();
}
}
}

View File

@@ -0,0 +1,62 @@
#ifndef SASS_FN_UTILS_H
#define SASS_FN_UTILS_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "units.hpp"
#include "backtrace.hpp"
#include "environment.hpp"
#include "ast_fwd_decl.hpp"
#include "error_handling.hpp"
namespace Sass {
#define FN_PROTOTYPE \
Env& env, \
Env& d_env, \
Context& ctx, \
Signature sig, \
SourceSpan pstate, \
Backtraces& traces, \
SelectorStack selector_stack, \
SelectorStack original_stack \
typedef const char* Signature;
typedef PreValue* (*Native_Function)(FN_PROTOTYPE);
#define BUILT_IN(name) PreValue* name(FN_PROTOTYPE)
#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)
// special function for weird hsla percent (10px == 10% == 10 != 0.1)
#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double
Definition* make_native_function(Signature, Native_Function, Context& ctx);
Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);
namespace Functions {
template <typename T>
T* get_arg(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces)
{
T* val = Cast<T>(env[argname]);
if (!val) {
error("argument `" + argname + "` of `" + sig + "` must be a " + T::type_name(), pstate, traces);
}
return val;
}
Map* get_arg_m(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // maps only
Number* get_arg_n(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // numbers only
double alpha_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // colors only
double color_num(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // colors only
double get_arg_r(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, double lo, double hi); // colors only
double get_arg_val(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces); // shared
SelectorListObj get_arg_sels(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx); // selectors only
CompoundSelectorObj get_arg_sel(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx); // selectors only
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
#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();
public:
Inspect(const Emitter& emi);
virtual ~Inspect();
// statements
virtual void operator()(Block*);
virtual void operator()(StyleRule*);
virtual void operator()(Bubble*);
virtual void operator()(SupportsRule*);
virtual void operator()(AtRootRule*);
virtual void operator()(AtRule*);
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()(WarningRule*);
virtual void operator()(ErrorRule*);
virtual void operator()(DebugRule*);
virtual void operator()(Comment*);
virtual void operator()(If*);
virtual void operator()(ForRule*);
virtual void operator()(EachRule*);
virtual void operator()(WhileRule*);
virtual void operator()(Return*);
virtual void operator()(ExtendRule*);
virtual void operator()(Definition*);
virtual void operator()(Mixin_Call*);
virtual void operator()(Content*);
// expressions
virtual void operator()(Map*);
virtual void operator()(Function*);
virtual void operator()(List*);
virtual void operator()(Binary_Expression*);
virtual void operator()(Unary_Expression*);
virtual void operator()(Function_Call*);
// virtual void operator()(Custom_Warning*);
// virtual void operator()(Custom_Error*);
virtual void operator()(Variable*);
virtual void operator()(Number*);
virtual void operator()(Color_RGBA*);
virtual void operator()(Color_HSLA*);
virtual void operator()(Boolean*);
virtual void operator()(String_Schema*);
virtual void operator()(String_Constant*);
virtual void operator()(String_Quoted*);
virtual void operator()(Custom_Error*);
virtual void operator()(Custom_Warning*);
virtual void operator()(SupportsOperation*);
virtual void operator()(SupportsNegation*);
virtual void operator()(SupportsDeclaration*);
virtual void operator()(Supports_Interpolation*);
virtual void operator()(MediaRule*);
virtual void operator()(CssMediaRule*);
virtual void operator()(CssMediaQuery*);
virtual void operator()(Media_Query*);
virtual void operator()(Media_Query_Expression*);
virtual void operator()(At_Root_Query*);
virtual void operator()(Null*);
virtual void operator()(Parent_Reference* 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()(PlaceholderSelector*);
virtual void operator()(TypeSelector*);
virtual void operator()(ClassSelector*);
virtual void operator()(IDSelector*);
virtual void operator()(AttributeSelector*);
virtual void operator()(PseudoSelector*);
virtual void operator()(SelectorComponent*);
virtual void operator()(SelectorCombinator*);
virtual void operator()(CompoundSelector*);
virtual void operator()(ComplexSelector*);
virtual void operator()(SelectorList*);
virtual sass::string lbracket(List*);
virtual sass::string rbracket(List*);
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,122 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <iostream>
#include <iomanip>
#include "lexer.hpp"
#include "constants.hpp"
#include "util_string.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); };
bool is_number(char chr) {
return Util::ascii_isdigit(static_cast<unsigned char>(chr)) ||
chr == '-' || chr == '+';
}
// check if char is within a reduced ascii range
// valid in a uri (copied from Ruby Sass)
bool is_uri_character(char chr)
{
unsigned int cmp = unsigned(chr);
return (cmp > 41 && cmp < 127) ||
cmp == ':' || cmp == '/';
}
// check if char is within a reduced ascii range
// valid for escaping (copied from Ruby Sass)
bool is_escapable_character(char chr)
{
unsigned int cmp = unsigned(chr);
return cmp > 31 && cmp < 127;
}
// Match word character (look ahead)
bool is_character(char chr)
{
// valid alpha, numeric or unicode char (plus hyphen)
return Util::ascii_isalnum(static_cast<unsigned char>(chr)) ||
!Util::ascii_isascii(static_cast<unsigned char>(chr)) ||
chr == '-';
}
//####################################
// BASIC CLASS MATCHERS
//####################################
// create matchers that advance the position
const char* space(const char* src) { return Util::ascii_isspace(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }
const char* alpha(const char* src) { return Util::ascii_isalpha(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }
const char* nonascii(const char* src) { return Util::ascii_isascii(static_cast<unsigned char>(*src)) ? nullptr : src + 1; }
const char* digit(const char* src) { return Util::ascii_isdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }
const char* xdigit(const char* src) { return Util::ascii_isxdigit(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }
const char* alnum(const char* src) { return Util::ascii_isalnum(static_cast<unsigned char>(*src)) ? src + 1 : nullptr; }
const char* hyphen(const char* src) { return *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); }
const char* hyphens(const char* src) { return one_plus<hyphen>(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) || *src == '#' ? 0 : src; }
// Match linefeed /(?:\n|\r\n?|\f)/
const char* re_linebreak(const char* src)
{
// end of file or unix linefeed return here
if (*src == 0) return src;
// end of file or unix linefeed return here
if (*src == '\n' || *src == '\f') 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 == '\f' ? 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;
}
}
}

View File

@@ -0,0 +1,304 @@
#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
//####################################
// Matches ASCII digits, +, and -.
bool is_number(char src);
bool is_uri_character(char src);
bool escapable_character(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* hyphen(const char* src);
const char* nonascii(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);
const char* hyphens(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 == NULL) return 0;
const char* pre = str;
if (src == NULL) 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 a single character literal.
// Regex equivalent: /(?:x)/i
// only define lower case alpha chars
template <char chr>
const char* insensitive(const char* src) {
return *src == chr || *src+32 == chr ? src + 1 : 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 == NULL) return 0;
const char* pre = str;
if (src == NULL) 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 for members of char class.
// Regex equivalent: /[^axy]/
template <const char* neg_char_class>
const char* neg_class_char(const char* src) {
if (*src == 0) return 0;
const char* cc = neg_char_class;
while (*cc && *src != *cc) ++cc;
return *cc ? 0 : src + 1;
}
// Match for members of char class.
// Regex equivalent: /[^axy]+/
template <const char* neg_char_class>
const char* neg_class_chars(const char* src) {
const char* p = src;
while (neg_class_char<neg_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

View File

@@ -0,0 +1,70 @@
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include <iostream>
#include <typeinfo>
#include <string>
#include "listize.hpp"
#include "context.hpp"
#include "backtrace.hpp"
#include "error_handling.hpp"
namespace Sass {
Listize::Listize()
{ }
Expression* Listize::perform(AST_Node* node)
{
Listize listize;
return node->perform(&listize);
}
Expression* Listize::operator()(SelectorList* sel)
{
List_Obj l = SASS_MEMORY_NEW(List, sel->pstate(), sel->length(), SASS_COMMA);
l->from_selector(true);
for (size_t i = 0, L = sel->length(); i < L; ++i) {
if (!sel->at(i)) continue;
l->append(sel->at(i)->perform(this));
}
if (l->length()) return l.detach();
return SASS_MEMORY_NEW(Null, l->pstate());
}
Expression* Listize::operator()(CompoundSelector* sel)
{
sass::string str;
for (size_t i = 0, L = sel->length(); i < L; ++i) {
Expression* e = (*sel)[i]->perform(this);
if (e) str += e->to_string();
}
return SASS_MEMORY_NEW(String_Quoted, sel->pstate(), str);
}
Expression* Listize::operator()(ComplexSelector* sel)
{
List_Obj l = SASS_MEMORY_NEW(List, sel->pstate());
// ToDo: investigate what this does
// Note: seems reated to parent ref
l->from_selector(true);
for (auto component : sel->elements()) {
if (CompoundSelectorObj compound = Cast<CompoundSelector>(component)) {
if (!compound->empty()) {
ExpressionObj hh = compound->perform(this);
if (hh) l->append(hh);
}
}
else if (component) {
l->append(SASS_MEMORY_NEW(String_Quoted, component->pstate(), component->to_string()));
}
}
if (l->length() == 0) return 0;
return l.detach();
}
}

View File

@@ -0,0 +1,37 @@
#ifndef SASS_LISTIZE_H
#define SASS_LISTIZE_H
// sass.hpp must go before all system headers to get the
// __EXTENSIONS__ fix on Solaris.
#include "sass.hpp"
#include "ast_fwd_decl.hpp"
#include "operation.hpp"
namespace Sass {
struct Backtrace;
class Listize : public Operation_CRTP<Expression*, Listize> {
public:
static Expression* perform(AST_Node* node);
public:
Listize();
~Listize() { }
Expression* operator()(SelectorList*);
Expression* operator()(ComplexSelector*);
Expression* operator()(CompoundSelector*);
// generic fallback
template <typename U>
Expression* fallback(U x)
{ return Cast<Expression>(x); }
};
}
#endif

View File

@@ -0,0 +1,19 @@
#ifndef SASS_MAPPING_H
#define SASS_MAPPING_H
#include "position.hpp"
#include "backtrace.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

View File

@@ -0,0 +1,12 @@
#ifndef SASS_MEMORY_H
#define SASS_MEMORY_H
#include "settings.hpp"
// Include memory headers
#include "memory/config.hpp"
#include "memory/allocator.hpp"
#include "memory/shared_ptr.hpp"
#include "memory/memory_pool.hpp"
#endif

View File

@@ -0,0 +1,48 @@
#include "../sass.hpp"
#include "allocator.hpp"
#include "memory_pool.hpp"
#if defined (_MSC_VER) // Visual studio
#define thread_local __declspec( thread )
#elif defined (__GCC__) // GCC
#define thread_local __thread
#endif
namespace Sass {
#ifdef SASS_CUSTOM_ALLOCATOR
// Only use PODs for thread_local
// Objects get unpredictable init order
static thread_local MemoryPool* pool;
static thread_local size_t allocations;
void* allocateMem(size_t size)
{
if (pool == nullptr) {
pool = new MemoryPool();
}
allocations++;
return pool->allocate(size);
}
void deallocateMem(void* ptr, size_t size)
{
// It seems thread_local variable might be discharged!?
// But the destructors of e.g. static strings is still
// called, although their memory was discharged too.
// Fine with me as long as address sanitizer is happy.
if (pool == nullptr || allocations == 0) { return; }
pool->deallocate(ptr);
if (--allocations == 0) {
delete pool;
pool = nullptr;
}
}
#endif
}

View File

@@ -0,0 +1,138 @@
#ifndef SASS_ALLOCATOR_H
#define SASS_ALLOCATOR_H
#include "config.hpp"
#include "../settings.hpp"
#include "../MurmurHash2.hpp"
#include <vector>
#include <limits>
#include <iostream>
#include <algorithm>
#include <functional>
namespace Sass {
#ifndef SASS_CUSTOM_ALLOCATOR
template <typename T> using Allocator = std::allocator<T>;
#else
void* allocateMem(size_t size);
void deallocateMem(void* ptr, size_t size = 1);
template<typename T>
class Allocator
{
public:
// Allocator traits
typedef T type;
typedef type value_type;
typedef value_type* pointer;
typedef value_type const* const_pointer;
typedef value_type& reference;
typedef value_type const& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U>
struct rebind
{
typedef Allocator<U> other;
};
// Constructor
Allocator(void) {}
// Copy Constructor
template<typename U>
Allocator(Allocator<U> const&)
{}
// allocate but don't initialize count of elements of type T
pointer allocate(size_type count, const_pointer /* hint */ = 0)
{
return (pointer)(Sass::allocateMem(count * sizeof(T)));
}
// deallocate storage ptr of deleted elements
void deallocate(pointer ptr, size_type count)
{
Sass::deallocateMem(ptr, count);
}
// return maximum number of elements that can be allocated
size_type max_size() const throw()
{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
// Address of object
type* address(type& obj) const { return &obj; }
type const* address(type const& obj) const { return &obj; }
// Construct object
void construct(type* ptr, type const& ref) const
{
// In-place copy construct
new(ptr) type(ref);
}
// Destroy object
void destroy(type* ptr) const
{
// Call destructor
ptr->~type();
}
};
template<typename T, typename U>
bool operator==(Allocator<T> const& left,
Allocator<U> const& right)
{
return true;
}
template<typename T, typename U>
bool operator!=(Allocator<T> const& left,
Allocator<U> const& right)
{
return !(left == right);
}
#endif
namespace sass {
template <typename T> using vector = std::vector<T, Sass::Allocator<T>>;
using string = std::basic_string<char, std::char_traits<char>, Sass::Allocator<char>>;
using sstream = std::basic_stringstream<char, std::char_traits<char>, Sass::Allocator<char>>;
using ostream = std::basic_ostringstream<char, std::char_traits<char>, Sass::Allocator<char>>;
using istream = std::basic_istringstream<char, std::char_traits<char>, Sass::Allocator<char>>;
}
}
#ifdef SASS_CUSTOM_ALLOCATOR
namespace std {
// Only GCC seems to need this specialization!?
template <> struct hash<Sass::sass::string> {
public:
inline size_t operator()(
const Sass::sass::string& name) const
{
return MurmurHash2(
(void*)name.c_str(),
(int)name.size(),
0x73617373);
}
};
}
#endif
#endif

View File

@@ -0,0 +1,20 @@
#ifndef SASS_MEMORY_CONFIG_H
#define SASS_MEMORY_CONFIG_H
// Define memory alignment requirements
#define SASS_MEM_ALIGN sizeof(unsigned int)
// Minimal alignment for memory fragments. Must be a multiple
// of `SASS_MEM_ALIGN` and should not be too big (maybe 1 or 2)
#define SassAllocatorHeadSize sizeof(unsigned int)
// The number of bytes we use for our book-keeping before every
// memory fragment. Needed to know to which bucket we belongs on
// deallocations, or if it should go directly to the `free` call.
#define SassAllocatorBookSize sizeof(unsigned int)
// Bytes reserve for book-keeping on the arenas
// Currently unused and for later optimization
#define SassAllocatorArenaHeadSize 0
#endif

Some files were not shown because too many files have changed in this diff Show More