Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 2 : // for details. All rights reserved. Use of this source code is governed by a 3 : // BSD-style license that can be found in the LICENSE file. 4 : 5 : import 'package:source_span/source_span.dart'; 6 : 7 : import 'visitor.dart'; 8 : 9 : /// The superclass of nodes in the boolean selector abstract syntax tree. 10 : abstract class Node { 11 : /// The span indicating where this node came from. 12 : /// 13 : /// This is a [FileSpan] because the nodes are parsed from a single continuous 14 : /// string, but the string itself isn't actually a file. It might come from a 15 : /// statically-parsed annotation or from a parameter. 16 : /// 17 : /// This may be `null` for nodes without source information. 18 : FileSpan? get span; 19 : 20 : /// All the variables in this node, in the order they appear. 21 : Iterable<String> get variables; 22 : 23 : /// Calls the appropriate [Visitor] method on [this] and returns the result. 24 : T accept<T>(Visitor<T> visitor); 25 : } 26 : 27 : /// A single variable. 28 : class VariableNode implements Node { 29 : @override 30 : final FileSpan? span; 31 : 32 : /// The variable name. 33 : final String name; 34 : 35 0 : @override 36 0 : Iterable<String> get variables => [name]; 37 : 38 5 : VariableNode(this.name, [this.span]); 39 : 40 5 : @override 41 5 : T accept<T>(Visitor<T> visitor) => visitor.visitVariable(this); 42 : 43 5 : @override 44 5 : String toString() => name; 45 : 46 0 : @override 47 0 : bool operator ==(other) => other is VariableNode && name == other.name; 48 : 49 0 : @override 50 0 : int get hashCode => name.hashCode; 51 : } 52 : 53 : /// A negation expression. 54 : class NotNode implements Node { 55 : @override 56 : final FileSpan? span; 57 : 58 : /// The expression being negated. 59 : final Node child; 60 : 61 0 : @override 62 0 : Iterable<String> get variables => child.variables; 63 : 64 0 : NotNode(this.child, [this.span]); 65 : 66 0 : @override 67 0 : T accept<T>(Visitor<T> visitor) => visitor.visitNot(this); 68 : 69 0 : @override 70 : String toString() => 71 0 : child is VariableNode || child is NotNode ? '!$child' : '!($child)'; 72 : 73 0 : @override 74 0 : bool operator ==(other) => other is NotNode && child == other.child; 75 : 76 0 : @override 77 0 : int get hashCode => ~child.hashCode; 78 : } 79 : 80 : /// An or expression. 81 : class OrNode implements Node { 82 0 : @override 83 0 : FileSpan? get span => _expandSafe(left.span, right.span); 84 : 85 : /// The left-hand branch of the expression. 86 : final Node left; 87 : 88 : /// The right-hand branch of the expression. 89 : final Node right; 90 : 91 : @override 92 : Iterable<String> get variables sync* { 93 : yield* left.variables; 94 : yield* right.variables; 95 : } 96 : 97 0 : OrNode(this.left, this.right); 98 : 99 0 : @override 100 0 : T accept<T>(Visitor<T> visitor) => visitor.visitOr(this); 101 : 102 0 : @override 103 : String toString() { 104 0 : var string1 = left is AndNode || left is ConditionalNode ? '($left)' : left; 105 : var string2 = 106 0 : right is AndNode || right is ConditionalNode ? '($right)' : right; 107 : 108 0 : return '$string1 || $string2'; 109 : } 110 : 111 0 : @override 112 : bool operator ==(other) => 113 0 : other is OrNode && left == other.left && right == other.right; 114 : 115 0 : @override 116 0 : int get hashCode => left.hashCode ^ right.hashCode; 117 : } 118 : 119 : /// An and expression. 120 : class AndNode implements Node { 121 0 : @override 122 0 : FileSpan? get span => _expandSafe(left.span, right.span); 123 : 124 : /// The left-hand branch of the expression. 125 : final Node left; 126 : 127 : /// The right-hand branch of the expression. 128 : final Node right; 129 : 130 : @override 131 : Iterable<String> get variables sync* { 132 : yield* left.variables; 133 : yield* right.variables; 134 : } 135 : 136 0 : AndNode(this.left, this.right); 137 : 138 0 : @override 139 0 : T accept<T>(Visitor<T> visitor) => visitor.visitAnd(this); 140 : 141 0 : @override 142 : String toString() { 143 0 : var string1 = left is OrNode || left is ConditionalNode ? '($left)' : left; 144 : var string2 = 145 0 : right is OrNode || right is ConditionalNode ? '($right)' : right; 146 : 147 0 : return '$string1 && $string2'; 148 : } 149 : 150 0 : @override 151 : bool operator ==(other) => 152 0 : other is AndNode && left == other.left && right == other.right; 153 : 154 0 : @override 155 0 : int get hashCode => left.hashCode ^ right.hashCode; 156 : } 157 : 158 : /// A ternary conditional expression. 159 : class ConditionalNode implements Node { 160 0 : @override 161 0 : FileSpan? get span => _expandSafe(condition.span, whenFalse.span); 162 : 163 : /// The condition expression to check. 164 : final Node condition; 165 : 166 : /// The branch to run if the condition is true. 167 : final Node whenTrue; 168 : 169 : /// The branch to run if the condition is false. 170 : final Node whenFalse; 171 : 172 : @override 173 : Iterable<String> get variables sync* { 174 : yield* condition.variables; 175 : yield* whenTrue.variables; 176 : yield* whenFalse.variables; 177 : } 178 : 179 0 : ConditionalNode(this.condition, this.whenTrue, this.whenFalse); 180 : 181 0 : @override 182 0 : T accept<T>(Visitor<T> visitor) => visitor.visitConditional(this); 183 : 184 0 : @override 185 : String toString() { 186 : var conditionString = 187 0 : condition is ConditionalNode ? '($condition)' : condition; 188 0 : var trueString = whenTrue is ConditionalNode ? '($whenTrue)' : whenTrue; 189 0 : return '$conditionString ? $trueString : $whenFalse'; 190 : } 191 : 192 0 : @override 193 : bool operator ==(other) => 194 0 : other is ConditionalNode && 195 0 : condition == other.condition && 196 0 : whenTrue == other.whenTrue && 197 0 : whenFalse == other.whenFalse; 198 : 199 0 : @override 200 : int get hashCode => 201 0 : condition.hashCode ^ whenTrue.hashCode ^ whenFalse.hashCode; 202 : } 203 : 204 : /// Like [FileSpan.expand], except if [start] and [end] are `null` or from 205 : /// different files it returns `null` rather than throwing an error. 206 0 : FileSpan? _expandSafe(FileSpan? start, FileSpan? end) { 207 : if (start == null || end == null) return null; 208 0 : if (start.file != end.file) return null; 209 0 : return start.expand(end); 210 : }