Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
21fc877
Init CFG Builder
jecisc May 12, 2026
01d3b7b
Merge branch 'main' into cfg
jecisc May 18, 2026
b078aea
Rename FASTPythonVisitor into FASTPythonTreeSitterVisitor
jecisc May 18, 2026
a0c74eb
Begin to add basic classes for CFG (Salvaging code from a dead image)
jecisc May 18, 2026
20d782f
Update visitor to be tow down
jecisc May 18, 2026
f018636
Start to implement the CFG
jecisc May 19, 2026
dff4f67
Get the simplest test to pass
jecisc May 19, 2026
2871cd1
Manage returns in CFG
jecisc May 20, 2026
9725a4f
Mark abstract classes as abstract
jecisc May 20, 2026
2b06bd5
Regenerate with new version of the generator
jecisc May 20, 2026
a9b6a77
Regenerate visitor with the bleeding edge Famix generator
jecisc May 21, 2026
201e1ee
Merge basic python visitor in the model package
jecisc May 21, 2026
952bc03
Begin to work on conditions
jecisc May 21, 2026
867bd54
Add a tab in the inspector to visualize a CFG
jecisc May 26, 2026
89f2e2d
CFG: Have the basic if/then case managed
jecisc May 26, 2026
ff77311
Manage if/then when it is not the last statement of the function
jecisc May 26, 2026
e6f5eb6
Implement an alternative CFG algo
jecisc May 27, 2026
b614617
Revert "Implement an alternative CFG algo "
jecisc May 27, 2026
ba5d3af
Build alterative CFG infra
jecisc May 27, 2026
e029cff
Better management of the creation of null nodes
jecisc May 27, 2026
d709300
Manage if in if
jecisc May 27, 2026
1bafbee
Manage elif
jecisc May 27, 2026
2d556ae
Start to manage while
jecisc May 27, 2026
8b541ba
Begin to manage for
jecisc May 27, 2026
2824bd4
Simplify the CFG visitor
jecisc May 28, 2026
7b6b657
Make CFG tests pass
jecisc May 28, 2026
7cd9583
Clean API
jecisc May 28, 2026
bcee95d
Tests for while and for were wrong
jecisc May 28, 2026
c6433e4
API simplification
jecisc May 28, 2026
df75aab
CFG: Fix for and while when they are not the first statement of their…
jecisc May 28, 2026
32db4d4
Begin to manage conditional expressions
jecisc May 28, 2026
68aa011
Begin to manage break statements (WIP)
jecisc May 28, 2026
85a3f03
jecisc Jun 1, 2026
5e1a151
Fix bug where I forgot to change #nextBlocks into #allFollowingBlocks…
jecisc Jun 1, 2026
f352ca3
Fix bugs in the elif management + add tests
jecisc Jun 1, 2026
d6bbe7a
Add a more complex test on while and elif
jecisc Jun 1, 2026
136fe9c
Utilities + add failing tests to fix
jecisc Jun 2, 2026
83f8662
Rewrite CFG algo to work better (WIP)
jecisc Jun 2, 2026
b7e058c
Simplify code after refactoring
jecisc Jun 2, 2026
3695999
Fix the setting of next blocks of breack statements
jecisc Jun 3, 2026
1715203
Cleaning + add new test
jecisc Jun 3, 2026
5e27b4c
Fix bug when we have a loop breaking in a loop
jecisc Jun 3, 2026
7388fb7
Add multiple tests on for loops and breaks
jecisc Jun 3, 2026
54441ce
WIP: Begin to manage switches
jecisc Jun 8, 2026
6e0f5c2
Manage basic switches
jecisc Jun 8, 2026
a9db572
Manage edge cases of match statement
jecisc Jun 8, 2026
0b9b95d
Begin management of continue statements
jecisc Jun 8, 2026
a02adcb
Finish to manage continue in for and while
jecisc Jun 8, 2026
4d97c9b
WIP on Try
jecisc Jun 9, 2026
31b7bf2
Fix bug in FAST model. Except clause should have only one child
jecisc Jun 9, 2026
2dcc8a9
WIP: Manage Try
jecisc Jun 9, 2026
75f2b7b
Add test on multiple excepts in try
jecisc Jun 10, 2026
82bfcb9
Manage else of try catch
jecisc Jun 10, 2026
66d8917
Finish to manage TryCatch in CFG
jecisc Jun 10, 2026
a240289
Ban annoying rule
jecisc Jun 10, 2026
98439d0
Manage else clause of loops
jecisc Jun 10, 2026
1470f48
Fix bug on loops in loops and start to clean the code
jecisc Jun 10, 2026
d65c53b
Add documentation WIP
jecisc Jun 11, 2026
a51e542
Move methods to the right class to be reused
jecisc Jun 11, 2026
cbccd72
Keep working on the documentation
jecisc Jun 11, 2026
e8213c2
Rename exception because I'll use it to manage the retruns also later
jecisc Jun 11, 2026
52efd85
Add a better management of the root of the context stack
jecisc Jun 11, 2026
d23a74f
Refactore the context management to allow the build of CFG from multi…
jecisc Jun 11, 2026
5db99fb
Allow methods, modules, lambdas or class as entry points of the CFG
jecisc Jun 11, 2026
0a62de1
Implement full CFG option
jecisc Jun 11, 2026
b14e3c5
Fix bug when there is code after a return
jecisc Jun 11, 2026
49f4304
Extract generic part of the algo in FAST
jecisc Jun 14, 2026
c6a8445
Merge branch 'main' into alternativeCFG
jecisc Jun 14, 2026
421b063
Add doc in README
jecisc Jun 14, 2026
fde4887
Move methods to FAST
jecisc Jun 14, 2026
c9567e2
Fix obsolete reference
jecisc Jun 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,27 @@ Or
FASTPythonImporter parseFile: myFile
```

# Documentation
## Documentation

The best documentation to read about this project is located in Pharo Tree Sitter's repository here: [https://github.com/Evref-BL/Pharo-Tree-Sitter/blob/main/resources/doc/fast_importer.md](https://github.com/Evref-BL/Pharo-Tree-Sitter/blob/main/resources/doc/fast_importer.md) and here: [https://github.com/Evref-BL/Pharo-Tree-Sitter/blob/main/resources/doc/ts_utilities.md](https://github.com/Evref-BL/Pharo-Tree-Sitter/blob/main/resources/doc/ts_utilities.md)


## Control flow graph

It is possible to get a control flow graph of your python entities like this:

```smalltalk
FASTPythonCFGVisitor buildCFGOf: aModel allFunctionDefinitions first.

