将一段imp程序转Kripke structure(KS)。
这是系统分析与验证一门课的作业,需要将一段imp程序转为一阶逻辑公式,再由一阶逻辑公式转为 Kripke structure(KS)。并且老师要求是不能使用python.因为github上有python和t实现了完整的过程。
https://github.com/lypnol/impy
其实也有一个rust 版本
https://gitlab.com/davirain.yin/imp-interpeter
关于阶逻辑公式和Kripke structure的概念,可以参考
https://blog.youkuaiyun.com/qq_37400312/article/details/109157582
https://blog.youkuaiyun.com/Campsisgrandiflora/article/details/110505997
将Imp程序翻译为一阶逻辑公式,显然需要词法、语法解析器。在小组分工中,我负责将一阶逻辑公式转为KS.
我使用的是.net c#语言。以下就从代码实现的角度简单的总结一下这个转化过程。
一阶逻辑公式构造Kripke structure(KS)
假定对于一段这样的代码:
V= {x, y, z}, initial value: x=y=z=0
Program
x=y+1; z=z+2;
for (y; y<=3; y++)
if x<y then x++; else y++;
作了这样的标签化处理:
现在已经通过词法、语法分析生成了一以下这样的一阶逻辑公式:
D:{0,1,2,3,4}
V:{x,y,z}
S0(x,y,z):x=0⋀y=0⋀z=0
R:
(x=0⋀y=0⋀z=0⋀PC=M⋀PC'=L1)⋁
(x=y+1⋀SAME(V\{x})⋀PC=L1⋀PC'=L12)⋁
(z=z+2⋀SAME(V\{z})⋀PC=L12⋀PC'=L2)⋁
(SAME(ALL)⋀PC=L2⋀PC'=L22)⋁
((y<=3)⋀SAME(ALL)⋀PC=L22⋀PC'=L3)⋁
(!(y<=3)⋀SAME(ALL)⋀PC=L22⋀PC'=M')⋁
((x<y)⋀SAME(ALL)⋀PC=L3⋀PC'=L31)⋁
(!(x<y)⋀SAME(ALL)⋀PC=L3⋀PC'=L32)⋁
(x=x+1⋀SAME(V\{x})⋀PC=L31⋀PC'=L23)⋁
(y=y+1⋀SAME(V\{y})⋀PC=L32⋀PC'=L23)⋁
(y=y+1⋀SAME(V\{y})⋀PC=L23⋀PC'=L22)
一阶逻辑公式中的R过析取符号⋁将状态集连接起来。
每一状态中可能有赋值表达式、条件表达式。同时通过计数器PC、PC'指示着当前的位置以及下一个状态的位置。
使用代码处理时,可以使用这样的内存结构来保存一阶逻辑,对于每一步的转换状态 (TransitionState),使用PC标识自己,即SelfPC,PC'为找下一状态的依据,即NextPC。
R中包含着多个TransitionState.
将S0作为初始状态(或者是SelfPC=M时表达式的值)。遍历每一个TransitionState,得到新的状态。
(1)顺序状态集得更新KS状态
对于顺序语句来讲,这是显而易见的,
比如对于语句1,程序将读取到当前状态为 (x=0) (y=0) (z=0), 并且根据NextPC= L1,找一下个状态PC=L1的状态语句,即2,通过表达式 x=y+1, 可以得到新状态 (x=1) (y=0) (z=0)
(在这里,c#对于表达式 x=y+1来获取状态,需要通过反射来执行,如果是js或者python可能会有eval等函数)
条件/循环的处理
对于包含SAME(ALL)的TransitionState,状态不需要更新,这时NextPC会找到多个,意味着遇到条件分支的情况。
比如遍历到以下状态集时,NextPC=L22, 可以找到两个,那么需要结合当前状态和条件语句,判断下步的是找L3还是M'。(条件语句的判断与赋值语句一样,需要通过反射来执行,获得true/false的结果)
当第一次遍历到这里时,可以知道y<=3是成立,而NextPC=L3,依然是条件分支情况,状态7或者8
结合当前状态和条件语句,可以判断是下一步是L31还是L32。执行L31或者32,可以得到新的状态。然后再根据新状态回到条件语句继续作判定,直到条件不成立。
可以将以上过程归纳为如下流程:
使用Graphivz绘制KS的状态集,结果如下:
以上为简单程序一阶逻辑公式到KS。(这里的简单的指的是不考虑并发)。以下再探讨并发进程的处理。
附上示例中的代码
https://download.youkuaiyun.com/download/mochounv/19414680
并发程序一阶逻辑公式到KS:
同样假定通过语法词法分析得到一阶逻辑公式如下:
我的实现基于这样的约定:
1 . 从 S0PC1=⊥∧PC2=⊥ 判定为程序为两个进程的并发的程序。且PC1和PC2为进程计数器标识。
结构的话,依然可以使用以上FirstOrderFormula 和 TransitionState的结构.
2. 以t为竞态变量(更为通用的处理方法应该是遍历了R集合为,如果一个条件没有出口,则应该是竞态条件)
内存结构与以上差不多
转为过程大致如下:
每对每一个状态条件:
构建进程未初始化状态
如构建 进程在入口状态
对于每一个进程:
查找当前遍历进程的下一下状态
判断是否到出口:
如果是则查看其它进程
如果不是则判断是否为等待状态
如果是则置状态为自循环状态,且将另一进程置为进程i
如果不是则生成新的状态
流程图如下:
以上FOL生成了这样的KS结构:
代码在这:
https://download.youkuaiyun.com/download/mochounv/19414680
以上两个示例的代码并不能非常通用,非并发和并发的状况也没处理为一个程序,实在时间限制。
程序中用于绘图的GraphViz,确实是一个非常方便的工具,既可以有c#(其它语言如java,python也行)接口,也可以基于dot的格式,生成文本,然后找个在线的GraphViz贴上文本就行。
http://www.graphviz.org/download GraphViz的下载地址。
https://github.com/remyzerems/GraphVizDotNetLib
GraphVizDotNetLib主要是通过P-invokde的方式,将GraphViz库中提供与绘制节点和和输出绘制相关的c++API封装成为c# API。并在GraphVizRenderer类中将调用这些API,实现通过传入dot 文本,输出 内存形式的图形数据。
在windows 10 中安装了graphviz时,选择把路径写到环境变量,则GraphVizDotNetLib会自动找到依赖的include和bin.
指定节点描述字符
http://cn.voidcc.com/question/p-cnrmzjdj-ms.html
节点带换行
https://www.it1352.com/1678876.html
在线绘图工具
Graphviz Visual Editor (magjac.com)