2
2
using System . Collections . Generic ;
3
3
using System . Diagnostics . Contracts ;
4
4
using System . Linq ;
5
+ using JetBrains . Annotations ;
5
6
6
7
namespace Microsoft . Dafny ;
7
8
@@ -11,13 +12,13 @@ public abstract class CalcOp {
11
12
/// Resulting operator "x op z" if "x this y" and "y other z".
12
13
/// Returns null if this and other are incompatible.
13
14
/// </summary>
14
- [ Pure ]
15
+ [ System . Diagnostics . Contracts . Pure ]
15
16
public abstract CalcOp ResultOp ( CalcOp other ) ;
16
17
17
18
/// <summary>
18
19
/// Returns an expression "line0 this line1".
19
20
/// </summary>
20
- [ Pure ]
21
+ [ System . Diagnostics . Contracts . Pure ]
21
22
public abstract Expression StepExpr ( Expression line0 , Expression line1 ) ;
22
23
}
23
24
@@ -32,7 +33,7 @@ void ObjectInvariant() {
32
33
/// <summary>
33
34
/// Is op a valid calculation operator?
34
35
/// </summary>
35
- [ Pure ]
36
+ [ System . Diagnostics . Contracts . Pure ]
36
37
public static bool ValidOp ( BinaryExpr . Opcode op ) {
37
38
return
38
39
op == BinaryExpr . Opcode . Eq || op == BinaryExpr . Opcode . Neq
@@ -44,7 +45,7 @@ public static bool ValidOp(BinaryExpr.Opcode op) {
44
45
/// <summary>
45
46
/// Is op a valid operator only for Boolean lines?
46
47
/// </summary>
47
- [ Pure ]
48
+ [ System . Diagnostics . Contracts . Pure ]
48
49
public static bool LogicOp ( BinaryExpr . Opcode op ) {
49
50
return op == BinaryExpr . Opcode . Iff || op == BinaryExpr . Opcode . Imp || op == BinaryExpr . Opcode . Exp ;
50
51
}
@@ -84,7 +85,7 @@ private bool Subsumes(BinaryCalcOp other) {
84
85
public override CalcOp ResultOp ( CalcOp other ) {
85
86
if ( other is BinaryCalcOp ) {
86
87
var o = ( BinaryCalcOp ) other ;
87
- if ( this . Subsumes ( o ) ) {
88
+ if ( Subsumes ( o ) ) {
88
89
return this ;
89
90
} else if ( o . Subsumes ( this ) ) {
90
91
return other ;
@@ -153,6 +154,48 @@ public override string ToString() {
153
154
154
155
}
155
156
157
+ /// <summary>
158
+ /// This method infers a default operator to be used between the steps.
159
+ /// Usually, we'd use == as the default operator. However, if the calculation
160
+ /// begins or ends with a boolean literal, then we can do better by selecting ==>
161
+ /// or <==. Also, if the calculation begins or ends with an empty set, then we can
162
+ /// do better by selecting <= or >=.
163
+ /// Note, these alternative operators are chosen only if they don't clash with something
164
+ /// supplied by the user.
165
+ /// If the rules come up with a good inferred default operator, then that default operator
166
+ /// is returned; otherwise, null is returned.
167
+ /// </summary>
168
+ [ CanBeNull ]
169
+ public CalcOp GetInferredDefaultOp ( ) {
170
+ CalcOp alternativeOp = null ;
171
+ if ( Lines . Count == 0 ) {
172
+ return null ;
173
+ }
174
+
175
+ if ( Expression . IsBoolLiteral ( Lines . First ( ) , out var firstOperatorIsBoolLiteral ) ) {
176
+ alternativeOp = new BinaryCalcOp ( firstOperatorIsBoolLiteral ? BinaryExpr . Opcode . Imp : BinaryExpr . Opcode . Exp ) ;
177
+ } else if ( Expression . IsBoolLiteral ( Lines . Last ( ) , out var lastOperatorIsBoolLiteral ) ) {
178
+ alternativeOp = new BinaryCalcOp ( lastOperatorIsBoolLiteral ? BinaryExpr . Opcode . Exp : BinaryExpr . Opcode . Imp ) ;
179
+ } else if ( Expression . IsEmptySetOrMultiset ( Lines . First ( ) ) ) {
180
+ alternativeOp = new BinaryCalcOp ( BinaryExpr . Opcode . Ge ) ;
181
+ } else if ( Expression . IsEmptySetOrMultiset ( Lines . Last ( ) ) ) {
182
+ alternativeOp = new BinaryCalcOp ( BinaryExpr . Opcode . Le ) ;
183
+ } else {
184
+ return null ;
185
+ }
186
+
187
+ // Check that the alternative operator is compatible with anything supplied by the user.
188
+ var resultOp = alternativeOp ;
189
+ foreach ( var stepOp in StepOps . Where ( stepOp => stepOp != null ) ) {
190
+ resultOp = resultOp . ResultOp ( stepOp ) ;
191
+ if ( resultOp == null ) {
192
+ // no go
193
+ return null ;
194
+ }
195
+ }
196
+ return alternativeOp ;
197
+ }
198
+
156
199
public readonly List < Expression > Lines ; // Last line is dummy, in order to form a proper step with the dangling hint
157
200
public readonly List < BlockStmt > Hints ; // Hints[i] comes after line i; block statement is used as a container for multiple sub-hints
158
201
public readonly CalcOp UserSuppliedOp ; // may be null, if omitted by the user
@@ -189,13 +232,13 @@ public CalcStmt(RangeToken rangeToken, CalcOp userSuppliedOp, List<Expression> l
189
232
Contract . Requires ( cce . NonNullElements ( hints ) ) ;
190
233
Contract . Requires ( hints . Count == Math . Max ( lines . Count - 1 , 0 ) ) ;
191
234
Contract . Requires ( stepOps . Count == hints . Count ) ;
192
- this . UserSuppliedOp = userSuppliedOp ;
193
- this . Lines = lines ;
194
- this . Hints = hints ;
235
+ UserSuppliedOp = userSuppliedOp ;
236
+ Lines = lines ;
237
+ Hints = hints ;
195
238
Steps = new List < Expression > ( ) ;
196
- this . StepOps = stepOps ;
197
- this . Result = null ;
198
- this . Attributes = attrs ;
239
+ StepOps = stepOps ;
240
+ Result = null ;
241
+ Attributes = attrs ;
199
242
}
200
243
201
244
public CalcStmt Clone ( Cloner cloner ) {
@@ -291,7 +334,7 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) {
291
334
var inOrdinal = false ;
292
335
var innerCalcIndent = indentBefore + formatter . SpaceTab ;
293
336
var extraHintIndent = 0 ;
294
- var ownedTokens = this . OwnedTokens ;
337
+ var ownedTokens = OwnedTokens ;
295
338
// First phase: We get the alignment
296
339
foreach ( var token in ownedTokens ) {
297
340
if ( formatter . SetIndentLabelTokens ( token , indentBefore ) ) {
@@ -366,7 +409,7 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) {
366
409
}
367
410
}
368
411
369
- foreach ( var hint in this . Hints ) {
412
+ foreach ( var hint in Hints ) {
370
413
// This block
371
414
if ( hint . Tok . pos != hint . EndToken . pos ) {
372
415
foreach ( var hintStep in hint . Body ) {
@@ -375,7 +418,7 @@ public bool SetIndent(int indentBefore, TokenNewIndentCollector formatter) {
375
418
}
376
419
}
377
420
378
- foreach ( var expression in this . Lines ) {
421
+ foreach ( var expression in Lines ) {
379
422
formatter . SetIndentations ( expression . StartToken , innerCalcIndent , innerCalcIndent , innerCalcIndent ) ;
380
423
}
381
424
0 commit comments