diff --git a/Makefile b/Makefile index 4f590bf0b2..bc7ad5d3ea 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,10 @@ SASS_SASSC_PATH ?= sassc SASS_SPEC_PATH ?= sass-spec SASSC_BIN = $(SASS_SASSC_PATH)/bin/sassc -SOURCES = ast.cpp bind.cpp constants.cpp context.cpp contextualize.cpp \ +SOURCES = ast.cpp base64vlq.cpp bind.cpp constants.cpp context.cpp contextualize.cpp \ copy_c_str.cpp error_handling.cpp eval.cpp expand.cpp extend.cpp file.cpp \ functions.cpp inspect.cpp output_compressed.cpp output_nested.cpp \ - parser.cpp prelexer.cpp sass.cpp sass_interface.cpp to_c.cpp to_string.cpp \ + parser.cpp prelexer.cpp sass.cpp sass_interface.cpp source_map.cpp to_c.cpp to_string.cpp \ units.cpp OBJECTS = $(SOURCES:.cpp=.o) diff --git a/Makefile.am b/Makefile.am index 0ca604661f..4fe11eae9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,10 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libsass.la -libsass_la_SOURCES = ast.cpp bind.cpp constants.cpp context.cpp contextualize.cpp \ +libsass_la_SOURCES = ast.cpp base64vlq.cpp bind.cpp constants.cpp context.cpp contextualize.cpp \ copy_c_str.cpp error_handling.cpp eval.cpp expand.cpp extend.cpp file.cpp \ functions.cpp inspect.cpp output_compressed.cpp output_nested.cpp \ - parser.cpp prelexer.cpp sass.cpp sass_interface.cpp to_c.cpp to_string.cpp \ + parser.cpp prelexer.cpp sass.cpp sass_interface.cpp source_map.cpp to_c.cpp to_string.cpp \ units.cpp libsass_la_LDFLAGS = -no-undefined -version-info 0:0:0 diff --git a/ast.cpp b/ast.cpp index bee616f818..9b1c3111c1 100644 --- a/ast.cpp +++ b/ast.cpp @@ -24,7 +24,7 @@ namespace Sass { { if (!tail()) return 0; if (!head()) return tail()->context(ctx); - return new (ctx.mem) Complex_Selector(path(), line(), combinator(), head(), tail()->context(ctx)); + return new (ctx.mem) Complex_Selector(path(), position(), combinator(), head(), tail()->context(ctx)); } Complex_Selector* Complex_Selector::innermost() diff --git a/ast.hpp b/ast.hpp index 1dbc3ef257..c333df799c 100644 --- a/ast.hpp +++ b/ast.hpp @@ -42,13 +42,21 @@ #endif #include "units.hpp" + +#ifndef SASS_ERROR_HANDLING #include "error_handling.hpp" +#endif + #include "ast_def_macros.hpp" #include #include #include +#ifndef SASS_POSITION +#include "position.hpp" +#endif + namespace Sass { using namespace std; @@ -94,9 +102,9 @@ namespace Sass { class Selector; class AST_Node { ADD_PROPERTY(string, path); - ADD_PROPERTY(size_t, line); + ADD_PROPERTY(Position, position); public: - AST_Node(string p, size_t l) : path_(p), line_(l) { } + AST_Node(string path, Position position) : path_(path), position_(position) { } virtual ~AST_Node() = 0; // virtual Block* block() { return 0; } ATTACH_OPERATIONS(); @@ -110,7 +118,7 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// class Statement : public AST_Node { public: - Statement(string p, size_t l) : AST_Node(p, l) { } + Statement(string path, Position position) : AST_Node(path, position) { } virtual ~Statement() = 0; // needed for rearranging nested rulesets during CSS emission virtual bool is_hoistable() { return false; } @@ -133,8 +141,8 @@ namespace Sass { else has_non_hoistable_ = true; }; public: - Block(string p, size_t l, size_t s = 0, bool r = false) - : Statement(p, l), + Block(string path, Position position, size_t s = 0, bool r = false) + : Statement(path, position), Vectorized(s), is_root_(r), has_hoistable_(false), has_non_hoistable_(false) { } @@ -148,8 +156,8 @@ namespace Sass { class Has_Block : public Statement { ADD_PROPERTY(Block*, block); public: - Has_Block(string p, size_t l, Block* b) - : Statement(p, l), block_(b) + Has_Block(string path, Position position, Block* b) + : Statement(path, position), block_(b) { } virtual ~Has_Block() = 0; }; @@ -163,8 +171,8 @@ namespace Sass { class Ruleset : public Has_Block { ADD_PROPERTY(Selector*, selector); public: - Ruleset(string p, size_t l, Selector* s, Block* b) - : Has_Block(p, l, b), selector_(s) + Ruleset(string path, Position position, Selector* s, Block* b) + : Has_Block(path, position, b), selector_(s) { } // nested rulesets need to be hoisted out of their enclosing blocks bool is_hoistable() { return true; } @@ -178,8 +186,8 @@ namespace Sass { class Propset : public Has_Block { ADD_PROPERTY(String*, property_fragment); public: - Propset(string p, size_t l, String* pf, Block* b = 0) - : Has_Block(p, l, b), property_fragment_(pf) + Propset(string path, Position position, String* pf, Block* b = 0) + : Has_Block(path, position, b), property_fragment_(pf) { } ATTACH_OPERATIONS(); }; @@ -192,8 +200,8 @@ namespace Sass { ADD_PROPERTY(List*, media_queries); ADD_PROPERTY(Selector*, enclosing_selector); public: - Media_Block(string p, size_t l, List* mqs, Block* b) - : Has_Block(p, l, b), media_queries_(mqs), enclosing_selector_(0) + Media_Block(string path, Position position, List* mqs, Block* b) + : Has_Block(path, position, b), media_queries_(mqs), enclosing_selector_(0) { } bool is_hoistable() { return true; } ATTACH_OPERATIONS(); @@ -207,8 +215,8 @@ namespace Sass { ADD_PROPERTY(string, keyword); ADD_PROPERTY(Selector*, selector); public: - At_Rule(string p, size_t l, string kwd, Selector* sel = 0, Block* b = 0) - : Has_Block(p, l, b), keyword_(kwd), selector_(sel) + At_Rule(string path, Position position, string kwd, Selector* sel = 0, Block* b = 0) + : Has_Block(path, position, b), keyword_(kwd), selector_(sel) { } ATTACH_OPERATIONS(); }; @@ -221,9 +229,9 @@ namespace Sass { ADD_PROPERTY(Expression*, value); ADD_PROPERTY(bool, is_important); public: - Declaration(string p, size_t l, + Declaration(string path, Position position, String* prop, Expression* val, bool i = false) - : Statement(p, l), property_(prop), value_(val), is_important_(i) + : Statement(path, position), property_(prop), value_(val), is_important_(i) { } ATTACH_OPERATIONS(); }; @@ -238,9 +246,9 @@ namespace Sass { ADD_PROPERTY(Expression*, value); ADD_PROPERTY(bool, is_guarded); public: - Assignment(string p, size_t l, + Assignment(string path, Position position, string var, Expression* val, bool guarded = false) - : Statement(p, l), variable_(var), value_(val), is_guarded_(guarded) + : Statement(path, position), variable_(var), value_(val), is_guarded_(guarded) { } ATTACH_OPERATIONS(); }; @@ -253,8 +261,8 @@ namespace Sass { vector files_; vector urls_; public: - Import(string p, size_t l) - : Statement(p, l), + Import(string path, Position position) + : Statement(path, position), files_(vector()), urls_(vector()) { } vector& files() { return files_; } @@ -265,8 +273,8 @@ namespace Sass { class Import_Stub : public Statement { ADD_PROPERTY(string, file_name); public: - Import_Stub(string p, size_t l, string f) - : Statement(p, l), file_name_(f) + Import_Stub(string path, Position position, string f) + : Statement(path, position), file_name_(f) { } ATTACH_OPERATIONS(); }; @@ -277,8 +285,8 @@ namespace Sass { class Warning : public Statement { ADD_PROPERTY(Expression*, message); public: - Warning(string p, size_t l, Expression* msg) - : Statement(p, l), message_(msg) + Warning(string path, Position position, Expression* msg) + : Statement(path, position), message_(msg) { } ATTACH_OPERATIONS(); }; @@ -289,8 +297,8 @@ namespace Sass { class Comment : public Statement { ADD_PROPERTY(String*, text); public: - Comment(string p, size_t l, String* txt) - : Statement(p, l), text_(txt) + Comment(string path, Position position, String* txt) + : Statement(path, position), text_(txt) { } ATTACH_OPERATIONS(); }; @@ -303,8 +311,8 @@ namespace Sass { ADD_PROPERTY(Block*, consequent); ADD_PROPERTY(Block*, alternative); public: - If(string p, size_t l, Expression* pred, Block* con, Block* alt = 0) - : Statement(p, l), predicate_(pred), consequent_(con), alternative_(alt) + If(string path, Position position, Expression* pred, Block* con, Block* alt = 0) + : Statement(path, position), predicate_(pred), consequent_(con), alternative_(alt) { } ATTACH_OPERATIONS(); }; @@ -318,9 +326,9 @@ namespace Sass { ADD_PROPERTY(Expression*, upper_bound); ADD_PROPERTY(bool, is_inclusive); public: - For(string p, size_t l, + For(string path, Position position, string var, Expression* lo, Expression* hi, Block* b, bool inc) - : Has_Block(p, l, b), + : Has_Block(path, position, b), variable_(var), lower_bound_(lo), upper_bound_(hi), is_inclusive_(inc) { } ATTACH_OPERATIONS(); @@ -333,8 +341,8 @@ namespace Sass { ADD_PROPERTY(string, variable); ADD_PROPERTY(Expression*, list); public: - Each(string p, size_t l, string var, Expression* lst, Block* b) - : Has_Block(p, l, b), variable_(var), list_(lst) + Each(string path, Position position, string var, Expression* lst, Block* b) + : Has_Block(path, position, b), variable_(var), list_(lst) { } ATTACH_OPERATIONS(); }; @@ -345,8 +353,8 @@ namespace Sass { class While : public Has_Block { ADD_PROPERTY(Expression*, predicate); public: - While(string p, size_t l, Expression* pred, Block* b) - : Has_Block(p, l, b), predicate_(pred) + While(string path, Position position, Expression* pred, Block* b) + : Has_Block(path, position, b), predicate_(pred) { } ATTACH_OPERATIONS(); }; @@ -357,8 +365,8 @@ namespace Sass { class Return : public Statement { ADD_PROPERTY(Expression*, value); public: - Return(string p, size_t l, Expression* val) - : Statement(p, l), value_(val) + Return(string path, Position position, Expression* val) + : Statement(path, position), value_(val) { } ATTACH_OPERATIONS(); }; @@ -369,8 +377,8 @@ namespace Sass { class Extension : public Statement { ADD_PROPERTY(Selector*, selector); public: - Extension(string p, size_t l, Selector* s) - : Statement(p, l), selector_(s) + Extension(string path, Position position, Selector* s) + : Statement(path, position), selector_(s) { } ATTACH_OPERATIONS(); }; @@ -384,7 +392,7 @@ namespace Sass { class Parameters; typedef Environment Env; typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Context&, Signature, const string&, size_t, Backtrace*); + typedef Expression* (*Native_Function)(Env&, Context&, Signature, const string&, Position, Backtrace*); typedef const char* Signature; class Definition : public Has_Block { public: @@ -398,13 +406,13 @@ namespace Sass { ADD_PROPERTY(bool, is_overload_stub); ADD_PROPERTY(Signature, signature); public: - Definition(string p, - size_t l, + Definition(string path, + Position position, string n, Parameters* params, Block* b, Type t) - : Has_Block(p, l, b), + : Has_Block(path, position, b), name_(n), parameters_(params), environment_(0), @@ -414,14 +422,14 @@ namespace Sass { is_overload_stub_(false), signature_(0) { } - Definition(string p, - size_t l, + Definition(string path, + Position position, Signature sig, string n, Parameters* params, Native_Function func_ptr, bool overload_stub = false) - : Has_Block(p, l, 0), + : Has_Block(path, position, 0), name_(n), parameters_(params), environment_(0), @@ -431,15 +439,15 @@ namespace Sass { is_overload_stub_(overload_stub), signature_(sig) { } - Definition(string p, - size_t l, + Definition(string path, + Position position, Signature sig, string n, Parameters* params, Sass_C_Function func_ptr, bool whatever, bool whatever2) - : Has_Block(p, l, 0), + : Has_Block(path, position, 0), name_(n), parameters_(params), environment_(0), @@ -460,8 +468,8 @@ namespace Sass { ADD_PROPERTY(string, name); ADD_PROPERTY(Arguments*, arguments); public: - Mixin_Call(string p, size_t l, string n, Arguments* args, Block* b = 0) - : Has_Block(p, l, b), name_(n), arguments_(args) + Mixin_Call(string path, Position position, string n, Arguments* args, Block* b = 0) + : Has_Block(path, position, b), name_(n), arguments_(args) { } ATTACH_OPERATIONS(); }; @@ -471,7 +479,7 @@ namespace Sass { /////////////////////////////////////////////////// class Content : public Statement { public: - Content(string p, size_t l) : Statement(p, l) { } + Content(string path, Position position) : Statement(path, position) { } ATTACH_OPERATIONS(); }; @@ -498,9 +506,9 @@ namespace Sass { ADD_PROPERTY(bool, is_interpolant); ADD_PROPERTY(Concrete_Type, concrete_type); public: - Expression(string p, size_t l, + Expression(string path, Position position, bool d = false, bool i = false, Concrete_Type ct = NONE) - : AST_Node(p, l), + : AST_Node(path, position), is_delayed_(d), is_interpolant_(i), concrete_type_(ct) { } virtual operator bool() { return true; } @@ -523,9 +531,9 @@ namespace Sass { ADD_PROPERTY(Separator, separator); ADD_PROPERTY(bool, is_arglist); public: - List(string p, size_t l, + List(string path, Position position, size_t size = 0, Separator sep = SPACE, bool argl = false) - : Expression(p, l), + : Expression(path, position), Vectorized(size), separator_(sep), is_arglist_(argl) { concrete_type(LIST); } @@ -553,9 +561,9 @@ namespace Sass { ADD_PROPERTY(Expression*, left); ADD_PROPERTY(Expression*, right); public: - Binary_Expression(string p, size_t l, + Binary_Expression(string path, Position position, Type t, Expression* lhs, Expression* rhs) - : Expression(p, l), type_(t), left_(lhs), right_(rhs) + : Expression(path, position), type_(t), left_(lhs), right_(rhs) { } ATTACH_OPERATIONS(); }; @@ -570,8 +578,8 @@ namespace Sass { ADD_PROPERTY(Type, type); ADD_PROPERTY(Expression*, operand); public: - Unary_Expression(string p, size_t l, Type t, Expression* o) - : Expression(p, l), type_(t), operand_(o) + Unary_Expression(string path, Position position, Type t, Expression* o) + : Expression(path, position), type_(t), operand_(o) { } ATTACH_OPERATIONS(); }; @@ -583,8 +591,8 @@ namespace Sass { ADD_PROPERTY(string, name); ADD_PROPERTY(Arguments*, arguments); public: - Function_Call(string p, size_t l, string n, Arguments* args) - : Expression(p, l), name_(n), arguments_(args) + Function_Call(string path, Position position, string n, Arguments* args) + : Expression(path, position), name_(n), arguments_(args) { concrete_type(STRING); } ATTACH_OPERATIONS(); }; @@ -596,8 +604,8 @@ namespace Sass { ADD_PROPERTY(String*, name); ADD_PROPERTY(Arguments*, arguments); public: - Function_Call_Schema(string p, size_t l, String* n, Arguments* args) - : Expression(p, l), name_(n), arguments_(args) + Function_Call_Schema(string path, Position position, String* n, Arguments* args) + : Expression(path, position), name_(n), arguments_(args) { concrete_type(STRING); } ATTACH_OPERATIONS(); }; @@ -608,8 +616,8 @@ namespace Sass { class Variable : public Expression { ADD_PROPERTY(string, name); public: - Variable(string p, size_t l, string n) - : Expression(p, l), name_(n) + Variable(string path, Position position, string n) + : Expression(path, position), name_(n) { } ATTACH_OPERATIONS(); }; @@ -625,8 +633,8 @@ namespace Sass { ADD_PROPERTY(Type, type); ADD_PROPERTY(string, value); public: - Textual(string p, size_t l, Type t, string val) - : Expression(p, l, true), type_(t), value_(val) + Textual(string path, Position position, Type t, string val) + : Expression(path, position, true), type_(t), value_(val) { } ATTACH_OPERATIONS(); }; @@ -639,8 +647,8 @@ namespace Sass { vector numerator_units_; vector denominator_units_; public: - Number(string p, size_t l, double val, string u = "") - : Expression(p, l), + Number(string path, Position position, double val, string u = "") + : Expression(path, position), value_(val), numerator_units_(vector()), denominator_units_(vector()) @@ -756,8 +764,8 @@ namespace Sass { ADD_PROPERTY(double, a); ADD_PROPERTY(string, disp); public: - Color(string p, size_t l, double r, double g, double b, double a = 1, const string disp = "") - : Expression(p, l), r_(r), g_(g), b_(b), a_(a), disp_(disp) + Color(string path, Position position, double r, double g, double b, double a = 1, const string disp = "") + : Expression(path, position), r_(r), g_(g), b_(b), a_(a), disp_(disp) { concrete_type(COLOR); } string type() { return "color"; } static string type_name() { return "color"; } @@ -770,7 +778,7 @@ namespace Sass { class Boolean : public Expression { ADD_PROPERTY(bool, value); public: - Boolean(string p, size_t l, bool val) : Expression(p, l), value_(val) + Boolean(string path, Position position, bool val) : Expression(path, position), value_(val) { concrete_type(BOOLEAN); } virtual operator bool() { return value_; } string type() { return "bool"; } @@ -786,8 +794,8 @@ namespace Sass { class String : public Expression { ADD_PROPERTY(bool, needs_unquoting); public: - String(string p, size_t l, bool unq = false, bool delayed = false) - : Expression(p, l, delayed), needs_unquoting_(unq) + String(string path, Position position, bool unq = false, bool delayed = false) + : Expression(path, position, delayed), needs_unquoting_(unq) { concrete_type(STRING); } static string type_name() { return "string"; } virtual ~String() = 0; @@ -802,8 +810,8 @@ namespace Sass { class String_Schema : public String, public Vectorized { ADD_PROPERTY(char, quote_mark); public: - String_Schema(string p, size_t l, size_t size = 0, bool unq = false, char qm = '\0') - : String(p, l, unq), Vectorized(size), quote_mark_(qm) + String_Schema(string path, Position position, size_t size = 0, bool unq = false, char qm = '\0') + : String(path, position, unq), Vectorized(size), quote_mark_(qm) { } string type() { return "string"; } static string type_name() { return "string"; } @@ -816,17 +824,17 @@ namespace Sass { class String_Constant : public String { ADD_PROPERTY(string, value); public: - String_Constant(string p, size_t l, string val, bool unq = false) - : String(p, l, unq, true), value_(val) + String_Constant(string path, Position position, string val, bool unq = false) + : String(path, position, unq, true), value_(val) { } - String_Constant(string p, size_t l, const char* beg, bool unq = false) - : String(p, l, unq, true), value_(string(beg)) + String_Constant(string path, Position position, const char* beg, bool unq = false) + : String(path, position, unq, true), value_(string(beg)) { } - String_Constant(string p, size_t l, const char* beg, const char* end, bool unq = false) - : String(p, l, unq, true), value_(string(beg, end-beg)) + String_Constant(string path, Position position, const char* beg, const char* end, bool unq = false) + : String(path, position, unq, true), value_(string(beg, end-beg)) { } - String_Constant(string p, size_t l, const Token& tok, bool unq = false) - : String(p, l, unq, true), value_(string(tok.begin, tok.end)) + String_Constant(string path, Position position, const Token& tok, bool unq = false) + : String(path, position, unq, true), value_(string(tok.begin, tok.end)) { } string type() { return "string"; } static string type_name() { return "string"; } @@ -844,9 +852,9 @@ namespace Sass { ADD_PROPERTY(bool, is_negated); ADD_PROPERTY(bool, is_restricted); public: - Media_Query(string p, size_t l, + Media_Query(string path, Position position, String* t = 0, size_t s = 0, bool n = false, bool r = false) - : Expression(p, l), Vectorized(s), + : Expression(path, position), Vectorized(s), media_type_(t), is_negated_(n), is_restricted_(r) { } ATTACH_OPERATIONS(); @@ -860,9 +868,9 @@ namespace Sass { ADD_PROPERTY(Expression*, value); ADD_PROPERTY(bool, is_interpolated); public: - Media_Query_Expression(string p, size_t l, + Media_Query_Expression(string path, Position position, Expression* f, Expression* v, bool i = false) - : Expression(p, l), feature_(f), value_(v), is_interpolated_(i) + : Expression(path, position), feature_(f), value_(v), is_interpolated_(i) { } ATTACH_OPERATIONS(); }; @@ -872,7 +880,7 @@ namespace Sass { ////////////////// class Null : public Expression { public: - Null(string p, size_t l) : Expression(p, l) { concrete_type(NULL_VAL); } + Null(string path, Position position) : Expression(path, position) { concrete_type(NULL_VAL); } string type() { return "null"; } static string type_name() { return "null"; } bool is_invisible() { return true; } @@ -888,8 +896,8 @@ namespace Sass { ADD_PROPERTY(Expression*, expression); ADD_PROPERTY(Env*, environment); public: - Thunk(string p, size_t l, Expression* exp, Env* env = 0) - : Expression(p, l), expression_(exp), environment_(env) + Thunk(string path, Position position, Expression* exp, Env* env = 0) + : Expression(path, position), expression_(exp), environment_(env) { } }; @@ -901,12 +909,12 @@ namespace Sass { ADD_PROPERTY(Expression*, default_value); ADD_PROPERTY(bool, is_rest_parameter); public: - Parameter(string p, size_t l, + Parameter(string p, Position pos, string n, Expression* def = 0, bool rest = false) - : AST_Node(p, l), name_(n), default_value_(def), is_rest_parameter_(rest) + : AST_Node(p, pos), name_(n), default_value_(def), is_rest_parameter_(rest) { if (default_value_ && is_rest_parameter_) { - error("variable-length parameter may not have a default value", path(), line()); + error("variable-length parameter may not have a default value", path(), position()); } } ATTACH_OPERATIONS(); @@ -925,31 +933,31 @@ namespace Sass { { if (p->default_value()) { if (has_rest_parameter_) { - error("optional parameters may not be combined with variable-length parameters", p->path(), p->line()); + error("optional parameters may not be combined with variable-length parameters", p->path(), p->position()); } 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->path(), p->line()); + error("functions and mixins cannot have more than one variable-length parameter", p->path(), p->position()); } if (has_optional_parameters_) { - error("optional parameters may not be combined with variable-length parameters", p->path(), p->line()); + error("optional parameters may not be combined with variable-length parameters", p->path(), p->position()); } has_rest_parameter_ = true; } else { if (has_rest_parameter_) { - error("required parameters must precede variable-length parameters", p->path(), p->line()); + error("required parameters must precede variable-length parameters", p->path(), p->position()); } if (has_optional_parameters_) { - error("required parameters must precede optional parameters", p->path(), p->line()); + error("required parameters must precede optional parameters", p->path(), p->position()); } } } public: - Parameters(string p, size_t l) - : AST_Node(p, l), + Parameters(string path, Position position) + : AST_Node(path, position), Vectorized(), has_optional_parameters_(false), has_rest_parameter_(false) @@ -965,11 +973,11 @@ namespace Sass { ADD_PROPERTY(string, name); ADD_PROPERTY(bool, is_rest_argument); public: - Argument(string p, size_t l, Expression* val, string n = "", bool rest = false) - : Expression(p, l), value_(val), name_(n), is_rest_argument_(rest) + Argument(string p, Position pos, Expression* val, string n = "", bool rest = false) + : Expression(p, pos), value_(val), name_(n), is_rest_argument_(rest) { if (!name_.empty() && is_rest_argument_) { - error("variable-length argument may not be passed by name", path(), line()); + error("variable-length argument may not be passed by name", path(), position()); } } ATTACH_OPERATIONS(); @@ -988,31 +996,31 @@ namespace Sass { { if (!a->name().empty()) { if (has_rest_argument_) { - error("named arguments must precede variable-length argument", a->path(), a->line()); + error("named arguments must precede variable-length argument", a->path(), a->position()); } 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->path(), a->line()); + error("functions and mixins may only be called with one variable-length argument", a->path(), a->position()); } if (has_named_arguments_) { - error("functions and mixins may not be called with both named arguments and variable-length arguments", a->path(), a->line()); + error("functions and mixins may not be called with both named arguments and variable-length arguments", a->path(), a->position()); } has_rest_argument_ = true; } else { if (has_rest_argument_) { - error("ordinal arguments must precede variable-length arguments", a->path(), a->line()); + error("ordinal arguments must precede variable-length arguments", a->path(), a->position()); } if (has_named_arguments_) { - error("ordinal arguments must precede named arguments", a->path(), a->line()); + error("ordinal arguments must precede named arguments", a->path(), a->position()); } } } public: - Arguments(string p, size_t l) - : Expression(p, l), + Arguments(string path, Position position) + : Expression(path, position), Vectorized(), has_named_arguments_(false), has_rest_argument_(false) @@ -1027,8 +1035,8 @@ namespace Sass { ADD_PROPERTY(bool, has_reference); ADD_PROPERTY(bool, has_placeholder); public: - Selector(string p, size_t l, bool r = false, bool h = false) - : AST_Node(p, l), has_reference_(r), has_placeholder_(h) + Selector(string path, Position position, bool r = false, bool h = false) + : AST_Node(path, position), has_reference_(r), has_placeholder_(h) { } virtual ~Selector() = 0; virtual Selector_Placeholder* find_placeholder(); @@ -1042,8 +1050,8 @@ namespace Sass { class Selector_Schema : public Selector { ADD_PROPERTY(String*, contents); public: - Selector_Schema(string p, size_t l, String* c) - : Selector(p, l), contents_(c) + Selector_Schema(string path, Position position, String* c) + : Selector(path, position), contents_(c) { } ATTACH_OPERATIONS(); }; @@ -1053,8 +1061,8 @@ namespace Sass { //////////////////////////////////////////// class Simple_Selector : public Selector { public: - Simple_Selector(string p, size_t l) - : Selector(p, l) + Simple_Selector(string path, Position position) + : Selector(path, position) { } virtual ~Simple_Selector() = 0; }; @@ -1066,8 +1074,8 @@ namespace Sass { class Selector_Reference : public Simple_Selector { ADD_PROPERTY(Selector*, selector); public: - Selector_Reference(string p, size_t l, Selector* r = 0) - : Simple_Selector(p, l), selector_(r) + Selector_Reference(string path, Position position, Selector* r = 0) + : Simple_Selector(path, position), selector_(r) { has_reference(true); } ATTACH_OPERATIONS(); }; @@ -1078,8 +1086,8 @@ namespace Sass { class Selector_Placeholder : public Simple_Selector { ADD_PROPERTY(string, name); public: - Selector_Placeholder(string p, size_t l, string n) - : Simple_Selector(p, l), name_(n) + Selector_Placeholder(string path, Position position, string n) + : Simple_Selector(path, position), name_(n) { has_placeholder(true); } virtual Selector_Placeholder* find_placeholder(); ATTACH_OPERATIONS(); @@ -1091,8 +1099,8 @@ namespace Sass { class Type_Selector : public Simple_Selector { ADD_PROPERTY(string, name); public: - Type_Selector(string p, size_t l, string n) - : Simple_Selector(p, l), name_(n) + Type_Selector(string path, Position position, string n) + : Simple_Selector(path, position), name_(n) { } ATTACH_OPERATIONS(); }; @@ -1103,8 +1111,8 @@ namespace Sass { class Selector_Qualifier : public Simple_Selector { ADD_PROPERTY(string, name); public: - Selector_Qualifier(string p, size_t l, string n) - : Simple_Selector(p, l), name_(n) + Selector_Qualifier(string path, Position position, string n) + : Simple_Selector(path, position), name_(n) { } ATTACH_OPERATIONS(); }; @@ -1117,8 +1125,8 @@ namespace Sass { ADD_PROPERTY(string, matcher); ADD_PROPERTY(string, value); public: - Attribute_Selector(string p, size_t l, string n, string m, string v) - : Simple_Selector(p, l), name_(n), matcher_(m), value_(v) + Attribute_Selector(string path, Position position, string n, string m, string v) + : Simple_Selector(path, position), name_(n), matcher_(m), value_(v) { } ATTACH_OPERATIONS(); }; @@ -1130,8 +1138,8 @@ namespace Sass { ADD_PROPERTY(string, name); ADD_PROPERTY(String*, expression); public: - Pseudo_Selector(string p, size_t l, string n, String* expr = 0) - : Simple_Selector(p, l), name_(n), expression_(expr) + Pseudo_Selector(string path, Position position, string n, String* expr = 0) + : Simple_Selector(path, position), name_(n), expression_(expr) { } ATTACH_OPERATIONS(); }; @@ -1142,8 +1150,8 @@ namespace Sass { class Negated_Selector : public Simple_Selector { ADD_PROPERTY(Selector*, selector); public: - Negated_Selector(string p, size_t l, Selector* sel) - : Simple_Selector(p, l), selector_(sel) + Negated_Selector(string path, Position position, Selector* sel) + : Simple_Selector(path, position), selector_(sel) { } ATTACH_OPERATIONS(); }; @@ -1160,8 +1168,8 @@ namespace Sass { if (s->has_placeholder()) has_placeholder(true); } public: - Compound_Selector(string p, size_t l, size_t s = 0) - : Selector(p, l), + Compound_Selector(string path, Position position, size_t s = 0) + : Selector(path, position), Vectorized(s) { } bool operator<(const Compound_Selector& rhs) const; @@ -1183,11 +1191,11 @@ namespace Sass { ADD_PROPERTY(Compound_Selector*, head); ADD_PROPERTY(Complex_Selector*, tail); public: - Complex_Selector(string p, size_t l, + Complex_Selector(string path, Position position, Combinator c, Compound_Selector* h, Complex_Selector* t) - : Selector(p, l), combinator_(c), head_(h), tail_(t) + : Selector(path, position), combinator_(c), head_(h), tail_(t) { if ((h && h->has_reference()) || (t && t->has_reference())) has_reference(true); if ((h && h->has_placeholder()) || (t && t->has_placeholder())) has_placeholder(true); @@ -1211,8 +1219,8 @@ namespace Sass { if (c->has_placeholder()) has_placeholder(true); } public: - Selector_List(string p, size_t l, size_t s = 0) - : Selector(p, l), Vectorized(s) + Selector_List(string path, Position position, size_t s = 0) + : Selector(path, position), Vectorized(s) { } virtual Selector_Placeholder* find_placeholder(); ATTACH_OPERATIONS(); diff --git a/backtrace.hpp b/backtrace.hpp index d7a5284a88..48294de71f 100644 --- a/backtrace.hpp +++ b/backtrace.hpp @@ -2,6 +2,10 @@ #include +#ifndef SASS_POSITION +#include "position.hpp" +#endif + namespace Sass { using namespace std; @@ -10,13 +14,13 @@ namespace Sass { Backtrace* parent; string path; - size_t line; + Position position; string caller; - Backtrace(Backtrace* prn, string pth, size_t ln, string c) + Backtrace(Backtrace* prn, string pth, Position position, string c) : parent(prn), path(pth), - line(ln), + position(position), caller(c) { } @@ -33,7 +37,7 @@ namespace Sass { << (warning ? " " : "") << this_point->path << ":" - << this_point->line + << this_point->position.line << this_point->parent->caller; this_point = this_point->parent; } diff --git a/base64vlq.cpp b/base64vlq.cpp new file mode 100644 index 0000000000..1e569ca430 --- /dev/null +++ b/base64vlq.cpp @@ -0,0 +1,43 @@ +#include "base64vlq.hpp" + +namespace Sass { + + string Base64VLQ::encode(const int number) const + { + string encoded = ""; + + int vlq = to_vlq_signed(number); + + do { + int digit = vlq & VLQ_BASE_MASK; + vlq >>= VLQ_BASE_SHIFT; + if (vlq > 0) { + digit |= VLQ_CONTINUATION_BIT; + } + encoded += base64_encode(digit); + } while (vlq > 0); + + return encoded; + } + + char Base64VLQ::base64_encode(const int number) const + { + int index = number; + if (index < 0) index = 0; + if (index > 63) index = 63; + return CHARACTERS[index]; + } + + int Base64VLQ::to_vlq_signed(const int number) const + { + return (number < 0) ? ((-number) << 1) + 1 : (number << 1) + 0; + } + + const char* Base64VLQ::CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + const int Base64VLQ::VLQ_BASE_SHIFT = 5; + const int Base64VLQ::VLQ_BASE = 1 << VLQ_BASE_SHIFT; + const int Base64VLQ::VLQ_BASE_MASK = VLQ_BASE - 1; + const int Base64VLQ::VLQ_CONTINUATION_BIT = VLQ_BASE; + +} diff --git a/base64vlq.hpp b/base64vlq.hpp new file mode 100644 index 0000000000..a1e089d697 --- /dev/null +++ b/base64vlq.hpp @@ -0,0 +1,28 @@ +#define SASS_BASE64VLQ + +#include + +namespace Sass { + using std::string; + + class Base64VLQ { + + public: + + string encode(const int number) const; + + private: + + char base64_encode(const int number) const; + + int to_vlq_signed(const int number) const; + + static const char* CHARACTERS; + + static const int VLQ_BASE_SHIFT; + static const int VLQ_BASE; + static const int VLQ_BASE_MASK; + static const int VLQ_CONTINUATION_BIT; + }; + +} diff --git a/bind.cpp b/bind.cpp index ccc61fa37d..0afb86a8ce 100644 --- a/bind.cpp +++ b/bind.cpp @@ -32,7 +32,7 @@ namespace Sass { stringstream msg; msg << callee << " only takes " << LP << " arguments; " << "given " << LA; - error(msg.str(), as->path(), as->line()); + error(msg.str(), as->path(), as->position()); } Parameter* p = (*ps)[ip]; Argument* a = (*as)[ia]; @@ -53,7 +53,7 @@ namespace Sass { List* arglist = 0; if (!env->current_frame_has(p->name())) { arglist = new (ctx.mem) List(p->path(), - p->line(), + p->position(), 0, List::COMMA, true); @@ -71,7 +71,7 @@ namespace Sass { stringstream msg; msg << "parameter " << p->name() << " provided more than once in call to " << callee; - error(msg.str(), a->path(), a->line()); + error(msg.str(), a->path(), a->position()); } List* arglist = static_cast(a->value()); // if it's the last param, move the whole arglist into it @@ -97,19 +97,19 @@ namespace Sass { if (!param_map.count(a->name())) { stringstream msg; msg << callee << " has no parameter named " << a->name(); - error(msg.str(), a->path(), a->line()); + error(msg.str(), a->path(), a->position()); } if (param_map[a->name()]->is_rest_parameter()) { stringstream msg; msg << "argument " << a->name() << " of " << callee << "cannot be used as named argument"; - error(msg.str(), a->path(), a->line()); + error(msg.str(), a->path(), a->position()); } if (env->current_frame_has(a->name())) { stringstream msg; msg << "parameter " << p->name() << "provided more than once in call to " << callee; - error(msg.str(), a->path(), a->line()); + error(msg.str(), a->path(), a->position()); } env->current_frame()[a->name()] = a->value(); ++ia; @@ -128,7 +128,7 @@ namespace Sass { if (!env->current_frame_has(leftover->name())) { if (leftover->is_rest_parameter()) { env->current_frame()[leftover->name()] = new (ctx.mem) List(leftover->path(), - leftover->line(), + leftover->position(), 0, List::COMMA, true); @@ -148,7 +148,7 @@ namespace Sass { stringstream msg; msg << "required parameter " << leftover->name() << " is missing in call to " << callee; - error(msg.str(), as->path(), as->line()); + error(msg.str(), as->path(), as->position()); } } } diff --git a/context.cpp b/context.cpp index daa50f16c5..1f589e3a6a 100644 --- a/context.cpp +++ b/context.cpp @@ -45,13 +45,17 @@ namespace Sass { include_paths (initializers.include_paths()), queue (vector >()), style_sheets (map()), + source_map(File::base_name(initializers.entry_point())), image_path (initializers.image_path()), source_comments (initializers.source_comments()), source_maps (initializers.source_maps()), output_style (initializers.output_style()), + source_map_file (initializers.source_map_file()), names_to_colors (map()), colors_to_names (map()) { + cwd = get_cwd(); + collect_include_paths(initializers.include_paths_c_str()); collect_include_paths(initializers.include_paths_array()); @@ -74,7 +78,7 @@ namespace Sass { size_t i = 0; while (color_names[i]) { string name(color_names[i]); - Color* value = new (mem) Color("[COLOR TABLE]", 0, + Color* value = new (mem) Color("[COLOR TABLE]", Position(), color_values[i*3], color_values[i*3+1], color_values[i*3+2]); @@ -89,10 +93,7 @@ namespace Sass { void Context::collect_include_paths(const char* paths_str) { - const size_t wd_len = 1024; - char wd[wd_len]; - include_paths.push_back(getcwd(wd, wd_len)); - if (*include_paths.back().rbegin() != '/') include_paths.back() += '/'; + include_paths.push_back(cwd); if (paths_str) { const char* beg = paths_str; @@ -118,9 +119,7 @@ namespace Sass { void Context::collect_include_paths(const char* paths_array[]) { - const size_t wd_len = 1024; - char wd[wd_len]; - include_paths.push_back(getcwd(wd, wd_len)); + include_paths.push_back(get_cwd()); if (*include_paths.back().rbegin() != '/') include_paths.back() += '/'; // if (paths_array) { @@ -148,6 +147,7 @@ namespace Sass { sources.push_back(contents); included_files.push_back(real_path); queue.push_back(make_pair(full_path, contents)); + source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); style_sheets[full_path] = 0; return full_path; } @@ -167,6 +167,7 @@ namespace Sass { sources.push_back(contents); included_files.push_back(real_path); queue.push_back(make_pair(full_path, contents)); + source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); style_sheets[full_path] = 0; return full_path; } @@ -178,6 +179,7 @@ namespace Sass { sources.push_back(contents); included_files.push_back(real_path); queue.push_back(make_pair(full_path, contents)); + source_map.files.push_back(resolve_relative_path(real_path, source_map_file, cwd)); style_sheets[full_path] = 0; return full_path; } @@ -196,13 +198,13 @@ namespace Sass { { Block* root = 0; for (size_t i = 0; i < queue.size(); ++i) { - Parser p(Parser::from_c_str(queue[i].second, *this, queue[i].first)); + Parser p(Parser::from_c_str(queue[i].second, *this, queue[i].first, Position(1 + i, 1, 1))); Block* ast = p.parse(); if (i == 0) root = ast; style_sheets[queue[i].first] = ast; } Env tge; - Backtrace backtrace(0, "", 0, ""); + Backtrace backtrace(0, "", Position(), ""); register_built_in_functions(*this, &tge); Eval eval(*this, &tge, &backtrace); Contextualize contextualize(*this, &eval, &tge, &backtrace); @@ -220,19 +222,38 @@ namespace Sass { case COMPRESSED: { Output_Compressed output_compressed(this); root->perform(&output_compressed); - result = copy_c_str(output_compressed.get_buffer().c_str()); + string output = output_compressed.get_buffer(); + if (source_maps) output += format_source_mapping_url(source_map_file); + result = copy_c_str(output.c_str()); } break; default: { Output_Nested output_nested(source_comments, this); root->perform(&output_nested); - result = copy_c_str(output_nested.get_buffer().c_str()); + string output = output_nested.get_buffer(); + if (source_maps) output += "\n" + format_source_mapping_url(source_map_file); + result = copy_c_str(output.c_str()); + } break; } return result; } + string Context::format_source_mapping_url(const string& file) const + { + return "/*# sourceMappingURL=" + File::base_name(file) + " */"; + } + + char* Context::generate_source_map() + { + if (!source_maps) return 0; + char* result = 0; + string map = source_map.generate_source_map(this); + result = copy_c_str(map.c_str()); + return result; + } + char* Context::compile_string() { if (!source_c_str) return 0; @@ -248,6 +269,15 @@ namespace Sass { return included_files; } + string Context::get_cwd() + { + const size_t wd_len = 1024; + char wd[wd_len]; + string cwd = getcwd(wd, wd_len); + if (cwd[cwd.length() - 1] != '/') cwd += '/'; + return cwd; + } + void register_function(Context& ctx, Signature sig, Native_Function f, Env* env) { Definition* def = make_native_function(sig, f, ctx); @@ -267,7 +297,7 @@ namespace Sass { void register_overload_stub(Context& ctx, string name, Env* env) { Definition* stub = new (ctx.mem) Definition("[built-in function]", - 0, + Position(), 0, name, 0, diff --git a/context.hpp b/context.hpp index 6a1757ff7d..73877bae1f 100644 --- a/context.hpp +++ b/context.hpp @@ -13,6 +13,10 @@ #include "environment.hpp" #endif +#ifndef SASS_SOURCE_MAP +#include "source_map.hpp" +#endif + namespace Sass { using namespace std; class AST_Node; @@ -35,11 +39,13 @@ namespace Sass { vector include_paths; vector > queue; // queue of files to be parsed map style_sheets; // map of paths to ASTs + SourceMap source_map; string image_path; // for the image-url Sass function bool source_comments; bool source_maps; Output_Style output_style; + string source_map_file; map names_to_colors; map colors_to_names; @@ -54,6 +60,7 @@ namespace Sass { KWD_ARG(Data, bool, source_comments); KWD_ARG(Data, bool, source_maps); KWD_ARG(Data, Output_Style, output_style); + KWD_ARG(Data, string, source_map_file) }; Context(Data); @@ -65,11 +72,16 @@ namespace Sass { string add_file(string, string); char* compile_string(); char* compile_file(); + char* generate_source_map(); std::vector get_included_files(); private: + string format_source_mapping_url(const string& file) const; + string get_cwd(); + vector included_files; + string cwd; // void register_built_in_functions(Env* env); // void register_function(Signature sig, Native_Function f, Env* env); diff --git a/contextualize.cpp b/contextualize.cpp index 4eae0b8d7c..89548b798b 100644 --- a/contextualize.cpp +++ b/contextualize.cpp @@ -31,7 +31,7 @@ namespace Sass { To_String to_string; string result_str(s->contents()->perform(eval->with(env, backtrace))->perform(&to_string)); result_str += '{'; // the parser looks for a brace to end the selector - Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->path(), s->line()).parse_selector_group(); + Selector* result_sel = Parser::from_c_str(result_str.c_str(), ctx, s->path(), s->position()).parse_selector_group(); return result_sel->perform(this); } @@ -40,7 +40,7 @@ namespace Sass { Selector_List* p = static_cast(parent); Selector_List* ss = 0; if (p) { - ss = new (ctx.mem) Selector_List(s->path(), s->line(), p->length() * s->length()); + ss = new (ctx.mem) Selector_List(s->path(), s->position(), p->length() * s->length()); for (size_t i = 0, L = p->length(); i < L; ++i) { for (size_t j = 0, L = s->length(); j < L; ++j) { parent = (*p)[i]; @@ -50,7 +50,7 @@ namespace Sass { } } else { - ss = new (ctx.mem) Selector_List(s->path(), s->line(), s->length()); + ss = new (ctx.mem) Selector_List(s->path(), s->position(), s->length()); for (size_t j = 0, L = s->length(); j < L; ++j) { Complex_Selector* comb = static_cast((*s)[j]->perform(this)); if (comb) *ss << comb; @@ -83,7 +83,7 @@ namespace Sass { if (placeholder && extender && s->perform(&to_string) == placeholder->perform(&to_string)) { return extender; } - Compound_Selector* ss = new (ctx.mem) Compound_Selector(s->path(), s->line(), s->length()); + Compound_Selector* ss = new (ctx.mem) Compound_Selector(s->path(), s->position(), s->length()); for (size_t i = 0, L = s->length(); i < L; ++i) { Simple_Selector* simp = static_cast((*s)[i]->perform(this)); if (simp) *ss << simp; @@ -96,7 +96,7 @@ namespace Sass { Selector* old_parent = parent; parent = 0; Negated_Selector* neg = new (ctx.mem) Negated_Selector(s->path(), - s->line(), + s->position(), s->selector()->perform(this)); parent = old_parent; return neg; diff --git a/error_handling.cpp b/error_handling.cpp index 8c0132b331..3f385a4a18 100644 --- a/error_handling.cpp +++ b/error_handling.cpp @@ -1,25 +1,28 @@ +#ifndef SASS_ERROR_HANDLING #include "error_handling.hpp" +#endif + #include "backtrace.hpp" #include "prelexer.hpp" namespace Sass { - Error::Error(Type type, string path, size_t line, string message) - : type(type), path(path), line(line), message(message) + Error::Error(Type type, string path, Position position, string message) + : type(type), path(path), position(position), message(message) { } - void error(string msg, string path, size_t line) - { throw Error(Error::syntax, path, line, msg); } + void error(string msg, string path, Position position) + { throw Error(Error::syntax, path, position, msg); } - void error(string msg, string path, size_t line, Backtrace* bt) + void error(string msg, string path, Position position, Backtrace* bt) { if (!path.empty() && Prelexer::string_constant(path.c_str())) path = path.substr(1, path.size() - 1); - Backtrace top(bt, path, line, ""); + Backtrace top(bt, path, position, ""); msg += top.to_string(); - throw Error(Error::syntax, path, line, msg); + throw Error(Error::syntax, path, position, msg); } } \ No newline at end of file diff --git a/error_handling.hpp b/error_handling.hpp index f98e6be1aa..c944b2da1d 100644 --- a/error_handling.hpp +++ b/error_handling.hpp @@ -1,6 +1,10 @@ #define SASS_ERROR_HANDLING #include +#ifndef SASS_POSITION +#include "position.hpp" +#endif + namespace Sass { using namespace std; @@ -11,14 +15,14 @@ namespace Sass { Type type; string path; - size_t line; + Position position; string message; - Error(Type type, string path, size_t line, string message); + Error(Type type, string path, Position position, string message); }; - void error(string msg, string path, size_t line); - void error(string msg, string path, size_t line, Backtrace* bt); + void error(string msg, string path, Position position); + void error(string msg, string path, Position position, Backtrace* bt); } \ No newline at end of file diff --git a/eval.cpp b/eval.cpp index dceab96ba8..458bf10f72 100644 --- a/eval.cpp +++ b/eval.cpp @@ -77,24 +77,24 @@ namespace Sass { string variable(f->variable()); Expression* low = f->lower_bound()->perform(this); if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->path(), low->line()); + error("lower bound of `@for` directive must be numeric", low->path(), low->position()); } Expression* high = f->upper_bound()->perform(this); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->path(), high->line()); + error("upper bound of `@for` directive must be numeric", high->path(), high->position()); } double lo = static_cast(low)->value(); double hi = static_cast(high)->value(); if (f->is_inclusive()) ++hi; Env new_env; - new_env[variable] = new (ctx.mem) Number(low->path(), low->line(), lo); + new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), lo); new_env.link(env); env = &new_env; Block* body = f->block(); Expression* val = 0; for (double i = lo; i < hi; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->line(), ++i)) { + (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) { val = body->perform(this); if (val) break; } @@ -108,7 +108,7 @@ namespace Sass { Expression* expr = e->list()->perform(this); List* list = 0; if (expr->concrete_type() != Expression::LIST) { - list = new (ctx.mem) List(expr->path(), expr->line(), 1, List::COMMA); + list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA); *list << expr; } else { @@ -153,7 +153,7 @@ namespace Sass { string indent(" "); string result(unquote(message->perform(&to_string))); cerr << prefix << result; - Backtrace top(backtrace, w->path(), w->line(), ""); + Backtrace top(backtrace, w->path(), w->position(), ""); cerr << top.to_string(true); cerr << endl << endl; return 0; @@ -162,7 +162,7 @@ namespace Sass { Expression* Eval::operator()(List* l) { List* ll = new (ctx.mem) List(l->path(), - l->line(), + l->position(), l->length(), l->separator(), l->is_arglist()); @@ -176,7 +176,7 @@ namespace Sass { bool eq(Expression*, Expression*, Context&, Eval*); bool lt(Expression*, Expression*, Context&); // -- arithmetic on the combinations that matter - Expression* op_numbers(Context&, Binary_Expression::Type, Expression*, Expression*); + Expression* op_numbers(Context&, Binary_Expression*, Expression*, Expression*); Expression* op_number_color(Context&, Binary_Expression::Type, Expression*, Expression*); Expression* op_color_number(Context&, Binary_Expression::Type, Expression*, Expression*); Expression* op_colors(Context&, Binary_Expression::Type, Expression*, Expression*); @@ -206,12 +206,12 @@ namespace Sass { // see if it's a relational expression switch(op_type) { - case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->path(), b->line(), eq(lhs, rhs, ctx)); - case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->path(), b->line(), !eq(lhs, rhs, ctx)); - case Binary_Expression::GT: return new (ctx.mem) Boolean(b->path(), b->line(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx)); - case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->path(), b->line(), !lt(lhs, rhs, ctx)); - case Binary_Expression::LT: return new (ctx.mem) Boolean(b->path(), b->line(), lt(lhs, rhs, ctx)); - case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->path(), b->line(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx)); + case Binary_Expression::EQ: return new (ctx.mem) Boolean(b->path(), b->position(), eq(lhs, rhs, ctx)); + case Binary_Expression::NEQ: return new (ctx.mem) Boolean(b->path(), b->position(), !eq(lhs, rhs, ctx)); + case Binary_Expression::GT: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx) && !eq(lhs, rhs, ctx)); + case Binary_Expression::GTE: return new (ctx.mem) Boolean(b->path(), b->position(), !lt(lhs, rhs, ctx)); + case Binary_Expression::LT: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx)); + case Binary_Expression::LTE: return new (ctx.mem) Boolean(b->path(), b->position(), lt(lhs, rhs, ctx) || eq(lhs, rhs, ctx)); default: break; } @@ -220,7 +220,7 @@ namespace Sass { Expression::Concrete_Type r_type = rhs->concrete_type(); if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) { - return op_numbers(ctx, op_type, lhs, rhs); + return op_numbers(ctx, b, lhs, rhs); } if (l_type == Expression::NUMBER && r_type == Expression::COLOR) { return op_number_color(ctx, op_type, lhs, rhs); @@ -249,10 +249,10 @@ namespace Sass { // Special cases: +/- variables which evaluate to null ouput just +/-, // but +/- null itself outputs the string if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) { - u->operand(new (ctx.mem) String_Constant(u->path(), u->line(), "")); + u->operand(new (ctx.mem) String_Constant(u->path(), u->position(), "")); } String_Constant* result = new (ctx.mem) String_Constant(u->path(), - u->line(), + u->position(), u->perform(&to_string)); return result; } @@ -269,12 +269,12 @@ namespace Sass { // if it doesn't exist, just pass it through as a literal if (!env->has(full_name)) { Function_Call* lit = new (ctx.mem) Function_Call(c->path(), - c->line(), + c->position(), c->name(), args); To_String to_string; return new (ctx.mem) String_Constant(c->path(), - c->line(), + c->position(), lit->perform(&to_string)); } @@ -305,12 +305,12 @@ namespace Sass { Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); backtrace = &here; result = body->perform(this); if (!result) { - error(string("function ") + c->name() + " did not return a value", c->path(), c->line()); + error(string("function ") + c->name() + " did not return a value", c->path(), c->position()); } backtrace = here.parent; env = old_env; @@ -322,10 +322,10 @@ namespace Sass { Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); backtrace = &here; - result = func(*env, ctx, def->signature(), c->path(), c->line(), backtrace); + result = func(*env, ctx, def->signature(), c->path(), c->position(), backtrace); backtrace = here.parent; env = old_env; @@ -337,15 +337,15 @@ namespace Sass { Env* old_env = env; env = &new_env; - Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); backtrace = &here; To_C to_c; Sass_Value c_val = c_func(args->perform(&to_c)); if (c_val.unknown.tag == SASS_ERROR) { - error("error in C function " + c->name() + ": " + c_val.error.message, c->path(), c->line(), backtrace); + error("error in C function " + c->name() + ": " + c_val.error.message, c->path(), c->position(), backtrace); } - result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->line()); + result = cval_to_astnode(c_val, ctx, backtrace, c->path(), c->position()); backtrace = here.parent; env = old_env; @@ -356,7 +356,7 @@ namespace Sass { stringstream ss; ss << full_name << arity; string resolved_name(ss.str()); - if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->path(), c->line()); + if (!env->has(resolved_name)) error("overloaded function `" + string(c->name()) + "` given wrong number of arguments", c->path(), c->position()); Definition* resolved_def = static_cast((*env)[resolved_name]); params = resolved_def->parameters(); Env newer_env; @@ -365,10 +365,10 @@ namespace Sass { Env* old_env = env; env = &newer_env; - Backtrace here(backtrace, c->path(), c->line(), ", in function `" + c->name() + "`"); + Backtrace here(backtrace, c->path(), c->position(), ", in function `" + c->name() + "`"); backtrace = &here; - result = resolved_def->native_function()(*env, ctx, resolved_def->signature(), c->path(), c->line(), backtrace); + result = resolved_def->native_function()(*env, ctx, resolved_def->signature(), c->path(), c->position(), backtrace); backtrace = here.parent; env = old_env; @@ -376,6 +376,7 @@ namespace Sass { // backtrace = here.parent; // env = old_env; + result->position(c->position()); return result; } @@ -383,7 +384,7 @@ namespace Sass { { Expression* evaluated_name = s->name()->perform(this); Expression* evaluated_args = s->arguments()->perform(this); - String_Schema* ss = new (ctx.mem) String_Schema(s->path(), s->line(), 2); + String_Schema* ss = new (ctx.mem) String_Schema(s->path(), s->position(), 2); (*ss) << evaluated_name << evaluated_args; return ss->perform(this); } @@ -395,7 +396,7 @@ namespace Sass { string name(v->name()); Expression* value = 0; if (env->has(name)) value = static_cast((*env)[name]); - else error("unbound variable " + v->name(), v->path(), v->line()); + else error("unbound variable " + v->name(), v->path(), v->position()); // cerr << "fetched a value of type " << typeid(*value).name() << endl; // if (value) cerr << "fetched a value: " << value->perform(&to_string) << endl; return value; @@ -409,18 +410,18 @@ namespace Sass { { case Textual::NUMBER: result = new (ctx.mem) Number(t->path(), - t->line(), + t->position(), atof(t->value().c_str())); break; case Textual::PERCENTAGE: result = new (ctx.mem) Number(t->path(), - t->line(), + t->position(), atof(t->value().c_str()), "%"); break; case Textual::DIMENSION: result = new (ctx.mem) Number(t->path(), - t->line(), + t->position(), atof(t->value().c_str()), Token(number(t->value().c_str()))); break; @@ -431,7 +432,7 @@ namespace Sass { string g(hext.substr(2,2)); string b(hext.substr(4,2)); result = new (ctx.mem) Color(t->path(), - t->line(), + t->position(), static_cast(strtol(r.c_str(), NULL, 16)), static_cast(strtol(g.c_str(), NULL, 16)), static_cast(strtol(b.c_str(), NULL, 16)), @@ -440,7 +441,7 @@ namespace Sass { } else { result = new (ctx.mem) Color(t->path(), - t->line(), + t->position(), static_cast(strtol(string(2,hext[0]).c_str(), NULL, 16)), static_cast(strtol(string(2,hext[1]).c_str(), NULL, 16)), static_cast(strtol(string(2,hext[2]).c_str(), NULL, 16)), @@ -488,7 +489,7 @@ namespace Sass { } } return new (ctx.mem) String_Constant(s->path(), - s->line(), + s->position(), acc); } @@ -497,7 +498,7 @@ namespace Sass { if (!s->is_delayed() && ctx.names_to_colors.count(s->value())) { Color* c = new (ctx.mem) Color(*ctx.names_to_colors[s->value()]); c->path(s->path()); - c->line(s->line()); + c->position(s->position()); return c; } return s; @@ -508,7 +509,7 @@ namespace Sass { String* t = q->media_type(); t = static_cast(t ? t->perform(this) : 0); Media_Query* qq = new (ctx.mem) Media_Query(q->path(), - q->line(), + q->position(), t, q->length(), q->is_negated(), @@ -526,7 +527,7 @@ namespace Sass { Expression* value = e->value(); value = (value ? value->perform(this) : 0); return new (ctx.mem) Media_Query_Expression(e->path(), - e->line(), + e->position(), feature, value, e->is_interpolated()); @@ -545,7 +546,7 @@ namespace Sass { val->is_delayed(false); if (a->is_rest_argument() && (val->concrete_type() != Expression::LIST)) { List* wrapper = new (ctx.mem) List(val->path(), - val->line(), + val->position(), 0, List::COMMA, true); @@ -553,7 +554,7 @@ namespace Sass { val = wrapper; } return new (ctx.mem) Argument(a->path(), - a->line(), + a->position(), val, a->name(), a->is_rest_argument()); @@ -561,7 +562,7 @@ namespace Sass { Expression* Eval::operator()(Arguments* a) { - Arguments* aa = new (ctx.mem) Arguments(a->path(), a->line()); + Arguments* aa = new (ctx.mem) Arguments(a->path(), a->position()); for (size_t i = 0, L = a->length(); i < L; ++i) { *aa << static_cast((*a)[i]->perform(this)); } @@ -635,7 +636,7 @@ namespace Sass { { if (lhs->concrete_type() != Expression::NUMBER || rhs->concrete_type() != Expression::NUMBER) - error("may only compare numbers", lhs->path(), lhs->line()); + error("may only compare numbers", lhs->path(), lhs->position()); Number* l = static_cast(lhs); Number* r = static_cast(rhs); Number tmp_r(*r); @@ -643,22 +644,23 @@ namespace Sass { string l_unit(l->unit()); string r_unit(tmp_r.unit()); if (!l_unit.empty() && !r_unit.empty() && l->unit() != tmp_r.unit()) { - error("cannot compare numbers with incompatible units", l->path(), l->line()); + error("cannot compare numbers with incompatible units", l->path(), l->position()); } return l->value() < tmp_r.value(); } - Expression* op_numbers(Context& ctx, Binary_Expression::Type op, Expression* lhs, Expression* rhs) + Expression* op_numbers(Context& ctx, Binary_Expression* b, Expression* lhs, Expression* rhs) { Number* l = static_cast(lhs); Number* r = static_cast(rhs); double lv = l->value(); double rv = r->value(); + Binary_Expression::Type op = b->type(); if (op == Binary_Expression::DIV && !rv) { - return new (ctx.mem) String_Constant(l->path(), l->line(), "Infinity"); + return new (ctx.mem) String_Constant(l->path(), b->position(), "Infinity"); } if (op == Binary_Expression::MOD && !rv) { - error("division by zero", r->path(), r->line()); + error("division by zero", r->path(), r->position()); } Number tmp(*r); @@ -667,9 +669,10 @@ namespace Sass { string r_unit(tmp.unit()); if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) { - error("cannot add or subtract numbers with incompatible units", l->path(), l->line()); + error("cannot add or subtract numbers with incompatible units", l->path(), l->position()); } Number* v = new (ctx.mem) Number(*l); + v->position(b->position()); if (l_unit.empty() && (op == Binary_Expression::ADD || op == Binary_Expression::SUB)) { v->numerator_units() = r->numerator_units(); v->denominator_units() = r->denominator_units(); @@ -709,7 +712,7 @@ namespace Sass { case Binary_Expression::ADD: case Binary_Expression::MUL: { return new (ctx.mem) Color(l->path(), - l->line(), + l->position(), ops[op](lv, r->r()), ops[op](lv, r->g()), ops[op](lv, r->b()), @@ -720,13 +723,13 @@ namespace Sass { string sep(op == Binary_Expression::SUB ? "-" : "/"); To_String to_string; return new (ctx.mem) String_Constant(l->path(), - l->line(), + l->position(), l->perform(&to_string) + sep + r->perform(&to_string)); } break; case Binary_Expression::MOD: { - error("cannot divide a number by a color", r->path(), r->line()); + error("cannot divide a number by a color", r->path(), r->position()); } break; default: break; // caller should ensure that we don't get here } @@ -739,9 +742,9 @@ namespace Sass { Color* l = static_cast(lhs); Number* r = static_cast(rhs); double rv = r->value(); - if (op == Binary_Expression::DIV && !rv) error("division by zero", r->path(), r->line()); + if (op == Binary_Expression::DIV && !rv) error("division by zero", r->path(), r->position()); return new (ctx.mem) Color(l->path(), - l->line(), + l->position(), ops[op](l->r(), rv), ops[op](l->g(), rv), ops[op](l->b(), rv), @@ -753,14 +756,14 @@ namespace Sass { Color* l = static_cast(lhs); Color* r = static_cast(rhs); if (l->a() != r->a()) { - error("alpha channels must be equal when combining colors", r->path(), r->line()); + error("alpha channels must be equal when combining colors", r->path(), r->position()); } if ((op == Binary_Expression::DIV || op == Binary_Expression::MOD) && (!r->r() || !r->g() ||!r->b())) { - error("division by zero", r->path(), r->line()); + error("division by zero", r->path(), r->position()); } return new (ctx.mem) Color(l->path(), - l->line(), + l->position(), ops[op](l->r(), r->r()), ops[op](l->g(), r->g()), ops[op](l->b(), r->b()), @@ -795,8 +798,8 @@ namespace Sass { rtype == Expression::STRING && !rhs->is_delayed() && ctx.names_to_colors.count(rstr)) { return op_number_color(ctx, op, rhs, ctx.names_to_colors[rstr]); } - if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->path(), lhs->line()); - if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->path(), lhs->line()); + if (op == Binary_Expression::MUL) error("invalid operands for multiplication", lhs->path(), lhs->position()); + if (op == Binary_Expression::MOD) error("invalid operands for modulo", lhs->path(), lhs->position()); string sep; switch (op) { case Binary_Expression::SUB: sep = "-"; break; @@ -808,40 +811,40 @@ namespace Sass { else if (rstr[0] == '"' || rstr[0] == '\'') q = rstr[0]; string result(unquote(lstr) + sep + unquote(rstr)); return new (ctx.mem) String_Constant(lhs->path(), - lhs->line(), + lhs->position(), unquoted ? result : quote(result, q)); } - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path, size_t line) + Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path, Position position) { using std::strlen; using std::strcpy; Expression* e = 0; switch (v.unknown.tag) { case SASS_BOOLEAN: { - e = new (ctx.mem) Boolean(path, line, v.boolean.value); + e = new (ctx.mem) Boolean(path, position, v.boolean.value); } break; case SASS_NUMBER: { - e = new (ctx.mem) Number(path, line, v.number.value, v.number.unit); + e = new (ctx.mem) Number(path, position, v.number.value, v.number.unit); } break; case SASS_COLOR: { - e = new (ctx.mem) Color(path, line, v.color.r, v.color.g, v.color.b, v.color.a); + e = new (ctx.mem) Color(path, position, v.color.r, v.color.g, v.color.b, v.color.a); } break; case SASS_STRING: { - e = new (ctx.mem) String_Constant(path, line, v.string.value); + e = new (ctx.mem) String_Constant(path, position, v.string.value); } break; case SASS_LIST: { - List* l = new (ctx.mem) List(path, line, v.list.length, v.list.separator == SASS_COMMA ? List::COMMA : List::SPACE); + List* l = new (ctx.mem) List(path, position, v.list.length, v.list.separator == SASS_COMMA ? List::COMMA : List::SPACE); for (size_t i = 0, L = v.list.length; i < L; ++i) { - *l << cval_to_astnode(v.list.values[i], ctx, backtrace, path, line); + *l << cval_to_astnode(v.list.values[i], ctx, backtrace, path, position); } e = l; } break; case SASS_NULL: { - e = new (ctx.mem) Null(path, line); + e = new (ctx.mem) Null(path, position); } break; case SASS_ERROR: { - error("error in C function: " + string(v.error.message), path, line, backtrace); + error("error in C function: " + string(v.error.message), path, position, backtrace); } break; } return e; diff --git a/eval.hpp b/eval.hpp index 5e644651ff..c17e25d687 100644 --- a/eval.hpp +++ b/eval.hpp @@ -14,6 +14,10 @@ #include "sass.h" #endif +#ifndef SASS_POSITION +#include "position.hpp" +#endif + namespace Sass { using namespace std; @@ -66,7 +70,7 @@ namespace Sass { Expression* fallback(U x) { return fallback_impl(x); } }; - Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path = "", size_t line = 0); + Expression* cval_to_astnode(Sass_Value v, Context& ctx, Backtrace* backtrace, string path = "", Position position = Position()); bool eq(Expression*, Expression*, Context&); bool lt(Expression*, Expression*, Context&); diff --git a/expand.cpp b/expand.cpp index f41877e68a..c1c720695a 100644 --- a/expand.cpp +++ b/expand.cpp @@ -31,7 +31,7 @@ namespace Sass { Env new_env; new_env.link(*env); env = &new_env; - Block* bb = new (ctx.mem) Block(b->path(), b->line(), b->length(), b->is_root()); + Block* bb = new (ctx.mem) Block(b->path(), b->position(), b->length(), b->is_root()); block_stack.push_back(bb); append_block(b); block_stack.pop_back(); @@ -46,7 +46,7 @@ namespace Sass { Selector* sel_ctx = r->selector()->perform(contextualize->with(selector_stack.back(), env, backtrace)); selector_stack.push_back(sel_ctx); Ruleset* rr = new (ctx.mem) Ruleset(r->path(), - r->line(), + r->position(), sel_ctx, r->block()->perform(this)->block()); selector_stack.pop_back(); @@ -63,10 +63,10 @@ namespace Sass { Statement* stm = (*expanded_block)[i]; if (typeid(*stm) == typeid(Declaration)) { Declaration* dec = static_cast(stm); - String_Schema* combined_prop = new (ctx.mem) String_Schema(p->path(), p->line()); + String_Schema* combined_prop = new (ctx.mem) String_Schema(p->path(), p->position()); if (!property_stack.empty()) { *combined_prop << property_stack.back() - << new (ctx.mem) String_Constant(p->path(), p->line(), "-") + << new (ctx.mem) String_Constant(p->path(), p->position(), "-") << dec->property(); // TODO: eval the prop into a string constant } else { @@ -76,7 +76,7 @@ namespace Sass { *current_block << dec; } else { - error("contents of namespaced properties must result in style declarations only", stm->path(), stm->line(), backtrace); + error("contents of namespaced properties must result in style declarations only", stm->path(), stm->position(), backtrace); } } @@ -89,7 +89,7 @@ namespace Sass { { Expression* media_queries = m->media_queries()->perform(eval->with(env, backtrace)); Media_Block* mm = new (ctx.mem) Media_Block(m->path(), - m->line(), + m->position(), static_cast(media_queries), m->block()->perform(this)->block()); mm->enclosing_selector(selector_stack.back()); @@ -104,7 +104,7 @@ namespace Sass { if (as) as = as->perform(contextualize->with(0, env, backtrace)); Block* bb = ab ? ab->perform(this)->block() : 0; At_Rule* aa = new (ctx.mem) At_Rule(a->path(), - a->line(), + a->position(), a->keyword(), as, bb); @@ -117,7 +117,7 @@ namespace Sass { String* old_p = d->property(); String* new_p = static_cast(old_p->perform(eval->with(env, backtrace))); return new (ctx.mem) Declaration(d->path(), - d->line(), + d->position(), new_p, d->value()->perform(eval->with(env, backtrace)), d->is_important()); @@ -176,23 +176,23 @@ namespace Sass { string variable(f->variable()); Expression* low = f->lower_bound()->perform(eval->with(env, backtrace)); if (low->concrete_type() != Expression::NUMBER) { - error("lower bound of `@for` directive must be numeric", low->path(), low->line(), backtrace); + error("lower bound of `@for` directive must be numeric", low->path(), low->position(), backtrace); } Expression* high = f->upper_bound()->perform(eval->with(env, backtrace)); if (high->concrete_type() != Expression::NUMBER) { - error("upper bound of `@for` directive must be numeric", high->path(), high->line(), backtrace); + error("upper bound of `@for` directive must be numeric", high->path(), high->position(), backtrace); } double lo = static_cast(low)->value(); double hi = static_cast(high)->value(); if (f->is_inclusive()) ++hi; Env new_env; - new_env[variable] = new (ctx.mem) Number(low->path(), low->line(), lo); + new_env[variable] = new (ctx.mem) Number(low->path(), low->position(), lo); new_env.link(env); env = &new_env; Block* body = f->block(); for (double i = lo; i < hi; - (*env)[variable] = new (ctx.mem) Number(low->path(), low->line(), ++i)) { + (*env)[variable] = new (ctx.mem) Number(low->path(), low->position(), ++i)) { append_block(body); } env = new_env.parent(); @@ -205,7 +205,7 @@ namespace Sass { Expression* expr = e->list()->perform(eval->with(env, backtrace)); List* list = 0; if (expr->concrete_type() != Expression::LIST) { - list = new (ctx.mem) List(expr->path(), expr->line(), 1, List::COMMA); + list = new (ctx.mem) List(expr->path(), expr->position(), 1, List::COMMA); *list << expr; } else { @@ -236,7 +236,7 @@ namespace Sass { Statement* Expand::operator()(Return* r) { - error("@return may only be used within a function", r->path(), r->line(), backtrace); + error("@return may only be used within a function", r->path(), r->position(), backtrace); return 0; } @@ -246,11 +246,11 @@ namespace Sass { if (!extender) return 0; Selector_List* extendee = static_cast(e->selector()->perform(contextualize->with(0, env, backtrace))); if (extendee->length() != 1) { - error("selector groups may not be extended", extendee->path(), extendee->line(), backtrace); + error("selector groups may not be extended", extendee->path(), extendee->position(), backtrace); } Complex_Selector* c = (*extendee)[0]; if (!c->head() || c->tail()) { - error("nested selectors may not be extended", c->path(), c->line(), backtrace); + error("nested selectors may not be extended", c->path(), c->position(), backtrace); } Compound_Selector* s = c->head(); for (size_t i = 0, L = extender->length(); i < L; ++i) { @@ -274,23 +274,23 @@ namespace Sass { { string full_name(c->name() + "[m]"); if (!env->has(full_name)) { - error("no mixin named " + c->name(), c->path(), c->line(), backtrace); + error("no mixin named " + c->name(), c->path(), c->position(), backtrace); } Definition* def = static_cast((*env)[full_name]); Block* body = def->block(); Parameters* params = def->parameters(); Arguments* args = static_cast(c->arguments() ->perform(eval->with(env, backtrace))); - Backtrace here(backtrace, c->path(), c->line(), ", in mixin `" + c->name() + "`"); + Backtrace here(backtrace, c->path(), c->position(), ", in mixin `" + c->name() + "`"); backtrace = &here; Env new_env; new_env.link(def->environment()); if (c->block()) { - // reprsent mixin content blocks as thunks/closures + // represent mixin content blocks as thunks/closures Definition* thunk = new (ctx.mem) Definition(c->path(), - c->line(), + c->position(), "@content", - new (ctx.mem) Parameters(c->path(), c->line()), + new (ctx.mem) Parameters(c->path(), c->position()), c->block(), Definition::MIXIN); thunk->environment(env); @@ -310,17 +310,17 @@ namespace Sass { // convert @content directives into mixin calls to the underlying thunk if (!env->has("@content[m]")) return 0; Mixin_Call* call = new (ctx.mem) Mixin_Call(c->path(), - c->line(), + c->position(), "@content", - new (ctx.mem) Arguments(c->path(), c->line())); + new (ctx.mem) Arguments(c->path(), c->position())); return call->perform(this); } inline Statement* Expand::fallback_impl(AST_Node* n) { - error("unknown internal error; please contact the LibSass maintainers", n->path(), n->line(), backtrace); - String_Constant* msg = new (ctx.mem) String_Constant("", 0, string("`Expand` doesn't handle ") + typeid(*n).name()); - return new (ctx.mem) Warning("", 0, msg); + error("unknown internal error; please contact the LibSass maintainers", n->path(), n->position(), backtrace); + String_Constant* msg = new (ctx.mem) String_Constant("", Position(), string("`Expand` doesn't handle ") + typeid(*n).name()); + return new (ctx.mem) Warning("", Position(), msg); } inline void Expand::append_block(Block* b) diff --git a/extend.cpp b/extend.cpp index aa6004dd3b..b2fd84bbe0 100644 --- a/extend.cpp +++ b/extend.cpp @@ -25,14 +25,14 @@ namespace Sass { bool extended = false; if (sg->has_placeholder()) { // To_String to_string; - Compound_Selector* placeholder = new (ctx.mem) Compound_Selector(sg->path(), sg->line(), 1); + Compound_Selector* placeholder = new (ctx.mem) Compound_Selector(sg->path(), sg->position(), 1); *placeholder << sg->find_placeholder(); // cerr << "placeholder: " << placeholder->perform(&to_string) << endl; // if the placeholder needs to be subbed if (extensions.count(*placeholder)) { // cerr << "need to sub " << placeholder->perform(&to_string) << " " << extensions.count(*placeholder) << " times" << endl; // perform each substitution and append it to the selector group of the ruleset - ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), extensions.count(*placeholder)); + ng = new (ctx.mem) Selector_List(sg->path(), sg->position(), extensions.count(*placeholder)); for (multimap::iterator extender = extensions.lower_bound(*placeholder), E = extensions.upper_bound(*placeholder); extender != E; ++extender) { @@ -51,7 +51,7 @@ namespace Sass { } } else { - ng = new (ctx.mem) Selector_List(sg->path(), sg->line(), sg->length()); + ng = new (ctx.mem) Selector_List(sg->path(), sg->position(), sg->length()); // for each selector in the group for (size_t i = 0, L = sg->length(); i < L; ++i) { Complex_Selector* sel = (*sg)[i]; @@ -87,11 +87,11 @@ namespace Sass { Selector_List* Extend::generate_extension(Complex_Selector* extendee, Complex_Selector* extender) { To_String to_string; - Selector_List* new_group = new (ctx.mem) Selector_List(extendee->path(), extendee->line()); + Selector_List* new_group = new (ctx.mem) Selector_List(extendee->path(), extendee->position()); Complex_Selector* extendee_context = extendee->context(ctx); Complex_Selector* extender_context = extender->context(ctx); if (extendee_context && extender_context) { - Complex_Selector* base = new (ctx.mem) Complex_Selector(new_group->path(), new_group->line(), Complex_Selector::ANCESTOR_OF, extender->base(), 0); + Complex_Selector* base = new (ctx.mem) Complex_Selector(new_group->path(), new_group->position(), Complex_Selector::ANCESTOR_OF, extender->base(), 0); extendee_context->innermost()->tail(extender); *new_group << extendee_context; // make another one so we don't erroneously share tails diff --git a/file.cpp b/file.cpp index fceaaed3b0..d17334f18a 100644 --- a/file.cpp +++ b/file.cpp @@ -31,17 +31,68 @@ namespace Sass { { if (l.empty()) return r; if (r.empty()) return l; - if (r[0] == '/') return r; - // TODO: UN-HACKIFY THIS - #ifdef _WIN32 - if (r.length() >= 2 && isalpha(r[0]) && r[1] == ':') return r; - #endif + if (is_absolute_path(r)) return r; if (l[l.length()-1] != '/') l += '/'; + + while ((r.length() > 3) && (r.substr(0, 3) == "../")) { + r = r.substr(3); + size_t index = l.find_last_of('/', l.length() - 2); + l = l.substr(0, index + 1); + } + return l + r; } + + bool is_absolute_path(const string& path) + { + if (path[0] == '/') return true; + // TODO: UN-HACKIFY THIS + #ifdef _WIN32 + if (path.length() >= 2 && isalpha(path[0]) && path[1] == ':') return true; + #endif + return false; + } + + string make_absolute_path(const string& path, const string& cwd) + { + return (is_absolute_path(path) ? path : join_paths(cwd, path)); + } - char* resolve_and_load(string path, string& real_path) + string resolve_relative_path(const string& uri, const string& base, const string& cwd) + { + string absolute_uri = make_absolute_path(uri, cwd); + string absolute_base = make_absolute_path(base, cwd); + + string stripped_uri = ""; + string stripped_base = ""; + + size_t index = 0; + size_t minSize = min(absolute_uri.size(), absolute_base.size()); + for (size_t i = 0; i < minSize; ++i) { + if (absolute_uri[i] != absolute_base[i]) break; + if (absolute_uri[i] == '/') index = i + 1; + } + for (size_t i = index; i < absolute_uri.size(); ++i) { + stripped_uri += absolute_uri[i]; + } + for (size_t i = index; i < absolute_base.size(); ++i) { + stripped_base += absolute_base[i]; + } + size_t directories = 0; + for (size_t i = 0; i < stripped_base.size(); ++i) { + if (stripped_base[i] == '/') ++directories; + } + string result = ""; + for (size_t i = 0; i < directories; ++i) { + result += "../"; + } + result += stripped_uri; + + return result; + } + + char* resolve_and_load(string path, string& real_path) { // Resolution order for ambiguous imports: // (1) filename as given diff --git a/file.hpp b/file.hpp index d827aad143..e095040cda 100644 --- a/file.hpp +++ b/file.hpp @@ -1,13 +1,16 @@ #include namespace Sass { - using namespace std; - struct Context; - namespace File { - string base_name(string); - string dir_name(string); - string join_paths(string, string); - char* resolve_and_load(string path, string& real_path); - char* read_file(string path); - } + using namespace std; + struct Context; + namespace File { + string base_name(string); + string dir_name(string); + string join_paths(string, string); + bool is_absolute_path(const string& path); + string make_absolute_path(const string& path, const string& cwd); + string resolve_relative_path(const string& uri, const string& base, const string& cwd); + char* resolve_and_load(string path, string& real_path); + char* read_file(string path); + } } diff --git a/functions.cpp b/functions.cpp index aeea960ac4..3ef0647de3 100644 --- a/functions.cpp +++ b/functions.cpp @@ -14,8 +14,8 @@ #include #include -#define ARG(argname, argtype) get_arg(argname, env, sig, path, line, backtrace) -#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, path, line, lo, hi, backtrace) +#define ARG(argname, argtype) get_arg(argname, env, sig, path, position, backtrace) +#define ARGR(argname, argtype, lo, hi) get_arg_r(argname, env, sig, path, position, lo, hi, backtrace) namespace Sass { using std::stringstream; @@ -23,12 +23,12 @@ namespace Sass { Definition* make_native_function(Signature sig, Native_Function f, Context& ctx) { - Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]", 0); + Parser sig_parser = Parser::from_c_str(sig, ctx, "[built-in function]"); sig_parser.lex(); string name(sig_parser.lexed); Parameters* params = sig_parser.parse_parameters(); return new (ctx.mem) Definition("[built-in function]", - 0, + Position(), sig, name, params, @@ -38,12 +38,12 @@ namespace Sass { Definition* make_c_function(Signature sig, Sass_C_Function f, Context& ctx) { - Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]", 0); + Parser sig_parser = Parser::from_c_str(sig, ctx, "[c function]"); sig_parser.lex(); string name(sig_parser.lexed); Parameters* params = sig_parser.parse_parameters(); return new (ctx.mem) Definition("[c function]", - 0, + Position(), sig, name, params, @@ -54,7 +54,7 @@ namespace Sass { namespace Functions { template - T* get_arg(const string& argname, Env& env, Signature sig, const string& path, size_t line, Backtrace* backtrace) + T* get_arg(const string& argname, Env& env, Signature sig, const string& path, Position position, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly! T* val = dynamic_cast(env[argname]); @@ -65,21 +65,21 @@ namespace Sass { msg += sig; msg += "` must be a "; msg += T::type_name(); - error(msg, path, line, backtrace); + error(msg, path, position, backtrace); } return val; } - Number* get_arg_r(const string& argname, Env& env, Signature sig, const string& path, size_t line, double lo, double hi, Backtrace* backtrace) + Number* get_arg_r(const string& argname, Env& env, Signature sig, const string& path, Position position, double lo, double hi, Backtrace* backtrace) { // Minimal error handling -- the expectation is that built-ins will be written correctly! - Number* val = get_arg(argname, env, sig, path, line, backtrace); + Number* val = get_arg(argname, env, sig, path, position, backtrace); double v = val->value(); if (!(lo <= v && v <= hi)) { stringstream msg; msg << "argument `" << argname << "` of `" << sig << "` must be between "; msg << lo << " and " << hi; - error(msg.str(), path, line, backtrace); + error(msg.str(), path, position, backtrace); } return val; } @@ -92,7 +92,7 @@ namespace Sass { BUILT_IN(rgb) { return new (ctx.mem) Color(path, - line, + position, ARGR("$red", Number, 0, 255)->value(), ARGR("$green", Number, 0, 255)->value(), ARGR("$blue", Number, 0, 255)->value()); @@ -102,7 +102,7 @@ namespace Sass { BUILT_IN(rgba_4) { return new (ctx.mem) Color(path, - line, + position, ARGR("$red", Number, 0, 255)->value(), ARGR("$green", Number, 0, 255)->value(), ARGR("$blue", Number, 0, 255)->value(), @@ -121,15 +121,15 @@ namespace Sass { Signature red_sig = "red($color)"; BUILT_IN(red) - { return new (ctx.mem) Number(path, line, ARG("$color", Color)->r()); } + { return new (ctx.mem) Number(path, position, ARG("$color", Color)->r()); } Signature green_sig = "green($color)"; BUILT_IN(green) - { return new (ctx.mem) Number(path, line, ARG("$color", Color)->g()); } + { return new (ctx.mem) Number(path, position, ARG("$color", Color)->g()); } Signature blue_sig = "blue($color)"; BUILT_IN(blue) - { return new (ctx.mem) Number(path, line, ARG("$color", Color)->b()); } + { return new (ctx.mem) Number(path, position, ARG("$color", Color)->b()); } Signature mix_sig = "mix($color-1, $color-2, $weight: 50%)"; BUILT_IN(mix) @@ -146,7 +146,7 @@ namespace Sass { double w2 = 1 - w1; return new (ctx.mem) Color(path, - line, + position, std::floor(w1*color1->r() + w2*color2->r()), std::floor(w1*color1->g() + w2*color2->g()), std::floor(w1*color1->b() + w2*color2->b()), @@ -204,7 +204,7 @@ namespace Sass { return m1; } - Color* hsla_impl(double h, double s, double l, double a, Context& ctx, const string& path, size_t line) + Color* hsla_impl(double h, double s, double l, double a, Context& ctx, const string& path, Position position) { h = static_cast(((static_cast(h) % 360) + 360) % 360) / 360.0; s /= 100.0; @@ -219,7 +219,7 @@ namespace Sass { double g = (h_to_rgb(m1, m2, h) * 255.0); double b = (h_to_rgb(m1, m2, h-1.0/3.0) * 255.0); - return new (ctx.mem) Color(path, line, r, g, b, a); + return new (ctx.mem) Color(path, position, r, g, b, a); } Signature hsl_sig = "hsl($hue, $saturation, $lightness)"; @@ -231,7 +231,7 @@ namespace Sass { 1.0, ctx, path, - line); + position); } Signature hsla_sig = "hsla($hue, $saturation, $lightness, $alpha)"; @@ -243,7 +243,7 @@ namespace Sass { ARGR("$alpha", Number, 0, 1)->value(), ctx, path, - line); + position); } Signature hue_sig = "hue($color)"; @@ -253,7 +253,7 @@ namespace Sass { HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); - return new (ctx.mem) Number(path, line, hsl_color.h, "deg"); + return new (ctx.mem) Number(path, position, hsl_color.h, "deg"); } Signature saturation_sig = "saturation($color)"; @@ -263,7 +263,7 @@ namespace Sass { HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); - return new (ctx.mem) Number(path, line, hsl_color.s, "%"); + return new (ctx.mem) Number(path, position, hsl_color.s, "%"); } Signature lightness_sig = "lightness($color)"; @@ -273,7 +273,7 @@ namespace Sass { HSL hsl_color = rgb_to_hsl(rgb_color->r(), rgb_color->g(), rgb_color->b()); - return new (ctx.mem) Number(path, line, hsl_color.l, "%"); + return new (ctx.mem) Number(path, position, hsl_color.l, "%"); } Signature adjust_hue_sig = "adjust-hue($color, $degrees)"; @@ -290,7 +290,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature lighten_sig = "lighten($color, $amount)"; @@ -313,7 +313,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature darken_sig = "darken($color, $amount)"; @@ -337,7 +337,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature saturate_sig = "saturate($color, $amount)"; @@ -361,7 +361,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature desaturate_sig = "desaturate($color, $amount)"; @@ -384,7 +384,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature grayscale_sig = "grayscale($color)"; @@ -400,7 +400,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature complement_sig = "complement($color)"; @@ -416,7 +416,7 @@ namespace Sass { rgb_color->a(), ctx, path, - line); + position); } Signature invert_sig = "invert($color)"; @@ -424,7 +424,7 @@ namespace Sass { { Color* rgb_color = ARG("$color", Color); return new (ctx.mem) Color(path, - line, + position, 255 - rgb_color->r(), 255 - rgb_color->g(), 255 - rgb_color->b(), @@ -440,10 +440,10 @@ namespace Sass { { String_Constant* ie_kwd = dynamic_cast(env["$color"]); if (ie_kwd) { - return new (ctx.mem) String_Constant(path, line, "alpha(" + ie_kwd->value() + ")"); + return new (ctx.mem) String_Constant(path, position, "alpha(" + ie_kwd->value() + ")"); } else { - return new (ctx.mem) Number(path, line, ARG("$color", Color)->a()); + return new (ctx.mem) Number(path, position, ARG("$color", Color)->a()); } } @@ -454,7 +454,7 @@ namespace Sass { Color* color = ARG("$color", Color); double alpha = color->a() + ARGR("$amount", Number, 0, 1)->value(); return new (ctx.mem) Color(path, - line, + position, color->r(), color->g(), color->b(), @@ -468,7 +468,7 @@ namespace Sass { Color* color = ARG("$color", Color); double alpha = color->a() - ARGR("$amount", Number, 0, 1)->value(); return new (ctx.mem) Color(path, - line, + position, color->r(), color->g(), color->b(), @@ -495,11 +495,11 @@ namespace Sass { bool hsl = h || s || l; if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `adjust-color`", path, line); + error("cannot specify both RGB and HSL values for `adjust-color`", path, position); } if (rgb) { return new (ctx.mem) Color(path, - line, + position, color->r() + (r ? r->value() : 0), color->g() + (g ? g->value() : 0), color->b() + (b ? b->value() : 0), @@ -513,17 +513,17 @@ namespace Sass { color->a() + (a ? a->value() : 0), ctx, path, - line); + position); } if (a) { return new (ctx.mem) Color(path, - line, + position, color->r(), color->g(), color->b(), color->a() + (a ? a->value() : 0)); } - error("not enough arguments for `adjust-color`", path, line); + error("not enough arguments for `adjust-color`", path, position); // unreachable return color; } @@ -544,7 +544,7 @@ namespace Sass { bool hsl = h || s || l; if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `scale-color`", path, line); + error("cannot specify both RGB and HSL values for `scale-color`", path, position); } if (rgb) { double rscale = (r ? ARGR("$red", Number, -100.0, 100.0)->value() : 0.0) / 100.0; @@ -552,7 +552,7 @@ namespace Sass { double bscale = (b ? ARGR("$blue", Number, -100.0, 100.0)->value() : 0.0) / 100.0; double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; return new (ctx.mem) Color(path, - line, + position, color->r() + rscale * (rscale > 0.0 ? 255 - color->r() : color->r()), color->g() + gscale * (gscale > 0.0 ? 255 - color->g() : color->g()), color->b() + bscale * (bscale > 0.0 ? 255 - color->b() : color->b()), @@ -568,18 +568,18 @@ namespace Sass { hsl_struct.s += sscale * (sscale > 0.0 ? 100.0 - hsl_struct.s : hsl_struct.s); hsl_struct.l += lscale * (lscale > 0.0 ? 100.0 - hsl_struct.l : hsl_struct.l); double alpha = color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->r()); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, line); + return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position); } if (a) { double ascale = (a ? ARGR("$alpha", Number, -100.0, 100.0)->value() : 0.0) / 100.0; return new (ctx.mem) Color(path, - line, + position, color->r(), color->g(), color->b(), color->a() + ascale * (ascale > 0.0 ? 1.0 - color->a() : color->a())); } - error("not enough arguments for `scale-color`", path, line); + error("not enough arguments for `scale-color`", path, position); // unreachable return color; } @@ -600,11 +600,11 @@ namespace Sass { bool hsl = h || s || l; if (rgb && hsl) { - error("cannot specify both RGB and HSL values for `change-color`", path, line); + error("cannot specify both RGB and HSL values for `change-color`", path, position); } if (rgb) { return new (ctx.mem) Color(path, - line, + position, r ? ARGR("$red", Number, 0, 255)->value() : color->r(), g ? ARGR("$green", Number, 0, 255)->value() : color->g(), b ? ARGR("$blue", Number, 0, 255)->value() : color->b(), @@ -616,18 +616,18 @@ namespace Sass { if (s) hsl_struct.s = ARGR("$saturation", Number, 0, 100)->value(); if (l) hsl_struct.l = ARGR("$lightness", Number, 0, 100)->value(); double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); - return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, line); + return hsla_impl(hsl_struct.h, hsl_struct.s, hsl_struct.l, alpha, ctx, path, position); } if (a) { double alpha = a ? ARGR("$alpha", Number, 0, 1.0)->value() : color->a(); return new (ctx.mem) Color(path, - line, + position, color->r(), color->g(), color->b(), alpha); } - error("not enough arguments for `change-color`", path, line); + error("not enough arguments for `change-color`", path, position); // unreachable return color; } @@ -659,7 +659,7 @@ namespace Sass { for (size_t i = 0, L = result.length(); i < L; ++i) { result[i] = std::toupper(result[i]); } - return new (ctx.mem) String_Constant(path, line, result); + return new (ctx.mem) String_Constant(path, position, result); } /////////////////// @@ -672,7 +672,7 @@ namespace Sass { To_String to_string; AST_Node* arg = env["$string"]; string str(unquote(arg->perform(&to_string))); - String_Constant* result = new (ctx.mem) String_Constant(path, line, str); + String_Constant* result = new (ctx.mem) String_Constant(path, position, str); result->is_delayed(true); return result; } @@ -683,7 +683,7 @@ namespace Sass { To_String to_string; AST_Node* arg = env["$string"]; string str(quote(arg->perform(&to_string), '"')); - String_Constant* result = new (ctx.mem) String_Constant(path, line, str); + String_Constant* result = new (ctx.mem) String_Constant(path, position, str); result->is_delayed(true); return result; } @@ -696,8 +696,8 @@ namespace Sass { BUILT_IN(percentage) { Number* n = ARG("$value", Number); - if (!n->is_unitless()) error("argument $value of `" + string(sig) + "` must be unitless", path, line); - return new (ctx.mem) Number(path, line, n->value() * 100, "%"); + if (!n->is_unitless()) error("argument $value of `" + string(sig) + "` must be unitless", path, position); + return new (ctx.mem) Number(path, position, n->value() * 100, "%"); } Signature round_sig = "round($value)"; @@ -706,7 +706,7 @@ namespace Sass { Number* n = ARG("$value", Number); Number* r = new (ctx.mem) Number(*n); r->path(path); - r->line(line); + r->position(position); r->value(std::floor(r->value() + 0.5)); return r; } @@ -717,7 +717,7 @@ namespace Sass { Number* n = ARG("$value", Number); Number* r = new (ctx.mem) Number(*n); r->path(path); - r->line(line); + r->position(position); r->value(std::ceil(r->value())); return r; } @@ -728,7 +728,7 @@ namespace Sass { Number* n = ARG("$value", Number); Number* r = new (ctx.mem) Number(*n); r->path(path); - r->line(line); + r->position(position); r->value(std::floor(r->value())); return r; } @@ -739,7 +739,7 @@ namespace Sass { Number* n = ARG("$value", Number); Number* r = new (ctx.mem) Number(*n); r->path(path); - r->line(line); + r->position(position); r->value(std::abs(r->value())); return r; } @@ -752,7 +752,7 @@ namespace Sass { Number* least = x1; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Number* xi = dynamic_cast((*arglist)[i]); - if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, line); + if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position); if (lt(xi, least, ctx)) least = xi; } return least; @@ -766,7 +766,7 @@ namespace Sass { Number* greatest = x1; for (size_t i = 0, L = arglist->length(); i < L; ++i) { Number* xi = dynamic_cast((*arglist)[i]); - if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, line); + if (!xi) error("`" + string(sig) + "` only takes numeric arguments", path, position); if (lt(greatest, xi, ctx)) greatest = xi; } return greatest; @@ -781,7 +781,7 @@ namespace Sass { { List* list = dynamic_cast(env["$list"]); return new (ctx.mem) Number(path, - line, + position, list ? list->length() : 1); } @@ -791,11 +791,11 @@ namespace Sass { List* l = dynamic_cast(env["$list"]); Number* n = ARG("$n", Number); if (!l) { - l = new (ctx.mem) List(path, line, 1); + l = new (ctx.mem) List(path, position, 1); *l << ARG("$list", Expression); } - if (l->empty()) error("argument `$list` of `" + string(sig) + "` must not be empty", path, line); - if (n->value() < 1) error("argument `$n` of `" + string(sig) + "` must be greater than or equal to 1", path, line); + if (l->empty()) error("argument `$list` of `" + string(sig) + "` must not be empty", path, position); + if (n->value() < 1) error("argument `$n` of `" + string(sig) + "` must be greater than or equal to 1", path, position); return (*l)[std::floor(n->value() - 1)]; } @@ -805,13 +805,13 @@ namespace Sass { List* l = dynamic_cast(env["$list"]); Expression* v = ARG("$value", Expression); if (!l) { - l = new (ctx.mem) List(path, line, 1); + l = new (ctx.mem) List(path, position, 1); *l << ARG("$list", Expression); } for (size_t i = 0, L = l->length(); i < L; ++i) { - if (eq((*l)[i], v, ctx)) return new (ctx.mem) Number(path, line, i+1); + if (eq((*l)[i], v, ctx)) return new (ctx.mem) Number(path, position, i+1); } - return new (ctx.mem) Boolean(path, line, false); + return new (ctx.mem) Boolean(path, position, false); } Signature join_sig = "join($list1, $list2, $separator: auto)"; @@ -822,20 +822,20 @@ namespace Sass { String_Constant* sep = ARG("$separator", String_Constant); List::Separator sep_val = (l1 ? l1->separator() : List::SPACE); if (!l1) { - l1 = new (ctx.mem) List(path, line, 1); + l1 = new (ctx.mem) List(path, position, 1); *l1 << ARG("$list1", Expression); sep_val = (l2 ? l2->separator() : List::SPACE); } if (!l2) { - l2 = new (ctx.mem) List(path, line, 1); + l2 = new (ctx.mem) List(path, position, 1); *l2 << ARG("$list2", Expression); } size_t len = l1->length() + l2->length(); string sep_str = unquote(sep->value()); if (sep_str == "space") sep_val = List::SPACE; else if (sep_str == "comma") sep_val = List::COMMA; - else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, line); - List* result = new (ctx.mem) List(path, line, len, sep_val); + else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position); + List* result = new (ctx.mem) List(path, position, len, sep_val); *result += l1; *result += l2; return result; @@ -848,14 +848,14 @@ namespace Sass { Expression* v = ARG("$val", Expression); String_Constant* sep = ARG("$separator", String_Constant); if (!l) { - l = new (ctx.mem) List(path, line, 1); + l = new (ctx.mem) List(path, position, 1); *l << ARG("$list", Expression); } - List* result = new (ctx.mem) List(path, line, l->length() + 1); + List* result = new (ctx.mem) List(path, position, l->length() + 1); string sep_str(unquote(sep->value())); if (sep_str == "space") result->separator(List::SPACE); else if (sep_str == "comma") result->separator(List::COMMA); - else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, line); + else if (sep_str != "auto") error("argument `$separator` of `" + string(sig) + "` must be `space`, `comma`, or `auto`", path, position); *result += l; *result << v; return result; @@ -869,16 +869,16 @@ namespace Sass { for (size_t i = 0, L = arglist->length(); i < L; ++i) { List* ith = dynamic_cast((*arglist)[i]); if (!ith) { - ith = new (ctx.mem) List(path, line, 1); + ith = new (ctx.mem) List(path, position, 1); *ith << (*arglist)[i]; (*arglist)[i] = ith; } shortest = (i ? std::min(shortest, ith->length()) : ith->length()); } - List* zippers = new (ctx.mem) List(path, line, shortest, List::COMMA); + List* zippers = new (ctx.mem) List(path, position, shortest, List::COMMA); size_t L = arglist->length(); for (size_t i = 0; i < shortest; ++i) { - List* zipper = new (ctx.mem) List(path, line, L); + List* zipper = new (ctx.mem) List(path, position, L); for (size_t j = 0; j < L; ++j) { *zipper << (*static_cast((*arglist)[j]))[i]; } @@ -895,12 +895,12 @@ namespace Sass { Expression* the_arg = (*arglist)[0]; arglist = dynamic_cast(the_arg); if (!arglist) { - List* result = new (ctx.mem) List(path, line, 1, List::COMMA); + List* result = new (ctx.mem) List(path, position, 1, List::COMMA); *result << the_arg; return result; } } - List* result = new (ctx.mem) List(path, line, 0, List::COMMA); + List* result = new (ctx.mem) List(path, position, 0, List::COMMA); for (size_t i = 0, L = arglist->length(); i < L; ++i) { Boolean* ith = dynamic_cast((*arglist)[i]); if (ith && ith->value() == false) continue; @@ -921,19 +921,19 @@ namespace Sass { To_String to_string; string str(v->perform(&to_string)); if (ctx.names_to_colors.count(str)) { - return new (ctx.mem) String_Constant(path, line, "color"); + return new (ctx.mem) String_Constant(path, position, "color"); } } - return new (ctx.mem) String_Constant(path, line, ARG("$value", Expression)->type()); + return new (ctx.mem) String_Constant(path, position, ARG("$value", Expression)->type()); } Signature unit_sig = "unit($number)"; BUILT_IN(unit) - { return new (ctx.mem) String_Constant(path, line, quote(ARG("$number", Number)->unit(), '"')); } + { return new (ctx.mem) String_Constant(path, position, quote(ARG("$number", Number)->unit(), '"')); } Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless) - { return new (ctx.mem) Boolean(path, line, ARG("$number", Number)->is_unitless()); } + { return new (ctx.mem) Boolean(path, position, ARG("$number", Number)->is_unitless()); } Signature comparable_sig = "comparable($number-1, $number-2)"; BUILT_IN(comparable) @@ -941,11 +941,11 @@ namespace Sass { Number* n1 = ARG("$number-1", Number); Number* n2 = ARG("$number-2", Number); if (n1->is_unitless() || n2->is_unitless()) { - return new (ctx.mem) Boolean(path, line, true); + return new (ctx.mem) Boolean(path, position, true); } Number tmp_n2(*n2); tmp_n2.normalize(n1->find_convertible_unit()); - return new (ctx.mem) Boolean(path, line, n1->unit() == tmp_n2.unit()); + return new (ctx.mem) Boolean(path, position, n1->unit() == tmp_n2.unit()); } //////////////////// @@ -954,7 +954,7 @@ namespace Sass { Signature not_sig = "not($value)"; BUILT_IN(sass_not) - { return new (ctx.mem) Boolean(path, line, ARG("$value", Expression)->is_false()); } + { return new (ctx.mem) Boolean(path, position, ARG("$value", Expression)->is_false()); } Signature if_sig = "if($condition, $if-true, $if-false)"; BUILT_IN(sass_if) @@ -971,7 +971,7 @@ namespace Sass { bool only_path = !ARG("$only-path", Expression)->is_false(); string full_path(quote(ctx.image_path + "/" + unquote(ipath->value()), '"')); if (!only_path) full_path = "url(" + full_path + ")"; - return new (ctx.mem) String_Constant(path, line, full_path); + return new (ctx.mem) String_Constant(path, position, full_path); } } diff --git a/functions.hpp b/functions.hpp index 6a1b9f6a10..ecda3d9873 100644 --- a/functions.hpp +++ b/functions.hpp @@ -10,8 +10,12 @@ #include +#ifndef SASS_POSITION +#include "position.hpp" +#endif + #define BUILT_IN(name) Expression*\ -name(Env& env, Context& ctx, Signature sig, const string& path, size_t line, Backtrace* backtrace) +name(Env& env, Context& ctx, Signature sig, const string& path, Position position, Backtrace* backtrace) namespace Sass { struct Context; @@ -21,7 +25,7 @@ namespace Sass { class Definition; typedef Environment Env; typedef const char* Signature; - typedef Expression* (*Native_Function)(Env&, Context&, Signature, const string&, size_t, Backtrace*); + typedef Expression* (*Native_Function)(Env&, Context&, Signature, const string&, Position, Backtrace*); Definition* make_native_function(Signature, Native_Function, Context&); Definition* make_c_function(Signature sig, Sass_C_Function f, Context& ctx); diff --git a/inspect.cpp b/inspect.cpp index 9c51cf9b27..1e3834e97f 100644 --- a/inspect.cpp +++ b/inspect.cpp @@ -15,26 +15,28 @@ namespace Sass { void Inspect::operator()(Block* block) { if (!block->is_root()) { - buffer += " {\n"; + append_multiline_part_to_buffer(" {\n"); ++indentation; } for (size_t i = 0, L = block->length(); i < L; ++i) { indent(); (*block)[i]->perform(this); // extra newline at the end of top-level statements - if (block->is_root()) buffer += '\n'; - buffer += '\n'; + if (block->is_root()) append_multiline_part_to_buffer("\n"); + append_multiline_part_to_buffer("\n"); } if (!block->is_root()) { --indentation; indent(); - buffer += "}"; + append_singleline_part_to_buffer("}"); } // remove extra newline that gets added after the last top-level block if (block->is_root()) { size_t l = buffer.length(); - if (l > 2 && buffer[l-1] == '\n' && buffer[l-2] == '\n') + if (l > 2 && buffer[l-1] == '\n' && buffer[l-2] == '\n') { buffer.erase(l-1); + if (ctx) ctx->source_map.remove_line(); + } } } @@ -47,76 +49,84 @@ namespace Sass { void Inspect::operator()(Propset* propset) { propset->property_fragment()->perform(this); - buffer += ": "; + append_singleline_part_to_buffer(": "); propset->block()->perform(this); } void Inspect::operator()(Media_Block* media_block) { - buffer += "@media "; + if (ctx) ctx->source_map.add_mapping(media_block); + append_singleline_part_to_buffer("@media "); media_block->media_queries()->perform(this); media_block->block()->perform(this); } void Inspect::operator()(At_Rule* at_rule) { - buffer += at_rule->keyword(); + append_singleline_part_to_buffer(at_rule->keyword()); if (at_rule->selector()) { - buffer += ' '; + append_singleline_part_to_buffer(" "); at_rule->selector()->perform(this); } if (at_rule->block()) { at_rule->block()->perform(this); } else { - buffer += ';'; + append_singleline_part_to_buffer(";"); } } void Inspect::operator()(Declaration* dec) { + if (ctx) ctx->source_map.add_mapping(dec->property()); dec->property()->perform(this); - buffer += ": "; + append_singleline_part_to_buffer(": "); + if (ctx) ctx->source_map.add_mapping(dec->value()); dec->value()->perform(this); - if (dec->is_important()) buffer += " !important"; - buffer += ';'; + if (dec->is_important()) append_singleline_part_to_buffer(" !important"); + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Assignment* assn) { - buffer += assn->variable(); - buffer += ": "; + append_singleline_part_to_buffer(assn->variable()); + append_singleline_part_to_buffer(": "); assn->value()->perform(this); - if (assn->is_guarded()) buffer += " !default"; - buffer += ';'; + if (assn->is_guarded()) append_singleline_part_to_buffer(" !default"); + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Import* import) { if (!import->urls().empty()) { - buffer += "@import "; + if (ctx) ctx->source_map.add_mapping(import); + append_singleline_part_to_buffer("@import "); import->urls().front()->perform(this); - buffer += ';'; + append_singleline_part_to_buffer(";"); for (size_t i = 1, S = import->urls().size(); i < S; ++i) { - buffer += "\n@import "; + append_multiline_part_to_buffer("\n"); + if (ctx) ctx->source_map.add_mapping(import); + append_singleline_part_to_buffer("@import "); import->urls()[i]->perform(this); - buffer += ';'; + append_singleline_part_to_buffer(";"); } } } void Inspect::operator()(Import_Stub* import) { - buffer += "@import "; - buffer += import->file_name(); - buffer += ';'; + if (ctx) ctx->source_map.add_mapping(import); + append_singleline_part_to_buffer("@import "); + append_singleline_part_to_buffer(import->file_name()); + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Warning* warning) { - buffer += "@warn "; + if (ctx) ctx->source_map.add_mapping(warning); + append_singleline_part_to_buffer("@warn "); warning->message()->perform(this); - buffer += ';'; + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Comment* comment) @@ -126,83 +136,87 @@ namespace Sass { void Inspect::operator()(If* cond) { - buffer += "@if "; + append_singleline_part_to_buffer("@if "); cond->predicate()->perform(this); cond->consequent()->perform(this); if (cond->alternative()) { - buffer += '\n'; + append_multiline_part_to_buffer("\n"); indent(); - buffer += "else"; + append_singleline_part_to_buffer("else"); cond->alternative()->perform(this); } } void Inspect::operator()(For* loop) { - buffer += string("@for "); - buffer += loop->variable(); - buffer += " from "; + append_singleline_part_to_buffer("@for "); + append_singleline_part_to_buffer(loop->variable()); + append_singleline_part_to_buffer(" from "); loop->lower_bound()->perform(this); - buffer += (loop->is_inclusive() ? " through " : " to "); + append_singleline_part_to_buffer((loop->is_inclusive() ? " through " : " to ")); loop->upper_bound()->perform(this); loop->block()->perform(this); } void Inspect::operator()(Each* loop) { - buffer += string("@each "); - buffer += loop->variable(); - buffer += " in "; + append_singleline_part_to_buffer("@each "); + append_singleline_part_to_buffer(loop->variable()); + append_singleline_part_to_buffer(" in "); loop->list()->perform(this); loop->block()->perform(this); } void Inspect::operator()(While* loop) { - buffer += "@while "; + append_singleline_part_to_buffer("@while "); loop->predicate()->perform(this); loop->block()->perform(this); } void Inspect::operator()(Return* ret) { - buffer += "@return "; + append_singleline_part_to_buffer("@return "); ret->value()->perform(this); - buffer += ';'; + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Extension* extend) { - buffer += "@extend "; + append_singleline_part_to_buffer("@extend "); extend->selector()->perform(this); - buffer += ';'; + append_singleline_part_to_buffer(";"); } void Inspect::operator()(Definition* def) { - if (def->type() == Definition::MIXIN) buffer += "@mixin "; - else buffer += "@function "; - buffer += def->name(); + if (def->type() == Definition::MIXIN) { + append_singleline_part_to_buffer("@mixin "); + } else { + append_singleline_part_to_buffer("@function "); + } + append_singleline_part_to_buffer(def->name()); def->parameters()->perform(this); def->block()->perform(this); } void Inspect::operator()(Mixin_Call* call) { - buffer += string("@include ") += call->name(); + append_singleline_part_to_buffer(string("@include ") += call->name()); if (call->arguments()) { call->arguments()->perform(this); } if (call->block()) { - buffer += ' '; + append_singleline_part_to_buffer(" "); call->block()->perform(this); } - if (!call->block()) buffer += ';'; + if (!call->block()) append_singleline_part_to_buffer(";"); } void Inspect::operator()(Content* content) { - buffer += "@content;"; + if (ctx) ctx->source_map.add_mapping(content); + append_singleline_part_to_buffer("@content;"); } void Inspect::operator()(List* list) @@ -215,7 +229,7 @@ namespace Sass { if (list_item->is_invisible()) { continue; } - if (items_output) buffer += sep; + if (items_output) append_singleline_part_to_buffer(sep); list_item->perform(this); items_output = true; } @@ -225,19 +239,19 @@ namespace Sass { { expr->left()->perform(this); switch (expr->type()) { - case Binary_Expression::AND: buffer += " and "; break; - case Binary_Expression::OR: buffer += " or "; break; - case Binary_Expression::EQ: buffer += " == "; break; - case Binary_Expression::NEQ: buffer += " != "; break; - case Binary_Expression::GT: buffer += " > "; break; - case Binary_Expression::GTE: buffer += " >= "; break; - case Binary_Expression::LT: buffer += " < "; break; - case Binary_Expression::LTE: buffer += " <= "; break; - case Binary_Expression::ADD: buffer += " + "; break; - case Binary_Expression::SUB: buffer += " - "; break; - case Binary_Expression::MUL: buffer += " * "; break; - case Binary_Expression::DIV: buffer += "/"; break; - case Binary_Expression::MOD: buffer += " % "; break; + case Binary_Expression::AND: append_singleline_part_to_buffer(" and "); break; + case Binary_Expression::OR: append_singleline_part_to_buffer(" or "); break; + case Binary_Expression::EQ: append_singleline_part_to_buffer(" == "); break; + case Binary_Expression::NEQ: append_singleline_part_to_buffer(" != "); break; + case Binary_Expression::GT: append_singleline_part_to_buffer(" > "); break; + case Binary_Expression::GTE: append_singleline_part_to_buffer(" >= "); break; + case Binary_Expression::LT: append_singleline_part_to_buffer(" < "); break; + case Binary_Expression::LTE: append_singleline_part_to_buffer(" <= "); break; + case Binary_Expression::ADD: append_singleline_part_to_buffer(" + "); break; + case Binary_Expression::SUB: append_singleline_part_to_buffer(" - "); break; + case Binary_Expression::MUL: append_singleline_part_to_buffer(" * "); break; + case Binary_Expression::DIV: append_singleline_part_to_buffer("/"); break; + case Binary_Expression::MOD: append_singleline_part_to_buffer(" % "); break; default: break; // shouldn't get here } expr->right()->perform(this); @@ -245,14 +259,14 @@ namespace Sass { void Inspect::operator()(Unary_Expression* expr) { - if (expr->type() == Unary_Expression::PLUS) buffer += '+'; - else buffer += '-'; + if (expr->type() == Unary_Expression::PLUS) append_singleline_part_to_buffer("+"); + else append_singleline_part_to_buffer("-"); expr->operand()->perform(this); } void Inspect::operator()(Function_Call* call) { - buffer += call->name(); + append_singleline_part_to_buffer(call->name()); call->arguments()->perform(this); } @@ -264,12 +278,12 @@ namespace Sass { void Inspect::operator()(Variable* var) { - buffer += var->name(); + append_singleline_part_to_buffer(var->name()); } void Inspect::operator()(Textual* txt) { - buffer += txt->value(); + append_singleline_part_to_buffer(txt->value()); } // helper functions for serializing numbers @@ -295,8 +309,6 @@ namespace Sass { void Inspect::operator()(Number* n) { - // buffer += double_to_string(n->value(), 5); - // buffer += n->unit(); stringstream ss; ss.precision(5); ss << fixed << n->value(); @@ -306,10 +318,10 @@ namespace Sass { } if (d[d.length()-1] == '.') d.resize(d.length()-1); if (n->numerator_units().size() > 1 || n->denominator_units().size() > 0) { - error(d + n->unit() + " is not a valid CSS value", n->path(), n->line()); + error(d + n->unit() + " is not a valid CSS value", n->path(), n->position()); } - buffer += d; - buffer += n->unit(); + append_singleline_part_to_buffer(d); + append_singleline_part_to_buffer(n->unit()); } // helper function for serializing colors @@ -360,12 +372,12 @@ namespace Sass { ss << static_cast(b) << ", "; ss << a << ')'; } - buffer += ss.str(); + append_singleline_part_to_buffer(ss.str()); } void Inspect::operator()(Boolean* b) { - buffer += (b->value() ? "true" : "false"); + append_singleline_part_to_buffer(b->value() ? "true" : "false"); } void Inspect::operator()(String_Schema* ss) @@ -373,30 +385,30 @@ namespace Sass { // Evaluation should turn these into String_Constants, so this method is // only for inspection purposes. for (size_t i = 0, L = ss->length(); i < L; ++i) { - if ((*ss)[i]->is_interpolant()) buffer += "#{"; + if ((*ss)[i]->is_interpolant()) append_singleline_part_to_buffer("#{"); (*ss)[i]->perform(this); - if ((*ss)[i]->is_interpolant()) buffer += '}'; + if ((*ss)[i]->is_interpolant()) append_singleline_part_to_buffer("}"); } } void Inspect::operator()(String_Constant* s) { - buffer += (s->needs_unquoting() ? unquote(s->value()) : s->value()); + append_singleline_part_to_buffer(s->needs_unquoting() ? unquote(s->value()) : s->value()); } void Inspect::operator()(Media_Query* mq) { size_t i = 0; if (mq->media_type()) { - if (mq->is_negated()) buffer += "not "; - else if (mq->is_restricted()) buffer += "only "; + if (mq->is_negated()) append_singleline_part_to_buffer("not "); + else if (mq->is_restricted()) append_singleline_part_to_buffer("only "); mq->media_type()->perform(this); } else { (*mq)[i++]->perform(this); } for (size_t L = mq->length(); i < L; ++i) { - buffer += " and "; + append_singleline_part_to_buffer(" and "); (*mq)[i]->perform(this); } } @@ -407,52 +419,52 @@ namespace Sass { mqe->feature()->perform(this); } else { - buffer += "("; + append_singleline_part_to_buffer("("); mqe->feature()->perform(this); if (mqe->value()) { - buffer += ": "; + append_singleline_part_to_buffer(": "); mqe->value()->perform(this); } - buffer += ')'; + append_singleline_part_to_buffer(")"); } } void Inspect::operator()(Null* n) { - buffer += "null"; + append_singleline_part_to_buffer("null"); } // parameters and arguments void Inspect::operator()(Parameter* p) { - buffer += p->name(); + append_singleline_part_to_buffer(p->name()); if (p->default_value()) { - buffer += ": "; + append_singleline_part_to_buffer(": "); p->default_value()->perform(this); } else if (p->is_rest_parameter()) { - buffer += "..."; + append_singleline_part_to_buffer("..."); } } void Inspect::operator()(Parameters* p) { - buffer += '('; + append_singleline_part_to_buffer("("); if (!p->empty()) { (*p)[0]->perform(this); for (size_t i = 1, L = p->length(); i < L; ++i) { - buffer += ", "; + append_singleline_part_to_buffer(", "); (*p)[i]->perform(this); } } - buffer += ')'; + append_singleline_part_to_buffer(")"); } void Inspect::operator()(Argument* a) { if (!a->name().empty()) { - buffer += a->name(); - buffer += ": "; + append_singleline_part_to_buffer(a->name()); + append_singleline_part_to_buffer(": "); } // Special case: argument nulls can be ignored if (a->value()->concrete_type() == Expression::NULL_VAL) { @@ -460,21 +472,21 @@ namespace Sass { } a->value()->perform(this); if (a->is_rest_argument()) { - buffer += "..."; + append_singleline_part_to_buffer("..."); } } void Inspect::operator()(Arguments* a) { - buffer += '('; + append_singleline_part_to_buffer("("); if (!a->empty()) { (*a)[0]->perform(this); for (size_t i = 1, L = a->length(); i < L; ++i) { - buffer += ", "; + append_singleline_part_to_buffer(", "); (*a)[i]->perform(this); } } - buffer += ')'; + append_singleline_part_to_buffer(")"); } // selectors @@ -486,49 +498,54 @@ namespace Sass { void Inspect::operator()(Selector_Reference* ref) { if (ref->selector()) ref->selector()->perform(this); - else buffer += '&'; + else append_singleline_part_to_buffer("&"); } void Inspect::operator()(Selector_Placeholder* s) { - buffer += s->name(); + append_singleline_part_to_buffer(s->name()); } void Inspect::operator()(Type_Selector* s) { - buffer += s->name(); + if (ctx) ctx->source_map.add_mapping(s); + append_singleline_part_to_buffer(s->name()); } void Inspect::operator()(Selector_Qualifier* s) { - buffer += s->name(); + if (ctx) ctx->source_map.add_mapping(s); + append_singleline_part_to_buffer(s->name()); } void Inspect::operator()(Attribute_Selector* s) { - buffer += '['; - buffer += s->name(); + if (ctx) ctx->source_map.add_mapping(s); + append_singleline_part_to_buffer("["); + append_singleline_part_to_buffer(s->name()); if (!s->matcher().empty()) { - buffer += s->matcher(); - buffer += s->value(); + append_singleline_part_to_buffer(s->matcher()); + append_singleline_part_to_buffer(s->value()); } - buffer += ']'; + append_singleline_part_to_buffer("]"); } void Inspect::operator()(Pseudo_Selector* s) { - buffer += s->name(); + if (ctx) ctx->source_map.add_mapping(s); + append_singleline_part_to_buffer(s->name()); if (s->expression()) { s->expression()->perform(this); - buffer += ')'; + append_singleline_part_to_buffer(")"); } } void Inspect::operator()(Negated_Selector* s) { - buffer += ":not("; + if (ctx) ctx->source_map.add_mapping(s); + append_singleline_part_to_buffer(":not("); s->selector()->perform(this); - buffer += ')'; + append_singleline_part_to_buffer(")"); } void Inspect::operator()(Compound_Selector* s) @@ -544,15 +561,15 @@ namespace Sass { Complex_Selector* tail = c->tail(); Complex_Selector::Combinator comb = c->combinator(); if (head) head->perform(this); - if (head && tail) buffer += ' '; + if (head && tail) append_singleline_part_to_buffer(" "); switch (comb) { - case Complex_Selector::ANCESTOR_OF: break; - case Complex_Selector::PARENT_OF: buffer += '>'; break; - case Complex_Selector::PRECEDES: buffer += '~'; break; - case Complex_Selector::ADJACENT_TO: buffer += '+'; break; + case Complex_Selector::ANCESTOR_OF: break; + case Complex_Selector::PARENT_OF: append_singleline_part_to_buffer(">"); break; + case Complex_Selector::PRECEDES: append_singleline_part_to_buffer("~"); break; + case Complex_Selector::ADJACENT_TO: append_singleline_part_to_buffer("+"); break; } if (tail && comb != Complex_Selector::ANCESTOR_OF) { - buffer += ' '; + append_singleline_part_to_buffer(" "); } if (tail) tail->perform(this); } @@ -562,7 +579,7 @@ namespace Sass { if (g->empty()) return; (*g)[0]->perform(this); for (size_t i = 1, L = g->length(); i < L; ++i) { - buffer += ", "; + append_singleline_part_to_buffer(", "); (*g)[i]->perform(this); } } @@ -571,7 +588,7 @@ namespace Sass { { } void Inspect::indent() - { buffer += string(2*indentation, ' '); } + { append_singleline_part_to_buffer(string(2*indentation, ' ')); } string unquote(const string& s) { @@ -607,5 +624,17 @@ namespace Sass { t.push_back(q); return t; } + + void Inspect::append_singleline_part_to_buffer(const string& text) + { + buffer += text; + if (ctx) ctx->source_map.update_column(text); + } + + void Inspect::append_multiline_part_to_buffer(const string& text) + { + buffer += text; + if (ctx) ctx->source_map.new_line(); + } } diff --git a/inspect.hpp b/inspect.hpp index 7b44cd11cb..e225078881 100644 --- a/inspect.hpp +++ b/inspect.hpp @@ -23,6 +23,9 @@ namespace Sass { void indent(); void fallback_impl(AST_Node* n); + + void append_singleline_part_to_buffer(const string& text); + void append_multiline_part_to_buffer(const string& text); public: diff --git a/mapping.hpp b/mapping.hpp new file mode 100644 index 0000000000..fc20436f0c --- /dev/null +++ b/mapping.hpp @@ -0,0 +1,17 @@ +#define SASS_MAPPING + +#ifndef SASS_POSITION +#include "position.hpp" +#endif + +namespace Sass { + + struct Mapping { + Position original_position; + Position generated_position; + + Mapping(const Position& original_position, const Position& generated_position) + : original_position(original_position), generated_position(generated_position) { } + }; + +} diff --git a/output_compressed.cpp b/output_compressed.cpp index b54b2c061b..e8042cb0a1 100644 --- a/output_compressed.cpp +++ b/output_compressed.cpp @@ -6,12 +6,12 @@ namespace Sass { using namespace std; - Output_Compressed::Output_Compressed(Context* ctx) : buffer("") { } + Output_Compressed::Output_Compressed(Context* ctx) : buffer(""), ctx(ctx) { } Output_Compressed::~Output_Compressed() { } inline void Output_Compressed::fallback_impl(AST_Node* n) { - Inspect i; + Inspect i(ctx); n->perform(&i); buffer += i.get_buffer(); } @@ -33,14 +33,14 @@ namespace Sass { if (b->has_non_hoistable()) { s->perform(this); - buffer += "{"; + append_singleline_part_to_buffer("{"); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { stm->perform(this); } } - buffer += "}"; + append_singleline_part_to_buffer("}"); } if (b->has_hoistable()) { @@ -58,16 +58,17 @@ namespace Sass { List* q = m->media_queries(); Block* b = m->block(); - buffer += "@media "; + ctx->source_map.add_mapping(m); + append_singleline_part_to_buffer("@media "); q->perform(this); - buffer += "{"; + append_singleline_part_to_buffer("{"); Selector* e = m->enclosing_selector(); bool hoisted = false; if (e && b->has_non_hoistable()) { hoisted = true; e->perform(this); - buffer += "{"; + append_singleline_part_to_buffer("{"); } for (size_t i = 0, L = b->length(); i < L; ++i) { @@ -78,7 +79,7 @@ namespace Sass { } if (hoisted) { - buffer += "}"; + append_singleline_part_to_buffer("}"); } for (size_t i = 0, L = b->length(); i < L; ++i) { @@ -88,7 +89,7 @@ namespace Sass { } } - buffer += "}"; + append_singleline_part_to_buffer("}"); } void Output_Compressed::operator()(At_Rule* a) @@ -97,18 +98,18 @@ namespace Sass { Selector* s = a->selector(); Block* b = a->block(); - buffer += kwd; + append_singleline_part_to_buffer(kwd); if (s) { - buffer += ' '; + append_singleline_part_to_buffer(" "); s->perform(this); } if (!b) { - buffer += ';'; + append_singleline_part_to_buffer(";"); return; } - buffer += "{"; + append_singleline_part_to_buffer("{"); for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; if (!stm->is_hoistable()) { @@ -123,7 +124,7 @@ namespace Sass { } } - buffer += "}"; + append_singleline_part_to_buffer("}"); } void Output_Compressed::operator()(Declaration* d) @@ -142,11 +143,13 @@ namespace Sass { } // Print if OK if(bPrintExpression) { + if (ctx) ctx->source_map.add_mapping(d->property()); d->property()->perform(this); - buffer += ":"; + append_singleline_part_to_buffer(":"); + if (ctx) ctx->source_map.add_mapping(d->value()); d->value()->perform(this); - if (d->is_important()) buffer += "!important"; - buffer += ';'; + if (d->is_important()) append_singleline_part_to_buffer("!important"); + append_singleline_part_to_buffer(";"); } } @@ -165,8 +168,8 @@ namespace Sass { for (size_t i = 1, L = list->length(); i < L; ++i) { Expression* next = (*list)[i]; bool next_invisible = next->is_invisible(); - if (i == 1 && !first_invisible && !next_invisible) buffer += sep; - else if (!next_invisible) buffer += sep; + if (i == 1 && !first_invisible && !next_invisible) append_singleline_part_to_buffer(sep); + else if (!next_invisible) append_singleline_part_to_buffer(sep); next->perform(this); } } @@ -177,39 +180,39 @@ namespace Sass { mqe->feature()->perform(this); } else { - buffer += "("; + append_singleline_part_to_buffer("("); mqe->feature()->perform(this); if (mqe->value()) { - buffer += ":"; + append_singleline_part_to_buffer(":"); mqe->value()->perform(this); } - buffer += ')'; + append_singleline_part_to_buffer(")"); } } void Output_Compressed::operator()(Argument* a) { if (!a->name().empty()) { - buffer += a->name(); - buffer += ":"; + append_singleline_part_to_buffer(a->name()); + append_singleline_part_to_buffer(":"); } a->value()->perform(this); if (a->is_rest_argument()) { - buffer += "..."; + append_singleline_part_to_buffer("..."); } } void Output_Compressed::operator()(Arguments* a) { - buffer += '('; + append_singleline_part_to_buffer("("); if (!a->empty()) { (*a)[0]->perform(this); for (size_t i = 1, L = a->length(); i < L; ++i) { - buffer += ","; + append_singleline_part_to_buffer(","); (*a)[i]->perform(this); } } - buffer += ')'; + append_singleline_part_to_buffer(")"); } void Output_Compressed::operator()(Complex_Selector* c) @@ -219,10 +222,10 @@ namespace Sass { Complex_Selector::Combinator comb = c->combinator(); if (head) head->perform(this); switch (comb) { - case Complex_Selector::ANCESTOR_OF: buffer += ' '; break; - case Complex_Selector::PARENT_OF: buffer += '>'; break; - case Complex_Selector::PRECEDES: buffer += '~'; break; - case Complex_Selector::ADJACENT_TO: buffer += '+'; break; + case Complex_Selector::ANCESTOR_OF: append_singleline_part_to_buffer(" "); break; + case Complex_Selector::PARENT_OF: append_singleline_part_to_buffer(">"); break; + case Complex_Selector::PRECEDES: append_singleline_part_to_buffer("~"); break; + case Complex_Selector::ADJACENT_TO: append_singleline_part_to_buffer("+"); break; } if (tail) tail->perform(this); } @@ -232,9 +235,15 @@ namespace Sass { if (g->empty()) return; (*g)[0]->perform(this); for (size_t i = 1, L = g->length(); i < L; ++i) { - buffer += ","; + append_singleline_part_to_buffer(","); (*g)[i]->perform(this); } } + + void Output_Compressed::append_singleline_part_to_buffer(const string& text) + { + buffer += text; + if (ctx) ctx->source_map.update_column(text); + } } diff --git a/output_compressed.hpp b/output_compressed.hpp index a573594c29..e7d7d31cce 100644 --- a/output_compressed.hpp +++ b/output_compressed.hpp @@ -14,8 +14,11 @@ namespace Sass { using Operation_CRTP::operator(); string buffer; + Context* ctx; void fallback_impl(AST_Node* n); + + void append_singleline_part_to_buffer(const string& text); public: Output_Compressed(Context* ctx = 0); diff --git a/output_nested.cpp b/output_nested.cpp index 05e404ec8a..9f0eb5b94a 100644 --- a/output_nested.cpp +++ b/output_nested.cpp @@ -26,7 +26,7 @@ namespace Sass { if (!b->is_root()) return; for (size_t i = 0, L = b->length(); i < L; ++i) { (*b)[i]->perform(this); - if (i < L-1) buffer += '\n'; + if (i < L-1) append_multiline_part_to_buffer("\n"); } } @@ -43,12 +43,12 @@ namespace Sass { indent(); if (source_comments) { stringstream ss; - ss << "/* line " << r->line() << ", " << r->path() << " */" << endl; - buffer += ss.str(); + ss << "/* line " << r->position().line << ", " << r->path() << " */" << endl; + append_singleline_part_to_buffer(ss.str()); indent(); } s->perform(this); - buffer += " {\n"; + append_multiline_part_to_buffer(" {\n"); ++indentation; for (size_t i = 0, L = b->length(); i < L; ++i) { Statement* stm = (*b)[i]; @@ -77,12 +77,13 @@ namespace Sass { if (!stm->is_hoistable() && bPrintExpression) { if (!stm->block()) indent(); stm->perform(this); - buffer += '\n'; + append_multiline_part_to_buffer("\n"); } } --indentation; buffer.erase(buffer.length()-1); - buffer += " }\n"; + if (ctx) ctx->source_map.remove_line(); + append_multiline_part_to_buffer(" }\n"); } if (b->has_hoistable()) { @@ -105,9 +106,10 @@ namespace Sass { bool decls = false; indent(); - buffer += "@media "; + ctx->source_map.add_mapping(m); + append_singleline_part_to_buffer("@media "); q->perform(this); - buffer += " {\n"; + append_multiline_part_to_buffer(" {\n"); Selector* e = m->enclosing_selector(); bool hoisted = false; @@ -116,7 +118,7 @@ namespace Sass { ++indentation; indent(); e->perform(this); - buffer += " {\n"; + append_multiline_part_to_buffer(" {\n"); } ++indentation; @@ -126,14 +128,15 @@ namespace Sass { if (!stm->is_hoistable()) { if (!stm->block()) indent(); stm->perform(this); - buffer += '\n'; + append_multiline_part_to_buffer("\n"); } } --indentation; if (hoisted) { buffer.erase(buffer.length()-1); - buffer += " }\n"; + if (ctx) ctx->source_map.remove_line(); + append_multiline_part_to_buffer(" }\n"); --indentation; } @@ -149,7 +152,8 @@ namespace Sass { if (decls) --indentation; buffer.erase(buffer.length()-1); - buffer += " }\n"; + if (ctx) ctx->source_map.remove_line(); + append_multiline_part_to_buffer(" }\n"); } void Output_Nested::operator()(At_Rule* a) @@ -160,18 +164,18 @@ namespace Sass { bool decls = false; // indent(); - buffer += kwd; + append_singleline_part_to_buffer(kwd); if (s) { - buffer += ' '; + append_singleline_part_to_buffer(" "); s->perform(this); } if (!b) { - buffer += ';'; + append_singleline_part_to_buffer(";"); return; } - buffer += " {\n"; + append_multiline_part_to_buffer(" {\n"); ++indentation; decls = true; for (size_t i = 0, L = b->length(); i < L; ++i) { @@ -179,7 +183,7 @@ namespace Sass { if (!stm->is_hoistable()) { if (!stm->block()) indent(); stm->perform(this); - buffer += '\n'; + append_multiline_part_to_buffer("\n"); } } --indentation; @@ -189,19 +193,33 @@ namespace Sass { Statement* stm = (*b)[i]; if (stm->is_hoistable()) { stm->perform(this); - buffer += '\n'; + append_multiline_part_to_buffer("\n"); } } if (decls) --indentation; buffer.erase(buffer.length()-1); + if (ctx) ctx->source_map.remove_line(); if (b->has_hoistable()) { buffer.erase(buffer.length()-1); + if (ctx) ctx->source_map.remove_line(); } - buffer += " }\n"; + append_multiline_part_to_buffer(" }\n"); } void Output_Nested::indent() - { buffer += string(2*indentation, ' '); } + { append_singleline_part_to_buffer(string(2*indentation, ' ')); } + + void Output_Nested::append_singleline_part_to_buffer(const string& text) + { + buffer += text; + if (ctx) ctx->source_map.update_column(text); + } + + void Output_Nested::append_multiline_part_to_buffer(const string& text) + { + buffer += text; + if (ctx) ctx->source_map.new_line(); + } } \ No newline at end of file diff --git a/output_nested.hpp b/output_nested.hpp index 5aa031cf63..f8762b492f 100644 --- a/output_nested.hpp +++ b/output_nested.hpp @@ -23,6 +23,9 @@ namespace Sass { void indent(); void fallback_impl(AST_Node* n); + + void append_singleline_part_to_buffer(const string& text); + void append_multiline_part_to_buffer(const string& text); public: diff --git a/parser.cpp b/parser.cpp index e0eaf95ce8..137ed2693b 100644 --- a/parser.cpp +++ b/parser.cpp @@ -16,18 +16,18 @@ namespace Sass { using namespace std; using namespace Constants; - Parser Parser::from_c_str(const char* str, Context& ctx, string path, size_t line) + Parser Parser::from_c_str(const char* str, Context& ctx, string path, Position source_position) { - Parser p(ctx, path, line); + Parser p(ctx, path, source_position); p.source = str; p.position = p.source; p.end = str + strlen(str); return p; } - Parser Parser::from_token(Token t, Context& ctx, string path, size_t line) + Parser Parser::from_token(Token t, Context& ctx, string path, Position source_position) { - Parser p(ctx, path, line); + Parser p(ctx, path, source_position); p.source = t.begin; p.position = p.source; p.end = t.end; @@ -36,15 +36,15 @@ namespace Sass { Block* Parser::parse() { - Block* root = new (ctx.mem) Block(path, line); + Block* root = new (ctx.mem) Block(path, source_position); root->is_root(true); read_bom(); lex< optional_spaces >(); Selector_Lookahead lookahead_result; while (position < end) { if (lex< block_comment >()) { - String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed); - Comment* comment = new (ctx.mem) Comment(path, line, contents); + String_Constant* contents = new (ctx.mem) String_Constant(path, source_position, lexed); + Comment* comment = new (ctx.mem) Comment(path, source_position, contents); (*root) << comment; } else if (peek< import >()) { @@ -52,7 +52,7 @@ namespace Sass { if (!imp->urls().empty()) (*root) << imp; if (!imp->files().empty()) { for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*root) << new (ctx.mem) Import_Stub(path, line, imp->files()[i]); + (*root) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]); } } if (!lex< exactly<';'> >()) error("top-level @import directive must be terminated by ';'"); @@ -117,18 +117,18 @@ namespace Sass { Import* Parser::parse_import() { lex< import >(); - Import* imp = new (ctx.mem) Import(path, line); + Import* imp = new (ctx.mem) Import(path, source_position); bool first = true; do { if (lex< string_constant >()) { string import_path(lexed); size_t dot = import_path.find_last_of('.'); if (dot != string::npos && import_path.substr(dot, 4) == ".css") { - String_Constant* loc = new (ctx.mem) String_Constant(path, line, import_path); - Argument* loc_arg = new (ctx.mem) Argument(path, line, loc); - Arguments* loc_args = new (ctx.mem) Arguments(path, line); + String_Constant* loc = new (ctx.mem) String_Constant(path, source_position, import_path); + Argument* loc_arg = new (ctx.mem) Argument(path, source_position, loc); + Arguments* loc_args = new (ctx.mem) Arguments(path, source_position); (*loc_args) << loc_arg; - Function_Call* new_url = new (ctx.mem) Function_Call(path, line, "url", loc_args); + Function_Call* new_url = new (ctx.mem) Function_Call(path, source_position, "url", loc_args); imp->urls().push_back(new_url); } else { @@ -159,21 +159,21 @@ namespace Sass { string which_str(lexed); if (!lex< identifier >()) error("invalid name in " + which_str + " definition"); string name(lexed); - size_t line_of_def = line; + Position source_position_of_def = source_position; Parameters* params = parse_parameters(); if (!peek< exactly<'{'> >()) error("body for " + which_str + " " + name + " must begin with a '{'"); if (which_type == Definition::MIXIN) stack.push_back(mixin_def); else stack.push_back(function_def); Block* body = parse_block(); stack.pop_back(); - Definition* def = new (ctx.mem) Definition(path, line_of_def, name, params, body, which_type); + Definition* def = new (ctx.mem) Definition(path, source_position_of_def, name, params, body, which_type); return def; } Parameters* Parser::parse_parameters() { string name(lexed); // for the error message - Parameters* params = new (ctx.mem) Parameters(path, line); + Parameters* params = new (ctx.mem) Parameters(path, source_position); if (lex< exactly<'('> >()) { // if there's anything there at all if (!peek< exactly<')'> >()) { @@ -189,6 +189,7 @@ namespace Sass { { lex< variable >(); string name(lexed); + Position pos = source_position; Expression* val = 0; bool is_rest = false; if (lex< exactly<':'> >()) { // there's a default value @@ -198,7 +199,7 @@ namespace Sass { else if (lex< exactly< ellipsis > >()) { is_rest = true; } - Parameter* p = new (ctx.mem) Parameter(path, line, name, val, is_rest); + Parameter* p = new (ctx.mem) Parameter(path, pos, name, val, is_rest); return p; } @@ -206,21 +207,21 @@ namespace Sass { { lex< include >() /* || lex< exactly<'+'> >() */; if (!lex< identifier >()) error("invalid name in @include directive"); - size_t line_of_call = line; + Position source_position_of_call = source_position; string name(lexed); Arguments* args = parse_arguments(); Block* content = 0; if (peek< exactly<'{'> >()) { content = parse_block(); } - Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, line_of_call, name, args, content); + Mixin_Call* the_call = new (ctx.mem) Mixin_Call(path, source_position_of_call, name, args, content); return the_call; } Arguments* Parser::parse_arguments() { string name(lexed); - Arguments* args = new (ctx.mem) Arguments(path, line); + Arguments* args = new (ctx.mem) Arguments(path, source_position); if (lex< exactly<'('> >()) { // if there's anything there at all @@ -240,10 +241,11 @@ namespace Sass { if (peek< sequence < variable, spaces_and_comments, exactly<':'> > >()) { lex< variable >(); string name(lexed); + Position p = source_position; lex< exactly<':'> >(); Expression* val = parse_space_list(); val->is_delayed(false); - arg = new (ctx.mem) Argument(path, line, val, name); + arg = new (ctx.mem) Argument(path, p, val, name); } else { bool is_arglist = false; @@ -252,7 +254,7 @@ namespace Sass { if (lex< exactly< ellipsis > >()) { is_arglist = true; } - arg = new (ctx.mem) Argument(path, line, val, "", is_arglist); + arg = new (ctx.mem) Argument(path, source_position, val, "", is_arglist); } return arg; } @@ -261,12 +263,12 @@ namespace Sass { { lex< variable >(); string name(lexed); - size_t var_line = line; + Position var_source_position = source_position; if (!lex< exactly<':'> >()) error("expected ':' after " + name + " in assignment statement"); Expression* val = parse_list(); val->is_delayed(false); bool is_guarded = lex< default_flag >(); - Assignment* var = new (ctx.mem) Assignment(path, var_line, name, val, is_guarded); + Assignment* var = new (ctx.mem) Assignment(path, var_source_position, name, val, is_guarded); return var; } @@ -278,9 +280,9 @@ namespace Sass { } else { lex< sequence< optional< exactly<'*'> >, identifier > >(); - property_segment = new (ctx.mem) String_Constant(path, line, lexed); + property_segment = new (ctx.mem) String_Constant(path, source_position, lexed); } - Propset* propset = new (ctx.mem) Propset(path, line, property_segment); + Propset* propset = new (ctx.mem) Propset(path, source_position, property_segment); lex< exactly<':'> >(); if (!peek< exactly<'{'> >()) error("expected a '{' after namespaced property"); @@ -299,57 +301,58 @@ namespace Sass { else { sel = parse_selector_group(); } - size_t r_line = line; + Position r_source_position = source_position; if (!peek< exactly<'{'> >()) error("expected a '{' after the selector"); Block* block = parse_block(); - Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_line, sel, block); + Ruleset* ruleset = new (ctx.mem) Ruleset(path, r_source_position, sel, block); return ruleset; } Selector_Schema* Parser::parse_selector_schema(const char* end_of_selector) { + lex< optional_spaces >(); const char* i = position; const char* p; - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); while (i < end_of_selector) { p = find_first_in_interval< exactly >(i, end_of_selector); if (p) { // accumulate the preceding segment if there is one - if (i < p) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, p)); + if (i < p) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // find the end of the interpolant and parse it const char* j = find_first_in_interval< exactly >(p, end_of_selector); - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j + 1; } else { // no interpolants left; add the last segment if there is one - if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, end_of_selector)); + if (i < end_of_selector) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, end_of_selector)); break; } } position = end_of_selector; - return new (ctx.mem) Selector_Schema(path, line, schema); + return new (ctx.mem) Selector_Schema(path, source_position, schema); } Selector_List* Parser::parse_selector_group() { To_String to_string; - Selector_List* group = new (ctx.mem) Selector_List(path, line); + Selector_List* group = new (ctx.mem) Selector_List(path, source_position); do { Complex_Selector* comb = parse_selector_combination(); if (!comb->has_reference()) { - size_t sel_line = line; - Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_line); - Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_line); + Position sel_source_position = source_position; + Selector_Reference* ref = new (ctx.mem) Selector_Reference(path, sel_source_position); + Compound_Selector* ref_wrap = new (ctx.mem) Compound_Selector(path, sel_source_position); (*ref_wrap) << ref; if (!comb->head()) { comb->head(ref_wrap); comb->has_reference(true); } else { - comb = new (ctx.mem) Complex_Selector(path, sel_line, Complex_Selector::ANCESTOR_OF, ref_wrap, comb); + comb = new (ctx.mem) Complex_Selector(path, sel_source_position, Complex_Selector::ANCESTOR_OF, ref_wrap, comb); comb->has_reference(true); } } @@ -361,7 +364,7 @@ namespace Sass { Complex_Selector* Parser::parse_selector_combination() { - size_t sel_line = 0; + Position sel_source_position = Position(); Compound_Selector* lhs; if (peek< exactly<'+'> >() || peek< exactly<'~'> >() || @@ -371,7 +374,7 @@ namespace Sass { } else { lhs = parse_simple_selector_sequence(); - sel_line = line; + sel_source_position = source_position; } Complex_Selector::Combinator cmb; @@ -391,21 +394,21 @@ namespace Sass { } else { rhs = parse_selector_combination(); - sel_line = line; + sel_source_position = source_position; } - if (!sel_line) sel_line = line; - return new (ctx.mem) Complex_Selector(path, sel_line, cmb, lhs, rhs); + if (!sel_source_position.line) sel_source_position = source_position; + return new (ctx.mem) Complex_Selector(path, sel_source_position, cmb, lhs, rhs); } Compound_Selector* Parser::parse_simple_selector_sequence() { - Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, line); + Compound_Selector* seq = new (ctx.mem) Compound_Selector(path, source_position); // check for backref or type selector, which are only allowed at the front if (lex< exactly<'&'> >()) { - (*seq) << new (ctx.mem) Selector_Reference(path, line); + (*seq) << new (ctx.mem) Selector_Reference(path, source_position); } else if (lex< sequence< negate< functional >, alternatives< type_selector, universal, string_constant, dimension, percentage, number > > >()) { - (*seq) << new (ctx.mem) Type_Selector(path, line, lexed); + (*seq) << new (ctx.mem) Type_Selector(path, source_position, lexed); } else { (*seq) << parse_simple_selector(); @@ -428,10 +431,10 @@ namespace Sass { Simple_Selector* Parser::parse_simple_selector() { if (lex< id_name >() || lex< class_name >()) { - return new (ctx.mem) Selector_Qualifier(path, line, lexed); + return new (ctx.mem) Selector_Qualifier(path, source_position, lexed); } else if (lex< string_constant >() || lex< number >()) { - return new (ctx.mem) Type_Selector(path, line, lexed); + return new (ctx.mem) Type_Selector(path, source_position, lexed); } else if (peek< pseudo_not >()) { return parse_negated_selector(); @@ -443,7 +446,7 @@ namespace Sass { return parse_attribute_selector(); } else if (lex< placeholder >()) { - return new (ctx.mem) Selector_Placeholder(path, line, lexed); + return new (ctx.mem) Selector_Placeholder(path, source_position, lexed); } else { error("invalid selector after " + lexed.to_string()); @@ -455,59 +458,60 @@ namespace Sass { Negated_Selector* Parser::parse_negated_selector() { lex< pseudo_not >(); - size_t nline = line; + Position nsource_position = source_position; Selector* negated = parse_selector_group(); if (!lex< exactly<')'> >()) { error("negated selector is missing ')'"); } - return new (ctx.mem) Negated_Selector(path, nline, negated); + return new (ctx.mem) Negated_Selector(path, nsource_position, negated); } Pseudo_Selector* Parser::parse_pseudo_selector() { if (lex< sequence< pseudo_prefix, functional > >() || lex< functional >()) { string name(lexed); String* expr = 0; + Position p = source_position; if (lex< alternatives< even, odd > >()) { - expr = new (ctx.mem) String_Constant(path, line, lexed); + expr = new (ctx.mem) String_Constant(path, p, lexed); } else if (peek< binomial >(position)) { lex< sequence< optional< coefficient >, exactly<'n'> > >(); - String_Constant* var_coef = new (ctx.mem) String_Constant(path, line, lexed); + String_Constant* var_coef = new (ctx.mem) String_Constant(path, p, lexed); lex< sign >(); - String_Constant* op = new (ctx.mem) String_Constant(path, line, lexed); + String_Constant* op = new (ctx.mem) String_Constant(path, p, lexed); // Binary_Expression::Type op = (lexed == "+" ? Binary_Expression::ADD : Binary_Expression::SUB); lex< digits >(); - String_Constant* constant = new (ctx.mem) String_Constant(path, line, lexed); - // expr = new (ctx.mem) Binary_Expression(path, line, op, var_coef, constant); - String_Schema* schema = new (ctx.mem) String_Schema(path, line, 3); + String_Constant* constant = new (ctx.mem) String_Constant(path, p, lexed); + // expr = new (ctx.mem) Binary_Expression(path, p, op, var_coef, constant); + String_Schema* schema = new (ctx.mem) String_Schema(path, p, 3); *schema << var_coef << op << constant; expr = schema; } else if (lex< sequence< optional, optional, exactly<'n'> > >()) { - expr = new (ctx.mem) String_Constant(path, line, lexed); + expr = new (ctx.mem) String_Constant(path, p, lexed); } else if (lex< sequence< optional, digits > >()) { - expr = new (ctx.mem) String_Constant(path, line, lexed); + expr = new (ctx.mem) String_Constant(path, p, lexed); } else if (lex< identifier >()) { - expr = new (ctx.mem) String_Constant(path, line, lexed); + expr = new (ctx.mem) String_Constant(path, p, lexed); } else if (lex< string_constant >()) { - expr = new (ctx.mem) String_Constant(path, line, lexed); + expr = new (ctx.mem) String_Constant(path, p, lexed); } else if (peek< exactly<')'> >()) { - expr = new (ctx.mem) String_Constant(path, line, ""); + expr = new (ctx.mem) String_Constant(path, p, ""); } else { error("invalid argument to " + name + "...)"); } if (!lex< exactly<')'> >()) error("unterminated argument to " + name + "...)"); - return new (ctx.mem) Pseudo_Selector(path, line, name, expr); + return new (ctx.mem) Pseudo_Selector(path, p, name, expr); } else if (lex < sequence< pseudo_prefix, identifier > >()) { - return new (ctx.mem) Pseudo_Selector(path, line, lexed); + return new (ctx.mem) Pseudo_Selector(path, source_position, lexed); } else { error("unrecognized pseudo-class or pseudo-element"); @@ -519,9 +523,10 @@ namespace Sass { Attribute_Selector* Parser::parse_attribute_selector() { lex< exactly<'['> >(); + Position p = source_position; if (!lex< type_selector >()) error("invalid attribute name in attribute selector"); string name(lexed); - if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, line, name, "", ""); + if (lex< exactly<']'> >()) return new (ctx.mem) Attribute_Selector(path, p, name, "", ""); if (!lex< alternatives< exact_match, class_match, dash_match, prefix_match, suffix_match, substring_match > >()) { error("invalid operator in attribute selector for " + name); @@ -530,7 +535,7 @@ namespace Sass { if (!lex< string_constant >() && !lex< identifier >()) error("expected a string constant or identifier in attribute selector for " + name); string value(lexed); if (!lex< exactly<']'> >()) error("unterminated attribute selector for " + name); - return new (ctx.mem) Attribute_Selector(path, line, name, matcher, value); + return new (ctx.mem) Attribute_Selector(path, p, name, matcher, value); } Block* Parser::parse_block() @@ -538,7 +543,7 @@ namespace Sass { lex< exactly<'{'> >(); bool semicolon = false; Selector_Lookahead lookahead_result; - Block* block = new (ctx.mem) Block(path, line); + Block* block = new (ctx.mem) Block(path, source_position); while (!lex< exactly<'}'> >()) { if (semicolon) { if (!lex< exactly<';'> >()) { @@ -546,27 +551,27 @@ namespace Sass { } semicolon = false; while (lex< block_comment >()) { - String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed); - Comment* comment = new (ctx.mem) Comment(path, line, contents); + String_Constant* contents = new (ctx.mem) String_Constant(path, source_position, lexed); + Comment* comment = new (ctx.mem) Comment(path, source_position, contents); (*block) << comment; } if (lex< exactly<'}'> >()) break; } if (lex< block_comment >()) { - String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed); - Comment* comment = new (ctx.mem) Comment(path, line, contents); + String_Constant* contents = new (ctx.mem) String_Constant(path, source_position, lexed); + Comment* comment = new (ctx.mem) Comment(path, source_position, contents); (*block) << comment; } else if (peek< import >(position)) { if (stack.back() == mixin_def || stack.back() == function_def) { - lex< import >(); // to adjust the line number + lex< import >(); // to adjust the source_position number error("@import directives are not allowed inside mixins and functions"); } Import* imp = parse_import(); if (!imp->urls().empty()) (*block) << imp; if (!imp->files().empty()) { for (size_t i = 0, S = imp->files().size(); i < S; ++i) { - (*block) << new (ctx.mem) Import_Stub(path, line, imp->files()[i]); + (*block) << new (ctx.mem) Import_Stub(path, source_position, imp->files()[i]); } } semicolon = true; @@ -588,7 +593,7 @@ namespace Sass { (*block) << parse_while_directive(); } else if (lex < return_directive >()) { - (*block) << new (ctx.mem) Return(path, line, parse_list()); + (*block) << new (ctx.mem) Return(path, source_position, parse_list()); semicolon = true; } else if (peek< warn >()) { @@ -611,7 +616,7 @@ namespace Sass { if (stack.back() != mixin_def) { error("@content may only be used within a mixin"); } - (*block) << new (ctx.mem) Content(path, line); + (*block) << new (ctx.mem) Content(path, source_position); semicolon = true; } /* @@ -626,7 +631,7 @@ namespace Sass { Selector* target; if (lookahead.has_interpolants) target = parse_selector_schema(lookahead.found); else target = parse_selector_group(); - (*block) << new (ctx.mem) Extension(path, line, target); + (*block) << new (ctx.mem) Extension(path, source_position, target); semicolon = true; } else if (peek< media >()) { @@ -660,7 +665,7 @@ namespace Sass { (*block) << decl; if (peek< exactly<'{'> >()) { // parse a propset that rides on the declaration's property - Propset* ps = new (ctx.mem) Propset(path, line, decl->property(), parse_block()); + Propset* ps = new (ctx.mem) Propset(path, source_position, decl->property(), parse_block()); (*block) << ps; } else { @@ -671,8 +676,8 @@ namespace Sass { } else lex< exactly<';'> >(); while (lex< block_comment >()) { - String_Constant* contents = new (ctx.mem) String_Constant(path, line, lexed); - Comment* comment = new (ctx.mem) Comment(path, line, contents); + String_Constant* contents = new (ctx.mem) String_Constant(path, source_position, lexed); + Comment* comment = new (ctx.mem) Comment(path, source_position, contents); (*block) << comment; } } @@ -685,7 +690,7 @@ namespace Sass { prop = parse_identifier_schema(); } else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) { - prop = new (ctx.mem) String_Constant(path, line, lexed); + prop = new (ctx.mem) String_Constant(path, source_position, lexed); } else { error("invalid property name"); @@ -693,7 +698,7 @@ namespace Sass { if (!lex< exactly<':'> >()) error("property \"" + string(lexed) + "\" must be followed by a ':'"); if (peek< exactly<';'> >()) error("style declaration must contain a value"); Expression* list = parse_list(); - return new (ctx.mem) Declaration(path, prop->line(), prop, list/*, lex()*/); + return new (ctx.mem) Declaration(path, prop->position(), prop, list/*, lex()*/); } Expression* Parser::parse_list() @@ -710,12 +715,12 @@ namespace Sass { peek< exactly<')'> >(position) || //peek< exactly<':'> >(position) || peek< exactly >(position)) - { return new (ctx.mem) List(path, line, 0); } + { return new (ctx.mem) List(path, source_position, 0); } Expression* list1 = parse_space_list(); // if it's a singleton, return it directly; don't wrap it if (!peek< exactly<','> >(position)) return list1; - List* comma_list = new (ctx.mem) List(path, line, 2, List::COMMA); + List* comma_list = new (ctx.mem) List(path, source_position, 2, List::COMMA); (*comma_list) << list1; while (lex< exactly<','> >()) @@ -742,7 +747,7 @@ namespace Sass { peek< default_flag >(position)) { return disj1; } - List* space_list = new (ctx.mem) List(path, line, 2, List::SPACE); + List* space_list = new (ctx.mem) List(path, source_position, 2, List::SPACE); (*space_list) << disj1; while (!(//peek< exactly<'!'> >(position) || @@ -810,7 +815,7 @@ namespace Sass { Expression* expr2 = parse_expression(); - return new (ctx.mem) Binary_Expression(path, expr1->line(), op, expr1, expr2); + return new (ctx.mem) Binary_Expression(path, expr1->position(), op, expr1, expr2); } Expression* Parser::parse_expression() @@ -873,18 +878,18 @@ namespace Sass { return parse_ie_stuff(); } else if (peek< ie_keyword_arg >()) { - String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, line, 3); - if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, line, lexed); + String_Schema* kwd_arg = new (ctx.mem) String_Schema(path, source_position, 3); + if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, lexed); else { lex< alternatives< identifier_schema, identifier > >(); - *kwd_arg << new (ctx.mem) String_Constant(path, line, lexed); + *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); } lex< exactly<'='> >(); - *kwd_arg << new (ctx.mem) String_Constant(path, line, lexed); - if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, line, lexed); + *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); + if (lex< variable >()) *kwd_arg << new (ctx.mem) Variable(path, source_position, lexed); else { lex< alternatives< identifier_schema, identifier, number > >(); - *kwd_arg << new (ctx.mem) String_Constant(path, line, lexed); + *kwd_arg << new (ctx.mem) String_Constant(path, source_position, lexed); } return kwd_arg; } @@ -898,10 +903,10 @@ namespace Sass { return parse_function_call(); } else if (lex< sequence< exactly<'+'>, spaces_and_comments, negate< number > > >()) { - return new (ctx.mem) Unary_Expression(path, line, Unary_Expression::PLUS, parse_factor()); + return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::PLUS, parse_factor()); } else if (lex< sequence< exactly<'-'>, spaces_and_comments, negate< number> > >()) { - return new (ctx.mem) Unary_Expression(path, line, Unary_Expression::MINUS, parse_factor()); + return new (ctx.mem) Unary_Expression(path, source_position, Unary_Expression::MINUS, parse_factor()); } else { return parse_value(); @@ -912,8 +917,8 @@ namespace Sass { { if (lex< uri_prefix >()) { // TODO: really need to clean up this chunk - Arguments* args = new (ctx.mem) Arguments(path, line); - Function_Call* result = new (ctx.mem) Function_Call(path, line, "url", args); + Arguments* args = new (ctx.mem) Arguments(path, source_position); + Function_Call* result = new (ctx.mem) Function_Call(path, source_position, "url", args); // gah, gonna have to use exception handling to do backtracking ... const char* here = position; try { @@ -921,21 +926,21 @@ namespace Sass { if (peek< string_constant >()) { // cerr << "parsing a url string" << endl; String* str = parse_string(); - (*args) << new (ctx.mem) Argument(path, line, str); + (*args) << new (ctx.mem) Argument(path, source_position, str); // cerr << "done" << endl; } else if (peek< sequence< url_schema, spaces_and_comments, exactly<')'> > >()) { // cerr << "url schema" << endl; lex< url_schema >(); - String_Schema* the_url = Parser::from_token(lexed, ctx, path, line).parse_url_schema(); - (*args) << new (ctx.mem) Argument(path, line, the_url); + String_Schema* the_url = Parser::from_token(lexed, ctx, path, source_position).parse_url_schema(); + (*args) << new (ctx.mem) Argument(path, source_position, the_url); // cerr << "done" << endl; } else if (peek< sequence< url_value, spaces_and_comments, exactly<')'> > >()) { // cerr << "url value" << endl; lex< url_value >(); - String_Constant* the_url = new (ctx.mem) String_Constant(path, line, lexed); - (*args) << new (ctx.mem) Argument(path, line, the_url); + String_Constant* the_url = new (ctx.mem) String_Constant(path, source_position, lexed); + (*args) << new (ctx.mem) Argument(path, source_position, the_url); // cerr << "done" << endl; } else { @@ -944,8 +949,8 @@ namespace Sass { const char* rparen = find_first< exactly<')'> >(position); if (!rparen) error("URI is missing ')'"); Token content_tok(Token(value, rparen)); - String_Constant* content_node = new (ctx.mem) String_Constant(path, line, content_tok); - (*args) << new (ctx.mem) Argument(path, line, content_node); + String_Constant* content_node = new (ctx.mem) String_Constant(path, source_position, content_tok); + (*args) << new (ctx.mem) Argument(path, source_position, content_node); position = rparen; // cerr << "done" << endl; } @@ -954,7 +959,7 @@ namespace Sass { // cerr << "expression" << endl; position = here; Expression* expr = parse_list(); - (*args) << new (ctx.mem) Argument(path, line, expr); + (*args) << new (ctx.mem) Argument(path, source_position, expr); // cerr << "done" << endl; } } @@ -966,43 +971,43 @@ namespace Sass { } if (lex< important >()) - { return new (ctx.mem) String_Constant(path, line, "!important"); } + { return new (ctx.mem) String_Constant(path, source_position, "!important"); } if (lex< value_schema >()) - { return Parser::from_token(lexed, ctx, path, line).parse_value_schema(); } + { return Parser::from_token(lexed, ctx, path, source_position).parse_value_schema(); } if (lex< sequence< true_val, negate< identifier > > >()) - { return new (ctx.mem) Boolean(path, line, true); } + { return new (ctx.mem) Boolean(path, source_position, true); } if (lex< sequence< false_val, negate< identifier > > >()) - { return new (ctx.mem) Boolean(path, line, false); } + { return new (ctx.mem) Boolean(path, source_position, false); } if (lex< sequence< null, negate< identifier > > >()) - { return new (ctx.mem) Null(path, line); } + { return new (ctx.mem) Null(path, source_position); } if (lex< identifier >()) { - String_Constant* str = new (ctx.mem) String_Constant(path, line, lexed); + String_Constant* str = new (ctx.mem) String_Constant(path, source_position, lexed); str->is_delayed(true); return str; } if (lex< percentage >()) - { return new (ctx.mem) Textual(path, line, Textual::PERCENTAGE, lexed); } + { return new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); } if (lex< dimension >()) - { return new (ctx.mem) Textual(path, line, Textual::DIMENSION, lexed); } + { return new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); } if (lex< number >()) - { return new (ctx.mem) Textual(path, line, Textual::NUMBER, lexed); } + { return new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); } if (lex< hex >()) - { return new (ctx.mem) Textual(path, line, Textual::HEX, lexed); } + { return new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); } if (peek< string_constant >()) { return parse_string(); } if (lex< variable >()) - { return new (ctx.mem) Variable(path, line, lexed); } + { return new (ctx.mem) Variable(path, source_position, lexed); } error("error reading values after " + lexed.to_string()); @@ -1018,23 +1023,23 @@ namespace Sass { // see if there any interpolants const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(str.begin, str.end); if (!p) { - String_Constant* str_node = new (ctx.mem) String_Constant(path, line, str); + String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str); str_node->is_delayed(true); return str_node; } - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); schema->quote_mark(*str.begin); while (i < str.end) { p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, str.end); if (p) { if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, p)); // accumulate the preceding segment if it's nonempty + (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty } const char* j = find_first_in_interval< exactly >(p, str.end); // find the closing brace if (j) { // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j+1; @@ -1045,7 +1050,7 @@ namespace Sass { } } else { // no interpolants left; add the last segment if nonempty - if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, str.end)); + if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end)); break; } } @@ -1062,22 +1067,22 @@ namespace Sass { // see if there any interpolants const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(str.begin, str.end); if (!p) { - String_Constant* str_node = new (ctx.mem) String_Constant(path, line, str); + String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str); str_node->is_delayed(true); return str_node; } - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); while (i < str.end) { p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, str.end); if (p) { if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, p)); // accumulate the preceding segment if it's nonempty + (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty } const char* j = find_first_in_interval< exactly >(p, str.end); // find the closing brace if (j) { // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j+1; @@ -1088,7 +1093,7 @@ namespace Sass { } } else { // no interpolants left; add the last segment if nonempty - if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, str.end)); + if (i < str.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, str.end)); break; } } @@ -1097,36 +1102,36 @@ namespace Sass { String_Schema* Parser::parse_value_schema() { - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); size_t num_items = 0; while (position < end) { if (lex< interpolant >()) { Token insides(Token(lexed.begin + 2, lexed.end - 1)); - Expression* interp_node = Parser::from_token(insides, ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; } else if (lex< identifier >()) { - (*schema) << new (ctx.mem) String_Constant(path, line, lexed); + (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); } else if (lex< percentage >()) { - (*schema) << new (ctx.mem) Textual(path, line, Textual::PERCENTAGE, lexed); + (*schema) << new (ctx.mem) Textual(path, source_position, Textual::PERCENTAGE, lexed); } else if (lex< dimension >()) { - (*schema) << new (ctx.mem) Textual(path, line, Textual::DIMENSION, lexed); + (*schema) << new (ctx.mem) Textual(path, source_position, Textual::DIMENSION, lexed); } else if (lex< number >()) { - (*schema) << new (ctx.mem) Textual(path, line, Textual::NUMBER, lexed); + (*schema) << new (ctx.mem) Textual(path, source_position, Textual::NUMBER, lexed); } else if (lex< hex >()) { - (*schema) << new (ctx.mem) Textual(path, line, Textual::HEX, lexed); + (*schema) << new (ctx.mem) Textual(path, source_position, Textual::HEX, lexed); } else if (lex< string_constant >()) { - (*schema) << new (ctx.mem) String_Constant(path, line, lexed); + (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); if (!num_items) schema->quote_mark(*lexed.begin); } else if (lex< variable >()) { - (*schema) << new (ctx.mem) Variable(path, line, lexed); + (*schema) << new (ctx.mem) Variable(path, source_position, lexed); } else { error("error parsing interpolated value"); @@ -1138,25 +1143,25 @@ namespace Sass { String_Schema* Parser::parse_url_schema() { - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); while (position < end) { if (position[0] == '/') { lexed = Token(position, position+1); - (*schema) << new (ctx.mem) String_Constant(path, line, lexed); + (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); ++position; } else if (lex< interpolant >()) { Token insides(Token(lexed.begin + 2, lexed.end - 1)); - Expression* interp_node = Parser::from_token(insides, ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; } else if (lex< sequence< identifier, exactly<':'> > >()) { - (*schema) << new (ctx.mem) String_Constant(path, line, lexed); + (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); } else if (lex< filename >()) { - (*schema) << new (ctx.mem) String_Constant(path, line, lexed); + (*schema) << new (ctx.mem) String_Constant(path, source_position, lexed); } else { error("error parsing interpolated url"); @@ -1173,20 +1178,20 @@ namespace Sass { // see if there any interpolants const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(id.begin, id.end); if (!p) { - return new (ctx.mem) String_Constant(path, line, id); + return new (ctx.mem) String_Constant(path, source_position, id); } - String_Schema* schema = new (ctx.mem) String_Schema(path, line); + String_Schema* schema = new (ctx.mem) String_Schema(path, source_position); while (i < id.end) { p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly > >(i, id.end); if (p) { if (i < p) { - (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, p)); // accumulate the preceding segment if it's nonempty + (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty } const char* j = find_first_in_interval< exactly >(p, id.end); // find the closing brace if (j) { // parse the interpolant and accumulate it - Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, line).parse_list(); + Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list(); interp_node->is_interpolant(true); (*schema) << interp_node; i = j+1; @@ -1197,7 +1202,7 @@ namespace Sass { } } else { // no interpolants left; add the last segment if nonempty - if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, line, Token(i, id.end)); + if (i < id.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, id.end)); break; } } @@ -1208,25 +1213,25 @@ namespace Sass { { lex< identifier >(); string name(lexed); - size_t line_of_call = line; + Position source_position_of_call = source_position; - Function_Call* the_call = new (ctx.mem) Function_Call(path, line_of_call, name, parse_arguments()); + Function_Call* the_call = new (ctx.mem) Function_Call(path, source_position_of_call, name, parse_arguments()); return the_call; } Function_Call_Schema* Parser::parse_function_call_schema() { String* name = parse_identifier_schema(); - size_t line_of_call = line; + Position source_position_of_call = source_position; - Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, line_of_call, name, parse_arguments()); + Function_Call_Schema* the_call = new (ctx.mem) Function_Call_Schema(path, source_position_of_call, name, parse_arguments()); return the_call; } If* Parser::parse_if_directive(bool else_if) { lex< if_directive >() || (else_if && lex< exactly >()); - size_t if_line = line; + Position if_source_position = source_position; Expression* predicate = parse_list(); predicate->is_delayed(false); if (!peek< exactly<'{'> >()) error("expected '{' after the predicate for @if"); @@ -1234,7 +1239,7 @@ namespace Sass { Block* alternative = 0; if (lex< else_directive >()) { if (peek< exactly >()) { - alternative = new (ctx.mem) Block(path, line); + alternative = new (ctx.mem) Block(path, source_position); (*alternative) << parse_if_directive(true); } else if (!peek< exactly<'{'> >()) { @@ -1244,13 +1249,13 @@ namespace Sass { alternative = parse_block(); } } - return new (ctx.mem) If(path, if_line, predicate, consequent, alternative); + return new (ctx.mem) If(path, if_source_position, predicate, consequent, alternative); } For* Parser::parse_for_directive() { lex< for_directive >(); - size_t for_line = line; + Position for_source_position = source_position; if (!lex< variable >()) error("@for directive requires an iteration variable"); string var(lexed); if (!lex< from >()) error("expected 'from' keyword in @for directive"); @@ -1264,13 +1269,13 @@ namespace Sass { upper_bound->is_delayed(false); if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @for directive"); Block* body = parse_block(); - return new (ctx.mem) For(path, for_line, var, lower_bound, upper_bound, body, inclusive); + return new (ctx.mem) For(path, for_source_position, var, lower_bound, upper_bound, body, inclusive); } Each* Parser::parse_each_directive() { lex < each_directive >(); - size_t each_line = line; + Position each_source_position = source_position; if (!lex< variable >()) error("@each directive requires an iteration variable"); string var(lexed); if (!lex< in >()) error("expected 'in' keyword in @each directive"); @@ -1284,23 +1289,23 @@ namespace Sass { } if (!peek< exactly<'{'> >()) error("expected '{' after the upper bound in @each directive"); Block* body = parse_block(); - return new (ctx.mem) Each(path, each_line, var, list, body); + return new (ctx.mem) Each(path, each_source_position, var, list, body); } While* Parser::parse_while_directive() { lex< while_directive >(); - size_t while_line = line; + Position while_source_position = source_position; Expression* predicate = parse_list(); predicate->is_delayed(false); Block* body = parse_block(); - return new (ctx.mem) While(path, while_line, predicate, body); + return new (ctx.mem) While(path, while_source_position, predicate, body); } Media_Block* Parser::parse_media_block() { lex< media >(); - size_t media_line = line; + Position media_source_position = source_position; List* media_queries = parse_media_queries(); @@ -1309,12 +1314,12 @@ namespace Sass { } Block* block = parse_block(); - return new (ctx.mem) Media_Block(path, media_line, media_queries, block); + return new (ctx.mem) Media_Block(path, media_source_position, media_queries, block); } List* Parser::parse_media_queries() { - List* media_queries = new (ctx.mem) List(path, line, 0, List::COMMA); + List* media_queries = new (ctx.mem) List(path, source_position, 0, List::COMMA); if (!peek< exactly<'{'> >()) (*media_queries) << parse_media_query(); while (lex< exactly<','> >()) (*media_queries) << parse_media_query(); return media_queries; @@ -1323,13 +1328,13 @@ namespace Sass { // Expression* Parser::parse_media_query() Media_Query* Parser::parse_media_query() { - Media_Query* media_query = new (ctx.mem) Media_Query(path, line); + Media_Query* media_query = new (ctx.mem) Media_Query(path, source_position); if (lex< exactly< not_kwd > >()) media_query->is_negated(true); else if (lex< exactly< only_kwd > >()) media_query->is_restricted(true); if (peek< identifier_schema >()) media_query->media_type(parse_identifier_schema()); - else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, line, lexed)); + else if (lex< identifier >()) media_query->media_type(new (ctx.mem) String_Constant(path, source_position, lexed)); else (*media_query) << parse_media_expression(); while (lex< exactly< and_kwd > >()) (*media_query) << parse_media_expression(); @@ -1341,7 +1346,7 @@ namespace Sass { { if (peek< identifier_schema >()) { String* ss = parse_identifier_schema(); - return new (ctx.mem) Media_Query_Expression(path, line, ss, 0, true); + return new (ctx.mem) Media_Query_Expression(path, source_position, ss, 0, true); } if (!lex< exactly<'('> >()) { error("media query expression must begin with '('"); @@ -1358,14 +1363,14 @@ namespace Sass { if (!lex< exactly<')'> >()) { error("unclosed parenthesis in media query expression"); } - return new (ctx.mem) Media_Query_Expression(path, feature->line(), feature, expression); + return new (ctx.mem) Media_Query_Expression(path, feature->position(), feature, expression); } At_Rule* Parser::parse_at_rule() { lex(); string kwd(lexed); - size_t at_line = line; + Position at_source_position = source_position; Selector* sel = 0; Selector_Lookahead lookahead = lookahead_for_extension_target(position); if (lookahead.found) { @@ -1378,13 +1383,13 @@ namespace Sass { } Block* body = 0; if (peek< exactly<'{'> >()) body = parse_block(); - return new (ctx.mem) At_Rule(path, at_line, kwd, sel, body); + return new (ctx.mem) At_Rule(path, at_source_position, kwd, sel, body); } Warning* Parser::parse_warning() { lex< warn >(); - return new (ctx.mem) Warning(path, line, parse_list()); + return new (ctx.mem) Warning(path, source_position, parse_list()); } Selector_Lookahead Parser::lookahead_for_selector(const char* start) @@ -1568,7 +1573,7 @@ namespace Sass { Expression* Parser::fold_operands(Expression* base, vector& operands, Binary_Expression::Type op) { for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = new (ctx.mem) Binary_Expression(path, line, op, base, operands[i]); + base = new (ctx.mem) Binary_Expression(path, source_position, op, base, operands[i]); Binary_Expression* b = static_cast(base); if (op == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); @@ -1584,7 +1589,7 @@ namespace Sass { Expression* Parser::fold_operands(Expression* base, vector& operands, vector& ops) { for (size_t i = 0, S = operands.size(); i < S; ++i) { - base = new (ctx.mem) Binary_Expression(path, line, ops[i], base, operands[i]); + base = new (ctx.mem) Binary_Expression(path, base->position(), ops[i], base, operands[i]); Binary_Expression* b = static_cast(base); if (ops[i] == Binary_Expression::DIV && b->left()->is_delayed() && b->right()->is_delayed()) { base->is_delayed(true); @@ -1597,9 +1602,9 @@ namespace Sass { return base; } - void Parser::error(string msg, size_t ln) + void Parser::error(string msg, Position pos) { - throw Error(Error::syntax, path, ln ? ln : line, msg); + throw Error(Error::syntax, path, pos.line ? pos : source_position, msg); } } diff --git a/parser.hpp b/parser.hpp index 7bd54686cd..e72015421f 100644 --- a/parser.hpp +++ b/parser.hpp @@ -19,6 +19,12 @@ #include "ast.hpp" #endif +#ifndef SASS_POSITION +#include "position.hpp" +#endif + +#include + struct Selector_Lookahead { const char* found; bool has_interpolants; @@ -42,19 +48,20 @@ namespace Sass { const char* position; const char* end; string path; - size_t line; + size_t column; + Position source_position; Token lexed; - Parser(Context& ctx, string path, size_t line) + Parser(Context& ctx, string path, Position source_position) : ctx(ctx), stack(vector()), - source(0), position(0), end(0), path(path), line(line) + source(0), position(0), end(0), path(path), column(1), source_position(source_position) { stack.push_back(nothing); } - static Parser from_string(string src, Context& ctx, string path = "", size_t line = 1); - static Parser from_c_str(const char* src, Context& ctx, string path = "", size_t line = 1); - static Parser from_token(Token t, Context& ctx, string path = "", size_t line = 1); + static Parser from_string(string src, Context& ctx, string path = "", Position source_position = Position()); + static Parser from_c_str(const char* src, Context& ctx, string path = "", Position source_position = Position()); + static Parser from_token(Token t, Context& ctx, string path = "", Position source_position = Position()); #ifdef __clang__ @@ -118,7 +125,7 @@ namespace Sass { else if (mx == spaces) { after_whitespace = spaces(position); if (after_whitespace) { - line += count_interval<'\n'>(position, after_whitespace); + source_position.line += count_interval<'\n'>(position, after_whitespace); lexed = Token(position, after_whitespace); return position = after_whitespace; } @@ -134,8 +141,25 @@ namespace Sass { } const char* after_token = mx(after_whitespace); if (after_token) { - line += count_interval<'\n'>(position, after_token); + size_t previous_line = source_position.line; + source_position.line += count_interval<'\n'>(position, after_token); + + size_t whitespace = 0; + const char* ptr = after_whitespace - 1; + while (ptr >= position) { + if (*ptr == '\n') + break; + whitespace++; + ptr--; + } + if (previous_line != source_position.line) { + column = 1; + } + + source_position.column = column + whitespace; + column += after_token - after_whitespace + whitespace; lexed = Token(after_whitespace, after_token); + return position = after_token; } else { @@ -149,7 +173,7 @@ namespace Sass { #endif - void error(string msg, size_t ln = 0); + void error(string msg, Position pos = Position()); void read_bom(); Block* parse(); diff --git a/position.hpp b/position.hpp new file mode 100644 index 0000000000..a9086b3357 --- /dev/null +++ b/position.hpp @@ -0,0 +1,22 @@ +#define SASS_POSITION + +#include + +namespace Sass { + + struct Position { + size_t file; + size_t line; + size_t column; + + Position() + : file(0), line(0), column(0) { } + + Position(const size_t file, const size_t line, const size_t column) + : file(file), line(line), column(column) { } + + Position(const size_t line, const size_t column) + : file(0), line(line), column(column) { } + }; + +} diff --git a/sass.cpp b/sass.cpp index 8ee18a20bc..2ff1377cc8 100644 --- a/sass.cpp +++ b/sass.cpp @@ -8,7 +8,10 @@ #endif #include "context.hpp" + +#ifndef SASS_ERROR_HANDLING #include "error_handling.hpp" +#endif extern "C" { using namespace std; @@ -59,7 +62,7 @@ extern "C" { } catch (Error& e) { stringstream msg_stream; - msg_stream << e.path << ":" << e.line << ": error: " << e.message << endl; + msg_stream << e.path << ":" << e.position.line << ": error: " << e.message << endl; string msg(msg_stream.str()); char* msg_str = (char*) malloc(msg.size() + 1); strcpy(msg_str, msg.c_str()); diff --git a/sass_interface.cpp b/sass_interface.cpp index 98ecd78c7a..154eed6573 100644 --- a/sass_interface.cpp +++ b/sass_interface.cpp @@ -6,7 +6,10 @@ #include "sass_interface.h" #include "context.hpp" + +#ifndef SASS_ERROR_HANDLING #include "error_handling.hpp" +#endif #include #include @@ -47,8 +50,10 @@ extern "C" { void sass_free_file_context(sass_file_context* ctx) { - if (ctx->output_string) free(ctx->output_string); - if (ctx->error_message) free(ctx->error_message); + if (ctx->output_string) free(ctx->output_string); + if (ctx->source_map_string) free(ctx->source_map_string); + if (ctx->source_map_file) free(ctx->source_map_file); + if (ctx->error_message) free(ctx->error_message); free_string_array(ctx->included_files, ctx->num_included_files); @@ -86,8 +91,8 @@ extern "C" { Context::Data().source_c_str(c_ctx->source_string) .entry_point("") .output_style((Output_Style) c_ctx->options.output_style) - .source_comments(c_ctx->options.source_comments) - .source_maps(c_ctx->options.source_comments) // fix this + .source_comments(c_ctx->options.source_comments == SASS_SOURCE_COMMENTS_DEFAULT) + .source_maps(false) // Only supported for files. .image_path(c_ctx->options.image_path) .include_paths_c_str(c_ctx->options.include_paths) .include_paths_array(0) @@ -101,7 +106,7 @@ extern "C" { } catch (Error& e) { stringstream msg_stream; - msg_stream << e.path << ":" << e.line << ": error: " << e.message << endl; + msg_stream << e.path << ":" << e.position.line << ": error: " << e.message << endl; c_ctx->error_message = strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; @@ -121,17 +126,25 @@ extern "C" { { using namespace Sass; try { + bool source_maps = false; + string source_map_file = ""; + if (c_ctx->source_map_file && (c_ctx->options.source_comments == SASS_SOURCE_COMMENTS_MAP)) { + source_maps = true; + source_map_file = c_ctx->source_map_file; + } Context cpp_ctx( Context::Data().entry_point(c_ctx->input_path) .output_style((Output_Style) c_ctx->options.output_style) - .source_comments(c_ctx->options.source_comments) - .source_maps(c_ctx->options.source_comments) // fix this + .source_comments(c_ctx->options.source_comments == SASS_SOURCE_COMMENTS_DEFAULT) + .source_maps(source_maps) + .source_map_file(source_map_file) .image_path(c_ctx->options.image_path) .include_paths_c_str(c_ctx->options.include_paths) .include_paths_array(0) .include_paths(vector()) ); c_ctx->output_string = cpp_ctx.compile_file(); + c_ctx->source_map_string = cpp_ctx.generate_source_map(); c_ctx->error_message = 0; c_ctx->error_status = 0; @@ -139,10 +152,11 @@ extern "C" { } catch (Error& e) { stringstream msg_stream; - msg_stream << e.path << ":" << e.line << ": error: " << e.message << endl; + msg_stream << e.path << ":" << e.position.line << ": error: " << e.message << endl; c_ctx->error_message = strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; + c_ctx->source_map_string = 0; } catch(bad_alloc& ba) { stringstream msg_stream; @@ -150,6 +164,7 @@ extern "C" { c_ctx->error_message = strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; + c_ctx->source_map_string = 0; } catch(string& bad_path) { // couldn't find the specified file in the include paths; report an error @@ -158,6 +173,7 @@ extern "C" { c_ctx->error_message = strdup(msg_stream.str().c_str()); c_ctx->error_status = 1; c_ctx->output_string = 0; + c_ctx->source_map_string = 0; } // TO DO: CATCH EVERYTHING ELSE return 0; diff --git a/sass_interface.h b/sass_interface.h index a4a8746a6e..e5a4cb1e15 100644 --- a/sass_interface.h +++ b/sass_interface.h @@ -36,6 +36,8 @@ struct sass_context { struct sass_file_context { const char* input_path; char* output_string; + char* source_map_string; + char* source_map_file; struct sass_options options; int error_status; char* error_message; diff --git a/sassc++.cpp b/sassc++.cpp index b68f029028..e4aa1491f3 100644 --- a/sassc++.cpp +++ b/sassc++.cpp @@ -2,7 +2,10 @@ #include #include #include "context.hpp" + +#ifndef SASS_ERROR_HANDLING #include "error_handling.hpp" +#endif using namespace std; @@ -27,7 +30,7 @@ int main(int argc, char** argv) } } catch (Sass::Error& e) { - cout << e.path << ":" << e.line << ": " << e.message << endl; + cout << e.path << ":" << e.position.line << ": " << e.message << endl; } catch (string& msg) { cout << msg << endl; diff --git a/source_map.cpp b/source_map.cpp new file mode 100644 index 0000000000..edc81822de --- /dev/null +++ b/source_map.cpp @@ -0,0 +1,99 @@ +#include "source_map.hpp" + +#ifndef SASS_CONTEXT +#include "context.hpp" +#endif + +namespace Sass { + + SourceMap::SourceMap(const string& file) : current_position(Position(1, 1)), file(file) { } + + string SourceMap::generate_source_map(Context* ctx) { + string result = "{\n"; + result += " \"version\": 3,\n"; + result += " \"file\": \"" + file + "\",\n"; + result += " \"sources\": ["; + for (size_t i = 0; i < files.size(); ++i) { + result+="\"" + files[i] + "\","; + } + if (!files.empty()) result.erase(result.length() - 1); + result += "],\n"; + result += " \"names\": [],\n"; + result += " \"mappings\": \"" + serialize_mappings() + "\"\n"; + result += "}"; + + return result; + } + + + string SourceMap::serialize_mappings() { + string result = ""; + + size_t previous_generated_line = 0; + size_t previous_generated_column = 0; + size_t previous_original_line = 0; + size_t previous_original_column = 0; + size_t previous_original_file = 0; + for (size_t i = 0; i < mappings.size(); ++i) { + const size_t generated_line = mappings[i].generated_position.line - 1; + const size_t generated_column = mappings[i].generated_position.column - 1; + const size_t original_line = mappings[i].original_position.line - 1; + const size_t original_column = mappings[i].original_position.column - 1; + const size_t original_file = mappings[i].original_position.file - 1; + + if (generated_line != previous_generated_line) { + previous_generated_column = 0; + while (generated_line != previous_generated_line) { + result += ";"; + previous_generated_line += 1; + } + } + else { + if (i > 0) result += ","; + } + + // generated column + result += base64vlq.encode(generated_column - previous_generated_column); + previous_generated_column = generated_column; + // file + result += base64vlq.encode(original_file - previous_original_file); + previous_original_file = original_file; + // source line + result += base64vlq.encode(original_line - previous_original_line); + previous_original_line = original_line; + // source column + result += base64vlq.encode(original_column - previous_original_column); + previous_original_column = original_column; + } + + return result; + } + + void SourceMap::new_line() + { + current_position.line += 1; + current_position.column = 1; + } + + void SourceMap::remove_line() + { + current_position.line -= 1; + current_position.column = 1; + } + + void SourceMap::update_column(const string& str) + { + current_position.column += str.length(); + } + + void SourceMap::update_column() + { + current_position.column += 1; + } + + void SourceMap::add_mapping(AST_Node* node) + { + mappings.push_back(Mapping(node->position(), current_position)); + } + +} diff --git a/source_map.hpp b/source_map.hpp new file mode 100644 index 0000000000..065d1606ca --- /dev/null +++ b/source_map.hpp @@ -0,0 +1,49 @@ +#define SASS_SOURCE_MAP + +#include + +#ifndef SASS_MAPPING +#include "mapping.hpp" +#endif + +#ifndef SASS_AST +#include "ast.hpp" +#endif + +#ifndef SASSS_BASE64VLQ +#include "base64vlq.hpp" +#endif + + + +namespace Sass { + using std::vector; + + struct Context; + + class SourceMap { + + public: + vector files; + + SourceMap(const string& file); + + void new_line(); + void remove_line(); + void update_column(const string& str); + void update_column(); + void add_mapping(AST_Node* node); + + string generate_source_map(Context* ctx); + + private: + + string serialize_mappings(); + + vector mappings; + Position current_position; + string file; + Base64VLQ base64vlq; + }; + +}