栈机解释器与表达式编译练习
前置:能阅读顺序执行、整数运算和栈的压入弹出;Python 3.10+ 用于自定义 32 位有符号栈机。教学范围为RISC-V 20240411非特权ISA概念;自定义栈机;Python 3.10+。核对日期2026-10-17。示例未执行,本轮没有安装依赖、生成协议代码、启动服务或浏览器预览。完整源码和输入文件见下方「同源完整文件」;推演结论只适用于写明的模型。
完整栈机解释器
完整栈机解释器仅支持PUSH、ADD、SUB、MUL与HALT,拒绝未知指令和栈下溢。每一步完整描述操作数变化,运算溢出报告错误;HALT要求只剩一个结果,指令结束没有HALT也失败,避免把半成品当合法程序。
栈机推演预期
把栈写在每一步旁边可以定位第一次偏离预期的位置。这里的SUB和MUL都将两个值归约成一个值;最终HALT检查栈大小,不能随意取顶部而丢弃其他结果。
查看图表代码
栈机推演预期为PUSH7、PUSH2、SUB、PUSH3、MUL、HALT得到15。SUB按左操作数7减右操作数2得5,再乘3。只写ADD没有压栈会报stack underflow;两个值直接HALT则违反单一结果契约。
表达式编译练习
表达式编译练习把(8-3)(2+4)改成后缀指令,先完成左子树,再完成右子树,最后乘。去掉括号后的8-32+4是另一棵树,不能靠改变打印形式代替解析优先级;结合规则与求值顺序也应分别说明。
同源完整文件
以下README含前置、输入、读者自跑步骤、预期结果、失败、恢复与清理;只做静态核对,没有执行。
model.py
python
import json
import sys
def evaluate(program):
stack = []
for pc, instruction in enumerate(program):
if not isinstance(instruction, list) or not instruction:
raise ValueError('instruction must be a nonempty array')
op, *args = instruction
if op == 'PUSH' and len(args) == 1 and type(args[0]) is int:
value = args[0]
elif op in ('ADD', 'SUB', 'MUL') and not args:
if len(stack) < 2:
raise ValueError('stack underflow at ' + str(pc))
right, left = stack.pop(), stack.pop()
value = {'ADD': lambda: left + right, 'SUB': lambda: left - right,
'MUL': lambda: left * right}[op]()
elif op == 'HALT' and not args:
if len(stack) != 1 or pc != len(program) - 1:
raise ValueError('HALT requires one result and must be final')
return stack[0]
else:
raise ValueError('invalid opcode or operands')
if not -(2**31) <= value < 2**31:
raise ValueError('signed 32-bit overflow')
stack.append(value)
raise ValueError('missing HALT')
if __name__ == '__main__':
try:
data = json.load(sys.stdin)
if not isinstance(data, list):
raise ValueError('program array required')
print(evaluate(data))
except (ValueError, TypeError) as error:
print(str(error), file=sys.stderr)
sys.exit(2)requirements.txt
text
# Python 3.10+ standard library only; no third-party dependency.README.md
md
# 数据表示与程序执行:完整示例
示例未执行。核对日期2026-10-17。教学版本族:RISC-V 20240411非特权ISA概念;自定义栈机;Python 3.10+。
## 文件与依赖
文件:model.py, requirements.txt。requirements.txt为同源依赖声明;所有代码和配置都随本文展示。本例使用 Python 3.10+ 标准库,无额外运行依赖。
## 输入与前置
前置:理解操作数栈、二元运算左右值与程序停止条件。使用 Python 3.10+,只依赖标准库;这是显式限制 32 位有符号结果的自定义栈机,不是 RISC-V 仿真器。输入是指令数组,具体程序与预期见下节。
读者将来自跑(本轮未执行):在本目录以Python 3.10+读取input.json,PowerShell示意`Get-Content -Raw input.json | python model.py`;Bash示意`python3 model.py < input.json`。把下面输入手工保存为UTF-8文件,输出按预期逐项比较。无需安装依赖,没有服务或网络调用;程序只读标准输入并写标准输出,不改原文件。失败信息写标准错误且退出2。恢复是修正输入后重读,清理只删除自己创建的input.json与输出文件,勿递归删除工作目录。
## 预期结果、失败与恢复
输入 [["PUSH",7],["PUSH",2],["SUB"],["PUSH",3],["MUL"],["HALT"]]预期15;[["ADD"],["HALT"]]报stack underflow;PUSH2147483648报signed 32-bit overflow。
失败要保留输入和错误,先与正文的不变量及边界核对。没有真实服务或性能验证,不以这些模型输出证明生产可用性。
## 独立练习
将(8-3)*(2+4)编译到本课栈机并给出每个二元运算后的栈。
参考答案:PUSH8 PUSH3 SUB后栈[5];PUSH2 PUSH4 ADD后栈[5,6];MUL后栈[30];HALT返回30。若把SUB误写为先弹出的减后弹出的,将得-30,能定位操作数顺序错误。先作答,再核对
将(8-3)*(2+4)编译到本课栈机并给出每个二元运算后的栈。
逐步参考解答
PUSH8 PUSH3 SUB后栈[5];PUSH2 PUSH4 ADD后栈[5,6];MUL后栈[30];HALT返回30。若把SUB误写为先弹出的减后弹出的,将得-30,能定位操作数顺序错误。
来源与适用边界
- 英文官方、作者或规范原文:本专题机制依据;按上述固定版本族阅读。
- 中文官方或作者资料:用于术语和讲解交叉核对,冲突以原始规范为准。
继续阅读
- 表示与执行状态
- 模型代价与规范
- 模型代价与规范