Commit 84754d53 by xzyfer Committed by Michael Mifsud

Bump LibSass 3.5.2

Fixes #2287
parent 7648fc46
{
"name": "node-sass",
"version": "4.8.2",
"libsass": "3.5.1",
"libsass": "3.5.2",
"description": "Wrapper around libsass",
"license": "MIT",
"bugs": "https://github.com/sass/node-sass/issues",
......
......@@ -13,6 +13,7 @@
'sources': [
'libsass/src/ast.cpp',
'libsass/src/ast_fwd_decl.cpp',
'libsass/src/backtrace.cpp',
'libsass/src/base64vlq.cpp',
'libsass/src/bind.cpp',
'libsass/src/cencode.c',
......@@ -35,6 +36,8 @@
'libsass/src/listize.cpp',
'libsass/src/memory/SharedPtr.cpp',
'libsass/src/node.cpp',
'libsass/src/operators.cpp',
'libsass/src/operators.hpp',
'libsass/src/output.cpp',
'libsass/src/parser.cpp',
'libsass/src/plugins.cpp',
......
......@@ -41,6 +41,8 @@ SOURCES = \
sass_context.cpp \
sass_functions.cpp \
sass2scss.cpp \
backtrace.cpp \
operators.cpp \
to_c.cpp \
to_value.cpp \
source_map.cpp \
......
......@@ -837,7 +837,7 @@ namespace Sass {
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;
}
......@@ -1171,7 +1171,7 @@ namespace Sass {
// check if we need to append some headers
// then we need to check for the combinator
// 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();
......@@ -1185,7 +1185,8 @@ namespace Sass {
// append old headers
if (h && h->length()) {
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()) {
Compound_Selector_Obj rh = last()->head();
size_t i;
......@@ -1258,21 +1259,21 @@ namespace Sass {
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;
Selector_List_Ptr ss = SASS_MEMORY_NEW(Selector_List, pstate());
Selector_List_Ptr ps = pstack.back();
for (size_t pi = 0, pL = ps->length(); pi < pL; ++pi) {
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);
}
}
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();
Compound_Selector_Obj head = this->head();
......@@ -1285,7 +1286,7 @@ namespace Sass {
}
// 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) {
......@@ -1331,7 +1332,7 @@ namespace Sass {
// keep old parser state
s->pstate(pstate());
// append new tail
s->append(ss);
s->append(ss, traces);
retval->append(s);
}
}
......@@ -1346,7 +1347,8 @@ namespace Sass {
// this is only if valid if the parent has no trailing op
// otherwise we cannot append more simple selectors to head
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);
Compound_Selector_Obj h = SASS_MEMORY_COPY(head_);
......@@ -1369,7 +1371,7 @@ namespace Sass {
// keep old parser state
s->pstate(pstate());
// append new tail
s->append(ss);
s->append(ss, traces);
retval->append(s);
}
}
......@@ -1406,7 +1408,7 @@ namespace Sass {
for (Simple_Selector_Obj ss : head->elements()) {
if (Wrapped_Selector_Ptr ws = Cast<Wrapped_Selector>(ss)) {
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 {
}
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());
......@@ -1766,31 +1768,31 @@ namespace Sass {
{
if (!a->name().empty()) {
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);
}
else if (a->is_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_) {
error("only keyword arguments may follow variable arguments", a->pstate());
coreError("only keyword arguments may follow variable arguments", a->pstate());
}
has_rest_argument(true);
}
else if (a->is_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);
}
else {
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()) {
error("ordinal arguments must precede named arguments", a->pstate());
coreError("ordinal arguments must precede named arguments", a->pstate());
}
}
}
......@@ -1907,6 +1909,7 @@ namespace Sass {
l.normalize(); r.normalize();
Units &lhs_unit = l, &rhs_unit = r;
if (!(lhs_unit == rhs_unit)) {
/* ToDo: do we always get usefull backtraces? */
throw Exception::IncompatibleUnits(rhs, *this);
}
return lhs_unit < rhs_unit ||
......
......@@ -572,13 +572,15 @@ namespace Sass {
// Trace.
/////////////////
class Trace : public Has_Block {
ADD_CONSTREF(char, type)
ADD_CONSTREF(std::string, name)
public:
Trace(ParserState pstate, std::string n, Block_Obj b = 0)
: Has_Block(pstate, b), name_(n)
Trace(ParserState pstate, std::string n, Block_Obj b = 0, char type = 'm')
: Has_Block(pstate, b), type_(type), name_(n)
{ }
Trace(const Trace* ptr)
: Has_Block(ptr),
type_(ptr->type_),
name_(ptr->name_)
{ }
ATTACH_AST_OPERATIONS(Trace)
......@@ -943,7 +945,7 @@ namespace Sass {
/////////////////////////////////////////////////////////////////////////////
struct Backtrace;
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>);
class Definition : public Has_Block {
public:
enum Type { MIXIN, FUNCTION };
......@@ -1184,6 +1186,27 @@ namespace Sass {
}
}
inline static const std::string sass_op_separator(enum Sass_OP op) {
switch (op) {
case AND: return "&&";
case OR: return "||";
case EQ: return "==";
case NEQ: return "!=";
case GT: return ">";
case GTE: return ">=";
case LT: return "<";
case LTE: return "<=";
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
case DIV: return "/";
case MOD: return "%";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
}
//////////////////////////////////////////////////////////////////////////
// Binary expressions. Represents logical, relational, and arithmetic
// operations. Templatized to avoid large switch statements and repetitive
......@@ -1208,44 +1231,10 @@ namespace Sass {
hash_(ptr->hash_)
{ }
const std::string type_name() {
switch (optype()) {
case AND: return "and";
case OR: return "or";
case EQ: return "eq";
case NEQ: return "neq";
case GT: return "gt";
case GTE: return "gte";
case LT: return "lt";
case LTE: return "lte";
case ADD: return "add";
case SUB: return "sub";
case MUL: return "mul";
case DIV: return "div";
case MOD: return "mod";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
return sass_op_to_name(optype());
}
const std::string separator() {
switch (optype()) {
case AND: return "&&";
case OR: return "||";
case EQ: return "==";
case NEQ: return "!=";
case GT: return ">";
case GTE: return ">=";
case LT: return "<";
case LTE: return "<=";
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
case DIV: return "/";
case MOD: return "%";
// this is only used internally!
case NUM_OPS: return "[OPS]";
default: return "invalid";
}
return sass_op_separator(optype());
}
bool is_left_interpolant(void) const;
bool is_right_interpolant(void) const;
......@@ -1360,7 +1349,7 @@ namespace Sass {
: Expression(pstate), value_(val), name_(n), is_rest_argument_(rest), is_keyword_argument_(keyword), hash_(0)
{
if (!name_.empty() && is_rest_argument_) {
error("variable-length argument may not be passed by name", pstate_);
coreError("variable-length argument may not be passed by name", pstate_);
}
}
Argument(const Argument* ptr)
......@@ -1372,7 +1361,7 @@ namespace Sass {
hash_(ptr->hash_)
{
if (!name_.empty() && is_rest_argument_) {
error("variable-length argument may not be passed by name", pstate_);
coreError("variable-length argument may not be passed by name", pstate_);
}
}
......@@ -1779,15 +1768,16 @@ namespace Sass {
// evaluation phase.
///////////////////////////////////////////////////////////////////////
class String_Schema : public String, public Vectorized<Expression_Obj> {
// ADD_PROPERTY(bool, has_interpolants)
ADD_PROPERTY(bool, css)
size_t hash_;
public:
String_Schema(ParserState pstate, size_t size = 0, bool has_interpolants = false)
: String(pstate), Vectorized<Expression_Obj>(size), hash_(0)
String_Schema(ParserState pstate, size_t size = 0, bool css = true)
: String(pstate), Vectorized<Expression_Obj>(size), css_(css), hash_(0)
{ concrete_type(STRING); }
String_Schema(const String_Schema* ptr)
: String(ptr),
Vectorized<Expression_Obj>(*ptr),
css_(ptr->css_),
hash_(ptr->hash_)
{ concrete_type(STRING); }
......@@ -1840,17 +1830,17 @@ namespace Sass {
value_(ptr->value_),
hash_(ptr->hash_)
{ }
String_Constant(ParserState pstate, std::string val)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val)), hash_(0)
String_Constant(ParserState pstate, std::string val, bool css = true)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(val, css)), hash_(0)
{ }
String_Constant(ParserState pstate, const char* beg)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg))), hash_(0)
String_Constant(ParserState pstate, const char* beg, bool css = true)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg), css)), hash_(0)
{ }
String_Constant(ParserState pstate, const char* beg, const char* end)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg))), hash_(0)
String_Constant(ParserState pstate, const char* beg, const char* end, bool css = true)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(beg, end-beg), css)), hash_(0)
{ }
String_Constant(ParserState pstate, const Token& tok)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end))), hash_(0)
String_Constant(ParserState pstate, const Token& tok, bool css = true)
: String(pstate), quote_mark_(0), can_compress_whitespace_(false), value_(read_css_string(std::string(tok.begin, tok.end), css)), hash_(0)
{ }
std::string type() const { return "string"; }
static std::string type_name() { return "string"; }
......@@ -1883,8 +1873,8 @@ namespace Sass {
public:
String_Quoted(ParserState pstate, std::string val, char q = 0,
bool keep_utf8_escapes = false, bool skip_unquoting = false,
bool strict_unquoting = true)
: String_Constant(pstate, val)
bool strict_unquoting = true, bool css = true)
: String_Constant(pstate, val, css)
{
if (skip_unquoting == false) {
value_ = unquote(value_, &quote_mark_, keep_utf8_escapes, strict_unquoting);
......@@ -2217,22 +2207,22 @@ namespace Sass {
{
if (p->default_value()) {
if (has_rest_parameter()) {
error("optional parameters may not be combined with variable-length parameters", p->pstate());
coreError("optional parameters may not be combined with variable-length parameters", p->pstate());
}
has_optional_parameters(true);
}
else if (p->is_rest_parameter()) {
if (has_rest_parameter()) {
error("functions and mixins cannot have more than one variable-length parameter", p->pstate());
coreError("functions and mixins cannot have more than one variable-length parameter", p->pstate());
}
has_rest_parameter(true);
}
else {
if (has_rest_parameter()) {
error("required parameters must precede variable-length parameters", p->pstate());
coreError("required parameters must precede variable-length parameters", p->pstate());
}
if (has_optional_parameters()) {
error("required parameters must precede optional parameters", p->pstate());
coreError("required parameters must precede optional parameters", p->pstate());
}
}
}
......@@ -2871,13 +2861,13 @@ namespace Sass {
Complex_Selector_Obj innermost() { return last(); };
size_t length() const;
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");
Selector_List_Ptr unify_with(Complex_Selector_Ptr rhs);
Combinator clear_innermost();
void append(Complex_Selector_Obj);
void append(Complex_Selector_Obj, Backtraces& traces);
void set_innermost(Complex_Selector_Obj, Combinator);
virtual size_t hash()
{
......@@ -2993,7 +2983,7 @@ namespace Sass {
virtual bool has_parent_ref() const;
virtual bool has_real_parent_ref() const;
void remove_parent_selectors();
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, bool implicit_parent = true);
Selector_List_Ptr resolve_parent_refs(std::vector<Selector_List_Obj>& pstack, Backtraces& traces, bool implicit_parent = true);
virtual bool is_superselector_of(Compound_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Complex_Selector_Obj sub, std::string wrapping = "");
virtual bool is_superselector_of(Selector_List_Obj sub, std::string wrapping = "");
......
......@@ -33,7 +33,7 @@ class LocalOption {
#define NESTING_GUARD(name) \
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()\
virtual void perform(Operation<void>* op) { (*op)(this); }\
......
......@@ -11,6 +11,7 @@
#include <unordered_map>
#include <unordered_set>
#include "memory/SharedPtr.hpp"
#include "sass/functions.h"
/////////////////////////////////////////////
// Forward declarations for the AST visitors.
......@@ -418,6 +419,8 @@ namespace Sass {
typedef std::set<Compound_Selector_Obj, OrderNodes> CompoundSelectorSet;
typedef std::unordered_set<Simple_Selector_Obj, HashNodes, CompareNodes> SimpleSelectorDict;
typedef std::vector<Sass_Import_Entry>* ImporterStack;
// only to switch implementations for testing
#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
#define SASS_BACKTRACE_H
#include <vector>
#include <sstream>
#include "file.hpp"
#include "position.hpp"
namespace Sass {
struct Backtrace {
Backtrace* parent;
ParserState pstate;
std::string caller;
std::string caller;
Backtrace(Backtrace* prn, ParserState pstate, std::string c)
: parent(prn),
pstate(pstate),
Backtrace(ParserState pstate, std::string c = "")
: pstate(pstate),
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()
{
size_t d = std::string::npos;
Backtrace* p = parent;
while (p) {
++d;
p = p->parent;
}
return d;
}
typedef std::vector<Backtrace> Backtraces;
};
const std::string traces_to_string(Backtraces traces, std::string indent = "\t");
}
......
......@@ -2,6 +2,7 @@
#include "bind.hpp"
#include "ast.hpp"
#include "context.hpp"
#include "expand.hpp"
#include "eval.hpp"
#include <map>
#include <iostream>
......@@ -53,7 +54,7 @@ namespace Sass {
std::stringstream msg;
msg << "wrong number of arguments (" << LA << " for " << LP << ")";
msg << " for `" << name << "'";
return error(msg.str(), as->pstate());
return error(msg.str(), as->pstate(), eval->exp.traces);
}
Parameter_Obj p = ps->at(ip);
......@@ -106,7 +107,8 @@ namespace Sass {
false,
false));
} 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 {
for (auto key : argmap->keys()) {
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());
if (!param_map.count(param)) {
std::stringstream msg;
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);
}
......@@ -240,7 +245,7 @@ namespace Sass {
std::stringstream msg;
msg << "parameter " << p->name()
<< " 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
env->local_frame()[p->name()] = a->value();
......@@ -254,7 +259,7 @@ namespace Sass {
} else {
std::stringstream msg;
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()]) {
......@@ -262,14 +267,14 @@ namespace Sass {
std::stringstream msg;
msg << "argument " << a->name() << " of " << callee
<< "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())) {
std::stringstream msg;
msg << "parameter " << p->name()
<< "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();
}
......@@ -294,7 +299,7 @@ namespace Sass {
}
else {
// 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 {
CheckNesting::CheckNesting()
: parents(std::vector<Statement_Ptr>()),
parent(0),
current_mixin_definition(0)
traces(std::vector<Backtrace>()),
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 old_parent = this->parent;
......@@ -62,6 +67,12 @@ namespace Sass {
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 (Has_Block_Ptr bb = Cast<Has_Block>(parent)) {
b = bb->block();
......@@ -73,9 +84,16 @@ namespace Sass {
n->perform(this);
}
}
this->parent = old_parent;
this->parents.pop_back();
if (Trace_Ptr trace = Cast<Trace>(parent)) {
if (trace->type() == 'i') {
this->traces.pop_back();
}
}
return b;
}
......@@ -126,75 +144,69 @@ namespace Sass {
if (!this->parent) return true;
if (Cast<Content>(node))
{ this->invalid_content_parent(this->parent); }
{ this->invalid_content_parent(this->parent, node); }
if (is_charset(node))
{ this->invalid_charset_parent(this->parent); }
{ this->invalid_charset_parent(this->parent, node); }
if (Cast<Extension>(node))
{ this->invalid_extend_parent(this->parent); }
{ this->invalid_extend_parent(this->parent, node); }
// if (Cast<Import>(node))
// { this->invalid_import_parent(this->parent); }
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))
{ this->invalid_function_parent(this->parent); }
{ this->invalid_function_parent(this->parent, node); }
if (this->is_function(this->parent))
{ this->invalid_function_child(node); }
if (Cast<Declaration>(node))
{ this->invalid_prop_parent(this->parent); }
if (Declaration_Ptr d = Cast<Declaration>(node))
{
this->invalid_prop_parent(this->parent, node);
this->invalid_value_child(d->value());
}
if (Cast<Declaration>(this->parent))
{ this->invalid_prop_child(node); }
if (Cast<Return>(node))
{ this->invalid_return_parent(this->parent); }
{ this->invalid_return_parent(this->parent, node); }
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) {
throw Exception::InvalidSass(
parent->pstate(),
"@content may only be used within a mixin."
);
error(node, traces, "@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 (!(
is_root_node(parent)
)) {
throw Exception::InvalidSass(
parent->pstate(),
"@charset may only be used at the root of a document."
);
error(node, traces, "@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 (!(
Cast<Ruleset>(parent) ||
Cast<Mixin_Call>(parent) ||
is_mixin(parent)
)) {
throw Exception::InvalidSass(
parent->pstate(),
"Extend directives may only be used within rules."
);
error(node, traces, "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) {
// if (
......@@ -206,10 +218,7 @@ namespace Sass {
// Cast<Mixin_Call>(pp) ||
// is_mixin(pp)
// ) {
// throw Exception::InvalidSass(
// parent->pstate(),
// "Import directives may not be defined within control directives or other mixins."
// );
// error(node, traces, "Import directives may not be defined within control directives or other mixins.");
// }
// }
......@@ -218,14 +227,11 @@ namespace Sass {
// }
// if (false/*n.css_import?*/) {
// throw Exception::InvalidSass(
// parent->pstate(),
// "CSS import directives may only be used at the root of a document."
// );
// error(node, traces, "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) {
if (
......@@ -237,15 +243,12 @@ namespace Sass {
Cast<Mixin_Call>(pp) ||
is_mixin(pp)
) {
throw Exception::InvalidSass(
parent->pstate(),
"Mixins may not be defined within control directives or other mixins."
);
error(node, traces, "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) {
if (
......@@ -257,10 +260,7 @@ namespace Sass {
Cast<Mixin_Call>(pp) ||
is_mixin(pp)
) {
throw Exception::InvalidSass(
parent->pstate(),
"Functions may not be defined within control directives or other mixins."
);
error(node, traces, "Functions may not be defined within control directives or other mixins.");
}
}
}
......@@ -282,10 +282,7 @@ namespace Sass {
Cast<Warning>(child) ||
Cast<Error>(child)
)) {
throw Exception::InvalidSass(
child->pstate(),
"Functions can only contain variable declarations and control directives."
);
error(child, traces, "Functions can only contain variable declarations and control directives.");
}
}
......@@ -301,14 +298,11 @@ namespace Sass {
Cast<Declaration>(child) ||
Cast<Mixin_Call>(child)
)) {
throw Exception::InvalidSass(
child->pstate(),
"Illegal nesting: Only properties may be nested beneath properties."
);
error(child, traces, "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 (!(
is_mixin(parent) ||
......@@ -318,20 +312,31 @@ namespace Sass {
Cast<Declaration>(parent) ||
Cast<Mixin_Call>(parent)
)) {
throw Exception::InvalidSass(
parent->pstate(),
"Properties are only allowed within rules, directives, mixin includes, or other properties."
);
error(node, traces, "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)) {
throw Exception::InvalidSass(
parent->pstate(),
"@return may only be used within a function."
);
error(node, traces, "@return may only be used within a function.");
}
}
......
......@@ -9,6 +9,7 @@ namespace Sass {
class CheckNesting : public Operation_CRTP<Statement_Ptr, CheckNesting> {
std::vector<Statement_Ptr> parents;
Backtraces traces;
Statement_Ptr parent;
Definition_Ptr current_mixin_definition;
......@@ -34,17 +35,18 @@ namespace Sass {
}
private:
void invalid_content_parent(Statement_Ptr);
void invalid_charset_parent(Statement_Ptr);
void invalid_extend_parent(Statement_Ptr);
void invalid_content_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_charset_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_extend_parent(Statement_Ptr, AST_Node_Ptr);
// void invalid_import_parent(Statement_Ptr);
void invalid_mixin_definition_parent(Statement_Ptr);
void invalid_function_parent(Statement_Ptr);
void invalid_mixin_definition_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_function_parent(Statement_Ptr, AST_Node_Ptr);
void invalid_function_child(Statement_Ptr);
void invalid_prop_child(Statement_Ptr);
void invalid_prop_parent(Statement_Ptr);
void invalid_return_parent(Statement_Ptr);
void invalid_prop_parent(Statement_Ptr, AST_Node_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);
......
......@@ -74,6 +74,7 @@ namespace Sass {
subset_map(),
import_stack(),
callee_stack(),
traces(),
c_compiler(NULL),
c_headers (std::vector<Sass_Importer_Entry>()),
......@@ -250,10 +251,9 @@ namespace Sass {
return vec;
}
// 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)
void Context::register_resource(const Include& inc, const Resource& res)
{
// do not parse same resource twice
......@@ -301,21 +301,22 @@ namespace Sass {
for (size_t i = 0; i < import_stack.size() - 2; ++i) {
auto parent = import_stack[i];
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:");
for (size_t n = 1; n < i + 2; ++n) {
stack += "\n " + std::string(import_stack[n]->imp_path) +
" imports " + std::string(import_stack[n+1]->imp_path);
stack += "\n " + std::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) +
" imports " + std::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd));
}
// implement error throw directly until we
// decided how to handle full stack traces
ParserState state = prstate ? *prstate : pstate;
throw Exception::InvalidSyntax(state, stack, &import_stack);
throw Exception::InvalidSyntax(pstate, traces, stack);
// error(stack, prstate ? *prstate : pstate, import_stack);
}
}
// 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
sass_import_take_source(import);
sass_import_take_srcmap(import);
......@@ -330,7 +331,15 @@ namespace Sass {
ast_pair(inc.abs_path, { res, root });
// register resulting resource
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`)
......@@ -350,7 +359,7 @@ namespace Sass {
for (size_t i = 0, L = resolved.size(); i < L; ++i)
{ msg_stream << " " << resolved[i].imp_path << "\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
......@@ -362,7 +371,7 @@ namespace Sass {
// the memory buffer returned must be freed by us!
if (char* contents = read_file(resolved[0].abs_path)) {
// 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[0];
}
......@@ -403,7 +412,7 @@ namespace Sass {
const Importer importer(imp_path, ctx_path);
Include include(load_import(importer, pstate));
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);
}
......@@ -448,9 +457,9 @@ namespace Sass {
// handle error message passed back from custom importer
// it may (or may not) override the line and column info
if (const char* err_message = sass_import_get_error_message(include_ent)) {
if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, &pstate);
if (line == std::string::npos && column == std::string::npos) error(err_message, pstate);
else error(err_message, ParserState(ctx_path, source, Position(line, column)));
if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, 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)), traces);
}
// content for import was set
else if (source) {
......@@ -462,7 +471,7 @@ namespace Sass {
// attach information to AST node
imp->incs().push_back(include);
// register the resource buffers
register_resource(include, { source, srcmap }, &pstate);
register_resource(include, { source, srcmap }, pstate);
}
// only a path was retuned
// try to load it like normal
......@@ -648,10 +657,9 @@ namespace Sass {
for (size_t i = 0, S = c_functions.size(); i < S; ++i)
{ register_c_function(*this, &global, c_functions[i]); }
// create initial backtrace entry
Backtrace backtrace(0, ParserState("", 0), "");
// create crtp visitor objects
Expand expand(*this, &global, &backtrace);
Cssize cssize(*this, &backtrace);
Expand expand(*this, &global);
Cssize cssize(*this);
CheckNesting check_nesting;
// check nesting in all files
for (auto sheet : sheets) {
......
......@@ -15,6 +15,7 @@
#include "environment.hpp"
#include "source_map.hpp"
#include "subset_map.hpp"
#include "backtrace.hpp"
#include "output.hpp"
#include "plugins.hpp"
#include "file.hpp"
......@@ -54,6 +55,7 @@ namespace Sass {
Subset_Map subset_map;
std::vector<Sass_Import_Entry> import_stack;
std::vector<Sass_Callee> callee_stack;
std::vector<Backtrace> traces;
struct Sass_Compiler* c_compiler;
......@@ -94,7 +96,8 @@ namespace Sass {
virtual char* render(Block_Obj root);
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);
Include load_import(const Importer&, ParserState pstate);
......
......@@ -5,15 +5,14 @@
#include "cssize.hpp"
#include "context.hpp"
#include "backtrace.hpp"
namespace Sass {
Cssize::Cssize(Context& ctx, Backtrace* bt)
Cssize::Cssize(Context& ctx)
: ctx(ctx),
traces(ctx.traces),
block_stack(std::vector<Block_Ptr>()),
p_stack(std::vector<Statement_Ptr>()),
backtrace(bt)
p_stack(std::vector<Statement_Ptr>())
{ }
Statement_Ptr Cssize::parent()
......@@ -33,7 +32,10 @@ namespace Sass {
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)
......@@ -149,7 +151,7 @@ namespace Sass {
// this should protect us (at least a bit) from our mess
// fixing this properly is harder that it should be ...
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,
r->pstate(),
......@@ -161,7 +163,7 @@ namespace Sass {
p_stack.pop_back();
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());
......
......@@ -13,14 +13,14 @@ namespace Sass {
class Cssize : public Operation_CRTP<Statement_Ptr, Cssize> {
Context& ctx;
std::vector<Block_Ptr> block_stack;
std::vector<Statement_Ptr> p_stack;
Backtrace* backtrace;
Backtraces& traces;
std::vector<Block_Ptr> block_stack;
std::vector<Statement_Ptr> p_stack;
Statement_Ptr fallback_impl(AST_Node_Ptr n);
public:
Cssize(Context&, Backtrace*);
Cssize(Context&);
~Cssize() { }
Selector_List_Ptr selector();
......
......@@ -79,7 +79,7 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env)
Trace_Ptr trace = Cast<Trace>(node);
std::cerr << ind << "Trace " << trace;
std::cerr << " (" << pstate_source_position(node) << ")"
<< " [name:" << trace->name() << "]"
<< " [name:" << trace->name() << ", type: " << trace->type() << "]"
<< std::endl;
debug_ast(trace->block(), ind + " ", env);
} else if (Cast<At_Root_Block>(node)) {
......@@ -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 << " " << expression->concrete_type();
std::cerr << " (" << pstate_source_position(node) << ")";
if (expression->css()) std::cerr << " [css]";
if (expression->is_delayed()) std::cerr << " [delayed]";
if (expression->is_interpolant()) std::cerr << " [is interpolant]";
if (expression->has_interpolant()) std::cerr << " [has interpolant]";
......
......@@ -10,19 +10,18 @@ namespace Sass {
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),
prefix("Error"), pstate(pstate),
import_stack(import_stack)
prefix("Error"), pstate(pstate), traces(traces)
{ }
InvalidSass::InvalidSass(ParserState pstate, std::string msg)
: Base(pstate, msg)
InvalidSass::InvalidSass(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg, traces)
{ }
InvalidParent::InvalidParent(Selector_Ptr parent, Selector_Ptr selector)
: Base(selector->pstate()), parent(parent), selector(selector)
InvalidParent::InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector)
: Base(selector->pstate(), def_msg, traces), parent(parent), selector(selector)
{
msg = "Invalid parent selector for \"";
msg += selector->to_string(Sass_Inspect_Options());
......@@ -31,15 +30,15 @@ namespace Sass {
msg += "\"";
}
InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, std::string name, const Argument_Ptr arg)
: Base(pstate), name(name), arg(arg)
InvalidVarKwdType::InvalidVarKwdType(ParserState pstate, Backtraces traces, std::string name, const Argument_Ptr arg)
: Base(pstate, def_msg, traces), name(name), arg(arg)
{
msg = "Variable keyword argument map must have string keys.\n";
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)
: Base(pstate), fn(fn), arg(arg), type(type), value(value)
InvalidArgumentType::InvalidArgumentType(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string type, const Value_Ptr value)
: Base(pstate, def_msg, traces), fn(fn), arg(arg), type(type), value(value)
{
msg = arg + ": \"";
if (value) msg += value->to_string(Sass_Inspect_Options());
......@@ -47,50 +46,24 @@ namespace Sass {
msg += " for `" + fn + "'";
}
MissingArgument::MissingArgument(ParserState pstate, std::string fn, std::string arg, std::string fntype)
: Base(pstate), fn(fn), arg(arg), fntype(fntype)
MissingArgument::MissingArgument(ParserState pstate, Backtraces traces, std::string fn, std::string arg, std::string fntype)
: Base(pstate, def_msg, traces), fn(fn), arg(arg), fntype(fntype)
{
msg = fntype + " " + fn;
msg += " is missing argument ";
msg += arg + ".";
}
InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
: Base(pstate, msg, import_stack)
InvalidSyntax::InvalidSyntax(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg, traces)
{ }
NestingLimitError::NestingLimitError(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
: Base(pstate, msg, import_stack)
NestingLimitError::NestingLimitError(ParserState pstate, Backtraces traces, std::string msg)
: Base(pstate, msg, traces)
{ }
UndefinedOperation::UndefinedOperation(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)
: lhs(lhs), rhs(rhs), op(op)
{
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)
DuplicateKeyError::DuplicateKeyError(Backtraces traces, const Map& dup, const Expression& org)
: Base(org.pstate(), def_msg, traces), dup(dup), org(org)
{
msg = "Duplicate key ";
msg += dup.get_duplicate_key()->inspect();
......@@ -99,8 +72,8 @@ namespace Sass {
msg += ").";
}
TypeMismatch::TypeMismatch(const Expression& var, const std::string type)
: Base(var.pstate()), var(var), type(type)
TypeMismatch::TypeMismatch(Backtraces traces, const Expression& var, const std::string type)
: Base(var.pstate(), def_msg, traces), var(var), type(type)
{
msg = var.to_string();
msg += " is not an ";
......@@ -108,15 +81,15 @@ namespace Sass {
msg += ".";
}
InvalidValue::InvalidValue(const Expression& val)
: Base(val.pstate()), val(val)
InvalidValue::InvalidValue(Backtraces traces, const Expression& val)
: Base(val.pstate(), def_msg, traces), val(val)
{
msg = val.to_string();
msg += " isn't a valid CSS value.";
}
StackError::StackError(const AST_Node& node)
: Base(node.pstate()), node(node)
StackError::StackError(Backtraces traces, const AST_Node& node)
: Base(node.pstate(), def_msg, traces), node(node)
{
msg = "stack level too deep";
}
......@@ -139,19 +112,44 @@ namespace Sass {
msg += "'.";
}
AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, const std::string& op)
: lhs(lhs), rhs(rhs), op(op)
AlphaChannelsNotEqual::AlphaChannelsNotEqual(Expression_Ptr_Const lhs, Expression_Ptr_Const rhs, enum Sass_OP op)
: OperationError(), lhs(lhs), rhs(rhs), op(op)
{
msg = "Alpha channels must be equal: ";
msg += lhs->to_string({ NESTED, 5 });
msg += " " + op + " ";
msg += " " + sass_op_to_name(op) + " ";
msg += rhs->to_string({ NESTED, 5 });
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)
: Base(pstate, err.what())
SassValueError::SassValueError(Backtraces traces, ParserState pstate, OperationError& err)
: Base(pstate, err.what(), traces)
{
msg = err.what();
prefix = err.errtype();
......@@ -162,7 +160,7 @@ namespace Sass {
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)
......@@ -178,8 +176,6 @@ namespace Sass {
void warn(std::string msg, ParserState pstate, Backtrace* bt)
{
Backtrace top(bt, pstate, "");
msg += top.to_string();
warn(msg, pstate);
}
......@@ -223,16 +219,17 @@ namespace Sass {
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, "");
msg += "\n" + top.to_string();
error(msg, pstate);
traces.push_back(Backtrace(pstate));
throw Exception::InvalidSyntax(pstate, traces, msg);
}
}
......@@ -5,6 +5,7 @@
#include <sstream>
#include <stdexcept>
#include "position.hpp"
#include "backtrace.hpp"
#include "ast_fwd_decl.hpp"
#include "sass/functions.h"
......@@ -25,9 +26,9 @@ namespace Sass {
std::string prefix;
public:
ParserState pstate;
std::vector<Sass_Import_Entry>* import_stack;
Backtraces traces;
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* what() const throw() { return msg.c_str(); }
virtual ~Base() throw() {};
......@@ -35,7 +36,7 @@ namespace Sass {
class InvalidSass : public Base {
public:
InvalidSass(ParserState pstate, std::string msg);
InvalidSass(ParserState pstate, Backtraces traces, std::string msg);
virtual ~InvalidSass() throw() {};
};
......@@ -44,7 +45,7 @@ namespace Sass {
Selector_Ptr parent;
Selector_Ptr selector;
public:
InvalidParent(Selector_Ptr parent, Selector_Ptr selector);
InvalidParent(Selector_Ptr parent, Backtraces traces, Selector_Ptr selector);
virtual ~InvalidParent() throw() {};
};
......@@ -54,7 +55,7 @@ namespace Sass {
std::string arg;
std::string fntype;
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() {};
};
......@@ -65,7 +66,7 @@ namespace Sass {
std::string type;
const Value_Ptr value;
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() {};
};
......@@ -74,52 +75,28 @@ namespace Sass {
std::string name;
const Argument_Ptr arg;
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() {};
};
class InvalidSyntax : public Base {
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() {};
};
class NestingLimitError : public Base {
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() {};
};
/* 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 {
protected:
const Map& dup;
const Expression& org;
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 ~DuplicateKeyError() throw() {};
};
......@@ -129,7 +106,7 @@ namespace Sass {
const Expression& var;
const std::string type;
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 ~TypeMismatch() throw() {};
};
......@@ -138,7 +115,7 @@ namespace Sass {
protected:
const Expression& val;
public:
InvalidValue(const Expression& val);
InvalidValue(Backtraces traces, const Expression& val);
virtual const char* errtype() const { return "Error"; }
virtual ~InvalidValue() throw() {};
};
......@@ -147,11 +124,35 @@ namespace Sass {
protected:
const AST_Node& node;
public:
StackError(const AST_Node& node);
StackError(Backtraces traces, const AST_Node& node);
virtual const char* errtype() const { return "SystemStackError"; }
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 {
protected:
// const Sass::UnitType lhs;
......@@ -166,16 +167,16 @@ namespace Sass {
protected:
Expression_Ptr_Const lhs;
Expression_Ptr_Const rhs;
const std::string op;
const Sass_OP op;
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 ~UndefinedOperation() throw() {};
};
class InvalidNullOperation : public UndefinedOperation {
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() {};
};
......@@ -183,16 +184,16 @@ namespace Sass {
protected:
Expression_Ptr_Const lhs;
Expression_Ptr_Const rhs;
const std::string op;
const Sass_OP op;
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 ~AlphaChannelsNotEqual() throw() {};
};
class SassValueError : public Base {
public:
SassValueError(ParserState pstate, OperationError& err);
SassValueError(Backtraces traces, ParserState pstate, OperationError& err);
virtual ~SassValueError() throw() {};
};
......@@ -207,8 +208,8 @@ namespace Sass {
void deprecated_bind(std::string msg, ParserState pstate);
// void deprecated(std::string msg, ParserState pstate, Backtrace* bt);
void error(std::string msg, ParserState pstate);
void error(std::string msg, ParserState pstate, Backtrace* bt);
void coreError(std::string msg, ParserState pstate);
void error(std::string msg, ParserState pstate, Backtraces& traces);
}
......
......@@ -12,6 +12,7 @@
#include "bind.hpp"
#include "util.hpp"
#include "inspect.hpp"
#include "operators.hpp"
#include "environment.hpp"
#include "position.hpp"
#include "sass/values.h"
......@@ -28,28 +29,10 @@
namespace Sass {
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
};
Eval::Eval(Expand& exp)
: exp(exp),
ctx(exp.ctx),
traces(exp.traces),
force(false),
is_in_comment(false),
is_in_selector_schema(false)
......@@ -69,11 +52,6 @@ namespace Sass {
return exp.selector();
}
Backtrace* Eval::backtrace()
{
return exp.backtrace();
}
Expression_Ptr Eval::operator()(Block_Ptr b)
{
Expression_Ptr val = 0;
......@@ -169,11 +147,13 @@ namespace Sass {
std::string variable(f->variable());
Expression_Obj low = f->lower_bound()->perform(this);
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(this);
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_end = Cast<Number>(high);
......@@ -182,7 +162,7 @@ namespace Sass {
std::stringstream msg; msg << "Incompatible units: '"
<< sass_end->unit() << "' and '"
<< sass_start->unit() << "'.";
error(msg.str(), low->pstate(), backtrace());
error(msg.str(), low->pstate(), traces);
}
double start = sass_start->value();
double end = sass_end->value();
......@@ -366,11 +346,12 @@ namespace Sass {
}
std::string result(unquote(message->to_sass()));
Backtrace top(backtrace(), w->pstate(), "");
std::cerr << "WARNING: " << result;
std::cerr << top.to_string();
std::cerr << std::endl << std::endl;
std::cerr << "WARNING: " << result << std::endl;
traces.push_back(Backtrace(w->pstate()));
std::cerr << traces_to_string(traces, " ");
std::cerr << std::endl;
ctx.c_options.output_style = outstyle;
traces.pop_back();
return 0;
}
......@@ -414,7 +395,7 @@ namespace Sass {
std::string result(unquote(message->to_sass()));
ctx.c_options.output_style = outstyle;
error(result, e->pstate());
error(result, e->pstate(), traces);
return 0;
}
......@@ -484,7 +465,8 @@ namespace Sass {
*lm << std::make_pair(key, val);
}
if (lm->has_duplicate_key()) {
throw Exception::DuplicateKeyError(*lm, *l);
traces.push_back(Backtrace(l->pstate()));
throw Exception::DuplicateKeyError(traces, *lm, *l);
}
lm->is_interpolant(l->is_interpolant());
......@@ -515,7 +497,8 @@ namespace Sass {
// make sure we're not starting with duplicate keys.
// the duplicate key state will have been set in the parser phase.
if (m->has_duplicate_key()) {
throw Exception::DuplicateKeyError(*m, *m);
traces.push_back(Backtrace(m->pstate()));
throw Exception::DuplicateKeyError(traces, *m, *m);
}
Map_Obj mm = SASS_MEMORY_NEW(Map,
......@@ -531,7 +514,8 @@ namespace Sass {
// check the evaluated keys aren't duplicates.
if (mm->has_duplicate_key()) {
throw Exception::DuplicateKeyError(*mm, *m);
traces.push_back(Backtrace(m->pstate()));
throw Exception::DuplicateKeyError(traces, *mm, *m);
}
mm->is_expanded(true);
......@@ -599,13 +583,14 @@ namespace Sass {
case Sass_OP::LTE: return *l_n < *r_n || *l_n == *r_n ? bool_true : bool_false;
case Sass_OP::GT: return *l_n < *r_n || *l_n == *r_n ? bool_false : bool_true;
case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
return op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate());
return Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, b_in->pstate());
default: break;
}
}
catch (Exception::OperationError& err)
{
throw Exception::SassValueError(b_in->pstate(), err);
traces.push_back(Backtrace(b_in->pstate()));
throw Exception::SassValueError(traces, b_in->pstate(), err);
}
}
// lhs is number and rhs is color
......@@ -619,13 +604,14 @@ namespace Sass {
case Sass_OP::LTE: return *l_n < *r_c || *l_n == *r_c ? bool_true : bool_false;
case Sass_OP::GT: return *l_n < *r_c || *l_n == *r_c ? bool_false : bool_true;
case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
return op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate());
return Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, b_in->pstate());
default: break;
}
}
catch (Exception::OperationError& err)
{
throw Exception::SassValueError(b_in->pstate(), err);
traces.push_back(Backtrace(b_in->pstate()));
throw Exception::SassValueError(traces, b_in->pstate(), err);
}
}
}
......@@ -641,13 +627,14 @@ namespace Sass {
case Sass_OP::LTE: return *l_c < *r_c || *l_c == *r_c ? bool_true : bool_false;
case Sass_OP::GT: return *l_c < *r_c || *l_c == *r_c ? bool_false : bool_true;
case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
return op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate());
return Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, b_in->pstate());
default: break;
}
}
catch (Exception::OperationError& err)
{
throw Exception::SassValueError(b_in->pstate(), err);
traces.push_back(Backtrace(b_in->pstate()));
throw Exception::SassValueError(traces, b_in->pstate(), err);
}
}
// lhs is color and rhs is number
......@@ -661,13 +648,14 @@ namespace Sass {
case Sass_OP::LTE: return *l_c < *r_n || *l_c == *r_n ? bool_true : bool_false;
case Sass_OP::GT: return *l_c < *r_n || *l_c == *r_n ? bool_false : bool_true;
case Sass_OP::ADD: case Sass_OP::SUB: case Sass_OP::MUL: case Sass_OP::DIV: case Sass_OP::MOD:
return op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate());
return Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, b_in->pstate());
default: break;
}
}
catch (Exception::OperationError& err)
{
throw Exception::SassValueError(b_in->pstate(), err);
traces.push_back(Backtrace(b_in->pstate()));
throw Exception::SassValueError(traces, b_in->pstate(), err);
}
}
}
......@@ -786,19 +774,20 @@ namespace Sass {
// see if it's a relational expression
try {
switch(op_type) {
case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), eq(lhs, rhs));
case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), !eq(lhs, rhs));
case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs));
case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), !lt(lhs, rhs, "gte"));
case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lt"));
case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs));
default: break;
case Sass_OP::EQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::eq(lhs, rhs));
case Sass_OP::NEQ: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::neq(lhs, rhs));
case Sass_OP::GT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gt(lhs, rhs));
case Sass_OP::GTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::gte(lhs, rhs));
case Sass_OP::LT: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lt(lhs, rhs));
case Sass_OP::LTE: return SASS_MEMORY_NEW(Boolean, b->pstate(), Operators::lte(lhs, rhs));
default: break;
}
}
catch (Exception::OperationError& err)
{
// throw Exception::Base(b->pstate(), err.what());
throw Exception::SassValueError(b->pstate(), err);
traces.push_back(Backtrace(b->pstate()));
throw Exception::SassValueError(traces, b->pstate(), err);
}
l_type = lhs->concrete_type();
......@@ -813,22 +802,22 @@ namespace Sass {
Number_Ptr l_n = Cast<Number>(lhs);
Number_Ptr r_n = Cast<Number>(rhs);
l_n->reduce(); r_n->reduce();
rv = op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate);
rv = Operators::op_numbers(op_type, *l_n, *r_n, ctx.c_options, pstate);
}
else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
Number_Ptr l_n = Cast<Number>(lhs);
Color_Ptr r_c = Cast<Color>(rhs);
rv = op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate);
rv = Operators::op_number_color(op_type, *l_n, *r_c, ctx.c_options, pstate);
}
else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {
Color_Ptr l_c = Cast<Color>(lhs);
Number_Ptr r_n = Cast<Number>(rhs);
rv = op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate);
rv = Operators::op_color_number(op_type, *l_c, *r_n, ctx.c_options, pstate);
}
else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {
Color_Ptr l_c = Cast<Color>(lhs);
Color_Ptr r_c = Cast<Color>(rhs);
rv = op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate);
rv = Operators::op_colors(op_type, *l_c, *r_c, ctx.c_options, pstate);
}
else {
To_Value to_value(ctx);
......@@ -842,12 +831,14 @@ namespace Sass {
// if (op_type == Sass_OP::DIV) interpolant = true;
// check for type violations
if (l_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) {
throw Exception::InvalidValue(*v_l);
traces.push_back(Backtrace(v_l->pstate()));
throw Exception::InvalidValue(traces, *v_l);
}
if (r_type == Expression::MAP || l_type == Expression::FUNCTION_VAL) {
throw Exception::InvalidValue(*v_r);
traces.push_back(Backtrace(v_r->pstate()));
throw Exception::InvalidValue(traces, *v_r);
}
Value_Ptr ex = op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress
Value_Ptr ex = Operators::op_strings(b->op(), *v_l, *v_r, ctx.c_options, pstate, !interpolant); // pass true to compress
if (String_Constant_Ptr str = Cast<String_Constant>(ex))
{
if (str->concrete_type() == Expression::STRING)
......@@ -866,8 +857,9 @@ namespace Sass {
}
catch (Exception::OperationError& err)
{
traces.push_back(Backtrace(b->pstate()));
// throw Exception::Base(b->pstate(), err.what());
throw Exception::SassValueError(b->pstate(), err);
throw Exception::SassValueError(traces, b->pstate(), err);
}
if (rv) {
......@@ -932,11 +924,11 @@ namespace Sass {
Expression_Ptr Eval::operator()(Function_Call_Ptr c)
{
if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {
if (traces.size() > Constants::MaxCallStack) {
// XXX: this is never hit via spec tests
std::ostringstream stm;
stm << "Stack depth exceeded max of " << Constants::MaxCallStack;
error(stm.str(), c->pstate(), backtrace());
error(stm.str(), c->pstate(), traces);
}
std::string name(Util::normalize_underscores(c->name()));
std::string full_name(name + "[f]");
......@@ -948,7 +940,7 @@ namespace Sass {
if (!env->has("*[f]")) {
for (Argument_Obj arg : args->elements()) {
if (List_Obj ls = Cast<List>(arg->value())) {
if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate());
if (ls->size() == 0) error("() isn't a valid CSS value.", c->pstate(), traces);
}
}
args = Cast<Arguments>(args->perform(this));
......@@ -957,7 +949,7 @@ namespace Sass {
c->name(),
args);
if (args->has_named_arguments()) {
error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());
error("Function " + c->name() + " doesn't support keyword arguments", c->pstate(), traces);
}
String_Quoted_Ptr str = SASS_MEMORY_NEW(String_Quoted,
c->pstate(),
......@@ -994,7 +986,7 @@ namespace Sass {
ss << full_name << L;
full_name = ss.str();
std::string resolved_name(full_name);
if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate());
if (!env->has(resolved_name)) error("overloaded function `" + std::string(c->name()) + "` given wrong number of arguments", c->pstate(), traces);
def = Cast<Definition>((*env)[resolved_name]);
}
......@@ -1011,8 +1003,8 @@ namespace Sass {
if (func || body) {
bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);
Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");
exp.backtrace_stack.push_back(&here);
std::string msg(", in function `" + c->name() + "`");
traces.push_back(Backtrace(c->pstate(), msg));
ctx.callee_stack.push_back({
c->name().c_str(),
c->pstate().path,
......@@ -1027,13 +1019,13 @@ namespace Sass {
result = body->perform(this);
}
else if (func) {
result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace(), exp.selector_stack);
result = func(fn_env, *env, ctx, def->signature(), c->pstate(), traces, exp.selector_stack);
}
if (!result) {
error(std::string("Function ") + c->name() + " did not return a value", c->pstate());
error(std::string("Function ") + c->name() + " finished without @return", c->pstate(), traces);
}
exp.backtrace_stack.pop_back();
ctx.callee_stack.pop_back();
traces.pop_back();
}
// else if it's a user-defined c function
......@@ -1051,9 +1043,8 @@ namespace Sass {
// populates env with default values for params
std::string ff(c->name());
bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);
Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");
exp.backtrace_stack.push_back(&here);
std::string msg(", in function `" + c->name() + "`");
traces.push_back(Backtrace(c->pstate(), msg));
ctx.callee_stack.push_back({
c->name().c_str(),
c->pstate().path,
......@@ -1074,14 +1065,14 @@ namespace Sass {
}
union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
if (sass_value_get_tag(c_val) == SASS_ERROR) {
error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), backtrace());
error("error in C function " + c->name() + ": " + sass_error_get_message(c_val), c->pstate(), traces);
} else if (sass_value_get_tag(c_val) == SASS_WARNING) {
error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), backtrace());
error("warning in C function " + c->name() + ": " + sass_warning_get_message(c_val), c->pstate(), traces);
}
result = cval_to_astnode(c_val, backtrace(), c->pstate());
result = cval_to_astnode(c_val, traces, c->pstate());
exp.backtrace_stack.pop_back();
ctx.callee_stack.pop_back();
traces.pop_back();
sass_delete_value(c_args);
if (c_val != c_args)
sass_delete_value(c_val);
......@@ -1115,7 +1106,7 @@ namespace Sass {
const std::string& name(v->name());
EnvResult rv(env->find(name));
if (rv.found) value = static_cast<Expression*>(rv.it->second.ptr());
else error("Undefined variable: \"" + v->name() + "\".", v->pstate());
else error("Undefined variable: \"" + v->name() + "\".", v->pstate(), traces);
if (Argument_Ptr arg = Cast<Argument>(value)) value = arg->value();
if (Number_Ptr nr = Cast<Number>(value)) nr->zero(true); // force flag
value->is_interpolant(v->is_interpolant());
......@@ -1159,7 +1150,8 @@ namespace Sass {
Number reduced(nr);
reduced.reduce();
if (!reduced.is_valid_css_unit()) {
throw Exception::InvalidValue(*nr);
traces.push_back(Backtrace(nr->pstate()));
throw Exception::InvalidValue(traces, *nr);
}
}
if (Argument_Ptr arg = Cast<Argument>(ex)) {
......@@ -1258,10 +1250,10 @@ namespace Sass {
}
if (!s->is_interpolant()) {
if (s->length() > 1 && res == "") return SASS_MEMORY_NEW(Null, s->pstate());
return SASS_MEMORY_NEW(String_Constant, s->pstate(), res);
return SASS_MEMORY_NEW(String_Constant, s->pstate(), res, s->css());
}
// string schema seems to have a special unquoting behavior (also handles "nested" quotes)
String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false);
String_Quoted_Obj str = SASS_MEMORY_NEW(String_Quoted, s->pstate(), res, 0, false, false, false, s->css());
// if (s->is_interpolant()) str->quote_mark(0);
// String_Constant_Ptr str = SASS_MEMORY_NEW(String_Constant, s->pstate(), res);
if (str->quote_mark()) str->quote_mark('*');
......@@ -1482,206 +1474,7 @@ namespace Sass {
// All the binary helpers.
bool Eval::eq(Expression_Obj lhs, Expression_Obj rhs)
{
// use compare operator from ast node
return lhs && rhs && *lhs == *rhs;
}
bool Eval::lt(Expression_Obj lhs, Expression_Obj rhs, std::string op)
{
Number_Obj l = Cast<Number>(lhs);
Number_Obj r = Cast<Number>(rhs);
// use compare operator from ast node
if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);
// use compare operator from ast node
return *l < *r;
}
Value_Ptr Eval::op_numbers(enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, const ParserState& pstate)
{
double lv = l.value();
double rv = r.value();
if (op == Sass_OP::DIV && rv == 0) {
// XXX: this is never hit via spec tests
return SASS_MEMORY_NEW(String_Quoted, pstate, lv ? "Infinity" : "NaN");
}
if (op == Sass_OP::MOD && !rv) {
// XXX: this is never hit via spec tests
throw Exception::ZeroDivisionError(l, r);
}
size_t l_n_units = l.numerators.size();
size_t l_d_units = l.numerators.size();
size_t r_n_units = r.denominators.size();
size_t r_d_units = r.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 (l.numerators == r.numerators) {
if (l.denominators == r.denominators) {
Number_Ptr v = SASS_MEMORY_COPY(&l);
v->value(ops[op](lv, rv));
return v;
}
}
}
}
Number_Obj v = SASS_MEMORY_COPY(&l);
if (l.is_unitless() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
v->numerators = r.numerators;
v->denominators = r.denominators;
}
if (op == Sass_OP::MUL) {
v->value(ops[op](lv, rv));
v->numerators.insert(v->numerators.end(),
r.numerators.begin(), r.numerators.end()
);
v->denominators.insert(v->denominators.end(),
r.denominators.begin(), r.denominators.end()
);
}
else if (op == Sass_OP::DIV) {
v->value(ops[op](lv, rv));
v->numerators.insert(v->numerators.end(),
r.denominators.begin(), r.denominators.end()
);
v->denominators.insert(v->denominators.end(),
r.numerators.begin(), r.numerators.end()
);
}
else {
Number ln(l), rn(r);
ln.reduce(); rn.reduce();
double f(rn.convert_factor(ln));
v->value(ops[op](lv, rn.value() * f));
}
v->reduce();
v->pstate(pstate);
return v.detach();
}
Value_Ptr Eval::op_number_color(enum Sass_OP op, const Number& l, const Color& r, struct Sass_Inspect_Options opt, const ParserState& pstate)
{
double lv = l.value();
switch (op) {
case Sass_OP::ADD:
case Sass_OP::MUL: {
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](lv, r.r()),
ops[op](lv, r.g()),
ops[op](lv, r.b()),
r.a());
}
case Sass_OP::SUB:
case Sass_OP::DIV: {
std::string sep(op == Sass_OP::SUB ? "-" : "/");
std::string color(r.to_string(opt));
return SASS_MEMORY_NEW(String_Quoted,
pstate,
l.to_string(opt)
+ sep
+ color);
}
case Sass_OP::MOD: {
throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op));
}
default: break; // caller should ensure that we don't get here
}
// unreachable
return NULL;
}
Value_Ptr Eval::op_color_number(enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, const ParserState& pstate)
{
double rv = r.value();
if (op == Sass_OP::DIV && !rv) {
// comparison of Fixnum with Float failed?
throw Exception::ZeroDivisionError(l, r);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](l.r(), rv),
ops[op](l.g(), rv),
ops[op](l.b(), rv),
l.a());
}
Value_Ptr Eval::op_colors(enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, const ParserState& pstate)
{
if (l.a() != r.a()) {
throw Exception::AlphaChannelsNotEqual(&l, &r, "+");
}
if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {
// comparison of Fixnum with Float failed?
throw Exception::ZeroDivisionError(l, r);
}
return SASS_MEMORY_NEW(Color,
pstate,
ops[op](l.r(), r.r()),
ops[op](l.g(), r.g()),
ops[op](l.b(), r.b()),
l.a());
}
Value_Ptr Eval::op_strings(Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, const ParserState& pstate, bool delayed)
{
Expression::Concrete_Type ltype = lhs.concrete_type();
Expression::Concrete_Type rtype = rhs.concrete_type();
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 (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
std::string sep;
switch (op) {
case Sass_OP::SUB: sep = "-"; break;
case Sass_OP::DIV: sep = "/"; break;
// cases are already handled above
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;
case Sass_OP::MUL: throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
case Sass_OP::MOD: throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
default: break;
}
if ( (sep == "") /* &&
(sep != "/" || !rqstr || !rqstr->quote_mark()) */
) {
// create a new 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);
}
if (sep != "" && !delayed) {
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);
}
Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtrace* backtrace, ParserState pstate)
Expression_Ptr cval_to_astnode(union Sass_Value* v, Backtraces traces, ParserState pstate)
{
using std::strlen;
using std::strcpy;
......@@ -1706,7 +1499,7 @@ namespace Sass {
case SASS_LIST: {
List_Ptr l = SASS_MEMORY_NEW(List, pstate, sass_list_get_length(v), sass_list_get_separator(v));
for (size_t i = 0, L = sass_list_get_length(v); i < L; ++i) {
l->append(cval_to_astnode(sass_list_get_value(v, i), backtrace, pstate));
l->append(cval_to_astnode(sass_list_get_value(v, i), traces, pstate));
}
l->is_bracketed(sass_list_get_is_bracketed(v));
e = l;
......@@ -1715,8 +1508,8 @@ namespace Sass {
Map_Ptr m = SASS_MEMORY_NEW(Map, pstate);
for (size_t i = 0, L = sass_map_get_length(v); i < L; ++i) {
*m << std::make_pair(
cval_to_astnode(sass_map_get_key(v, i), backtrace, pstate),
cval_to_astnode(sass_map_get_value(v, i), backtrace, pstate));
cval_to_astnode(sass_map_get_key(v, i), traces, pstate),
cval_to_astnode(sass_map_get_value(v, i), traces, pstate));
}
e = m;
} break;
......@@ -1724,10 +1517,10 @@ namespace Sass {
e = SASS_MEMORY_NEW(Null, pstate);
} break;
case SASS_ERROR: {
error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, backtrace);
error("Error in C function: " + std::string(sass_error_get_message(v)), pstate, traces);
} break;
case SASS_WARNING: {
error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, backtrace);
error("Warning in C function: " + std::string(sass_warning_get_message(v)), pstate, traces);
} break;
default: break;
}
......@@ -1771,7 +1564,7 @@ namespace Sass {
{
bool implicit_parent = !exp.old_at_root_without_rule;
if (is_in_selector_schema) exp.selector_stack.push_back(0);
Selector_List_Obj resolved = s->resolve_parent_refs(exp.selector_stack, implicit_parent);
Selector_List_Obj resolved = s->resolve_parent_refs(exp.selector_stack, traces, implicit_parent);
if (is_in_selector_schema) exp.selector_stack.pop_back();
for (size_t i = 0; i < resolved->length(); i++) {
Complex_Selector_Ptr is = resolved->at(i)->first();
......@@ -1807,7 +1600,7 @@ namespace Sass {
result_str = unquote(Util::rtrim(result_str));
char* temp_cstr = sass_copy_c_string(result_str.c_str());
ctx.strings.push_back(temp_cstr); // attach to context
Parser p = Parser::from_c_str(temp_cstr, ctx, s->pstate());
Parser p = Parser::from_c_str(temp_cstr, ctx, traces, s->pstate());
p.last_media_block = s->media_block();
// a selector schema may or may not connect to parent?
bool chroot = s->connect_parent() == false;
......
......@@ -18,8 +18,9 @@ namespace Sass {
Expression_Ptr fallback_impl(AST_Node_Ptr n);
public:
Expand& exp;
Expand& exp;
Context& ctx;
Backtraces& traces;
Eval(Expand& exp);
~Eval();
......@@ -31,7 +32,6 @@ namespace Sass {
Boolean_Obj bool_false;
Env* environment();
Backtrace* backtrace();
Selector_List_Obj selector();
// for evaluating function bodies
......@@ -91,22 +91,12 @@ namespace Sass {
template <typename U>
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:
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 {
// simple endless recursion protection
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),
traces(ctx.traces),
eval(Eval(*this)),
recursions(0),
in_keyframes(false),
......@@ -27,8 +28,7 @@ namespace Sass {
block_stack(std::vector<Block_Ptr>()),
call_stack(std::vector<AST_Node_Obj>()),
selector_stack(std::vector<Selector_List_Obj>()),
media_block_stack(std::vector<Media_Block_Ptr>()),
backtrace_stack(std::vector<Backtrace*>())
media_block_stack(std::vector<Media_Block_Ptr>())
{
env_stack.push_back(0);
env_stack.push_back(env);
......@@ -37,8 +37,6 @@ namespace Sass {
if (stack == NULL) { selector_stack.push_back(0); }
else { selector_stack.insert(selector_stack.end(), stack->begin(), stack->end()); }
media_block_stack.push_back(0);
backtrace_stack.push_back(0);
backtrace_stack.push_back(bt);
}
Env* Expand::environment()
......@@ -55,13 +53,6 @@ namespace Sass {
return 0;
}
Backtrace* Expand::backtrace()
{
if (backtrace_stack.size() > 0)
return backtrace_stack.back();
return 0;
}
// blocks create new variable scopes
Block_Ptr Expand::operator()(Block_Ptr b)
{
......@@ -126,7 +117,7 @@ namespace Sass {
Parent_Selector_Ptr ptr = Cast<Parent_Selector>(header);
if (ptr == NULL || (!ptr->real() || has_parent_selector)) continue;
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();
}
......@@ -136,7 +127,7 @@ namespace Sass {
else {
if (sel->length() == 0 || sel->has_parent_ref()) {
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 {
std::string str_mq(mq->to_string(ctx.c_options));
char* str = sass_copy_c_string(str_mq.c_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
cpy->media_queries(mq);
media_block_stack.push_back(cpy);
......@@ -349,10 +340,11 @@ namespace Sass {
Statement_Ptr Expand::operator()(Import_Stub_Ptr i)
{
traces.push_back(Backtrace(i->pstate()));
// get parent node from call stack
AST_Node_Obj parent = call_stack.back();
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
Sass_Import_Entry import = sass_make_import(
......@@ -361,10 +353,18 @@ namespace Sass {
0, 0
);
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);
append_block(ctx.sheets.at(abs_path).root);
sass_delete_import(ctx.import_stack.back());
ctx.import_stack.pop_back();
block_stack.pop_back();
traces.pop_back();
return 0;
}
......@@ -428,11 +428,13 @@ namespace Sass {
std::string variable(f->variable());
Expression_Obj low = f->lower_bound()->perform(&eval);
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);
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_end = Cast<Number>(high);
......@@ -441,7 +443,7 @@ namespace Sass {
std::stringstream msg; msg << "Incompatible units: '"
<< sass_start->unit() << "' and '"
<< sass_end->unit() << "'.";
error(msg.str(), low->pstate(), backtrace());
error(msg.str(), low->pstate(), traces);
}
double start = sass_start->value();
double end = sass_end->value();
......@@ -579,7 +581,7 @@ namespace Sass {
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;
}
......@@ -593,7 +595,7 @@ namespace Sass {
if (tail->head()) for (Simple_Selector_Obj header : tail->head()->elements()) {
if (Cast<Parent_Selector>(header) == NULL) continue; // skip all others
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();
}
......@@ -607,7 +609,7 @@ namespace Sass {
Complex_Selector_Obj c = complex_sel;
if (!c->head() || c->tail()) {
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();
if (contextualized->is_optional()) target->is_optional(true);
......@@ -694,7 +696,7 @@ namespace Sass {
Statement_Ptr Expand::operator()(Mixin_Call_Ptr c)
{
if (recursions > maxRecursion) {
throw Exception::StackError(*c);
throw Exception::StackError(traces, *c);
}
recursions ++;
......@@ -702,19 +704,19 @@ namespace Sass {
Env* env = environment();
std::string full_name(c->name() + "[m]");
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]);
Block_Obj body = def->block();
Parameters_Obj params = def->parameters();
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);
Arguments_Obj args = Cast<Arguments>(rv);
Backtrace new_bt(backtrace(), c->pstate(), ", in mixin `" + c->name() + "`");
backtrace_stack.push_back(&new_bt);
std::string msg(", in mixin `" + c->name() + "`");
traces.push_back(Backtrace(c->pstate(), msg));
ctx.callee_stack.push_back({
c->name().c_str(),
c->pstate().path,
......@@ -758,9 +760,9 @@ namespace Sass {
block_stack.pop_back();
env->del_global("is_in_mixin");
env_stack.pop_back();
backtrace_stack.pop_back();
ctx.callee_stack.pop_back();
env_stack.pop_back();
traces.pop_back();
recursions --;
return trace.detach();
......@@ -795,7 +797,7 @@ namespace Sass {
{
std::string err =std:: string("`Expand` doesn't handle ") + typeid(*n).name();
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);
}
......
......@@ -20,9 +20,9 @@ namespace Sass {
Env* environment();
Selector_List_Obj selector();
Backtrace* backtrace();
Context& ctx;
Backtraces& traces;
Eval eval;
size_t recursions;
bool in_keyframes;
......@@ -35,7 +35,6 @@ namespace Sass {
std::vector<AST_Node_Obj> call_stack;
std::vector<Selector_List_Obj> selector_stack;
std::vector<Media_Block_Ptr> media_block_stack;
std::vector<Backtrace*> backtrace_stack;
Boolean_Obj bool_true;
......@@ -45,7 +44,7 @@ namespace Sass {
void expand_selector_list(Selector_Obj, Selector_List_Obj extender);
public:
Expand(Context&, Env*, Backtrace*, std::vector<Selector_List_Obj>* stack = NULL);
Expand(Context&, Env*, std::vector<Selector_List_Obj>* stack = NULL);
~Expand() { }
Block_Ptr operator()(Block_Ptr);
......
......@@ -1718,7 +1718,7 @@ namespace Sass {
err << "You may only @extend selectors within the same directive.\n";
err << "From \"@extend " << ext.second->to_string() << "\"";
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;
}
......@@ -2098,7 +2098,7 @@ namespace Sass {
error("\"" + str_sel + "\" failed to @extend \"" + str_ext + "\".\n"
"The selector \"" + str_ext + "\" was not found.\n"
"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 {
#ifndef _WIN32
char 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");
std::string cwd = pwd;
#else
......@@ -74,11 +76,15 @@ namespace Sass {
bool file_exists(const std::string& path)
{
#ifdef _WIN32
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16
std::string abspath(join_paths(get_cwd(), path));
std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));
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 &&
(!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)));
#else
......@@ -200,6 +206,13 @@ namespace Sass {
if (is_absolute_path(r)) return r;
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)) == "..\\")) {
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] == '\\');
......@@ -395,11 +408,15 @@ namespace Sass {
#ifdef _WIN32
BYTE* pBuffer;
DWORD dwBytes;
wchar_t resolved[32768];
// windows unicode filepaths are encoded in utf16
std::string abspath(join_paths(get_cwd(), path));
std::wstring wpath(UTF_8::convert_to_utf16("\\\\?\\" + abspath));
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;
DWORD dwFileLength = GetFileSize(hFile, NULL);
if (dwFileLength == INVALID_FILE_SIZE) return 0;
......
......@@ -10,6 +10,7 @@
#include "eval.hpp"
#include "util.hpp"
#include "expand.hpp"
#include "operators.hpp"
#include "utf8_string.hpp"
#include "sass/base.h"
#include "utf8.h"
......@@ -30,27 +31,26 @@
#include "wincrypt.h"
#endif
#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, backtrace)
#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, backtrace, ctx)
#define ARGNR(argname) get_arg_nr(argname, env, sig, pstate, backtrace)
#define ARG(argname, argtype) get_arg<argtype>(argname, env, sig, pstate, traces)
#define ARGM(argname, argtype, ctx) get_arg_m(argname, env, sig, pstate, traces, ctx)
// return a number object (copied since we want to have reduced units)
#define ARGN(argname) get_arg_n(argname, env, sig, pstate, backtrace) // Number copy
#define ARGN(argname) get_arg_n(argname, env, sig, pstate, traces) // Number copy
// special function for weird hsla percent (10px == 10% == 10 != 0.1)
#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, backtrace) // double
#define ARGVAL(argname) get_arg_val(argname, env, sig, pstate, traces) // double
// macros for common ranges (u mean unsigned or upper, r for full range)
#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 0.0, 1.0) // double
#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 1.0, 1.0) // double
#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 0.0, 255.0) // double
#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 255.0, 255.0) // double
#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 0.0, 100.0) // double
#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, backtrace, - 100.0, 100.0) // double
#define DARG_U_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 1.0) // double
#define DARG_R_FACT(argname) get_arg_r(argname, env, sig, pstate, traces, - 1.0, 1.0) // double
#define DARG_U_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 255.0) // double
#define DARG_R_BYTE(argname) get_arg_r(argname, env, sig, pstate, traces, - 255.0, 255.0) // double
#define DARG_U_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 0.0, 100.0) // double
#define DARG_R_PRCT(argname) get_arg_r(argname, env, sig, pstate, traces, - 100.0, 100.0) // double
// macros for color related inputs (rbg and alpha/opacity values)
#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, backtrace) // double
#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, backtrace) // double
#define COLOR_NUM(argname) color_num(argname, env, sig, pstate, traces) // double
#define ALPHA_NUM(argname) alpha_num(argname, env, sig, pstate, traces) // double
namespace Sass {
using std::stringstream;
......@@ -58,7 +58,7 @@ namespace Sass {
Definition_Ptr make_native_function(Signature sig, Native_Function func, Context& ctx)
{
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));
Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[built-in function]"));
sig_parser.lex<Prelexer::identifier>();
std::string name(Util::normalize_underscores(sig_parser.lexed));
Parameters_Obj params = sig_parser.parse_parameters();
......@@ -76,7 +76,7 @@ namespace Sass {
using namespace Prelexer;
const char* sig = sass_function_get_signature(c_func);
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[c function]"));
Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, ParserState("[c function]"));
// allow to overload generic callback plus @warn, @error and @debug with custom functions
sig_parser.lex < alternatives < identifier, exactly <'*'>,
exactly < Constants::warn_kwd >,
......@@ -102,28 +102,28 @@ namespace Sass {
namespace Functions {
inline void handle_utf8_error (const ParserState& pstate, Backtrace* backtrace)
inline void handle_utf8_error (const ParserState& pstate, Backtraces traces)
{
try {
throw;
}
catch (utf8::invalid_code_point) {
std::string msg("utf8::invalid_code_point");
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
catch (utf8::not_enough_room) {
std::string msg("utf8::not_enough_room");
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
catch (utf8::invalid_utf8) {
std::string msg("utf8::invalid_utf8");
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
catch (...) { throw; }
}
template <typename T>
T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
T* get_arg(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
T* val = Cast<T>(env[argname]);
......@@ -134,12 +134,12 @@ namespace Sass {
msg += sig;
msg += "` must be a ";
msg += T::type_name();
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
return val;
}
Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx)
Map_Ptr get_arg_m(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Map_Ptr val = Cast<Map>(env[argname]);
......@@ -149,14 +149,14 @@ namespace Sass {
if (lval && lval->length() == 0) return SASS_MEMORY_NEW(Map, pstate, 0);
// fallback on get_arg for error handling
val = get_arg<Map>(argname, env, sig, pstate, backtrace);
val = get_arg<Map>(argname, env, sig, pstate, traces);
return val;
}
double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, double lo, double hi)
double get_arg_r(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, double lo, double hi)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
double v = tmpnr.value();
......@@ -164,33 +164,24 @@ namespace Sass {
std::stringstream msg;
msg << "argument `" << argname << "` of `" << sig << "` must be between ";
msg << lo << " and " << hi;
error(msg.str(), pstate, backtrace);
error(msg.str(), pstate, traces);
}
return v;
}
const Number& get_arg_nr(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number tmpnr(val);
tmpnr.reduce();
return tmpnr;
}
Number_Ptr get_arg_n(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
val = SASS_MEMORY_COPY(val);
val->reduce();
return val;
}
double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
double get_arg_v(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
/*
......@@ -204,18 +195,18 @@ namespace Sass {
return tmpnr.value();
}
double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
double get_arg_val(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)
{
// Minimal error handling -- the expectation is that built-ins will be written correctly!
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
return tmpnr.value();
}
double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace)
double color_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces)
{
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
if (tmpnr.unit() == "%") {
......@@ -226,8 +217,8 @@ namespace Sass {
}
inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace) {
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, backtrace);
inline double alpha_num(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces) {
Number_Ptr val = get_arg<Number>(argname, env, sig, pstate, traces);
Number tmpnr(val);
tmpnr.reduce();
if (tmpnr.unit() == "%") {
......@@ -237,40 +228,40 @@ namespace Sass {
}
}
#define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, backtrace, ctx)
#define ARGSEL(argname, seltype, contextualize) get_arg_sel<seltype>(argname, env, sig, pstate, traces, ctx)
template <typename T>
T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx);
T get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx);
template <>
Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {
Selector_List_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {
Expression_Obj exp = ARG(argname, Expression);
if (exp->concrete_type() == Expression::NULL_VAL) {
std::stringstream msg;
msg << argname << ": null is not a valid selector: it must be a string,\n";
msg << "a list of strings, or a list of lists of strings for `" << function_name(sig) << "'";
error(msg.str(), pstate);
error(msg.str(), pstate, traces);
}
if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string(ctx.c_options);
return Parser::parse_selector(exp_src.c_str(), ctx);
return Parser::parse_selector(exp_src.c_str(), ctx, traces);
}
template <>
Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtrace* backtrace, Context& ctx) {
Compound_Selector_Obj get_arg_sel(const std::string& argname, Env& env, Signature sig, ParserState pstate, Backtraces traces, Context& ctx) {
Expression_Obj exp = ARG(argname, Expression);
if (exp->concrete_type() == Expression::NULL_VAL) {
std::stringstream msg;
msg << argname << ": null is not a string for `" << function_name(sig) << "'";
error(msg.str(), pstate);
error(msg.str(), pstate, traces);
}
if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string(ctx.c_options);
Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx);
Selector_List_Obj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces);
if (sel_list->length() == 0) return NULL;
Complex_Selector_Obj first = sel_list->first();
if (!first->tail()) return first->head();
......@@ -870,7 +861,7 @@ namespace Sass {
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate);
error("Cannot specify HSL and RGB values for a color at the same time for `adjust-color'", pstate, traces);
}
if (rgb) {
double rr = r ? DARG_R_BYTE("$red") : 0;
......@@ -904,7 +895,7 @@ namespace Sass {
color->b(),
color->a() + (a ? a->value() : 0));
}
error("not enough arguments for `adjust-color'", pstate);
error("not enough arguments for `adjust-color'", pstate, traces);
// unreachable
return color;
}
......@@ -925,7 +916,7 @@ namespace Sass {
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate);
error("Cannot specify HSL and RGB values for a color at the same time for `scale-color'", pstate, traces);
}
if (rgb) {
double rscale = (r ? DARG_R_PRCT("$red") : 0.0) / 100.0;
......@@ -960,7 +951,7 @@ namespace Sass {
color->b(),
color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a()));
}
error("not enough arguments for `scale-color'", pstate);
error("not enough arguments for `scale-color'", pstate, traces);
// unreachable
return color;
}
......@@ -981,7 +972,7 @@ namespace Sass {
bool hsl = h || s || l;
if (rgb && hsl) {
error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate);
error("Cannot specify HSL and RGB values for a color at the same time for `change-color'", pstate, traces);
}
if (rgb) {
return SASS_MEMORY_NEW(Color,
......@@ -1008,7 +999,7 @@ namespace Sass {
color->b(),
alpha);
}
error("not enough arguments for `change-color'", pstate);
error("not enough arguments for `change-color'", pstate, traces);
// unreachable
return color;
}
......@@ -1101,7 +1092,7 @@ namespace Sass {
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, backtrace); }
catch (...) { handle_utf8_error(pstate, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)len);
}
......@@ -1147,7 +1138,7 @@ namespace Sass {
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, backtrace); }
catch (...) { handle_utf8_error(pstate, traces); }
return SASS_MEMORY_NEW(String_Quoted, pstate, str);
}
......@@ -1171,7 +1162,7 @@ namespace Sass {
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, backtrace); }
catch (...) { handle_utf8_error(pstate, traces); }
// return something even if we had an error (-1)
return SASS_MEMORY_NEW(Number, pstate, (double)index);
}
......@@ -1224,7 +1215,7 @@ namespace Sass {
}
// handle any invalid utf8 errors
// other errors will be re-thrown
catch (...) { handle_utf8_error(pstate, backtrace); }
catch (...) { handle_utf8_error(pstate, traces); }
return SASS_MEMORY_NEW(String_Quoted, pstate, newstr);
}
......@@ -1278,7 +1269,7 @@ namespace Sass {
BUILT_IN(percentage)
{
Number_Obj n = ARGN("$number");
if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate);
if (!n->is_unitless()) error("argument $number of `" + std::string(sig) + "` must be unitless", pstate, traces);
return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%");
}
......@@ -1327,7 +1318,7 @@ namespace Sass {
Expression_Obj val = arglist->value_at_index(i);
Number_Obj xi = Cast<Number>(val);
if (!xi) {
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate);
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces);
}
if (least) {
if (*xi < *least) least = xi;
......@@ -1345,7 +1336,7 @@ namespace Sass {
Expression_Obj val = arglist->value_at_index(i);
Number_Obj xi = Cast<Number>(val);
if (!xi) {
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate);
error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces);
}
if (greatest) {
if (*greatest < *xi) greatest = xi;
......@@ -1366,13 +1357,13 @@ namespace Sass {
if (lv < 1) {
stringstream err;
err << "$limit " << lv << " must be greater than or equal to 1 for `random'";
error(err.str(), pstate);
error(err.str(), pstate, traces);
}
bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON;
if (!eq_int) {
stringstream err;
err << "Expected $limit to be an integer but got " << lv << " for `random'";
error(err.str(), pstate);
error(err.str(), pstate, traces);
}
std::uniform_real_distribution<> distributor(1, lv + 1);
uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
......@@ -1383,9 +1374,11 @@ namespace Sass {
double distributed = static_cast<double>(distributor(rand));
return SASS_MEMORY_NEW(Number, pstate, distributed);
} else if (v) {
throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number", v);
traces.push_back(Backtrace(pstate));
throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v);
} else {
throw Exception::InvalidArgumentType(pstate, "random", "$limit", "number");
traces.push_back(Backtrace(pstate));
throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number");
}
}
......@@ -1428,15 +1421,15 @@ namespace Sass {
if (Selector_List_Ptr sl = Cast<Selector_List>(env["$list"])) {
size_t len = m ? m->length() : sl->length();
bool empty = m ? m->empty() : sl->empty();
if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);
if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);
double index = std::floor(nr < 0 ? len + nr : nr - 1);
if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);
if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);
// return (*sl)[static_cast<int>(index)];
Listize listize;
return (*sl)[static_cast<int>(index)]->perform(&listize);
}
List_Obj l = Cast<List>(env["$list"]);
if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate);
if (nr == 0) error("argument `$n` of `" + std::string(sig) + "` must be non-zero", pstate, traces);
// if the argument isn't a list, then wrap it in a singleton list
if (!m && !l) {
l = SASS_MEMORY_NEW(List, pstate, 1);
......@@ -1444,9 +1437,9 @@ namespace Sass {
}
size_t len = m ? m->length() : l->length();
bool empty = m ? m->empty() : l->empty();
if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);
if (empty) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);
double index = std::floor(nr < 0 ? len + nr : nr - 1);
if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);
if (index < 0 || index > len - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);
if (m) {
l = SASS_MEMORY_NEW(List, pstate, 1);
......@@ -1475,9 +1468,9 @@ namespace Sass {
if (m) {
l = m->to_list(pstate);
}
if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate);
if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate, traces);
double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1);
if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate);
if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate, traces);
List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed());
for (size_t i = 0, L = l->length(); i < L; ++i) {
result->append(((i == index) ? v : (*l)[i]));
......@@ -1499,7 +1492,7 @@ namespace Sass {
l = m->to_list(pstate);
}
for (size_t i = 0, L = l->length(); i < L; ++i) {
if (Eval::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));
if (Operators::eq(l->value_at_index(i), v)) return SASS_MEMORY_NEW(Number, pstate, (double)(i+1));
}
return SASS_MEMORY_NEW(Null, pstate);
}
......@@ -1536,7 +1529,7 @@ namespace Sass {
std::string sep_str = unquote(sep->value());
if (sep_str == "space") sep_val = SASS_SPACE;
else if (sep_str == "comma") sep_val = SASS_COMMA;
else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);
else if (sep_str != "auto") error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);
String_Constant_Obj bracketed_as_str = Cast<String_Constant>(bracketed);
bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto";
if (!bracketed_is_auto) {
......@@ -1571,7 +1564,7 @@ namespace Sass {
if (sep_str != "auto") { // check default first
if (sep_str == "space") result->separator(SASS_SPACE);
else if (sep_str == "comma") result->separator(SASS_COMMA);
else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate);
else error("argument `$separator` of `" + std::string(sig) + "` must be `space`, `comma`, or `auto`", pstate, traces);
}
if (l->is_arglist()) {
result->append(SASS_MEMORY_NEW(Argument,
......@@ -1712,7 +1705,7 @@ namespace Sass {
for (auto key : m->keys()) {
remove = false;
for (size_t j = 0, K = arglist->length(); j < K && !remove; ++j) {
remove = Eval::eq(key, arglist->value_at_index(j));
remove = Operators::eq(key, arglist->value_at_index(j));
}
if (!remove) *result << std::make_pair(key, m->at(key));
}
......@@ -1809,7 +1802,7 @@ namespace Sass {
{
String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);
if (!ss) {
error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, backtrace);
error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);
}
std::string name = Util::normalize_underscores(unquote(ss->value()));
......@@ -1893,7 +1886,7 @@ namespace Sass {
}
}
Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, name, args);
Expand expand(ctx, &d_env, backtrace, &selector_stack);
Expand expand(ctx, &d_env, &selector_stack);
func->via_call(true); // calc invoke is allowed
if (ff) func->func(ff);
return func->perform(&expand.eval);
......@@ -1914,7 +1907,7 @@ namespace Sass {
// { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }
BUILT_IN(sass_if)
{
Expand expand(ctx, &d_env, backtrace, &selector_stack);
Expand expand(ctx, &d_env, &selector_stack);
Expression_Obj cond = ARG("$condition", Expression)->perform(&expand.eval);
bool is_true = !cond->is_false();
Expression_Obj res = ARG(is_true ? "$if-true" : "$if-false", Expression);
......@@ -1961,7 +1954,7 @@ namespace Sass {
// Not enough parameters
if( arglist->length() == 0 )
error("$selectors: At least one selector must be passed for `selector-nest'", pstate);
error("$selectors: At least one selector must be passed for `selector-nest'", pstate, traces);
// Parse args into vector of selectors
std::vector<Selector_List_Obj> parsedSelectors;
......@@ -1971,13 +1964,13 @@ namespace Sass {
std::stringstream msg;
msg << "$selectors: null is not a valid selector: it must be a string,\n";
msg << "a list of strings, or a list of lists of strings for 'selector-nest'";
error(msg.str(), pstate);
error(msg.str(), pstate, traces);
}
if (String_Constant_Obj str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string(ctx.c_options);
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);
parsedSelectors.push_back(sel);
}
......@@ -1995,7 +1988,7 @@ namespace Sass {
Selector_List_Obj child = *itr;
std::vector<Complex_Selector_Obj> exploded;
selector_stack.push_back(result);
Selector_List_Obj rv = child->resolve_parent_refs(selector_stack);
Selector_List_Obj rv = child->resolve_parent_refs(selector_stack, traces);
selector_stack.pop_back();
for (size_t m = 0, mLen = rv->length(); m < mLen; ++m) {
exploded.push_back((*rv)[m]);
......@@ -2014,7 +2007,7 @@ namespace Sass {
// Not enough parameters
if( arglist->length() == 0 )
error("$selectors: At least one selector must be passed for `selector-append'", pstate);
error("$selectors: At least one selector must be passed for `selector-append'", pstate, traces);
// Parse args into vector of selectors
std::vector<Selector_List_Obj> parsedSelectors;
......@@ -2024,13 +2017,13 @@ namespace Sass {
std::stringstream msg;
msg << "$selectors: null is not a valid selector: it must be a string,\n";
msg << "a list of strings, or a list of lists of strings for 'selector-append'";
error(msg.str(), pstate);
error(msg.str(), pstate, traces);
}
if (String_Constant_Ptr str = Cast<String_Constant>(exp)) {
str->quote_mark(0);
}
std::string exp_src = exp->to_string();
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx);
Selector_List_Obj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces);
parsedSelectors.push_back(sel);
}
......@@ -2068,7 +2061,7 @@ namespace Sass {
msg += "\" to \"";
msg += parentSeqClone->to_string();
msg += "\" for `selector-append'";
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
// Cannot be a Universal selector
......@@ -2079,7 +2072,7 @@ namespace Sass {
msg += "\" to \"";
msg += parentSeqClone->to_string();
msg += "\" for `selector-append'";
error(msg, pstate, backtrace);
error(msg, pstate, traces);
}
// TODO: Add check for namespace stuff
......@@ -2202,7 +2195,7 @@ namespace Sass {
BUILT_IN(content_exists)
{
if (!d_env.has_global("is_in_mixin")) {
error("Cannot call content-exists() except within a mixin.", pstate, backtrace);
error("Cannot call content-exists() except within a mixin.", pstate, traces);
}
return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));
}
......@@ -2212,7 +2205,7 @@ namespace Sass {
{
String_Constant_Ptr ss = Cast<String_Constant>(env["$name"]);
if (!ss) {
error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, backtrace);
error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);
}
std::string name = Util::normalize_underscores(unquote(ss->value()));
......@@ -2231,7 +2224,7 @@ namespace Sass {
if (!d_env.has_global(full_name)) {
error("Function not found: " + name, pstate, backtrace);
error("Function not found: " + name, pstate, traces);
}
Definition_Ptr def = Cast<Definition>(d_env[full_name]);
......
......@@ -8,12 +8,12 @@
#include "sass/functions.h"
#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 {
struct Backtrace;
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_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 {
void Output::operator()(Number_Ptr n)
{
// use values to_string facility
std::string res = n->to_string(opt);
// check for a valid unit here
// includes result for reporting
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
append_token(res, n);
}
......@@ -37,8 +38,8 @@ namespace Sass {
void Output::operator()(Map_Ptr m)
{
std::string dbg(m->to_string(opt));
error(dbg + " isn't a valid CSS value.", m->pstate());
// should be handle in check_expression
throw Exception::InvalidValue({}, *m);
}
OutputBuffer Output::get_buffer(void)
......
......@@ -30,11 +30,11 @@ namespace Sass {
using namespace Constants;
using namespace Prelexer;
Parser Parser::from_c_str(const char* beg, Context& ctx, ParserState pstate, const char* source)
Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate);
Parser p(ctx, pstate, traces);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = p.position + strlen(p.position);
......@@ -44,11 +44,11 @@ namespace Sass {
return p;
}
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, ParserState pstate, const char* source)
Parser Parser::from_c_str(const char* beg, const char* end, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
pstate.offset.column = 0;
pstate.offset.line = 0;
Parser p(ctx, pstate);
Parser p(ctx, pstate, traces);
p.source = source ? source : beg;
p.position = beg ? beg : p.source;
p.end = end ? end : p.position + strlen(p.position);
......@@ -66,9 +66,9 @@ namespace Sass {
pstate.offset.line = 0;
}
Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, ParserState pstate, const char* source)
Selector_List_Obj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
Parser p = Parser::from_c_str(beg, ctx, pstate, source);
Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source);
// ToDo: ruby sass errors on parent references
// ToDo: remap the source-map entries somehow
return p.parse_selector_list(false);
......@@ -80,9 +80,9 @@ namespace Sass {
&& ! peek_css<exactly<'{'>>(start);
}
Parser Parser::from_token(Token t, Context& ctx, ParserState pstate, const char* source)
Parser Parser::from_token(Token t, Context& ctx, Backtraces traces, ParserState pstate, const char* source)
{
Parser p(ctx, pstate);
Parser p(ctx, pstate, traces);
p.source = source ? source : t.begin;
p.position = t.begin ? t.begin : p.source;
p.end = t.end ? t.end : p.position + strlen(p.position);
......@@ -105,7 +105,8 @@ namespace Sass {
// report invalid utf8
if (it != end) {
pstate += Offset::init(position, it);
throw Exception::InvalidSass(pstate, "Invalid UTF-8 sequence");
traces.push_back(Backtrace(pstate));
throw Exception::InvalidSass(pstate, traces, "Invalid UTF-8 sequence");
}
// create a block AST node to hold children
......@@ -240,7 +241,7 @@ namespace Sass {
Scope parent = stack.empty() ? Scope::Rules : stack.back();
if (parent != Scope::Function && parent != Scope::Root && parent != Scope::Rules && parent != Scope::Media) {
if (! peek_css< uri_prefix >(position)) { // this seems to go in ruby sass 3.4.20
error("Import directives may not be used within control directives or mixins.", pstate);
error("Import directives may not be used within control directives or mixins.");
}
}
// this puts the parsed doc into sheets
......@@ -350,14 +351,14 @@ namespace Sass {
args->append(SASS_MEMORY_NEW(Argument, braced_url->pstate(), braced_url));
}
else {
error("malformed URL", pstate);
error("malformed URL");
}
if (!lex< exactly<')'> >()) error("URI is missing ')'", pstate);
if (!lex< exactly<')'> >()) error("URI is missing ')'");
to_import.push_back(std::pair<std::string, Function_Call_Obj>("", result));
}
else {
if (first) error("@import directive requires a url or quoted path", pstate);
else error("expecting another url or quoted path in @import list", pstate);
if (first) error("@import directive requires a url or quoted path");
else error("expecting another url or quoted path in @import list");
}
first = false;
} while (lex_css< exactly<','> >());
......@@ -384,10 +385,10 @@ namespace Sass {
Definition_Obj Parser::parse_definition(Definition::Type which_type)
{
std::string which_str(lexed);
if (!lex< identifier >()) error("invalid name in " + which_str + " definition", pstate);
if (!lex< identifier >()) error("invalid name in " + which_str + " definition");
std::string name(Util::normalize_underscores(lexed));
if (which_type == Definition::FUNCTION && (name == "and" || name == "or" || name == "not"))
{ error("Invalid function name \"" + name + "\".", pstate); }
{ error("Invalid function name \"" + name + "\"."); }
ParserState source_position_of_def = pstate;
Parameters_Obj params = parse_parameters();
if (which_type == Definition::MIXIN) stack.push_back(Scope::Mixin);
......@@ -494,7 +495,7 @@ namespace Sass {
{
std::string name(Util::normalize_underscores(lexed));
ParserState var_source_position = pstate;
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement", pstate);
if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement");
if (peek_css< alternatives < exactly<';'>, end_of_file > >()) {
css_error("Invalid CSS", " after ", ": expected expression (e.g. 1px, bold), was ");
}
......@@ -583,7 +584,7 @@ namespace Sass {
}
// pass inner expression to the parser to resolve nested interpolations
pstate.add(p, p+2);
Expression_Obj interpolant = Parser::from_c_str(p+2, j, ctx, pstate).parse_list();
Expression_Obj interpolant = Parser::from_c_str(p+2, j, ctx, traces, pstate).parse_list();
// set status on the list expression
interpolant->is_interpolant(true);
// schema->has_interpolants(true);
......@@ -904,7 +905,7 @@ namespace Sass {
ParserState nsource_position = pstate;
Selector_List_Obj negated = parse_selector_list(true);
if (!lex< exactly<')'> >()) {
error("negated selector is missing ')'", pstate);
error("negated selector is missing ')'");
}
name.erase(name.size() - 1);
return SASS_MEMORY_NEW(Wrapped_Selector, nsource_position, name, negated);
......@@ -981,7 +982,7 @@ namespace Sass {
Attribute_Selector_Obj Parser::parse_attribute_selector()
{
ParserState p = pstate;
if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector", pstate);
if (!lex_css< attribute_name >()) error("invalid attribute name in attribute selector");
std::string name(lexed);
if (lex_css< re_attr_sensitive_close >()) {
return SASS_MEMORY_NEW(Attribute_Selector, p, name, "", 0, 0);
......@@ -992,7 +993,7 @@ namespace Sass {
}
if (!lex_css< alternatives< exact_match, class_match, dash_match,
prefix_match, suffix_match, substring_match > >()) {
error("invalid operator in attribute selector for " + name, pstate);
error("invalid operator in attribute selector for " + name);
}
std::string matcher(lexed);
......@@ -1004,7 +1005,7 @@ namespace Sass {
value = parse_interpolated_chunk(lexed, true); // needed!
}
else {
error("expected a string constant or identifier in attribute selector for " + name, pstate);
error("expected a string constant or identifier in attribute selector for " + name);
}
if (lex_css< re_attr_sensitive_close >()) {
......@@ -1014,7 +1015,7 @@ namespace Sass {
char modifier = lexed.begin[0];
return SASS_MEMORY_NEW(Attribute_Selector, p, name, matcher, value, modifier);
}
error("unterminated attribute selector for " + name, pstate);
error("unterminated attribute selector for " + name);
return NULL; // to satisfy compilers (error must not return)
}
......@@ -1026,7 +1027,7 @@ namespace Sass {
while (lex< block_comment >()) {
bool is_important = lexed.begin[2] == '!';
// flag on second param is to skip loosely over comments
String_Obj contents = parse_interpolated_chunk(lexed, true);
String_Obj contents = parse_interpolated_chunk(lexed, true, false);
block->append(SASS_MEMORY_NEW(Comment, pstate, contents, is_important));
}
}
......@@ -1049,8 +1050,8 @@ namespace Sass {
}
bool is_indented = true;
const std::string property(lexed);
if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + property + "\" must be followed by a ':'", pstate);
if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value", pstate);
if (!lex_css< one_plus< exactly<':'> > >()) error("property \"" + escape_string(property) + "\" must be followed by a ':'");
if (!is_custom_property && match< sequence< optional_css_comments, exactly<';'> > >()) error("style declaration must contain a value");
if (match< sequence< optional_css_comments, exactly<'{'> > >()) is_indented = false; // don't indent if value is empty
if (is_custom_property) {
return SASS_MEMORY_NEW(Declaration, prop->pstate(), prop, parse_css_variable_value(), false, true);
......@@ -1448,7 +1449,7 @@ namespace Sass {
// parse_map may return a list
Expression_Obj value = parse_map();
// lex the expected closing parenthesis
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis", pstate);
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis");
// expression can be evaluated
return value;
}
......@@ -1456,7 +1457,7 @@ namespace Sass {
// explicit bracketed
Expression_Obj value = parse_bracket_list();
// lex the expected closing square bracket
if (!lex_css< exactly<']'> >()) error("unclosed squared bracket", pstate);
if (!lex_css< exactly<']'> >()) error("unclosed squared bracket");
return value;
}
// string may be interpolated
......@@ -1721,7 +1722,7 @@ namespace Sass {
// this parses interpolation inside other strings
// means the result should later be quoted again
String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant)
String_Obj Parser::parse_interpolated_chunk(Token chunk, bool constant, bool css)
{
const char* i = chunk.begin;
// see if there any interpolants
......@@ -1729,12 +1730,12 @@ namespace Sass {
find_first_in_interval< exactly<hash_lbrace>, block_comment >(i, chunk.end);
if (!p) {
String_Quoted_Ptr str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end));
String_Quoted_Ptr str_quoted = SASS_MEMORY_NEW(String_Quoted, pstate, std::string(i, chunk.end), 0, false, false, true, css);
if (!constant && str_quoted->quote_mark()) str_quoted->quote_mark('*');
return str_quoted;
}
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate);
String_Schema_Obj schema = SASS_MEMORY_NEW(String_Schema, pstate, 0, css);
schema->is_interpolant(true);
while (i < chunk.end) {
p = constant ? find_first_in_interval< exactly<hash_lbrace> >(i, chunk.end) :
......@@ -1742,7 +1743,7 @@ namespace Sass {
if (p) {
if (i < p) {
// accumulate the preceding segment if it's nonempty
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, p)));
schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, p), css));
}
// we need to skip anything inside strings
// create a new target in parser/prelexer
......@@ -1752,19 +1753,19 @@ namespace Sass {
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p + 2, chunk.end); // find the closing brace
if (j) { --j;
// parse the interpolant and accumulate it
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, traces, pstate, source).parse_list();
interp_node->is_interpolant(true);
schema->append(interp_node);
i = j;
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside string constant " + chunk.to_string(), pstate);
error("unterminated interpolant inside string constant " + chunk.to_string());
}
}
else { // no interpolants left; add the last segment if nonempty
// check if we need quotes here (was not sure after merge)
if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, chunk.end)));
if (i < chunk.end) schema->append(SASS_MEMORY_NEW(String_Constant, pstate, std::string(i, chunk.end), css));
break;
}
++ i;
......@@ -1884,14 +1885,14 @@ namespace Sass {
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, str.end); // find the closing brace
if (j) {
// parse the interpolant and accumulate it
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list();
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, traces, pstate, source).parse_list();
interp_node->is_interpolant(true);
schema->append(interp_node);
i = j;
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside IE function " + str.to_string(), pstate);
error("unterminated interpolant inside IE function " + str.to_string());
}
}
else { // no interpolants left; add the last segment if nonempty
......@@ -2062,7 +2063,7 @@ namespace Sass {
const char* j = skip_over_scopes< exactly<hash_lbrace>, exactly<rbrace> >(p+2, id.end); // find the closing brace
if (j) {
// parse the interpolant and accumulate it
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, pstate, source).parse_list(DELAYED);
Expression_Obj interp_node = Parser::from_token(Token(p+2, j), ctx, traces, pstate, source).parse_list(DELAYED);
interp_node->is_interpolant(true);
schema->append(interp_node);
// schema->has_interpolants(true);
......@@ -2070,7 +2071,7 @@ namespace Sass {
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside interpolated identifier " + id.to_string(), pstate);
error("unterminated interpolant inside interpolated identifier " + id.to_string());
}
}
else { // no interpolants left; add the last segment if nonempty
......@@ -2171,7 +2172,7 @@ namespace Sass {
std::string name(lexed);
if (Util::normalize_underscores(name) == "content-exists" && stack.back() != Scope::Mixin)
{ error("Cannot call content-exists() except within a mixin.", pstate); }
{ error("Cannot call content-exists() except within a mixin."); }
ParserState call_pos = pstate;
Arguments_Obj args = parse_arguments();
......@@ -2221,12 +2222,12 @@ namespace Sass {
bool root = block_stack.back()->is_root();
lex_variable();
std::string var(Util::normalize_underscores(lexed));
if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive", pstate);
if (!lex< kwd_from >()) error("expected 'from' keyword in @for directive");
Expression_Obj lower_bound = parse_expression();
bool inclusive = false;
if (lex< kwd_through >()) inclusive = true;
else if (lex< kwd_to >()) inclusive = false;
else error("expected 'through' or 'to' keyword in @for directive", pstate);
else error("expected 'through' or 'to' keyword in @for directive");
Expression_Obj upper_bound = parse_expression();
Block_Obj body = parse_block(root);
stack.pop_back();
......@@ -2268,10 +2269,10 @@ namespace Sass {
lex_variable();
vars.push_back(Util::normalize_underscores(lexed));
while (lex< exactly<','> >()) {
if (!lex< variable >()) error("@each directive requires an iteration variable", pstate);
if (!lex< variable >()) error("@each directive requires an iteration variable");
vars.push_back(Util::normalize_underscores(lexed));
}
if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive", pstate);
if (!lex< kwd_in >()) error("expected 'in' keyword in @each directive");
Expression_Obj list = parse_list();
Block_Obj body = parse_block(root);
stack.pop_back();
......@@ -2360,11 +2361,11 @@ namespace Sass {
return SASS_MEMORY_NEW(Media_Query_Expression, pstate, ss, 0, true);
}
if (!lex_css< exactly<'('> >()) {
error("media query expression must begin with '('", pstate);
error("media query expression must begin with '('");
}
Expression_Obj feature;
if (peek_css< exactly<')'> >()) {
error("media feature required in media query expression", pstate);
error("media feature required in media query expression");
}
feature = parse_expression();
Expression_Obj expression = 0;
......@@ -2372,7 +2373,7 @@ namespace Sass {
expression = parse_list(DELAYED);
}
if (!lex_css< exactly<')'> >()) {
error("unclosed parenthesis in media query expression", pstate);
error("unclosed parenthesis in media query expression");
}
return SASS_MEMORY_NEW(Media_Query_Expression, feature->pstate(), feature, expression);
}
......@@ -2453,7 +2454,7 @@ namespace Sass {
if (lex_css< exactly<':'> >()) {
expression = parse_list(DELAYED);
}
if (!feature || !expression) error("@supports condition expected declaration", pstate);
if (!feature || !expression) error("@supports condition expected declaration");
cond = SASS_MEMORY_NEW(Supports_Declaration,
feature->pstate(),
feature,
......@@ -2472,10 +2473,10 @@ namespace Sass {
Supports_Condition_Obj cond = parse_supports_condition();
if (cond != 0) {
if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);
if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");
} else {
cond = parse_supports_declaration();
if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration", pstate);
if (!lex < exactly <')'> >()) error("unclosed parenthesis in @supports declaration");
}
lex < css_whitespace >();
return cond;
......@@ -2508,14 +2509,14 @@ namespace Sass {
At_Root_Query_Obj Parser::parse_at_root_query()
{
if (peek< exactly<')'> >()) error("at-root feature required in at-root expression", pstate);
if (peek< exactly<')'> >()) error("at-root feature required in at-root expression");
if (!peek< alternatives< kwd_with_directive, kwd_without_directive > >()) {
css_error("Invalid CSS", " after ", ": expected \"with\" or \"without\", was ");
}
Expression_Obj feature = parse_list();
if (!lex_css< exactly<':'> >()) error("style declaration must contain a value", pstate);
if (!lex_css< exactly<':'> >()) error("style declaration must contain a value");
Expression_Obj expression = parse_list();
List_Obj value = SASS_MEMORY_NEW(List, feature->pstate(), 1);
......@@ -2528,7 +2529,7 @@ namespace Sass {
value->pstate(),
feature,
value);
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression", pstate);
if (!lex_css< exactly<')'> >()) error("unclosed parenthesis in @at-root expression");
return cond;
}
......@@ -2536,7 +2537,7 @@ namespace Sass {
{
std::string kwd(lexed);
if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);
if (lexed == "@else") error("Invalid CSS: @else must come after @if");
// this whole branch is never hit via spec tests
......@@ -2568,7 +2569,7 @@ namespace Sass {
{
std::string kwd(lexed);
if (lexed == "@else") error("Invalid CSS: @else must come after @if", pstate);
if (lexed == "@else") error("Invalid CSS: @else must come after @if");
Directive_Obj at_rule = SASS_MEMORY_NEW(Directive, pstate, kwd);
Lookahead lookahead = lookahead_for_include(position);
......@@ -2725,7 +2726,7 @@ namespace Sass {
stack.back() != Scope::Mixin &&
stack.back() != Scope::Control &&
stack.back() != Scope::Rules) {
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
error("Illegal nesting: Only properties may be nested beneath properties.");
}
return SASS_MEMORY_NEW(Warning, pstate, parse_list(DELAYED));
}
......@@ -2737,7 +2738,7 @@ namespace Sass {
stack.back() != Scope::Mixin &&
stack.back() != Scope::Control &&
stack.back() != Scope::Rules) {
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
error("Illegal nesting: Only properties may be nested beneath properties.");
}
return SASS_MEMORY_NEW(Error, pstate, parse_list(DELAYED));
}
......@@ -2749,7 +2750,7 @@ namespace Sass {
stack.back() != Scope::Mixin &&
stack.back() != Scope::Control &&
stack.back() != Scope::Rules) {
error("Illegal nesting: Only properties may be nested beneath properties.", pstate);
error("Illegal nesting: Only properties may be nested beneath properties.");
}
return SASS_MEMORY_NEW(Debug, pstate, parse_list(DELAYED));
}
......@@ -2955,7 +2956,7 @@ namespace Sass {
break;
default: break;
}
if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding, pstate);
if (skip > 0 && !utf_8) error("only UTF-8 documents are currently supported; your document appears to be " + encoding);
position += skip;
}
......@@ -3035,7 +3036,15 @@ namespace Sass {
void Parser::error(std::string msg, Position pos)
{
throw Exception::InvalidSass(ParserState(path, source, pos.line ? pos : before_token, Offset(0, 0)), msg);
Position p(pos.line ? pos : before_token);
ParserState pstate(path, source, p, Offset(0, 0));
traces.push_back(Backtrace(pstate));
throw Exception::InvalidSass(pstate, traces, msg);
}
void Parser::error(std::string msg)
{
error(msg, pstate);
}
// print a css parsing error with actual context information from parsed source
......@@ -3106,7 +3115,7 @@ namespace Sass {
// Hotfix when source is null, probably due to interpolation parsing!?
if (source == NULL || *source == 0) source = pstate.src;
// now pass new message to the more generic error function
error(msg + prefix + quote(left) + middle + quote(right), pstate);
error(msg + prefix + quote(left) + middle + quote(right));
}
}
......@@ -45,22 +45,26 @@ namespace Sass {
Position before_token;
Position after_token;
ParserState pstate;
Backtraces traces;
size_t indentation;
size_t nestings;
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(),
source(0), position(0), end(0), before_token(pstate), after_token(pstate), pstate(pstate), indentation(0), nestings(0)
{ stack.push_back(Scope::Root); }
source(0), position(0), end(0), before_token(pstate), after_token(pstate),
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_c_str(const char* src, Context& ctx, 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_token(Token t, Context& ctx, ParserState pstate = ParserState("[TOKEN]"), 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, Backtraces, ParserState pstate = ParserState("[CSTRING]"), 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
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__
......@@ -232,6 +236,7 @@ namespace Sass {
#endif
void error(std::string msg);
void error(std::string msg, Position pos);
// generate message with given and expected sample
// text before and in the middle are configurable
......@@ -283,7 +288,7 @@ namespace Sass {
Function_Call_Schema_Obj parse_function_call_schema();
String_Obj parse_url_function_string();
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_Constant_Obj parse_static_value();
String_Schema_Obj parse_css_variable_value(bool top_level = true);
......
......@@ -35,7 +35,6 @@ namespace Sass {
catch (Exception::Base& e) {
std::stringstream msg_stream;
std::string cwd(Sass::File::get_cwd());
std::string msg_prefix(e.errtype());
bool got_newline = false;
msg_stream << msg_prefix << ": ";
......@@ -55,20 +54,17 @@ namespace Sass {
++msg;
}
if (!got_newline) msg_stream << "\n";
if (e.import_stack) {
for (size_t i = 1; i < e.import_stack->size() - 1; ++i) {
std::string path((*e.import_stack)[i]->imp_path);
std::string rel_path(Sass::File::abs2rel(path, cwd, cwd));
msg_stream << std::string(msg_prefix.size() + 2, ' ');
msg_stream << (i == 1 ? " on line " : " from line ");
msg_stream << e.pstate.line + 1 << " of " << rel_path << "\n";
}
}
else {
if (e.traces.empty()) {
// we normally should have some traces, still here as a fallback
std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
msg_stream << std::string(msg_prefix.size() + 2, ' ');
msg_stream << " on line " << e.pstate.line + 1 << " of " << rel_path << "\n";
}
else {
std::string rel_path(Sass::File::abs2rel(e.pstate.path, cwd, cwd));
msg_stream << traces_to_string(e.traces, " ");
}
// now create the code trace (ToDo: maybe have util functions?)
if (e.pstate.line != std::string::npos && e.pstate.column != std::string::npos) {
......
......@@ -4,6 +4,7 @@
#include "util.hpp"
#include "eval.hpp"
#include "values.hpp"
#include "operators.hpp"
#include "sass/values.h"
#include "sass_values.hpp"
......@@ -299,41 +300,41 @@ extern "C" {
// see if it's a relational expression
switch(op) {
case Sass_OP::EQ: return sass_make_boolean(Eval::eq(lhs, rhs));
case Sass_OP::NEQ: return sass_make_boolean(!Eval::eq(lhs, rhs));
case Sass_OP::GT: return sass_make_boolean(!Eval::lt(lhs, rhs, "gt") && !Eval::eq(lhs, rhs));
case Sass_OP::GTE: return sass_make_boolean(!Eval::lt(lhs, rhs, "gte"));
case Sass_OP::LT: return sass_make_boolean(Eval::lt(lhs, rhs, "lt"));
case Sass_OP::LTE: return sass_make_boolean(Eval::lt(lhs, rhs, "lte") || Eval::eq(lhs, rhs));
case Sass_OP::EQ: return sass_make_boolean(Operators::eq(lhs, rhs));
case Sass_OP::NEQ: return sass_make_boolean(Operators::neq(lhs, rhs));
case Sass_OP::GT: return sass_make_boolean(Operators::gt(lhs, rhs));
case Sass_OP::GTE: return sass_make_boolean(Operators::gte(lhs, rhs));
case Sass_OP::LT: return sass_make_boolean(Operators::lt(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::OR: return ast_node_to_sass_value(lhs->is_false() ? rhs : lhs);
default: break;
default: break;
}
if (sass_value_is_number(a) && sass_value_is_number(b)) {
Number_Ptr_Const l_n = Cast<Number>(lhs);
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)) {
Number_Ptr_Const l_n = Cast<Number>(lhs);
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)) {
Color_Ptr_Const l_c = Cast<Color>(lhs);
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)) {
Color_Ptr_Const l_c = Cast<Color>(lhs);
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 */ {
Value_Ptr l_v = Cast<Value>(lhs);
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?
......
......@@ -96,8 +96,9 @@ namespace Sass {
}
// 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("");
bool esc = false;
for (auto i : str) {
......@@ -180,6 +181,23 @@ namespace Sass {
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 str = "";
......
......@@ -22,11 +22,12 @@ namespace Sass {
const char* safe_str(const char *, const char* = "");
void free_string_array(char **);
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 string_to_output(const std::string& str);
std::string comment_to_string(const std::string& text);
std::string read_hex_escapes(const std::string& str);
std::string escape_string(const std::string& str);
void newline_to_space(std::string& str);
std::string quote(const std::string&, char q = 0);
......
......@@ -97,6 +97,8 @@
<ClCompile Include="$(LIBSASS_SRC_DIR)\plugins.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\position.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)\sass.cpp" />
<ClCompile Include="$(LIBSASS_SRC_DIR)\sass_context.cpp" />
......
......@@ -302,6 +302,12 @@
<ClCompile Include="$(LIBSASS_SRC_DIR)\prelexer.cpp">
<Filter>Sources</Filter>
</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">
<Filter>Sources</Filter>
</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