From 366bc110c39c26c9267a1cc06e578beb94cd93ef Mon Sep 17 00:00:00 2001 From: Stijn Van Nieuwenhuyse Date: Fri, 6 Dec 2013 20:48:58 +0100 Subject: [PATCH] Add support for Source Map Revision 3. The parser has been adjusted to keep track of the current position (line and column) instead of the current line. The position to the start of the token an AST node is generated from, is stored in the node. During code generation we keep track of the current position in the target file. For each node of interest we create a mapping between the position in the source and target file. Finally, a source map following the Source Map Revision 3 specification can be generated for the collected mappings. --- Makefile | 4 +- Makefile.am | 4 +- ast.cpp | 2 +- ast.hpp | 284 +++++++++++++++++----------------- backtrace.hpp | 12 +- base64vlq.cpp | 43 ++++++ base64vlq.hpp | 28 ++++ bind.cpp | 16 +- context.cpp | 56 +++++-- context.hpp | 12 ++ contextualize.cpp | 10 +- error_handling.cpp | 17 ++- error_handling.hpp | 12 +- eval.cpp | 141 ++++++++--------- eval.hpp | 6 +- expand.cpp | 52 +++---- extend.cpp | 10 +- file.cpp | 63 +++++++- file.hpp | 21 +-- functions.cpp | 178 +++++++++++----------- functions.hpp | 8 +- inspect.cpp | 263 ++++++++++++++++++-------------- inspect.hpp | 3 + mapping.hpp | 17 +++ output_compressed.cpp | 75 +++++---- output_compressed.hpp | 3 + output_nested.cpp | 58 ++++--- output_nested.hpp | 3 + parser.cpp | 345 +++++++++++++++++++++--------------------- parser.hpp | 42 +++-- position.hpp | 22 +++ sass.cpp | 5 +- sass_interface.cpp | 32 +++- sass_interface.h | 2 + sassc++.cpp | 5 +- source_map.cpp | 99 ++++++++++++ source_map.hpp | 49 ++++++ 37 files changed, 1252 insertions(+), 750 deletions(-) create mode 100644 base64vlq.cpp create mode 100644 base64vlq.hpp create mode 100644 mapping.hpp create mode 100644 position.hpp create mode 100644 source_map.cpp create mode 100644 source_map.hpp 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; + }; + +}