diff --git a/Makefile.conf b/Makefile.conf index 161ee9767..8ebba3ad2 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -11,6 +11,7 @@ SOURCES = \ context.cpp \ constants.cpp \ fn_utils.cpp \ + fn_numbers.cpp \ functions.cpp \ color_maps.cpp \ environment.cpp \ diff --git a/src/fn_numbers.cpp b/src/fn_numbers.cpp new file mode 100644 index 000000000..249d69856 --- /dev/null +++ b/src/fn_numbers.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ast.hpp" +#include "sass.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(GetSeed())); + + /////////////////// + // NUMBER FUNCTIONS + /////////////////// + + Signature percentage_sig = "percentage($number)"; + BUILT_IN(percentage) + { + Number_Obj n = ARGN("$number"); + if (!n->is_unitless()) error("argument $number of `" + std::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_Ptr arglist = ARG("$numbers", List); + Number_Obj least = NULL; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj val = arglist->value_at_index(i); + Number_Obj xi = Cast(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_Ptr arglist = ARG("$numbers", List); + Number_Obj greatest = NULL; + for (size_t i = 0, L = arglist->length(); i < L; ++i) { + Expression_Obj val = arglist->value_at_index(i); + Number_Obj xi = Cast(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_Ptr v = Cast(arg); + Number_Ptr l = Cast(arg); + Boolean_Ptr b = Cast(arg); + if (l) { + double lv = l->value(); + if (lv < 1) { + std::stringstream 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) { + std::stringstream 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(distributor(rand)); + return SASS_MEMORY_NEW(Number, pstate, (double)distributed); + } + else if (b) { + std::uniform_real_distribution<> distributor(0, 1); + double distributed = static_cast(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) + { + std::stringstream ss; + std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 + uint_fast32_t distributed = static_cast(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"); + std::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($number-1, $number-2)"; + BUILT_IN(comparable) + { + Number_Obj n1 = ARGN("$number-1"); + Number_Obj n2 = ARGN("$number-2"); + 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); + } + + } + +} \ No newline at end of file diff --git a/src/fn_numbers.hpp b/src/fn_numbers.hpp new file mode 100644 index 000000000..dba96be0b --- /dev/null +++ b/src/fn_numbers.hpp @@ -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 \ No newline at end of file diff --git a/src/fn_utils.hpp b/src/fn_utils.hpp index eaeac0228..c373a71fb 100644 --- a/src/fn_utils.hpp +++ b/src/fn_utils.hpp @@ -13,9 +13,6 @@ namespace Sass { #define ARG(argname, argtype) get_arg(argname, env, sig, pstate, traces) #define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces) - // 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 - // special function for weird hsla percent (10px == 10% == 10 != 0.1) #define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double diff --git a/src/functions.hpp b/src/functions.hpp index fa6d5d7f0..23a789930 100644 --- a/src/functions.hpp +++ b/src/functions.hpp @@ -55,15 +55,6 @@ namespace Sass { extern Signature str_slice_sig; extern Signature to_upper_case_sig; extern Signature to_lower_case_sig; - extern Signature percentage_sig; - extern Signature round_sig; - extern Signature ceil_sig; - extern Signature floor_sig; - extern Signature abs_sig; - extern Signature min_sig; - extern Signature max_sig; - extern Signature inspect_sig; - extern Signature random_sig; extern Signature length_sig; extern Signature nth_sig; extern Signature index_sig; @@ -72,9 +63,6 @@ namespace Sass { extern Signature zip_sig; extern Signature list_separator_sig; extern Signature type_of_sig; - extern Signature unit_sig; - extern Signature unitless_sig; - extern Signature comparable_sig; extern Signature variable_exists_sig; extern Signature global_variable_exists_sig; extern Signature function_exists_sig; @@ -91,7 +79,6 @@ namespace Sass { extern Signature map_has_key_sig; extern Signature keywords_sig; extern Signature set_nth_sig; - extern Signature unique_id_sig; extern Signature selector_nest_sig; extern Signature selector_append_sig; extern Signature selector_extend_sig; @@ -139,15 +126,7 @@ namespace Sass { BUILT_IN(str_slice); BUILT_IN(to_upper_case); BUILT_IN(to_lower_case); - BUILT_IN(percentage); - BUILT_IN(round); - BUILT_IN(ceil); - BUILT_IN(floor); - BUILT_IN(abs); - BUILT_IN(min); - BUILT_IN(max); BUILT_IN(inspect); - BUILT_IN(random); BUILT_IN(length); BUILT_IN(nth); BUILT_IN(index); @@ -156,9 +135,6 @@ namespace Sass { BUILT_IN(zip); BUILT_IN(list_separator); BUILT_IN(type_of); - BUILT_IN(unit); - BUILT_IN(unitless); - BUILT_IN(comparable); BUILT_IN(variable_exists); BUILT_IN(global_variable_exists); BUILT_IN(function_exists); @@ -175,7 +151,6 @@ namespace Sass { BUILT_IN(map_has_key); BUILT_IN(keywords); BUILT_IN(set_nth); - BUILT_IN(unique_id); BUILT_IN(selector_nest); BUILT_IN(selector_append); BUILT_IN(selector_extend); diff --git a/win/libsass.targets b/win/libsass.targets index 8bf68df60..176f29e2a 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -34,6 +34,7 @@ + @@ -88,6 +89,7 @@ + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 1f301f769..46f42ae59 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -114,6 +114,9 @@ Headers + + Headers + Headers @@ -275,6 +278,9 @@ Sources + + Sources + Sources