Commit df9b4e6a by Aaron Leung

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

parent bf146431
......@@ -60,17 +60,18 @@ namespace Sass {
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);
doc.path = path;
doc.line = 1;
doc.root = ctx.new_Node(Node::root, path, 1, 0);
doc.lexed = Token::make();
doc.own_source = false;
doc.own_source = own_source;
doc.source = 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;
}
......
#include <map>
#ifndef SASS_PRELEXER_INCLUDED
#include "prelexer.hpp"
#endif
#ifndef SASS_NODE_INCLUDED
#include "node.hpp"
#endif
#include "prelexer.hpp"
#ifndef SASS_CONTEXT_INCLUDED
#include "context.hpp"
#endif
struct Selector_Lookahead {
const char* found;
......@@ -41,7 +46,7 @@ namespace Sass {
~Document();
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);
template <prelexer mx>
......
......@@ -870,34 +870,38 @@ namespace Sass {
const char* q;
bool saw_interpolant = false;
while ((q = peek< identifier >(p)) ||
(q = peek< id_name >(p)) ||
(q = peek< class_name >(p)) ||
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
(q = peek< string_constant >(p)) ||
(q = peek< exactly<'*'> >(p)) ||
(q = peek< exactly<'('> >(p)) ||
(q = peek< exactly<')'> >(p)) ||
(q = peek< exactly<'['> >(p)) ||
(q = peek< exactly<']'> >(p)) ||
(q = peek< exactly<'+'> >(p)) ||
(q = peek< exactly<'~'> >(p)) ||
(q = peek< exactly<'>'> >(p)) ||
(q = peek< exactly<','> >(p)) ||
(q = peek< binomial >(p)) ||
while ((q = peek< identifier >(p)) ||
(q = peek< id_name >(p)) ||
(q = peek< class_name >(p)) ||
(q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
(q = peek< string_constant >(p)) ||
(q = peek< exactly<'*'> >(p)) ||
(q = peek< exactly<'('> >(p)) ||
(q = peek< exactly<')'> >(p)) ||
(q = peek< exactly<'['> >(p)) ||
(q = peek< exactly<']'> >(p)) ||
(q = peek< exactly<'+'> >(p)) ||
(q = peek< exactly<'~'> >(p)) ||
(q = peek< exactly<'>'> >(p)) ||
(q = peek< exactly<','> >(p)) ||
(q = peek< binomial >(p)) ||
(q = peek< sequence< optional<sign>,
optional<digits>,
exactly<'n'> > >(p)) ||
exactly<'n'> > >(p)) ||
(q = peek< sequence< optional<sign>,
digits > >(p)) ||
(q = peek< number >(p)) ||
(q = peek< exactly<'&'> >(p)) ||
digits > >(p)) ||
(q = peek< number >(p)) ||
(q = peek< exactly<'&'> >(p)) ||
(q = peek< alternatives<exact_match,
class_match,
dash_match,
prefix_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))) {
p = q;
if (*(p - 1) == '}') saw_interpolant = true;
......
#include "prelexer.hpp"
#include "eval_apply.hpp"
#include "document.hpp"
#include "error.hpp"
#include <iostream>
#include <sstream>
......@@ -16,7 +17,7 @@ namespace Sass {
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())
{
......@@ -30,22 +31,49 @@ namespace Sass {
Node args(expr[1]);
if (!env.query(name)) throw_eval_error("mixin " + name.to_string() + " is undefined", expr.path(), expr.line());
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 += expansion;
return expr;
} break;
case Node::propset:
case Node::propset: {
eval(expr[1], env, f_env, new_Node, ctx);
return expr;
} break;
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;
} 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: {
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;
} break;
......@@ -54,7 +82,7 @@ namespace Sass {
Environment new_frame;
new_frame.link(env);
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;
} break;
......@@ -63,11 +91,11 @@ namespace Sass {
Node val(expr[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], env, f_env, new_Node);
if (val[i].should_eval()) val[i] = eval(val[i], env, f_env, new_Node, ctx);
}
}
else {
val = eval(val, env, f_env, new_Node);
val = eval(val, env, f_env, new_Node, ctx);
}
Node var(expr[0]);
if (env.query(var.token())) {
......@@ -83,28 +111,28 @@ namespace Sass {
Node rhs(expr[1]);
if (rhs.type() == Node::comma_list || rhs.type() == Node::space_list) {
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) {
eval(rhs, env, f_env, new_Node);
eval(rhs, env, f_env, new_Node, ctx);
}
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;
} break;
case Node::comma_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;
} break;
case Node::disjunction: {
Node result;
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;
else return result;
}
......@@ -114,7 +142,7 @@ namespace Sass {
case Node::conjunction: {
Node result;
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;
}
return result;
......@@ -122,9 +150,9 @@ namespace Sass {
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 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 F(new_Node(Node::boolean, lhs.path(), lhs.line(), false));
......@@ -144,11 +172,11 @@ namespace Sass {
case Node::expression: {
Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, new_Node);
Node rhs(eval(expr[2], 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, ctx));
accumulate(expr[1].type(), acc, rhs, new_Node);
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);
}
return acc.size() == 1 ? acc[0] : acc;
......@@ -157,11 +185,11 @@ namespace Sass {
case Node::term: {
if (expr.should_eval()) {
Node acc(new_Node(Node::expression, expr.path(), expr.line(), 1));
acc << eval(expr[0], env, f_env, new_Node);
Node rhs(eval(expr[2], 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, ctx));
accumulate(expr[1].type(), acc, rhs, new_Node);
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);
}
return acc.size() == 1 ? acc[0] : acc;
......@@ -212,11 +240,11 @@ namespace Sass {
// TO DO: default-constructed Function should be a generic callback (maybe)
pair<string, size_t> sig(expr[0].token().to_string(), expr[1].size());
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;
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()) {
return arg;
}
......@@ -227,7 +255,7 @@ namespace Sass {
} break;
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()) {
return new_Node(expr.path(), expr.line(), -arg.numeric_value());
}
......@@ -240,13 +268,13 @@ namespace Sass {
case Node::string_schema:
case Node::value_schema: {
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;
} break;
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;
} break;
......@@ -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 body(new_Node(mixin[2])); // clone the body
......@@ -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 (!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 {
......@@ -384,7 +412,7 @@ namespace Sass {
}
Node param(params[j]);
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;
}
}
......@@ -394,19 +422,19 @@ namespace Sass {
Node param(params[i]);
Token name(param[0].token());
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
bindings.link(env.global ? *env.global : env);
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;
}
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;
// bind arguments
......@@ -414,11 +442,11 @@ namespace Sass {
if (args[i].type() == Node::assignment) {
Node arg(args[i]);
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 {
// 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;
}
}
......
......@@ -11,10 +11,10 @@
namespace Sass {
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);
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_function(const Function& f, 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, Context& src_refs);
}
\ No newline at end of file
......@@ -39,21 +39,8 @@ extern "C" {
{
using namespace Sass;
doc.parse_scss();
// cerr << "PARSED" << endl;
eval(doc.root, doc.context.global_env, doc.context.function_env, doc.context.new_Node);
// cerr << "EVALUATED" << endl;
eval(doc.root, doc.context.global_env, doc.context.function_env, doc.context.new_Node, doc.context);
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);
strcpy(c_output, output.c_str());
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