1
+ #include < cstdint>
2
+ #include < cstdlib>
3
+ #include < cmath>
4
+ #include < cctype>
5
+ #include < random>
6
+ #include < sstream>
7
+ #include < iomanip>
8
+ #include < algorithm>
9
+
10
+ #include " ast.hpp"
11
+ #include " sass.hpp"
12
+ #include " units.hpp"
13
+ #include " fn_utils.hpp"
14
+ #include " fn_numbers.hpp"
15
+
16
+ #ifdef __MINGW32__
17
+ #include " windows.h"
18
+ #include " wincrypt.h"
19
+ #endif
20
+
21
+ namespace Sass {
22
+
23
+ namespace Functions {
24
+
25
+ #ifdef __MINGW32__
26
+ uint64_t GetSeed ()
27
+ {
28
+ HCRYPTPROV hp = 0 ;
29
+ BYTE rb[8 ];
30
+ CryptAcquireContext (&hp, 0 , 0 , PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
31
+ CryptGenRandom (hp, sizeof (rb), rb);
32
+ CryptReleaseContext (hp, 0 );
33
+
34
+ uint64_t seed;
35
+ memcpy (&seed, &rb[0 ], sizeof (seed));
36
+
37
+ return seed;
38
+ }
39
+ #else
40
+ uint64_t GetSeed ()
41
+ {
42
+ std::random_device rd;
43
+ return rd ();
44
+ }
45
+ #endif
46
+
47
+ // note: the performance of many implementations of
48
+ // random_device degrades sharply once the entropy pool
49
+ // is exhausted. For practical use, random_device is
50
+ // generally only used to seed a PRNG such as mt19937.
51
+ static std::mt19937 rand (static_cast <unsigned int >(GetSeed()));
52
+
53
+ // /////////////////
54
+ // NUMBER FUNCTIONS
55
+ // /////////////////
56
+
57
+ Signature percentage_sig = " percentage($number)" ;
58
+ BUILT_IN (percentage)
59
+ {
60
+ Number_Obj n = ARGN (" $number" );
61
+ if (!n->is_unitless ()) error (" argument $number of `" + std::string (sig) + " ` must be unitless" , pstate, traces);
62
+ return SASS_MEMORY_NEW (Number, pstate, n->value () * 100 , " %" );
63
+ }
64
+
65
+ Signature round_sig = " round($number)" ;
66
+ BUILT_IN (round)
67
+ {
68
+ Number_Obj r = ARGN (" $number" );
69
+ r->value (Sass::round (r->value (), ctx.c_options .precision ));
70
+ r->pstate (pstate);
71
+ return r.detach ();
72
+ }
73
+
74
+ Signature ceil_sig = " ceil($number)" ;
75
+ BUILT_IN (ceil)
76
+ {
77
+ Number_Obj r = ARGN (" $number" );
78
+ r->value (std::ceil (r->value ()));
79
+ r->pstate (pstate);
80
+ return r.detach ();
81
+ }
82
+
83
+ Signature floor_sig = " floor($number)" ;
84
+ BUILT_IN (floor)
85
+ {
86
+ Number_Obj r = ARGN (" $number" );
87
+ r->value (std::floor (r->value ()));
88
+ r->pstate (pstate);
89
+ return r.detach ();
90
+ }
91
+
92
+ Signature abs_sig = " abs($number)" ;
93
+ BUILT_IN (abs)
94
+ {
95
+ Number_Obj r = ARGN (" $number" );
96
+ r->value (std::abs (r->value ()));
97
+ r->pstate (pstate);
98
+ return r.detach ();
99
+ }
100
+
101
+ Signature min_sig = " min($numbers...)" ;
102
+ BUILT_IN (min)
103
+ {
104
+ List_Ptr arglist = ARG (" $numbers" , List);
105
+ Number_Obj least = NULL ;
106
+ for (size_t i = 0 , L = arglist->length (); i < L; ++i) {
107
+ Expression_Obj val = arglist->value_at_index (i);
108
+ Number_Obj xi = Cast<Number>(val);
109
+ if (!xi) {
110
+ error (" \" " + val->to_string (ctx.c_options ) + " \" is not a number for `min'" , pstate, traces);
111
+ }
112
+ if (least) {
113
+ if (*xi < *least) least = xi;
114
+ } else least = xi;
115
+ }
116
+ return least.detach ();
117
+ }
118
+
119
+ Signature max_sig = " max($numbers...)" ;
120
+ BUILT_IN (max)
121
+ {
122
+ List_Ptr arglist = ARG (" $numbers" , List);
123
+ Number_Obj greatest = NULL ;
124
+ for (size_t i = 0 , L = arglist->length (); i < L; ++i) {
125
+ Expression_Obj val = arglist->value_at_index (i);
126
+ Number_Obj xi = Cast<Number>(val);
127
+ if (!xi) {
128
+ error (" \" " + val->to_string (ctx.c_options ) + " \" is not a number for `max'" , pstate, traces);
129
+ }
130
+ if (greatest) {
131
+ if (*greatest < *xi) greatest = xi;
132
+ } else greatest = xi;
133
+ }
134
+ return greatest.detach ();
135
+ }
136
+
137
+ Signature random_sig = " random($limit:false)" ;
138
+ BUILT_IN (random)
139
+ {
140
+ AST_Node_Obj arg = env[" $limit" ];
141
+ Value_Ptr v = Cast<Value>(arg);
142
+ Number_Ptr l = Cast<Number>(arg);
143
+ Boolean_Ptr b = Cast<Boolean>(arg);
144
+ if (l) {
145
+ double lv = l->value ();
146
+ if (lv < 1 ) {
147
+ std::stringstream err;
148
+ err << " $limit " << lv << " must be greater than or equal to 1 for `random'" ;
149
+ error (err.str (), pstate, traces);
150
+ }
151
+ bool eq_int = std::fabs (trunc (lv) - lv) < NUMBER_EPSILON;
152
+ if (!eq_int) {
153
+ std::stringstream err;
154
+ err << " Expected $limit to be an integer but got " << lv << " for `random'" ;
155
+ error (err.str (), pstate, traces);
156
+ }
157
+ std::uniform_real_distribution<> distributor (1 , lv + 1 );
158
+ uint_fast32_t distributed = static_cast <uint_fast32_t >(distributor (rand));
159
+ return SASS_MEMORY_NEW (Number, pstate, (double )distributed);
160
+ }
161
+ else if (b) {
162
+ std::uniform_real_distribution<> distributor (0 , 1 );
163
+ double distributed = static_cast <double >(distributor (rand));
164
+ return SASS_MEMORY_NEW (Number, pstate, distributed);
165
+ } else if (v) {
166
+ traces.push_back (Backtrace (pstate));
167
+ throw Exception::InvalidArgumentType (pstate, traces, " random" , " $limit" , " number" , v);
168
+ } else {
169
+ traces.push_back (Backtrace (pstate));
170
+ throw Exception::InvalidArgumentType (pstate, traces, " random" , " $limit" , " number" );
171
+ }
172
+ }
173
+
174
+ Signature unique_id_sig = " unique-id()" ;
175
+ BUILT_IN (unique_id)
176
+ {
177
+ std::stringstream ss;
178
+ std::uniform_real_distribution<> distributor (0 , 4294967296 ); // 16^8
179
+ uint_fast32_t distributed = static_cast <uint_fast32_t >(distributor (rand));
180
+ ss << " u" << std::setfill (' 0' ) << std::setw (8 ) << std::hex << distributed;
181
+ return SASS_MEMORY_NEW (String_Quoted, pstate, ss.str ());
182
+ }
183
+
184
+ Signature unit_sig = " unit($number)" ;
185
+ BUILT_IN (unit)
186
+ {
187
+ Number_Obj arg = ARGN (" $number" );
188
+ std::string str (quote (arg->unit (), ' "' ));
189
+ return SASS_MEMORY_NEW (String_Quoted, pstate, str);
190
+ }
191
+
192
+ Signature unitless_sig = " unitless($number)" ;
193
+ BUILT_IN (unitless)
194
+ {
195
+ Number_Obj arg = ARGN (" $number" );
196
+ bool unitless = arg->is_unitless ();
197
+ return SASS_MEMORY_NEW (Boolean, pstate, unitless);
198
+ }
199
+
200
+ Signature comparable_sig = " comparable($number-1, $number-2)" ;
201
+ BUILT_IN (comparable)
202
+ {
203
+ Number_Obj n1 = ARGN (" $number-1" );
204
+ Number_Obj n2 = ARGN (" $number-2" );
205
+ if (n1->is_unitless () || n2->is_unitless ()) {
206
+ return SASS_MEMORY_NEW (Boolean, pstate, true );
207
+ }
208
+ // normalize into main units
209
+ n1->normalize (); n2->normalize ();
210
+ Units &lhs_unit = *n1, &rhs_unit = *n2;
211
+ bool is_comparable = (lhs_unit == rhs_unit);
212
+ return SASS_MEMORY_NEW (Boolean, pstate, is_comparable);
213
+ }
214
+
215
+ }
216
+
217
+ }
0 commit comments