19. Phân tích cú pháp
19. Phân tích cú pháp
Phân tích cú pháp là giai đoạn biến luồng mã thông báo thành cấu trúc cú pháp.
Trình mã thông báo nhận dạng các đơn vị từ vựng. Trình phân tích cú pháp nhận ra cấu trúc ngữ pháp. Nó quyết định xem một chuỗi mã thông báo có phải là chương trình Python hợp lệ hay không và khi nó hợp lệ sẽ xây dựng một cây cú pháp trừu tượng.
Đối với nguồn này:python def add(a, b): return a + b trình mã thông báo tạo ra các mã thông báo như:text NAME "def" NAME "add" LPAR "(" NAME "a" COMMA "," NAME "b" RPAR ")" COLON ":" NEWLINE "\n" INDENT " " NAME "return" NAME "a" PLUS "+" NAME "b" NEWLINE "\n" DEDENT "" ENDMARKER "" Trình phân tích cú pháp đưa ra cấu trúc mã thông báo đó:text Module FunctionDef name="add" arguments arg "a" arg "b" body Return BinOp Name "a" Add Name "b" Sự khác biệt là quan trọng. Token hóa biết điều đódef, add, (, a, Và:hiện hữu. Phân tích cú pháp biết rằng chúng cùng nhau tạo thành một định nghĩa hàm.
19.1 Vị trí trong Đường dẫn biên dịch
Phân tích cú pháp nằm giữa mã thông báo và biên dịch ngữ nghĩa.```text source bytes ↓ encoding detection ↓ tokenization ↓ parsing ↓ abstract syntax tree ↓ symbol table ↓ compiler ↓ code object ↓ bytecode execution
Trách nhiệm chính của nó là:```text
recognize valid Python grammar
reject invalid syntax
group tokens into statements and expressions
apply operator precedence
handle indentation-based blocks
build AST nodes
produce useful syntax errors
```Tài liệu tham khảo ngôn ngữ Python hiển thị toàn bộ ngữ pháp được CPython sử dụng, bắt nguồn từ`Grammar/python.gram`, đồng thời bỏ qua các chi tiết triển khai liên quan đến việc tạo mã và khôi phục lỗi. ([Tài liệu Python][1])
## 19.2 CPython sử dụng trình phân tích cú pháp PEG
CPython hiện đại sử dụng trình phân tích cú pháp PEG.
PEG có nghĩa là Phân tích ngữ pháp biểu thức. CPython đã chuyển từ trình phân tích cú pháp LL(1) cũ hơn sang trình phân tích cú pháp PEG trong Python 3.9. Trình phân tích cú pháp PEG cho phép diễn đạt trực tiếp hơn ngữ pháp của Python và giúp CPython linh hoạt hơn khi thêm cú pháp.
Trình phân tích cú pháp PEG hoạt động bằng cách thử các lựa chọn thay thế ngữ pháp theo thứ tự. Phương án thay thế đầu tiên thành công sẽ được chọn.
Một quy tắc đơn giản hóa có thể trông giống như:```text
statement:
| function_def
| class_def
| if_stmt
| while_stmt
| simple_stmt
```Với các mã thông báo đầu vào, trình phân tích cú pháp sẽ cố gắng khớp một câu lệnh. Nếu phương án đầu tiên thất bại, nó sẽ thử phương án tiếp theo, v.v.
Sự lựa chọn có thứ tự này khác với một số hệ thống ngữ pháp phi ngữ cảnh trong đó các lựa chọn thay thế không có thứ tự. Trong PEG, trật tự rất quan trọng.
##19.3 Quy tắc ngữ pháp
Ngữ pháp xác định chuỗi mã thông báo hợp lệ.
Một quy tắc định nghĩa hàm đơn giản có thể được hình dung như sau:```text
function_def:
"def" NAME "(" parameters? ")" ":" block
```Điều này có nghĩa là:```text
the keyword def
then a function name
then an opening parenthesis
then optional parameters
then a closing parenthesis
then a colon
then a block
```Ngữ pháp CPython thực chi tiết hơn vì nó hỗ trợ trình trang trí, hàm không đồng bộ, chú thích, tham số chỉ vị trí, tham số chỉ từ khóa, giá trị mặc định, tham số loại, chú thích trả về và khôi phục lỗi.
Tuy nhiên, mô hình cơ bản vẫn là:```text
tokens are matched against grammar rules
rules call other rules
successful rules produce AST structure
failed rules produce backtracking or syntax errors
```## 19.4 Câu lệnh và biểu thức
Cú pháp Python có các câu lệnh và biểu thức.
Các câu lệnh thực hiện các hành động:```python
x = 1
return x
if x:
print(x)
```Biểu thức tạo ra các giá trị:```python
x + 1
f(a, b)
items[0]
lambda x: x + 1
```Trình phân tích cú pháp phải phân biệt chúng vì không phải mọi biểu thức đều được phép ở mọi nơi và không phải mọi câu lệnh đều có thể xuất hiện bên trong một biểu thức.
Có hiệu lực:```python
if ready:
run()
```Không hợp lệ:```python
x = if ready: run()
```Có hiệu lực:```python
value = a if ready else b
```Ngữ pháp mã hóa những khác biệt này.`if`như một tuyên bố thuộc về một khu vực ngữ pháp. Biểu thức điều kiện thuộc về người khác.
## 19,5 Block và Suites
Các khối Python được biểu diễn bởi`INDENT`Và`DEDENT`mã thông báo.
Trình phân tích cú pháp không đếm khoảng trắng một cách trực tiếp. Trình mã thông báo đã chuyển đổi thụt lề thành mã thông báo cấu trúc.
Ví dụ:```python
if x:
a = 1
b = 2
c = 3
```Hình dạng mã thông báo:```text
NAME "if"
NAME "x"
COLON ":"
NEWLINE
INDENT
NAME "a"
EQUAL
NUMBER "1"
NEWLINE
NAME "b"
EQUAL
NUMBER "2"
NEWLINE
DEDENT
NAME "c"
EQUAL
NUMBER "3"
NEWLINE
ENDMARKER
```Trình phân tích cú pháp xem khối này như một bộ:```text
If
test: Name "x"
body:
Assign a = 1
Assign b = 2
Module body:
Assign c = 3
```Thiết kế này giúp việc xử lý thụt đầu dòng nằm ngoài hầu hết các quy tắc ngữ pháp. Ngữ pháp có thể nói là “chặn” và dựa vào mã thông báo được tạo ra`INDENT`Và`DEDENT`mã thông báo.
## 19.6 Phân tích biểu thức và mức độ ưu tiên
Biểu thức phân tích cú pháp yêu cầu quyền ưu tiên.
Coi như:```python
x = 1 + 2 * 3
```Trình phân tích cú pháp phải tạo ra:```text
1 + (2 * 3)
```không:```text
(1 + 2) * 3
```Hình dạng AST là:```text
Assign
target: Name "x"
value:
BinOp Add
left: Constant 1
right:
BinOp Mult
left: Constant 2
right: Constant 3
```Các quy tắc ngữ pháp mã hóa mức độ ưu tiên bằng cách xếp lớp các biểu thức.
Một mô hình đơn giản hóa:```text
expression
conditional expression
sum
term
sum "+" term
sum "-" term
term
factor
term "*" factor
term "/" factor
factor
atom
"-" factor
"+" factor
atom
NAME
NUMBER
STRING
"(" expression ")"
```Các cấu trúc có mức độ ưu tiên cao hơn xuất hiện sâu hơn trong ngữ pháp. Phép nhân liên kết chặt chẽ hơn phép cộng vì các quy tắc cộng sử dụng các biểu thức cấp nhân làm toán hạng.
## 19.7 Tính liên kết
Quyền ưu tiên quyết định toán tử nào liên kết chặt chẽ hơn. Tính kết hợp quyết định cách thức các toán tử của cùng một nhóm ưu tiên.
Ví dụ:```python
x = a - b - c
```Điều này phân tích như sau:```text
(a - b) - c
```không:```text
a - (b - c)
```Phép trừ là liên kết trái.
Nhưng lũy thừa hoạt động khác nhau:```python
x = a ** b ** c
```Điều này phân tích như sau:```text
a ** (b ** c)
```Ngữ pháp phải mã hóa điều này một cách rõ ràng. Tính kết hợp không phải là thuộc tính thời gian chạy. Nó được cố định bằng cấu trúc phân tích trước khi bắt đầu đánh giá.
## 19.8 Lệnh gọi, thuộc tính và đăng ký
Python có các dạng biểu thức hậu tố:```python
obj.attr
obj.method(x)
items[i]
items[i:j]
f(x)(y).z
```Những chuỗi này chặt chẽ.
Ví dụ:```python
result = client.session.get(url).json()["items"][0]
```Trình phân tích cú pháp nhóm nhóm này thành một chuỗi các thao tác trên biểu thức chính:```text
Name "client"
Attribute "session"
Attribute "get"
Call args=[Name "url"]
Attribute "json"
Call args=[]
Subscript "items"
Subscript 0
```Đây là lý do tại sao các lệnh gọi, thuộc tính và chỉ số dưới liên kết chặt chẽ hơn các toán tử số học:```python
x = f(a) + b.c
```Các cuộc gọi và thuộc tính được phân tích cú pháp trước khi biểu thức bổ sung được hình thành.
## 19.9 Phân tích bài tập
Cú pháp gán bị hạn chế hơn cú pháp biểu thức.
Các mục tiêu phân công hợp lệ bao gồm:```python
x = 1
obj.attr = 1
items[0] = 1
a, b = pair
```Các mục tiêu chuyển nhượng không hợp lệ bao gồm:```python
1 = x
a + b = x
f() = x
```Trình phân tích cú pháp phải nhận ra rằng vế trái của phép gán không chỉ là bất kỳ biểu thức nào. Nó phải là một mục tiêu hợp lệ.
Điều này cũng ảnh hưởng đến bài tập tăng cường:```python
x += 1
obj.value *= 2
items[i] -= 3
```và bài tập có chú thích:```python
x: int = 1
name: str
```Trình phân tích cú pháp và trình tạo AST của CPython phải duy trì sự khác biệt giữa biểu thức được sử dụng để đọc và mục tiêu được sử dụng để viết.
Ở cấp độ AST, tên mang ngữ cảnh:```text
Name id="x", ctx=Load
Name id="x", ctx=Store
Name id="x", ctx=Del
```Ví dụ:```python
x = x + 1
```bên trái`x`là`Store`. Bên phải`x`là`Load`.
## 19.10 Sự mơ hồ và lựa chọn có trật tự
Phân tích cú pháp PEG sử dụng các lựa chọn thay thế được sắp xếp.
Hãy xem xét cú pháp trong đó nhiều lựa chọn ngữ pháp bắt đầu tương tự nhau. Trình phân tích cú pháp sẽ thử một giải pháp thay thế trước tiên. Nếu thành công, kết quả đó sẽ được sử dụng. Nếu thất bại, các lựa chọn thay thế sau này có thể được thử.
Điều này mang lại cho các tác giả ngữ pháp một cách có kiểm soát để giải quyết sự mơ hồ.
Ví dụ đơn giản:```text
small_stmt:
| assignment
| expression
```Phương án gán phải được xem xét trước phương án thay thế biểu thức đơn giản, bởi vì một phép gán thường bắt đầu bằng một mục tiêu giống như biểu thức.
đầu vào:```python
x = 1
```Tiền tố`x`có thể bắt đầu một biểu thức, nhưng câu lệnh đầy đủ là phép gán. Ngữ pháp phải được sắp xếp và cấu trúc sao cho bài tập sẽ thành công.
PEG cũng hỗ trợ toán tử “cắt” trong ký hiệu ngữ pháp CPython. Tài liệu tham khảo ngôn ngữ ghi chú`~`điểm đánh dấu trong ngữ pháp dưới dạng một phần cắt cam kết với giải pháp thay thế hiện tại và không thực hiện được quy tắc nếu giải pháp thay thế đó không thành công. ([Tài liệu Python][1])
Việc cắt giảm rất hữu ích cho hiệu suất và chẩn đoán. Sau khi trình phân tích cú pháp đã thấy đủ mã thông báo để biết cấu trúc nào đang phân tích cú pháp, nó có thể ngừng xem xét các lựa chọn thay thế không liên quan.
## 19.11 Nhìn về phía trước
Người phân tích cú pháp thường cần kiểm tra các mã thông báo sắp tới trước khi quyết định phân tích cái gì.
Lookahead hỏi: “Mã thông báo tiếp theo hoặc chuỗi mã thông báo tiếp theo có khớp với mẫu này không?”
Ví dụ:```python
x: int
```Điều này có thể bắt đầu một bài tập có chú thích.
Ví dụ:```python
x + y
```Đây là một tuyên bố biểu hiện.
Cả hai đều bắt đầu bằng`NAME`. Trình phân tích cú pháp cần nhiều ngữ cảnh hơn mã thông báo đầu tiên.
Lookahead cũng giúp loại bỏ cú pháp sớm và chọn thông báo lỗi tốt hơn.
Một mô hình nhìn trước đơn giản hóa:```text
if next token is NAME and following token is ":":
parse annotated assignment
else:
parse expression statement
```Ngữ pháp CPython thực sự sử dụng các cơ chế phong phú hơn, nhưng mô hình tinh thần vẫn giống nhau: một số quyết định yêu cầu xem xét trước mà không tiêu thụ mã thông báo vĩnh viễn.
## 19.12 Quay lại
Trình phân tích cú pháp PEG có thể quay lại.
Quay lại có nghĩa là trình phân tích cú pháp thử một đường dẫn ngữ pháp, không thành công, khôi phục vị trí mã thông báo trước đó và thử một đường dẫn khác.
Mô hình ví dụ:```text
try parse async function definition
if it fails:
restore token position
try parse normal function definition
if it fails:
restore token position
try parse simple statement
```Việc quay lui làm cho việc biên soạn ngữ pháp trở nên linh hoạt hơn so với trình phân tích cú pháp xem trước một mã thông báo nghiêm ngặt.
Nhưng việc quay lại không hạn chế có thể tốn kém. Trình phân tích cú pháp của CPython sử dụng cấu trúc ngữ pháp, các đoạn cắt, ghi nhớ và các quy tắc được nhắm mục tiêu để duy trì hiệu quả phân tích cú pháp.
## 19.13 AST Xây dựng
Phân tích cú pháp trong CPython không chỉ chấp nhận hoặc từ chối cú pháp. Nó xây dựng một AST.
Nguồn ví dụ:```python
x = 1 + 2
```Hình dạng AST:```text
Module
Assign
targets:
Name id="x", ctx=Store
value:
BinOp
left: Constant 1
op: Add
right: Constant 2
```Ở cấp độ Python:```python
import ast
tree = ast.parse("x = 1 + 2\n")
print(ast.dump(tree, indent=4))
```Điều này tạo ra một cây có cấu trúc.
AST vẫn không phải là mã byte. Nó là một cây cú pháp với các chú thích ngữ nghĩa như ngữ cảnh tải/lưu trữ và vị trí nguồn.
## 19.14 Vị trí nguồn
Các nút AST mang thông tin vị trí nguồn.
Điều này bao gồm số dòng và độ lệch cột. CPython hiện đại cũng theo dõi vị trí kết thúc của nhiều nút.
Ví dụ:```python
import ast
tree = ast.parse("x = 1 + 2\n")
node = tree.body[0]
print(node.lineno)
print(node.col_offset)
print(node.end_lineno)
print(node.end_col_offset)
```Hỗ trợ vị trí nguồn:```text
tracebacks
syntax errors
debuggers
coverage tools
profilers
AST tools
editor integrations
```Nếu không có vị trí nguồn chính xác, CPython vẫn có thể thực thi mã, nhưng chất lượng chẩn đoán và công cụ sẽ kém hơn nhiều.
## 19.15 Lỗi cú pháp
Khi phân tích cú pháp thất bại, CPython tăng`SyntaxError`hoặc một lớp con như`IndentationError`.
Một lỗi cú pháp sẽ báo cáo:```text
filename
line number
column offset
source text
message
```Ví dụ:```python
if x
pass
```Trình phân tích cú pháp thấy rằng`if`câu lệnh thiếu dấu hai chấm.
Một điểm lỗi hữu ích gần nơi không thể phân tích cú pháp:```text
SyntaxError: expected ':'
```Lỗi phân tích cú pháp không phải lúc nào cũng đơn giản. Bởi vì trình phân tích cú pháp có thể quay lại, nên nó phải nhớ vị trí lỗi hữu ích nhất thay vì chỉ vị trí lỗi thay thế cuối cùng.
Lỗi cú pháp tốt là một phần quan trọng của kỹ thuật phân tích cú pháp.
## 19.16 Quy tắc không hợp lệ và chẩn đoán tốt hơn
Ngữ pháp của CPython bao gồm các quy tắc được sử dụng riêng để đưa ra thông báo lỗi tốt hơn.
Một trình phân tích cú pháp có thể từ chối cú pháp xấu một cách tổng quát:```text
SyntaxError: invalid syntax
```Nhưng CPython thường cố gắng phát hiện những lỗi phổ biến:```python
if x = 1:
pass
```Điều này có thể tạo ra thông báo hữu ích hơn lỗi phân tích cú pháp chung.
Các quy tắc ngữ pháp không hợp lệ không nhằm mục đích chấp nhận các chương trình. Chúng được sử dụng để nhận ra các dạng sai và nâng cao khả năng chẩn đoán tốt hơn.
Mô hình thực tế:```text
normal grammar accepts valid Python
invalid rules recognize common invalid Python
error machinery reports a targeted message
```Điều này giữ cho trình phân tích cú pháp luôn chặt chẽ trong khi cải thiện trải nghiệm người dùng.
## 19.17 Đầu ra của trình phân tích cú pháp chưa được xác thực đầy đủ
Phân tích cú pháp thành công có nghĩa là nguồn khớp với ngữ pháp Python. Điều đó không có nghĩa là chương trình hoàn toàn hợp lệ theo mọi nghĩa sau này.
Một số kiểm tra xảy ra sau khi phân tích cú pháp.
Ví dụ:```python
return 1
```Ở cấp độ mô-đun, điều này có hình dạng cú pháp giống như một câu lệnh return, nhưng nó không hợp lệ khi ở bên ngoài một hàm.
Một ví dụ khác:```python
break
```Ở cấp độ mô-đun, về mặt ngữ pháp đây là một câu lệnh break, nhưng nó không hợp lệ khi ở bên ngoài vòng lặp.
Những ràng buộc này thường được kiểm tra trong quá trình xác thực AST, phân tích bảng ký hiệu hoặc biên dịch.
Các giai đoạn là riêng biệt:```text
parser:
Can these tokens form a syntax tree?
later compiler checks:
Is this syntax legal in this context?
Which names are local, global, free, or cell variables?
Can this construct be compiled here?
```## 19.18`ast.parse`Công chúng`ast.parse()`hàm hiển thị bộ máy phân tích cú pháp của CPython ở cấp độ Python.
Ví dụ:```python
import ast
src = """
def add(a, b):
return a + b
"""
tree = ast.parse(src)
print(ast.dump(tree, indent=4))
```Điều này hữu ích cho:```text
linters
formatters
static analyzers
code generation tools
security scanners
refactoring tools
documentation tools
```Nhưng`ast.parse()`trả về AST, không phải mã thông báo và không phải mã byte.
Đối với mã byte, hãy sử dụng`compile()`Và`dis`:
```python
import dis
code = compile("x = 1 + 2\n", "<input>", "exec")
dis.dis(code)
```Mối quan hệ là:```text
tokenize.tokenize() source to tokens
ast.parse() source to AST
compile() source or AST to code object
dis.dis() code object to bytecode display
```## 19.19 Chế độ phân tích cú pháp
Nguồn Python có thể được phân tích cú pháp ở các chế độ khác nhau.
Các chế độ phổ biến:
| Chế độ | Ý nghĩa |
| -------- | ---------------------------------------- |
|`exec`| Phân tích một mô-đun hoặc chuỗi câu lệnh |
|`eval`| Phân tích một biểu thức |
|`single`| Phân tích đầu vào tương tác |
Ví dụ:```python
compile("x = 1", "<input>", "exec")
compile("1 + 2", "<input>", "eval")
compile("print(1)", "<input>", "single")
evalchế độ từ chối các câu lệnh:```python
compile("x = 1", "", "eval")
Chế độ phân tích cú pháp xác định ký hiệu bắt đầu ngữ pháp.
## 19.20 Chuỗi F và Trình phân tích cú pháp
CPython hiện đại phân tích các biểu thức chuỗi f thông qua máy phân tích cú pháp.
Trước Python 3.12, phân tích cú pháp biểu thức chuỗi f có nhiều hành vi trong trường hợp đặc biệt hơn. Python 3.12 đã thay đổi chuỗi f thông qua PEP 701, đưa chúng vào ngữ pháp đầy đủ hơn. Tài liệu Python 3.12 lưu ý rằng chuỗi f được phân tích cú pháp bằng trình phân tích cú pháp PEG, cho phép thông báo lỗi chính xác hơn và hiệu suất mã thông báo được cải thiện do những thay đổi này. ([Tài liệu Python][2])
Ví dụ:```python
name = "Ada"
text = f"hello {name.upper()}"
```Biểu thức được nhúng:```python
name.upper()
```được phân tích cú pháp dưới dạng cú pháp biểu thức Python.
Điều này có nghĩa là chuỗi f không chỉ là phép nội suy chuỗi khi chạy. Chúng chứa cú pháp lồng nhau phải được phân tích cú pháp và biểu diễn theo cấu trúc.
## 19.21 Tệp phân tích cú pháp trong CPython
Các vùng nguồn quan trọng liên quan đến trình phân tích cú pháp bao gồm:```text
Grammar/python.gram
Parser/
Python/ast.c
Include/internal/
Lib/ast.py
Lib/test/test_grammar.py
Lib/test/test_syntax.py
```Ranh giới tệp chính xác có thể thay đổi, nhưng sự phân chia khái niệm vẫn ổn định:
| Khu vực | Vai trò |
| ---------------- | --------------------------------------------- |
| Ngữ pháp | Xác định cú pháp Python |
| Trình tạo trình phân tích cú pháp | Tạo việc triển khai trình phân tích cú pháp từ ngữ pháp |
| Thời gian chạy của trình phân tích cú pháp | Tiêu thụ mã thông báo và áp dụng các quy tắc ngữ pháp |
| AST xây dựng | Xây dựng các nút AST |
| Xác thực AST | Kiểm tra tính đúng đắn về cấu trúc |
| Kiểm tra | Bảo vệ cú pháp và chẩn đoán |
Khi đọc CPython, hãy bắt đầu với ngữ pháp. Sau đó làm theo cách quy tắc ngữ pháp tạo nút AST.
## 19.22 Ví dụ: Phân tích hàm
Nguồn:```python
def square(x):
return x * x
```Chế độ xem cấp mã thông báo:```text
NAME "def"
NAME "square"
LPAR "("
NAME "x"
RPAR ")"
COLON ":"
NEWLINE
INDENT
NAME "return"
NAME "x"
STAR "*"
NAME "x"
NEWLINE
DEDENT
ENDMARKER
```Chế độ xem cấp AST:```text
Module
FunctionDef
name: "square"
args:
arguments
arg: "x"
body:
Return
BinOp
left: Name "x", Load
op: Mult
right: Name "x", Load
```Trình phân tích cú pháp tạo ra hệ thống phân cấp này vì ngữ pháp cho biết:```text
a module contains statements
a function definition is a statement
a function body is a block
a return statement can contain an expression
a multiplication expression contains two operands
a name expression loads a value
```Mỗi cấp độ mang lại ý nghĩa cho luồng mã thông báo phẳng.
## 19.23 Ví dụ: Phân tích một`if`Tuyên bố
Nguồn:```python
if score >= 60:
result = "pass"
else:
result = "fail"
```Hình dạng AST:```text
Module
If
test:
Compare
left: Name "score"
op: GtE
comparator: Constant 60
body:
Assign
target: Name "result", Store
value: Constant "pass"
orelse:
Assign
target: Name "result", Store
value: Constant "fail"
```Trình phân tích cú pháp phải đính kèm`else`đến đúng`if`.
Mã thông báo thụt lề giúp ích ở đây. các`else`xuất hiện sau`DEDENT`kết thúc khối đầu tiên, nhưng ở cùng mức thụt lề với khối`if`.
Đây là nơi cú pháp nhạy cảm với bố cục của Python tương tác trực tiếp với cấu trúc trình phân tích cú pháp.
## 19.24 Ví dụ: Phân tích cú pháp hiểu
Nguồn:```python
squares = [x * x for x in values if x > 0]
```Hình dạng AST:```text
Assign
target: Name "squares", Store
value:
ListComp
element:
BinOp
Name "x"
Mult
Name "x"
generators:
comprehension
target: Name "x", Store
iter: Name "values", Load
ifs:
Compare
Name "x"
Gt
Constant 0
```Phần hiểu là cú pháp biểu thức, nhưng chúng chứa các mục tiêu giống như phép gán và các mệnh đề lồng nhau.
Trình phân tích cú pháp phải phân biệt:```python
[x * x for x in values]
```từ một danh sách theo nghĩa đen:```python
[x * x, y]
```Cả hai đều bắt đầu bằng`[`và một biểu hiện. Chuỗi mã thông báo sau biểu thức đầu tiên xác định cấu trúc mà trình phân tích cú pháp xây dựng.
## 19.25 Ranh giới của trình phân tích cú pháp
Trình phân tích cú pháp không thực thi mã.
Nó không gọi các chức năng:```python
f()
```Nó chỉ xây dựng một nút cuộc gọi.
Nó không nhập mô-đun:```python
import os
```Nó chỉ xây dựng một nút nhập.
Nó không giải quyết tên:```python
x + y
```Nó chỉ xây dựng các nút tên và hoạt động nhị phân.
Nó không đánh giá các hằng số vượt quá những gì lớp AST thể hiện.
Công việc của trình phân tích cú pháp là có tính cấu trúc. Các giai đoạn sau chỉ định phạm vi, tạo mã byte và thực hiện các hoạt động.
## 19.26 Mô hình tinh thần tối thiểu
Sử dụng mô hình này:```text
The tokenizer emits a flat stream of tokens.
The parser matches that stream against Python grammar.
CPython’s modern parser is PEG-based.
Grammar alternatives are ordered.
The parser builds an AST.
The AST records statements, expressions, contexts, and source locations.
Syntax errors are raised when the token stream cannot form valid grammar.
Some validity checks happen after parsing.
```Phân tích cú pháp là nơi văn bản nguồn đầu tiên trở thành hình cây. Mọi thứ sau giai đoạn này hoạt động theo cấu trúc thay vì thứ tự mã thông báo thô.