"or"

aModel allFunctionDefinitions first cfg
```

A CFG can be done on a function, method, class, module or lambda.

You can visualize it in the inspector as a visualization and you can also export your CFG as a mermaid visualization using `#asMermaidScript`

## Moose versions compatibility

| Version | Compatible Moose versions |
Expand Down
7 changes: 3 additions & 4 deletions src/BaselineOfFASTPython/BaselineOfFASTPython.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ BaselineOfFASTPython >> baseline: spec [
package: 'FAST-Python-Model' with: [ spec requires: #( 'FAST' 'TreeSitter' ) ];
package: 'FAST-Python-Model-Generator';
package: 'FAST-Python-Model-Tests' with: [ spec requires: #( 'FAST-Python-Model' ) ];
package: 'FAST-Python-Tools' with: [ spec requires: #( 'FAST-Python-Model' 'FAST-Python-Visitor' ) ];
package: 'FAST-Python-Tools-Tests' with: [ spec requires: #( 'FAST-Python-Tools' ) ];
package: 'FAST-Python-Visitor' with: [ spec requires: #( 'FAST-Python-Model' ) ].
package: 'FAST-Python-Tools' with: [ spec requires: #( 'FAST-Python-Model' ) ];
package: 'FAST-Python-Tools-Tests' with: [ spec requires: #( 'FAST-Python-Tools' ) ].

"Groups"
spec
group: 'Core' with: #( 'FAST-Python-Model' 'FAST-Python-Tools' 'FAST-Python-Visitor' );
group: 'Core' with: #( 'FAST-Python-Model' 'FAST-Python-Tools' );
group: 'Generator' with: #( 'FAST-Python-Model-Generator' );
group: 'Tests' with: #( 'FAST-Python-Model-Tests' 'FAST-Python-Tools-Tests' ) ]
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ Class {
'tCallable',
'tSliceIndex',
'tAwaitable',
'tExecutable'
'tExecutable',
'tBreakStatement',
'tContinueStatement'
],
#category : 'FAST-Python-Model-Generator',
#package : 'FAST-Python-Model-Generator'
Expand All @@ -173,12 +175,6 @@ FASTPythonMetamodelGenerator class >> packageName [
^ #'FAST-Python-Model'
]

{ #category : 'accessing' }
FASTPythonMetamodelGenerator class >> packageNameForVisitor [

^ #'FAST-Python-Visitor'
]

{ #category : 'accessing' }
FASTPythonMetamodelGenerator class >> prefix [

Expand Down Expand Up @@ -246,7 +242,7 @@ FASTPythonMetamodelGenerator >> defineClasses [

comparisonOperator := builder newClassNamed: #ComparisonOperator.

(comprehension := builder newClassNamed: #Comprehension) comment:
(comprehension := builder newAbstractClassNamed: #Comprehension) comment:
'I am an abstract class for comprehensions. Comprehensions are a structure in python to generate some collections or a generator.'.

(complex := builder newClassNamed: #Complex) comment: 'I represent a complex number such as `2j`'.
Expand Down Expand Up @@ -279,7 +275,7 @@ FASTPythonMetamodelGenerator >> defineClasses [
escapeSequence := builder newClassNamed: #EscapeSequence.
exceptClause := builder newClassNamed: #ExceptClause.
execStatement := builder newClassNamed: #ExecStatement.
expression := builder newClassNamed: #Expression.
expression := builder newAbstractClassNamed: #Expression.
finallyClause := builder newClassNamed: #FinallyClause.
float := builder newClassNamed: #Float.
forInClause := builder newClassNamed: #ForInClause.
Expand All @@ -306,7 +302,7 @@ FASTPythonMetamodelGenerator >> defineClasses [
lambdaParameters := builder newClassNamed: #LambdaParameters.
list := builder newClassNamed: #List.
listComprehension := builder newClassNamed: #ListComprehension.
literal := builder newClassNamed: #Literal.
literal := builder newAbstractClassNamed: #Literal.
matchStatement := builder newClassNamed: #MatchStatement.
memberType := builder newClassNamed: #MemberType.
methodDefinition := builder newClassNamed: #MethodDefinition.
Expand All @@ -331,7 +327,7 @@ FASTPythonMetamodelGenerator >> defineClasses [
'In Python a collection (list or dictionary) splat is the unpacking of a collection in a call or in the definition of another definition like `[ *list , 4 ]` or `func(*list)`'.
splatParameter := builder newClassNamed: #SplatParameter.
splatType := builder newClassNamed: #SplatType.
statement := builder newClassNamed: #Statement.
statement := builder newAbstractClassNamed: #Statement.
string := builder newClassNamed: #String.
subscript := builder newClassNamed: #Subscript.
thenClause := builder newClassNamed: #ThenClause.
Expand Down Expand Up @@ -395,6 +391,7 @@ FASTPythonMetamodelGenerator >> defineHierarchy [
boolean --|> tBooleanLiteral.

breakStatement --|> statement.
breakStatement --|> tBreakStatement.

call --|> expression.
call --|> tInvocation.
Expand Down Expand Up @@ -433,6 +430,7 @@ FASTPythonMetamodelGenerator >> defineHierarchy [
conditionalExpression --|> tExecutable.

continueStatement --|> statement.
continueStatement --|> tContinueStatement.

constrainedType --|> tTypeContent.

Expand Down Expand Up @@ -743,7 +741,7 @@ FASTPythonMetamodelGenerator >> defineRelations [
((deleteStatement property: #expression) comment: 'The expression to apply the delete on')
<>- ((tDeletable property: #parentDeleteStatement) comment: 'The delete statement that own the expression (if it''s the case)').

(exceptClause property: #expressions) <>-* (expression property: #parentExceptClause). "We could point less things than expression, but in reality in Python any expression can be provided. It will just end up in runtime error."
(exceptClause property: #expression) <>- (expression property: #parentExceptClause). "We could point less things than expression, but in reality in Python any expression can be provided. It will just end up in runtime error."

(execStatement property: #code) <>- (tExecutable property: #parentExecStatement).
(execStatement property: #scopes) <>-* (expression property: #parentExecStatementScopes).
Expand Down Expand Up @@ -902,8 +900,10 @@ FASTPythonMetamodelGenerator >> defineTraits [
tAssignment := self remoteTrait: #TAssignment withPrefix: #FAST.
tBinaryExpression := self remoteTrait: #TBinaryExpression withPrefix: #FAST.
tBooleanLiteral := self remoteTrait: #TBooleanLiteral withPrefix: #FAST.
tBreakStatement := self remoteTrait: #TBreakStatement withPrefix: #FAST.
tComment := self remoteTrait: #TComment withPrefix: #FAST.
tConditionalStatement := self remoteTrait: #TConditionalStatement withPrefix: #FAST.
tContinueStatement := self remoteTrait: #TContinueStatement withPrefix: #FAST.
tExpression := self remoteTrait: #TExpression withPrefix: #FAST.
tInvocation := self remoteTrait: #TInvocation withPrefix: #FAST.
tLiteral := self remoteTrait: #TLiteral withPrefix: #FAST.
Expand Down
19 changes: 19 additions & 0 deletions src/FAST-Python-Model/FASTPyBreakStatement.class.st
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
"
I represent a break statement used to break loops.

## Relations
======================

### Parents
| Relation | Origin | Opposite | Type | Comment |
|---|
| `parentLoopStatement` | `FASTTStatement` | `body` | `FASTTLoopStatement` | Optional loop of which this statement is the body|
| `statementContainer` | `FASTTStatement` | `statements` | `FASTTStatementBlock` | Block containing this statement.|


## Properties
======================

| Name | Type | Default value | Comment |
|---|
| `endPos` | `Number` | nil | |
| `startPos` | `Number` | nil | |

"
Class {
#name : 'FASTPyBreakStatement',
#superclass : 'FASTPyStatement',
#traits : 'FASTTBreakStatement',
#classTraits : 'FASTTBreakStatement classTrait',
#category : 'FAST-Python-Model-Entities',
#package : 'FAST-Python-Model',
#tag : 'Entities'
Expand Down
8 changes: 8 additions & 0 deletions src/FAST-Python-Model/FASTPyComprehension.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ FASTPyComprehension class >> annotation [
<FMClass: #Comprehension super: #FASTPyExpression>
<package: #'FAST-Python-Model'>
<generated>
<abstract>
]

{ #category : 'testing' }
FASTPyComprehension class >> isAbstract [

<generated>
^ self == FASTPyComprehension
]

{ #category : 'adding' }
Expand Down
2 changes: 2 additions & 0 deletions src/FAST-Python-Model/FASTPyContinueStatement.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ I represent a continue statement to use in loops.
Class {
#name : 'FASTPyContinueStatement',
#superclass : 'FASTPyStatement',
#traits : 'FASTTContinueStatement',
#classTraits : 'FASTTContinueStatement classTrait',
#category : 'FAST-Python-Model-Entities',
#package : 'FAST-Python-Model',
#tag : 'Entities'
Expand Down
14 changes: 14 additions & 0 deletions src/FAST-Python-Model/FASTPyEntity.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,27 @@ FASTPyEntity >> isBooleanLiteral [
^ false
]

{ #category : 'testing' }
FASTPyEntity >> isBreakStatement [

<generated>
^ false
]

{ #category : 'testing' }
FASTPyEntity >> isClassDefinition [

<generated>
^ false
]

{ #category : 'testing' }
FASTPyEntity >> isContinueStatement [

<generated>
^ false
]

{ #category : 'testing' }
FASTPyEntity >> isExpression [

Expand Down
21 changes: 7 additions & 14 deletions src/FAST-Python-Model/FASTPyExceptClause.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
### Children
| Relation | Origin | Opposite | Type | Comment |
|---|
| `expressions` | `FASTPyExceptClause` | `parentExceptClause` | `FASTPyExpression` | |
| `expression` | `FASTPyExceptClause` | `parentExceptClause` | `FASTPyExpression` | |
| `statements` | `FASTTStatementBlock` | `statementContainer` | `FASTTStatement` | Statements enclosed in this block|


Expand All @@ -34,7 +34,7 @@ Class {
#classTraits : 'FASTTStatementBlock classTrait',
#instVars : [
'#alias => FMProperty',
'#expressions => FMMany type: #FASTPyExpression opposite: #parentExceptClause',
'#expression => FMOne type: #FASTPyExpression opposite: #parentExceptClause',
'#parentTryStatement => FMOne type: #FASTPyTryStatement opposite: #excepts'
],
#category : 'FAST-Python-Model-Entities',
Expand All @@ -50,12 +50,6 @@ FASTPyExceptClause class >> annotation [
<generated>
]

{ #category : 'adding' }
FASTPyExceptClause >> addExpression: anObject [
<generated>
^ self expressions add: anObject
]

{ #category : 'accessing' }
FASTPyExceptClause >> alias [

Expand All @@ -72,19 +66,18 @@ FASTPyExceptClause >> alias: anObject [
]

{ #category : 'accessing' }
FASTPyExceptClause >> expressions [
"Relation named: #expressions type: #FASTPyExpression opposite: #parentExceptClause"
FASTPyExceptClause >> expression [
"Relation named: #expression type: #FASTPyExpression opposite: #parentExceptClause"

<generated>
<derived>
^ expressions
^ expression
]

{ #category : 'accessing' }
FASTPyExceptClause >> expressions: anObject [
FASTPyExceptClause >> expression: anObject [

<generated>
expressions value: anObject
expression := anObject
]

{ #category : 'accessing' }
Expand Down
23 changes: 20 additions & 3 deletions src/FAST-Python-Model/FASTPyExpression.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
| `parentConditionalExpressionElse` | `FASTPyExpression` | `elseExpression` | `FASTPyConditionalExpression` | |
| `parentConditionalExpressionThen` | `FASTPyExpression` | `thenExpression` | `FASTPyConditionalExpression` | |
| `parentDefaultParameterValue` | `FASTPyExpression` | `defaultValue` | `FASTPyParameter` | |
| `parentExceptClause` | `FASTPyExpression` | `expressions` | `FASTPyExceptClause` | |
| `parentExceptClause` | `FASTPyExpression` | `expression` | `FASTPyExceptClause` | |
| `parentExecStatementScopes` | `FASTPyExpression` | `scopes` | `FASTPyExecStatement` | |
| `parentExpression` | `FASTTExpression` | `expression` | `FASTTUnaryExpression` | Parent (unary) expression|
| `parentExpressionLeft` | `FASTTExpression` | `leftOperand` | `FASTTBinaryExpression` | Parent (binary) expression of which I am left side|
Expand Down Expand Up @@ -75,7 +75,7 @@ Class {
'#parentConditionalExpressionElse => FMOne type: #FASTPyConditionalExpression opposite: #elseExpression',
'#parentConditionalExpressionThen => FMOne type: #FASTPyConditionalExpression opposite: #thenExpression',
'#parentDefaultParameterValue => FMOne type: #FASTPyParameter opposite: #defaultValue',
'#parentExceptClause => FMOne type: #FASTPyExceptClause opposite: #expressions',
'#parentExceptClause => FMOne type: #FASTPyExceptClause opposite: #expression',
'#parentExecStatementScopes => FMOne type: #FASTPyExecStatement opposite: #scopes',
'#parentForInClauseLeft => FMOne type: #FASTPyForInClause opposite: #left',
'#parentForInClauseRight => FMOne type: #FASTPyForInClause opposite: #right',
Expand Down Expand Up @@ -106,6 +106,14 @@ FASTPyExpression class >> annotation [
<FMClass: #Expression super: #FASTPyStatement>
<package: #'FAST-Python-Model'>
<generated>
<abstract>
]

{ #category : 'testing' }
FASTPyExpression class >> isAbstract [

<generated>
^ self == FASTPyExpression
]

{ #category : 'accessing' }
Expand All @@ -125,6 +133,14 @@ FASTPyExpression >> collectionInitializer: anObject [
collectionInitializer := anObject
]

{ #category : 'testing' }
FASTPyExpression >> isExpressionStatement [

self containersDo: [ :container | (container isOfType: FASTTStatementBlock) ifTrue: [ ^ true ] ].

^ false
]

{ #category : 'accessing' }
FASTPyExpression >> parentAssertStatement [
"Relation named: #parentAssertStatement type: #FASTPyAssertStatement opposite: #expressions"
Expand Down Expand Up @@ -313,10 +329,11 @@ FASTPyExpression >> parentDefaultParameterValue: anObject [

{ #category : 'accessing' }
FASTPyExpression >> parentExceptClause [
"Relation named: #parentExceptClause type: #FASTPyExceptClause opposite: #expressions"
"Relation named: #parentExceptClause type: #FASTPyExceptClause opposite: #expression"

<generated>
<container>
<derived>
^ parentExceptClause
]

Expand Down
8 changes: 8 additions & 0 deletions src/FAST-Python-Model/FASTPyLiteral.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ FASTPyLiteral class >> annotation [
<FMClass: #Literal super: #FASTPyExpression>
<package: #'FAST-Python-Model'>
<generated>
<abstract>
]

{ #category : 'testing' }
FASTPyLiteral class >> isAbstract [

<generated>
^ self == FASTPyLiteral
]
18 changes: 18 additions & 0 deletions src/FAST-Python-Model/FASTPyModel.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,21 @@ FASTPyModel class >> annotation [
<package: #'FAST-Python-Model'>
<generated>
]

{ #category : 'accessing' }
FASTPyModel >> allClassDefinitions [

^ self allWithType: FASTPyClassDefinition
]

{ #category : 'accessing' }
FASTPyModel >> allFunctionDefinitions [

^ self allWithType: FASTPyFunctionDefinition
]

{ #category : 'accessing' }
FASTPyModel >> allModules [

^ self allWithType: FASTPyModule
]
8 changes: 8 additions & 0 deletions src/FAST-Python-Model/FASTPyStatement.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,12 @@ FASTPyStatement class >> annotation [
<FMClass: #Statement super: #FASTPyEntity>
<package: #'FAST-Python-Model'>
<generated>
<abstract>
]

{ #category : 'testing' }
FASTPyStatement class >> isAbstract [

<generated>
^ self == FASTPyStatement
]
Loading