Always read and follow the coding conventions in ~/.opencode/AGENTS.md if present.
A Pharo Smalltalk project (not Python) that provides a Famix AST representation for Python code, built on TreeSitter. Part of the moosetechnology/FAST ecosystem.
Source code format: Tonel (all source in src/).
This is a Pharo Smalltalk project. There are no npm/pip/make commands.
Install dependencies in a Pharo image:
Metacello new
githubUser: 'moosetechnology' project: 'FAST-Python' commitish: 'main' path: 'src';
baseline: 'FASTPython';
loadCI command (what actually runs):
smalltalkci -s Pharo64-13 # or Pharo64-14CI is defined in .github/workflows/tests.yml. Tests run via smalltalkci using the smalltalk.ston spec. Coverage is collected in lcov format for FAST-Python.* packages.
There is no local test runner script outside of smalltalkci. You need a Pharo image with the project loaded to run tests interactively.
src/
BaselineOfFASTPython/ -- Metacello baseline (dependency & group definitions)
FAST-Python-Model-Generator/ -- Metamodel generator (Famix code generation)
FAST-Python-Model/ -- Generated model classes (FASTPy*), do NOT edit by hand
FAST-Python-Tools/ -- Importer, CFG, local resolver, SSA, TreeSitter visitor, variable-analysis extensions
FAST-Python-Tools-Tests/ -- All tests (tags: Core = abstract test case, Model = model tests, Analysis = variable-analysis tests)
Baseline groups:
Core= Model + ToolsGenerator= Model-Generator onlyTests= Tools-Tests only (the former Model-Tests package was merged into Tools-Tests)
Dependencies (from baseline):
FASTv3:github://moosetechnology/FAST:v3/src(loads'All'group)TreeSitterv2.0.0:github://Evref-BL/Pharo-Tree-Sitter:v.2.0.0/src
FAST-Python-Model/ contains ~130 generated class files (FASTPy*). These are produced by FASTPythonMetamodelGenerator in FAST-Python-Model-Generator/. Do not edit model files directly -- edit the generator and re-run it. The generator also produces a visitor trait (FASTPyTVisitor).
| Class | Role |
|---|---|
FASTPythonImporter |
Parse Python source string or file → FAST model. Extends TSFASTAbstractImporter. |
FASTPythonTreeSitterVisitor |
Walks TreeSitter CST → builds FAST model. 1100+ lines, the core import logic. |
FASTPythonCFGVisitor |
Builds control flow graph. Uses FASTTCFGUtility trait from FAST. |
FASTPythonLocalResolverVisitor |
Links entity usages to their local declarations (scope-aware). |
FASTPythonSSAVisitor |
SSA transform. Uses FASTCFGTVisitor trait from FAST. Requires local resolution first. |
FASTPythonMetamodelGenerator |
Generates the Famix metamodel. Run via FASTPythonMetamodelGenerator new generate. |
Typical analysis pipeline:
model := FASTPythonImporter parseFile: aFile.
FASTPythonLocalResolverVisitor resolve: model module.
model allFunctionDefinitions first cfg. "CFG"
FASTPythonSSAVisitor resolve: model allFunctionDefinitions first. "SSA (after resolution)"- Python-specific variable APIs (
usedVariables,isResolvedVariable,transitiveAssignedExpressions,transitiveAssignedExpressionsMap,internalAccesses,allNodesUsingMe,statementsUsingMe,allNodesUsingMyVersion,statementsUsingMyVersion,callsOnVariable,callsOnVariableVersion) are single implementations onFASTPyEntityinsrc/FAST-Python-Tools/FASTPyEntity.extension.st(protocol*FAST-Python-Tools). The transitive ones require SSA resolution; the others require local resolution. The*MyVersionand*VariableVersionvariants also require SSA. - Contrast: FAST-level helpers (
versionWriteAccesses,assignedExpressionsMap,versionAccesses, ...) follow a mass-extension convention -- one identical copy per FAST class in protocol*FAST-Core-Tools, living in the FAST dependency repo, not here.
- Base test class:
FASTPythonAbstractTestCase(in Tools-Tests, tagCore) -- provides aparse:helper that wrapsFASTPythonImporterwith error reporting. Themodelinstance variable has no accessor. - Variable-analysis tests:
FASTPythonVariablesAnalysisTestis a slim base providingparseAndResolve:(parse + local resolution + SSA). Four subclasses in tagAnalysis:FASTPythonAssignedExpressionsInVariablesTest,FASTPythonIsResolvedVariablesTest,FASTPythonUsedVariablesTest,FASTPythonInternalAccessesTest. Since each class tests a single concept, test methods use the plaintestsprotocol -- no specialized protocols. - Documentation:
resources/doc/analysis.mddocuments the local resolver, SSA, shadowing and variable-query APIs. Convention: questions are written as bold questions without new TOC entries, and only implemented API is documented. - Importer tests:
FASTPythonImporterTestextendsTSFASTAbstractImporterTest(from TreeSitter). The test file is ~53k lines -- generated tests for every AST node type. Test generation snippet is in the class comment. - CFG/Resolver/SSA tests:
FASTPythonCFGTest,FASTPythonLocalResolverTest,FASTPythonSSATest.
- TreeSitter python grammar version matters: After v0.25, expression statements were removed from the tree. If tests fail unexpectedly, check your
tree-sitter-pythonversion first (see README). - Pharo 13 is the primary target; Pharo 14 is also tested. The baseline references
FAST:v3which requires Moose 13. - The importer uses the master branch of
TSLibrariesPython(hardcoded inFASTPythonTreeSitterVisitor class >> initialize). expression --|> statement: In Python, expressions can be expression statements. This is reflected in the model hierarchy and affects how the visitor processes nodes.- CFG uses
FASTTCFGUtilityfrom FAST (the parent project). SSA usesFASTCFGTVisitor. These traits define the core graph traversal protocol. - Local resolver scope management: The resolver maintains a
scopesstack. Shadowing is handled by checking entity kind consistency (same kind = shared declaration, different kind = new declaration). - Python 3 only for resolver: The local resolver is Python 3 scoped. Python 2 comprehension scoping (no scope) is not supported (issue #26).
- Non-local declarations are not deduplicated: Each access to an unknown field creates a separate
FASTNonLocalDeclaration(issue #26 in the repo). - In-image method installation: the MCP
pharo_method_compilecan be blocked by critiques (e.g.ReDeadBlockRule). Fallback:Behavior>>compile:classified:with method source without the outer[ ]brackets -- with brackets the body parses as an inner block and a silent no-op wrapper method gets installed. Use CR line returns (see global conventions). - The importer cannot parse CR line endings: pass the string through
withPlatformLineEndingsbefore parsing to avoid errors. assertCollection:hasSameElements:is multiplicity-insensitive in current images (#(a a)vs#(a)passes) -- add explicitsizeassertions to catch duplicates.modelivar ofFASTPythonAbstractTestCasehas no accessor -- in DoIt/evaluate code useinstVarNamed: #model.