Commit b7df7897 by Aaron Leung

General framework for built-in Sass functions.

parent c60e12c2
#define SASS_CONTEXT_INCLUDED #define SASS_CONTEXT_INCLUDED
#include "functions.hpp"
namespace Sass { namespace Sass {
using std::map; using std::map;
...@@ -35,6 +37,7 @@ namespace Sass { ...@@ -35,6 +37,7 @@ namespace Sass {
struct Context { struct Context {
Environment global_env; Environment global_env;
map<string, Function> function_env;
vector<char*> source_refs; vector<char*> source_refs;
size_t ref_count; size_t ref_count;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
namespace Sass { namespace Sass {
using std::cerr; using std::endl; using std::cerr; using std::endl;
Node eval(Node& expr, Environment& env) Node eval(Node& expr, Environment& env, map<string, Function>& f_env)
{ {
switch (expr.type) switch (expr.type)
{ {
...@@ -16,28 +16,23 @@ namespace Sass { ...@@ -16,28 +16,23 @@ namespace Sass {
case Node::expansion: { case Node::expansion: {
Token name(expr[0].content.token); Token name(expr[0].content.token);
// cerr << "EVALUATING EXPANSION: " << string(name) << endl;
Node args(expr[1]); Node args(expr[1]);
Node mixin(env[name]); Node mixin(env[name]);
Node expansion(apply(mixin, args, env)); Node expansion(apply_mixin(mixin, args, env, f_env));
expr.content.children->pop_back(); expr.content.children->pop_back();
expr.content.children->pop_back(); expr.content.children->pop_back();
expr += expansion; expr += expansion;
// expr[0].has_rules_or_comments |= expansion[0].has_rules_or_comments;
// expr[0].has_rulesets |= expansion[0].has_rulesets;
// expr[0].has_propsets |= expansion[0].has_propsets;
// expr[0].has_expansions |= expansion[0].has_expansions;
return expr; return expr;
} break; } break;
case Node::ruleset: { case Node::ruleset: {
eval(expr[1], env); eval(expr[1], env, f_env);
return expr; return expr;
} break; } break;
case Node::root: { case Node::root: {
for (int i = 0; i < expr.size(); ++i) { for (int i = 0; i < expr.size(); ++i) {
eval(expr[i], env); eval(expr[i], env, f_env);
} }
return expr; return expr;
} break; } break;
...@@ -46,7 +41,7 @@ namespace Sass { ...@@ -46,7 +41,7 @@ namespace Sass {
Environment current; Environment current;
current.link(env); current.link(env);
for (int i = 0; i < expr.size(); ++i) { for (int i = 0; i < expr.size(); ++i) {
eval(expr[i], current); eval(expr[i], current, f_env);
} }
return expr; return expr;
} break; } break;
...@@ -55,11 +50,11 @@ namespace Sass { ...@@ -55,11 +50,11 @@ namespace Sass {
Node val(expr[1]); Node val(expr[1]);
if (val.type == Node::comma_list || val.type == Node::space_list) { if (val.type == Node::comma_list || val.type == Node::space_list) {
for (int i = 0; i < val.size(); ++i) { for (int i = 0; i < val.size(); ++i) {
if (val[i].eval_me) val[i] = eval(val[i], env); if (val[i].eval_me) val[i] = eval(val[i], env, f_env);
} }
} }
else { else {
val = eval(val, env); val = eval(val, env, f_env);
} }
Node var(expr[0]); Node var(expr[0]);
if (env.query(var.content.token)) { if (env.query(var.content.token)) {
...@@ -75,31 +70,28 @@ namespace Sass { ...@@ -75,31 +70,28 @@ namespace Sass {
Node rhs(expr[1]); Node rhs(expr[1]);
if (rhs.type == Node::comma_list || rhs.type == Node::space_list) { if (rhs.type == Node::comma_list || rhs.type == Node::space_list) {
for (int i = 0; i < rhs.size(); ++i) { for (int i = 0; i < rhs.size(); ++i) {
if (rhs[i].eval_me) rhs[i] = eval(rhs[i], env); if (rhs[i].eval_me) rhs[i] = eval(rhs[i], env, f_env);
} }
} }
else { else {
if (rhs.eval_me) expr[1] = eval(rhs, env); if (rhs.eval_me) expr[1] = eval(rhs, env, f_env);
} }
return expr; return expr;
} break; } break;
case Node::comma_list: case Node::comma_list:
case Node::space_list: { case Node::space_list: {
if (expr.eval_me) { if (expr.eval_me) expr[0] = eval(expr[0], env, f_env);
// *(expr.children->begin()) = eval(expr[0], env);
expr[0] = eval(expr[0], env);
}
return expr; return expr;
} break; } break;
case Node::expression: { case Node::expression: {
Node acc(Node::expression, expr.line_number, 1); Node acc(Node::expression, expr.line_number, 1);
acc << eval(expr[0], env); acc << eval(expr[0], env, f_env);
Node rhs(eval(expr[2], env)); Node rhs(eval(expr[2], env, f_env));
accumulate(expr[1].type, acc, rhs); accumulate(expr[1].type, acc, rhs);
for (int i = 3; i < expr.size(); i += 2) { for (int i = 3; i < expr.size(); i += 2) {
Node rhs(eval(expr[i+1], env)); Node rhs(eval(expr[i+1], env, f_env));
accumulate(expr[i].type, acc, rhs); accumulate(expr[i].type, acc, rhs);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
...@@ -108,11 +100,11 @@ namespace Sass { ...@@ -108,11 +100,11 @@ namespace Sass {
case Node::term: { case Node::term: {
if (expr.eval_me) { if (expr.eval_me) {
Node acc(Node::expression, expr.line_number, 1); Node acc(Node::expression, expr.line_number, 1);
acc << eval(expr[0], env); acc << eval(expr[0], env, f_env);
Node rhs(eval(expr[2], env)); Node rhs(eval(expr[2], env, f_env));
accumulate(expr[1].type, acc, rhs); accumulate(expr[1].type, acc, rhs);
for (int i = 3; i < expr.size(); i += 2) { for (int i = 3; i < expr.size(); i += 2) {
Node rhs(eval(expr[i+1], env)); Node rhs(eval(expr[i+1], env, f_env));
accumulate(expr[i].type, acc, rhs); accumulate(expr[i].type, acc, rhs);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
...@@ -139,7 +131,6 @@ namespace Sass { ...@@ -139,7 +131,6 @@ namespace Sass {
Token hext(Token::make(expr.content.token.begin+1, expr.content.token.end)); Token hext(Token::make(expr.content.token.begin+1, expr.content.token.end));
if (hext.length() == 6) { if (hext.length() == 6) {
for (int i = 0; i < 6; i += 2) { for (int i = 0; i < 6; i += 2) {
// Node thing(expr.line_number, static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
triple << Node(expr.line_number, static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16))); triple << Node(expr.line_number, static_cast<double>(std::strtol(string(hext.begin+i, 2).c_str(), NULL, 16)));
} }
} }
...@@ -155,6 +146,11 @@ namespace Sass { ...@@ -155,6 +146,11 @@ namespace Sass {
return env[expr.content.token]; return env[expr.content.token];
} break; } break;
case Node::function_call: {
// TO DO: default-constructed Function should be a generic callback
return apply_function(f_env[expr[0].content.token.to_string()], expr[1], env, f_env);
} break;
default: { default: {
return expr; return expr;
} }
...@@ -251,46 +247,66 @@ namespace Sass { ...@@ -251,46 +247,66 @@ namespace Sass {
} }
} }
Node apply(Node& mixin, const Node& args, Environment& env) Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<string, Function>& f_env)
{ {
// cerr << "APPLYING MIXIN: " << string(mixin[0].token) << endl;
Node params(mixin[1]); Node params(mixin[1]);
Node body(mixin[2].clone()); Node body(mixin[2].clone());
Environment m_env; Environment bindings;
// bind arguments // bind arguments
for (int i = 0, j = 0; i < args.size(); ++i) { for (int i = 0, j = 0; i < args.size(); ++i) {
if (args[i].type == Node::assignment) { if (args[i].type == Node::assignment) {
Node arg(args[i]); Node arg(args[i]);
Token name(arg[0].content.token); Token name(arg[0].content.token);
if (!m_env.query(name)) { if (!bindings.query(name)) {
m_env[name] = eval(arg[1], env); bindings[name] = eval(arg[1], env, f_env);
} }
} }
else { else {
// TO DO: ensure (j < params.size()) // TO DO: ensure (j < params.size())
Node param(params[j]); Node param(params[j]);
Token name(param.type == Node::variable ? param.content.token : param[0].content.token); Token name(param.type == Node::variable ? param.content.token : param[0].content.token);
m_env[name] = eval(args[i], env); bindings[name] = eval(args[i], env, f_env);
++j; ++j;
} }
} }
// cerr << "BOUND ARGS FOR " << string(mixin[0].token) << endl;
// plug the holes with default arguments if any // plug the holes with default arguments if any
for (int i = 0; i < params.size(); ++i) { for (int i = 0; i < params.size(); ++i) {
if (params[i].type == Node::assignment) { if (params[i].type == Node::assignment) {
Node param(params[i]); Node param(params[i]);
Token name(param[0].content.token); Token name(param[0].content.token);
if (!m_env.query(name)) { if (!bindings.query(name)) {
m_env[name] = eval(param[1], env); bindings[name] = eval(param[1], env, f_env);
} }
} }
} }
// cerr << "BOUND DEFAULT ARGS FOR " << string(mixin[0].token) << endl; // lexically link the new environment and eval the mixin's body
m_env.link(env.global ? *env.global : env); bindings.link(env.global ? *env.global : env);
// cerr << "LINKED ENVIRONMENT FOR " << string(mixin[0].token) << endl << endl;
for (int i = 0; i < body.size(); ++i) { for (int i = 0; i < body.size(); ++i) {
body[i] = eval(body[i], m_env); body[i] = eval(body[i], bindings, f_env);
} }
return body; return body;
} }
Node apply_function(const Function& f, const Node& args, Environment& env, map<string, Function>& f_env)
{
Node params(f.parameters);
Environment bindings;
// bind arguments
for (int i = 0, j = 0; i < args.size(); ++i) {
if (args[i].type == Node::assignment) {
Node arg(args[i]);
Token name(arg[0].content.token);
bindings[name] = eval(arg[1], env, f_env);
}
else {
// TO DO: ensure (j < params.size())
Node param(params[j]);
Token name(param.type == Node::variable ? param.content.token : param[0].content.token);
bindings[name] = eval(args[i], env, f_env);
++j;
}
}
return f(bindings);
}
} }
\ No newline at end of file
...@@ -11,9 +11,10 @@ ...@@ -11,9 +11,10 @@
namespace Sass { namespace Sass {
using std::map; using std::map;
Node eval(Node& expr, Environment& env); Node eval(Node& expr, Environment& env, map<string, Function>& f_env);
Node accumulate(Node::Type op, Node& acc, Node& rhs); Node accumulate(Node::Type op, Node& acc, Node& rhs);
double operate(Node::Type op, double lhs, double rhs); double operate(Node::Type op, double lhs, double rhs);
Node apply(Node& mixin, const Node& args, Environment& env); Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<string, Function>& f_env);
Node apply_function(const Function& f, const Node& args, Environment& env, map<string, Function>& f_env);
} }
\ No newline at end of file
#include "functions.hpp"
#include "node.hpp"
namespace Sass {
Node rgb(const Environment& bindings) {
}
\ No newline at end of file
namespace Sass {
struct Environment;
struct Function {
typedef Node (*Primitive)(const Node&, const Environment&);
string name;
Node parameters;
Primitive primitive;
Function()
{ }
Function(string name, Node parameters, Primitive primitive)
: name(name), parameters(parameters), primitive(primitive)
{ }
Node operator()(const Environment& bindings) const
{ return primitive(parameters, bindings); }
};
}
...@@ -29,12 +29,14 @@ int main(int argc, char* argv[]) { ...@@ -29,12 +29,14 @@ int main(int argc, char* argv[]) {
} }
Document doc(path, 0); Document doc(path, 0);
cerr << "PREPARING TO PARSE DOCUMENT" << endl; cerr << "INITIALIZED DOCUMENT OBJECT" << endl;
doc.parse_scss(); doc.parse_scss();
cerr << "SUCCESSFULLY PARSED DOCUMENT" << endl; cerr << "PARSED DOCUMENT" << endl;
// doc.eval_pending();
eval(doc.root, doc.context.global_env); eval(doc.root, doc.context.global_env, doc.context.function_env);
cerr << "SUCCESSFULLY EVALED DOCUMENT" << endl; cerr << "EVALUATED DOCUMENT" << endl;
string output = doc.emit_css(style); string output = doc.emit_css(style);
// cerr << "Fresh nodes:\t" << Node::fresh << endl; // cerr << "Fresh nodes:\t" << Node::fresh << endl;
......
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