Add an explicit IfClause class

This commit is contained in:
Natalie Weizenbaum 2017-12-02 14:34:03 -08:00
parent ce1ea0d4a3
commit 3de6680bcb
4 changed files with 45 additions and 24 deletions

View File

@ -3,7 +3,6 @@
// https://opensource.org/licenses/MIT.
import 'package:source_span/source_span.dart';
import 'package:tuple/tuple.dart';
import '../../../visitor/interface/statement.dart';
import '../expression.dart';
@ -18,30 +17,50 @@ class IfRule implements Statement {
/// The first clause whose expression evaluates to `true` will have its
/// statements executed. If no expression evaluates to `true`, `lastClause`
/// will be executed if it's not `null`.
final List<Tuple2<Expression, List<Statement>>> clauses;
final List<IfClause> clauses;
/// The final, unconditional `@else` clause.
///
/// This is `null` if there is no unconditional `@else`.
final List<Statement> lastClause;
final IfClause lastClause;
final FileSpan span;
IfRule(Iterable<Tuple2<Expression, Iterable<Statement>>> clauses, this.span,
{Iterable<Statement> lastClause})
: clauses = new List.unmodifiable(clauses.map((pair) =>
new Tuple2(pair.item1, new List.unmodifiable(pair.item2)))),
lastClause =
lastClause == null ? null : new List.unmodifiable(lastClause);
IfRule(Iterable<IfClause> clauses, this.span, {this.lastClause})
: clauses = new List.unmodifiable(clauses) {
assert(clauses.every((clause) => clause.expression != null));
assert(lastClause?.expression == null);
}
T accept<T>(StatementVisitor<T> visitor) => visitor.visitIfRule(this);
String toString() {
var first = true;
return clauses.map((pair) {
return clauses.map((clause) {
var name = first ? 'if' : 'else';
first = false;
return '@$name ${pair.item1} {${pair.item2.join(" ")}}';
return '@$name ${clause.expression} {${clause.children.join(" ")}}';
}).join(' ');
}
}
/// A single clause in an `@if` rule.
class IfClause {
/// The expression to evaluate to determine whether to run this rule, or
/// `null` if this is the final unconditional `@else` clause.
final Expression expression;
/// The statements to evaluate if this clause matches.
final List<Statement> children;
IfClause(this.expression, Iterable<Statement> children)
: children = new List.unmodifiable(children);
IfClause.last(Iterable<Statement> children)
: expression = null,
children = new List.unmodifiable(children);
String toString() =>
(expression == null ? "@else" : "@if $expression") +
" {${children.join(' ')}}";
}

View File

@ -671,16 +671,16 @@ abstract class StylesheetParser extends Parser {
var expression = _expression();
var children = this.children(child);
var clauses = [new Tuple2(expression, children)];
List<Statement> lastClause;
var clauses = [new IfClause(expression, children)];
IfClause lastClause;
while (scanElse(ifIndentation)) {
whitespace();
if (scanIdentifier("if")) {
whitespace();
clauses.add(new Tuple2(_expression(), this.children(child)));
clauses.add(new IfClause(_expression(), this.children(child)));
} else {
lastClause = this.children(child);
lastClause = new IfClause.last(this.children(child));
break;
}
}

View File

@ -634,16 +634,17 @@ class _EvaluateVisitor
Future<Value> visitIfRule(IfRule node) async {
var clause = node.lastClause;
for (var pair in node.clauses) {
if ((await pair.item1.accept(this)).isTruthy) {
clause = pair.item2;
for (var clauseToCheck in node.clauses) {
if ((await clauseToCheck.expression.accept(this)).isTruthy) {
clause = clauseToCheck;
break;
}
}
if (clause == null) return null;
return await _environment.scope(
() => _handleReturn<Statement>(clause, (child) => child.accept(this)),
() => _handleReturn<Statement>(
clause.children, (child) => child.accept(this)),
semiGlobal: true);
}

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/synchronize.dart for details.
//
// Checksum: ae64ba442752642066f0e9e038f5f2c1bbfa866b
// Checksum: 33e96540f3e5999ed172d168221b27910e8672b1
import 'dart:math' as math;
@ -629,16 +629,17 @@ class _EvaluateVisitor
Value visitIfRule(IfRule node) {
var clause = node.lastClause;
for (var pair in node.clauses) {
if (pair.item1.accept(this).isTruthy) {
clause = pair.item2;
for (var clauseToCheck in node.clauses) {
if (clauseToCheck.expression.accept(this).isTruthy) {
clause = clauseToCheck;
break;
}
}
if (clause == null) return null;
return _environment.scope(
() => _handleReturn<Statement>(clause, (child) => child.accept(this)),
() => _handleReturn<Statement>(
clause.children, (child) => child.accept(this)),
semiGlobal: true);
}