Skip to content

Commit d415445

Browse files
authored
Merge pull request #14 from mainsail-org/master
introduction to the rustpython parser
2 parents 372d9bb + 5826249 commit d415445

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

Diff for: _posts/2020-03-12-thing-explainer-parser.markdown

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
layout: post
3+
title: "Introduction to the RustPython parser"
4+
date: 2020-04-02 11:34:01 -0400
5+
---
6+
7+
This post goes over the RustPython parser. You can see the source code at [RustPython/parser/](https://github.com/RustPython/RustPython/tree/master/parser).
8+
9+
When you write code in python and run it, an interpreter, such as the RustPython interpreter, acts as the translator between you and your machine.
10+
11+
The interpreter has the job of turning your human code into byte code that a python virtual machine can run. Bytecode is an intermediate code between source code and machine code. This makes it portable across multiple hardware and operating systems. Bytecode "works" as long as you implement a virtual machine(vm) that can run it. There is a performance penalty for this flexibility. RustPython has a vm under [RustPython/vm/](https://github.com/RustPython/RustPython/tree/master/vm). Other posts, will go into the details of that vm but now let's figure out how to turn code into bytecode.
12+
13+
14+
## How does bytecode look like
15+
16+
Seeing is believing. To see what bytecode looks like, you can use a Python module called [`dis`](https://docs.python.org/3/library/dis.html). dis stands for disassembler. You can write source code then see how its bytecode looks like. Here is an example:
17+
18+
![bytecode](/assets/media/bytecode.jpg)
19+
20+
21+
## How RustPython turns your code to bytecode
22+
23+
Here are the main steps that RustPython currently does:
24+
- parse the line of source code into tokens
25+
- determine if the tokens have a valid syntax
26+
- create an Abstract Syntax Tree (AST)
27+
- compile the AST into bytecode
28+
29+
This list of steps introduces some new concepts like: tokens and abstract syntax trees. We'll explain and demistify those.
30+
31+
32+
### Step 1: parsing source code into tokens
33+
34+
The fastest way to understand what tokens are, is to see them. Conveniently, Python comes with a [tokenizer](https://docs.python.org/3/library/tokenize.html). Here is what happen if I run the tokenizer on the function that I created earlier.
35+
`$ python -m tokenize file.py`
36+
37+
38+
39+
`file.py` has the function that I used in the previous example.
40+
41+
```
42+
def add(x,y):
43+
return x+y
44+
```
45+
46+
**Tokenize output:**
47+
![tokenzizing](/assets/media/tokenizing.jpg)
48+
49+
50+
A picture IS worth a thousand word 😛 Those are the tokens. They are the basic "units" in the programming language. They are the keywords and operators that you typed. Even new lines and identations count.
51+
52+
If you want to sound fancy:
53+
- The tokens are the basic "lexical components"
54+
- The parsing process is called "Lexical Analysis"
55+
- The thing that does the process is a "lexer"
56+
57+
Here is the link to the RustPython lexer.
58+
59+
**`RustPython/parser/lexer.rs`** >>
60+
[source code](https://github.com/RustPython/RustPython/blob/master/parser/src/lexer.rs)
61+
62+
63+
If you want dive into the details of lexical analysis, check out [Python in a nutshell / Lexical structure](https://learning.oreilly.com/library/view/python-in-a/9781491913833/ch03.html#python_language-id00003)
64+
65+
66+
### Step 2 : determine if the tokens are valid syntax
67+
68+
In the previous step, if you add random stuff to your function and tokenize it, it will work and still tokenize.
69+
70+
![tokenzizing](/assets/media/tokenizing-with-errors.jpg)
71+
72+
So don't hate on the whole interpreter when you get error messages! or at least don't hate on the tokenizer!
73+
74+
To determine if the tokens are valid syntax, first you need a definition of what a valid syntax is. Python has a defined "grammar" or set of rules. The official reference is on [this link](https://docs.python.org/3/reference/grammar.html). There, you will find a machine readable file. You may read a book to know the rules of python, but words are too "fluffy", the machine needs a very strict set of rules encoded in a file. [This video](https://www.youtube.com/watch?v=KGMFvy2d5OI) explains the notation and the Python grammar.
75+
As the presenter puts it, this is the spirit of the beast (python) and it is only ~10KB 😭 (compare that to the size of python books you had to read!)
76+
77+
So, we have the rules or grammar of a programming language in a machine encoded format... now we need to write something that verifies that those rules were followed... This sounds like something that other people could use and like something that should exist as an open source project! 🤔
78+
79+
Sure enough, there is a whole Rust framework called `LALRPOP`. It takes the tokens generated by the lexer, verifies the syntax and turns the tokens into an AST (Abstract Syntax Tree). More information and a tutorial can be found in the [LALRPOP book](https://lalrpop.github.io/lalrpop/README.html).
80+
81+
RustPython does one nice extra thing on top of `LALRPOP `. It masks the errors and provides you with safer, nicer errors. You can see the code for this in `RustPython/parser/src/error.rs`
82+
83+
**Using RustPython to generate an AST**
84+
85+
You can do:
86+
87+
```
88+
use rustpython_parser::{parser, ast};
89+
let python_source = "print('Hello world')";
90+
let python_ast = parser::parse_expression(python_source).unwrap();
91+
```
92+
93+
## Recap 🥴 🥵
94+
95+
As a recap, when you write a line of python code and "run it", here is what happens:
96+
97+
**your code** (in `file.py` or interactive shell)
98+
⭣ parse the line of source code into tokens
99+
⭣ determine if the tokens are valid syntax
100+
⭣ create an Abstract Syntax Tree (AST)
101+
⭣ compile the AST into bytecode
102+
**bytecode** (in `__pycache__/file.pyc` or in memory)
103+
104+
The compiler is under **`RustPython/compiler`** we'll dive into the details in a next post. In the meantime, check out the parser source code in [RustPython/parser/](https://github.com/RustPython/RustPython/tree/master/parser).

0 commit comments

Comments
 (0)