diff --git a/Makefile.conf b/Makefile.conf index 4bc0360e09..492987036f 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -35,6 +35,7 @@ SOURCES = \ units.cpp \ values.cpp \ plugins.cpp \ + source.cpp \ position.cpp \ lexer.cpp \ parser.cpp \ diff --git a/src/ast.hpp b/src/ast.hpp index 7823f7f26c..c3b1e3ba9c 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -81,20 +81,18 @@ namespace Sass { virtual sass::string to_string() const; virtual void cloneChildren() {}; // generic find function (not fully implemented yet) - // ToDo: add specific implementions to all children + // ToDo: add specific implementations to all children virtual bool find ( bool (*f)(AST_Node_Obj) ) { return f(this); }; void update_pstate(const SourceSpan& pstate); - Offset off() { return pstate().off(); } - Position pos() { return pstate().pos(); } - // Some obects are not meant to be compared - // ToDo: maybe fallback to pointer comparison? + // Some objects are not meant to be compared + // ToDo: maybe fall-back to pointer comparison? virtual bool operator== (const AST_Node& rhs) const { throw std::runtime_error("operator== not implemented"); } // We can give some reasonable implementations by using - // inverst operators on the specialized implementations + // invert operators on the specialized implementations virtual bool operator!= (const AST_Node& rhs) const { // Unequal if not equal return !(*this == rhs); diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index c4f4229201..0d5b7975f7 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -12,6 +12,11 @@ ///////////////////////////////////////////// namespace Sass { + class SourceData; + class SourceFile; + class SynthFile; + class ItplFile; + class AST_Node; class ParentStatement; @@ -127,6 +132,11 @@ namespace Sass { typedef SharedImpl type##Obj; \ typedef SharedImpl type##_Obj; \ + IMPL_MEM_OBJ(SourceData); + IMPL_MEM_OBJ(SourceFile); + IMPL_MEM_OBJ(SynthFile); + IMPL_MEM_OBJ(ItplFile); + IMPL_MEM_OBJ(AST_Node); IMPL_MEM_OBJ(Statement); IMPL_MEM_OBJ(Block); diff --git a/src/context.cpp b/src/context.cpp index b6c56d4f9e..7fa7d78183 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -17,6 +17,7 @@ #include "expand.hpp" #include "parser.hpp" #include "cssize.hpp" +#include "source.hpp" namespace Sass { using namespace Constants; @@ -273,11 +274,11 @@ namespace Sass { // get pointer to the loaded content const char* contents = resources[idx].contents; - // keep a copy of the path around (for parserstates) - // ToDo: we clean it, but still not very elegant!? - strings.push_back(sass_copy_c_string(inc.abs_path.c_str())); + SourceFileObj source = SASS_MEMORY_NEW(SourceFile, + inc.abs_path.c_str(), contents, idx); + // create the initial parser state from resource - SourceSpan pstate(strings.back(), contents, idx); + SourceSpan pstate(source); // check existing import stack for possible recursion for (size_t i = 0; i < import_stack.size() - 2; ++i) { @@ -298,7 +299,7 @@ namespace Sass { } // create a parser instance from the given c_str buffer - Parser p(Parser::from_c_str(contents, *this, traces, pstate)); + Parser p(source, *this, traces); // do not yet dispose these buffers sass_import_take_source(import); sass_import_take_srcmap(import); @@ -441,7 +442,7 @@ namespace Sass { if (const char* err_message = sass_import_get_error_message(include_ent)) { if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate); if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces); - else error(err_message, SourceSpan(ctx_path, source, Position(line, column)), traces); + else { error(err_message, { pstate.source, { line, column } }, traces); } } // content for import was set else if (source) { diff --git a/src/debugger.hpp b/src/debugger.hpp index 26a0d2aed4..703d387183 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -310,9 +310,10 @@ inline sass::string longToHex(long long t) { inline sass::string pstate_source_position(AST_Node* node) { sass::sstream str; - Position start(node->pstate()); - Position end(start + node->pstate().offset); - str << (start.file == sass::string::npos ? 99999999 : start.file) + Offset start(node->pstate().position); + Offset end(start + node->pstate().offset); + size_t file = node->pstate().getSrcId(); + str << (file == sass::string::npos ? 99999999 : file) << "@[" << start.line << ":" << start.column << "]" << "-[" << end.line << ":" << end.column << "]"; #ifdef DEBUG_SHARED_PTR @@ -419,7 +420,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) std::cerr << ind << "Parent_Reference " << selector; std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; - std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << std::endl; } else if (Cast(node)) { PseudoSelector* selector = Cast(node); @@ -460,7 +461,6 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " <" << selector->hash() << ">"; std::cerr << " <<" << selector->ns_name() << ">>"; - std::cerr << " <" << prettyprint(selector->pstate().token.ws_before()) << ">"; std::cerr << std::endl; } else if (Cast(node)) { @@ -600,8 +600,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) Comment* block = Cast(node); std::cerr << ind << "Comment " << block; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " " << block->tabs() << - " <" << prettyprint(block->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << " " << block->tabs() << std::endl; debug_ast(block->text(), ind + "// ", env); } else if (Cast(node)) { If* block = Cast(node); @@ -881,7 +880,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; if (expression->quote_mark()) std::cerr << " [quote_mark: " << expression->quote_mark() << "]"; - std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << std::endl; } else if (Cast(node)) { String_Constant* expression = Cast(node); std::cerr << ind << "String_Constant " << expression; @@ -892,7 +891,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) std::cerr << " [" << prettyprint(expression->value()) << "]"; if (expression->is_delayed()) std::cerr << " [delayed]"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; - std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << std::endl; } else if (Cast(node)) { String_Schema* expression = Cast(node); std::cerr << ind << "String_Schema " << expression; @@ -905,7 +904,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) if (expression->has_interpolant()) std::cerr << " [has interpolant]"; if (expression->is_left_interpolant()) std::cerr << " [left interpolant] "; if (expression->is_right_interpolant()) std::cerr << " [right interpolant] "; - std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); } } else if (Cast(node)) { String* expression = Cast(node); @@ -913,7 +912,7 @@ inline void debug_ast(AST_Node* node, sass::string ind, Env* env) std::cerr << " " << expression->concrete_type(); std::cerr << " (" << pstate_source_position(node) << ")"; if (expression->is_interpolant()) std::cerr << " [interpolant]"; - std::cerr << " <" << prettyprint(expression->pstate().token.ws_before()) << ">" << std::endl; + std::cerr << std::endl; } else if (Cast(node)) { Expression* expression = Cast(node); std::cerr << ind << "Expression " << expression; diff --git a/src/error_handling.cpp b/src/error_handling.cpp index 676fdce0db..d96fe529eb 100644 --- a/src/error_handling.cpp +++ b/src/error_handling.cpp @@ -18,8 +18,8 @@ namespace Sass { prefix("Error"), pstate(pstate), traces(traces) { } - InvalidSass::InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg, char* owned_src) - : Base(pstate, msg, traces), owned_src(owned_src) + InvalidSass::InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg) + : Base(pstate, msg, traces) { } diff --git a/src/error_handling.hpp b/src/error_handling.hpp index b185fac3ef..a7e8efc8ca 100644 --- a/src/error_handling.hpp +++ b/src/error_handling.hpp @@ -41,20 +41,8 @@ namespace Sass { class InvalidSass : public Base { public: - InvalidSass(InvalidSass& other) : Base(other), owned_src(other.owned_src) { - // Assumes that `this` will outlive `other`. - other.owned_src = nullptr; - } - - // Required because the copy constructor's argument is not const. - // Can't use `std::move` here because we build on Visual Studio 2013. - InvalidSass(InvalidSass &&other) : Base(other), owned_src(other.owned_src) { - other.owned_src = nullptr; - } - - InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg, char* owned_src = nullptr); - virtual ~InvalidSass() throw() { sass_free_memory(owned_src); }; - char *owned_src; + InvalidSass(SourceSpan pstate, Backtraces traces, sass::string msg); + virtual ~InvalidSass() throw() {}; }; class InvalidParent : public Base { diff --git a/src/eval.cpp b/src/eval.cpp index d9cf2e7ab3..080bfa74eb 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -1503,9 +1503,9 @@ namespace Sass { ExpressionObj sel = s->contents()->perform(this); sass::string result_str(sel->to_string(options())); result_str = unquote(Util::rtrim(result_str)); - char* temp_cstr = sass_copy_c_string(result_str.c_str()); - ctx.strings.push_back(temp_cstr); // attach to context - Parser p = Parser::from_c_str(temp_cstr, ctx, traces, s->pstate()); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, + result_str.c_str(), s->pstate()); + Parser p(source, ctx, traces); // If a schema contains a reference to parent it is already // connected to it, so don't connect implicitly anymore diff --git a/src/expand.cpp b/src/expand.cpp index 9eee6afe75..b9731782a5 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -258,9 +258,9 @@ namespace Sass { { ExpressionObj mq = eval(m->schema()); sass::string str_mq(mq->to_css(ctx.c_options)); - char* str = sass_copy_c_string(str_mq.c_str()); - ctx.strings.push_back(str); - Parser parser(Parser::from_c_str(str, ctx, traces, mq->pstate())); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, + str_mq.c_str(), m->pstate()); + Parser parser(source, ctx, traces); // Create a new CSS only representation of the media rule CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block()); sass::vector parsed = parser.parseCssMediaQueries(); diff --git a/src/extender.cpp b/src/extender.cpp index 1e77ac0b52..937ea5bb00 100644 --- a/src/extender.cpp +++ b/src/extender.cpp @@ -388,7 +388,7 @@ namespace Sass { CssMediaRuleObj mediaContext; if (mediaContexts.hasKey(rule)) mediaContext = mediaContexts.get(rule); SelectorListObj ext = extendList(rule, newExtensions, mediaContext); - // If no extends actually happenedit (for example becaues unification + // If no extends actually happened (for example because unification // failed), we don't need to re-register the selector. if (ObjEqualityFn(oldValue, ext)) continue; rule->elements(ext->elements()); @@ -1063,7 +1063,7 @@ namespace Sass { // TODO(nweiz): I think there may be a way to get perfect trimming // without going quadratic by building some sort of trie-like // data structure that can be used to look up superselectors. - // TODO(mgreter): Check how this perfoms in C++ (up the limit) + // TODO(mgreter): Check how this performs in C++ (up the limit) if (selectors.size() > 100) return selectors; // This is n² on the sequences, but only comparing between separate sequences diff --git a/src/fn_selectors.cpp b/src/fn_selectors.cpp index 46f541a78f..7494451e0e 100644 --- a/src/fn_selectors.cpp +++ b/src/fn_selectors.cpp @@ -36,7 +36,8 @@ namespace Sass { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); - SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); + SelectorListObj sel = Parser::parse_selector(source, ctx, traces); parsedSelectors.push_back(sel); } @@ -90,9 +91,8 @@ namespace Sass { str->quote_mark(0); } sass::string exp_src = exp->to_string(); - SelectorListObj sel = Parser::parse_selector(exp_src.c_str(), ctx, traces, - exp->pstate(), pstate.src, - /*allow_parent=*/true); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); + SelectorListObj sel = Parser::parse_selector(source, ctx, traces, true); for (auto& complex : sel->elements()) { if (complex->empty()) { diff --git a/src/fn_utils.cpp b/src/fn_utils.cpp index 6c53641a71..bcf5363973 100644 --- a/src/fn_utils.cpp +++ b/src/fn_utils.cpp @@ -10,12 +10,13 @@ namespace Sass { Definition* make_native_function(Signature sig, Native_Function func, Context& ctx) { - Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, SourceSpan("[built-in function]")); + SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[built-in function]", sig, std::string::npos); + Parser sig_parser(source, ctx, ctx.traces); sig_parser.lex(); sass::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters_Obj params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(Definition, - SourceSpan("[built-in function]"), + SourceSpan(source), sig, name, params, @@ -26,9 +27,9 @@ namespace Sass { Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx) { using namespace Prelexer; - const char* sig = sass_function_get_signature(c_func); - Parser sig_parser = Parser::from_c_str(sig, ctx, ctx.traces, SourceSpan("[c function]")); + SourceFile* source = SASS_MEMORY_NEW(SourceFile, "[c function]", sig, std::string::npos); + Parser sig_parser(source, ctx, ctx.traces); // allow to overload generic callback plus @warn, @error and @debug with custom functions sig_parser.lex < alternatives < identifier, exactly <'*'>, exactly < Constants::warn_kwd >, @@ -38,7 +39,7 @@ namespace Sass { sass::string name(Util::normalize_underscores(sig_parser.lexed)); Parameters_Obj params = sig_parser.parse_parameters(); return SASS_MEMORY_NEW(Definition, - SourceSpan("[c function]"), + SourceSpan(source), sig, name, params, @@ -130,7 +131,8 @@ namespace Sass { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); - return Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); + return Parser::parse_selector(source, ctx, traces, false); } CompoundSelectorObj get_arg_sel(const sass::string& argname, Env& env, Signature sig, SourceSpan pstate, Backtraces traces, Context& ctx) { @@ -144,7 +146,8 @@ namespace Sass { str->quote_mark(0); } sass::string exp_src = exp->to_string(ctx.c_options); - SelectorListObj sel_list = Parser::parse_selector(exp_src.c_str(), ctx, traces, exp->pstate(), pstate.src, /*allow_parent=*/false); + ItplFile* source = SASS_MEMORY_NEW(ItplFile, exp_src.c_str(), exp->pstate()); + SelectorListObj sel_list = Parser::parse_selector(source, ctx, traces, false); if (sel_list->length() == 0) return {}; return sel_list->first()->first(); } diff --git a/src/parser.cpp b/src/parser.cpp index ae4510ff45..2dd3c59601 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -23,21 +23,29 @@ namespace Sass { using namespace Constants; using namespace Prelexer; - Parser Parser::from_c_str(const char* beg, Context& ctx, Backtraces traces, SourceSpan pstate, const char* source, bool allow_parent) - { - pstate.offset.column = 0; - pstate.offset.line = 0; - Parser p(ctx, pstate, traces, allow_parent); - p.source = source ? source : beg; - p.position = beg ? beg : p.source; - p.end = p.position + strlen(p.position); + + Parser::Parser(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) : + SourceSpan(source), + ctx(ctx), + source(source), + begin(source->begin()), + position(source->begin()), + end(source->end()), + before_token(0, 0), + after_token(0, 0), + pstate(source->getSourceSpan()), + traces(traces), + indentation(0), + nestings(0), + allow_parent(allow_parent) + { Block_Obj root = SASS_MEMORY_NEW(Block, pstate); - p.block_stack.push_back(root); + stack.push_back(Scope::Root); + block_stack.push_back(root); root->is_root(true); - return p; } - void Parser::advanceToNextToken() { + void Parser::advanceToNextToken() { lex < css_comments >(false); // advance to position pstate.position += pstate.offset; @@ -45,9 +53,9 @@ namespace Sass { pstate.offset.line = 0; } - SelectorListObj Parser::parse_selector(const char* beg, Context& ctx, Backtraces traces, SourceSpan pstate, const char* source, bool allow_parent) + SelectorListObj Parser::parse_selector(SourceData* source, Context& ctx, Backtraces traces, bool allow_parent) { - Parser p = Parser::from_c_str(beg, ctx, traces, pstate, source, allow_parent); + Parser p(source, ctx, traces, allow_parent); // ToDo: remap the source-map entries somehow return p.parseSelectorList(false); } @@ -2745,51 +2753,51 @@ namespace Sass { size_t skip = 0; sass::string encoding; bool utf_8 = false; - switch ((unsigned char) source[0]) { + switch ((unsigned char)position[0]) { case 0xEF: - skip = check_bom_chars(source, end, utf_8_bom, 3); + skip = check_bom_chars(position, end, utf_8_bom, 3); encoding = "UTF-8"; utf_8 = true; break; case 0xFE: - skip = check_bom_chars(source, end, utf_16_bom_be, 2); + skip = check_bom_chars(position, end, utf_16_bom_be, 2); encoding = "UTF-16 (big endian)"; break; case 0xFF: - skip = check_bom_chars(source, end, utf_16_bom_le, 2); - skip += (skip ? check_bom_chars(source, end, utf_32_bom_le, 4) : 0); + skip = check_bom_chars(position, end, utf_16_bom_le, 2); + skip += (skip ? check_bom_chars(position, end, utf_32_bom_le, 4) : 0); encoding = (skip == 2 ? "UTF-16 (little endian)" : "UTF-32 (little endian)"); break; case 0x00: - skip = check_bom_chars(source, end, utf_32_bom_be, 4); + skip = check_bom_chars(position, end, utf_32_bom_be, 4); encoding = "UTF-32 (big endian)"; break; case 0x2B: - skip = check_bom_chars(source, end, utf_7_bom_1, 4) - | check_bom_chars(source, end, utf_7_bom_2, 4) - | check_bom_chars(source, end, utf_7_bom_3, 4) - | check_bom_chars(source, end, utf_7_bom_4, 4) - | check_bom_chars(source, end, utf_7_bom_5, 5); + skip = check_bom_chars(position, end, utf_7_bom_1, 4) + | check_bom_chars(position, end, utf_7_bom_2, 4) + | check_bom_chars(position, end, utf_7_bom_3, 4) + | check_bom_chars(position, end, utf_7_bom_4, 4) + | check_bom_chars(position, end, utf_7_bom_5, 5); encoding = "UTF-7"; break; case 0xF7: - skip = check_bom_chars(source, end, utf_1_bom, 3); + skip = check_bom_chars(position, end, utf_1_bom, 3); encoding = "UTF-1"; break; case 0xDD: - skip = check_bom_chars(source, end, utf_ebcdic_bom, 4); + skip = check_bom_chars(position, end, utf_ebcdic_bom, 4); encoding = "UTF-EBCDIC"; break; case 0x0E: - skip = check_bom_chars(source, end, scsu_bom, 3); + skip = check_bom_chars(position, end, scsu_bom, 3); encoding = "SCSU"; break; case 0xFB: - skip = check_bom_chars(source, end, bocu_1_bom, 3); + skip = check_bom_chars(position, end, bocu_1_bom, 3); encoding = "BOCU-1"; break; case 0x84: - skip = check_bom_chars(source, end, gb_18030_bom, 4); + skip = check_bom_chars(position, end, gb_18030_bom, 4); encoding = "GB-18030"; break; default: break; @@ -2872,24 +2880,10 @@ namespace Sass { return base; } - void Parser::error(sass::string msg, Position pos) - { - Position p(pos.line ? pos : before_token); - SourceSpan pstate(path, source, p, Offset(0, 0)); - // `pstate.src` may not outlive stack unwind so we must copy it. - // This is needed since we often parse dynamically generated code, - // e.g. for interpolations, and we normally don't want to keep this - // memory around after we parsed the AST tree successfully. Only on - // errors we want to preserve them for better error reporting. - char *src_copy = sass_copy_c_string(pstate.src); - pstate.src = src_copy; - traces.push_back(Backtrace(pstate)); - throw Exception::InvalidSass(pstate, traces, msg, src_copy); - } - void Parser::error(sass::string msg) { - error(msg, pstate.position); + traces.push_back(Backtrace(pstate)); + throw Exception::InvalidSass(pstate, traces, msg); } // print a css parsing error with actual context information from parsed source @@ -2902,13 +2896,13 @@ namespace Sass { if (!pos) pos = position; const char* last_pos(pos); - if (last_pos > source) { - utf8::prior(last_pos, source); + if (last_pos > begin) { + utf8::prior(last_pos, begin); } // backup position to last significant char - while (trim && last_pos > source && last_pos < end) { + while (trim && last_pos > begin&& last_pos < end) { if (!Util::ascii_isspace(static_cast(*last_pos))) break; - utf8::prior(last_pos, source); + utf8::prior(last_pos, begin); } bool ellipsis_left = false; @@ -2917,9 +2911,9 @@ namespace Sass { if (*pos_left) utf8::next(pos_left, end); if (*end_left) utf8::next(end_left, end); - while (pos_left > source) { + while (pos_left > begin) { if (utf8::distance(pos_left, end_left) >= max_len) { - utf8::prior(pos_left, source); + utf8::prior(pos_left, begin); ellipsis_left = *(pos_left) != '\n' && *(pos_left) != '\r'; utf8::next(pos_left, end); @@ -2927,13 +2921,13 @@ namespace Sass { } const char* prev = pos_left; - utf8::prior(prev, source); + utf8::prior(prev, begin); if (*prev == '\r') break; if (*prev == '\n') break; pos_left = prev; } - if (pos_left < source) { - pos_left = source; + if (pos_left < begin) { + pos_left = begin; } bool ellipsis_right = false; @@ -2957,8 +2951,6 @@ namespace Sass { size_t right_subpos = right.size() > 15 ? right.size() - 15 : 0; if (left_subpos && ellipsis_left) left = ellipsis + left.substr(left_subpos); if (right_subpos && ellipsis_right) right = right.substr(right_subpos) + ellipsis; - // Hotfix when source is null, probably due to interpolation parsing!? - if (source == NULL || *source == 0) source = pstate.src; // now pass new message to the more generic error function error(msg + prefix + quote(left) + middle + quote(right)); } diff --git a/src/parser.hpp b/src/parser.hpp index fae428ffad..25c39b968f 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -13,6 +13,7 @@ #include "context.hpp" #include "position.hpp" #include "prelexer.hpp" +#include "source.hpp" #ifndef MAX_NESTING // Note that this limit is not an exact science @@ -42,31 +43,23 @@ namespace Sass { Context& ctx; sass::vector block_stack; sass::vector stack; - const char* source; + SourceDataObj source; + const char* begin; const char* position; const char* end; - Position before_token; - Position after_token; + Offset before_token; + Offset after_token; SourceSpan pstate; Backtraces traces; size_t indentation; size_t nestings; bool allow_parent; - Token lexed; - Parser(Context& ctx, const SourceSpan& pstate, Backtraces traces, bool allow_parent = true) - : SourceSpan(pstate), ctx(ctx), block_stack(), stack(0), - source(0), position(0), end(0), before_token(pstate.position), after_token(pstate.position), - pstate(pstate), traces(traces), indentation(0), nestings(0), allow_parent(allow_parent) - { - stack.push_back(Scope::Root); - } + Parser(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true); - // static Parser from_string(const sass::string& src, Context& ctx, SourceSpan pstate = SourceSpan("[STRING]")); - static Parser from_c_str(const char* src, Context& ctx, Backtraces, SourceSpan pstate = SourceSpan("[CSTRING]"), const char* source = nullptr, bool allow_parent = true); // special static parsers to convert strings into certain selectors - static SelectorListObj parse_selector(const char* src, Context& ctx, Backtraces, SourceSpan pstate = SourceSpan("[SELECTOR]"), const char* source = nullptr, bool allow_parent = true); + static SelectorListObj parse_selector(SourceData* source, Context& ctx, Backtraces, bool allow_parent = true); #ifdef __clang__ @@ -187,7 +180,7 @@ namespace Sass { after_token.add(it_before_token, it_after_token); // ToDo: could probably do this incremental on original object (API wants offset?) - pstate = SourceSpan(path, source, before_token, after_token - before_token); + pstate = SourceSpan(source, before_token, after_token - before_token); // advance internal char iterator return position = it_after_token; @@ -204,8 +197,8 @@ namespace Sass { Token prev = lexed; // store previous pointer const char* oldpos = position; - Position bt = before_token; - Position at = after_token; + Offset bt = before_token; + Offset at = after_token; SourceSpan op = pstate; // throw away comments // update srcmap position @@ -239,7 +232,6 @@ namespace Sass { #endif void error(sass::string msg); - void error(sass::string msg, Position pos); // generate message with given and expected sample // text before and in the middle are configurable void css_error(const sass::string& msg, diff --git a/src/parser_selectors.cpp b/src/parser_selectors.cpp index 0224ddee01..6f60e616c4 100644 --- a/src/parser_selectors.cpp +++ b/src/parser_selectors.cpp @@ -148,7 +148,7 @@ namespace Sass { if (!seq->empty()) { sel = seq->last()->to_string({ NESTED, 5 }); } // ToDo: parser should throw parser exceptions error("Invalid CSS after \"" + sel + "\": expected \"{\", was \"" + found + "\"\n\n" - "\"" + found + "\" may only be used at the beginning of a compound selector.", state.position); + "\"" + found + "\" may only be used at the beginning of a compound selector."); } // parse functional else if (match < re_functional >()) diff --git a/src/position.cpp b/src/position.cpp index f26d126870..33efdf90e4 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -3,6 +3,7 @@ #include "sass.hpp" #include "position.hpp" +#include "source.hpp" namespace Sass { @@ -56,7 +57,7 @@ namespace Sass { // skip over 10xxxxxx // is 1st bit not set if ((chr & 128) == 0) { - // regular ascii char + // regular ASCII char column += 1; } // is 2nd bit not set @@ -117,11 +118,11 @@ namespace Sass { : Offset(line, column), file(file) { } - SourceSpan::SourceSpan(const char* path, const char* src, const size_t file) - : position(file, 0, 0), offset(0, 0), path(path), src(src) { } + SourceSpan::SourceSpan(const char* path) + : source(SASS_MEMORY_NEW(SynthFile, path)), position(0, 0), offset(0, 0) { } - SourceSpan::SourceSpan(const char* path, const char* src, const Position& position, Offset offset) - : position(position), offset(offset), path(path), src(src) { } + SourceSpan::SourceSpan(SourceDataObj source, const Offset& position, const Offset& offset) + : source(source), position(position), offset(offset) { } Position Position::add(const char* begin, const char* end) { @@ -161,21 +162,4 @@ namespace Sass { return Offset(line - off.line, off.line == line ? column - off.column : column); } - /* not used anymore - remove? - std::ostream& operator<<(std::ostream& strm, const Offset& off) - { - if (off.line == string::npos) strm << "-1:"; else strm << off.line << ":"; - if (off.column == string::npos) strm << "-1"; else strm << off.column; - return strm; - } */ - - /* not used anymore - remove? - std::ostream& operator<<(std::ostream& strm, const Position& pos) - { - if (pos.file != string::npos) strm << pos.file << ":"; - if (pos.line == string::npos) strm << "-1:"; else strm << pos.line << ":"; - if (pos.column == string::npos) strm << "-1"; else strm << pos.column; - return strm; - } */ - } diff --git a/src/position.hpp b/src/position.hpp index 22353cba58..45d78a0494 100644 --- a/src/position.hpp +++ b/src/position.hpp @@ -3,7 +3,8 @@ #include #include -// #include +#include "source_data.hpp" +#include "ast_fwd_decl.hpp" namespace Sass { @@ -101,36 +102,43 @@ namespace Sass { class SourceSpan { - public: // c-tor - SourceSpan(const char* path, const char* src = 0, const size_t file = sass::string::npos); - SourceSpan(const char* path, const char* src, const Position& position, Offset offset = Offset(0, 0)); + public: + + SourceSpan(const char* path); - public: // down casts - Offset off() { return position; } - Position pos() { return position; } - SourceSpan pstate() { return *this; } + SourceSpan(SourceDataObj source, + const Offset& position = Offset(0, 0), + const Offset& offset = Offset(0, 0)); const char* getPath() const { - return path; + return source->getPath(); } + const char* getRawData() const { - return src; + return source->getRawData(); + } + + Offset getPosition() const { + return position; } + size_t getLine() const { return position.line + 1; } + size_t getColumn() const { return position.column + 1; } + size_t getSrcId() const { - return position.file; + return source == nullptr + ? std::string::npos + : source->getSrcId(); } - public: - Position position; + SourceDataObj source; + Offset position; Offset offset; - const char* path; - const char* src; }; diff --git a/src/sass_context.cpp b/src/sass_context.cpp index 630701d279..e10f046345 100644 --- a/src/sass_context.cpp +++ b/src/sass_context.cpp @@ -77,12 +77,13 @@ namespace Sass { // now create the code trace (ToDo: maybe have util functions?) if (e.pstate.position.line != sass::string::npos && e.pstate.position.column != sass::string::npos && - e.pstate.src != nullptr) { - size_t lines = e.pstate.position.line; + e.pstate.source != nullptr) { + Offset offset(e.pstate.position); + size_t lines = offset.line; // scan through src until target line // move line_beg pointer to line start const char* line_beg; - for (line_beg = e.pstate.src; *line_beg != '\0'; ++line_beg) { + for (line_beg = e.pstate.getRawData(); *line_beg != '\0'; ++line_beg) { if (lines == 0) break; if (*line_beg == '\n') --lines; } @@ -96,12 +97,12 @@ namespace Sass { size_t move_in = 0; size_t shorten = 0; size_t left_chars = 42; size_t max_chars = 76; // reported excerpt should not exceed `max_chars` chars - if (e.pstate.position.column > line_len) left_chars = e.pstate.position.column; - if (e.pstate.position.column > left_chars) move_in = e.pstate.position.column - left_chars; + if (offset.column > line_len) left_chars = offset.column; + if (offset.column > left_chars) move_in = offset.column - left_chars; if (line_len > max_chars + move_in) shorten = line_len - move_in - max_chars; utf8::advance(line_beg, move_in, line_end); utf8::retreat(line_end, shorten, line_beg); - sass::string sanitized; sass::string marker(e.pstate.position.column - move_in, '-'); + sass::string sanitized; sass::string marker(offset.column - move_in, '-'); utf8::replace_invalid(line_beg, line_end, std::back_inserter(sanitized)); msg_stream << ">> " << sanitized << "\n"; msg_stream << " " << marker << "^\n"; diff --git a/src/sass_values.cpp b/src/sass_values.cpp index 4ebf8b3785..55bcb4d6a9 100644 --- a/src/sass_values.cpp +++ b/src/sass_values.cpp @@ -343,7 +343,7 @@ extern "C" { rv = Operators::op_strings(op, *lhs, *rhs, options, lhs->pstate()); } - // ToDo: maybe we should should return null value? + // ToDo: maybe we should return null value? if (!rv) return sass_make_error("invalid return value"); // convert result back to ast node diff --git a/src/source.cpp b/src/source.cpp new file mode 100644 index 0000000000..1ffab30b94 --- /dev/null +++ b/src/source.cpp @@ -0,0 +1,69 @@ +#include +#include +#include "source.hpp" +#include "utf8/checked.h" +#include "position.hpp" + +namespace Sass { + + SourceData::SourceData() + : SharedObj() + { + } + + SourceFile::SourceFile( + const char* path, + const char* data, + size_t srcid) : + SourceData(), + path(sass_copy_c_string(path)), + data(sass_copy_c_string(data)), + length(0), + srcid(srcid) + { + length = strlen(data); + } + + SourceFile::~SourceFile() { + sass_free_memory(path); + sass_free_memory(data); + } + + const char* SourceFile::end() const + { + return data + length; + } + + const char* SourceFile::begin() const + { + return data; + } + + const char* SourceFile::getRawData() const + { + return data; + } + + SourceSpan SourceFile::getSourceSpan() + { + return SourceSpan(this); + } + + ItplFile::ItplFile(const char* data, const SourceSpan& pstate) : + SourceFile(pstate.getPath(), + data, pstate.getSrcId()), + pstate(pstate) + {} + + const char* ItplFile::getRawData() const + { + return pstate.getRawData(); + } + + SourceSpan ItplFile::getSourceSpan() + { + return SourceSpan(pstate); + } + +} + diff --git a/src/source.hpp b/src/source.hpp new file mode 100644 index 0000000000..77c591e250 --- /dev/null +++ b/src/source.hpp @@ -0,0 +1,95 @@ +#ifndef SASS_SOURCE_H +#define SASS_SOURCE_H + +#include "sass.hpp" +#include "memory.hpp" +#include "position.hpp" +#include "source_data.hpp" + +namespace Sass { + + class SourceFile : + public SourceData { + protected: + char* path; + char* data; + size_t length; + size_t srcid; + public: + + SourceFile( + const char* path, + const char* data, + size_t srcid); + + ~SourceFile(); + + const char* end() const override final; + const char* begin() const override final; + virtual const char* getRawData() const override; + virtual SourceSpan getSourceSpan() override; + + size_t size() const override final { + return length; + } + + virtual const char* getPath() const override { + return path; + } + + virtual size_t getSrcId() const override { + return srcid; + } + + }; + + class SynthFile : + public SourceData { + protected: + const char* path; + public: + + SynthFile( + const char* path) : + path(path) + {} + + ~SynthFile() {} + + const char* end() const override final { return nullptr; } + const char* begin() const override final { return nullptr; }; + virtual const char* getRawData() const override { return nullptr; }; + virtual SourceSpan getSourceSpan() override { return SourceSpan(path); }; + + size_t size() const override final { + return 0; + } + + virtual const char* getPath() const override { + return path; + } + + virtual size_t getSrcId() const override { + return std::string::npos; + } + + }; + + + class ItplFile : + public SourceFile { + private: + SourceSpan pstate; + public: + + ItplFile(const char* data, + const SourceSpan& pstate); + + // Offset getPosition() const override final; + const char* getRawData() const override final; + SourceSpan getSourceSpan() override final; + }; + +} + +#endif diff --git a/src/source_data.hpp b/src/source_data.hpp new file mode 100644 index 0000000000..c52b02141d --- /dev/null +++ b/src/source_data.hpp @@ -0,0 +1,32 @@ +#ifndef SASS_SOURCE_DATA_H +#define SASS_SOURCE_DATA_H + +#include "sass.hpp" +#include "memory.hpp" + +namespace Sass { + + class SourceSpan; + + class SourceData : + public SharedObj { + public: + SourceData(); + virtual size_t size() const = 0; + virtual size_t getSrcId() const = 0; + virtual const char* end() const = 0; + virtual const char* begin() const = 0; + virtual const char* getPath() const = 0; + // virtual Offset getPosition() const = 0; + virtual const char* getRawData() const = 0; + virtual SourceSpan getSourceSpan() = 0; + + sass::string to_string() const override { + return sass::string{ begin(), end() }; + } + ~SourceData() {} + }; + +} + +#endif diff --git a/src/source_map.cpp b/src/source_map.cpp index 431d0a2150..285d77f839 100644 --- a/src/source_map.cpp +++ b/src/source_map.cpp @@ -175,23 +175,27 @@ namespace Sass { void SourceMap::add_open_mapping(const AST_Node* node) { - mappings.push_back(Mapping(node->pstate().position, current_position)); + const SourceSpan& span(node->pstate()); + Position from(span.getSrcId(), span.position); + mappings.push_back(Mapping(from, current_position)); } void SourceMap::add_close_mapping(const AST_Node* node) { - mappings.push_back(Mapping(node->pstate().position + node->pstate().offset, current_position)); + const SourceSpan& span(node->pstate()); + Position to(span.getSrcId(), span.position + span.offset); + mappings.push_back(Mapping(to, current_position)); } SourceSpan SourceMap::remap(const SourceSpan& pstate) { for (size_t i = 0; i < mappings.size(); ++i) { if ( - mappings[i].generated_position.file == pstate.position.file && + mappings[i].generated_position.file == pstate.getSrcId() && mappings[i].generated_position.line == pstate.position.line && mappings[i].generated_position.column == pstate.position.column - ) return SourceSpan(pstate.path, pstate.src, mappings[i].original_position, pstate.offset); + ) return SourceSpan(pstate.source, mappings[i].original_position, pstate.offset); } - return SourceSpan(pstate.path, pstate.src, Position(-1, -1, -1), Offset(0, 0)); + return SourceSpan(pstate.source, Position(-1, -1, -1), Offset(0, 0)); } diff --git a/win/libsass.targets b/win/libsass.targets index 0a781af46c..6aa17d1203 100644 --- a/win/libsass.targets +++ b/win/libsass.targets @@ -60,6 +60,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/win/libsass.vcxproj.filters b/win/libsass.vcxproj.filters index 6897471e7e..8396b45150 100644 --- a/win/libsass.vcxproj.filters +++ b/win/libsass.vcxproj.filters @@ -195,6 +195,9 @@ Headers + + Headers + Headers @@ -377,6 +380,9 @@ Sources + + Sources + Sources