18. Mã thông báo
18. Token hóa
Mã thông báo là giai đoạn cấu trúc đầu tiên trong quy trình biên dịch của CPython.
Nó nhận văn bản nguồn Python và tạo ra một luồng mã thông báo. Trình phân tích cú pháp sử dụng luồng mã thông báo đó và xây dựng cấu trúc cú pháp từ nó.
Ở giai đoạn này, CPython vẫn chưa biết liệu một chương trình có ý nghĩa hay không. Nó chỉ nhận dạng các đơn vị từ vựng: tên, số, chuỗi, toán tử, dòng mới, thụt lề và dấu cuối tập tin.
Trình mã thông báo biến điều này:python def add(a, b): return a + b thành một dòng có hình dạng như thế này: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 "" Tên mã thông báo chính xác và giao diện trình phân tích cú pháp khác nhau giữa các phiên bản CPython, nhưng ý tưởng cốt lõi là ổn định. Thư viện chuẩn Python hiển thị mã thông báo cấp Python thông quatokenize, trong khi trình phân tích cú pháp của CPython sử dụng mã thông báo C của riêng nó trong nội bộ. Công chúngtokenizemô-đun cũng trả về các nhận xét và mã thông báo mã hóa ban đầu, giúp nó phù hợp với các công cụ như trình định dạng và tô sáng cú pháp. ([Tài liệu Python][1])
18.1 Vị trí trong Đường dẫn biên dịch
Đường dẫn nguồn thực thi đầy đủ là:```text bytes from file or string ↓ encoding detection ↓ decoded source text ↓ tokenizer ↓ token stream ↓ parser ↓ abstract syntax tree ↓ symbol table ↓ code object ↓ bytecode execution
Trình mã thông báo trả lời các câu hỏi như:```text
Where does this logical line end?
Is this identifier a name?
Is this numeric literal well-formed?
Is this string literal closed?
Did indentation increase or decrease?
Is this character part of an operator?
Has the source reached end-of-file?
```Trình phân tích cú pháp trả lời các câu hỏi khác nhau:```text
Is this a valid function definition?
Is this expression allowed here?
Does this statement match a grammar rule?
Does this sequence form a valid pattern match?
How should these tokens be grouped into an AST?
```Trình mã thông báo không tạo AST. Nó chỉ tạo ra một chuỗi các sự kiện từ vựng.
## 18.2 Đầu vào và mã hóa nguồn
Nguồn Python thường bắt đầu bằng byte.
Trước khi quá trình mã hóa có thể được tiến hành, CPython phải xác định cách giải mã các byte đó thành văn bản. Các tệp nguồn Python theo mặc định là UTF-8, nhưng một tệp có thể khai báo một mã hóa khác ở gần đầu.
Khai báo mã hóa điển hình:```python
# -*- coding: latin-1 -*-
```hoặc:```python
# coding: utf-8
```Trình mã thông báo phải xử lý việc này sớm vì nó không thể phân loại các ký tự nguồn một cách đáng tin cậy cho đến khi nguồn được giải mã.
Ở cấp độ Python,`tokenize.tokenize()`bắt đầu bằng cách trả lại một`ENCODING`mã thông báo. các`token`tài liệu lưu ý rằng điều này`ENCODING`mã thông báo là cần thiết cho Python`tokenize`mô-đun và không được mã thông báo C của CPython sử dụng theo cách tương tự. ([Tài liệu Python][2])
Mô hình thực tế là:```text
read first source lines
detect encoding declaration if present
decode source bytes
normalize line handling
begin lexical scanning
```Đây là lý do tại sao việc mã thông báo không chỉ là một vòng lặp trên các ký tự. Nó cũng sở hữu ranh giới giữa byte nguồn bên ngoài và văn bản nguồn bên trong.
## 18.3 Đường vật lý và Đường logic
Nguồn Python có dòng vật lý và dòng logic.
Một dòng vật lý là một dòng trong tệp nguồn.
Một dòng logic là một câu lệnh hoặc đơn vị biểu thức Python hoàn chỉnh mà trình phân tích cú pháp nhìn thấy.
Thông thường chúng giống nhau:```python
x = 1
y = 2
```Ở đây, mỗi dòng vật lý cũng là một dòng logic.
Nhưng Python cho phép nối dòng rõ ràng bằng dấu gạch chéo ngược:```python
x = 1 + \
2 + \
3
```Đây là một dòng logic trải rộng trên ba dòng vật lý.
Python cũng cho phép nối dòng ngầm bên trong dấu ngoặc đơn, dấu ngoặc và dấu ngoặc nhọn:```python
values = [
1,
2,
3,
]
```Bên trong các dấu phân cách nhóm, dòng mới không kết thúc câu lệnh logic. Trình mã thông báo theo dõi độ sâu lồng nhau để có thể phân biệt các dòng mới quan trọng với các dòng mới không quan trọng.
Về mặt khái niệm:```text
paren_level = 0
when "(" or "[" or "{" appears:
paren_level += 1
when ")" or "]" or "}" appears:
paren_level -= 1
when newline appears:
if paren_level == 0:
emit NEWLINE
else:
ignore as statement terminator
```Quy tắc này rất cần thiết cho cú pháp nhiều dòng dễ đọc của Python.
## 18.4`NEWLINE`Và`NL`Mã thông báo cấp Python phân biệt giữa dòng mới theo logic và dòng mới không kết thúc.
Tại công chúng`tokenize`cấp độ:
| Mã thông báo | Ý nghĩa |
| --------- | ---------------------------------------- |
|`NEWLINE`| Kết thúc một dòng hợp lý |
|`NL`| Dòng mới không kết thúc một dòng hợp lý |
Ví dụ:```python
x = (
1 +
2
)
y = 3
```Các dòng mới bên trong dấu ngoặc đơn không phải là dấu kết thúc câu lệnh. Trình phân tích cú pháp không nên xử lý`1 +`như một tuyên bố hoàn chỉnh. Những ngắt dòng đó tồn tại để bố trí, không phải ngữ pháp.
Trong trình mã thông báo đối diện với công cụ, chúng xuất hiện dưới dạng`NL`. Trong mô hình đối mặt với trình phân tích cú pháp, chúng bị bỏ qua hoặc xử lý khác với các dòng mới logic thực sự.
Sự khác biệt này rất quan trọng đối với các trình định dạng và linters. Trình định dạng có thể quan tâm đến mọi dòng mới vật lý. Trình phân tích cú pháp chỉ cần cấu trúc logic.
## 18.5 Thụt lề dưới dạng mã thông báo
Python sử dụng thụt lề làm cú pháp.
Điều đó có nghĩa là trình mã thông báo phải biến khoảng trắng ở đầu thành mã thông báo.
Ví dụ:```python
if ready:
run()
log()
finish()
```Trình phân tích cú pháp không thể hiểu nguồn này nếu chỉ sử dụng tên và dấu câu. Nó cần ranh giới khối rõ ràng.
Trình mã thông báo phát ra:```text
NAME "if"
NAME "ready"
COLON ":"
NEWLINE "\n"
INDENT " "
NAME "run"
LPAR "("
RPAR ")"
NEWLINE "\n"
NAME "log"
LPAR "("
RPAR ")"
NEWLINE "\n"
DEDENT ""
NAME "finish"
LPAR "("
RPAR ")"
NEWLINE "\n"
ENDMARKER ""
```Thụt lề tạo ra một khối bắt đầu ảo. Việc thụt lề tạo ra một kết thúc khối ảo.
CPython duy trì một ngăn xếp thụt đầu dòng. Khi bắt đầu một dòng logic, bộ mã thông báo sẽ đo khoảng trắng ở đầu và so sánh nó với mức thụt lề hiện tại.
Mô hình đơn giản hóa:```text
indent_stack = [0]
at beginning of logical line:
col = indentation_column()
if col > indent_stack[-1]:
push col
emit INDENT
else if col == indent_stack[-1]:
emit no indentation token
else:
while col < indent_stack[-1]:
pop
emit DEDENT
if col != indent_stack[-1]:
report indentation error
```Kỷ luật ngăn xếp này giải thích tại sao việc thụt lề không nhất quán là một lỗi từ vựng trước khi quá trình phân tích cú pháp thông thường có thể tiếp tục.
## 18.6 Tab, Dấu cách và Cột thụt lề
Thụt lề được đo bằng cột, không phải ký tự thô.
Dấu cách tiến lên một cột. Các tab tiến tới điểm dừng tab tiếp theo. Việc xử lý tab của Python tồn tại để tương thích, nhưng việc trộn lẫn các tab và dấu cách có thể tạo ra lỗi và lỗi thụt vào không rõ ràng.
Ví dụ:```python
if x:
\tprint("tab")
print("spaces")
```Căn chỉnh trực quan có thể phụ thuộc vào cài đặt trình soạn thảo. CPython không thể tin tưởng cách trình soạn thảo của con người hiển thị điều này. Nó tính toán thụt lề bằng cách sử dụng các quy tắc tab của ngôn ngữ và phát sinh lỗi khi thụt lề không nhất quán.
Điểm nội bộ quan trọng là thụt lề không được lưu trữ dưới dạng “số ký tự đầu”. Nó được chuyển đổi thành mức thụt lề. Các mức đó được so sánh với ngăn xếp thụt lề.
## 18.7 Dòng trống và Dòng chỉ nhận xét
Các dòng trống thường không tạo ra các mã thông báo có ý nghĩa đối với trình phân tích cú pháp.
Ví dụ:```python
x = 1
y = 2
```Dòng trống không kết thúc một khối hoặc tạo một câu lệnh.
Các dòng chỉ nhận xét hoạt động tương tự đối với trình phân tích cú pháp:```python
x = 1
# comment
y = 2
```Công chúng`tokenize`mô-đun trả về nhận xét vì các công cụ cần chúng. Trình phân tích cú pháp của CPython không coi nhận xét là cú pháp.
Sự khác biệt này rất quan trọng:
| Người tiêu dùng | Cần bình luận? | Lý do |
| ------------------ | --------------: | ---------------------------------- |
| Trình phân tích cú pháp | Không | Bình luận không ảnh hưởng tới ngữ pháp |
| Trình định dạng | Có | Bình luận phải được bảo tồn |
| Công cụ tô sáng cú pháp | Có | Bình luận cần tạo kiểu |
| Kẻ nói dối | Có | Bình luận có thể chứa chỉ thị |
| Trình kiểm tra loại | Đôi khi | Bình luận có thể chứa loại bình luận |
Mã thông báo công khai là một API công cụ. Mã thông báo C là một phần của giao diện người dùng của trình biên dịch.
## 18.8 Tên, Từ khóa và Từ khóa mềm
Mã định danh được mã hóa dưới dạng tên.
Ví dụ:```python
total = price + tax
```Trình mã thông báo thấy:```text
NAME "total"
EQUAL "="
NAME "price"
PLUS "+"
NAME "tax"
```Từ khóa Python truyền thống bao gồm các từ như:```text
def
class
if
else
while
for
try
except
return
yield
import
from
with
lambda
```Ở cấp độ từ vựng, đây là những chuỗi hình tên. Trình mã thông báo hoặc trình phân tích cú pháp có thể phân loại chúng theo nhu cầu ngữ pháp.
Python hiện đại cũng có từ khóa mềm. Từ khóa mềm chỉ hoạt động giống như từ khóa ở các vị trí ngữ pháp cụ thể.
Ví dụ bao gồm các từ được sử dụng bởi khớp mẫu:```python
match value:
case 0:
pass
matchVàcasevẫn có thể được sử dụng như tên thông thường trong các ngữ cảnh khác mà ngữ pháp cho phép.
Đây là một lý do khiến quá trình mã hóa và phân tích cú pháp phải hợp tác. Một tokenizer đã chuyển đổi vĩnh viễn mỗi lần xuất hiện củamatchvào mã thông báo từ khóa cứng sẽ từ chối mã hợp lệ trong bối cảnhmatchchỉ là một cái tên.
Nguyên tắc thực hành:```text hard keyword: reserved everywhere soft keyword: special only in selected grammar positions name: ordinary identifier
Mã định danh Python có thể chứa nhiều ký tự Unicode.
Ví dụ:```python
π = 3.14159
面积 = 42
```Trình mã thông báo phải nhận dạng các ký tự bắt đầu và tiếp tục định danh theo quy tắc định danh của Python. Điều này mang lại sự hỗ trợ Unicode rộng rãi cho mã nguồn Python.
Nhưng số nhận dạng vẫn được chuẩn hóa và kiểm tra theo quy tắc ngôn ngữ. Không phải mọi ký tự Unicode đều hợp pháp trong tên và một số ký tự trông giống nhau có thể khác biệt.
Từ góc độ nội bộ, việc xử lý mã định danh yêu cầu:```text
Unicode-aware character classification
identifier start validation
identifier continuation validation
normalization rules
error reporting for invalid characters
```Điều này làm cho mã thông báo Python phức tạp hơn mã thông báo ngôn ngữ chỉ ASCII.
## 18.10 Chữ số
Trình mã thông báo nhận dạng các chữ số trước khi trình phân tích cú pháp xây dựng các biểu thức.
Ví dụ:```python
123
0b1010
0o755
0xff
1_000_000
3.14
10.
.5
1e9
1.2e-3
3j
```Chúng trở thành mã thông báo số.
Trình mã thông báo phải xác thực dạng từ vựng:```text
base prefixes
digits allowed in each base
underscore placement
decimal points
exponents
imaginary suffix
```Một số số không hợp lệ không thành công trong quá trình mã thông báo:```python
0b102
1__2
```Trình mã thông báo không đánh giá số học tùy ý. Nó chỉ nhận ra mã thông báo theo nghĩa đen. Các giai đoạn biên dịch sau này chuyển đổi văn bản mã thông báo thành đối tượng Python tương ứng.
Ví dụ:```python
x = 1 + 2
```Mã thông báo thấy:```text
NAME "x"
EQUAL "="
NUMBER "1"
PLUS "+"
NUMBER "2"
```Sự thật là`1 + 2`có thể được gấp lại thành`3`thuộc về tối ưu hóa trình biên dịch sau này, không phải mã thông báo.
## 18.11 Chuỗi ký tự
Mã thông báo chuỗi phức tạp hơn mã thông báo số.
Python hỗ trợ:```python
"hello"
'hello'
"""hello"""
'''hello'''
r"\n"
b"bytes"
f"value={x}"
fr"path={name}\n"
```Trình mã thông báo phải xác định:```text
string prefixes
quote style
single-line or triple-quoted form
raw strings
bytes strings
f-strings
escape sequences
line continuation rules
string termination
```Mã thông báo chuỗi thông thường được nhận dạng là một đơn vị từ vựng:```python
x = "hello"
```Luồng mã thông báo:```text
NAME "x"
EQUAL "="
STRING "\"hello\""
```Chuỗi trích dẫn ba có thể kéo dài các dòng vật lý:```python
text = """
line 1
line 2
"""
```Trình mã thông báo phải tiếp tục quét cho đến khi tìm thấy trích dẫn ba phù hợp.
Các chuỗi bị hủy là lỗi mã thông báo:```python
x = "missing end
```Trình phân tích cú pháp không thể khôi phục ngữ pháp có ý nghĩa từ một chuỗi bị kết thúc vì trình mã thông báo không thể tạo ra luồng mã thông báo hợp lệ.
## 18.12 Dây F
Chuỗi F đặc biệt vì chúng chứa cả nội dung chuỗi ký tự và các biểu thức Python được nhúng.
Ví dụ:```python
name = "Ada"
text = f"hello {name.upper()}"
```Bên trong chuỗi, phần này là văn bản bằng chữ:```text
hello
```Phần này là cú pháp biểu thức Python:```python
name.upper()
```Trình mã thông báo và trình phân tích cú pháp phải hợp tác để xử lý cấu trúc lồng nhau này.
Về mặt khái niệm:```text
enter f-string mode
scan literal characters
when "{" starts expression:
tokenize embedded Python expression
parse embedded expression
return to f-string literal scanning
finish at closing quote
```Việc xử lý biểu thức lồng nhau làm cho chuỗi f phong phú hơn nhiều so với các chuỗi ký tự thông thường. Chúng không chỉ là các mã thông báo chuỗi có thể thay thế văn bản sau này. Chúng chứa cú pháp phải được phân tích cú pháp thành các nút biểu thức.
## 18.13 Toán tử và dấu phân cách
Các toán tử và dấu phân cách trong Python bao gồm các dạng một ký tự và nhiều ký tự.
Ví dụ:```text
+ - * / // % **
= == != < <= > >=
:= -> @ @=
( ) [ ] { }
, : . ; ...
```Trình mã thông báo thường áp dụng hành vi khớp dài nhất.
Ví dụ, khi đọc`**=`, nó sẽ tạo ra một mã thông báo toán tử gán quyền thay vì`*`, `*`, Và`=`.
Logic đơn giản hóa:```text
if next characters form "**=":
emit DOUBLESTAR_EQUAL
else if next characters form "**":
emit DOUBLESTAR
else if next character is "*":
emit STAR
```Quy tắc này phổ biến trong các tokenizer. Nó giúp trình phân tích cú pháp không phải xây dựng lại các toán tử nhiều ký tự từ các phần nhỏ hơn.
## 18.14 Mã thông báo lỗi và lỗi từ vựng
Một số lỗi xuất hiện trước khi phân tích cú pháp.
Ví dụ:```python
x = "unterminated
if x:
a = 1
b = 2
x = 0b123
```Đây là những lỗi từ vựng hoặc thụt lề.
Trình mã thông báo phải báo cáo đủ thông tin để chẩn đoán hữu ích:```text
filename
line number
column offset
source line
error type
error message
```Các lỗi ở giai đoạn mã thông báo phổ biến bao gồm:
| Lỗi | Nguyên nhân |
| ------------------ | --------------------------------------------- |
|`SyntaxError`| Cấu trúc từ vựng hoặc chuỗi mã thông báo không hợp lệ |
|`IndentationError`| Mức thụt lề không hợp lệ |
|`TabError`| Thụt lề mơ hồ từ các tab và dấu cách |
|`TokenError`| Lỗi mã thông báo công khai do đầu vào không đầy đủ |
Không phải mọi`SyntaxError`bắt nguồn từ việc mã hóa. Nhiều đến từ việc phân tích cú pháp. Tuy nhiên, trình mã thông báo sở hữu các lỗi ngăn cản luồng mã thông báo hợp lệ tồn tại.
## 18.15 Kết thúc tệp và thụt lề tổng hợp
Ở cuối tệp, CPython phải đóng mọi khối thụt lề đang mở.
Ví dụ:```python
if x:
if y:
run()
```Nguồn kết thúc trong khi hai mức thụt lề vẫn hoạt động. Tokenizer phát ra tổng hợp`DEDENT`mã thông báo trước`ENDMARKER`.
Về mặt khái niệm:```text
NAME "if"
NAME "x"
COLON ":"
NEWLINE
INDENT
NAME "if"
NAME "y"
COLON ":"
NEWLINE
INDENT
NAME "run"
LPAR
RPAR
NEWLINE
DEDENT
DEDENT
ENDMARKER
```Điều này cho phép trình phân tích cú pháp nhìn thấy các kết thúc khối ngay cả khi không có dấu ngoặc nhọn đóng rõ ràng.
Do đó, trình mã thông báo tạo mã thông báo không có ký tự trực tiếp trong tệp nguồn.`INDENT`, `DEDENT`, Và`ENDMARKER`là các mã thông báo cấu trúc.
## 18.16 Trạng thái mã thông báo
Một mã thông báo có trạng thái.
Nó phải nhớ:```text
current input pointer
current line
current column
current indentation stack
current nesting level
whether scanning begins a line
whether inside a string
whether inside an f-string expression
whether an encoding was detected
whether interactive mode is active
pending INDENT or DEDENT tokens
error state
```Một máy quét không trạng thái sẽ không đủ cho Python vì ý nghĩa phụ thuộc vào bố cục và ngữ cảnh.
Ví dụ:```python
x = [
1,
2,
]
```Dòng mới sau`1,`xuất hiện bên trong dấu ngoặc. Nó không nên trở nên logic`NEWLINE`.
Ví dụ:```python
if x:
y = 1
z = 2
```Khoảng trắng hàng đầu trước`z`gây ra một`DEDENT`.
Những quyết định đó đòi hỏi trạng thái được ghi nhớ.
## 18.17 Mã thông báo tương tác
Đầu vào tương tác có trường hợp đặc biệt.
Trong REPL, CPython thường cần quyết định xem dữ liệu đầu vào đã hoàn tất hay chưa.
Ví dụ:```python
>>> if x:
...
```Điều này là không đầy đủ vì dự kiến sẽ có phần thân khối.
Ví dụ:```python
>>> x = (1 +
...
```Điều này chưa đầy đủ vì biểu thức trong ngoặc đơn vẫn mở.
Trình mã thông báo và trình phân tích cú pháp hợp tác để quyết định xem nên yêu cầu một dòng khác hay phát sinh lỗi. Do đó, chế độ tương tác khác với chế độ tập tin. Kết thúc đầu vào trong một tệp có nghĩa là EOF thực sự. Kết thúc đầu vào trong REPL có thể có nghĩa là “yêu cầu thêm văn bản”.
## 18.18 Công khai`tokenize`mô-đun
Thư viện chuẩn hiển thị mã thông báo thông qua`tokenize`.
Ví dụ:```python
from io import BytesIO
import tokenize
src = b"x = 1 + 2\n"
for tok in tokenize.tokenize(BytesIO(src).readline):
print(tok)
```Đầu ra có hình dạng như sau:```text
TokenInfo(type=ENCODING, string='utf-8', ...)
TokenInfo(type=NAME, string='x', ...)
TokenInfo(type=OP, string='=', ...)
TokenInfo(type=NUMBER, string='1', ...)
TokenInfo(type=OP, string='+', ...)
TokenInfo(type=NUMBER, string='2', ...)
TokenInfo(type=NEWLINE, string='\n', ...)
TokenInfo(type=ENDMARKER, string='', ...)
```Trình mã thông báo công khai hữu ích cho:```text
formatters
linters
code generators
syntax highlighters
refactoring tools
documentation tools
source-to-source transforms
```các`tokenize`tài liệu mô tả nó như một máy quét từ vựng cho nguồn Python và lưu ý rằng nó trả về các nhận xét dưới dạng mã thông báo, điều này làm cho nó hữu ích cho các máy in và máy tô màu đẹp. ([Tài liệu Python][1])
## 18.19 Mã thông báo C so với Mã thông báo Python
Có hai khái niệm tokenizer liên quan trong CPython:
| Thành phần | Vị trí | Mục đích |
| ----------------- | --------------------------------- | ----------------------------------- |
| Mã thông báo C | Nội bộ của trình biên dịch/phân tích cú pháp CPython | Cung cấp trình phân tích cú pháp trong quá trình biên dịch |
|`Lib/tokenize.py`| Thư viện chuẩn | Đưa mã thông báo vào các công cụ Python |
Chúng không phải là giao diện giống hệt nhau.
Mã thông báo C được tối ưu hóa cho đường dẫn trình biên dịch của CPython. Nó tạo ra những gì trình phân tích cú pháp cần.
Mã thông báo Python là một giao diện công cụ công cộng. Nó bảo tồn các bình luận, hiển thị mã hóa, trả về phong phú`TokenInfo`đối tượng và được thiết kế cho người tiêu dùng bên ngoài.
Sự khác biệt này giải thích tại sao mã thông báo truyền từ`tokenize`có thể chứa thông tin mà trình phân tích cú pháp bỏ qua.
## 18.20 Ví dụ chi tiết về token hóa
Hãy xem xét nguồn này:```python
def area(r):
pi = 3.14159
return pi * r * r
```Luồng mã thông báo được đơn giản hóa:```text
NAME "def"
NAME "area"
LPAR "("
NAME "r"
RPAR ")"
COLON ":"
NEWLINE "\n"
INDENT " "
NAME "pi"
EQUAL "="
NUMBER "3.14159"
NEWLINE "\n"
NAME "return"
NAME "pi"
STAR "*"
NAME "r"
STAR "*"
NAME "r"
NEWLINE "\n"
DEDENT ""
ENDMARKER ""
```Những điểm quan trọng:
1.`def`có hình dạng từ vựng nhưng đóng vai trò như một từ khóa về mặt ngữ pháp.
2. Thân hàm bắt đầu vì mức thụt lề tăng sau`NEWLINE`.
3.`3.14159`là một mã thông báo số duy nhất.
4.`return pi * r * r`là một dòng logic.
5. Cơ thể chức năng kết thúc thông qua một cơ chế tổng hợp`DEDENT`.
6. Tệp kết thúc thông qua`ENDMARKER`.
Trình phân tích cú pháp nhận luồng này và khớp nó với các quy tắc ngữ pháp cho định nghĩa hàm, bộ, phép gán, câu lệnh trả về và biểu thức.
## 18.21 Tokenization không hiểu đầy đủ ngữ nghĩa
Trình mã thông báo không biết rằng tên này không được xác định:```python
print(missing_name)
```Nó không biết rằng cuộc gọi này sẽ thất bại:```python
1()
```Nó không biết liệu nhập khẩu này có tồn tại hay không:```python
import does_not_exist
```Nó chỉ phát ra mã thông báo.
Việc kiểm tra ngữ nghĩa diễn ra sau đó, thường là trong thời gian chạy.
Token hóa có chủ ý nông cạn. Nó nhận dạng dạng từ vựng chứ không phải ý nghĩa chương trình.
## 18.22 Tại sao việc mã hóa lại quan trọng
Mã thông báo có vẻ nhỏ nhưng nó định hình toàn bộ ngôn ngữ.
Nó định nghĩa:```text
how indentation becomes syntax
how source bytes become characters
how comments are ignored or preserved
how strings are delimited
how f-strings embed expressions
how operators are recognized
how logical lines are formed
how parser input is structured
```Đối với những người đóng góp CPython, lỗi mã thông báo có thể ảnh hưởng đến cú pháp, chẩn đoán, công cụ, khả năng tương thích và bảo mật. Một thay đổi từ vựng nhỏ có thể thay đổi cách phân tích cú pháp mọi tệp Python.
Đối với các tác giả công cụ, mã thông báo thường là lớp tốt nhất để làm việc. Nó lưu giữ thông tin cấp nguồn mà AST loại bỏ, bao gồm các nhận xét, khoảng cách chính xác, dòng vật lý và chính tả của toán tử.
## 18.23 Mô hình tinh thần tối thiểu
Sử dụng mô hình này:```text
The tokenizer reads decoded Python source.
It emits lexical tokens.
It tracks indentation, nesting, strings, and line boundaries.
It inserts structural tokens such as INDENT, DEDENT, and ENDMARKER.
It reports lexical errors before parsing.
The parser consumes tokens and builds syntax structure.
```Đó là cầu nối từ văn bản nguồn thô đến ngữ pháp.