diff --git a/include/sass/values.h b/include/sass/values.h index 14aa7fbdf8..c00d091ecd 100644 --- a/include/sass/values.h +++ b/include/sass/values.h @@ -35,13 +35,6 @@ enum Sass_Separator { SASS_HASH }; - // Tags for denoting Sass list delimiters -enum Sass_List_Delimiter { - SASS_NO_DELIMITER, - SASS_PARENTHESIS, - SASS_BRACKETS -}; - // Value Operators enum Sass_OP { AND, OR, // logical connectives diff --git a/src/ast.hpp b/src/ast.hpp index d94f45d7e3..cfa134de22 100644 --- a/src/ast.hpp +++ b/src/ast.hpp @@ -1047,17 +1047,16 @@ namespace Sass { private: ADD_PROPERTY(enum Sass_Separator, separator) ADD_PROPERTY(bool, is_arglist) - ADD_PROPERTY(enum Sass_List_Delimiter, delimiter) + ADD_PROPERTY(bool, is_bracketed) ADD_PROPERTY(bool, from_selector) public: List(ParserState pstate, - size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, - enum Sass_List_Delimiter delimiter = SASS_NO_DELIMITER) + size_t size = 0, enum Sass_Separator sep = SASS_SPACE, bool argl = false, bool bracket = false) : Value(pstate), Vectorized(size), separator_(sep), is_arglist_(argl), - delimiter_(delimiter), + is_bracketed_(bracket), from_selector_(false) { concrete_type(LIST); } List(const List* ptr) @@ -1065,7 +1064,7 @@ namespace Sass { Vectorized(*ptr), separator_(ptr->separator_), is_arglist_(ptr->is_arglist_), - delimiter_(ptr->delimiter_), + is_bracketed_(ptr->is_bracketed_), from_selector_(ptr->from_selector_) { concrete_type(LIST); } std::string type() { return is_arglist_ ? "arglist" : "list"; } @@ -1074,7 +1073,6 @@ namespace Sass { return separator() == SASS_SPACE ? " " : (compressed ? "," : ", "); } - bool is_bracketed() const { return delimiter() == SASS_BRACKETS; } bool is_invisible() const { return empty() && !is_bracketed(); } Expression_Obj value_at_index(size_t i); diff --git a/src/debugger.hpp b/src/debugger.hpp index 8c213b61b1..cea262d3f6 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -595,11 +595,11 @@ inline void debug_ast(AST_Node_Ptr node, std::string ind, Env* env) std::cerr << " (" << pstate_source_position(node) << ")"; std::cerr << " (" << expression->length() << ") " << (expression->separator() == SASS_COMMA ? "Comma " : expression->separator() == SASS_HASH ? "Map " : "Space ") << - (expression->delimiter() == SASS_PARENTHESIS ? "Parenthesis " : expression->delimiter() == SASS_BRACKETS ? "Bracket " : "None ") << " [delayed: " << expression->is_delayed() << "] " << " [interpolant: " << expression->is_interpolant() << "] " << " [listized: " << expression->from_selector() << "] " << " [arglist: " << expression->is_arglist() << "] " << + " [bracketed: " << expression->is_bracketed() << "] " << " [expanded: " << expression->is_expanded() << "] " << " [hash: " << expression->hash() << "] " << std::endl; diff --git a/src/eval.cpp b/src/eval.cpp index 4448a07eda..1d3c2de5aa 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -462,7 +462,7 @@ namespace Sass { l->length(), l->separator(), l->is_arglist(), - l->delimiter()); + l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { ll->append((*l)[i]->perform(this)); } diff --git a/src/functions.cpp b/src/functions.cpp index fe7cdad255..172cce2bb5 100644 --- a/src/functions.cpp +++ b/src/functions.cpp @@ -1304,7 +1304,7 @@ namespace Sass { if (l->empty()) error("argument `$list` of `" + std::string(sig) + "` must not be empty", pstate); double index = std::floor(n->value() < 0 ? l->length() + n->value() : n->value() - 1); if (index < 0 || index > l->length() - 1) error("index out of bounds for `" + std::string(sig) + "`", pstate); - List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->delimiter()); + List_Ptr result = SASS_MEMORY_NEW(List, pstate, l->length(), l->separator(), false, l->is_bracketed()); for (size_t i = 0, L = l->length(); i < L; ++i) { result->append(((i == index) ? v : (*l)[i])); } @@ -1340,12 +1340,12 @@ namespace Sass { String_Constant_Obj sep = ARG("$separator", String_Constant); enum Sass_Separator sep_val = (l1 ? l1->separator() : SASS_SPACE); Value* bracketed = ARG("$bracketed", Value); - enum Sass_List_Delimiter delimiter = (l1 ? l1->delimiter() : SASS_NO_DELIMITER); + bool is_bracketed = (l1 ? l1->is_bracketed() : false); if (!l1) { l1 = SASS_MEMORY_NEW(List, pstate, 1); l1->append(ARG("$list1", Expression)); sep_val = (l2 ? l2->separator() : SASS_SPACE); - delimiter = (l2 ? l2->delimiter() : SASS_NO_DELIMITER); + is_bracketed = (l2 ? l2->is_bracketed() : false); } if (!l2) { l2 = SASS_MEMORY_NEW(List, pstate, 1); @@ -1366,9 +1366,9 @@ namespace Sass { String_Constant_Obj bracketed_as_str = SASS_MEMORY_CAST_PTR(String_Constant, bracketed); bool bracketed_is_auto = bracketed_as_str && unquote(bracketed_as_str->value()) == "auto"; if (!bracketed_is_auto) { - delimiter = bracketed->is_false() ? SASS_NO_DELIMITER : SASS_BRACKETS; + is_bracketed = !bracketed->is_false(); } - List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, delimiter); + List_Obj result = SASS_MEMORY_NEW(List, pstate, len, sep_val, false, is_bracketed); result->concat(&l1); result->concat(&l2); return result.detach(); diff --git a/src/parser.cpp b/src/parser.cpp index 73c3c23940..855afdf0ed 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1000,13 +1000,8 @@ namespace Sass { List_Obj map = SASS_MEMORY_NEW(List, pstate, 0, SASS_HASH); // it's not a map so return the lexed value as a list value - if (!lex_css< exactly<':'> >()) { - List_Obj list = SASS_MEMORY_CAST(List, key); - if (list && list->delimiter() == SASS_NO_DELIMITER) { - list->delimiter(SASS_PARENTHESIS); - } - return key; - } + if (!lex_css< exactly<':'> >()) + { return key; } Expression_Obj value = parse_space_list(); @@ -1037,6 +1032,49 @@ namespace Sass { return ↦ } + Expression_Obj Parser::parse_bracket_list() + { + // check if we have an empty list + // return the empty list as such + if (peek_css< list_terminator >(position)) + { + // return an empty list (nothing to delay) + return SASS_MEMORY_NEW(List, pstate, 0, SASS_SPACE, false, true); + } + + bool has_paren = peek_css< exactly<'('> >(); + + // now try to parse a space list + Expression_Obj list = parse_space_list(); + // if it's a singleton, return it (don't wrap it) + if (!peek_css< exactly<','> >(position)) { + List_Obj l = SASS_MEMORY_CAST(List, list); + if (!l || l->is_bracketed() || has_paren) { + List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, true); + bracketed_list->append(&list); + return &bracketed_list; + } + l->is_bracketed(&list); + return &l; + } + + // if we got so far, we actually do have a comma list + List_Obj bracketed_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_COMMA, false, true); + // wrap the first expression + bracketed_list->append(list); + + while (lex_css< exactly<','> >()) + { + // check for abort condition + if (peek_css< list_terminator >(position) + ) { break; } + // otherwise add another expression + bracketed_list->append(parse_space_list()); + } + // return the list + return &bracketed_list; + } + // parse list returns either a space separated list, // a comma separated list or any bare expression found. // so to speak: we unwrap items from lists if possible here! @@ -1050,19 +1088,7 @@ namespace Sass { { // check if we have an empty list // return the empty list as such - if (peek_css< alternatives < - // exactly<'!'>, - exactly<';'>, - exactly<'}'>, - exactly<'{'>, - exactly<')'>, - exactly<']'>, - exactly<':'>, - end_of_file, - exactly, - default_flag, - global_flag - > >(position)) + if (peek_css< list_terminator >(position)) { // return an empty list (nothing to delay) return SASS_MEMORY_NEW(List, pstate, 0); @@ -1086,18 +1112,7 @@ namespace Sass { while (lex_css< exactly<','> >()) { // check for abort condition - if (peek_css< alternatives < - exactly<';'>, - exactly<'}'>, - exactly<'{'>, - exactly<')'>, - exactly<']'>, - exactly<':'>, - end_of_file, - exactly, - default_flag, - global_flag - > >(position) + if (peek_css< list_terminator >(position) ) { break; } // otherwise add another expression comma_list->append(parse_space_list()); @@ -1112,39 +1127,16 @@ namespace Sass { { Expression_Obj disj1 = parse_disjunction(); // if it's a singleton, return it (don't wrap it) - if (peek_css< alternatives < - // exactly<'!'>, - exactly<';'>, - exactly<'}'>, - exactly<'{'>, - exactly<')'>, - exactly<']'>, - exactly<','>, - exactly<':'>, - end_of_file, - exactly, - default_flag, - global_flag - > >(position) - ) { return disj1; } + if (peek_css< space_list_terminator >(position) + ) { + return disj1; } List_Obj space_list = SASS_MEMORY_NEW(List, pstate, 2, SASS_SPACE); space_list->append(disj1); - while (!(peek_css< alternatives < - // exactly<'!'>, - exactly<';'>, - exactly<'}'>, - exactly<'{'>, - exactly<')'>, - exactly<']'>, - exactly<','>, - exactly<':'>, - end_of_file, - exactly, - default_flag, - global_flag - > >(position)) && peek_css< optional_css_whitespace >() != end + while ( + !(peek_css< space_list_terminator >(position)) && + peek_css< optional_css_whitespace >() != end ) { // the space is parsed implicitly? space_list->append(parse_disjunction()); @@ -1339,18 +1331,10 @@ namespace Sass { } else if (lex_css< exactly<'['> >()) { // explicit bracketed - Expression_Obj value = parse_list(); + Expression_Obj value = parse_bracket_list(); // lex the expected closing square bracket if (!lex_css< exactly<']'> >()) error("unclosed squared bracket", pstate); - // fix delimiter - List_Obj list = SASS_MEMORY_CAST(List, value); - if (!list || list->delimiter() != SASS_NO_DELIMITER) { - List_Ptr outer_list = SASS_MEMORY_NEW(List, pstate, 1, SASS_SPACE, false, SASS_BRACKETS); - outer_list->append(&value); - return outer_list; - } - list->delimiter(SASS_BRACKETS); - return value; + return &value; } // string may be interpolated // if (lex< quoted_string >()) { diff --git a/src/parser.hpp b/src/parser.hpp index f4be978261..c6a248998e 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -257,6 +257,7 @@ namespace Sass { bool parse_number_prefix(); Declaration_Obj parse_declaration(); Expression_Obj parse_map(); + Expression_Obj parse_bracket_list(); Expression_Obj parse_list(bool delayed = false); Expression_Obj parse_comma_list(bool delayed = false); Expression_Obj parse_space_list(); diff --git a/src/prelexer.cpp b/src/prelexer.cpp index cab812bba1..5b930ffa4a 100644 --- a/src/prelexer.cpp +++ b/src/prelexer.cpp @@ -1420,6 +1420,28 @@ namespace Sass { >(src); } + const char* list_terminator(const char* src) { + return alternatives < + exactly<';'>, + exactly<'}'>, + exactly<'{'>, + exactly<')'>, + exactly<']'>, + exactly<':'>, + end_of_file, + exactly, + default_flag, + global_flag + >(src); + }; + + const char* space_list_terminator(const char* src) { + return alternatives < + exactly<','>, + list_terminator + >(src); + }; + // const char* real_uri_prefix(const char* src) { // return alternatives< diff --git a/src/prelexer.hpp b/src/prelexer.hpp index 69c4043237..7cac454a12 100644 --- a/src/prelexer.hpp +++ b/src/prelexer.hpp @@ -355,6 +355,10 @@ namespace Sass { const char* ie_keyword_arg_value(const char* src); const char* ie_keyword_arg_property(const char* src); + // characters that terminate parsing of a list + const char* list_terminator(const char* src); + const char* space_list_terminator(const char* src); + // match url() const char* H(const char* src); const char* W(const char* src);