From 8e21db11d683ebd15f58208c12b82001babc5668 Mon Sep 17 00:00:00 2001 From: Lee Dogeon Date: Mon, 15 Dec 2025 09:57:06 +0900 Subject: [PATCH] Update `architecture/architecture.md` (#6437) * Replace architecture overview image with mermaid graph * Update architecture.md with correct paths and links - Fix file paths to reflect crates/ directory structure - Update GitHub permalink references to current commit - Remove trailing slashes from package names - Use archive.org for broken desosa.nl link - Change master to main branch reference * Update architecture documentation for ruff_python_parser RustPython now uses ruff_python_parser from astral-sh/ruff instead of the separate RustPython/Parser project. Update architecture documentation to reflect this change. --- architecture/architecture.md | 70 ++++++++++++++++++++++++++--------- architecture/overview.png | Bin 35246 -> 0 bytes 2 files changed, 53 insertions(+), 17 deletions(-) delete mode 100644 architecture/overview.png diff --git a/architecture/architecture.md b/architecture/architecture.md index a59b6498b..4b4110ee8 100644 --- a/architecture/architecture.md +++ b/architecture/architecture.md @@ -20,7 +20,43 @@ If, after reading this, you want to contribute to RustPython, take a look at the A high-level overview of the workings of RustPython is visible in the figure below, showing how Python source files are interpreted. -![overview.png](overview.png) +```mermaid +flowchart TB + SourceCode["🐍 Source code"] + + subgraph Interpreter["RustPython Interpreter"] + direction TB + + subgraph Parser["Parser"] + ParserBox["• Tokenize source code
• Validate tokens
• Create AST"] + end + + AST[["AST"]] + + subgraph Compiler["Compiler"] + CompilerBox["• Converts AST to bytecode"] + end + + Bytecode[["Bytecode"]] + + subgraph VM["VM"] + VMBox["• Executes bytecode given input"] + end + + Parser --> AST + AST --> Compiler + Compiler --> Bytecode + Bytecode --> VM + end + + SourceCode -------> Interpreter + + Input[["Input"]] + Output["Code gets executed"] + + Input --> VM + VM --> Output +``` Main architecture of RustPython. @@ -36,9 +72,9 @@ The main entry point of RustPython is located in `src/main.rs` and simply forwar For each of the three components, the entry point is as follows: -- Parser: The Parser is located in a separate project, [RustPython/Parser][10]. See the documentation there for more information. -- Compiler: `compile`, located in [`vm/src/vm/compile.rs`][11], this eventually forwards a call to [`compiler::compile`][12]. -- VM: `run_code_obj`, located in [`vm/src/vm/mod.rs`][13]. This creates a new frame in which the bytecode is executed. +- Parser: The Parser is located in a separate project, [ruff_python_parser][10]. See the documentation there for more information. +- Compiler: `compile`, located in [`crates/vm/src/vm/compile.rs`][11], this eventually forwards a call to [`compiler::compile`][12]. +- VM: `run_code_obj`, located in [`crates/vm/src/vm/mod.rs`][13]. This creates a new frame in which the bytecode is executed. ## Components @@ -46,7 +82,7 @@ Here we give a brief overview of each component and its function. For more detai ### Compiler -This component, implemented as the `rustpython-compiler/` package, is responsible for translating a Python source file into its equivalent bytecode representation. As an example, the following Python file: +This component, implemented as the `rustpython-compiler` package, is responsible for translating a Python source file into its equivalent bytecode representation. As an example, the following Python file: ```python def f(x): @@ -71,11 +107,11 @@ The Parser is the main sub-component of the compiler. All the functionality requ 1. Lexical Analysis 2. Parsing -The functionality for parsing resides in the RustPython/Parser project. See the documentation there for more information. +The functionality for parsing resides in the ruff_python_parser project in the astral-sh/ruff repository. See the documentation there for more information. ### VM -The Virtual Machine (VM) is responsible for executing the bytecode generated by the compiler. It is implemented in the `rustpython-vm/` package. The VM is currently implemented as a stack machine, meaning that it uses a stack to store intermediate results. In the `rustpython-vm/` package, additional sub-components are present, for example: +The Virtual Machine (VM) is responsible for executing the bytecode generated by the compiler. It is implemented in the `rustpython-vm` package. The VM is currently implemented as a stack machine, meaning that it uses a stack to store intermediate results. In the `rustpython-vm` package, additional sub-components are present, for example: - builtins: the built in objects of Python, such as `int` and `str`. - stdlib: parts of the standard library that contains built-in modules needed for the VM to function, such as `sys`. @@ -122,7 +158,7 @@ The RustPython executable/REPL (Read-Eval-Print-Loop) is implemented here, which Some things to note: - The CLI is defined in the [`run` function of `src/lib.rs`][16]. -- The interface and helper for the REPL are defined in this package, but the actual REPL can be found in `vm/src/readline.rs` +- The interface and helper for the REPL are defined in this package, but the actual REPL can be found in `crates/vm/src/readline.rs` ### WASM @@ -133,18 +169,18 @@ Crate for WebAssembly build, which compiles the RustPython package to a format t Integration and snippets that test for additional edge-cases, implementation specific functionality and bug report snippets. [1]: https://github.com/RustPython/RustPython -[2]: https://2021.desosa.nl/projects/rustpython/posts/vision/ +[2]: https://web.archive.org/web/20240723122357/https://2021.desosa.nl/projects/rustpython/posts/vision/ [3]: https://www.youtube.com/watch?v=nJDY9ASuiLc&t=213s [4]: https://rustpython.github.io/2020/04/02/thing-explainer-parser.html [5]: https://rustpython.github.io/demo/ [6]: https://rustpython.github.io/demo/notebook/ -[7]: https://github.com/RustPython/RustPython/blob/master/DEVELOPMENT.md +[7]: https://github.com/RustPython/RustPython/blob/main/DEVELOPMENT.md [8]: https://rustpython.github.io/guideline/2020/04/04/how-to-contribute-by-cpython-unittest.html -[9]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/src/lib.rs#L63 -[10]: https://github.com/RustPython/Parser -[11]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/vm/src/vm/compile.rs#LL10C17-L10C17 -[12]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/vm/src/vm/compile.rs#L26 -[13]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/vm/src/vm/mod.rs#L356 -[14]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/common/src/float_ops.rs +[9]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/src/lib.rs#L72 +[10]: https://github.com/astral-sh/ruff/tree/2bffef59665ce7d2630dfd72ee99846663660db8/crates/ruff_python_parser +[11]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/crates/vm/src/vm/compile.rs#L10 +[12]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/crates/vm/src/vm/compile.rs#L26 +[13]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/crates/vm/src/vm/mod.rs#L433 +[14]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/crates/common/src/float_ops.rs [15]: https://rustpython.github.io/guideline/2020/04/04/how-to-contribute-by-cpython-unittest.html -[16]: https://github.com/RustPython/RustPython/blob/0e24cf48c63ae4ca9f829e88142a987cab3a9966/src/lib.rs#L63 +[16]: https://github.com/RustPython/RustPython/blob/d36a2cffdef2218f3264cef9145a1f781d474ea3/src/lib.rs#L72 diff --git a/architecture/overview.png b/architecture/overview.png deleted file mode 100644 index ce66706f3c83ebe48d4df1a796723a8aa9ffde4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35246 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