Commit df9b4e6a by Aaron Leung

Selector interpolation works robustly (sans a few special cases).

parent bf146431
...@@ -60,17 +60,18 @@ namespace Sass { ...@@ -60,17 +60,18 @@ namespace Sass {
return doc; return doc;
} }
Document Document::make_from_source_chars(Context& ctx, char* src, string path) Document Document::make_from_source_chars(Context& ctx, char* src, string path, bool own_source)
{ {
Document doc(ctx); Document doc(ctx);
doc.path = path; doc.path = path;
doc.line = 1; doc.line = 1;
doc.root = ctx.new_Node(Node::root, path, 1, 0); doc.root = ctx.new_Node(Node::root, path, 1, 0);
doc.lexed = Token::make(); doc.lexed = Token::make();
doc.own_source = false; doc.own_source = own_source;
doc.source = src; doc.source = src;
doc.end = src + std::strlen(src); doc.end = src + std::strlen(src);
doc.position = doc.end; doc.position = src;
if (own_source) doc.context.source_refs.push_back(src);
return doc; return doc;
} }
......
#include <map> #include <map>
#ifndef SASS_PRELEXER_INCLUDED
#include "prelexer.hpp"
#endif
#ifndef SASS_NODE_INCLUDED #ifndef SASS_NODE_INCLUDED
#include "node.hpp" #include "node.hpp"
#endif #endif
#include "prelexer.hpp" #ifndef SASS_CONTEXT_INCLUDED
#include "context.hpp" #include "context.hpp"
#endif
struct Selector_Lookahead { struct Selector_Lookahead {
const char* found; const char* found;
...@@ -41,7 +46,7 @@ namespace Sass { ...@@ -41,7 +46,7 @@ namespace Sass {
~Document(); ~Document();
static Document make_from_file(Context& ctx, string path); static Document make_from_file(Context& ctx, string path);
static Document make_from_source_chars(Context& ctx, char* src, string path = ""); static Document make_from_source_chars(Context& ctx, char* src, string path = "", bool own_source = false);
static Document make_from_token(Context& ctx, Token t, string path = "", size_t line_number = 1); static Document make_from_token(Context& ctx, Token t, string path = "", size_t line_number = 1);
template <prelexer mx> template <prelexer mx>
......
...@@ -898,6 +898,10 @@ namespace Sass { ...@@ -898,6 +898,10 @@ namespace Sass {
prefix_match, prefix_match,
suffix_match, suffix_match,
substring_match> >(p)) || substring_match> >(p)) ||
(q = peek< sequence< exactly<'.'>, interpolant > >(p)) ||
(q = peek< sequence< exactly<'#'>, interpolant > >(p)) ||
(q = peek< sequence< exactly<'-'>, interpolant > >(p)) ||
(q = peek< sequence< pseudo_prefix, interpolant > >(p)) ||
(q = peek< interpolant >(p))) { (q = peek< interpolant >(p))) {
p = q; p = q;
if (*(p - 1) == '}') saw_interpolant = true; if (*(p - 1) == '}') saw_interpolant = true;
......
#include "prelexer.hpp" #include "prelexer.hpp"
#include "eval_apply.hpp" #include "eval_apply.hpp"
#include "document.hpp"
#include "error.hpp" #include "error.hpp"
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
...@@ -16,7 +17,7 @@ namespace Sass { ...@@ -16,7 +17,7 @@ namespace Sass {
throw Error(Error::evaluation, path, line, message); throw Error(Error::evaluation, path, line, message);
} }
Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node) Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx)
{ {
switch (expr.type()) switch (expr.type())
{ {
...@@ -30,22 +31,49 @@ namespace Sass { ...@@ -30,22 +31,49 @@ namespace Sass {
Node args(expr[1]); Node args(expr[1]);
if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.path(), expr.line()); if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.path(), expr.line());
Node mixin(env[name]); Node mixin(env[name]);
Node expansion(apply_mixin(mixin, args, env, f_env, new_Node)); Node expansion(apply_mixin(mixin, args, env, f_env, new_Node, ctx));
expr.pop_back(); expr.pop_back();
expr.pop_back(); expr.pop_back();
expr += expansion; expr += expansion;
return expr; return expr;
} break; } break;
case Node::propset: case Node::propset: {
eval(expr[1], env, f_env, new_Node, ctx);
return expr;
} break;
case Node::ruleset: { case Node::ruleset: {
eval(expr[1], env, f_env, new_Node); if (expr[0].type() == Node::selector_schema) {
expr[0] = eval(expr[0], env, f_env, new_Node, ctx);
}
eval(expr[1], env, f_env, new_Node, ctx);
return expr; return expr;
} break; } break;
case Node::selector_schema: {
string expansion;
for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], env, f_env, new_Node, ctx);
if (expr[i].type() == Node::string_constant) {
expansion += expr[i].token().unquote();
}
else {
expansion += expr[i].to_string("");
}
}
expansion += " {"; // the parser looks for an lbrace to end a selector
char* expn_src = new char[expansion.size() + 1];
strcpy(expn_src, expansion.c_str());
Document needs_reparsing(Document::make_from_source_chars(ctx, expn_src, expr.path(), true));
needs_reparsing.line = expr.line(); // set the line number to the original node's line
Node sel(needs_reparsing.parse_selector_group());
return sel;
} break;
case Node::root: { case Node::root: {
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
eval(expr[i], env, f_env, new_Node); eval(expr[i], env, f_env, new_Node, ctx);
} }
return expr; return expr;
} break; } break;
...@@ -54,7 +82,7 @@ namespace Sass { ...@@ -54,7 +82,7 @@ namespace Sass {
Environment new_frame; Environment new_frame;
new_frame.link(env); new_frame.link(env);
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
eval(expr[i], new_frame, f_env, new_Node); eval(expr[i], new_frame, f_env, new_Node, ctx);
} }
return expr; return expr;
} break; } break;
...@@ -63,11 +91,11 @@ namespace Sass { ...@@ -63,11 +91,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 (size_t i = 0, S = val.size(); i < S; ++i) { for (size_t i = 0, S = val.size(); i < S; ++i) {
if (val[i].should_eval()) val[i] = eval(val[i], env, f_env, new_Node); if (val[i].should_eval()) val[i] = eval(val[i], env, f_env, new_Node, ctx);
} }
} }
else { else {
val = eval(val, env, f_env, new_Node); val = eval(val, env, f_env, new_Node, ctx);
} }
Node var(expr[0]); Node var(expr[0]);
if (env.query(var.token())) { if (env.query(var.token())) {
...@@ -83,28 +111,28 @@ namespace Sass { ...@@ -83,28 +111,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 (size_t i = 0, S = rhs.size(); i < S; ++i) { for (size_t i = 0, S = rhs.size(); i < S; ++i) {
if (rhs[i].should_eval()) rhs[i] = eval(rhs[i], env, f_env, new_Node); if (rhs[i].should_eval()) rhs[i] = eval(rhs[i], env, f_env, new_Node, ctx);
} }
} }
else if (rhs.type() == Node::value_schema || rhs.type() == Node::string_schema) { else if (rhs.type() == Node::value_schema || rhs.type() == Node::string_schema) {
eval(rhs, env, f_env, new_Node); eval(rhs, env, f_env, new_Node, ctx);
} }
else { else {
if (rhs.should_eval()) expr[1] = eval(rhs, env, f_env, new_Node); if (rhs.should_eval()) expr[1] = eval(rhs, env, f_env, new_Node, ctx);
} }
return expr; return expr;
} break; } break;
case Node::comma_list: case Node::comma_list:
case Node::space_list: { case Node::space_list: {
if (expr.should_eval()) expr[0] = eval(expr[0], env, f_env, new_Node); if (expr.should_eval()) expr[0] = eval(expr[0], env, f_env, new_Node, ctx);
return expr; return expr;
} break; } break;
case Node::disjunction: { case Node::disjunction: {
Node result; Node result;
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], env, f_env, new_Node); result = eval(expr[i], env, f_env, new_Node, ctx);
if (result.type() == Node::boolean && result.boolean_value() == false) continue; if (result.type() == Node::boolean && result.boolean_value() == false) continue;
else return result; else return result;
} }
...@@ -114,7 +142,7 @@ namespace Sass { ...@@ -114,7 +142,7 @@ namespace Sass {
case Node::conjunction: { case Node::conjunction: {
Node result; Node result;
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
result = eval(expr[i], env, f_env, new_Node); result = eval(expr[i], env, f_env, new_Node, ctx);
if (result.type() == Node::boolean && result.boolean_value() == false) return result; if (result.type() == Node::boolean && result.boolean_value() == false) return result;
} }
return result; return result;
...@@ -122,9 +150,9 @@ namespace Sass { ...@@ -122,9 +150,9 @@ namespace Sass {
case Node::relation: { case Node::relation: {
Node lhs(eval(expr[0], env, f_env, new_Node)); Node lhs(eval(expr[0], env, f_env, new_Node, ctx));
Node op(expr[1]); Node op(expr[1]);
Node rhs(eval(expr[2], env, f_env, new_Node)); Node rhs(eval(expr[2], env, f_env, new_Node, ctx));
Node T(new_Node(Node::boolean, lhs.path(), lhs.line(), true)); Node T(new_Node(Node::boolean, lhs.path(), lhs.line(), true));
Node F(new_Node(Node::boolean, lhs.path(), lhs.line(), false)); Node F(new_Node(Node::boolean, lhs.path(), lhs.line(), false));
...@@ -144,11 +172,11 @@ namespace Sass { ...@@ -144,11 +172,11 @@ namespace Sass {
case Node::expression: { case Node::expression: {
Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1)); Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, new_Node); acc << eval(expr[0], env, f_env, new_Node, ctx);
Node rhs(eval(expr[2], env, f_env, new_Node)); Node rhs(eval(expr[2], env, f_env, new_Node, ctx));
accumulate(expr[1].type(), acc, rhs, new_Node); accumulate(expr[1].type(), acc, rhs, new_Node);
for (size_t i = 3, S = expr.size(); i < S; i += 2) { for (size_t i = 3, S = expr.size(); i < S; i += 2) {
Node rhs(eval(expr[i+1], env, f_env, new_Node)); Node rhs(eval(expr[i+1], env, f_env, new_Node, ctx));
accumulate(expr[i].type(), acc, rhs, new_Node); accumulate(expr[i].type(), acc, rhs, new_Node);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
...@@ -157,11 +185,11 @@ namespace Sass { ...@@ -157,11 +185,11 @@ namespace Sass {
case Node::term: { case Node::term: {
if (expr.should_eval()) { if (expr.should_eval()) {
Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1)); Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, new_Node); acc << eval(expr[0], env, f_env, new_Node, ctx);
Node rhs(eval(expr[2], env, f_env, new_Node)); Node rhs(eval(expr[2], env, f_env, new_Node, ctx));
accumulate(expr[1].type(), acc, rhs, new_Node); accumulate(expr[1].type(), acc, rhs, new_Node);
for (size_t i = 3, S = expr.size(); i < S; i += 2) { for (size_t i = 3, S = expr.size(); i < S; i += 2) {
Node rhs(eval(expr[i+1], env, f_env, new_Node)); Node rhs(eval(expr[i+1], env, f_env, new_Node, ctx));
accumulate(expr[i].type(), acc, rhs, new_Node); accumulate(expr[i].type(), acc, rhs, new_Node);
} }
return acc.size() == 1 ? acc[0] : acc; return acc.size() == 1 ? acc[0] : acc;
...@@ -212,11 +240,11 @@ namespace Sass { ...@@ -212,11 +240,11 @@ namespace Sass {
// TO DO: default-constructed Function should be a generic callback (maybe) // TO DO: default-constructed Function should be a generic callback (maybe)
pair<string, size_t> sig(expr[0].token().to_string(), expr[1].size()); pair<string, size_t> sig(expr[0].token().to_string(), expr[1].size());
if (!f_env.count(sig)) return expr; if (!f_env.count(sig)) return expr;
return apply_function(f_env[sig], expr[1], env, f_env, new_Node); return apply_function(f_env[sig], expr[1], env, f_env, new_Node, ctx);
} break; } break;
case Node::unary_plus: { case Node::unary_plus: {
Node arg(eval(expr[0], env, f_env, new_Node)); Node arg(eval(expr[0], env, f_env, new_Node, ctx));
if (arg.is_numeric()) { if (arg.is_numeric()) {
return arg; return arg;
} }
...@@ -227,7 +255,7 @@ namespace Sass { ...@@ -227,7 +255,7 @@ namespace Sass {
} break; } break;
case Node::unary_minus: { case Node::unary_minus: {
Node arg(eval(expr[0], env, f_env, new_Node)); Node arg(eval(expr[0], env, f_env, new_Node, ctx));
if (arg.is_numeric()) { if (arg.is_numeric()) {
return new_Node(expr.path(), expr.line(), -arg.numeric_value()); return new_Node(expr.path(), expr.line(), -arg.numeric_value());
} }
...@@ -240,13 +268,13 @@ namespace Sass { ...@@ -240,13 +268,13 @@ namespace Sass {
case Node::string_schema: case Node::string_schema:
case Node::value_schema: { case Node::value_schema: {
for (size_t i = 0, S = expr.size(); i < S; ++i) { for (size_t i = 0, S = expr.size(); i < S; ++i) {
expr[i] = eval(expr[i], env, f_env, new_Node); expr[i] = eval(expr[i], env, f_env, new_Node, ctx);
} }
return expr; return expr;
} break; } break;
case Node::css_import: { case Node::css_import: {
expr[0] = eval(expr[0], env, f_env, new_Node); expr[0] = eval(expr[0], env, f_env, new_Node, ctx);
return expr; return expr;
} break; } break;
...@@ -350,7 +378,7 @@ namespace Sass { ...@@ -350,7 +378,7 @@ namespace Sass {
} }
} }
Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node) Node apply_mixin(Node& mixin, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx)
{ {
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
...@@ -372,7 +400,7 @@ namespace Sass { ...@@ -372,7 +400,7 @@ namespace Sass {
} }
if (!valid_param) throw_eval_error("mixin " + mixin[0].to_string("") + " has no parameter named " + name.to_string(), arg.path(), arg.line()); if (!valid_param) throw_eval_error("mixin " + mixin[0].to_string("") + " has no parameter named " + name.to_string(), arg.path(), arg.line());
if (!bindings.query(name)) { if (!bindings.query(name)) {
bindings[name] = eval(arg[1], env, f_env, new_Node); bindings[name] = eval(arg[1], env, f_env, new_Node, ctx);
} }
} }
else { else {
...@@ -384,7 +412,7 @@ namespace Sass { ...@@ -384,7 +412,7 @@ namespace Sass {
} }
Node param(params[j]); Node param(params[j]);
Token name(param.type() == Node::variable ? param.token() : param[0].token()); Token name(param.type() == Node::variable ? param.token() : param[0].token());
bindings[name] = eval(args[i], env, f_env, new_Node); bindings[name] = eval(args[i], env, f_env, new_Node, ctx);
++j; ++j;
} }
} }
...@@ -394,19 +422,19 @@ namespace Sass { ...@@ -394,19 +422,19 @@ namespace Sass {
Node param(params[i]); Node param(params[i]);
Token name(param[0].token()); Token name(param[0].token());
if (!bindings.query(name)) { if (!bindings.query(name)) {
bindings[name] = eval(param[1], env, f_env, new_Node); bindings[name] = eval(param[1], env, f_env, new_Node, ctx);
} }
} }
} }
// lexically link the new environment and eval the mixin's body // lexically link the new environment and eval the mixin's body
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) {
body[i] = eval(body[i], bindings, f_env, new_Node); body[i] = eval(body[i], bindings, f_env, new_Node, ctx);
} }
return body; return body;
} }
Node apply_function(const Function& f, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node) Node apply_function(const Function& f, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& ctx)
{ {
map<Token, Node> bindings; map<Token, Node> bindings;
// bind arguments // bind arguments
...@@ -414,11 +442,11 @@ namespace Sass { ...@@ -414,11 +442,11 @@ namespace Sass {
if (args[i].type() == Node::assignment) { if (args[i].type() == Node::assignment) {
Node arg(args[i]); Node arg(args[i]);
Token name(arg[0].token()); Token name(arg[0].token());
bindings[name] = eval(arg[1], env, f_env, new_Node); bindings[name] = eval(arg[1], env, f_env, new_Node, ctx);
} }
else { else {
// TO DO: ensure (j < f.parameters.size()) // TO DO: ensure (j < f.parameters.size())
bindings[f.parameters[j]] = eval(args[i], env, f_env, new_Node); bindings[f.parameters[j]] = eval(args[i], env, f_env, new_Node, ctx);
++j; ++j;
} }
} }
......
...@@ -11,10 +11,10 @@ ...@@ -11,10 +11,10 @@
namespace Sass { namespace Sass {
using std::map; using std::map;
Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node); Node eval(Node& expr, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& src_refs);
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, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node); Node apply_mixin(Node& mixin, const Node& args, 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, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node); Node apply_function(const Function& f, const Node& args, Environment& env, map<pair<string, size_t>, Function>& f_env, Node_Factory& new_Node, Context& src_refs);
} }
\ No newline at end of file
...@@ -39,21 +39,8 @@ extern "C" { ...@@ -39,21 +39,8 @@ extern "C" {
{ {
using namespace Sass; using namespace Sass;
doc.parse_scss(); doc.parse_scss();
// cerr << "PARSED" << endl; eval(doc.root, doc.context.global_env, doc.context.function_env, doc.context.new_Node, doc.context);
eval(doc.root, doc.context.global_env, doc.context.function_env, doc.context.new_Node);
// cerr << "EVALUATED" << endl;
string output(doc.emit_css(static_cast<Document::CSS_Style>(style))); string output(doc.emit_css(static_cast<Document::CSS_Style>(style)));
// cerr << "EMITTED" << endl;
// cerr << "Allocations:\t" << Node::allocations << endl;
// cerr << "Destructions:\t" << Node::destructed << endl;
// cerr << "Registry size:\t" << doc.context.registry.size() << endl;
// for (size_t i = 0; i < doc.context.registry.size(); ++i) {
// delete doc.context.registry[i];
// }
// cerr << "Deallocations:\t" << i << endl;
char* c_output = (char*) malloc(output.size() + 1); char* c_output = (char*) malloc(output.size() + 1);
strcpy(c_output, output.c_str()); strcpy(c_output, output.c_str());
return c_output; return c_output;
......
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