“操作语义区分正常执行和无法继续的状态。本页监测失败返回REJECT;有限命令预算耗尽返回UNKNOWN。它们是分析报告,不能未经新合同直接当成公开程序输出。非干扰的双运行条件在这里是:两个初…”
形式陈述
操作语义先从程序的抽象语法确定配置集合
两种常见判断形状是
前者是单步转移,完整执行由有限或无限路径组成;后者用一棵有限推导树直接证明表达式求值到结果。二者都要另行定义值、最终配置与可观察行为,箭头符号本身不会提供这些分类。
若语言有可观察错误,应使用显式 error 配置或错误判断,而不要让错误项仅仅“没有规则”。发散可由无限转移轨迹或共归纳判断
动态来源跟踪在当前值旁携带来源集合,动态信息流监测再加入pc、敏感升级和显式拒绝。这些状态扩展不改变区分结果的责任:运行预算耗尽只能记为未完成,不能直接当成发散证明;诊断标签若对外公开,也会改变原来的观察接口。
抽象机器把控制项、环境、存储与续延栈等成分显式化为机器配置。它是操作语义的一种形式,不与小步或大步判断简单同义。
语义还可以是确定的或非确定的。确定性要求
直觉
操作语义把语言规范写成一台数学解释器。规则匹配当前配置的形状,前提描述子计算,结论给出下一配置或最终结果。推导树既是行为证据,也是证明确定性、模拟和类型保持时可归纳分析的对象。
与把程序组合地映到数学对象的指称语义相比,操作语义直接保留执行结构,因此容易表达求值顺序、异常传播、状态更新与并发交错。一次语义步的粒度由规范者选择,不应未经成本对应证明就当作处理器指令。
例子与边界
以自然数和加法表达式为例,小步语义可用规则固定左到右顺序:
于是同一个表达式产生可观察的中间轨迹
大步语义不列出这些中间配置。取
直接推出
两套规则在这个终止、无错误片段上给出相同最终值,但提供的证据不同:小步轨迹记录中间项与顺序,大步推导组合子结果。大步规则把两个前提左右排开,并不单凭版面就规定时间先后;本例没有副作用,交换子计算也不改变结果。带状态的大步规则则应把第一个子计算的输出存储传给第二个子计算,显式落实顺序。
对应证明的一个方向是:假设大步推导的两个子树已经分别产生有限小步路径,先用左位置规则提升第一条路径,再用右位置规则提升第二条,最后执行一次加法。反方向可证明“一步化简不改变大步结果”,再沿小步路径归纳。这样得到的是全体项上的命题,不能由几个最终值相同的算例替代。
加入赋值后,配置必须携带存储,例如 x := 1; x 的后半段无法观察前一步更新。异常若可捕获,控制栈或上下文也会影响传播位置。
必须区分三种未得到普通值的情形。true + 1 若既不是结果也没有规则,就是 stuck,可能代表非法程序或规范遗漏;若语言规定除零得到 error,则 while true do skip 或 λ 项
公理语义从另一角度用前置条件、后置条件和证明规则描述程序性质,不必列出执行步骤。操作规则可作为 Hoare 规则可靠性的参照,但两套语义承担不同职责。
推论与应用
小步语义适合观察中间状态、并发交错和非终止,大步语义适合直接组织终止求值。二者只在明确值、错误和发散条件后才有可证明的对应命题。
求值上下文可集中描述允许计算的位置;在另行证明唯一分解后,确定求值才可表述为“寻找唯一 redex”。环境求值则常把函数值表示为代码与定义环境组成的闭包。按需调用与代数效应处理器会分别加入堆更新或 perform/handle 转移;这些实例扩展配置和规则,却仍使用同一语义接口。
在这套接口上,进展与保持定理把类型判断与执行步骤联系起来;程序等价和编译器正确性则比较两套关系是否保持可观察行为。它们需要先固定语义,不能反过来代替动态规则。
面向程序分析时,控制流图提取可能的控制转移,收集语义在各程序点汇总操作语义可达的具体状态,抽象机器则把控制、环境和存储显式化为可执行配置。它们分别是结构投影、语义汇总和执行模型;模型检查或抽象解释算法在这些对象上工作,但语义步数仍不自动等于机器运行成本。
参考资料
- Gordon D. Plotkin, “A Structural Approach to Operational Semantics,” DAIMI FN-19, 1981; reprinted in Journal of Logic and Algebraic Programming 60–61, 2004。
- Glynn Winskel, The Formal Semantics of Programming Languages, MIT Press, 1993, Chapters 2–4。
- Benjamin C. Pierce, Types and Programming Languages, MIT Press, 2002, Chapters 3, 5, and 8。