Skip to content

Commit 5a7dbc8

Browse files
authored
Implement 3.5 first class functions (#2373)
Fixes #2277 Spec sass/sass-spec#1108
1 parent cb9f28c commit 5a7dbc8

13 files changed

+148
-8
lines changed

src/ast.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,16 @@ namespace Sass {
23292329
return rhs.concrete_type() == NULL_VAL;
23302330
}
23312331

2332+
bool Function::operator== (const Expression& rhs) const
2333+
{
2334+
if (Function_Ptr_Const r = Cast<Function>(&rhs)) {
2335+
Definition_Ptr_Const d1 = Cast<Definition>(definition());
2336+
Definition_Ptr_Const d2 = Cast<Definition>(r->definition());
2337+
return d1 && d2 && d1 == d2 && is_css() == r->is_css();
2338+
}
2339+
return false;
2340+
}
2341+
23322342
size_t List::size() const {
23332343
if (!is_arglist_) return length();
23342344
// arglist expects a list of arguments
@@ -2464,6 +2474,7 @@ namespace Sass {
24642474
IMPLEMENT_AST_OPERATORS(Custom_Error);
24652475
IMPLEMENT_AST_OPERATORS(List);
24662476
IMPLEMENT_AST_OPERATORS(Map);
2477+
IMPLEMENT_AST_OPERATORS(Function);
24672478
IMPLEMENT_AST_OPERATORS(Number);
24682479
IMPLEMENT_AST_OPERATORS(Binary_Expression);
24692480
IMPLEMENT_AST_OPERATORS(String_Schema);
@@ -2514,5 +2525,4 @@ namespace Sass {
25142525
IMPLEMENT_AST_OPERATORS(Placeholder_Selector);
25152526
IMPLEMENT_AST_OPERATORS(Definition);
25162527
IMPLEMENT_AST_OPERATORS(Declaration);
2517-
25182528
}

src/ast.hpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ namespace Sass {
169169
MAP,
170170
SELECTOR,
171171
NULL_VAL,
172+
FUNCTION_VAL,
172173
C_WARNING,
173174
C_ERROR,
174175
FUNCTION,
@@ -1434,18 +1435,54 @@ namespace Sass {
14341435
ATTACH_OPERATIONS()
14351436
};
14361437

1438+
////////////////////////////////////////////////////
1439+
// Function reference.
1440+
////////////////////////////////////////////////////
1441+
class Function : public Value {
1442+
public:
1443+
ADD_PROPERTY(Definition_Obj, definition)
1444+
ADD_PROPERTY(bool, is_css)
1445+
public:
1446+
Function(ParserState pstate, Definition_Obj def, bool css)
1447+
: Value(pstate), definition_(def), is_css_(css)
1448+
{ concrete_type(FUNCTION_VAL); }
1449+
Function(const Function* ptr)
1450+
: Value(ptr), definition_(ptr->definition_), is_css_(ptr->is_css_)
1451+
{ concrete_type(FUNCTION_VAL); }
1452+
1453+
std::string type() const { return "function"; }
1454+
static std::string type_name() { return "function"; }
1455+
bool is_invisible() const { return true; }
1456+
1457+
std::string name() {
1458+
if (definition_) {
1459+
return definition_->name();
1460+
}
1461+
return "";
1462+
}
1463+
1464+
virtual bool operator== (const Expression& rhs) const;
1465+
1466+
ATTACH_AST_OPERATIONS(Function)
1467+
ATTACH_OPERATIONS()
1468+
};
1469+
14371470
//////////////////
14381471
// Function calls.
14391472
//////////////////
14401473
class Function_Call : public PreValue {
14411474
HASH_CONSTREF(std::string, name)
14421475
HASH_PROPERTY(Arguments_Obj, arguments)
1476+
HASH_PROPERTY(Function_Obj, func)
14431477
ADD_PROPERTY(bool, via_call)
14441478
ADD_PROPERTY(void*, cookie)
14451479
size_t hash_;
14461480
public:
14471481
Function_Call(ParserState pstate, std::string n, Arguments_Obj args, void* cookie)
1448-
: PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(cookie), hash_(0)
1482+
: PreValue(pstate), name_(n), arguments_(args), func_(0), via_call_(false), cookie_(cookie), hash_(0)
1483+
{ concrete_type(FUNCTION); }
1484+
Function_Call(ParserState pstate, std::string n, Arguments_Obj args, Function_Obj func)
1485+
: PreValue(pstate), name_(n), arguments_(args), func_(func), via_call_(false), cookie_(0), hash_(0)
14491486
{ concrete_type(FUNCTION); }
14501487
Function_Call(ParserState pstate, std::string n, Arguments_Obj args)
14511488
: PreValue(pstate), name_(n), arguments_(args), via_call_(false), cookie_(0), hash_(0)
@@ -1454,11 +1491,17 @@ namespace Sass {
14541491
: PreValue(ptr),
14551492
name_(ptr->name_),
14561493
arguments_(ptr->arguments_),
1494+
func_(ptr->func_),
14571495
via_call_(ptr->via_call_),
14581496
cookie_(ptr->cookie_),
14591497
hash_(ptr->hash_)
14601498
{ concrete_type(FUNCTION); }
14611499

1500+
bool is_css() {
1501+
if (func_) return func_->is_css();
1502+
return false;
1503+
}
1504+
14621505
virtual bool operator==(const Expression& rhs) const
14631506
{
14641507
try

src/ast_fwd_decl.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ namespace Sass {
132132
class Map;
133133
typedef Map* Map_Ptr;
134134
typedef Map const* Map_Ptr_Const;
135+
class Function;
136+
typedef Function* Function_Ptr;
137+
typedef Function const* Function_Ptr_Const;
135138

136139
class Mixin_Call;
137140
typedef Mixin_Call* Mixin_Call_Ptr;
@@ -311,6 +314,7 @@ namespace Sass {
311314
IMPL_MEM_OBJ(Expression);
312315
IMPL_MEM_OBJ(List);
313316
IMPL_MEM_OBJ(Map);
317+
IMPL_MEM_OBJ(Function);
314318
IMPL_MEM_OBJ(Binary_Expression);
315319
IMPL_MEM_OBJ(Unary_Expression);
316320
IMPL_MEM_OBJ(Function_Call);

src/context.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,7 @@ namespace Sass {
833833
register_function(ctx, feature_exists_sig, feature_exists, env);
834834
register_function(ctx, call_sig, call, env);
835835
register_function(ctx, content_exists_sig, content_exists, env);
836+
register_function(ctx, get_function_sig, get_function, env);
836837
// Boolean Functions
837838
register_function(ctx, not_sig, sass_not, env);
838839
register_function(ctx, if_sig, sass_if, env);

src/debugger.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,17 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
512512
std::cerr << " [" << expression->name() << "]";
513513
if (expression->is_delayed()) std::cerr << " [delayed]";
514514
if (expression->is_interpolant()) std::cerr << " [interpolant]";
515+
if (expression->is_css()) std::cerr << " [css]";
515516
std::cerr << std::endl;
516517
debug_ast(expression->arguments(), ind + " args: ", env);
518+
debug_ast(expression->func(), ind + " func: ", env);
519+
} else if (Cast<Function>(node)) {
520+
Function_Ptr expression = Cast<Function>(node);
521+
std::cerr << ind << "Function " << expression;
522+
std::cerr << " (" << pstate_source_position(node) << ")";
523+
if (expression->is_css()) std::cerr << " [css]";
524+
std::cerr << std::endl;
525+
debug_ast(expression->definition(), ind + " definition: ", env);
517526
} else if (Cast<Arguments>(node)) {
518527
Arguments_Ptr expression = Cast<Arguments>(node);
519528
std::cerr << ind << "Arguments " << expression;

src/eval.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,10 +838,10 @@ namespace Sass {
838838
if (op_type == Sass_OP::SUB) interpolant = false;
839839
// if (op_type == Sass_OP::DIV) interpolant = true;
840840
// check for type violations
841-
if (l_type == Expression::MAP) {
841+
if (l_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) {
842842
throw Exception::InvalidValue(*v_l);
843843
}
844-
if (r_type == Expression::MAP) {
844+
if (r_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) {
845845
throw Exception::InvalidValue(*v_r);
846846
}
847847
Value_Ptr ex = op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress
@@ -976,6 +976,8 @@ namespace Sass {
976976
}
977977
Definition_Ptr def = Cast<Definition>((*env)[full_name]);
978978

979+
if (c->func()) def = c->func()->definition();
980+
979981
if (def->is_overload_stub()) {
980982
std::stringstream ss;
981983
size_t L = args->length();
@@ -998,6 +1000,8 @@ namespace Sass {
9981000
Native_Function func = def->native_function();
9991001
Sass_Function_Entry c_function = def->c_function();
10001002

1003+
if (c->is_css()) return result.detach();
1004+
10011005
Parameters_Obj params = def->parameters();
10021006
Env fn_env(def->environment());
10031007
exp.env_stack.push_back(&fn_env);

src/functions.cpp

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,9 +1719,14 @@ namespace Sass {
17191719
Signature function_exists_sig = "function-exists($name)";
17201720
BUILT_IN(function_exists)
17211721
{
1722-
std::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
1722+
String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);
1723+
if (!ss) {
1724+
error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, backtrace);
1725+
}
1726+
1727+
std::string name = Util::normalize_underscores(unquote(ss->value()));
17231728

1724-
if(d_env.has_global(s+"[f]")) {
1729+
if(d_env.has_global(name+"[f]")) {
17251730
return SASS_MEMORY_NEW(Boolean, pstate, true);
17261731
}
17271732
else {
@@ -1758,7 +1763,20 @@ namespace Sass {
17581763
Signature call_sig = "call($name, $args...)";
17591764
BUILT_IN(call)
17601765
{
1761-
std::string name = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
1766+
std::string name;
1767+
Function_Ptr ff = Cast<Function>(env["$name"]);
1768+
String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);
1769+
1770+
if (ss) {
1771+
name = Util::normalize_underscores(unquote(ss->value()));
1772+
std::cerr << "DEPRECATION WARNING: ";
1773+
std::cerr << "Passing a string to call() is deprecated and will be illegal " << std::endl;
1774+
std::cerr << "in Sass 4.0. Use call(get-function(" + quote(name) + ")) instead. " << std::endl;
1775+
std::cerr << std::endl;
1776+
} else if (ff) {
1777+
name = ff->name();
1778+
}
1779+
17621780
List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));
17631781

17641782
Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);
@@ -1789,8 +1807,8 @@ namespace Sass {
17891807
Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args);
17901808
Expand expand(ctx, &d_env, backtrace, &selector_stack);
17911809
func->via_call(true); // calc invoke is allowed
1810+
if (ff) func->func(ff);
17921811
return func->perform(&expand.eval);
1793-
17941812
}
17951813

17961814
////////////////////
@@ -2100,5 +2118,36 @@ namespace Sass {
21002118
}
21012119
return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));
21022120
}
2121+
2122+
Signature get_function_sig = "get-function($name, $css: false)";
2123+
BUILT_IN(get_function)
2124+
{
2125+
String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);
2126+
if (!ss) {
2127+
error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, backtrace);
2128+
}
2129+
2130+
std::string name = Util::normalize_underscores(unquote(ss->value()));
2131+
std::string full_name = name + "[f]";
2132+
2133+
Boolean_Obj css = ARG("$css", Boolean);
2134+
if (!css->is_false()) {
2135+
Definition_Ptr def = SASS_MEMORY_NEW(Definition,
2136+
pstate,
2137+
name,
2138+
SASS_MEMORY_NEW(Parameters, pstate),
2139+
SASS_MEMORY_NEW(Block, pstate, 0, false),
2140+
Definition::FUNCTION);
2141+
return SASS_MEMORY_NEW(Function, pstate, def, true);
2142+
}
2143+
2144+
2145+
if (!d_env.has_global(full_name)) {
2146+
error("Function not found: " + name, pstate, backtrace);
2147+
}
2148+
2149+
Definition_Ptr def = Cast<Definition>(d_env[full_name]);
2150+
return SASS_MEMORY_NEW(Function, pstate, def, false);
2151+
}
21032152
}
21042153
}

src/functions.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ namespace Sass {
107107
extern Signature selector_parse_sig;
108108
extern Signature is_bracketed_sig;
109109
extern Signature content_exists_sig;
110+
extern Signature get_function_sig;
110111

111112
BUILT_IN(rgb);
112113
BUILT_IN(rgba_4);
@@ -190,6 +191,7 @@ namespace Sass {
190191
BUILT_IN(selector_parse);
191192
BUILT_IN(is_bracketed);
192193
BUILT_IN(content_exists);
194+
BUILT_IN(get_function);
193195
}
194196
}
195197

src/inspect.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,14 @@ namespace Sass {
829829
append_string(")");
830830
}
831831

832+
void Inspect::operator()(Function_Ptr f)
833+
{
834+
append_token("get-function", f);
835+
append_string("(");
836+
append_string(quote(f->name()));
837+
append_string(")");
838+
}
839+
832840
void Inspect::operator()(Null_Ptr n)
833841
{
834842
// output the final token

src/inspect.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace Sass {
4848
virtual void operator()(Content_Ptr);
4949
// expressions
5050
virtual void operator()(Map_Ptr);
51+
virtual void operator()(Function_Ptr);
5152
virtual void operator()(List_Ptr);
5253
virtual void operator()(Binary_Expression_Ptr);
5354
virtual void operator()(Unary_Expression_Ptr);

0 commit comments

Comments
 (0)