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 : accept(Visitor visitor);
25 : }
26 :
27 : /// A single variable.
28 : class VariableNode implements Node {
29 : final FileSpan span;
30 :
31 : /// The variable name.
32 : final String name;
33 :
34 0 : Iterable<String> get variables => [name];
35 :
36 0 : VariableNode(this.name, [this.span]);
37 :
38 0 : accept(Visitor visitor) => visitor.visitVariable(this);
39 :
40 0 : String toString() => name;
41 :
42 0 : bool operator==(other) => other is VariableNode && name == other.name;
43 :
44 0 : int get hashCode => name.hashCode;
45 : }
46 :
47 : /// A negation expression.
48 : class NotNode implements Node {
49 : final FileSpan span;
50 :
51 : /// The expression being negated.
52 : final Node child;
53 :
54 0 : Iterable<String> get variables => child.variables;
55 :
56 0 : NotNode(this.child, [this.span]);
57 :
58 0 : accept(Visitor visitor) => visitor.visitNot(this);
59 :
60 0 : String toString() => child is VariableNode || child is NotNode
61 0 : ? "!$child"
62 0 : : "!($child)";
63 :
64 0 : bool operator==(other) => other is NotNode && child == other.child;
65 :
66 0 : int get hashCode => ~child.hashCode;
67 : }
68 :
69 : /// An or expression.
70 : class OrNode implements Node {
71 0 : FileSpan get span => _expandSafe(left.span, right.span);
72 :
73 : /// The left-hand branch of the expression.
74 : final Node left;
75 :
76 : /// The right-hand branch of the expression.
77 : final Node right;
78 :
79 : Iterable<String> get variables sync* {
80 0 : yield* left.variables;
81 0 : yield* right.variables;
82 : }
83 :
84 0 : OrNode(this.left, this.right);
85 :
86 0 : accept(Visitor visitor) => visitor.visitOr(this);
87 :
88 : String toString() {
89 0 : var string1 = left is AndNode || left is ConditionalNode
90 0 : ? "($left)"
91 0 : : left;
92 0 : var string2 = right is AndNode || right is ConditionalNode
93 0 : ? "($right)"
94 0 : : right;
95 :
96 0 : return "$string1 || $string2";
97 : }
98 :
99 : bool operator==(other) =>
100 0 : other is OrNode && left == other.left && right == other.right;
101 :
102 0 : int get hashCode => left.hashCode ^ right.hashCode;
103 : }
104 :
105 : /// An and expression.
106 : class AndNode implements Node {
107 0 : FileSpan get span => _expandSafe(left.span, right.span);
108 :
109 : /// The left-hand branch of the expression.
110 : final Node left;
111 :
112 : /// The right-hand branch of the expression.
113 : final Node right;
114 :
115 : Iterable<String> get variables sync* {
116 0 : yield* left.variables;
117 0 : yield* right.variables;
118 : }
119 :
120 0 : AndNode(this.left, this.right);
121 :
122 0 : accept(Visitor visitor) => visitor.visitAnd(this);
123 :
124 : String toString() {
125 0 : var string1 = left is OrNode || left is ConditionalNode
126 0 : ? "($left)"
127 0 : : left;
128 0 : var string2 = right is OrNode || right is ConditionalNode
129 0 : ? "($right)"
130 0 : : right;
131 :
132 0 : return "$string1 && $string2";
133 : }
134 :
135 : bool operator==(other) =>
136 0 : other is AndNode && left == other.left && right == other.right;
137 :
138 0 : int get hashCode => left.hashCode ^ right.hashCode;
139 : }
140 :
141 : /// A ternary conditional expression.
142 : class ConditionalNode implements Node {
143 0 : FileSpan get span => _expandSafe(condition.span, whenFalse.span);
144 :
145 : /// The condition expression to check.
146 : final Node condition;
147 :
148 : /// The branch to run if the condition is true.
149 : final Node whenTrue;
150 :
151 : /// The branch to run if the condition is false.
152 : final Node whenFalse;
153 :
154 : Iterable<String> get variables sync* {
155 0 : yield* condition.variables;
156 0 : yield* whenTrue.variables;
157 0 : yield* whenFalse.variables;
158 : }
159 :
160 0 : ConditionalNode(this.condition, this.whenTrue, this.whenFalse);
161 :
162 0 : accept(Visitor visitor) => visitor.visitConditional(this);
163 :
164 : String toString() {
165 : var conditionString =
166 0 : condition is ConditionalNode ? "($condition)" : condition;
167 0 : var trueString = whenTrue is ConditionalNode ? "($whenTrue)" : whenTrue;
168 0 : return "$conditionString ? $trueString : $whenFalse";
169 : }
170 :
171 : bool operator==(other) =>
172 0 : other is ConditionalNode &&
173 0 : condition == other.condition &&
174 0 : whenTrue == other.whenTrue &&
175 0 : whenFalse == other.whenFalse;
176 :
177 : int get hashCode =>
178 0 : condition.hashCode ^ whenTrue.hashCode ^ whenFalse.hashCode;
179 : }
180 :
181 : /// Like [FileSpan.expand], except if [start] and [end] are `null` or from
182 : /// different files it returns `null` rather than throwing an error.
183 : FileSpan _expandSafe(FileSpan start, FileSpan end) {
184 : if (start == null || end == null) return null;
185 0 : if (start.file != end.file) return null;
186 0 : return start.expand(end);
187 : }
|