Skip to content

Commit 81363be

Browse files
committed
Refactor feature query support to better match the reference impl
The aim of this refactor is simple to bring the code and AST more inline with the Ruby Sass implementation to help us better mimic their quirks.
1 parent 9b4e424 commit 81363be

15 files changed

+252
-179
lines changed

src/ast.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ namespace Sass {
1414

1515
static Null sass_null(Sass::Null(ParserState("null")));
1616

17+
const bool Supports_Operator::needs_parens(Supports_Condition* cond) {
18+
return dynamic_cast<Supports_Negation*>(cond) ||
19+
(dynamic_cast<Supports_Operator*>(cond) &&
20+
dynamic_cast<Supports_Operator*>(cond)->operand() != operand());
21+
}
22+
23+
const bool Supports_Negation::needs_parens(Supports_Condition* cond) {
24+
return dynamic_cast<Supports_Negation*>(cond) ||
25+
dynamic_cast<Supports_Operator*>(cond);
26+
}
27+
1728
void AST_Node::update_pstate(const ParserState& pstate)
1829
{
1930
pstate_.offset += pstate - pstate_ + pstate.offset;

src/ast.hpp

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -444,20 +444,6 @@ namespace Sass {
444444
ATTACH_OPERATIONS()
445445
};
446446

447-
//////////////////
448-
// Query features.
449-
//////////////////
450-
class Supports_Block : public Has_Block {
451-
ADD_PROPERTY(Supports_Query*, queries)
452-
public:
453-
Supports_Block(ParserState pstate, Supports_Query* queries = 0, Block* block = 0)
454-
: Has_Block(pstate, block), queries_(queries)
455-
{ statement_type(SUPPORTS); }
456-
bool is_hoistable() { return true; }
457-
bool bubbles() { return true; }
458-
ATTACH_OPERATIONS()
459-
};
460-
461447
///////////////////////////////////////////////////////////////////////
462448
// At-rules -- arbitrary directives beginning with "@" that may have an
463449
// optional statement block.
@@ -1461,34 +1447,90 @@ namespace Sass {
14611447
ATTACH_OPERATIONS()
14621448
};
14631449

1464-
///////////////////
1465-
// Feature queries.
1466-
///////////////////
1467-
class Supports_Query : public Expression, public Vectorized<Supports_Condition*> {
1450+
////////////////////
1451+
// `@supports` rule.
1452+
////////////////////
1453+
class Supports_Block : public Has_Block {
1454+
ADD_PROPERTY(Supports_Condition*, condition)
1455+
public:
1456+
Supports_Block(ParserState pstate, Supports_Condition* condition, Block* block = 0)
1457+
: Has_Block(pstate, block), condition_(condition)
1458+
{ statement_type(SUPPORTS); }
1459+
bool is_hoistable() { return true; }
1460+
bool bubbles() { return true; }
1461+
ATTACH_OPERATIONS()
1462+
};
1463+
1464+
//////////////////////////////////////////////////////
1465+
// The abstract superclass of all Supports conditions.
1466+
//////////////////////////////////////////////////////
1467+
class Supports_Condition : public Expression {
14681468
public:
1469-
Supports_Query(ParserState pstate, size_t s = 0)
1470-
: Expression(pstate), Vectorized<Supports_Condition*>(s)
1469+
Supports_Condition(ParserState pstate)
1470+
: Expression(pstate)
14711471
{ }
1472+
virtual const bool needs_parens(Supports_Condition* cond) { return false; }
14721473
ATTACH_OPERATIONS()
14731474
};
14741475

1475-
////////////////////////////////////////////////////////
1476-
// Feature expressions (for use inside feature queries).
1477-
////////////////////////////////////////////////////////
1478-
class Supports_Condition : public Expression, public Vectorized<Supports_Condition*> {
1476+
////////////////////////////////////////////////////////////
1477+
// An operator condition (e.g. `CONDITION1 and CONDITION2`).
1478+
////////////////////////////////////////////////////////////
1479+
class Supports_Operator : public Supports_Condition {
14791480
public:
1480-
enum Operand { NONE, AND, OR, NOT };
1481+
enum Operand { AND, OR };
14811482
private:
1482-
ADD_PROPERTY(String*, feature)
1483-
ADD_PROPERTY(Expression*, value)
1484-
ADD_PROPERTY(Operand, operand)
1485-
ADD_PROPERTY(bool, is_root)
1483+
ADD_PROPERTY(Supports_Condition*, left);
1484+
ADD_PROPERTY(Supports_Condition*, right);
1485+
ADD_PROPERTY(Operand, operand);
1486+
public:
1487+
Supports_Operator(ParserState pstate, Supports_Condition* l, Supports_Condition* r, Operand o)
1488+
: Supports_Condition(pstate), left_(l), right_(r), operand_(o)
1489+
{ }
1490+
virtual const bool needs_parens(Supports_Condition* cond);
1491+
ATTACH_OPERATIONS()
1492+
};
1493+
1494+
//////////////////////////////////////////
1495+
// A negation condition (`not CONDITION`).
1496+
//////////////////////////////////////////
1497+
class Supports_Negation : public Supports_Condition {
1498+
private:
1499+
ADD_PROPERTY(Supports_Condition*, condition);
1500+
public:
1501+
Supports_Negation(ParserState pstate, Supports_Condition* c)
1502+
: Supports_Condition(pstate), condition_(c)
1503+
{ }
1504+
virtual const bool needs_parens(Supports_Condition* cond);
1505+
ATTACH_OPERATIONS()
1506+
};
1507+
1508+
/////////////////////////////////////////////////////
1509+
// A declaration condition (e.g. `(feature: value)`).
1510+
/////////////////////////////////////////////////////
1511+
class Supports_Declaration : public Supports_Condition {
1512+
private:
1513+
ADD_PROPERTY(Expression*, feature);
1514+
ADD_PROPERTY(Expression*, value);
1515+
public:
1516+
Supports_Declaration(ParserState pstate, Expression* f, Expression* v)
1517+
: Supports_Condition(pstate), feature_(f), value_(v)
1518+
{ }
1519+
virtual const bool needs_parens(Supports_Condition* cond) { return false; }
1520+
ATTACH_OPERATIONS()
1521+
};
1522+
1523+
///////////////////////////////////////////////
1524+
// An interpolation condition (e.g. `#{$var}`).
1525+
///////////////////////////////////////////////
1526+
class Supports_Interpolation : public Supports_Condition {
1527+
private:
1528+
ADD_PROPERTY(Expression*, value);
14861529
public:
1487-
Supports_Condition(ParserState pstate, size_t s = 0, String* f = 0,
1488-
Expression* v = 0, Operand o = NONE, bool r = false)
1489-
: Expression(pstate), Vectorized<Supports_Condition*>(s),
1490-
feature_(f), value_(v), operand_(o), is_root_(r)
1530+
Supports_Interpolation(ParserState pstate, Expression* v)
1531+
: Supports_Condition(pstate), value_(v)
14911532
{ }
1533+
virtual const bool needs_parens(Supports_Condition* cond) { return false; }
14921534
ATTACH_OPERATIONS()
14931535
};
14941536

src/ast_fwd_decl.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ namespace Sass {
5959
class String_Quoted;
6060
class Media_Query;
6161
class Media_Query_Expression;
62-
class Supports_Query;
6362
class Supports_Condition;
63+
class Supports_Operator;
64+
class Supports_Negation;
65+
class Supports_Declaration;
66+
class Supports_Interpolation;
6467
class At_Root_Expression;
6568
class Null;
6669
class Parent_Selector;

src/context.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "sass2scss.h"
3333
#include "prelexer.hpp"
3434
#include "emitter.hpp"
35+
#include "debugger.hpp"
3536

3637
namespace Sass {
3738
using namespace Constants;

src/cssize.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ namespace Sass {
167167
p_stack.push_back(m);
168168

169169
Supports_Block* mm = new (ctx.mem) Supports_Block(m->pstate(),
170-
m->queries(),
170+
m->condition(),
171171
m->block()->perform(this)->block());
172172
mm->tabs(m->tabs());
173173

@@ -264,7 +264,7 @@ namespace Sass {
264264
Block* wrapper_block = new (ctx.mem) Block(m->block()->pstate());
265265
*wrapper_block << new_rule;
266266
Supports_Block* mm = new (ctx.mem) Supports_Block(m->pstate(),
267-
m->queries(),
267+
m->condition(),
268268
wrapper_block);
269269

270270
mm->tabs(m->tabs());

src/debugger.hpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,27 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
225225
cerr << ind << "Supports_Block " << block;
226226
cerr << " (" << pstate_source_position(node) << ")";
227227
cerr << " " << block->tabs() << endl;
228-
if (block->block()) for(auto i : block->block()->elements()) { debug_ast(i, ind + " ", env); }
228+
debug_ast(block->condition(), ind + " =@ ");
229+
} else if (dynamic_cast<Supports_Operator*>(node)) {
230+
Supports_Operator* block = dynamic_cast<Supports_Operator*>(node);
231+
cerr << ind << "Supports_Operator " << block;
232+
cerr << " (" << pstate_source_position(node) << ")"
233+
<< endl;
234+
debug_ast(block->left(), ind + " left) ");
235+
debug_ast(block->right(), ind + " right) ");
236+
} else if (dynamic_cast<Supports_Negation*>(node)) {
237+
Supports_Negation* block = dynamic_cast<Supports_Negation*>(node);
238+
cerr << ind << "Supports_Negation " << block;
239+
cerr << " (" << pstate_source_position(node) << ")"
240+
<< endl;
241+
debug_ast(block->condition(), ind + " condition) ");
242+
} else if (dynamic_cast<Supports_Declaration*>(node)) {
243+
Supports_Declaration* block = dynamic_cast<Supports_Declaration*>(node);
244+
cerr << ind << "Supports_Declaration " << block;
245+
cerr << " (" << pstate_source_position(node) << ")"
246+
<< endl;
247+
debug_ast(block->feature(), ind + " feature) ");
248+
debug_ast(block->value(), ind + " value) ");
229249
} else if (dynamic_cast<Block*>(node)) {
230250
Block* root_block = dynamic_cast<Block*>(node);
231251
cerr << ind << "Block " << root_block;

src/eval.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -959,30 +959,40 @@ namespace Sass {
959959
return s;
960960
}
961961

962-
Expression* Eval::operator()(Supports_Query* q)
962+
Expression* Eval::operator()(Supports_Operator* c)
963963
{
964-
Supports_Query* qq = new (ctx.mem) Supports_Query(q->pstate(),
965-
q->length());
966-
for (size_t i = 0, L = q->length(); i < L; ++i) {
967-
*qq << static_cast<Supports_Condition*>((*q)[i]->perform(this));
968-
}
969-
return qq;
964+
Expression* left = c->left()->perform(this);
965+
Expression* right = c->right()->perform(this);
966+
Supports_Operator* cc = new (ctx.mem) Supports_Operator(c->pstate(),
967+
static_cast<Supports_Condition*>(left),
968+
static_cast<Supports_Condition*>(right),
969+
c->operand());
970+
return cc;
970971
}
971972

972-
Expression* Eval::operator()(Supports_Condition* c)
973+
Expression* Eval::operator()(Supports_Negation* c)
973974
{
974-
String* feature = c->feature();
975-
Expression* value = c->value();
976-
value = (value ? value->perform(this) : 0);
977-
Supports_Condition* cc = new (ctx.mem) Supports_Condition(c->pstate(),
978-
c->length(),
975+
Expression* condition = c->condition()->perform(this);
976+
Supports_Negation* cc = new (ctx.mem) Supports_Negation(c->pstate(),
977+
static_cast<Supports_Condition*>(condition));
978+
return cc;
979+
}
980+
981+
Expression* Eval::operator()(Supports_Declaration* c)
982+
{
983+
Expression* feature = c->feature()->perform(this);
984+
Expression* value = c->value()->perform(this);
985+
Supports_Declaration* cc = new (ctx.mem) Supports_Declaration(c->pstate(),
979986
feature,
980-
value,
981-
c->operand(),
982-
c->is_root());
983-
for (size_t i = 0, L = c->length(); i < L; ++i) {
984-
*cc << static_cast<Supports_Condition*>((*c)[i]->perform(this));
985-
}
987+
value);
988+
return cc;
989+
}
990+
991+
Expression* Eval::operator()(Supports_Interpolation* c)
992+
{
993+
Expression* value = c->value()->perform(this);
994+
Supports_Interpolation* cc = new (ctx.mem) Supports_Interpolation(c->pstate(),
995+
value);
986996
return cc;
987997
}
988998

src/eval.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ namespace Sass {
6161
Expression* operator()(Media_Query*);
6262
Expression* operator()(Media_Query_Expression*);
6363
Expression* operator()(At_Root_Expression*);
64-
Expression* operator()(Supports_Query*);
65-
Expression* operator()(Supports_Condition*);
64+
Expression* operator()(Supports_Operator*);
65+
Expression* operator()(Supports_Negation*);
66+
Expression* operator()(Supports_Declaration*);
67+
Expression* operator()(Supports_Interpolation*);
6668
Expression* operator()(Null*);
6769
Expression* operator()(Argument*);
6870
Expression* operator()(Arguments*);

src/expand.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,10 @@ namespace Sass {
146146

147147
Statement* Expand::operator()(Supports_Block* f)
148148
{
149-
Expression* queries = f->queries()->perform(&eval);
149+
Expression* condition = f->condition()->perform(&eval);
150150
Supports_Block* ff = new (ctx.mem) Supports_Block(f->pstate(),
151-
static_cast<Supports_Query*>(queries),
151+
static_cast<Supports_Condition*>(condition),
152152
f->block()->perform(this)->block());
153-
// ff->selector(selector());
154153
return ff;
155154
}
156155

src/inspect.cpp

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ namespace Sass {
8383
append_indentation();
8484
append_token("@supports", feature_block);
8585
append_mandatory_space();
86-
feature_block->queries()->perform(this);
86+
feature_block->condition()->perform(this);
8787
feature_block->block()->perform(this);
8888
}
8989

@@ -516,43 +516,49 @@ namespace Sass {
516516
// output the final token
517517
append_token(res, s);
518518
}
519-
520-
void Inspect::operator()(Supports_Query* fq)
519+
void Inspect::operator()(Supports_Operator* so)
521520
{
522-
size_t i = 0;
523-
(*fq)[i++]->perform(this);
524-
for (size_t L = fq->length(); i < L; ++i) {
525-
(*fq)[i]->perform(this);
526-
}
527-
}
528521

529-
void Inspect::operator()(Supports_Condition* fqc)
530-
{
531-
if (fqc->operand() == Supports_Condition::AND) {
532-
append_mandatory_space();
533-
append_token("and", fqc);
534-
append_mandatory_space();
535-
} else if (fqc->operand() == Supports_Condition::OR) {
522+
if (so->needs_parens(so->left())) append_string("(");
523+
so->left()->perform(this);
524+
if (so->needs_parens(so->left())) append_string(")");
525+
526+
if (so->operand() == Supports_Operator::AND) {
536527
append_mandatory_space();
537-
append_token("or", fqc);
528+
append_token("and", so);
538529
append_mandatory_space();
539-
} else if (fqc->operand() == Supports_Condition::NOT) {
530+
} else if (so->operand() == Supports_Operator::OR) {
540531
append_mandatory_space();
541-
append_token("not", fqc);
532+
append_token("or", so);
542533
append_mandatory_space();
543534
}
544535

545-
if (!fqc->is_root()) append_string("(");
536+
if (so->needs_parens(so->right())) append_string("(");
537+
so->right()->perform(this);
538+
if (so->needs_parens(so->right())) append_string(")");
539+
}
546540

547-
if (!fqc->length()) {
548-
fqc->feature()->perform(this);
549-
append_string(": "); // verified
550-
fqc->value()->perform(this);
551-
}
552-
for (size_t i = 0, L = fqc->length(); i < L; ++i)
553-
(*fqc)[i]->perform(this);
541+
void Inspect::operator()(Supports_Negation* sn)
542+
{
543+
append_token("not", sn);
544+
append_mandatory_space();
545+
if (sn->needs_parens(sn->condition())) append_string("(");
546+
sn->condition()->perform(this);
547+
if (sn->needs_parens(sn->condition())) append_string(")");
548+
}
554549

555-
if (!fqc->is_root()) append_string(")");
550+
void Inspect::operator()(Supports_Declaration* sd)
551+
{
552+
append_string("(");
553+
sd->feature()->perform(this);
554+
append_string(": ");
555+
sd->value()->perform(this);
556+
append_string(")");
557+
}
558+
559+
void Inspect::operator()(Supports_Interpolation* sd)
560+
{
561+
sd->value()->perform(this);
556562
}
557563

558564
void Inspect::operator()(Media_Query* mq)

0 commit comments

Comments
 (0)