Skip to content

Commit

Permalink
Implement full rgb overloads
Browse files Browse the repository at this point in the history
  • Loading branch information
mgreter committed Jul 20, 2019
1 parent 661a51e commit abebb51
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 134 deletions.
6 changes: 5 additions & 1 deletion src/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,11 @@ namespace Sass {
{
using namespace Functions;
// RGB Functions
register_function(ctx, rgb_sig, rgb, env);
register_overload_stub(ctx, "rgb", env, 3);
register_function(ctx, rgb_4_sig, rgb_4, 4, env);
register_function(ctx, rgb_3_sig, rgb_3, 3, env);
register_function(ctx, rgb_2_sig, rgb_2, 2, env);
register_function(ctx, rgb_1_sig, rgb_1, 1, env);

register_overload_stub(ctx, "rgba", env, 4);
register_function(ctx, rgba_4_sig, rgba_4, 4, env);
Expand Down
271 changes: 140 additions & 131 deletions src/fn_colors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,116 @@ namespace Sass {
return value;
}


/// Returns whether [value] is an unquoted string that start with `var(` and
/// contains `/`.
bool _isVarSlash(Expression* value) {
if (String_Constant * string = Cast<String_Constant>(value)) {
return starts_with(string->value(), "var(") &&
string_constains(string->value(), '/');
}
if (StringLiteral * string = Cast<StringLiteral>(value)) {
return starts_with(string->text(), "var(") &&
string_constains(string->text(), '/');
}
return false;
}


Expression* _parseChannels(std::string name, std::vector<std::string> argumentNames, Value* channels, ParserState pstate, Backtraces traces)
{
if (isVar(channels)) {
std::stringstream fncall;
fncall << name << "("
<< channels->to_css() << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}

// Expression* list = Listize::perform(channels);

if (List * list = Cast<List>(channels)) {
bool isCommaSeparated = list->separator() == SASS_COMMA;
bool isBracketed = list->is_bracketed();
if (isCommaSeparated || isBracketed) {
std::stringstream msg;
msg << "$channels must be";
if (isBracketed) msg << " an unbracketed";
if (isCommaSeparated) {
msg << (isBracketed ? "," : " a");
msg << " space-separated";
}
msg << " list.";
error(msg.str(), pstate, traces);
}

if (list->length() > 3) {
error(
"Only 3 elements allowed, but ${list.length} were passed.",
pstate, traces);
}
else if (list->length() < 3) {
bool hasVar = false;
for (Expression* item : list->elements()) {
if (isVar(item)) {
hasVar = true;
break;
}
}
if (hasVar || (!list->empty() && _isVarSlash(list->last()))) {
std::stringstream fncall;
fncall << name << "(";
for (size_t i = 0, iL = list->length(); i < iL; i++) {
if (i > 0) { fncall << ", "; }
fncall << list->get(i)->to_css();
}
fncall << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}
else {
std::string argument = argumentNames[list->length()];
error(
"Missing element " + argument + ".",
pstate, traces);
}
}

Number* secondNumber = Cast<Number>(list->get(2));
String_Constant* secondString = Cast<String_Constant>(list->get(2));
if (secondNumber && secondNumber->hasAsSlash()) {
List* rv = SASS_MEMORY_NEW(List, pstate);
rv->append(list->get(0));
rv->append(list->get(1));
rv->append(secondNumber->lhsAsSlash());
rv->append(secondNumber->rhsAsSlash());
return rv;
}
else if (secondString &&
// !maybeSlashSeparated.hasQuotes &&
string_constains(secondString->value(), '/')) {
std::stringstream fncall;
fncall << name << "(";
for (size_t i = 0, iL = list->length(); i < iL; i++) {
if (i > 0) { fncall << ", "; }
fncall << list->get(i)->to_css();

}
fncall << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}
else {
return list;
}
}
else {
error("$channels must be a space-separated list.",
pstate, traces);
}
return nullptr;
}

AST_Node* getArg(std::string name, Env& env)
{
if (env.has_local(name)) {
Expand Down Expand Up @@ -133,7 +243,7 @@ namespace Sass {
fncall << _r->to_css() << ", ";
fncall << _g->to_css() << ", ";
fncall << _b->to_css() << ", ";
fncall << _a->to_css() << ")";
fncall << (_a ? _a->to_css() : "1") << ")";
return SASS_MEMORY_NEW(StringLiteral, pstate, fncall.str());
}

Expand Down Expand Up @@ -227,154 +337,53 @@ namespace Sass {
return _rgbTwoArg("rgba", env, rgba_2_sig, pstate, traces);
}

/// Returns whether [value] is an unquoted string that start with `var(` and
/// contains `/`.
bool _isVarSlash(Expression* value) {
if (String_Constant * string = Cast<String_Constant>(value)) {
return starts_with(string->value(), "var(") &&
string_constains(string->value(), '/');
Signature rgba_1_sig = "rgba($channels)";
BUILT_IN(rgba_1)
{
Value* channels = ARG("$channels", Value, "a value");
ExpressionObj parsed = _parseChannels("rgba",
{ "$red", "$green", "$blue" }, channels, pstate, traces);
if (StringLiteral * str = Cast<StringLiteral>(parsed)) {
return str;
}
if (StringLiteral * string = Cast<StringLiteral>(value)) {
return starts_with(string->text(), "var(") &&
string_constains(string->text(), '/');
if (List * list = Cast<List>(parsed)) {
return _rgb("rgba", list->elements(), rgba_1_sig, pstate, traces);
}
return false;
return nullptr;
}


Expression* _parseChannels(std::string name, std::vector<std::string> argumentNames, Value* channels, ParserState pstate, Backtraces traces)
Signature rgb_4_sig = "rgb($red, $green, $blue, $alpha)";
BUILT_IN(rgb_4)
{
if (isVar(channels)) {
std::stringstream fncall;
fncall << name << "("
<< channels->to_css() << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}

// Expression* list = Listize::perform(channels);

if (List * list = Cast<List>(channels)) {
bool isCommaSeparated = list->separator() == SASS_COMMA;
bool isBracketed = list->is_bracketed();
if (isCommaSeparated || isBracketed) {
std::stringstream msg;
msg << "$channels must be";
if (isBracketed) msg << " an unbracketed";
if (isCommaSeparated) {
msg << (isBracketed ? "," : " a");
msg << " space-separated";
}
msg << " list.";
error(msg.str(), pstate, traces);
}

if (list->length() > 3) {
error(
"Only 3 elements allowed, but ${list.length} were passed.",
pstate, traces);
}
else if (list->length() < 3) {
bool hasVar = false;
for (Expression* item : list->elements()) {
if (isVar(item)) {
hasVar = true;
break;
}
}
if (hasVar || (!list->empty() && _isVarSlash(list->last()))) {
std::stringstream fncall;
fncall << name << "(";
for (size_t i = 0, iL = list->length(); i < iL; i++) {
if (i > 0) { fncall << ", "; }
fncall << list->get(i)->to_css();
}
fncall << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}
else {
std::string argument = argumentNames[list->length()];
error(
"Missing element " + argument + ".",
pstate, traces);
}
}
return _rgb("rgb", env, rgb_4_sig, pstate, traces);
}

Number* secondNumber = Cast<Number>(list->get(2));
String_Constant* secondString = Cast<String_Constant>(list->get(2));
if (secondNumber && secondNumber->hasAsSlash()) {
List* rv = SASS_MEMORY_NEW(List, pstate);
rv->append(list->get(0));
rv->append(list->get(1));
rv->append(secondNumber->lhsAsSlash());
rv->append(secondNumber->rhsAsSlash());
return rv;
}
else if (secondString &&
// !maybeSlashSeparated.hasQuotes &&
string_constains(secondString->value(), '/')) {
std::stringstream fncall;
fncall << name << "(";
for (size_t i = 0, iL = list->length(); i < iL; i++) {
if (i > 0) { fncall << ", "; }
fncall << list->get(i)->to_css();
Signature rgb_3_sig = "rgb($red, $green, $blue)";
BUILT_IN(rgb_3)
{
return _rgb("rgb", env, rgb_3_sig, pstate, traces);
}

}
fncall << ")";
return SASS_MEMORY_NEW(StringLiteral,
pstate, fncall.str());
}
else {
return list;
}
}
else {
error("$channels must be a space-separated list.",
pstate, traces);
}
return nullptr;
Signature rgb_2_sig = "rgb($color, $alpha)";
BUILT_IN(rgb_2)
{
return _rgbTwoArg("rgb", env, rgb_2_sig, pstate, traces);
}

Signature rgba_1_sig = "rgba($channels)";
BUILT_IN(rgba_1)
Signature rgb_1_sig = "rgb($channels)";
BUILT_IN(rgb_1)
{
Value* channels = ARG("$channels", Value, "a value");
ExpressionObj parsed = _parseChannels("rgba",
ExpressionObj parsed = _parseChannels("rgb",
{ "$red", "$green", "$blue" }, channels, pstate, traces);
if (StringLiteral * str = Cast<StringLiteral>(parsed)) {
return str;
}
if (List * list = Cast<List>(parsed)) {
return _rgb("rgb", list->elements(), rgba_1_sig, pstate, traces);
}
}

Signature rgb_sig = "rgb($red, $green, $blue)";
BUILT_IN(rgb)
{
if (
isSpecialNumber(env["$red"]) ||
isSpecialNumber(env["$green"]) ||
isSpecialNumber(env["$blue"])
) {
return SASS_MEMORY_NEW(String_Constant, pstate, "rgb("
+ env["$red"]->to_string()
+ ", "
+ env["$green"]->to_string()
+ ", "
+ env["$blue"]->to_string()
+ ")"
);
return _rgb("rgb", list->elements(), rgb_1_sig, pstate, traces);
}

return SASS_MEMORY_NEW(Color_RGBA,
pstate,
COLOR_NUM("$red"),
COLOR_NUM("$green"),
COLOR_NUM("$blue"));
return nullptr;
}

////////////////
// RGB FUNCTIONS
////////////////
Expand Down
10 changes: 8 additions & 2 deletions src/fn_colors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ namespace Sass {
#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 rgb_4_sig;
extern Signature rgb_3_sig;
extern Signature rgb_2_sig;
extern Signature rgb_1_sig;
extern Signature rgba_4_sig;
extern Signature rgba_3_sig;
extern Signature rgba_2_sig;
Expand Down Expand Up @@ -55,7 +58,10 @@ namespace Sass {
extern Signature change_color_sig;
extern Signature ie_hex_str_sig;

BUILT_IN(rgb);
BUILT_IN(rgb_1);
BUILT_IN(rgb_2);
BUILT_IN(rgb_3);
BUILT_IN(rgb_4);
BUILT_IN(rgba_4);
BUILT_IN(rgba_3);
BUILT_IN(rgba_2);
Expand Down

0 comments on commit abebb51

Please sign in to comment.