PROMPT = "=>> "
PROMPT_CONTINUE = " >> "
def start() -> None:
lines: list[str] = []
while True:
try:
if line := input(
PROMPT if not lines else PROMPT_CONTINUE
): # read a line of input with prompt
lines.append(line)
else:
print("\n".join(lines))
lines.clear()
except KeyboardInterrupt: # handle Ctrl+C
print("<KeyboardInterrupt>")
except EOFError: # handle Ctrl+D
print("Ctrl-D detected, exiting REPL.")
exit(0)支持多行输入的 REPL 框架
约 670 字大约 2 分钟
2025-12-08
这一章,我们来处理 REPL。暂时不用看到那个从源代码到求值的流程图了。REPL 是 Read-Eval-Print-Loop 的缩写。作为参考我们看看 Python 是怎么做的。下面是输出结果。L1-L2 是 Python 版本号等一些信息。L3 有个提示符号>>>,表示等待用户输入。我们输入的1+2,在 L4 显示结果3。L5 和 L7 都显示>>>等等用户输入。L5 的时候我们输入Ctrl + C。于是在 L6 显示了KeyboardInterrupt。类似地,L8 我们输入的Ctrl+D,触发了EOFError异常,程序结束。当然这个EOFError并没有显示出来。
Python 3.14.0 (main, Nov 19 2025, 22:48:15) [Clang 21.1.4 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1+2
3
>>>
KeyboardInterrupt
>>>我们也想要一个类似功能的东西,这样就可以输入一些算式查看运算结果了。但是这得一步一步来,在这一章我们没有实现 Evaluation。我们只是简单地输出接收到的输入。但是与 Python 不同的是,我们可以输入多行代码,每一行将来都是一个独立的语句。当我们最后直接输入一个回车的时候才会将我们之前的输入一五一十地打印出来。来看看我们的程序输入一些字符串会发生什么。
Hello! This is PyEC! Type any command or Ctrl+D to exit.
=>> 123
>>
123
=>> 456
>> hello
>>
456
hello
=>> ^C<KeyboardInterrupt>
=>>
=>> Ctrl-D detected, exiting REPL.我们输入123,在 L4 显示了123。L5-L6 我们输入456和hello,在 L8-L9 把我们的输入打印出来了。L10 我们输入Ctrl+C,触发了KeyboardInterrupt异常。L13 我们输入Ctrl+D,触发了EOFError异常,程序结束。
下面是一个基本的 REPL 工作过程。
至此我们要开始写一些代码来实现以上的功能。
L6 的lines用来存储我们输入的多行代码。REPL 中的 Loop 表现为一个无限循环。循环体是个 try-except 块。在 try 块中,我们调用input()函数获取用户输入。如果用户输入为空,我们就跳过这一次循环。否则,我们就将用户输入添加到lines中。在 except 块中,我们处理KeyboardInterrupt和EOFError异常。如果触发了这两个异常,我们就打印出相应的提示信息,然后退出循环。现在我们就可以试着打一些字看看程序有没有正常工作。
最后把改动提交到 Git 仓库。
$ git add .
$ git commit -m "make a dummy repl"