try:
if line := input(
PROMPT if not lines else PROMPT_CONTINUE
): # read a line of input with prompt
lines.append(line)
else:
lexer = Lexer("\n".join(lines))
while True:
try:
token = lexer.next_token()
print(repr(token))
if token.type == TokenType.EOF:
break
except LexErr as err:
print(err, file=sys.stderr)
break
lines.clear()词法分析:总结
约 694 字大约 2 分钟
2026-02-17
现在我们来总结一下词法分析的内容。词法分析的实际上是对每个字符进行扫描,符合词法规则的就转换成下一个符号,否则就抛出异常。我们在讨论测试的时候曾经提到过正则表达式,当时我们用来测试抛出异常中的错误信息是不是符合预期。正则表达式也经常被经常被用来描述词法规则。以下是一个常用的元字符。我们手动实现了整数和浮点数的词法分析。如果我们用正则表达式,整数应当表示为\d+。而浮点数应当表示为\d+\.\d*。注意.已经是个元字符,所以在匹配.的时候需要转义使用\.。此外,由于我们后续将使用Decimal来转换整数和浮点数的字符串,它可以接受12.作为输入。因此我们用了\d*,也就是 0 个或多个,而不是\d+一个或多个数字。
| 元字符 | 说明 |
|---|---|
. | 匹配除换行符外的任意单个字符 |
\d | 匹配数字(等价于 [0-9]) |
\w | 匹配字母、数字、下划线(等价于 [a-zA-Z0-9_]) |
\s | 匹配空白字符(空格、制表符等) |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
* | 匹配前面的子表达式零次或多次 |
+ | 匹配前面的子表达式一次或多次 |
? | 匹配前面的子表达式零次或一次 |
{n} | 匹配前面的子表达式恰好 n 次 |
[abc] | 匹配方括号中的任意一个字符 |
(...) | 分组,也用于提取子串 |
| | 或运算,匹配左边或右边的表达式 |
还记得我们的 REPL 吗?让我们修改一下代码,将词法分析器和解析器整合到 REPL 中。
这是整合后的 REPL,呈现的效果。输入一些字符串,输出对应的符号的 repr 结果。
Hello! This is PyEC! Type any command or Ctrl+D to exit.
=>> 123
>>
Token(type=TokenType.INT, literal='123', line=1, column=1)
Token(type=TokenType.EOF, literal='\x00', line=1, column=4)
=>> 888 abc
>>
Token(type=TokenType.INT, literal='888', line=1, column=1)
Token(type=TokenType.WHITESPACE, literal=' ', line=1, column=4)
Illegal character: 'a' at line 1, col 5
=>> 589
>> 7 *2
>>
Token(type=TokenType.INT, literal='589', line=1, column=1)
Token(type=TokenType.NEWLINE, literal='\n', line=1, column=4)
Token(type=TokenType.INT, literal='7', line=2, column=1)
Token(type=TokenType.WHITESPACE, literal=' ', line=2, column=2)
Token(type=TokenType.ASTERISK, literal='*', line=2, column=3)
Token(type=TokenType.INT, literal='2', line=2, column=4)
Token(type=TokenType.EOF, literal='\x00', line=2, column=5)如果你也已经实现了类似效果,那么词法分析的内容就已经完成了。提交到 Git。
$ git add .
$ git commit -m "lexer: repl"