Skip to content

Commit f352ca3

Browse files
committed
Fix bugs in the elif management + add tests
1 parent 5e1a151 commit f352ca3

4 files changed

Lines changed: 330 additions & 9 deletions

File tree

src/FAST-Python-Tools-Tests/FASTPythonCFGTest.class.st

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,178 @@ FASTPythonCFGTest >> testFunctionWithIfThenElif [
254254
self assert: nullBlock isFinal
255255
]
256256

257+
{ #category : 'tests - if' }
258+
FASTPythonCFGTest >> testFunctionWithIfThenElifElif [
259+
260+
| thenBlock elifConditionBlock elifBlock elifConditionBlock2 elifBlock2 nullBlock |
261+
self buildCFGFor: 'def f(i):
262+
if i > 3:
263+
print(i)
264+
elif i < 8:
265+
pass
266+
elif i < 10:
267+
a()'.
268+
269+
self assert: startBlock isConditional.
270+
self deny: startBlock isFinal.
271+
self assert: startBlock statements size equals: 1.
272+
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
273+
self assert: startBlock nextBlocks size equals: 2.
274+
self assertEmpty: (startBlock nextBlocks select: #isNullBlock).
275+
276+
thenBlock := startBlock nextTrueBlock.
277+
self deny: thenBlock isConditional.
278+
self deny: thenBlock isFinal.
279+
self assert: thenBlock statements size equals: 1.
280+
self assert: (thenBlock statements first isOfType: FASTPyCall).
281+
282+
elifConditionBlock := startBlock nextFalseBlock.
283+
self assert: elifConditionBlock isConditional.
284+
self deny: elifConditionBlock isFinal.
285+
self assert: elifConditionBlock statements size equals: 1.
286+
self assert: (elifConditionBlock statements first isOfType: FASTPyComparisonOperator).
287+
288+
elifBlock := elifConditionBlock nextTrueBlock.
289+
self deny: elifBlock isConditional.
290+
self deny: elifBlock isFinal.
291+
self assert: elifBlock statements size equals: 1.
292+
self assert: (elifBlock statements first isOfType: FASTPyPassStatement).
293+
294+
elifConditionBlock2 := elifConditionBlock nextFalseBlock.
295+
self assert: elifConditionBlock2 isConditional.
296+
self deny: elifConditionBlock2 isFinal.
297+
self assert: elifConditionBlock2 statements size equals: 1.
298+
self assert: (elifConditionBlock2 statements first isOfType: FASTPyComparisonOperator).
299+
300+
elifBlock2 := elifConditionBlock2 nextTrueBlock.
301+
self deny: elifBlock2 isConditional.
302+
self deny: elifBlock2 isFinal.
303+
self assert: elifBlock2 statements size equals: 1.
304+
self assert: (elifBlock2 statements first isOfType: FASTPyCall).
305+
306+
nullBlock := thenBlock nextBlock.
307+
self assert: nullBlock isNullBlock.
308+
self assert: nullBlock identicalTo: elifBlock nextBlock.
309+
self assert: nullBlock identicalTo: elifConditionBlock2 nextFalseBlock.
310+
self assert: nullBlock identicalTo: elifBlock2 nextBlock.
311+
self assert: nullBlock isFinal
312+
]
313+
314+
{ #category : 'tests - if' }
315+
FASTPythonCFGTest >> testFunctionWithIfThenElifElifElse [
316+
317+
| thenBlock elifConditionBlock elifBlock elifConditionBlock2 elifBlock2 elseBlock nullBlock |
318+
self buildCFGFor: 'def f(i):
319+
if i > 3:
320+
print(i)
321+
elif i < 8:
322+
pass
323+
elif i < 10:
324+
a()
325+
else:
326+
b()'.
327+
328+
self assert: startBlock isConditional.
329+
self deny: startBlock isFinal.
330+
self assert: startBlock statements size equals: 1.
331+
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
332+
self assert: startBlock nextBlocks size equals: 2.
333+
self assertEmpty: (startBlock nextBlocks select: #isNullBlock).
334+
335+
thenBlock := startBlock nextTrueBlock.
336+
self deny: thenBlock isConditional.
337+
self deny: thenBlock isFinal.
338+
self assert: thenBlock statements size equals: 1.
339+
self assert: (thenBlock statements first isOfType: FASTPyCall).
340+
341+
elifConditionBlock := startBlock nextFalseBlock.
342+
self assert: elifConditionBlock isConditional.
343+
self deny: elifConditionBlock isFinal.
344+
self assert: elifConditionBlock statements size equals: 1.
345+
self assert: (elifConditionBlock statements first isOfType: FASTPyComparisonOperator).
346+
347+
elifBlock := elifConditionBlock nextTrueBlock.
348+
self deny: elifBlock isConditional.
349+
self deny: elifBlock isFinal.
350+
self assert: elifBlock statements size equals: 1.
351+
self assert: (elifBlock statements first isOfType: FASTPyPassStatement).
352+
353+
elifConditionBlock2 := elifConditionBlock nextFalseBlock.
354+
self assert: elifConditionBlock2 isConditional.
355+
self deny: elifConditionBlock2 isFinal.
356+
self assert: elifConditionBlock2 statements size equals: 1.
357+
self assert: (elifConditionBlock2 statements first isOfType: FASTPyComparisonOperator).
358+
359+
elifBlock2 := elifConditionBlock2 nextTrueBlock.
360+
self deny: elifBlock2 isConditional.
361+
self deny: elifBlock2 isFinal.
362+
self assert: elifBlock2 statements size equals: 1.
363+
self assert: (elifBlock2 statements first isOfType: FASTPyCall).
364+
365+
elseBlock := elifConditionBlock2 nextFalseBlock.
366+
self deny: elseBlock isConditional.
367+
self deny: elseBlock isFinal.
368+
self assert: elseBlock statements size equals: 1.
369+
self assert: (elseBlock statements first isOfType: FASTPyCall).
370+
371+
nullBlock := thenBlock nextBlock.
372+
self assert: nullBlock isNullBlock.
373+
self assert: nullBlock identicalTo: elifBlock nextBlock.
374+
self assert: nullBlock identicalTo: elifBlock2 nextBlock.
375+
self assert: nullBlock identicalTo: elseBlock nextBlock.
376+
self assert: nullBlock isFinal
377+
]
378+
379+
{ #category : 'tests - if' }
380+
FASTPythonCFGTest >> testFunctionWithIfThenElifElse [
381+
382+
| thenBlock elifConditionBlock elifBlock elseBlock nullBlock |
383+
self buildCFGFor: 'def f(i):
384+
if i > 3:
385+
print(i)
386+
elif i < 8:
387+
pass
388+
else:
389+
a()'.
390+
391+
self assert: startBlock isConditional.
392+
self deny: startBlock isFinal.
393+
self assert: startBlock statements size equals: 1.
394+
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
395+
self assert: startBlock nextBlocks size equals: 2.
396+
self assertEmpty: (startBlock nextBlocks select: #isNullBlock).
397+
398+
thenBlock := startBlock nextTrueBlock.
399+
self deny: thenBlock isConditional.
400+
self deny: thenBlock isFinal.
401+
self assert: thenBlock statements size equals: 1.
402+
self assert: (thenBlock statements first isOfType: FASTPyCall).
403+
404+
elifConditionBlock := startBlock nextFalseBlock.
405+
self assert: elifConditionBlock isConditional.
406+
self deny: elifConditionBlock isFinal.
407+
self assert: elifConditionBlock statements size equals: 1.
408+
self assert: (elifConditionBlock statements first isOfType: FASTPyComparisonOperator).
409+
410+
elifBlock := elifConditionBlock nextTrueBlock.
411+
self deny: elifBlock isConditional.
412+
self deny: elifBlock isFinal.
413+
self assert: elifBlock statements size equals: 1.
414+
self assert: (elifBlock statements first isOfType: FASTPyPassStatement).
415+
416+
elseBlock := elifConditionBlock nextFalseBlock.
417+
self deny: elseBlock isConditional.
418+
self deny: elseBlock isFinal.
419+
self assert: elseBlock statements size equals: 1.
420+
self assert: (elseBlock statements first isOfType: FASTPyCall).
421+
422+
nullBlock := elseBlock nextBlock.
423+
self assert: nullBlock isNullBlock.
424+
self assert: nullBlock identicalTo: thenBlock nextBlock.
425+
self assert: nullBlock identicalTo: elifBlock nextBlock.
426+
self assert: nullBlock isFinal
427+
]
428+
257429
{ #category : 'tests - if' }
258430
FASTPythonCFGTest >> testFunctionWithIfThenElse [
259431

@@ -672,3 +844,126 @@ FASTPythonCFGTest >> testFunctionWithWhileWithBreakInIfThenBreakingAndElse [
672844
self assert: thenBlock nextBlock equals: nullBlock.
673845
self assert: nullBlock isFinal
674846
]
847+
848+
{ #category : 'tests - while' }
849+
FASTPythonCFGTest >> testFunctionWithWhileWithBreakInIfThenBreakingAndElseBreaking [
850+
851+
| ifConditionalBlock thenBlock esleBlock nullBlock |
852+
self buildCFGFor: 'def f(i):
853+
while i < 4:
854+
a()
855+
if i > 2:
856+
b()
857+
break
858+
c()
859+
else:
860+
d()
861+
break
862+
e()'.
863+
864+
self assert: startBlock isConditional.
865+
self deny: startBlock isFinal.
866+
self assert: startBlock statements size equals: 1.
867+
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
868+
self assert: startBlock nextBlocks size equals: 2.
869+
self assert: (startBlock nextBlocks select: #isNullBlock) size equals: 1.
870+
871+
ifConditionalBlock := startBlock nextTrueBlock.
872+
self assert: ifConditionalBlock isConditional.
873+
self deny: ifConditionalBlock isFinal.
874+
self assert: ifConditionalBlock statements size equals: 2.
875+
self assert: (ifConditionalBlock statements first isOfType: FASTPyCall).
876+
self assert: (ifConditionalBlock statements second isOfType: FASTPyComparisonOperator).
877+
878+
thenBlock := ifConditionalBlock nextTrueBlock.
879+
self deny: thenBlock isConditional.
880+
self deny: thenBlock isFinal.
881+
self assert: thenBlock statements size equals: 2.
882+
self assert: (thenBlock statements first isOfType: FASTPyCall).
883+
self assert: (thenBlock statements second isOfType: FASTPyBreakStatement).
884+
885+
esleBlock := ifConditionalBlock nextFalseBlock.
886+
self deny: esleBlock isConditional.
887+
self deny: esleBlock isFinal.
888+
self assert: esleBlock statements size equals: 2.
889+
self assert: (esleBlock statements first isOfType: FASTPyCall).
890+
self assert: (thenBlock statements second isOfType: FASTPyBreakStatement).
891+
892+
nullBlock := startBlock nextFalseBlock.
893+
self assert: nullBlock isNullBlock.
894+
self assert: thenBlock nextBlock equals: nullBlock.
895+
self assert: esleBlock nextBlock equals: nullBlock.
896+
self assert: nullBlock isFinal
897+
]
898+
899+
{ #category : 'tests - while' }
900+
FASTPythonCFGTest >> testFunctionWithWhileWithBreakInIfThenElifAndElseAllBreaking [
901+
902+
| ifConditionalBlock thenBlock elifConditionalBlock elifBlock elseBlock lastBlock |
903+
self buildCFGFor: 'def f(i):
904+
while i < 4:
905+
a()
906+
if i > 2:
907+
b()
908+
break
909+
c()
910+
elif i > 1:
911+
d()
912+
break
913+
e()
914+
else:
915+
f()
916+
break
917+
g()
918+
h()'.
919+
920+
self assert: startBlock isConditional.
921+
self deny: startBlock isFinal.
922+
self assert: startBlock statements size equals: 1.
923+
self assert: (startBlock statements first class isOfType: FASTPyComparisonOperator).
924+
self assert: startBlock nextBlocks size equals: 2.
925+
self assertEmpty: (startBlock nextBlocks select: #isNullBlock).
926+
927+
ifConditionalBlock := startBlock nextTrueBlock.
928+
self assert: ifConditionalBlock isConditional.
929+
self deny: ifConditionalBlock isFinal.
930+
self assert: ifConditionalBlock statements size equals: 2.
931+
self assert: (ifConditionalBlock statements first isOfType: FASTPyCall).
932+
self assert: (ifConditionalBlock statements second isOfType: FASTPyComparisonOperator).
933+
934+
thenBlock := ifConditionalBlock nextTrueBlock.
935+
self deny: thenBlock isConditional.
936+
self deny: thenBlock isFinal.
937+
self assert: thenBlock statements size equals: 2.
938+
self assert: (thenBlock statements first isOfType: FASTPyCall).
939+
self assert: (thenBlock statements second isOfType: FASTPyBreakStatement).
940+
941+
elifConditionalBlock := ifConditionalBlock nextFalseBlock.
942+
self assert: elifConditionalBlock isConditional.
943+
self deny: elifConditionalBlock isFinal.
944+
self assert: elifConditionalBlock statements size equals: 1.
945+
self assert: (elifConditionalBlock statements first isOfType: FASTPyComparisonOperator).
946+
947+
elifBlock := elifConditionalBlock nextTrueBlock.
948+
self deny: elifBlock isConditional.
949+
self deny: elifBlock isFinal.
950+
self assert: elifBlock statements size equals: 2.
951+
self assert: (elifBlock statements first isOfType: FASTPyCall).
952+
self assert: (elifBlock statements second isOfType: FASTPyBreakStatement).
953+
954+
elseBlock := elifConditionalBlock nextFalseBlock.
955+
self deny: elseBlock isConditional.
956+
self deny: elseBlock isFinal.
957+
self assert: elseBlock statements size equals: 2.
958+
self assert: (elseBlock statements first isOfType: FASTPyCall).
959+
self assert: (thenBlock statements second isOfType: FASTPyBreakStatement).
960+
961+
lastBlock := startBlock nextFalseBlock.
962+
self deny: lastBlock isNullBlock.
963+
self assert: lastBlock equals: thenBlock nextBlock.
964+
self assert: lastBlock equals: elifBlock nextBlock.
965+
self assert: lastBlock equals: elseBlock nextBlock.
966+
self assert: lastBlock isFinal.
967+
self assert: lastBlock statements size equals: 1.
968+
self assert: (lastBlock statements first isOfType: FASTPyCall)
969+
]

src/FAST-Python-Tools/FASTCFGConditionalBlock.class.st

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,15 @@ FASTCFGConditionalBlock >> nextTrueBlock [
7272

7373
^ self nextBlocks first
7474
]
75+
76+
{ #category : 'printing' }
77+
FASTCFGConditionalBlock >> printOn: aStrem [
78+
79+
super printOn: aStrem.
80+
81+
self statements ifNotEmpty: [
82+
aStrem
83+
nextPutAll: ' [ ';
84+
nextPutAll: self statements last sourceCode;
85+
nextPutAll: ' ]' ]
86+
]

src/FAST-Python-Tools/FASTPythonCFGVisitor.class.st

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ FASTPythonCFGVisitor >> visitFASTPyConditionalExpression: aConditionalExpression
3939
FASTPythonCFGVisitor >> visitFASTPyElifClause: anElifClause [
4040

4141
self visitFASTTConditionalStatement: anElifClause.
42-
43-
self buildAndUseConditionalDuring: [ self visitFASTTStatementBlock: anElifClause ]
42+
43+
"In the case of the elif, the else clause is in the parent and not here. So we create a conditional that will be automatically popped when we will finalize the containing if."
44+
self buildNewConditional.
45+
self visitFASTTStatementBlock: anElifClause
4446
]
4547

4648
{ #category : 'visiting' }

src/FAST-Python-Tools/FASTTCFGUtility.trait.st

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,16 @@ FASTTCFGUtility >> allBlocks [
3434
FASTTCFGUtility >> buildAndUseConditionalDuring: aBlock [
3535

3636
| conditional |
37-
[
38-
conditional := self buildBlockOfType: FASTCFGConditionalBlock.
39-
currentConditionals push: conditional.
37+
conditional := self buildNewConditional.
4038

41-
aBlock value ] ensure: [
42-
self assert: conditional == currentConditionals top description: 'The top conditional does not match the one produced before the execution of the block'.
43-
^ currentConditionals pop ]
39+
aBlock value.
40+
41+
"Sometimes it is possible that we will need to pop multiple conditionals.
42+
For example, in the case of elif. Usually the elif contains the `then` block but not the `else`. The else will be either the next elif, or next else or next block after the if.
43+
In that case, #buildNewConditional allows to push conditional blocks that I will clause with the containing conditional here. "
44+
[ conditional == currentConditionals top ] whileFalse: [ currentConditionals pop ].
45+
46+
^ currentConditionals pop
4447
]
4548

4649
{ #category : 'running' }
@@ -84,6 +87,15 @@ FASTTCFGUtility >> buildCFGForModel: aFASTModel [
8487
^ startBlock
8588
]
8689

90+
{ #category : 'running' }
91+
FASTTCFGUtility >> buildNewConditional [
92+
93+
| conditional |
94+
conditional := self buildBlockOfType: FASTCFGConditionalBlock.
95+
currentConditionals push: conditional.
96+
^ conditional
97+
]
98+
8799
{ #category : 'accessing' }
88100
FASTTCFGUtility >> currentStatements [
89101
^ currentStatements
@@ -123,5 +135,5 @@ FASTTCFGUtility >> shouldBuildNullBlock [
123135

124136
(self allBlocks select: [ :block | block isConditional and: [ block isFull not ] ]) ifNotEmpty: [ ^ true ].
125137

126-
^ (self allBlocks reject: #isFinal) size = 1
138+
^ (self allBlocks select: #isFinal) size > 1
127139
]

0 commit comments

Comments
 (0)