arXiv:2605.11772v1 [cs.SE] 12 May 2026
Breaking the Dependency Chaos: A Constraint-Driven Python Dependency Resolution Strategy with Selective LLM Imputation Kowshik Chowdhury
Dipayan Banik
Shazibul Islam Shamim
[email protected] Kennesaw State University Marietta, Georgia, USA
[email protected] Danovo Energy Solutions Raleigh, North Carolina, USA
[email protected] Kennesaw State University Marietta, Georgia, USA
Abstract
1
Dependency resolution is the task of selecting package versions that can be installed together without conflicts. It accounts for a significant share of build failures in modern software projects. In the Python ecosystem, this task is especially challenging due to Python 2/3 incompatibilities, deprecated packages, and widespread missing metadata. Recent work, such as PLLM, tackles this problem by using large language models (LLMs) to infer Python and package versions from code and iteratively repairing them based on build errors. We present SMT-LLM, a hybrid system that replaces LLM-only version guessing with formal constraint solving. SMT-LLM uses deterministic import extraction and Python version detection via abstract syntax tree (AST) analysis, the vermin tool to infer minimum Python versions, and a five-tier import-to-package resolver that queries PyPI before any LLM call. We construct a constraint graph from PyPI metadata and LLM-imputed dependencies for packages with missing metadata, then solve for consistent version assignments using a Z3 satisfiability modulo theories (SMT) solver. On the HG2.9K benchmark using Gemma2:9B (10 GB VRAM), SMT-LLM resolves 83.6% of snippets compared to PLLM’s 54.8%, while reducing median resolution time from 151.5 s to 23.9 s (6.3× faster) and average LLM calls from ∼24.9 to 2.26 per snippet (11× reduction).
Dependency conflicts and failed dependency resolution are widespread in the Python package ecosystem, regularly breaking builds and delaying development. A study of 235 real-world cases from PyPI projects shows that even small changes to version constraints can introduce new conflicts and installation failures, making dependency management difficult in practice [13]. Given only a code snippet—often years old and missing a requirements.txt file—a dependency resolver must choose the right packages, version numbers, and Python interpreter. It must also handle Python 2/3 compatibility issues, deprecated packages, and missing package metadata [6]. Existing LLM-based methods, such as PLLM, use iterative prompting to infer these dependencies, but they still fail on nearly half of the HG2.9K benchmark, leaving many snippets unresolved [1, 5]. Key Insight. Our investigation of PLLM’s failures reveals three structural limitations. L1: Initial guess quality. PLLM guesses both the Python version and all module versions in a single prompt with no metadata lookup. Versions are chosen independently, so conflicting pairs like flask==2.0 (requires werkzeug>=2.0) with werkzeug==0.16 go undetected until pip install fails. This also misidentifies the Python version: 17.1% of snippets produce SyntaxErrors from running Python 2 code under Python 3. L2: Repair loop. On failure, PLLM prompts the LLM to replace one module version at a time, ignoring constraint hints pip already provides. If pip reports “requires werkzeug>=2.0”, PLLM discards this and guesses again. This one-at-a-time strategy cannot fix conflicts requiring several packages to change together. L3: Search strategy. PLLM launches three Python versions in parallel but never shares outcomes across them. Python 3.6 may fail five times while 3.8 has already succeeded, yet 3.6 keeps retrying because no earlytermination signal propagates across parallel tracks. Our SMT-LLM approach addresses these limitations by restricting the LLM to verifiable factual queries (e.g., “What are flask== 0.10’s install_requires?”) and delegating everything else to deterministic tools. Specifically, SMT-LLM (i) replaces version guessing with AST-based analysis and a five-tier PyPI resolver (addressing L1), (ii) parses pip errors into formal Z3 [3] constraints instead of blind re-prompting (addressing L2), and (iii) tests Python versions sequentially, stopping at the first success (addressing L3). The contributions are: ❶ A seven-stage hybrid pipeline (Figure 1) combining AST-based static analysis, a five-tier PyPI resolver, LLM-imputed constraint graphs, and Z3 SMT solving with Docker-based validation (§2). ❷ A hard/soft constraint distinction enabling the solver to trust PyPI metadata while treating LLM-imputed dependencies as relaxable (§2, Stage D). ❸ An error-driven constraint refinement loop that parses Docker failures into formal Z3 constraints, strictly narrowing the search space (§2, Stage G). ❹ A Recovery Ladder with
CCS Concepts • Software and its engineering → Empirical software validation; Software configuration management and version control systems; Maintaining software.
Keywords Python dependency resolution, SMT solving, package version conflict, constraint satisfaction, PyPI, LLM-assisted software engineering ACM Reference Format: Kowshik Chowdhury, Dipayan Banik, and Shazibul Islam Shamim. 2026. Breaking the Dependency Chaos: A Constraint-Driven Python Dependency Resolution Strategy with Selective LLM Imputation. In 34th ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (FSE Companion ’26), July 05–09, 2026, Montreal, QC, Canada. ACM, New York, NY, USA, 4 pages. https://doi.org/10.1145/ 3803437.3808241
This work is licensed under a Creative Commons Attribution 4.0 International License. FSE Companion ’26, Montreal, QC, Canada © 2026 Copyright held by the owner/author(s). ACM ISBN 979-8-4007-2636-1/2026/07 https://doi.org/10.1145/3803437.3808241
Introduction
FSE Companion ’26, July 05–09, 2026, Montreal, QC, Canada
A
G
Import Extractor AST + Regex + Alias heuristic (np.->numpy, pd.->pandas, cv2->opencv-python)
B
Import List (e.g. ["flask" "numpy", "Cv2")
Successful
Failure
C
F
Candidate Versions e.g. ["2.7", "3.6", "3.8", "3.9"] Gemma2
Package Mapper
No
FROM python: {version} RUN pip install ...
Tier 1: Regex Patterns Tier 2: LLM Fallback Gemma2
New Constraint
Recovery Ladder
Docker Build + Run Budget Exhausted?
FROM python:{version} RUN pip install ...
Error Classifier
PHASE 2: LLM alias re-resolve (Re-resolve unmapped imports via LLM)
Gemma2
5-tier strategy: Collision Table -> Disk Cache -> PyPI HEAD -> Lookup Name Variants -> LLM Fallback
Constraint Graph Builder PyPI Fetch — get all versions per package Era-Biased Selection — estimate snippet era from PyPI upload times; rank candidates by temporal proximity Metadata Check --HARD: PyPI requires_dist SOFT: LLM imputation when metadata is absent
Yes
Final Fail?
Version Detector Version Analyzer (Vermin) → [py2_min, py3_min] Framework Era Heuristic — flask.ext.* → Python 2 only Shebang Override — python3 shebang → skip Python 2 Py2→3 Conversion — 2to3 -w -n when Python 2-only; recheck with Vermin
D
Kowshik Chowdhury, Dipayan Banik, and Shazibul Islam Shamim
PHASE 3: Python version sweep Sweep all versions (2.7->3.9))
Import -> Package Map (e.g. {cv2: "opencvpython", flask: "flask"})
Gemma2
Pinned Solution {e.g. python: "3.8", flask: "2.3.3", werkzeug: "2.3.7"}
E Constraint Graph flask==2.3.3 → werkzeug>=2.3.3 [HARD] keras==1.2.2 → theano>=0.8 [SOFT]
Loop budget: 7( CLI ) / 5( API )
PHASE 4: Unknown - OtherPass Mark as OtherPass (env limit, not a pip dependency failure))
SMT Solver (Z3) Boolean var per (package, version), ALO + AMO + dependency implications Strategy: Hard + Soft -> relax soft -> greedy
Figure 1: Overview of the SMT-LLM pipeline LLM alias re-resolution, deterministic Python version sweep, and environment-limitation classification (§2, Stage G). ❺ Evaluation on HG2.9K showing an 83.6% resolution rate, with substantially fewer LLM calls per snippet than PLLM (2.26 vs. ∼24.9) (Table 1).
2
The SMT-LLM Pipeline
We retain PLLM’s Docker-based validation backend but replace its LLM-centric pipeline with seven stages of deterministic analysis, Z3 constraint solving, and selective LLM imputation (Gemma-2:9B [12] via Ollama, 10 GB VRAM), as illustrated in Figure 1. Stage A: Import Extraction. SMT-LLM parses Import and ImportFrom nodes via AST, falling back to regex for Python 2 files that fail parsing [16]. Standard-library modules are filtered using sys.stdlib_module_names, a hardcoded Python 2 stdlib set, and runtime importlib.util.find_spec(). Django-style submodule imports (e.g., from X.models import Y) are excluded as projectlocal code. Snippets whose imports map exclusively to non-Linux, platform-specific modules (Sublime Text APIs, macOS Cocoa bindings, Blender internals, winreg, win32api) are labeled OtherPass and bypass Docker entirely. These modules are embedded in their host applications, not distributed via PyPI, so detecting them early prevents unnecessary Docker builds that cannot succeed regardless of the selected version. Stage B: Python Version Detection. We use vermin1 , a static analysis tool that inspects AST node types to infer the minimum compatible Python 2 and Python 3 versions. When vermin cannot determine compatibility, the pipeline falls back to [2.7, 3.6, 3.8, 3.9]. For Python 2-only snippets, the pipeline invokes 2to3 within a python:3.9 Docker container when the local lib2to3 module is unavailable (removed in Python 3.13+). This avoids deprecated Python 2.7 Debian Buster images whose EOL apt mirrors are increasingly unreliable. Stage C: Import-to-Package Mapping. Each import must resolve to its PyPI distribution (e.g., sklearn→scikit-learn). The collision table contains 36 curated mappings from PyPI metadata 1 https://github.com/netromdk/vermin/
and mapping databases (e.g., pipreqs [8]). Tier 3 sends parallel PyPI HEAD requests testing exact-case, lowercase, and capitalized variants. Tier 4 applies nine structural name-variant patterns (e.g., python-{name}, py{name}). The LLM fallback (Tier 5) is accepted only if validated against PyPI. The pipeline persists all non-trivial mappings to disk for reuse in subsequent runs. Stage D: Constraint Graph Construction. For each package, the PyPI JSON API enumerates candidate versions after filtering yanked releases and Python-incompatible builds. Wheel-available versions are sorted first to avoid source-compilation failures on legacy interpreters. The two-pass era-biased selection picks up to eight candidates: the first pass estimates the snippet’s authorship era as the median of per-package midpoint PyPI upload times; the second re-ranks by temporal proximity, keeping the five nearest plus three uniformly sampled from the remainder. This limit keeps Z3’s Boolean variable count in the millisecond regime; the Docker retry loop covers versions outside this window. requires_dist metadata (PEP 508 specifiers, e.g., werkzeug>=2.3.3) produces hard edges. When requires_dist is null, common in pre-2015 packages – the LLM imputes dependencies as a factual recall task (e.g., “What are the direct pip-install dependencies of theano==0.9.0?”), producing soft edges (relaxable on UNSAT). Packages with zero installable versions are replaced before the Docker loop begins. Stage E: SMT Solving. The constraint graph is encoded as a Z3 [3] Boolean satisfiability instance, with a Boolean variable for each (package, version) pair. The constraint classes are asserted: (1) AtLeast-One (ALO): every package in the import list must have at least one version selected, ensuring no dependency is left unresolved; (2) At-Most-One (AMO): each package can have at most one version selected, preventing conflicting installs of the same package. Together, ALO and AMO guarantee exactly one version per package; (3) dependency implications ensure that selecting a version entails selecting a compatible version of each declared dependency, with package names normalized across hyphens, underscores, and dots. The solver first attempts satisfiability with all hard and soft constraints; on UNSAT, it retries with soft constraints relaxed; if
SMT-LLM: Constraint-Driven Python Dependency Resolution
FSE Companion ’26, July 05–09, 2026, Montreal, QC, Canada
still UNSAT, a greedy fallback selects the second-newest version per package. The solver terminates in under one second, yielding a globally consistent pinned environment. Stage F: Docker Validation. The Dockerfile targets --platform=linux/amd64. For EOL Buster images (Python 2.7, 3.6, 3.7), apt sources are redirected to archive.debian.org. Before the first build, solved packages are checked against a curated table of 46 C-extension packages and their Debian build dependencies (e.g., scipy→gfortran+libopenblas-dev); matching apt packages are injected into the Dockerfile, eliminating a wasted first-build failure for known native-code packages. Packages install via BuildKit pip-cache mounts shared across builds. Build and run timeouts are 450 s and 60 s; a clean exit denotes Pass. When validation fails, the pipeline enters the error classification and re-solving stage. Stage G: Error Classification and Re-Solving. Docker failures are classified via an ordered, first-match regex taxonomy with eleven types: VersionNotFound, DependencyConflict, ModuleNotFound, ImportError, SyntaxError, NonZeroCode, AttributeError, SystemLibError, ContainerTimeout, EnvironmentErrorFallback, and ExecutionError; an LLM fallback handles unmatched logs. Each error injects a new constraint; Z3 re-solves and Docker retries for up to five iterations, with a deduplication guard on repeated (python, packages, apt) states. NonZeroCode consults the apt build-dependency table and attempts a binary-variant swap (e.g., psycopg2→psycopg2-binary) before any LLM call. The Recovery Ladder’s version sweep uses a three-iteration budget per candidate.
3
Results
We evaluate SMT-LLM with PLLM, the strongest baseline by fix rate, on 2,891 benchmark gists [1]. Table 1 summarizes the overall performance and Figure 2 covers the 2,483 snippets resolved by at least one tool; the remaining 408 (14.1%) could not be fixed by either tool. The failed snippets are dominated by dead or renamed PyPI packages that neither tool can map to an installable dependency. Table 1: Efficiency comparison: PLLM vs SMT-LLM PLLM
SMT-LLM
Success Rate Median Time (s) P90 Time (s)
54.8% 151.5 491.0
83.6% 23.9 186.6
Version Detection SyntaxError Rate No-LLM Pass First-Build Pass
17.1% 0% ∼8%
0.5% 45.0% 42.5%
Repair Loop LLM Calls / Snippet Docker Iters / Snippet
∼24.9 ∼23.9
2.26 4.9
Search Strategy Single-Version Pass
0%
62.5%
SMT-LLM fixes 2,417 snippets (83.6%), a 28.8 percentage-point improvement over PLLM’s 54.8%. Of these, 1,517 are shared fixes, indicating a common core of easy repairs, while SMT-LLM uniquely resolves 900 snippets compared to only 66 by PLLM. Beyond fix rate, SMT-LLM reduces median resolution time from 151.5 s to 23.9 s (6.3× speedup), driven by three architectural advances. First,
AST-based Python version detection reduces SyntaxError rates from 17.1% to 0.5%; combined with constraint-aware package selection, 42.5% of first Docker builds succeed without retry and 45% of successful resolutions require zero LLM calls. Second, structured error feedback replaces PLLM’s re-prompting: pip error messages are parsed into Z3 constraints, reducing average LLM calls from ∼24.9 to 2.26 and Docker iterations from 23.9 to 4.9 per snippet. Third, sequential version testing with early termination resolves 62.5% of snippets on the first candidate version alone. This eliminates PLLM’s redundant parallel computation, where all three versions run to completion even after one succeeds. LLM stochasticity has limited impact: calls use temperature 0.1 with local caching, 45% of resolutions require zero LLM calls, and the primary nondeterminism source is Z3, not the LLM (Section 4). Key Result. Compared to PLLM, SMT-LLM improves the fix rate from 54.8% to 83.6%, reduces median resolution time by 6.3×, reduces LLM calls by 11×, and requires 5× fewer Docker iterations, with 45% of successful resolutions without any LLM calls. PLLM
66 (3%)
SMT-LLM
1517 (61%)
900 (36%)
Figure 2: Overlap of successfully fixed snippets: PLLM vs SMT-LLM
4
Limitations and Root-Cause Analysis
Table 2 summarizes the 474 failures by root cause. Missing modules dominate, from platform-embedded SDKs (Pythonista, IDA Pro, Rhino3D, Maya) and project-local imports absent from PyPI. Build /wheel failures trace largely to Python 2-only bindings lacking Python 3 releases (pygtk alone accounts for 64% of VersionNotFound cases). Import and attribute errors reflect restructured internals (e.g., App Engine NDB migration, removed IPython and ggplot APIs). The remaining cases comprise Python 2 syntax (print statements, ur” prefixes) resisting 2to3, and Blender bpy scripts requiring an embedded runtime unreachable through pip. Additionally, both PLLM and SMT-LLM exhibit non-deterministic behavior in version selection: PLLM because the LLM’s predictions vary across invocations, and SMT-LLM because the Z3 solver may return different valid assignments when multiple satisfying solutions exist.
5
Threats to Validity
Local-module ambiguity. Static import extraction cannot distinguish project-local modules from genuine PyPI packages. When a gist snippet is extracted from a larger project, imports such as from protobuf import IpcConnectionContext_pb2 (gist 5441636—a local directory of generated Protocol Buffer files), import settings (gist 4413028—a Django project settings file) are all treated as thirdparty dependencies. SMT-LLM exhausts its replacement heuristics before dropping the unresolvable import, wasting Docker iterations. We partially mitigate this by filtering identity-mismatch drops from
FSE Companion ’26, July 05–09, 2026, Montreal, QC, Canada
Kowshik Chowdhury, Dipayan Banik, and Shazibul Islam Shamim
Table 2: Root-cause breakdown of 474 SMT-LLM failures Category
Root Cause
Missing modules Import errors Version not found Non-zero exit Attribute errors Syntax errors System libraries
Platform SDKs, local imports Deprecated or renamed internals Needed version not on PyPI Runtime failure after install API changes across versions Python 2 syntax resists 2to3 Missing OS libs (GTK, RPi)
N (%) 276 (58.2) 65 (13.7) 36 (7.6) 34 (7.2) 34 (7.2) 15 (3.2) 14 (3.0)
the final module list, but local modules that fail to build remain indistinguishable from genuinely deprecated packages. PyPI temporal drift. The HG2.9K gists were authored between 2011 and 2019, but the constraint graph queries today’s PyPI index. Packages have since been yanked, renamed, or stripped of older wheels, creating a mismatch between what snippets originally required and what remains available. Despite era-biased version selection, this accounts for 70 of 474 failures (14.8%): VersionNotFound (36) when no compatible wheel exists for the target Python, and NonZeroCode (34) when source distributions fail to compile against modern system libraries. Pinning to a historical PyPI snapshot could reduce this gap but is beyond our current scope. Cache accumulation. The persistent mapping cache accelerates resolution by reusing previously verified import-to-package mappings across runs. However, an incorrect LLM-generated mapping (e.g., block_diag → blockdiag instead of recognizing it as scipy.linalg.block_diag) becomes a cached false mapping that silently affects all subsequent snippets sharing that import. We address this through periodic manual audits of the cache, but systematic validation against PyPI metadata remains future work.
6
Related Work
Python dependency resolution has attracted growing attention as the ecosystem’s package volume and version-conflict frequency continue to rise [7]. Horton and Parnin [4] introduced the HG2.9K benchmark of GitHub gists, exposing the brittleness of standard tools such as pip and pipreqs, which rely on greedy backtracking and lack cross-package global reasoning. In the broader software engineering landscape, SAT and SMT solvers have long underpinned package managers for other ecosystems; Debian’s apt models upgrades as a pseudo-Boolean optimization problem [11], and Eclipse’s p2 provisioning uses SAT to resolve OSGi bundles [9], yet these techniques have seen limited adoption in Python’s loosely specified, metadata-sparse packaging ecosystem. Concurrently, LLMs have demonstrated effectiveness in code repair [14], test generation [10], and fault localization [15], but their use as constraint generators rather than end-to-end solvers remains underexplored. SMT-LLM bridges this gap by using the LLM only to fill metadata gaps and classify errors, while delegating version selection to Z3.
7
Conclusion
SMT-LLM resolves 2,417 of 2,891 HG2.9K snippets (83.6%), combining Z3 constraint solving with selective LLM imputation to replace PLLM’s iterative guess-and-check loop. The 474 remaining failures stem from platform-specific SDKs (Sublime Text, IDA Pro, Blender), project-local modules absent from PyPI, and legacy C-extension builds whose binary wheels no longer exist; the practical ceiling for container-based resolution against a live package index. This
represents a 52.6% relative improvement over PLLM’s 54.8% while requiring 11× fewer LLM calls and 5× fewer Docker iterations per snippet. These results suggest that when a problem splits into factual lookup and combinatorial search, restricting the LLM to verifiable queries and delegating version selection to a formal solver like Z3 is faster, cheaper, and more reproducible than end-to-end neural reasoning. Future work. We plan to address the Z3 solver non-determinism through deterministic pinning strategies, integrate historical PyPI snapshots to reduce temporal-drift failures, and develop a classifier to distinguish project-internal imports from third-party dependencies before resolution begins.
8
Data Availability
To support reproducibility, the SMT-LLM implementation, Docker runner, and all evaluation artifacts are publicly available [2].
References [1] Antony Bartlett, Cynthia Liem, and Annibale Panichella. 2025. The Last Dependency Crusade: Solving Python Dependency Conflicts with LLMs. 2025 40th IEEE/ACM International Conference on Automated Software Engineering Workshops (ASEW) (2025), 66–73. https://api.semanticscholar.org/CorpusID:275921543 [2] Kowshik Chowdhury. 2026. A hybrid SMT + selective-LLM pipeline for Python dependency resolution (For FSE-AIWare ’26). https://github.com/Kowshik-18/ SMT-LLM. [Online; accessed 10-March-2026]. [3] Leonardo De Moura and Nikolaj Bjørner. 2008. Z3: An efficient SMT solver. In International conference on Tools and Algorithms for the Construction and Analysis of Systems. Springer, 337–340. [4] Eric Horton and Chris Parnin. 2018. Gistable: Evaluating the Executability of Python Code Snippets on GitHub. In Proc. IEEE International Conference on Software Maintenance and Evolution (ICSME). doi:10.1109/ICSME.2018.00029 [5] Eric Horton and Chris Parnin. 2019. Dockerizeme: Automatic inference of environment dependencies for python code snippets. In 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE). IEEE, 328–338. [6] Xinyu Jia, Yu Zhou, Yasir Hussain, and Wenhua Yang. 2024. An empirical study on Python library dependency and conflict issues. In 2024 IEEE 24th International Conference on Software Quality, Reliability and Security (QRS). IEEE, 504–515. [7] Zhijie Jia et al. 2024. A Survey on Python Dependency Conflicts. IEEE Transactions on Software Engineering (2024). doi:10.1109/TSE.2024.3458529 [8] V. Kravcenko. 2023. pipreqs: Generate pip requirements.txt based on imports. https://github.com/bndr/pipreqs. [9] Daniel Le Berre and Pascal Rapicault. 2009. Dependency Management for the Eclipse Ecosystem. In Proc. International Workshop on Open Component Ecosystems (IWOCE). doi:10.1145/1595800.1595803 [10] Caroline Lemieux, Jeevana Priya Inala, Shuvendu K. Lahiri, and Siddhartha Sen. 2023. CodaMosa: Escaping Coverage Plateaus in Test Generation with PreTrained Large Language Models. In Proc. IEEE/ACM International Conference on Software Engineering (ICSE). doi:10.1109/ICSE48619.2023.00085 [11] Fabio Mancinelli, Jaap Boender, Roberto Di Cosmo, and Jerome Vouillon. 2006. Managing the Complexity of Large Free and Open Source Package-Based Software Distributions. In Proc. IEEE/ACM International Conference on Automated Software Engineering (ASE). doi:10.1109/ASE.2006.49 [12] Ollama. 2026. Gemma 2 9B. https://ollama.com/library/gemma2:9b. [Online; accessed 05-March-2026]. [13] Ying Wang, Ming Wen, Yepang Liu, Yibo Wang, Zhenming Li, Chao Wang, Hai Yu, Shing-Chi Cheung, Chang Xu, and Zhiliang Zhu. 2020. Watchman: Monitoring dependency conflicts for python library ecosystem. In Proceedings of the ACM/IEEE 42nd international conference on software engineering. 125–135. [14] Chunqiu Steven Xia, Yuxiang Wei, and Lingming Zhang. 2023. Automated Program Repair in the Era of Large Pre-Trained Language Models. Proc. IEEE/ACM International Conference on Software Engineering (ICSE) (2023). doi:10.1109/ ICSE48619.2023.00129 [15] Aidan Z.H. Yang, Claire Le Goues, Ruben Martins, and Vincent J. Hellendoorn. 2024. Large Language Models for Test-Free Fault Localization. In Proc. IEEE/ACM International Conference on Software Engineering (ICSE). doi:10.1145/3597503. 3623342 [16] Min Yi. 2026. Abstract Syntax Tree (AST) Deep Dive: From Theory to Practical Compiler Implementation. https://dev.to/min_yi_e5fbf986e24f1c42df/ abstract-syntax-tree-ast-deep-dive-from-theory-to-practical-compilerimplementation-4jpo. [Online; accessed 05-March-2026].