Skip to content

Commit 661a51e

Browse files
committed
Implement color fn with one channels arg
1 parent fc3cd34 commit 661a51e

File tree

3 files changed

+197
-28
lines changed

3 files changed

+197
-28
lines changed

src/ast_values.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ namespace Sass {
231231
std::string type() const override { return "number"; }
232232
static std::string type_name() { return "number"; }
233233

234+
bool hasAsSlash() {
235+
return !lhsAsSlash_.isNull()
236+
&& !rhsAsSlash_.isNull();
237+
}
238+
234239
// cancel out unnecessary units
235240
// result will be in input units
236241
void reduce();

src/fn_colors.cpp

Lines changed: 188 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "fn_colors.hpp"
99
#include "util.hpp"
1010
#include "util_string.hpp"
11+
#include "listize.hpp"
1112

1213
namespace Sass {
1314

@@ -38,32 +39,6 @@ namespace Sass {
3839

3940
}
4041

41-
Signature rgb_sig = "rgb($red, $green, $blue)";
42-
BUILT_IN(rgb)
43-
{
44-
if (
45-
isSpecialNumber(env["$red"]) ||
46-
isSpecialNumber(env["$green"]) ||
47-
isSpecialNumber(env["$blue"])
48-
) {
49-
return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
50-
+ env["$red"]->to_string()
51-
+ ", "
52-
+ env["$green"]->to_string()
53-
+ ", "
54-
+ env["$blue"]->to_string()
55-
+ ")"
56-
);
57-
}
58-
59-
return SASS_MEMORY_NEW(Color_RGBA,
60-
pstate,
61-
COLOR_NUM("$red"),
62-
COLOR_NUM("$green"),
63-
COLOR_NUM("$blue"));
64-
}
65-
66-
6742
/// Asserts that [number] is a percentage or has no units, and normalizes the
6843
/// value.
6944
///
@@ -101,6 +76,50 @@ namespace Sass {
10176
return nullptr;
10277
}
10378

79+
Number* assertNumber(AST_Node* node, std::string name, ParserState pstate, Backtraces traces)
80+
{
81+
if (node == nullptr) return nullptr;
82+
Number* nr = Cast<Number>(node);
83+
if (nr == nullptr) {
84+
error(name + ": " + node->to_css()
85+
+ " is not a number.", pstate, traces);
86+
}
87+
return nr;
88+
}
89+
90+
Value* _rgb(std::string name, std::vector<ExpressionObj> arguments, Signature sig, ParserState pstate, Backtraces traces)
91+
{
92+
AST_Node* _r = arguments[0];
93+
AST_Node* _g = arguments[1];
94+
AST_Node* _b = arguments[2];
95+
AST_Node* _a = nullptr;
96+
if (arguments.size() > 3) {
97+
_a = arguments[3];
98+
}
99+
// Check if any `calc()` or `var()` are passed
100+
if (isSpecialNumber(_r) || isSpecialNumber(_g) || isSpecialNumber(_b) || isSpecialNumber(_a)) {
101+
std::stringstream fncall;
102+
fncall << name << "(";
103+
fncall << _r->to_css() << ", ";
104+
fncall << _g->to_css() << ", ";
105+
fncall << _b->to_css() << ", ";
106+
fncall << (_a ? _a->to_css() : "1") << ")";
107+
return SASS_MEMORY_NEW(StringLiteral, pstate, fncall.str());
108+
}
109+
110+
Number* r = assertNumber(_r, "$red", pstate, traces);
111+
Number* g = assertNumber(_g, "$green", pstate, traces);
112+
Number* b = assertNumber(_b, "$blue", pstate, traces);
113+
Number* a = _a ? assertNumber(_a, "$alpha", pstate, traces) : nullptr;
114+
115+
return SASS_MEMORY_NEW(Color_RGBA, pstate,
116+
fuzzyRound(_percentageOrUnitless(r, 255, "red", traces)),
117+
fuzzyRound(_percentageOrUnitless(g, 255, "red", traces)),
118+
fuzzyRound(_percentageOrUnitless(b, 255, "red", traces)),
119+
a ? _percentageOrUnitless(a, 1.0, "alpha", traces) : 1.0);
120+
121+
}
122+
104123
Value* _rgb(std::string name, Env& env, Signature sig, ParserState pstate, Backtraces traces)
105124
{
106125
AST_Node* _r = getArg("$red", env);
@@ -208,11 +227,152 @@ namespace Sass {
208227
return _rgbTwoArg("rgba", env, rgba_2_sig, pstate, traces);
209228
}
210229

230+
/// Returns whether [value] is an unquoted string that start with `var(` and
231+
/// contains `/`.
232+
bool _isVarSlash(Expression* value) {
233+
if (String_Constant * string = Cast<String_Constant>(value)) {
234+
return starts_with(string->value(), "var(") &&
235+
string_constains(string->value(), '/');
236+
}
237+
if (StringLiteral * string = Cast<StringLiteral>(value)) {
238+
return starts_with(string->text(), "var(") &&
239+
string_constains(string->text(), '/');
240+
}
241+
return false;
242+
}
243+
244+
245+
Expression* _parseChannels(std::string name, std::vector<std::string> argumentNames, Value* channels, ParserState pstate, Backtraces traces)
246+
{
247+
if (isVar(channels)) {
248+
std::stringstream fncall;
249+
fncall << name << "("
250+
<< channels->to_css() << ")";
251+
return SASS_MEMORY_NEW(StringLiteral,
252+
pstate, fncall.str());
253+
}
254+
255+
// Expression* list = Listize::perform(channels);
256+
257+
if (List * list = Cast<List>(channels)) {
258+
bool isCommaSeparated = list->separator() == SASS_COMMA;
259+
bool isBracketed = list->is_bracketed();
260+
if (isCommaSeparated || isBracketed) {
261+
std::stringstream msg;
262+
msg << "$channels must be";
263+
if (isBracketed) msg << " an unbracketed";
264+
if (isCommaSeparated) {
265+
msg << (isBracketed ? "," : " a");
266+
msg << " space-separated";
267+
}
268+
msg << " list.";
269+
error(msg.str(), pstate, traces);
270+
}
271+
272+
if (list->length() > 3) {
273+
error(
274+
"Only 3 elements allowed, but ${list.length} were passed.",
275+
pstate, traces);
276+
}
277+
else if (list->length() < 3) {
278+
bool hasVar = false;
279+
for (Expression* item : list->elements()) {
280+
if (isVar(item)) {
281+
hasVar = true;
282+
break;
283+
}
284+
}
285+
if (hasVar || (!list->empty() && _isVarSlash(list->last()))) {
286+
std::stringstream fncall;
287+
fncall << name << "(";
288+
for (size_t i = 0, iL = list->length(); i < iL; i++) {
289+
if (i > 0) { fncall << ", "; }
290+
fncall << list->get(i)->to_css();
291+
}
292+
fncall << ")";
293+
return SASS_MEMORY_NEW(StringLiteral,
294+
pstate, fncall.str());
295+
}
296+
else {
297+
std::string argument = argumentNames[list->length()];
298+
error(
299+
"Missing element " + argument + ".",
300+
pstate, traces);
301+
}
302+
}
303+
304+
Number* secondNumber = Cast<Number>(list->get(2));
305+
String_Constant* secondString = Cast<String_Constant>(list->get(2));
306+
if (secondNumber && secondNumber->hasAsSlash()) {
307+
List* rv = SASS_MEMORY_NEW(List, pstate);
308+
rv->append(list->get(0));
309+
rv->append(list->get(1));
310+
rv->append(secondNumber->lhsAsSlash());
311+
rv->append(secondNumber->rhsAsSlash());
312+
return rv;
313+
}
314+
else if (secondString &&
315+
// !maybeSlashSeparated.hasQuotes &&
316+
string_constains(secondString->value(), '/')) {
317+
std::stringstream fncall;
318+
fncall << name << "(";
319+
for (size_t i = 0, iL = list->length(); i < iL; i++) {
320+
if (i > 0) { fncall << ", "; }
321+
fncall << list->get(i)->to_css();
322+
323+
}
324+
fncall << ")";
325+
return SASS_MEMORY_NEW(StringLiteral,
326+
pstate, fncall.str());
327+
}
328+
else {
329+
return list;
330+
}
331+
}
332+
else {
333+
error("$channels must be a space-separated list.",
334+
pstate, traces);
335+
}
336+
return nullptr;
337+
}
338+
211339
Signature rgba_1_sig = "rgba($channels)";
212340
BUILT_IN(rgba_1)
213341
{
214-
std::cerr << "qweqwe\n";
215-
return nullptr;
342+
Value* channels = ARG("$channels", Value, "a value");
343+
ExpressionObj parsed = _parseChannels("rgba",
344+
{ "$red", "$green", "$blue" }, channels, pstate, traces);
345+
if (StringLiteral * str = Cast<StringLiteral>(parsed)) {
346+
return str;
347+
}
348+
if (List * list = Cast<List>(parsed)) {
349+
return _rgb("rgb", list->elements(), rgba_1_sig, pstate, traces);
350+
}
351+
}
352+
353+
Signature rgb_sig = "rgb($red, $green, $blue)";
354+
BUILT_IN(rgb)
355+
{
356+
if (
357+
isSpecialNumber(env["$red"]) ||
358+
isSpecialNumber(env["$green"]) ||
359+
isSpecialNumber(env["$blue"])
360+
) {
361+
return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
362+
+ env["$red"]->to_string()
363+
+ ", "
364+
+ env["$green"]->to_string()
365+
+ ", "
366+
+ env["$blue"]->to_string()
367+
+ ")"
368+
);
369+
}
370+
371+
return SASS_MEMORY_NEW(Color_RGBA,
372+
pstate,
373+
COLOR_NUM("$red"),
374+
COLOR_NUM("$green"),
375+
COLOR_NUM("$blue"));
216376
}
217377

218378
////////////////

src/util.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ namespace Sass {
5555
return true;
5656
}
5757

58+
inline bool string_constains(const std::string& str, const char chr) {
59+
return str.find(chr) != std::string::npos;
60+
}
61+
5862
// C++20 `starts_with` equivalent.
5963
// See https://en.cppreference.com/w/cpp/string/basic_string/starts_with
6064
inline bool starts_with(const std::string& str, const char* prefix, size_t prefix_len) {

0 commit comments

Comments
 (0)