Commit e6ced9b7 by Aaron Leung

Working on pure-Sass function evaluator. Needs a different evaluation algorithm.

parent 94de55d7
...@@ -504,6 +504,12 @@ namespace Sass { ...@@ -504,6 +504,12 @@ namespace Sass {
else if (peek < while_directive >()) { else if (peek < while_directive >()) {
block << parse_while_directive(surrounding_ruleset, inside_of); block << parse_while_directive(surrounding_ruleset, inside_of);
} }
else if (lex < return_directive >()) {
Node ret_expr(context.new_Node(Node::return_directive, path, line, 1));
ret_expr << parse_list();
block << ret_expr;
semicolon = true;
}
else if (inside_of == Node::function) { else if (inside_of == Node::function) {
throw_syntax_error("only variable declarations and control directives are allowed inside functions"); throw_syntax_error("only variable declarations and control directives are allowed inside functions");
} }
......
...@@ -124,6 +124,8 @@ namespace Sass { ...@@ -124,6 +124,8 @@ namespace Sass {
val = eval(val, prefix, env, f_env, new_Node, ctx); val = eval(val, prefix, env, f_env, new_Node, ctx);
} }
Node var(expr[0]); Node var(expr[0]);
// If a binding exists (possible upframe), then update it.
// Otherwise, make a new on in the current frame.
if (env.query(var.token())) { if (env.query(var.token())) {
env[var.token()] = val; env[var.token()] = val;
} }
...@@ -496,6 +498,7 @@ namespace Sass { ...@@ -496,6 +498,7 @@ namespace Sass {
Node params(mixin[1]); Node params(mixin[1]);
Node body(new_Node(mixin[2])); // clone the body Node body(new_Node(mixin[2])); // clone the body
Environment bindings; Environment bindings;
// TO DO: REFACTOR THE ARG-BINDER
// bind arguments // bind arguments
for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) { for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
if (args[i].type() == Node::assignment) { if (args[i].type() == Node::assignment) {
...@@ -539,11 +542,14 @@ namespace Sass { ...@@ -539,11 +542,14 @@ namespace Sass {
} }
} }
} }
// END ARG-BINDER
// link the new environment and eval the mixin's body // link the new environment and eval the mixin's body
if (dynamic_scope) { if (dynamic_scope) {
bindings.link(env); bindings.link(env);
} }
else { else {
// C-style scope for now (current/global, nothing in between). May need
// to implement full lexical scope someday.
bindings.link(env.global ? *env.global : env); bindings.link(env.global ? *env.global : env);
} }
for (size_t i = 0, S = body.size(); i < S; ++i) { for (size_t i = 0, S = body.size(); i < S; ++i) {
...@@ -557,6 +563,7 @@ namespace Sass { ...@@ -557,6 +563,7 @@ namespace Sass {
Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx) Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx)
{ {
if (f.primitive) {
map<Token, Node> bindings; map<Token, Node> bindings;
// bind arguments // bind arguments
for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) { for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
...@@ -573,6 +580,121 @@ namespace Sass { ...@@ -573,6 +580,121 @@ namespace Sass {
} }
return f(bindings, new_Node); return f(bindings, new_Node);
} }
else {
cerr << "applying pure-Sass function" << endl;
Node params(f.definition[1]);
Node body(new_Node(f.definition[2]));
Environment bindings;
// TO DO: REFACTOR THE ARG-BINDER
// bind arguments
for (size_t i = 0, j = 0, S = args.size(); i < S; ++i) {
if (args[i].type() == Node::assignment) {
Node arg(args[i]);
Token name(arg[0].token());
// check that the keyword arg actually names a formal parameter
bool valid_param = false;
for (size_t k = 0, S = params.size(); k < S; ++k) {
Node param_k = params[k];
if (param_k.type() == Node::assignment) param_k = param_k[0];
if (arg[0] == param_k) {
valid_param = true;
break;
}
}
if (!valid_param) throw_eval_error("mixin " + f.name + " has no parameter named " + name.to_string(), arg.path(), arg.line());
if (!bindings.query(name)) {
bindings[name] = eval(arg[1], prefix, env, f_env, new_Node, ctx);
}
}
else {
// ensure that the number of ordinal args < params.size()
if (j >= params.size()) {
stringstream ss;
ss << "mixin " << f.name << " only takes " << params.size() << ((params.size() == 1) ? " argument" : " arguments");
throw_eval_error(ss.str(), args[i].path(), args[i].line());
}
Node param(params[j]);
Token name(param.type() == Node::variable ? param.token() : param[0].token());
bindings[name] = eval(args[i], prefix, env, f_env, new_Node, ctx);
++j;
}
}
// plug the holes with default arguments if any
for (size_t i = 0, S = params.size(); i < S; ++i) {
if (params[i].type() == Node::assignment) {
Node param(params[i]);
Token name(param[0].token());
if (!bindings.query(name)) {
bindings[name] = eval(param[1], prefix, env, f_env, new_Node, ctx);
}
}
}
// END ARG-BINDER
bindings.link(env.global ? *env.global : env);
return function_eval(body, bindings, new_Node, ctx);
}
}
// Special function for evaluating pure Sass functions. The evaluation
// algorithm is different in this case because the body needs to be
// executed and a single value needs to be returned directly, rather than
// styles being expanded and spliced in place.
Node function_eval(Node body, Environment& bindings, Node_Factory& new_Node, Context& ctx)
{
for (size_t i = 0, S = body.size(); i < S; ++i) {
Node stm(body[i]);
switch (stm.type())
{
case Node::assignment: {
Node val(stm[1]);
if (val.type() == Node::comma_list || val.type() == Node::space_list) {
for (size_t i = 0, S = val.size(); i < S; ++i) {
if (val[i].should_eval()) val[i] = eval(val[i], Node(), bindings, ctx.function_env, new_Node, ctx);
}
}
else {
val = eval(val, Node(), bindings, ctx.function_env, new_Node, ctx);
}
Node var(stm[0]);
// If a binding exists (possible upframe), then update it.
// Otherwise, make a new on in the current frame.
if (bindings.query(var.token())) {
bindings[var.token()] = val;
}
else {
bindings.current_frame[var.token()] = val;
}
} break;
case Node::if_directive: {
} break;
case Node::for_through_directive:
case Node::for_to_directive: {
} break;
case Node::each_directive: {
} break;
case Node::while_directive: {
} break;
case Node::return_directive: {
} break;
default: {
} break;
}
}
return new_Node(Node::none, "", 0, 0);
}
// Expand a selector with respect to its prefix/context. Two separate cases: // Expand a selector with respect to its prefix/context. Two separate cases:
// when the selector has backrefs, substitute the prefix for each occurrence // when the selector has backrefs, substitute the prefix for each occurrence
......
...@@ -11,12 +11,13 @@ ...@@ -11,12 +11,13 @@
namespace Sass { namespace Sass {
using std::map; using std::map;
Node eval(Node expr, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& src_refs); Node eval(Node expr, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx);
Node function_eval(Node stm, Environment& bindings, Node_Factory& new_Node, Context& ctx);
Node accumulate(Node::Type op, Node acc, Node rhs, Node_Factory& new_Node); Node accumulate(Node::Type op, Node acc, Node rhs, Node_Factory& new_Node);
double operate(Node::Type op, double lhs, double rhs); double operate(Node::Type op, double lhs, double rhs);
Node apply_mixin(Node mixin, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& src_refs, bool dynamic_scope = false); Node apply_mixin(Node mixin, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx, bool dynamic_scope = false);
Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& src_refs); Node apply_function(const Function& f, const Node args, Node prefix, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx);
Node expand_selector(Node sel, Node pre, Node_Factory& new_Node); Node expand_selector(Node sel, Node pre, Node_Factory& new_Node);
Node expand_backref(Node sel, Node pre); Node expand_backref(Node sel, Node pre);
void extend_selectors(vector<pair<Node, Node> >&, Node_Factory&); void extend_selectors(vector<pair<Node, Node> >&, Node_Factory&);
......
...@@ -153,6 +153,8 @@ namespace Sass { ...@@ -153,6 +153,8 @@ namespace Sass {
for_to_directive, for_to_directive,
each_directive, each_directive,
while_directive, while_directive,
return_directive,
content_directive,
variable, variable,
assignment assignment
......
...@@ -112,7 +112,7 @@ namespace Sass { ...@@ -112,7 +112,7 @@ namespace Sass {
return exactly<function_kwd>(src); return exactly<function_kwd>(src);
} }
extern const char return_kwd[] = "@return"; extern const char return_kwd[] = "@return";
const char* ret(const char* src) { const char* return_directive(const char* src) {
return exactly<return_kwd>(src); return exactly<return_kwd>(src);
} }
extern const char include_kwd[] = "@include"; extern const char include_kwd[] = "@include";
......
...@@ -310,7 +310,7 @@ namespace Sass { ...@@ -310,7 +310,7 @@ namespace Sass {
const char* import(const char* src); const char* import(const char* src);
const char* mixin(const char* src); const char* mixin(const char* src);
const char* function(const char* src); const char* function(const char* src);
const char* ret(const char* src); const char* return_directive(const char* src);
const char* include(const char* src); const char* include(const char* src);
const char* extend(const char* src); const char* extend(const char* src);
...@@ -409,7 +409,6 @@ namespace Sass { ...@@ -409,7 +409,6 @@ namespace Sass {
if (mx(beg)) return beg; if (mx(beg)) return beg;
++beg; ++beg;
} }
return 0; return 0;
} }
template <char c> template <char c>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment