diff --git a/Makefile.conf b/Makefile.conf index 08c455667..d5c2e23e3 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -13,6 +13,7 @@ SOURCES = \ fn_utils.cpp \ fn_colors.cpp \ fn_numbers.cpp \ + fn_strings.cpp \ fn_selectors.cpp \ functions.cpp \ color_maps.cpp \ diff --git a/src/context.cpp b/src/context.cpp index 58dbc7663..d8c688e5f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -32,6 +32,7 @@ #include "fn_utils.hpp" #include "fn_colors.hpp" #include "fn_numbers.hpp" +#include "fn_strings.hpp" #include "fn_selectors.hpp" namespace Sass { diff --git a/src/fn_strings.cpp b/src/fn_strings.cpp new file mode 100644 index 000000000..7949cc4f1 --- /dev/null +++ b/src/fn_strings.cpp @@ -0,0 +1,258 @@ +#include "ast.hpp" +#include "fn_utils.hpp" +#include "fn_strings.hpp" + +namespace Sass { + + namespace Functions { + + void handle_utf8_error (const ParserState& pstate, Backtraces traces) + { + try { + throw; + } + catch (utf8::invalid_code_point) { + std::string msg("utf8::invalid_code_point"); + error(msg, pstate, traces); + } + catch (utf8::not_enough_room) { + std::string msg("utf8::not_enough_room"); + error(msg, pstate, traces); + } + catch (utf8::invalid_utf8) { + std::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_Ptr string_quoted = Cast(arg)) { + String_Constant_Ptr 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_Ptr str = Cast(arg)) { + return str; + } + else if (Value_Ptr ex = Cast(arg)) { + Sass_Output_Style oldstyle = ctx.c_options.output_style; + ctx.c_options.output_style = SASS_STYLE_NESTED; + std::string val(arg->to_string(ctx.c_options)); + val = Cast(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) + { + AST_Node_Obj arg = env["$string"]; + // only set quote mark to true if already a string + if (String_Quoted_Ptr qstr = Cast(arg)) { + qstr->quote_mark('*'); + return qstr; + } + // all other nodes must be converted to a string node + std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); + String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); + result->quote_mark('*'); + return result; + } + + + Signature str_length_sig = "str-length($string)"; + BUILT_IN(str_length) + { + size_t len = std::string::npos; + try { + String_Constant_Ptr 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) + { + std::string str; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + str = s->value(); + str = unquote(str); + String_Constant_Ptr i = ARG("$insert", String_Constant); + std::string ins = i->value(); + ins = unquote(ins); + double index = ARGVAL("$index"); + 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(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(index)), ins); + } + else { + // negative and past string length + str = ins + str; + } + + if (String_Quoted_Ptr ss = Cast(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 = std::string::npos; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + String_Constant_Ptr t = ARG("$substring", String_Constant); + std::string str = s->value(); + str = unquote(str); + std::string substr = t->value(); + substr = unquote(substr); + + size_t c_index = str.find(substr); + if(c_index == std::string::npos) { + return SASS_MEMORY_NEW(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) + { + std::string newstr; + try { + String_Constant_Ptr s = ARG("$string", String_Constant); + double start_at = ARGVAL("$start-at"); + double end_at = ARGVAL("$end-at"); + String_Quoted_Ptr ss = Cast(s); + + std::string str = unquote(s->value()); + + size_t size = utf8::distance(str.begin(), str.end()); + + if (!Cast(env["$end-at"])) { + end_at = -1; + } + + 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 = 0; + } + else if (start_at == 0) { ++ start_at; } + + if (start_at <= end_at) + { + std::string::iterator start = str.begin(); + utf8::advance(start, start_at - 1, str.end()); + std::string::iterator end = start; + utf8::advance(end, end_at - start_at + 1, str.end()); + newstr = std::string(start, end); + } + if (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_Ptr s = ARG("$string", String_Constant); + std::string str = s->value(); + + for (size_t i = 0, L = str.length(); i < L; ++i) { + if (Sass::Util::isAscii(str[i])) { + str[i] = std::toupper(str[i]); + } + } + + if (String_Quoted_Ptr ss = Cast(s)) { + String_Quoted_Ptr 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_Ptr s = ARG("$string", String_Constant); + std::string str = s->value(); + + for (size_t i = 0, L = str.length(); i < L; ++i) { + if (Sass::Util::isAscii(str[i])) { + str[i] = std::tolower(str[i]); + } + } + + if (String_Quoted_Ptr ss = Cast(s)) { + String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); + cpy->value(str); + return cpy; + } else { + return SASS_MEMORY_NEW(String_Quoted, pstate, str); + } + } + + } + +} diff --git a/src/fn_strings.hpp b/src/fn_strings.hpp new file mode 100644 index 000000000..4a1ed1900 --- /dev/null +++ b/src/fn_strings.hpp @@ -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 diff --git a/src/functions.cpp b/src/functions.cpp index b2ba02451..b0b8e7a66 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -28,6 +28,7 @@ #include "fn_utils.hpp" #include "fn_colors.hpp" #include "fn_numbers.hpp" +#include "fn_strings.hpp" #include "fn_selectors.hpp" namespace Sass { @@ -42,26 +43,6 @@ namespace Sass { namespace Functions { - inline void handle_utf8_error (const ParserState& pstate, Backtraces traces) - { - try { - throw; - } - catch (utf8::invalid_code_point) { - std::string msg("utf8::invalid_code_point"); - error(msg, pstate, traces); - } - catch (utf8::not_enough_room) { - std::string msg("utf8::not_enough_room"); - error(msg, pstate, traces); - } - catch (utf8::invalid_utf8) { - std::string msg("utf8::invalid_utf8"); - error(msg, pstate, traces); - } - catch (...) { throw; } - } - // features static std::set features { "global-variable-shadowing", @@ -71,232 +52,6 @@ namespace Sass { "custom-property" }; - /////////////////// - // STRING FUNCTIONS - /////////////////// - - Signature unquote_sig = "unquote($string)"; - BUILT_IN(sass_unquote) - { - AST_Node_Obj arg = env["$string"]; - if (String_Quoted_Ptr string_quoted = Cast(arg)) { - String_Constant_Ptr 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_Ptr str = Cast(arg)) { - return str; - } - else if (Expression_Ptr ex = Cast(arg)) { - Sass_Output_Style oldstyle = ctx.c_options.output_style; - ctx.c_options.output_style = SASS_STYLE_NESTED; - std::string val(arg->to_string(ctx.c_options)); - val = Cast(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) - { - AST_Node_Obj arg = env["$string"]; - // only set quote mark to true if already a string - if (String_Quoted_Ptr qstr = Cast(arg)) { - qstr->quote_mark('*'); - return qstr; - } - // all other nodes must be converted to a string node - std::string str(quote(arg->to_string(ctx.c_options), String_Constant::double_quote())); - String_Quoted_Ptr result = SASS_MEMORY_NEW(String_Quoted, pstate, str); - result->quote_mark('*'); - return result; - } - - - Signature str_length_sig = "str-length($string)"; - BUILT_IN(str_length) - { - size_t len = std::string::npos; - try { - String_Constant_Ptr 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) - { - std::string str; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - str = s->value(); - str = unquote(str); - String_Constant_Ptr i = ARG("$insert", String_Constant); - std::string ins = i->value(); - ins = unquote(ins); - double index = ARGVAL("$index"); - 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(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(index)), ins); - } - else { - // negative and past string length - str = ins + str; - } - - if (String_Quoted_Ptr ss = Cast(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 = std::string::npos; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - String_Constant_Ptr t = ARG("$substring", String_Constant); - std::string str = s->value(); - str = unquote(str); - std::string substr = t->value(); - substr = unquote(substr); - - size_t c_index = str.find(substr); - if(c_index == std::string::npos) { - return SASS_MEMORY_NEW(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) - { - std::string newstr; - try { - String_Constant_Ptr s = ARG("$string", String_Constant); - double start_at = ARGVAL("$start-at"); - double end_at = ARGVAL("$end-at"); - String_Quoted_Ptr ss = Cast(s); - - std::string str = unquote(s->value()); - - size_t size = utf8::distance(str.begin(), str.end()); - - if (!Cast(env["$end-at"])) { - end_at = -1; - } - - 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 = 0; - } - else if (start_at == 0) { ++ start_at; } - - if (start_at <= end_at) - { - std::string::iterator start = str.begin(); - utf8::advance(start, start_at - 1, str.end()); - std::string::iterator end = start; - utf8::advance(end, end_at - start_at + 1, str.end()); - newstr = std::string(start, end); - } - if (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_Ptr s = ARG("$string", String_Constant); - std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::toupper(str[i]); - } - } - - if (String_Quoted_Ptr ss = Cast(s)) { - String_Quoted_Ptr 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_Ptr s = ARG("$string", String_Constant); - std::string str = s->value(); - - for (size_t i = 0, L = str.length(); i < L; ++i) { - if (Sass::Util::isAscii(str[i])) { - str[i] = std::tolower(str[i]); - } - } - - if (String_Quoted_Ptr ss = Cast(s)) { - String_Quoted_Ptr cpy = SASS_MEMORY_COPY(ss); - cpy->value(str); - return cpy; - } else { - return SASS_MEMORY_NEW(String_Quoted, pstate, str); - } - } ///////////////// // LIST FUNCTIONS ///////////////// diff --git a/src/functions.hpp b/src/functions.hpp index 58c57b87f..e6bbc07ce 100644 --- a/src/functions.hpp +++ b/src/functions.hpp @@ -17,14 +17,6 @@ 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; extern Signature nth_sig; extern Signature index_sig; @@ -53,15 +45,6 @@ namespace Sass { extern Signature content_exists_sig; extern Signature get_function_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(inspect); BUILT_IN(length); BUILT_IN(nth); BUILT_IN(index); diff --git a/win/libsass.targets b/win/libsass.targets index 97dbecec8..21e74f82f 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -36,6 +36,7 @@ + @@ -93,6 +94,7 @@ + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index db2f369ad..30b43fbf0 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -120,6 +120,9 @@ Headers + + Headers + Headers @@ -290,6 +293,9 @@ Sources + + Sources + Sources