Commit 84754d53 by xzyfer Committed by Michael Mifsud

Bump LibSass 3.5.2

Fixes #2287
parent 7648fc46
{ {
"name": "node-sass", "name": "node-sass",
"version": "4.8.2", "version": "4.8.2",
"libsass": "3.5.1", "libsass": "3.5.2",
"description": "Wrapper around libsass", "description": "Wrapper around libsass",
"license": "MIT", "license": "MIT",
"bugs": "https://github.com/sass/node-sass/issues", "bugs": "https://github.com/sass/node-sass/issues",
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
'sources': [ 'sources': [
'libsass/src/ast.cpp', 'libsass/src/ast.cpp',
'libsass/src/ast_fwd_decl.cpp', 'libsass/src/ast_fwd_decl.cpp',
'libsass/src/backtrace.cpp',
'libsass/src/base64vlq.cpp', 'libsass/src/base64vlq.cpp',
'libsass/src/bind.cpp', 'libsass/src/bind.cpp',
'libsass/src/cencode.c', 'libsass/src/cencode.c',
...@@ -35,6 +36,8 @@ ...@@ -35,6 +36,8 @@
'libsass/src/listize.cpp', 'libsass/src/listize.cpp',
'libsass/src/memory/SharedPtr.cpp', 'libsass/src/memory/SharedPtr.cpp',
'libsass/src/node.cpp', 'libsass/src/node.cpp',
'libsass/src/operators.cpp',
'libsass/src/operators.hpp',
'libsass/src/output.cpp', 'libsass/src/output.cpp',
'libsass/src/parser.cpp', 'libsass/src/parser.cpp',
'libsass/src/plugins.cpp', 'libsass/src/plugins.cpp',
......
...@@ -41,6 +41,8 @@ SOURCES = \ ...@@ -41,6 +41,8 @@ SOURCES = \
sass_context.cpp \ sass_context.cpp \
sass_functions.cpp \ sass_functions.cpp \
sass2scss.cpp \ sass2scss.cpp \
backtrace.cpp \
operators.cpp \
to_c.cpp \ to_c.cpp \
to_value.cpp \ to_value.cpp \
source_map.cpp \ source_map.cpp \
......
...@@ -837,7 +837,7 @@ namespace Sass { ...@@ -837,7 +837,7 @@ namespace Sass {
return lhs_list->is_superselector_of(rhs_list); return lhs_list->is_superselector_of(rhs_list);
} }
} }
error("is_superselector expected a Selector_List", sub->pstate()); coreError("is_superselector expected a Selector_List", sub->pstate());
return false; return false;
} }
...@@ -1171,7 +1171,7 @@ namespace Sass { ...@@ -1171,7 +1171,7 @@ namespace Sass {
// check if we need to append some headers // check if we need to append some headers
// then we need to check for the combinator // then we need to check for the combinator
// only then we can safely set the new tail // only then we can safely set the new tail
void Complex_Selector::append(Complex_Selector_Obj ss) void Complex_Selector::append(Complex_Selector_Obj ss, Backtraces& traces)
{ {
Complex_Selector_Obj t = ss->tail(); Complex_Selector_Obj t = ss->tail();
...@@ -1185,7 +1185,8 @@ namespace Sass { ...@@ -1185,7 +1185,8 @@ namespace Sass {
// append old headers // append old headers
if (h && h->length()) { if (h && h->length()) {
if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) { if (last()->combinator() != ANCESTOR_OF && c != ANCESTOR_OF) {
error("Invalid parent selector", pstate_); traces.push_back(Backtrace(pstate()));
throw Exception::InvalidParent(this, traces, ss);
} else if (last()->head_ && last()->head_->length()) { } else if (last()->head_ && last()->head_->length()) {
Compound_Selector_Obj rh = last()->head(); Compound_Selector_Obj rh = last()->head();
size_t i; size_t i;
...@@ -1258,21 +1259,21 @@ namespace Sass { ...@@ -1258,21 +1259,21 @@ namespace Sass {
return list; return list;
} }
Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent) Selector_List_Ptr Selector_List::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)
{ {
if (!this->has_parent_ref()) return this; if (!this->has_parent_ref()) return this;
Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate()); Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());
Selector_List_Ptr ps = pstack.back(); Selector_List_Ptr ps = pstack.back();
for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) { for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {
for (size_t si = 0, sL = this->length(); si < sL; ++si) { for (size_t si = 0, sL = this->length(); si < sL; ++si) {
Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, implicit_parent); Selector_List_Obj rv = at(si)->resolve_parent_refs(pstack, traces, implicit_parent);
ss->concat(rv); ss->concat(rv);
} }
} }
return ss; return ss;
} }
Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent) Selector_List_Ptr Complex_Selector::resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent)
{ {
Complex_Selector_Obj tail = this->tail(); Complex_Selector_Obj tail = this->tail();
Compound_Selector_Obj head = this->head(); Compound_Selector_Obj head = this->head();
...@@ -1285,7 +1286,7 @@ namespace Sass { ...@@ -1285,7 +1286,7 @@ namespace Sass {
} }
// first resolve_parent_refs the tail (which may return an expanded list) // first resolve_parent_refs the tail (which may return an expanded list)
Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, implicit_parent) : 0; Selector_List_Obj tails = tail ? tail->resolve_parent_refs(pstack, traces, implicit_parent) : 0;
if (head && head->length() > 0) { if (head && head->length() > 0) {
...@@ -1331,7 +1332,7 @@ namespace Sass { ...@@ -1331,7 +1332,7 @@ namespace Sass {
// keep old parser state // keep old parser state
s->pstate(pstate()); s->pstate(pstate());
// append new tail // append new tail
s->append(ss); s->append(ss, traces);
retval->append(s); retval->append(s);
} }
} }
...@@ -1346,7 +1347,8 @@ namespace Sass { ...@@ -1346,7 +1347,8 @@ namespace Sass {
// this is only if valid if the parent has no trailing op // this is only if valid if the parent has no trailing op
// otherwise we cannot append more simple selectors to head // otherwise we cannot append more simple selectors to head
if (parent->last()->combinator() != ANCESTOR_OF) { if (parent->last()->combinator() != ANCESTOR_OF) {
throw Exception::InvalidParent(parent, ss); traces.push_back(Backtrace(pstate()));
throw Exception::InvalidParent(parent, traces, ss);
} }
ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL); ss->tail(tail ? SASS_MEMORY_CLONE(tail) : NULL);
Compound_Selector_Obj h = SASS_MEMORY_COPY(head_); Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);
...@@ -1369,7 +1371,7 @@ namespace Sass { ...@@ -1369,7 +1371,7 @@ namespace Sass {
// keep old parser state // keep old parser state
s->pstate(pstate()); s->pstate(pstate());
// append new tail // append new tail
s->append(ss); s->append(ss, traces);
retval->append(s); retval->append(s);
} }
} }
...@@ -1406,7 +1408,7 @@ namespace Sass { ...@@ -1406,7 +1408,7 @@ namespace Sass {
for (Simple_Selector_Obj ss : head->elements()) { for (Simple_Selector_Obj ss : head->elements()) {
if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) { if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {
if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) { if (Selector_List_Ptr sl = Cast<Selector_List>(ws->selector())) {
if (parents) ws->selector(sl->resolve_parent_refs(pstack, implicit_parent)); if (parents) ws->selector(sl->resolve_parent_refs(pstack, traces, implicit_parent));
} }
} }
} }
...@@ -1690,7 +1692,7 @@ namespace Sass { ...@@ -1690,7 +1692,7 @@ namespace Sass {
} }
if (!pIter->head() || pIter->tail()) { if (!pIter->head() || pIter->tail()) {
error("nested selectors may not be extended", c->pstate()); coreError("nested selectors may not be extended", c->pstate());
} }
compound_sel->is_optional(extendee->is_optional()); compound_sel->is_optional(extendee->is_optional());
...@@ -1766,31 +1768,31 @@ namespace Sass { ...@@ -1766,31 +1768,31 @@ namespace Sass {
{ {
if (!a->name().empty()) { if (!a->name().empty()) {
if (has_keyword_argument()) { if (has_keyword_argument()) {
error("named arguments must precede variable-length argument", a->pstate()); coreError("named arguments must precede variable-length argument", a->pstate());
} }
has_named_arguments(true); has_named_arguments(true);
} }
else if (a->is_rest_argument()) { else if (a->is_rest_argument()) {
if (has_rest_argument()) { if (has_rest_argument()) {
error("functions and mixins may only be called with one variable-length argument", a->pstate()); coreError("functions and mixins may only be called with one variable-length argument", a->pstate());
} }
if (has_keyword_argument_) { if (has_keyword_argument_) {
error("only keyword arguments may follow variable arguments", a->pstate()); coreError("only keyword arguments may follow variable arguments", a->pstate());
} }
has_rest_argument(true); has_rest_argument(true);
} }
else if (a->is_keyword_argument()) { else if (a->is_keyword_argument()) {
if (has_keyword_argument()) { if (has_keyword_argument()) {
error("functions and mixins may only be called with one keyword argument", a->pstate()); coreError("functions and mixins may only be called with one keyword argument", a->pstate());
} }
has_keyword_argument(true); has_keyword_argument(true);
} }
else { else {
if (has_rest_argument()) { if (has_rest_argument()) {
error("ordinal arguments must precede variable-length arguments", a->pstate()); coreError("ordinal arguments must precede variable-length arguments", a->pstate());
} }
if (has_named_arguments()) { if (has_named_arguments()) {
error("ordinal arguments must precede named arguments", a->pstate()); coreError("ordinal arguments must precede named arguments", a->pstate());
} }
} }
} }
...@@ -1907,6 +1909,7 @@ namespace Sass { ...@@ -1907,6 +1909,7 @@ namespace Sass {
l.normalize(); r.normalize(); l.normalize(); r.normalize();
Units &lhs_unit = l, &rhs_unit = r; Units &lhs_unit = l, &rhs_unit = r;
if (!(lhs_unit == rhs_unit)) { if (!(lhs_unit == rhs_unit)) {
/* ToDo: do we always get usefull backtraces? */
throw Exception::IncompatibleUnits(rhs, *this); throw Exception::IncompatibleUnits(rhs, *this);
} }
return lhs_unit < rhs_unit || return lhs_unit < rhs_unit ||
......
...@@ -33,7 +33,7 @@ class LocalOption { ...@@ -33,7 +33,7 @@ class LocalOption {
#define NESTING_GUARD(name) \ #define NESTING_GUARD(name) \
LocalOption<size_t> cnt_##name(name, name + 1); \ LocalOption<size_t> cnt_##name(name, name + 1); \
if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate); \ if (name > MAX_NESTING) throw Exception::NestingLimitError(pstate, traces); \
#define ATTACH_OPERATIONS()\ #define ATTACH_OPERATIONS()\
virtual void perform(Operation<void>* op) { (*op)(this); }\ virtual void perform(Operation<void>* op) { (*op)(this); }\
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include "memory/SharedPtr.hpp" #include "memory/SharedPtr.hpp"
#include "sass/functions.h"
///////////////////////////////////////////// /////////////////////////////////////////////
// Forward declarations for the AST visitors. // Forward declarations for the AST visitors.
...@@ -418,6 +419,8 @@ namespace Sass { ...@@ -418,6 +419,8 @@ namespace Sass {
typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet; typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;
typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict; typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;
typedef std::vector<Sass_Import_Entry>* ImporterStack;
// only to switch implementations for testing // only to switch implementations for testing
#define environment_map std::map #define environment_map std::map
......
#include "backtrace.hpp"
namespace Sass {
const std::string traces_to_string(Backtraces traces, std::string indent) {
std::stringstream ss;
std::string cwd(File::get_cwd());
bool first = true;
size_t i_beg = traces.size() - 1;
size_t i_end = std::string::npos;
for (size_t i = i_beg; i != i_end; i --) {
const Backtrace& trace = traces[i];
// make path relative to the current directory
std::string rel_path(File::abs2rel(trace.pstate.path, cwd, cwd));
// skip functions on error cases (unsure why ruby sass does this)
// if (trace.caller.substr(0, 6) == ", in f") continue;
if (first) {
ss << indent;
ss << "on line ";
ss << trace.pstate.line + 1;
ss << " of " << rel_path;
// ss << trace.caller;
first = false;
} else {
ss << trace.caller;
ss << std::endl;
ss << indent;
ss << "from line ";
ss << trace.pstate.line + 1;
ss << " of " << rel_path;
}
}
ss << std::endl;
return ss.str();
}
};
#ifndef SASS_BACKTRACE_H #ifndef SASS_BACKTRACE_H
#define SASS_BACKTRACE_H #define SASS_BACKTRACE_H
#include <vector>
#include <sstream> #include <sstream>
#include "file.hpp" #include "file.hpp"
#include "position.hpp" #include "position.hpp"
namespace Sass { namespace Sass {
struct Backtrace { struct Backtrace {
Backtrace* parent;
ParserState pstate; ParserState pstate;
std::string caller; std::string caller;
Backtrace(Backtrace* prn, ParserState pstate, std::string c) Backtrace(ParserState pstate, std::string c = "")
: parent(prn), : pstate(pstate),
pstate(pstate),
caller(c) caller(c)
{ } { }
const std::string to_string(bool warning = false) };
{
size_t i = -1;
std::stringstream ss;
std::string cwd(Sass::File::get_cwd());
Backtrace* this_point = this;
if (!warning) ss << std::endl << "Backtrace:";
// the first tracepoint (which is parent-less) is an empty placeholder
while (this_point->parent) {
// make path relative to the current directory
std::string rel_path(Sass::File::abs2rel(this_point->pstate.path, cwd, cwd));
if (warning) {
ss << std::endl
<< "\t"
<< (++i == 0 ? "on" : "from")
<< " line "
<< this_point->pstate.line + 1
<< " of "
<< rel_path;
} else {
ss << std::endl
<< "\t"
<< rel_path
<< ":"
<< this_point->pstate.line + 1
<< this_point->parent->caller;
}
this_point = this_point->parent;
}
return ss.str();
}
size_t depth() typedef std::vector<Backtrace> Backtraces;
{
size_t d = std::string::npos;
Backtrace* p = parent;
while (p) {
++d;
p = p->parent;
}
return d;
}
}; const std::string traces_to_string(Backtraces traces, std::string indent = "\t");
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include "bind.hpp" #include "bind.hpp"
#include "ast.hpp" #include "ast.hpp"
#include "context.hpp" #include "context.hpp"
#include "expand.hpp"
#include "eval.hpp" #include "eval.hpp"
#include <map> #include <map>
#include <iostream> #include <iostream>
...@@ -53,7 +54,7 @@ namespace Sass { ...@@ -53,7 +54,7 @@ namespace Sass {
std::stringstream msg; std::stringstream msg;
msg << "wrong number of arguments (" << LA << " for " << LP << ")"; msg << "wrong number of arguments (" << LA << " for " << LP << ")";
msg << " for `" << name << "'"; msg << " for `" << name << "'";
return error(msg.str(), as->pstate()); return error(msg.str(), as->pstate(), eval->exp.traces);
} }
Parameter_Obj p = ps->at(ip); Parameter_Obj p = ps->at(ip);
...@@ -106,7 +107,8 @@ namespace Sass { ...@@ -106,7 +107,8 @@ namespace Sass {
false, false,
false)); false));
} else { } else {
throw Exception::InvalidVarKwdType(key->pstate(), key->inspect(), a); eval->exp.traces.push_back(Backtrace(key->pstate()));
throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);
} }
} }
...@@ -219,13 +221,16 @@ namespace Sass { ...@@ -219,13 +221,16 @@ namespace Sass {
for (auto key : argmap->keys()) { for (auto key : argmap->keys()) {
String_Constant_Ptr val = Cast<String_Constant>(key); String_Constant_Ptr val = Cast<String_Constant>(key);
if (val == NULL) throw Exception::InvalidVarKwdType(key->pstate(), key->inspect(), a); if (val == NULL) {
eval->exp.traces.push_back(Backtrace(key->pstate()));
throw Exception::InvalidVarKwdType(key->pstate(), eval->exp.traces, key->inspect(), a);
}
std::string param = "$" + unquote(val->value()); std::string param = "$" + unquote(val->value());
if (!param_map.count(param)) { if (!param_map.count(param)) {
std::stringstream msg; std::stringstream msg;
msg << callee << " has no parameter named " << param; msg << callee << " has no parameter named " << param;
error(msg.str(), a->pstate()); error(msg.str(), a->pstate(), eval->exp.traces);
} }
env->local_frame()[param] = argmap->at(key); env->local_frame()[param] = argmap->at(key);
} }
...@@ -240,7 +245,7 @@ namespace Sass { ...@@ -240,7 +245,7 @@ namespace Sass {
std::stringstream msg; std::stringstream msg;
msg << "parameter " << p->name() msg << "parameter " << p->name()
<< " provided more than once in call to " << callee; << " provided more than once in call to " << callee;
error(msg.str(), a->pstate()); error(msg.str(), a->pstate(), eval->exp.traces);
} }
// ordinal arg -- bind it to the next param // ordinal arg -- bind it to the next param
env->local_frame()[p->name()] = a->value(); env->local_frame()[p->name()] = a->value();
...@@ -254,7 +259,7 @@ namespace Sass { ...@@ -254,7 +259,7 @@ namespace Sass {
} else { } else {
std::stringstream msg; std::stringstream msg;
msg << callee << " has no parameter named " << a->name(); msg << callee << " has no parameter named " << a->name();
error(msg.str(), a->pstate()); error(msg.str(), a->pstate(), eval->exp.traces);
} }
} }
if (param_map[a->name()]) { if (param_map[a->name()]) {
...@@ -262,14 +267,14 @@ namespace Sass { ...@@ -262,14 +267,14 @@ namespace Sass {
std::stringstream msg; std::stringstream msg;
msg << "argument " << a->name() << " of " << callee msg << "argument " << a->name() << " of " << callee
<< "cannot be used as named argument"; << "cannot be used as named argument";
error(msg.str(), a->pstate()); error(msg.str(), a->pstate(), eval->exp.traces);
} }
} }
if (env->has_local(a->name())) { if (env->has_local(a->name())) {
std::stringstream msg; std::stringstream msg;
msg << "parameter " << p->name() msg << "parameter " << p->name()
<< "provided more than once in call to " << callee; << "provided more than once in call to " << callee;
error(msg.str(), a->pstate()); error(msg.str(), a->pstate(), eval->exp.traces);
} }
env->local_frame()[a->name()] = a->value(); env->local_frame()[a->name()] = a->value();
} }
...@@ -294,7 +299,7 @@ namespace Sass { ...@@ -294,7 +299,7 @@ namespace Sass {
} }
else { else {
// param is unbound and has no default value -- error // param is unbound and has no default value -- error
throw Exception::MissingArgument(as->pstate(), name, leftover->name(), type); throw Exception::MissingArgument(as->pstate(), eval->exp.traces, name, leftover->name(), type);
} }
} }
} }
......
...@@ -7,10 +7,15 @@ namespace Sass { ...@@ -7,10 +7,15 @@ namespace Sass {
CheckNesting::CheckNesting() CheckNesting::CheckNesting()
: parents(std::vector<Statement_Ptr>()), : parents(std::vector<Statement_Ptr>()),
parent(0), traces(std::vector<Backtrace>()),
current_mixin_definition(0) parent(0), current_mixin_definition(0)
{ } { }
void error(AST_Node_Ptr node, Backtraces traces, std::string msg) {
traces.push_back(Backtrace(node->pstate()));
throw Exception::InvalidSass(node->pstate(), traces, msg);
}
Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent) Statement_Ptr CheckNesting::visit_children(Statement_Ptr parent)
{ {
Statement_Ptr old_parent = this->parent; Statement_Ptr old_parent = this->parent;
...@@ -62,6 +67,12 @@ namespace Sass { ...@@ -62,6 +67,12 @@ namespace Sass {
Block_Ptr b = Cast<Block>(parent); Block_Ptr b = Cast<Block>(parent);
if (Trace_Ptr trace = Cast<Trace>(parent)) {
if (trace->type() == 'i') {
this->traces.push_back(Backtrace(trace->pstate()));
}
}
if (!b) { if (!b) {
if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) { if (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {
b = bb->block(); b = bb->block();
...@@ -73,9 +84,16 @@ namespace Sass { ...@@ -73,9 +84,16 @@ namespace Sass {
n->perform(this); n->perform(this);
} }
} }
this->parent = old_parent; this->parent = old_parent;
this->parents.pop_back(); this->parents.pop_back();
if (Trace_Ptr trace = Cast<Trace>(parent)) {
if (trace->type() == 'i') {
this->traces.pop_back();
}
}
return b; return b;
} }
...@@ -126,75 +144,69 @@ namespace Sass { ...@@ -126,75 +144,69 @@ namespace Sass {
if (!this->parent) return true; if (!this->parent) return true;
if (Cast<Content>(node)) if (Cast<Content>(node))
{ this->invalid_content_parent(this->parent); } { this->invalid_content_parent(this->parent, node); }
if (is_charset(node)) if (is_charset(node))
{ this->invalid_charset_parent(this->parent); } { this->invalid_charset_parent(this->parent, node); }
if (Cast<Extension>(node)) if (Cast<Extension>(node))
{ this->invalid_extend_parent(this->parent); } { this->invalid_extend_parent(this->parent, node); }
// if (Cast<Import>(node)) // if (Cast<Import>(node))
// { this->invalid_import_parent(this->parent); } // { this->invalid_import_parent(this->parent); }
if (this->is_mixin(node)) if (this->is_mixin(node))
{ this->invalid_mixin_definition_parent(this->parent); } { this->invalid_mixin_definition_parent(this->parent, node); }
if (this->is_function(node)) if (this->is_function(node))
{ this->invalid_function_parent(this->parent); } { this->invalid_function_parent(this->parent, node); }
if (this->is_function(this->parent)) if (this->is_function(this->parent))
{ this->invalid_function_child(node); } { this->invalid_function_child(node); }
if (Cast<Declaration>(node)) if (Declaration_Ptr d = Cast<Declaration>(node))
{ this->invalid_prop_parent(this->parent); } {
this->invalid_prop_parent(this->parent, node);
this->invalid_value_child(d->value());
}
if (Cast<Declaration>(this->parent)) if (Cast<Declaration>(this->parent))
{ this->invalid_prop_child(node); } { this->invalid_prop_child(node); }
if (Cast<Return>(node)) if (Cast<Return>(node))
{ this->invalid_return_parent(this->parent); } { this->invalid_return_parent(this->parent, node); }
return true; return true;
} }
void CheckNesting::invalid_content_parent(Statement_Ptr parent) void CheckNesting::invalid_content_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
if (!this->current_mixin_definition) { if (!this->current_mixin_definition) {
throw Exception::InvalidSass( error(node, traces, "@content may only be used within a mixin.");
parent->pstate(),
"@content may only be used within a mixin."
);
} }
} }
void CheckNesting::invalid_charset_parent(Statement_Ptr parent) void CheckNesting::invalid_charset_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
if (!( if (!(
is_root_node(parent) is_root_node(parent)
)) { )) {
throw Exception::InvalidSass( error(node, traces, "@charset may only be used at the root of a document.");
parent->pstate(),
"@charset may only be used at the root of a document."
);
} }
} }
void CheckNesting::invalid_extend_parent(Statement_Ptr parent) void CheckNesting::invalid_extend_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
if (!( if (!(
Cast<Ruleset>(parent) || Cast<Ruleset>(parent) ||
Cast<Mixin_Call>(parent) || Cast<Mixin_Call>(parent) ||
is_mixin(parent) is_mixin(parent)
)) { )) {
throw Exception::InvalidSass( error(node, traces, "Extend directives may only be used within rules.");
parent->pstate(),
"Extend directives may only be used within rules."
);
} }
} }
// void CheckNesting::invalid_import_parent(Statement_Ptr parent) // void CheckNesting::invalid_import_parent(Statement_Ptr parent, AST_Node_Ptr node)
// { // {
// for (auto pp : this->parents) { // for (auto pp : this->parents) {
// if ( // if (
...@@ -206,10 +218,7 @@ namespace Sass { ...@@ -206,10 +218,7 @@ namespace Sass {
// Cast<Mixin_Call>(pp) || // Cast<Mixin_Call>(pp) ||
// is_mixin(pp) // is_mixin(pp)
// ) { // ) {
// throw Exception::InvalidSass( // error(node, traces, "Import directives may not be defined within control directives or other mixins.");
// parent->pstate(),
// "Import directives may not be defined within control directives or other mixins."
// );
// } // }
// } // }
...@@ -218,14 +227,11 @@ namespace Sass { ...@@ -218,14 +227,11 @@ namespace Sass {
// } // }
// if (false/*n.css_import?*/) { // if (false/*n.css_import?*/) {
// throw Exception::InvalidSass( // error(node, traces, "CSS import directives may only be used at the root of a document.");
// parent->pstate(),
// "CSS import directives may only be used at the root of a document."
// );
// } // }
// } // }
void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent) void CheckNesting::invalid_mixin_definition_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
for (Statement_Ptr pp : this->parents) { for (Statement_Ptr pp : this->parents) {
if ( if (
...@@ -237,15 +243,12 @@ namespace Sass { ...@@ -237,15 +243,12 @@ namespace Sass {
Cast<Mixin_Call>(pp) || Cast<Mixin_Call>(pp) ||
is_mixin(pp) is_mixin(pp)
) { ) {
throw Exception::InvalidSass( error(node, traces, "Mixins may not be defined within control directives or other mixins.");
parent->pstate(),
"Mixins may not be defined within control directives or other mixins."
);
} }
} }
} }
void CheckNesting::invalid_function_parent(Statement_Ptr parent) void CheckNesting::invalid_function_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
for (Statement_Ptr pp : this->parents) { for (Statement_Ptr pp : this->parents) {
if ( if (
...@@ -257,10 +260,7 @@ namespace Sass { ...@@ -257,10 +260,7 @@ namespace Sass {
Cast<Mixin_Call>(pp) || Cast<Mixin_Call>(pp) ||
is_mixin(pp) is_mixin(pp)
) { ) {
throw Exception::InvalidSass( error(node, traces, "Functions may not be defined within control directives or other mixins.");
parent->pstate(),
"Functions may not be defined within control directives or other mixins."
);
} }
} }
} }
...@@ -282,10 +282,7 @@ namespace Sass { ...@@ -282,10 +282,7 @@ namespace Sass {
Cast<Warning>(child) || Cast<Warning>(child) ||
Cast<Error>(child) Cast<Error>(child)
)) { )) {
throw Exception::InvalidSass( error(child, traces, "Functions can only contain variable declarations and control directives.");
child->pstate(),
"Functions can only contain variable declarations and control directives."
);
} }
} }
...@@ -301,14 +298,11 @@ namespace Sass { ...@@ -301,14 +298,11 @@ namespace Sass {
Cast<Declaration>(child) || Cast<Declaration>(child) ||
Cast<Mixin_Call>(child) Cast<Mixin_Call>(child)
)) { )) {
throw Exception::InvalidSass( error(child, traces, "Illegal nesting: Only properties may be nested beneath properties.");
child->pstate(),
"Illegal nesting: Only properties may be nested beneath properties."
);
} }
} }
void CheckNesting::invalid_prop_parent(Statement_Ptr parent) void CheckNesting::invalid_prop_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
if (!( if (!(
is_mixin(parent) || is_mixin(parent) ||
...@@ -318,20 +312,31 @@ namespace Sass { ...@@ -318,20 +312,31 @@ namespace Sass {
Cast<Declaration>(parent) || Cast<Declaration>(parent) ||
Cast<Mixin_Call>(parent) Cast<Mixin_Call>(parent)
)) { )) {
throw Exception::InvalidSass( error(node, traces, "Properties are only allowed within rules, directives, mixin includes, or other properties.");
parent->pstate(), }
"Properties are only allowed within rules, directives, mixin includes, or other properties." }
);
void CheckNesting::invalid_value_child(AST_Node_Ptr d)
{
if (Map_Ptr m = Cast<Map>(d)) {
traces.push_back(Backtrace(m->pstate()));
throw Exception::InvalidValue(traces, *m);
} }
if (Number_Ptr n = Cast<Number>(d)) {
if (!n->is_valid_css_unit()) {
traces.push_back(Backtrace(n->pstate()));
throw Exception::InvalidValue(traces, *n);
}
}
// error(dbg + " isn't a valid CSS value.", m->pstate(),);
} }
void CheckNesting::invalid_return_parent(Statement_Ptr parent) void CheckNesting::invalid_return_parent(Statement_Ptr parent, AST_Node_Ptr node)
{ {
if (!this->is_function(parent)) { if (!this->is_function(parent)) {
throw Exception::InvalidSass( error(node, traces, "@return may only be used within a function.");
parent->pstate(),
"@return may only be used within a function."
);
} }
} }
......
...@@ -9,6 +9,7 @@ namespace Sass { ...@@ -9,6 +9,7 @@ namespace Sass {
class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> { class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {
std::vector<Statement_Ptr> parents; std::vector<Statement_Ptr> parents;
Backtraces traces;
Statement_Ptr parent; Statement_Ptr parent;
Definition_Ptr current_mixin_definition; Definition_Ptr current_mixin_definition;
...@@ -34,17 +35,18 @@ namespace Sass { ...@@ -34,17 +35,18 @@ namespace Sass {
} }
private: private:
void invalid_content_parent(Statement_Ptr); void invalid_content_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_charset_parent(Statement_Ptr); void invalid_charset_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_extend_parent(Statement_Ptr); void invalid_extend_parent(Statement_Ptr, AST_Node_Ptr);
// void invalid_import_parent(Statement_Ptr); // void invalid_import_parent(Statement_Ptr);
void invalid_mixin_definition_parent(Statement_Ptr); void invalid_mixin_definition_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_function_parent(Statement_Ptr); void invalid_function_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_function_child(Statement_Ptr); void invalid_function_child(Statement_Ptr);
void invalid_prop_child(Statement_Ptr); void invalid_prop_child(Statement_Ptr);
void invalid_prop_parent(Statement_Ptr); void invalid_prop_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_return_parent(Statement_Ptr); void invalid_return_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_value_child(AST_Node_Ptr);
bool is_transparent_parent(Statement_Ptr, Statement_Ptr); bool is_transparent_parent(Statement_Ptr, Statement_Ptr);
......
...@@ -74,6 +74,7 @@ namespace Sass { ...@@ -74,6 +74,7 @@ namespace Sass {
subset_map(), subset_map(),
import_stack(), import_stack(),
callee_stack(), callee_stack(),
traces(),
c_compiler(NULL), c_compiler(NULL),
c_headers (std::vector<Sass_Importer_Entry>()), c_headers (std::vector<Sass_Importer_Entry>()),
...@@ -250,10 +251,9 @@ namespace Sass { ...@@ -250,10 +251,9 @@ namespace Sass {
return vec; return vec;
} }
// register include with resolved path and its content // register include with resolved path and its content
// memory of the resources will be freed by us on exit // memory of the resources will be freed by us on exit
void Context::register_resource(const Include& inc, const Resource& res, ParserState* prstate) void Context::register_resource(const Include& inc, const Resource& res)
{ {
// do not parse same resource twice // do not parse same resource twice
...@@ -301,21 +301,22 @@ namespace Sass { ...@@ -301,21 +301,22 @@ namespace Sass {
for (size_t i = 0; i < import_stack.size() - 2; ++i) { for (size_t i = 0; i < import_stack.size() - 2; ++i) {
auto parent = import_stack[i]; auto parent = import_stack[i];
if (std::strcmp(parent->abs_path, import->abs_path) == 0) { if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
std::string cwd(File::get_cwd());
// make path relative to the current directory
std::string stack("An @import loop has been found:"); std::string stack("An @import loop has been found:");
for (size_t n = 1; n < i + 2; ++n) { for (size_t n = 1; n < i + 2; ++n) {
stack += "\n " + std::string(import_stack[n]->imp_path) + stack += "\n " + std::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) +
" imports " + std::string(import_stack[n+1]->imp_path); " imports " + std::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd));
} }
// implement error throw directly until we // implement error throw directly until we
// decided how to handle full stack traces // decided how to handle full stack traces
ParserState state = prstate ? *prstate : pstate; throw Exception::InvalidSyntax(pstate, traces, stack);
throw Exception::InvalidSyntax(state, stack, &import_stack);
// error(stack, prstate ? *prstate : pstate, import_stack); // error(stack, prstate ? *prstate : pstate, import_stack);
} }
} }
// create a parser instance from the given c_str buffer // create a parser instance from the given c_str buffer
Parser p(Parser::from_c_str(contents, *this, pstate)); Parser p(Parser::from_c_str(contents, *this, traces, pstate));
// do not yet dispose these buffers // do not yet dispose these buffers
sass_import_take_source(import); sass_import_take_source(import);
sass_import_take_srcmap(import); sass_import_take_srcmap(import);
...@@ -330,7 +331,15 @@ namespace Sass { ...@@ -330,7 +331,15 @@ namespace Sass {
ast_pair(inc.abs_path, { res, root }); ast_pair(inc.abs_path, { res, root });
// register resulting resource // register resulting resource
sheets.insert(ast_pair); sheets.insert(ast_pair);
}
// register include with resolved path and its content
// memory of the resources will be freed by us on exit
void Context::register_resource(const Include& inc, const Resource& res, ParserState& prstate)
{
traces.push_back(Backtrace(prstate));
register_resource(inc, res);
traces.pop_back();
} }
// Add a new import to the context (called from `import_url`) // Add a new import to the context (called from `import_url`)
...@@ -350,7 +359,7 @@ namespace Sass { ...@@ -350,7 +359,7 @@ namespace Sass {
for (size_t i = 0, L = resolved.size(); i < L; ++i) for (size_t i = 0, L = resolved.size(); i < L; ++i)
{ msg_stream << " " << resolved[i].imp_path << "\n"; } { msg_stream << " " << resolved[i].imp_path << "\n"; }
msg_stream << "Please delete or rename all but one of these files." << "\n"; msg_stream << "Please delete or rename all but one of these files." << "\n";
error(msg_stream.str(), pstate); error(msg_stream.str(), pstate, traces);
} }
// process the resolved entry // process the resolved entry
...@@ -362,7 +371,7 @@ namespace Sass { ...@@ -362,7 +371,7 @@ namespace Sass {
// the memory buffer returned must be freed by us! // the memory buffer returned must be freed by us!
if (char* contents = read_file(resolved[0].abs_path)) { if (char* contents = read_file(resolved[0].abs_path)) {
// register the newly resolved file resource // register the newly resolved file resource
register_resource(resolved[0], { contents, 0 }, &pstate); register_resource(resolved[0], { contents, 0 }, pstate);
// return resolved entry // return resolved entry
return resolved[0]; return resolved[0];
} }
...@@ -403,7 +412,7 @@ namespace Sass { ...@@ -403,7 +412,7 @@ namespace Sass {
const Importer importer(imp_path, ctx_path); const Importer importer(imp_path, ctx_path);
Include include(load_import(importer, pstate)); Include include(load_import(importer, pstate));
if (include.abs_path.empty()) { if (include.abs_path.empty()) {
error("File to import not found or unreadable: " + imp_path + ".\nParent style sheet: " + ctx_path, pstate); error("File to import not found or unreadable: " + imp_path + ".", pstate, traces);
} }
imp->incs().push_back(include); imp->incs().push_back(include);
} }
...@@ -448,9 +457,9 @@ namespace Sass { ...@@ -448,9 +457,9 @@ namespace Sass {
// handle error message passed back from custom importer // handle error message passed back from custom importer
// it may (or may not) override the line and column info // it may (or may not) override the line and column info
if (const char* err_message = sass_import_get_error_message(include_ent)) { if (const char* err_message = sass_import_get_error_message(include_ent)) {
if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate); if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate);
if (line == std::string::npos && column == std::string::npos) error(err_message, pstate); if (line == std::string::npos && column == std::string::npos) error(err_message, pstate, traces);
else error(err_message, ParserState(ctx_path, source, Position(line, column))); else error(err_message, ParserState(ctx_path, source, Position(line, column)), traces);
} }
// content for import was set // content for import was set
else if (source) { else if (source) {
...@@ -462,7 +471,7 @@ namespace Sass { ...@@ -462,7 +471,7 @@ namespace Sass {
// attach information to AST node // attach information to AST node
imp->incs().push_back(include); imp->incs().push_back(include);
// register the resource buffers // register the resource buffers
register_resource(include, { source, srcmap }, &pstate); register_resource(include, { source, srcmap }, pstate);
} }
// only a path was retuned // only a path was retuned
// try to load it like normal // try to load it like normal
...@@ -648,10 +657,9 @@ namespace Sass { ...@@ -648,10 +657,9 @@ namespace Sass {
for (size_t i = 0, S = c_functions.size(); i < S; ++i) for (size_t i = 0, S = c_functions.size(); i < S; ++i)
{ register_c_function(*this, &global, c_functions[i]); } { register_c_function(*this, &global, c_functions[i]); }
// create initial backtrace entry // create initial backtrace entry
Backtrace backtrace(0, ParserState("", 0), "");
// create crtp visitor objects // create crtp visitor objects
Expand expand(*this, &global, &backtrace); Expand expand(*this, &global);
Cssize cssize(*this, &backtrace); Cssize cssize(*this);
CheckNesting check_nesting; CheckNesting check_nesting;
// check nesting in all files // check nesting in all files
for (auto sheet : sheets) { for (auto sheet : sheets) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "environment.hpp" #include "environment.hpp"
#include "source_map.hpp" #include "source_map.hpp"
#include "subset_map.hpp" #include "subset_map.hpp"
#include "backtrace.hpp"
#include "output.hpp" #include "output.hpp"
#include "plugins.hpp" #include "plugins.hpp"
#include "file.hpp" #include "file.hpp"
...@@ -54,6 +55,7 @@ namespace Sass { ...@@ -54,6 +55,7 @@ namespace Sass {
Subset_Map subset_map; Subset_Map subset_map;
std::vector<Sass_Import_Entry> import_stack; std::vector<Sass_Import_Entry> import_stack;
std::vector<Sass_Callee> callee_stack; std::vector<Sass_Callee> callee_stack;
std::vector<Backtrace> traces;
struct Sass_Compiler* c_compiler; struct Sass_Compiler* c_compiler;
...@@ -94,7 +96,8 @@ namespace Sass { ...@@ -94,7 +96,8 @@ namespace Sass {
virtual char* render(Block_Obj root); virtual char* render(Block_Obj root);
virtual char* render_srcmap(); virtual char* render_srcmap();
void register_resource(const Include&, const Resource&, ParserState* = 0); void register_resource(const Include&, const Resource&);
void register_resource(const Include&, const Resource&, ParserState&);
std::vector<Include> find_includes(const Importer& import); std::vector<Include> find_includes(const Importer& import);
Include load_import(const Importer&, ParserState pstate); Include load_import(const Importer&, ParserState pstate);
......
...@@ -5,15 +5,14 @@ ...@@ -5,15 +5,14 @@
#include "cssize.hpp" #include "cssize.hpp"
#include "context.hpp" #include "context.hpp"
#include "backtrace.hpp"
namespace Sass { namespace Sass {
Cssize::Cssize(Context& ctx, Backtrace* bt) Cssize::Cssize(Context& ctx)
: ctx(ctx), : ctx(ctx),
traces(ctx.traces),
block_stack(std::vector<Block_Ptr>()), block_stack(std::vector<Block_Ptr>()),
p_stack(std::vector<Statement_Ptr>()), p_stack(std::vector<Statement_Ptr>())
backtrace(bt)
{ } { }
Statement_Ptr Cssize::parent() Statement_Ptr Cssize::parent()
...@@ -33,7 +32,10 @@ namespace Sass { ...@@ -33,7 +32,10 @@ namespace Sass {
Statement_Ptr Cssize::operator()(Trace_Ptr t) Statement_Ptr Cssize::operator()(Trace_Ptr t)
{ {
return t->block()->perform(this); traces.push_back(Backtrace(t->pstate()));
auto result = t->block()->perform(this);
traces.pop_back();
return result;
} }
Statement_Ptr Cssize::operator()(Declaration_Ptr d) Statement_Ptr Cssize::operator()(Declaration_Ptr d)
...@@ -149,7 +151,7 @@ namespace Sass { ...@@ -149,7 +151,7 @@ namespace Sass {
// this should protect us (at least a bit) from our mess // this should protect us (at least a bit) from our mess
// fixing this properly is harder that it should be ... // fixing this properly is harder that it should be ...
if (Cast<Statement>(bb) == NULL) { if (Cast<Statement>(bb) == NULL) {
error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate()); error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
} }
Ruleset_Obj rr = SASS_MEMORY_NEW(Ruleset, Ruleset_Obj rr = SASS_MEMORY_NEW(Ruleset,
r->pstate(), r->pstate(),
...@@ -161,7 +163,7 @@ namespace Sass { ...@@ -161,7 +163,7 @@ namespace Sass {
p_stack.pop_back(); p_stack.pop_back();
if (!rr->block()) { if (!rr->block()) {
error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate()); error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
} }
Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate()); Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());
......
...@@ -13,14 +13,14 @@ namespace Sass { ...@@ -13,14 +13,14 @@ namespace Sass {
class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> { class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> {
Context& ctx; Context& ctx;
Backtraces& traces;
std::vector<Block_Ptr> block_stack; std::vector<Block_Ptr> block_stack;
std::vector<Statement_Ptr> p_stack; std::vector<Statement_Ptr> p_stack;
Backtrace* backtrace;
Statement_Ptr fallback_impl(AST_Node_Ptr n); Statement_Ptr fallback_impl(AST_Node_Ptr n);
public: public:
Cssize(Context&, Backtrace*); Cssize(Context&);
~Cssize() { } ~Cssize() { }
Selector_List_Ptr selector(); Selector_List_Ptr selector();
......
...@@ -79,7 +79,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) ...@@ -79,7 +79,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
Trace_Ptr trace = Cast<Trace>(node); Trace_Ptr trace = Cast<Trace>(node);
std::cerr << ind << "Trace " << trace; std::cerr << ind << "Trace " << trace;
std::cerr << " (" << pstate_source_position(node) << ")" std::cerr << " (" << pstate_source_position(node) << ")"
<< " [name:" << trace->name() << "]" << " [name:" << trace->name() << ", type: " << trace->type() << "]"
<< std::endl; << std::endl;
debug_ast(trace->block(), ind + " ", env); debug_ast(trace->block(), ind + " ", env);
} else if (Cast<At_Root_Block>(node)) { } else if (Cast<At_Root_Block>(node)) {
...@@ -664,6 +664,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) ...@@ -664,6 +664,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
std::cerr << " (" << pstate_source_position(expression) << ")"; std::cerr << " (" << pstate_source_position(expression) << ")";
std::cerr << " " << expression->concrete_type(); std::cerr << " " << expression->concrete_type();
std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << pstate_source_position(node) << ")";
if (expression->css()) std::cerr << " [css]";
if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_delayed()) std::cerr << " [delayed]";
if (expression->is_interpolant()) std::cerr << " [is interpolant]"; if (expression->is_interpolant()) std::cerr << " [is interpolant]";
if (expression->has_interpolant()) std::cerr << " [has interpolant]"; if (expression->has_interpolant()) std::cerr << " [has interpolant]";
......
...@@ -10,19 +10,18 @@ namespace Sass { ...@@ -10,19 +10,18 @@ namespace Sass {
namespace Exception { namespace Exception {
Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack) Base::Base(ParserState pstate, std::string msg, Backtraces traces)
: std::runtime_error(msg), msg(msg), : std::runtime_error(msg), msg(msg),
prefix("Error"), pstate(pstate), prefix("Error"), pstate(pstate), traces(traces)
import_stack(import_stack)
{ } { }
InvalidSass::InvalidSass(ParserState pstate, std::string msg) InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg) : Base(pstate, msg, traces)
{ } { }
InvalidParent::InvalidParent(Selector_Ptr parent, Selector_Ptr selector) InvalidParent::InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector)
: Base(selector->pstate()), parent(parent), selector(selector) : Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector)
{ {
msg = "Invalid parent selector for \""; msg = "Invalid parent selector for \"";
msg += selector->to_string(Sass_Inspect_Options()); msg += selector->to_string(Sass_Inspect_Options());
...@@ -31,15 +30,15 @@ namespace Sass { ...@@ -31,15 +30,15 @@ namespace Sass {
msg += "\""; msg += "\"";
} }
InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, std::string name, const Argument_Ptr arg) InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg)
: Base(pstate), name(name), arg(arg) : Base(pstate, def_msg, traces), name(name), arg(arg)
{ {
msg = "Variable keyword argument map must have string keys.\n"; msg = "Variable keyword argument map must have string keys.\n";
msg += name + " is not a string in " + arg->to_string() + "."; msg += name + " is not a string in " + arg->to_string() + ".";
} }
InvalidArgumentType::InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value) InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value)
: Base(pstate), fn(fn), arg(arg), type(type), value(value) : Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value)
{ {
msg = arg + ": \""; msg = arg + ": \"";
if (value) msg += value->to_string(Sass_Inspect_Options()); if (value) msg += value->to_string(Sass_Inspect_Options());
...@@ -47,50 +46,24 @@ namespace Sass { ...@@ -47,50 +46,24 @@ namespace Sass {
msg += " for `" + fn + "'"; msg += " for `" + fn + "'";
} }
MissingArgument::MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype) MissingArgument::MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype)
: Base(pstate), fn(fn), arg(arg), fntype(fntype) : Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype)
{ {
msg = fntype + " " + fn; msg = fntype + " " + fn;
msg += " is missing argument "; msg += " is missing argument ";
msg += arg + "."; msg += arg + ".";
} }
InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack) InvalidSyntax::InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg, import_stack) : Base(pstate, msg, traces)
{ } { }
NestingLimitError::NestingLimitError(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack) NestingLimitError::NestingLimitError(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg, import_stack) : Base(pstate, msg, traces)
{ } { }
UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op) DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org)
: lhs(lhs), rhs(rhs), op(op) : Base(org.pstate(), def_msg, traces), dup(dup), org(org)
{
msg = def_op_msg + ": \"";
msg += lhs->to_string({ NESTED, 5 });
msg += " " + op + " ";
msg += rhs->to_string({ TO_SASS, 5 });
msg += "\".";
}
InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)
: UndefinedOperation(lhs, rhs, op)
{
msg = def_op_null_msg + ": \"";
msg += lhs->inspect();
msg += " " + op + " ";
msg += rhs->inspect();
msg += "\".";
}
ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)
: lhs(lhs), rhs(rhs)
{
msg = "divided by 0";
}
DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org)
: Base(org.pstate()), dup(dup), org(org)
{ {
msg = "Duplicate key "; msg = "Duplicate key ";
msg += dup.get_duplicate_key()->inspect(); msg += dup.get_duplicate_key()->inspect();
...@@ -99,8 +72,8 @@ namespace Sass { ...@@ -99,8 +72,8 @@ namespace Sass {
msg += ")."; msg += ").";
} }
TypeMismatch::TypeMismatch(const Expression& var, const std::string type) TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const std::string type)
: Base(var.pstate()), var(var), type(type) : Base(var.pstate(), def_msg, traces), var(var), type(type)
{ {
msg = var.to_string(); msg = var.to_string();
msg += " is not an "; msg += " is not an ";
...@@ -108,15 +81,15 @@ namespace Sass { ...@@ -108,15 +81,15 @@ namespace Sass {
msg += "."; msg += ".";
} }
InvalidValue::InvalidValue(const Expression& val) InvalidValue::InvalidValue(Backtraces traces, const Expression& val)
: Base(val.pstate()), val(val) : Base(val.pstate(), def_msg, traces), val(val)
{ {
msg = val.to_string(); msg = val.to_string();
msg += " isn't a valid CSS value."; msg += " isn't a valid CSS value.";
} }
StackError::StackError(const AST_Node& node) StackError::StackError(Backtraces traces, const AST_Node& node)
: Base(node.pstate()), node(node) : Base(node.pstate(), def_msg, traces), node(node)
{ {
msg = "stack level too deep"; msg = "stack level too deep";
} }
...@@ -139,19 +112,44 @@ namespace Sass { ...@@ -139,19 +112,44 @@ namespace Sass {
msg += "'."; msg += "'.";
} }
AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op) AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)
: lhs(lhs), rhs(rhs), op(op) : OperationError(), lhs(lhs), rhs(rhs), op(op)
{ {
msg = "Alpha channels must be equal: "; msg = "Alpha channels must be equal: ";
msg += lhs->to_string({ NESTED, 5 }); msg += lhs->to_string({ NESTED, 5 });
msg += " " + op + " "; msg += " " + sass_op_to_name(op) + " ";
msg += rhs->to_string({ NESTED, 5 }); msg += rhs->to_string({ NESTED, 5 });
msg += "."; msg += ".";
} }
ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)
: OperationError(), lhs(lhs), rhs(rhs)
{
msg = "divided by 0";
}
UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)
: OperationError(), lhs(lhs), rhs(rhs), op(op)
{
msg = def_op_msg + ": \"";
msg += lhs->to_string({ NESTED, 5 });
msg += " " + sass_op_to_name(op) + " ";
msg += rhs->to_string({ TO_SASS, 5 });
msg += "\".";
}
InvalidNullOperation::InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)
: UndefinedOperation(lhs, rhs, op)
{
msg = def_op_null_msg + ": \"";
msg += lhs->inspect();
msg += " " + sass_op_to_name(op) + " ";
msg += rhs->inspect();
msg += "\".";
}
SassValueError::SassValueError(ParserState pstate, OperationError& err) SassValueError::SassValueError(Backtraces traces, ParserState pstate, OperationError& err)
: Base(pstate, err.what()) : Base(pstate, err.what(), traces)
{ {
msg = err.what(); msg = err.what();
prefix = err.errtype(); prefix = err.errtype();
...@@ -162,7 +160,7 @@ namespace Sass { ...@@ -162,7 +160,7 @@ namespace Sass {
void warn(std::string msg, ParserState pstate) void warn(std::string msg, ParserState pstate)
{ {
std::cerr << "Warning: " << msg<< std::endl; std::cerr << "Warning: " << msg << std::endl;
} }
void warning(std::string msg, ParserState pstate) void warning(std::string msg, ParserState pstate)
...@@ -178,8 +176,6 @@ namespace Sass { ...@@ -178,8 +176,6 @@ namespace Sass {
void warn(std::string msg, ParserState pstate, Backtrace* bt) void warn(std::string msg, ParserState pstate, Backtrace* bt)
{ {
Backtrace top(bt, pstate, "");
msg += top.to_string();
warn(msg, pstate); warn(msg, pstate);
} }
...@@ -223,16 +219,17 @@ namespace Sass { ...@@ -223,16 +219,17 @@ namespace Sass {
std::cerr << "This will be an error in future versions of Sass." << std::endl; std::cerr << "This will be an error in future versions of Sass." << std::endl;
} }
void error(std::string msg, ParserState pstate) // should be replaced with error with backtraces
void coreError(std::string msg, ParserState pstate)
{ {
throw Exception::InvalidSyntax(pstate, msg); Backtraces traces;
throw Exception::InvalidSyntax(pstate, traces, msg);
} }
void error(std::string msg, ParserState pstate, Backtrace* bt) void error(std::string msg, ParserState pstate, Backtraces& traces)
{ {
Backtrace top(bt, pstate, ""); traces.push_back(Backtrace(pstate));
msg += "\n" + top.to_string(); throw Exception::InvalidSyntax(pstate, traces, msg);
error(msg, pstate);
} }
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include "position.hpp" #include "position.hpp"
#include "backtrace.hpp"
#include "ast_fwd_decl.hpp" #include "ast_fwd_decl.hpp"
#include "sass/functions.h" #include "sass/functions.h"
...@@ -25,9 +26,9 @@ namespace Sass { ...@@ -25,9 +26,9 @@ namespace Sass {
std::string prefix; std::string prefix;
public: public:
ParserState pstate; ParserState pstate;
std::vector<Sass_Import_Entry>* import_stack; Backtraces traces;
public: public:
Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0); Base(ParserState pstate, std::string msg, Backtraces traces);
virtual const char* errtype() const { return prefix.c_str(); } virtual const char* errtype() const { return prefix.c_str(); }
virtual const char* what() const throw() { return msg.c_str(); } virtual const char* what() const throw() { return msg.c_str(); }
virtual ~Base() throw() {}; virtual ~Base() throw() {};
...@@ -35,7 +36,7 @@ namespace Sass { ...@@ -35,7 +36,7 @@ namespace Sass {
class InvalidSass : public Base { class InvalidSass : public Base {
public: public:
InvalidSass(ParserState pstate, std::string msg); InvalidSass(ParserState pstate, Backtraces traces, std::string msg);
virtual ~InvalidSass() throw() {}; virtual ~InvalidSass() throw() {};
}; };
...@@ -44,7 +45,7 @@ namespace Sass { ...@@ -44,7 +45,7 @@ namespace Sass {
Selector_Ptr parent; Selector_Ptr parent;
Selector_Ptr selector; Selector_Ptr selector;
public: public:
InvalidParent(Selector_Ptr parent, Selector_Ptr selector); InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector);
virtual ~InvalidParent() throw() {}; virtual ~InvalidParent() throw() {};
}; };
...@@ -54,7 +55,7 @@ namespace Sass { ...@@ -54,7 +55,7 @@ namespace Sass {
std::string arg; std::string arg;
std::string fntype; std::string fntype;
public: public:
MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype); MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype);
virtual ~MissingArgument() throw() {}; virtual ~MissingArgument() throw() {};
}; };
...@@ -65,7 +66,7 @@ namespace Sass { ...@@ -65,7 +66,7 @@ namespace Sass {
std::string type; std::string type;
const Value_Ptr value; const Value_Ptr value;
public: public:
InvalidArgumentType(ParserState pstate, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0); InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value = 0);
virtual ~InvalidArgumentType() throw() {}; virtual ~InvalidArgumentType() throw() {};
}; };
...@@ -74,52 +75,28 @@ namespace Sass { ...@@ -74,52 +75,28 @@ namespace Sass {
std::string name; std::string name;
const Argument_Ptr arg; const Argument_Ptr arg;
public: public:
InvalidVarKwdType(ParserState pstate, std::string name, const Argument_Ptr arg = 0); InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg = 0);
virtual ~InvalidVarKwdType() throw() {}; virtual ~InvalidVarKwdType() throw() {};
}; };
class InvalidSyntax : public Base { class InvalidSyntax : public Base {
public: public:
InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0); InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg);
virtual ~InvalidSyntax() throw() {}; virtual ~InvalidSyntax() throw() {};
}; };
class NestingLimitError : public Base { class NestingLimitError : public Base {
public: public:
NestingLimitError(ParserState pstate, std::string msg = def_nesting_limit, std::vector<Sass_Import_Entry>* import_stack = 0); NestingLimitError(ParserState pstate, Backtraces traces, std::string msg = def_nesting_limit);
virtual ~NestingLimitError() throw() {}; virtual ~NestingLimitError() throw() {};
}; };
/* common virtual base class (has no pstate) */
class OperationError : public std::runtime_error {
protected:
std::string msg;
public:
OperationError(std::string msg = def_op_msg)
: std::runtime_error(msg), msg(msg)
{};
public:
virtual const char* errtype() const { return "Error"; }
virtual const char* what() const throw() { return msg.c_str(); }
virtual ~OperationError() throw() {};
};
class ZeroDivisionError : public OperationError {
protected:
const Expression& lhs;
const Expression& rhs;
public:
ZeroDivisionError(const Expression& lhs, const Expression& rhs);
virtual const char* errtype() const { return "ZeroDivisionError"; }
virtual ~ZeroDivisionError() throw() {};
};
class DuplicateKeyError : public Base { class DuplicateKeyError : public Base {
protected: protected:
const Map& dup; const Map& dup;
const Expression& org; const Expression& org;
public: public:
DuplicateKeyError(const Map& dup, const Expression& org); DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org);
virtual const char* errtype() const { return "Error"; } virtual const char* errtype() const { return "Error"; }
virtual ~DuplicateKeyError() throw() {}; virtual ~DuplicateKeyError() throw() {};
}; };
...@@ -129,7 +106,7 @@ namespace Sass { ...@@ -129,7 +106,7 @@ namespace Sass {
const Expression& var; const Expression& var;
const std::string type; const std::string type;
public: public:
TypeMismatch(const Expression& var, const std::string type); TypeMismatch(Backtraces traces, const Expression& var, const std::string type);
virtual const char* errtype() const { return "Error"; } virtual const char* errtype() const { return "Error"; }
virtual ~TypeMismatch() throw() {}; virtual ~TypeMismatch() throw() {};
}; };
...@@ -138,7 +115,7 @@ namespace Sass { ...@@ -138,7 +115,7 @@ namespace Sass {
protected: protected:
const Expression& val; const Expression& val;
public: public:
InvalidValue(const Expression& val); InvalidValue(Backtraces traces, const Expression& val);
virtual const char* errtype() const { return "Error"; } virtual const char* errtype() const { return "Error"; }
virtual ~InvalidValue() throw() {}; virtual ~InvalidValue() throw() {};
}; };
...@@ -147,11 +124,35 @@ namespace Sass { ...@@ -147,11 +124,35 @@ namespace Sass {
protected: protected:
const AST_Node& node; const AST_Node& node;
public: public:
StackError(const AST_Node& node); StackError(Backtraces traces, const AST_Node& node);
virtual const char* errtype() const { return "SystemStackError"; } virtual const char* errtype() const { return "SystemStackError"; }
virtual ~StackError() throw() {}; virtual ~StackError() throw() {};
}; };
/* common virtual base class (has no pstate or trace) */
class OperationError : public std::runtime_error {
protected:
std::string msg;
public:
OperationError(std::string msg = def_op_msg)
: std::runtime_error(msg), msg(msg)
{};
public:
virtual const char* errtype() const { return "Error"; }
virtual const char* what() const throw() { return msg.c_str(); }
virtual ~OperationError() throw() {};
};
class ZeroDivisionError : public OperationError {
protected:
const Expression& lhs;
const Expression& rhs;
public:
ZeroDivisionError(const Expression& lhs, const Expression& rhs);
virtual const char* errtype() const { return "ZeroDivisionError"; }
virtual ~ZeroDivisionError() throw() {};
};
class IncompatibleUnits : public OperationError { class IncompatibleUnits : public OperationError {
protected: protected:
// const Sass::UnitType lhs; // const Sass::UnitType lhs;
...@@ -166,16 +167,16 @@ namespace Sass { ...@@ -166,16 +167,16 @@ namespace Sass {
protected: protected:
Expression_Ptr_Const lhs; Expression_Ptr_Const lhs;
Expression_Ptr_Const rhs; Expression_Ptr_Const rhs;
const std::string op; const Sass_OP op;
public: public:
UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op); UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);
// virtual const char* errtype() const { return "Error"; } // virtual const char* errtype() const { return "Error"; }
virtual ~UndefinedOperation() throw() {}; virtual ~UndefinedOperation() throw() {};
}; };
class InvalidNullOperation : public UndefinedOperation { class InvalidNullOperation : public UndefinedOperation {
public: public:
InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op); InvalidNullOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);
virtual ~InvalidNullOperation() throw() {}; virtual ~InvalidNullOperation() throw() {};
}; };
...@@ -183,16 +184,16 @@ namespace Sass { ...@@ -183,16 +184,16 @@ namespace Sass {
protected: protected:
Expression_Ptr_Const lhs; Expression_Ptr_Const lhs;
Expression_Ptr_Const rhs; Expression_Ptr_Const rhs;
const std::string op; const Sass_OP op;
public: public:
AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op); AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op);
// virtual const char* errtype() const { return "Error"; } // virtual const char* errtype() const { return "Error"; }
virtual ~AlphaChannelsNotEqual() throw() {}; virtual ~AlphaChannelsNotEqual() throw() {};
}; };
class SassValueError : public Base { class SassValueError : public Base {
public: public:
SassValueError(ParserState pstate, OperationError& err); SassValueError(Backtraces traces, ParserState pstate, OperationError& err);
virtual ~SassValueError() throw() {}; virtual ~SassValueError() throw() {};
}; };
...@@ -207,8 +208,8 @@ namespace Sass { ...@@ -207,8 +208,8 @@ namespace Sass {
void deprecated_bind(std::string msg, ParserState pstate); void deprecated_bind(std::string msg, ParserState pstate);
// void deprecated(std::string msg, ParserState pstate, Backtrace* bt); // void deprecated(std::string msg, ParserState pstate, Backtrace* bt);
void error(std::string msg, ParserState pstate); void coreError(std::string msg, ParserState pstate);
void error(std::string msg, ParserState pstate, Backtrace* bt); void error(std::string msg, ParserState pstate, Backtraces& traces);
} }
......
...@@ -20,6 +20,7 @@ namespace Sass { ...@@ -20,6 +20,7 @@ namespace Sass {
public: public:
Expand& exp; Expand& exp;
Context& ctx; Context& ctx;
Backtraces& traces;
Eval(Expand& exp); Eval(Expand& exp);
~Eval(); ~Eval();
...@@ -31,7 +32,6 @@ namespace Sass { ...@@ -31,7 +32,6 @@ namespace Sass {
Boolean_Obj bool_false; Boolean_Obj bool_false;
Env* environment(); Env* environment();
Backtrace* backtrace();
Selector_List_Obj selector(); Selector_List_Obj selector();
// for evaluating function bodies // for evaluating function bodies
...@@ -91,22 +91,12 @@ namespace Sass { ...@@ -91,22 +91,12 @@ namespace Sass {
template <typename U> template <typename U>
Expression_Ptr fallback(U x) { return fallback_impl(x); } Expression_Ptr fallback(U x) { return fallback_impl(x); }
// -- only need to define two comparisons, and the rest can be implemented in terms of them
static bool eq(Expression_Obj, Expression_Obj);
static bool lt(Expression_Obj, Expression_Obj, std::string op);
// -- arithmetic on the combinations that matter
static Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate);
static Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate);
static Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate);
static Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate);
static Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool interpolant = false);
private: private:
void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false); void interpolation(Context& ctx, std::string& res, Expression_Obj ex, bool into_quotes, bool was_itpl = false);
}; };
Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate = ParserState("[AST]")); Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate = ParserState("[AST]"));
} }
......
...@@ -16,8 +16,9 @@ namespace Sass { ...@@ -16,8 +16,9 @@ namespace Sass {
// simple endless recursion protection // simple endless recursion protection
const size_t maxRecursion = 500; const size_t maxRecursion = 500;
Expand::Expand(Context& ctx, Env* env, Backtrace* bt, std::vector<Selector_List_Obj>* stack) Expand::Expand(Context& ctx, Env* env, std::vector<Selector_List_Obj>* stack)
: ctx(ctx), : ctx(ctx),
traces(ctx.traces),
eval(Eval(*this)), eval(Eval(*this)),
recursions(0), recursions(0),
in_keyframes(false), in_keyframes(false),
...@@ -27,8 +28,7 @@ namespace Sass { ...@@ -27,8 +28,7 @@ namespace Sass {
block_stack(std::vector<Block_Ptr>()), block_stack(std::vector<Block_Ptr>()),
call_stack(std::vector<AST_Node_Obj>()), call_stack(std::vector<AST_Node_Obj>()),
selector_stack(std::vector<Selector_List_Obj>()), selector_stack(std::vector<Selector_List_Obj>()),
media_block_stack(std::vector<Media_Block_Ptr>()), media_block_stack(std::vector<Media_Block_Ptr>())
backtrace_stack(std::vector<Backtrace*>())
{ {
env_stack.push_back(0); env_stack.push_back(0);
env_stack.push_back(env); env_stack.push_back(env);
...@@ -37,8 +37,6 @@ namespace Sass { ...@@ -37,8 +37,6 @@ namespace Sass {
if (stack == NULL) { selector_stack.push_back(0); } if (stack == NULL) { selector_stack.push_back(0); }
else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); } else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }
media_block_stack.push_back(0); media_block_stack.push_back(0);
backtrace_stack.push_back(0);
backtrace_stack.push_back(bt);
} }
Env* Expand::environment() Env* Expand::environment()
...@@ -55,13 +53,6 @@ namespace Sass { ...@@ -55,13 +53,6 @@ namespace Sass {
return 0; return 0;
} }
Backtrace* Expand::backtrace()
{
if (backtrace_stack.size() > 0)
return backtrace_stack.back();
return 0;
}
// blocks create new variable scopes // blocks create new variable scopes
Block_Ptr Expand::operator()(Block_Ptr b) Block_Ptr Expand::operator()(Block_Ptr b)
{ {
...@@ -126,7 +117,7 @@ namespace Sass { ...@@ -126,7 +117,7 @@ namespace Sass {
Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header); Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);
if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue; if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue;
std::string sel_str(complex_selector->to_string(ctx.c_options)); std::string sel_str(complex_selector->to_string(ctx.c_options));
error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), backtrace()); error("Base-level rules cannot contain the parent-selector-referencing character '&'.", header->pstate(), traces);
} }
tail = tail->tail(); tail = tail->tail();
} }
...@@ -136,7 +127,7 @@ namespace Sass { ...@@ -136,7 +127,7 @@ namespace Sass {
else { else {
if (sel->length() == 0 || sel->has_parent_ref()) { if (sel->length() == 0 || sel->has_parent_ref()) {
if (sel->has_real_parent_ref() && !has_parent_selector) { if (sel->has_real_parent_ref() && !has_parent_selector) {
error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), backtrace()); error("Base-level rules cannot contain the parent-selector-referencing character '&'.", sel->pstate(), traces);
} }
} }
} }
...@@ -189,7 +180,7 @@ namespace Sass { ...@@ -189,7 +180,7 @@ namespace Sass {
std::string str_mq(mq->to_string(ctx.c_options)); std::string str_mq(mq->to_string(ctx.c_options));
char* str = sass_copy_c_string(str_mq.c_str()); char* str = sass_copy_c_string(str_mq.c_str());
ctx.strings.push_back(str); ctx.strings.push_back(str);
Parser p(Parser::from_c_str(str, ctx, mq->pstate())); Parser p(Parser::from_c_str(str, ctx, traces, mq->pstate()));
mq = p.parse_media_queries(); // re-assign now mq = p.parse_media_queries(); // re-assign now
cpy->media_queries(mq); cpy->media_queries(mq);
media_block_stack.push_back(cpy); media_block_stack.push_back(cpy);
...@@ -349,10 +340,11 @@ namespace Sass { ...@@ -349,10 +340,11 @@ namespace Sass {
Statement_Ptr Expand::operator()(Import_Stub_Ptr i) Statement_Ptr Expand::operator()(Import_Stub_Ptr i)
{ {
traces.push_back(Backtrace(i->pstate()));
// get parent node from call stack // get parent node from call stack
AST_Node_Obj parent = call_stack.back(); AST_Node_Obj parent = call_stack.back();
if (Cast<Block>(parent) == NULL) { if (Cast<Block>(parent) == NULL) {
error("Import directives may not be used within control directives or mixins.", i->pstate()); error("Import directives may not be used within control directives or mixins.", i->pstate(), traces);
} }
// we don't seem to need that actually afterall // we don't seem to need that actually afterall
Sass_Import_Entry import = sass_make_import( Sass_Import_Entry import = sass_make_import(
...@@ -361,10 +353,18 @@ namespace Sass { ...@@ -361,10 +353,18 @@ namespace Sass {
0, 0 0, 0
); );
ctx.import_stack.push_back(import); ctx.import_stack.push_back(import);
Block_Obj trace_block = SASS_MEMORY_NEW(Block, i->pstate());
Trace_Obj trace = SASS_MEMORY_NEW(Trace, i->pstate(), i->imp_path(), trace_block, 'i');
block_stack.back()->append(trace);
block_stack.push_back(trace_block);
const std::string& abs_path(i->resource().abs_path); const std::string& abs_path(i->resource().abs_path);
append_block(ctx.sheets.at(abs_path).root); append_block(ctx.sheets.at(abs_path).root);
sass_delete_import(ctx.import_stack.back()); sass_delete_import(ctx.import_stack.back());
ctx.import_stack.pop_back(); ctx.import_stack.pop_back();
block_stack.pop_back();
traces.pop_back();
return 0; return 0;
} }
...@@ -428,11 +428,13 @@ namespace Sass { ...@@ -428,11 +428,13 @@ namespace Sass {
std::string variable(f->variable()); std::string variable(f->variable());
Expression_Obj low = f->lower_bound()->perform(&eval); Expression_Obj low = f->lower_bound()->perform(&eval);
if (low->concrete_type() != Expression::NUMBER) { if (low->concrete_type() != Expression::NUMBER) {
throw Exception::TypeMismatch(*low, "integer"); traces.push_back(Backtrace(low->pstate()));
throw Exception::TypeMismatch(traces, *low, "integer");
} }
Expression_Obj high = f->upper_bound()->perform(&eval); Expression_Obj high = f->upper_bound()->perform(&eval);
if (high->concrete_type() != Expression::NUMBER) { if (high->concrete_type() != Expression::NUMBER) {
throw Exception::TypeMismatch(*high, "integer"); traces.push_back(Backtrace(high->pstate()));
throw Exception::TypeMismatch(traces, *high, "integer");
} }
Number_Obj sass_start = Cast<Number>(low); Number_Obj sass_start = Cast<Number>(low);
Number_Obj sass_end = Cast<Number>(high); Number_Obj sass_end = Cast<Number>(high);
...@@ -441,7 +443,7 @@ namespace Sass { ...@@ -441,7 +443,7 @@ namespace Sass {
std::stringstream msg; msg << "Incompatible units: '" std::stringstream msg; msg << "Incompatible units: '"
<< sass_start->unit() << "' and '" << sass_start->unit() << "' and '"
<< sass_end->unit() << "'."; << sass_end->unit() << "'.";
error(msg.str(), low->pstate(), backtrace()); error(msg.str(), low->pstate(), traces);
} }
double start = sass_start->value(); double start = sass_start->value();
double end = sass_end->value(); double end = sass_end->value();
...@@ -579,7 +581,7 @@ namespace Sass { ...@@ -579,7 +581,7 @@ namespace Sass {
Statement_Ptr Expand::operator()(Return_Ptr r) Statement_Ptr Expand::operator()(Return_Ptr r)
{ {
error("@return may only be used within a function", r->pstate(), backtrace()); error("@return may only be used within a function", r->pstate(), traces);
return 0; return 0;
} }
...@@ -593,7 +595,7 @@ namespace Sass { ...@@ -593,7 +595,7 @@ namespace Sass {
if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) { if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {
if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others
std::string sel_str(complex_selector->to_string(ctx.c_options)); std::string sel_str(complex_selector->to_string(ctx.c_options));
error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), backtrace()); error("Can't extend " + sel_str + ": can't extend parent selectors", header->pstate(), traces);
} }
tail = tail->tail(); tail = tail->tail();
} }
...@@ -607,7 +609,7 @@ namespace Sass { ...@@ -607,7 +609,7 @@ namespace Sass {
Complex_Selector_Obj c = complex_sel; Complex_Selector_Obj c = complex_sel;
if (!c->head() || c->tail()) { if (!c->head() || c->tail()) {
std::string sel_str(contextualized->to_string(ctx.c_options)); std::string sel_str(contextualized->to_string(ctx.c_options));
error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), backtrace()); error("Can't extend " + sel_str + ": can't extend nested selectors", c->pstate(), traces);
} }
Compound_Selector_Obj target = c->head(); Compound_Selector_Obj target = c->head();
if (contextualized->is_optional()) target->is_optional(true); if (contextualized->is_optional()) target->is_optional(true);
...@@ -694,7 +696,7 @@ namespace Sass { ...@@ -694,7 +696,7 @@ namespace Sass {
Statement_Ptr Expand::operator()(Mixin_Call_Ptr c) Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)
{ {
if (recursions > maxRecursion) { if (recursions > maxRecursion) {
throw Exception::StackError(*c); throw Exception::StackError(traces, *c);
} }
recursions ++; recursions ++;
...@@ -702,19 +704,19 @@ namespace Sass { ...@@ -702,19 +704,19 @@ namespace Sass {
Env* env = environment(); Env* env = environment();
std::string full_name(c->name() + "[m]"); std::string full_name(c->name() + "[m]");
if (!env->has(full_name)) { if (!env->has(full_name)) {
error("no mixin named " + c->name(), c->pstate(), backtrace()); error("no mixin named " + c->name(), c->pstate(), traces);
} }
Definition_Obj def = Cast<Definition>((*env)[full_name]); Definition_Obj def = Cast<Definition>((*env)[full_name]);
Block_Obj body = def->block(); Block_Obj body = def->block();
Parameters_Obj params = def->parameters(); Parameters_Obj params = def->parameters();
if (c->block() && c->name() != "@content" && !body->has_content()) { if (c->block() && c->name() != "@content" && !body->has_content()) {
error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), backtrace()); error("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces);
} }
Expression_Obj rv = c->arguments()->perform(&eval); Expression_Obj rv = c->arguments()->perform(&eval);
Arguments_Obj args = Cast<Arguments>(rv); Arguments_Obj args = Cast<Arguments>(rv);
Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`"); std::string msg(", in mixin `" + c->name() + "`");
backtrace_stack.push_back(&new_bt); traces.push_back(Backtrace(c->pstate(), msg));
ctx.callee_stack.push_back({ ctx.callee_stack.push_back({
c->name().c_str(), c->name().c_str(),
c->pstate().path, c->pstate().path,
...@@ -758,9 +760,9 @@ namespace Sass { ...@@ -758,9 +760,9 @@ namespace Sass {
block_stack.pop_back(); block_stack.pop_back();
env->del_global("is_in_mixin"); env->del_global("is_in_mixin");
env_stack.pop_back();
backtrace_stack.pop_back();
ctx.callee_stack.pop_back(); ctx.callee_stack.pop_back();
env_stack.pop_back();
traces.pop_back();
recursions --; recursions --;
return trace.detach(); return trace.detach();
...@@ -795,7 +797,7 @@ namespace Sass { ...@@ -795,7 +797,7 @@ namespace Sass {
{ {
std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name(); std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();
String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err); String_Quoted_Obj msg = SASS_MEMORY_NEW(String_Quoted, ParserState("[WARN]"), err);
error("unknown internal error; please contact the LibSass maintainers", n->pstate(), backtrace()); error("unknown internal error; please contact the LibSass maintainers", n->pstate(), traces);
return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg); return SASS_MEMORY_NEW(Warning, ParserState("[WARN]"), msg);
} }
......
...@@ -20,9 +20,9 @@ namespace Sass { ...@@ -20,9 +20,9 @@ namespace Sass {
Env* environment(); Env* environment();
Selector_List_Obj selector(); Selector_List_Obj selector();
Backtrace* backtrace();
Context& ctx; Context& ctx;
Backtraces& traces;
Eval eval; Eval eval;
size_t recursions; size_t recursions;
bool in_keyframes; bool in_keyframes;
...@@ -35,7 +35,6 @@ namespace Sass { ...@@ -35,7 +35,6 @@ namespace Sass {
std::vector<AST_Node_Obj> call_stack; std::vector<AST_Node_Obj> call_stack;
std::vector<Selector_List_Obj> selector_stack; std::vector<Selector_List_Obj> selector_stack;
std::vector<Media_Block_Ptr> media_block_stack; std::vector<Media_Block_Ptr> media_block_stack;
std::vector<Backtrace*> backtrace_stack;
Boolean_Obj bool_true; Boolean_Obj bool_true;
...@@ -45,7 +44,7 @@ namespace Sass { ...@@ -45,7 +44,7 @@ namespace Sass {
void expand_selector_list(Selector_Obj, Selector_List_Obj extender); void expand_selector_list(Selector_Obj, Selector_List_Obj extender);
public: public:
Expand(Context&, Env*, Backtrace*, std::vector<Selector_List_Obj>* stack = NULL); Expand(Context&, Env*, std::vector<Selector_List_Obj>* stack = NULL);
~Expand() { } ~Expand() { }
Block_Ptr operator()(Block_Ptr); Block_Ptr operator()(Block_Ptr);
......
...@@ -1718,7 +1718,7 @@ namespace Sass { ...@@ -1718,7 +1718,7 @@ namespace Sass {
err << "You may only @extend selectors within the same directive.\n"; err << "You may only @extend selectors within the same directive.\n";
err << "From \"@extend " << ext.second->to_string() << "\""; err << "From \"@extend " << ext.second->to_string() << "\"";
err << " on line " << pstate.line+1 << " of " << rel_path << "\n"; err << " on line " << pstate.line+1 << " of " << rel_path << "\n";
error(err.str(), selector->pstate()); error(err.str(), selector->pstate(), eval->exp.traces);
} }
if (entries.size() > 0) hasExtension = true; if (entries.size() > 0) hasExtension = true;
} }
...@@ -2098,7 +2098,7 @@ namespace Sass { ...@@ -2098,7 +2098,7 @@ namespace Sass {
error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n" error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"
"The selector \"" + str_ext + "\" was not found.\n" "The selector \"" + str_ext + "\" was not found.\n"
"Use \"@extend " + str_ext + " !optional\" if the" "Use \"@extend " + str_ext + " !optional\" if the"
" extend should be able to fail.", (ext ? ext->pstate() : NULL)); " extend should be able to fail.", (ext ? ext->pstate() : NULL), eval->exp.traces);
} }
} }
......
...@@ -56,6 +56,8 @@ namespace Sass { ...@@ -56,6 +56,8 @@ namespace Sass {
#ifndef _WIN32 #ifndef _WIN32
char wd[wd_len]; char wd[wd_len];
char* pwd = getcwd(wd, wd_len); char* pwd = getcwd(wd, wd_len);
// we should check error for more detailed info (e.g. ENOENT)
// http://man7.org/linux/man-pages/man2/getcwd.2.html#ERRORS
if (pwd == NULL) throw Exception::OperationError("cwd gone missing"); if (pwd == NULL) throw Exception::OperationError("cwd gone missing");
std::string cwd = pwd; std::string cwd = pwd;
#else #else
...@@ -74,11 +76,15 @@ namespace Sass { ...@@ -74,11 +76,15 @@ namespace Sass {
bool file_exists(const std::string& path) bool file_exists(const std::string& path)
{ {
#ifdef _WIN32 #ifdef _WIN32
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16 // windows unicode filepaths are encoded in utf16
std::string abspath(join_paths(get_cwd(), path)); std::string abspath(join_paths(get_cwd(), path));
std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath)); std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));
std::replace(wpath.begin(), wpath.end(), '/', '\\'); std::replace(wpath.begin(), wpath.end(), '/', '\\');
DWORD dwAttrib = GetFileAttributesW(wpath.c_str()); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);
if (rv > 32767) throw Exception::OperationError("Path is too long");
if (rv == 0) throw Exception::OperationError("Path could not be resolved");
DWORD dwAttrib = GetFileAttributesW(resolved);
return (dwAttrib != INVALID_FILE_ATTRIBUTES && return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY))); (!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)));
#else #else
...@@ -200,6 +206,13 @@ namespace Sass { ...@@ -200,6 +206,13 @@ namespace Sass {
if (is_absolute_path(r)) return r; if (is_absolute_path(r)) return r;
if (l[l.length()-1] != '/') l += '/'; if (l[l.length()-1] != '/') l += '/';
// this does a logical cleanup of the right hand path
// Note that this does collapse x/../y sections into y.
// This is by design. If /foo on your system is a symlink
// to /bar/baz, then /foo/../cd is actually /bar/cd,
// not /cd as a naive ../ removal would give you.
// will only work on leading double dot dirs on rhs
// therefore it is safe if lhs is already resolved cwd
while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) { while ((r.length() > 3) && ((r.substr(0, 3) == "../") || (r.substr(0, 3)) == "..\\")) {
size_t L = l.length(), pos = find_last_folder_separator(l, L - 2); size_t L = l.length(), pos = find_last_folder_separator(l, L - 2);
bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\'); bool is_slash = pos + 2 == L && (l[pos+1] == '/' || l[pos+1] == '\\');
...@@ -395,11 +408,15 @@ namespace Sass { ...@@ -395,11 +408,15 @@ namespace Sass {
#ifdef _WIN32 #ifdef _WIN32
BYTE* pBuffer; BYTE* pBuffer;
DWORD dwBytes; DWORD dwBytes;
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16 // windows unicode filepaths are encoded in utf16
std::string abspath(join_paths(get_cwd(), path)); std::string abspath(join_paths(get_cwd(), path));
std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath)); std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));
std::replace(wpath.begin(), wpath.end(), '/', '\\'); std::replace(wpath.begin(), wpath.end(), '/', '\\');
HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); DWORD rv = GetFullPathNameW(wpath.c_str(), 32767, resolved, NULL);
if (rv > 32767) throw Exception::OperationError("Path is too long");
if (rv == 0) throw Exception::OperationError("Path could not be resolved");
HANDLE hFile = CreateFileW(resolved, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) return 0; if (hFile == INVALID_HANDLE_VALUE) return 0;
DWORD dwFileLength = GetFileSize(hFile, NULL); DWORD dwFileLength = GetFileSize(hFile, NULL);
if (dwFileLength == INVALID_FILE_SIZE) return 0; if (dwFileLength == INVALID_FILE_SIZE) return 0;
......
...@@ -8,12 +8,12 @@ ...@@ -8,12 +8,12 @@
#include "sass/functions.h" #include "sass/functions.h"
#define BUILT_IN(name) Expression_Ptr \ #define BUILT_IN(name) Expression_Ptr \
name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtrace* backtrace, std::vector<Selector_List_Obj> selector_stack) name(Env& env, Env& d_env, Context& ctx, Signature sig, ParserState pstate, Backtraces traces, std::vector<Selector_List_Obj> selector_stack)
namespace Sass { namespace Sass {
struct Backtrace; struct Backtrace;
typedef const char* Signature; typedef const char* Signature;
typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*, std::vector<Selector_List_Obj>); typedef Expression_Ptr (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtraces, std::vector<Selector_List_Obj>);
Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx); Definition_Ptr make_native_function(Signature, Native_Function, Context& ctx);
Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx); Definition_Ptr make_c_function(Sass_Function_Entry c_func, Context& ctx);
......
#include "sass.hpp"
#include "operators.hpp"
namespace Sass {
namespace Operators {
inline double add(double x, double y) { return x + y; }
inline double sub(double x, double y) { return x - y; }
inline double mul(double x, double y) { return x * y; }
inline double div(double x, double y) { return x / y; } // x/0 checked by caller
inline double mod(double x, double y) { // x/0 checked by caller
if ((x > 0 && y < 0) || (x < 0 && y > 0)) {
double ret = std::fmod(x, y);
return ret ? ret + y : ret;
} else {
return std::fmod(x, y);
}
}
typedef double (*bop)(double, double);
bop ops[Sass_OP::NUM_OPS] = {
0, 0, // and, or
0, 0, 0, 0, 0, 0, // eq, neq, gt, gte, lt, lte
add, sub, mul, div, mod
};
/* static function, has no pstate or traces */
bool eq(Expression_Obj lhs, Expression_Obj rhs)
{
// operation is undefined if one is not a number
if (!lhs || !rhs) throw Exception::UndefinedOperation(lhs, rhs, Sass_OP::EQ);
// use compare operator from ast node
return *lhs == *rhs;
}
/* static function, throws OperationError, has no pstate or traces */
bool cmp(Expression_Obj lhs, Expression_Obj rhs, const Sass_OP op)
{
// can only compare numbers!?
Number_Obj l = Cast<Number>(lhs);
Number_Obj r = Cast<Number>(rhs);
// operation is undefined if one is not a number
if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);
// use compare operator from ast node
return *l < *r;
}
/* static functions, throws OperationError, has no pstate or traces */
bool lt(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LT); }
bool neq(Expression_Obj lhs, Expression_Obj rhs) { return eq(lhs, rhs) == false; }
bool gt(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GT) && neq(lhs, rhs); }
bool lte(Expression_Obj lhs, Expression_Obj rhs) { return cmp(lhs, rhs, Sass_OP::LTE) || eq(lhs, rhs); }
bool gte(Expression_Obj lhs, Expression_Obj rhs) { return !cmp(lhs, rhs, Sass_OP::GTE) || eq(lhs, rhs); }
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
enum Sass_OP op = operand.operand;
String_Quoted_Ptr lqstr = Cast<String_Quoted>(&lhs);
String_Quoted_Ptr rqstr = Cast<String_Quoted>(&rhs);
std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));
std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));
if (Cast<Null>(&lhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);
if (Cast<Null>(&rhs)) throw Exception::InvalidNullOperation(&lhs, &rhs, op);
std::string sep;
switch (op) {
case Sass_OP::ADD: sep = ""; break;
case Sass_OP::SUB: sep = "-"; break;
case Sass_OP::DIV: sep = "/"; break;
case Sass_OP::EQ: sep = "=="; break;
case Sass_OP::NEQ: sep = "!="; break;
case Sass_OP::LT: sep = "<"; break;
case Sass_OP::GT: sep = ">"; break;
case Sass_OP::LTE: sep = "<="; break;
case Sass_OP::GTE: sep = ">="; break;
default:
throw Exception::UndefinedOperation(&lhs, &rhs, op);
break;
}
if (op == Sass_OP::ADD) {
// create string that might be quoted on output (but do not unquote what we pass)
return SASS_MEMORY_NEW(String_Quoted, pstate, lstr + rstr, 0, false, true);
}
// add whitespace around operator
// but only if result is not delayed
if (sep != "" && delayed == false) {
if (operand.ws_before) sep = " " + sep;
if (operand.ws_after) sep = sep + " ";
}
if (op == Sass_OP::SUB || op == Sass_OP::DIV) {
if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);
if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);
}
return SASS_MEMORY_NEW(String_Constant, pstate, lstr + sep + rstr);
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_colors(enum Sass_OP op, const Color& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
if (lhs.a() != rhs.a()) {
throw Exception::AlphaChannelsNotEqual(&lhs, &rhs, op);
}
if (op == Sass_OP::DIV && (!rhs.r() || !rhs.g() || !rhs.b())) {
throw Exception::ZeroDivisionError(lhs, rhs);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lhs.r(), rhs.r()),
ops[op](lhs.g(), rhs.g()),
ops[op](lhs.b(), rhs.b()),
lhs.a());
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_numbers(enum Sass_OP op, const Number& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double lval = lhs.value();
double rval = rhs.value();
if (op == Sass_OP::DIV && rval == 0) {
std::string result(lval ? "Infinity" : "NaN");
return SASS_MEMORY_NEW(String_Quoted, pstate, result);
}
if (op == Sass_OP::MOD && rval == 0) {
throw Exception::ZeroDivisionError(lhs, rhs);
}
size_t l_n_units = lhs.numerators.size();
size_t l_d_units = lhs.numerators.size();
size_t r_n_units = rhs.denominators.size();
size_t r_d_units = rhs.denominators.size();
// optimize out the most common and simplest case
if (l_n_units == r_n_units && l_d_units == r_d_units) {
if (l_n_units + l_d_units <= 1 && r_n_units + r_d_units <= 1) {
if (lhs.numerators == rhs.numerators) {
if (lhs.denominators == rhs.denominators) {
Number_Ptr v = SASS_MEMORY_COPY(&lhs);
v->value(ops[op](lval, rval));
return v;
}
}
}
}
Number_Obj v = SASS_MEMORY_COPY(&lhs);
if (lhs.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
v->numerators = rhs.numerators;
v->denominators = rhs.denominators;
}
if (op == Sass_OP::MUL) {
v->value(ops[op](lval, rval));
v->numerators.insert(v->numerators.end(),
rhs.numerators.begin(), rhs.numerators.end()
);
v->denominators.insert(v->denominators.end(),
rhs.denominators.begin(), rhs.denominators.end()
);
v->reduce();
}
else if (op == Sass_OP::DIV) {
v->value(ops[op](lval, rval));
v->numerators.insert(v->numerators.end(),
rhs.denominators.begin(), rhs.denominators.end()
);
v->denominators.insert(v->denominators.end(),
rhs.numerators.begin(), rhs.numerators.end()
);
v->reduce();
}
else {
Number ln(lhs), rn(rhs);
ln.reduce(); rn.reduce();
double f(rn.convert_factor(ln));
v->value(ops[op](lval, rn.value() * f));
}
v->pstate(pstate);
return v.detach();
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_number_color(enum Sass_OP op, const Number& lhs, const Color& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double lval = lhs.value();
switch (op) {
case Sass_OP::ADD:
case Sass_OP::MUL: {
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lval, rhs.r()),
ops[op](lval, rhs.g()),
ops[op](lval, rhs.b()),
rhs.a());
}
case Sass_OP::SUB:
case Sass_OP::DIV: {
std::string color(rhs.to_string(opt));
return SASS_MEMORY_NEW(String_Quoted,
pstate,
lhs.to_string(opt)
+ sass_op_separator(op)
+ color);
}
default: break;
}
throw Exception::UndefinedOperation(&lhs, &rhs, op);
}
/* static function, throws OperationError, has no traces but optional pstate for returned value */
Value_Ptr op_color_number(enum Sass_OP op, const Color& lhs, const Number& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
double rval = rhs.value();
if (op == Sass_OP::DIV && rval == 0) {
// comparison of Fixnum with Float failed?
throw Exception::ZeroDivisionError(lhs, rhs);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lhs.r(), rval),
ops[op](lhs.g(), rval),
ops[op](lhs.b(), rval),
lhs.a());
}
}
}
#ifndef SASS_OPERATORS_H
#define SASS_OPERATORS_H
#include "values.hpp"
#include "sass/values.h"
namespace Sass {
namespace Operators {
// equality operator using AST Node operator==
bool eq(Expression_Obj, Expression_Obj);
bool neq(Expression_Obj, Expression_Obj);
// specific operators based on cmp and eq
bool lt(Expression_Obj, Expression_Obj);
bool gt(Expression_Obj, Expression_Obj);
bool lte(Expression_Obj, Expression_Obj);
bool gte(Expression_Obj, Expression_Obj);
// arithmetic for all the combinations that matter
Value_Ptr op_strings(Sass::Operand, Value&, Value&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);
Value_Ptr op_colors(enum Sass_OP, const Color&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);
Value_Ptr op_numbers(enum Sass_OP, const Number&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);
Value_Ptr op_number_color(enum Sass_OP, const Number&, const Color&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);
Value_Ptr op_color_number(enum Sass_OP, const Color&, const Number&, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed = false);
};
}
#endif
...@@ -19,13 +19,14 @@ namespace Sass { ...@@ -19,13 +19,14 @@ namespace Sass {
void Output::operator()(Number_Ptr n) void Output::operator()(Number_Ptr n)
{ {
// use values to_string facility
std::string res = n->to_string(opt);
// check for a valid unit here // check for a valid unit here
// includes result for reporting // includes result for reporting
if (!n->is_valid_css_unit()) { if (!n->is_valid_css_unit()) {
throw Exception::InvalidValue(*n); // should be handle in check_expression
throw Exception::InvalidValue({}, *n);
} }
// use values to_string facility
std::string res = n->to_string(opt);
// output the final token // output the final token
append_token(res, n); append_token(res, n);
} }
...@@ -37,8 +38,8 @@ namespace Sass { ...@@ -37,8 +38,8 @@ namespace Sass {
void Output::operator()(Map_Ptr m) void Output::operator()(Map_Ptr m)
{ {
std::string dbg(m->to_string(opt)); // should be handle in check_expression
error(dbg + " isn't a valid CSS value.", m->pstate()); throw Exception::InvalidValue({}, *m);
} }
OutputBuffer Output::get_buffer(void) OutputBuffer Output::get_buffer(void)
......
...@@ -45,22 +45,26 @@ namespace Sass { ...@@ -45,22 +45,26 @@ namespace Sass {
Position before_token; Position before_token;
Position after_token; Position after_token;
ParserState pstate; ParserState pstate;
Backtraces traces;
size_t indentation; size_t indentation;
size_t nestings; size_t nestings;
Token lexed; Token lexed;
Parser(Context& ctx, const ParserState& pstate) Parser(Context& ctx, const ParserState& pstate, Backtraces traces)
: ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(), : ParserState(pstate), ctx(ctx), block_stack(), stack(0), last_media_block(),
source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0), nestings(0) source(0), position(0), end(0), before_token(pstate), after_token(pstate),
{ stack.push_back(Scope::Root); } pstate(pstate), traces(traces), indentation(0), nestings(0)
{
stack.push_back(Scope::Root);
}
// static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]")); // static Parser from_string(const std::string& src, Context& ctx, ParserState pstate = ParserState("[STRING]"));
static Parser from_c_str(const char* src, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); static Parser from_c_str(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
static Parser from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0); static Parser from_c_str(const char* beg, const char* end, Context& ctx, Backtraces, ParserState pstate = ParserState("[CSTRING]"), const char* source = 0);
static Parser from_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0); static Parser from_token(Token t, Context& ctx, Backtraces, ParserState pstate = ParserState("[TOKEN]"), const char* source = 0);
// special static parsers to convert strings into certain selectors // special static parsers to convert strings into certain selectors
static Selector_List_Obj parse_selector(const char* src, Context& ctx, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0); static Selector_List_Obj parse_selector(const char* src, Context& ctx, Backtraces, ParserState pstate = ParserState("[SELECTOR]"), const char* source = 0);
#ifdef __clang__ #ifdef __clang__
...@@ -232,6 +236,7 @@ namespace Sass { ...@@ -232,6 +236,7 @@ namespace Sass {
#endif #endif
void error(std::string msg);
void error(std::string msg, Position pos); void error(std::string msg, Position pos);
// generate message with given and expected sample // generate message with given and expected sample
// text before and in the middle are configurable // text before and in the middle are configurable
...@@ -283,7 +288,7 @@ namespace Sass { ...@@ -283,7 +288,7 @@ namespace Sass {
Function_Call_Schema_Obj parse_function_call_schema(); Function_Call_Schema_Obj parse_function_call_schema();
String_Obj parse_url_function_string(); String_Obj parse_url_function_string();
String_Obj parse_url_function_argument(); String_Obj parse_url_function_argument();
String_Obj parse_interpolated_chunk(Token, bool constant = false); String_Obj parse_interpolated_chunk(Token, bool constant = false, bool css = true);
String_Obj parse_string(); String_Obj parse_string();
String_Constant_Obj parse_static_value(); String_Constant_Obj parse_static_value();
String_Schema_Obj parse_css_variable_value(bool top_level = true); String_Schema_Obj parse_css_variable_value(bool top_level = true);
......
...@@ -35,7 +35,6 @@ namespace Sass { ...@@ -35,7 +35,6 @@ namespace Sass {
catch (Exception::Base& e) { catch (Exception::Base& e) {
std::stringstream msg_stream; std::stringstream msg_stream;
std::string cwd(Sass::File::get_cwd()); std::string cwd(Sass::File::get_cwd());
std::string msg_prefix(e.errtype()); std::string msg_prefix(e.errtype());
bool got_newline = false; bool got_newline = false;
msg_stream << msg_prefix << ": "; msg_stream << msg_prefix << ": ";
...@@ -55,19 +54,16 @@ namespace Sass { ...@@ -55,19 +54,16 @@ namespace Sass {
++msg; ++msg;
} }
if (!got_newline) msg_stream << "\n"; if (!got_newline) msg_stream << "\n";
if (e.import_stack) {
for (size_t i = 1; i < e.import_stack->size() - 1; ++i) { if (e.traces.empty()) {
std::string path((*e.import_stack)[i]->imp_path); // we normally should have some traces, still here as a fallback
std::string rel_path(Sass::File::abs2rel(path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
msg_stream << std::string(msg_prefix.size() + 2, ' '); msg_stream << std::string(msg_prefix.size() + 2, ' ');
msg_stream << (i == 1 ? " on line " : " from line "); msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";
msg_stream << e.pstate.line + 1 << " of " << rel_path << "\n";
}
} }
else { else {
std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd)); std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
msg_stream << std::string(msg_prefix.size() + 2, ' '); msg_stream << traces_to_string(e.traces, " ");
msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";
} }
// now create the code trace (ToDo: maybe have util functions?) // now create the code trace (ToDo: maybe have util functions?)
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "util.hpp" #include "util.hpp"
#include "eval.hpp" #include "eval.hpp"
#include "values.hpp" #include "values.hpp"
#include "operators.hpp"
#include "sass/values.h" #include "sass/values.h"
#include "sass_values.hpp" #include "sass_values.hpp"
...@@ -299,12 +300,12 @@ extern "C" { ...@@ -299,12 +300,12 @@ extern "C" {
// see if it's a relational expression // see if it's a relational expression
switch(op) { switch(op) {
case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs)); case Sass_OP::EQ: return sass_make_boolean(Operators::eq(lhs, rhs));
case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs)); case Sass_OP::NEQ: return sass_make_boolean(Operators::neq(lhs, rhs));
case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs, "gt") && !Eval::eq(lhs, rhs)); case Sass_OP::GT: return sass_make_boolean(Operators::gt(lhs, rhs));
case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs, "gte")); case Sass_OP::GTE: return sass_make_boolean(Operators::gte(lhs, rhs));
case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs, "lt")); case Sass_OP::LT: return sass_make_boolean(Operators::lt(lhs, rhs));
case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs, "lte") || Eval::eq(lhs, rhs)); case Sass_OP::LTE: return sass_make_boolean(Operators::lte(lhs, rhs));
case Sass_OP::AND: return ast_node_to_sass_value(lhs->is_false() ? lhs : rhs); case Sass_OP::AND: return ast_node_to_sass_value(lhs->is_false() ? lhs : rhs);
case Sass_OP::OR: return ast_node_to_sass_value(lhs->is_false() ? rhs : lhs); case Sass_OP::OR: return ast_node_to_sass_value(lhs->is_false() ? rhs : lhs);
default: break; default: break;
...@@ -313,27 +314,27 @@ extern "C" { ...@@ -313,27 +314,27 @@ extern "C" {
if (sass_value_is_number(a) && sass_value_is_number(b)) { if (sass_value_is_number(a) && sass_value_is_number(b)) {
Number_Ptr_Const l_n = Cast<Number>(lhs); Number_Ptr_Const l_n = Cast<Number>(lhs);
Number_Ptr_Const r_n = Cast<Number>(rhs); Number_Ptr_Const r_n = Cast<Number>(rhs);
rv = Eval::op_numbers(op, *l_n, *r_n, options, l_n->pstate()); rv = Operators::op_numbers(op, *l_n, *r_n, options, l_n->pstate());
} }
else if (sass_value_is_number(a) && sass_value_is_color(a)) { else if (sass_value_is_number(a) && sass_value_is_color(a)) {
Number_Ptr_Const l_n = Cast<Number>(lhs); Number_Ptr_Const l_n = Cast<Number>(lhs);
Color_Ptr_Const r_c = Cast<Color>(rhs); Color_Ptr_Const r_c = Cast<Color>(rhs);
rv = Eval::op_number_color(op, *l_n, *r_c, options, l_n->pstate()); rv = Operators::op_number_color(op, *l_n, *r_c, options, l_n->pstate());
} }
else if (sass_value_is_color(a) && sass_value_is_number(b)) { else if (sass_value_is_color(a) && sass_value_is_number(b)) {
Color_Ptr_Const l_c = Cast<Color>(lhs); Color_Ptr_Const l_c = Cast<Color>(lhs);
Number_Ptr_Const r_n = Cast<Number>(rhs); Number_Ptr_Const r_n = Cast<Number>(rhs);
rv = Eval::op_color_number(op, *l_c, *r_n, options, l_c->pstate()); rv = Operators::op_color_number(op, *l_c, *r_n, options, l_c->pstate());
} }
else if (sass_value_is_color(a) && sass_value_is_color(b)) { else if (sass_value_is_color(a) && sass_value_is_color(b)) {
Color_Ptr_Const l_c = Cast<Color>(lhs); Color_Ptr_Const l_c = Cast<Color>(lhs);
Color_Ptr_Const r_c = Cast<Color>(rhs); Color_Ptr_Const r_c = Cast<Color>(rhs);
rv = Eval::op_colors(op, *l_c, *r_c, options, l_c->pstate()); rv = Operators::op_colors(op, *l_c, *r_c, options, l_c->pstate());
} }
else /* convert other stuff to string and apply operation */ { else /* convert other stuff to string and apply operation */ {
Value_Ptr l_v = Cast<Value>(lhs); Value_Ptr l_v = Cast<Value>(lhs);
Value_Ptr r_v = Cast<Value>(rhs); Value_Ptr r_v = Cast<Value>(rhs);
rv = Eval::op_strings(op, *l_v, *r_v, options, l_v->pstate()); rv = Operators::op_strings(op, *l_v, *r_v, options, l_v->pstate());
} }
// ToDo: maybe we should should return null value? // ToDo: maybe we should should return null value?
......
...@@ -96,8 +96,9 @@ namespace Sass { ...@@ -96,8 +96,9 @@ namespace Sass {
} }
// read css string (handle multiline DELIM) // read css string (handle multiline DELIM)
std::string read_css_string(const std::string& str) std::string read_css_string(const std::string& str, bool css)
{ {
if (!css) return str;
std::string out(""); std::string out("");
bool esc = false; bool esc = false;
for (auto i : str) { for (auto i : str) {
...@@ -180,6 +181,23 @@ namespace Sass { ...@@ -180,6 +181,23 @@ namespace Sass {
return out; return out;
} }
std::string escape_string(const std::string& str)
{
std::string out("");
for (auto i : str) {
if (i == '\n') {
out += "\\n";
} else if (i == '\r') {
out += "\\r";
} else if (i == '\t') {
out += "\\t";
} else {
out += i;
}
}
return out;
}
std::string comment_to_string(const std::string& text) std::string comment_to_string(const std::string& text)
{ {
std::string str = ""; std::string str = "";
......
...@@ -22,11 +22,12 @@ namespace Sass { ...@@ -22,11 +22,12 @@ namespace Sass {
const char* safe_str(const char *, const char* = ""); const char* safe_str(const char *, const char* = "");
void free_string_array(char **); void free_string_array(char **);
char **copy_strings(const std::vector<std::string>&, char ***, int = 0); char **copy_strings(const std::vector<std::string>&, char ***, int = 0);
std::string read_css_string(const std::string& str); std::string read_css_string(const std::string& str, bool css = true);
std::string evacuate_escapes(const std::string& str); std::string evacuate_escapes(const std::string& str);
std::string string_to_output(const std::string& str); std::string string_to_output(const std::string& str);
std::string comment_to_string(const std::string& text); std::string comment_to_string(const std::string& text);
std::string read_hex_escapes(const std::string& str); std::string read_hex_escapes(const std::string& str);
std::string escape_string(const std::string& str);
void newline_to_space(std::string& str); void newline_to_space(std::string& str);
std::string quote(const std::string&, char q = 0); std::string quote(const std::string&, char q = 0);
......
...@@ -97,6 +97,8 @@ ...@@ -97,6 +97,8 @@
<ClCompile Include="$(LIBSASS_SRC_DIR)\plugins.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\plugins.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\position.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\position.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\prelexer.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\prelexer.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\backtrace.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\operators.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\remove_placeholders.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\remove_placeholders.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\sass.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\sass.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\sass_context.cpp" /> <ClCompile Include="$(LIBSASS_SRC_DIR)\sass_context.cpp" />
......
...@@ -302,6 +302,12 @@ ...@@ -302,6 +302,12 @@
<ClCompile Include="$(LIBSASS_SRC_DIR)\prelexer.cpp"> <ClCompile Include="$(LIBSASS_SRC_DIR)\prelexer.cpp">
<Filter>Sources</Filter> <Filter>Sources</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="$(LIBSASS_SRC_DIR)\backtrace.cpp">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="$(LIBSASS_SRC_DIR)\operators.cpp">
<Filter>Sources</Filter>
</ClCompile>
<ClCompile Include="$(LIBSASS_SRC_DIR)\remove_placeholders.cpp"> <ClCompile Include="$(LIBSASS_SRC_DIR)\remove_placeholders.cpp">
<Filter>Sources</Filter> <Filter>Sources</Filter>
</ClCompile> </ClCompile>
......
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