Skip to content

Commit d3cd623

Browse files
committed
Merge pull request #1426 from xzyfer/feat/feature-query-refactor
Refactor feature query support to better match the reference impl
2 parents 295b152 + 81363be commit d3cd623

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)