From c04f5d7ba4f83fecd462df18ea8ad048a34c26ab Mon Sep 17 00:00:00 2001 From: xzyfer Date: Sun, 25 Nov 2018 17:34:31 +1100 Subject: [PATCH] Implement passing arguments to @content See sass/sass#871 Spec https://github.com/sass/sass-spec/pull/1313 Fixes #2723 --- src/ast.hpp | 16 +++++++++------- src/constants.cpp | 3 +++ src/constants.hpp | 3 +++ src/debugger.hpp | 11 +++-------- src/expand.cpp | 9 +++++++-- src/parser.cpp | 21 +++++++++++++++++++-- src/prelexer.cpp | 3 +++ src/prelexer.hpp | 1 + 8 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/ast.hpp b/src/ast.hpp index 7e3747772..dfc59f12c 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -994,14 +994,16 @@ namespace Sass { class Mixin_Call final : public Has_Block { ADD_CONSTREF(std::string, name) ADD_PROPERTY(Arguments_Obj, arguments) + ADD_PROPERTY(Parameters_Obj, block_parameters) public: - Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Block_Obj b = {}) - : Has_Block(pstate, b), name_(n), arguments_(args) + Mixin_Call(ParserState pstate, std::string n, Arguments_Obj args, Parameters_Obj b_params = {}, Block_Obj b = {}) + : Has_Block(pstate, b), name_(n), arguments_(args), block_parameters_(b_params) { } Mixin_Call(const Mixin_Call* ptr) : Has_Block(ptr), name_(ptr->name_), - arguments_(ptr->arguments_) + arguments_(ptr->arguments_), + block_parameters_(ptr->block_parameters_) { } ATTACH_AST_OPERATIONS(Mixin_Call) ATTACH_CRTP_PERFORM_METHODS() @@ -1011,15 +1013,15 @@ namespace Sass { // The @content directive for mixin content blocks. /////////////////////////////////////////////////// class Content final : public Statement { - ADD_PROPERTY(Media_Block_Ptr, media_block) + ADD_PROPERTY(Arguments_Obj, arguments) public: - Content(ParserState pstate) + Content(ParserState pstate, Arguments_Obj args) : Statement(pstate), - media_block_(NULL) + arguments_(args) { statement_type(CONTENT); } Content(const Content* ptr) : Statement(ptr), - media_block_(ptr->media_block_) + arguments_(ptr->arguments_) { statement_type(CONTENT); } ATTACH_AST_OPERATIONS(Content) ATTACH_CRTP_PERFORM_METHODS() diff --git a/src/constants.cpp b/src/constants.cpp index ad6e0f62e..67b5b2c7e 100644 --- a/src/constants.cpp +++ b/src/constants.cpp @@ -125,6 +125,9 @@ namespace Sass { extern const char true_kwd[] = "true"; extern const char false_kwd[] = "false"; + // definition keywords + extern const char using_kwd[] = "using"; + // miscellaneous punctuation and delimiters extern const char percent_str[] = "%"; extern const char empty_str[] = ""; diff --git a/src/constants.hpp b/src/constants.hpp index 4a9327469..7297e5dfa 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -127,6 +127,9 @@ namespace Sass { extern const char true_kwd[]; extern const char false_kwd[]; + // definition keywords + extern const char using_kwd[]; + // miscellaneous punctuation and delimiters extern const char percent_str[]; extern const char empty_str[]; diff --git a/src/debugger.hpp b/src/debugger.hpp index a91f619dd..16c625609 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -398,8 +398,8 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) Content_Ptr block = Cast(node); std::cerr << ind << "Content " << block; std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " [@media:" << block->media_block() << "]"; std::cerr << " " << block->tabs() << std::endl; + debug_ast(block->arguments(), ind + " args: ", env); } else if (Cast(node)) { Import_Stub_Ptr block = Cast(node); std::cerr << ind << "Import_Stub " << block; @@ -480,7 +480,8 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(block) << ")"; std::cerr << " [" << block->name() << "]"; std::cerr << " [has_content: " << block->has_content() << "] " << std::endl; - debug_ast(block->arguments(), ind + " args: "); + debug_ast(block->arguments(), ind + " args: ", env); + debug_ast(block->block_parameters(), ind + " block_params: ", env); if (block->block()) debug_ast(block->block(), ind + " ", env); } else if (Ruleset_Ptr ruleset = Cast(node)) { std::cerr << ind << "Ruleset " << ruleset; @@ -605,12 +606,6 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) " [hash: " << expression->hash() << "] " << std::endl; for(const auto& i : expression->elements()) { debug_ast(i, ind + " ", env); } - } else if (Cast(node)) { - Content_Ptr expression = Cast(node); - std::cerr << ind << "Content " << expression; - std::cerr << " (" << pstate_source_position(node) << ")"; - std::cerr << " [@media:" << expression->media_block() << "]"; - std::cerr << " [Statement]" << std::endl; } else if (Cast(node)) { Boolean_Ptr expression = Cast(node); std::cerr << ind << "Boolean " << expression; diff --git a/src/expand.cpp b/src/expand.cpp index d094ec509..dd16f75d5 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -730,11 +730,13 @@ namespace Sass { Env new_env(def->environment()); env_stack.push_back(&new_env); if (c->block()) { + Parameters_Obj params = c->block_parameters(); + if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate()); // represent mixin content blocks as thunks/closures Definition_Obj thunk = SASS_MEMORY_NEW(Definition, c->pstate(), "@content", - SASS_MEMORY_NEW(Parameters, c->pstate()), + params, c->block(), Definition::MIXIN); thunk->environment(env); @@ -779,10 +781,13 @@ namespace Sass { selector_stack.push_back({}); } + Arguments_Obj args = c->arguments(); + if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate()); + Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, c->pstate(), "@content", - SASS_MEMORY_NEW(Arguments, c->pstate())); + args); Trace_Obj trace = Cast(call->perform(this)); diff --git a/src/parser.cpp b/src/parser.cpp index 516d593db..3cdf0a99e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -644,13 +644,27 @@ namespace Sass { // normalize underscores to hyphens std::string name(Util::normalize_underscores(lexed)); // create the initial mixin call object - Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, {}, {}); + Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call, pstate, name, {}, {}, {}); // parse mandatory arguments call->arguments(parse_arguments()); + // parse using and optional block parameters + bool has_parameters = lex< kwd_using >(); + + if (has_parameters) { + if (!peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \"(\", was "); + } else { + if (peek< exactly<'('> >()) css_error("Invalid CSS", " after ", ": expected \";\", was "); + } + + if (has_parameters) call->block_parameters(parse_parameters()); + // parse optional block if (peek < exactly <'{'> >()) { call->block(parse_block()); } + else if (has_parameters) { + css_error("Invalid CSS", " after ", ": expected \"{\", was "); + } // return ast node return call.detach(); } @@ -2202,7 +2216,10 @@ namespace Sass { Content_Obj Parser::parse_content_directive() { - return SASS_MEMORY_NEW(Content, pstate); + ParserState call_pos = pstate; + Arguments_Obj args = parse_arguments(); + + return SASS_MEMORY_NEW(Content, call_pos, args); } If_Obj Parser::parse_if_directive(bool else_if) diff --git a/src/prelexer.cpp b/src/prelexer.cpp index a43b1ee3c..216407af6 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -1260,6 +1260,9 @@ namespace Sass { const char* kwd_lte(const char* src) { return exactly(src); } + const char* kwd_using(const char* src) { + return keyword(src); + } // match specific IE syntax const char* ie_progid(const char* src) { diff --git a/src/prelexer.hpp b/src/prelexer.hpp index 2d8f83164..d5a086cca 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -25,6 +25,7 @@ namespace Sass { const char* kwd_gte(const char* src); const char* kwd_lt(const char* src); const char* kwd_lte(const char* src); + const char* kwd_using(const char* src); // Match standard control chars const char* kwd_at(const char* src);