Page 81 - 《软件学报》2021年第6期
P. 81
苏卓 等:基于分支标记的数据流模型的代码生成方法 1655
27: return CGLT
算法 3 的主体流程是:首先构造一个空的代码生成位置表 CGLT,如第 1 行、第 2 行所示;然后,逐层逐组件
地根据组件地分支信息向代码生成位置表中添加元素.这里很重要的一点是,每一层在执行的时候会使用上一
层计算得到的 CGLT.也就是说,组件寻找 Inserter 和插入 Inserter 以及 Branch 不会影响到当前层的其他组件.
在每一层计算完之后,由算法第 23 行的 CGLT.update(⋅)来整理在这一层中插入的各种元素,让它们能够在下一
层的计算中生效.第 5 行~第 21 行为针对每个组件的处理代码,首先获得组件的分支路径集合,如第 5 行、第 6
行所示;之后,根据该组件的分支路径来确定如何修改代码生成位置表.若该组件的所有分支路径不存在冲突,
也就是这些分支路径的条件不会同时成立,就对这些分支路径分别处理,如第 8 行~第 16 行所示.对于每条分支
路径,若在代码生成位置表中可以直接找到带有该分支路径的 Inserter,就直接在该 Inserter 位置插入组件的执
行代码;如果没有,则去掉分支路径的最后一个分支作为 subBranchPath 去寻找 Inserter;如果去掉一个分支后仍
然找不到,就再去掉最后一个分支作为新的 subBranchPath,直到找到存在的 Inserter.这个迭代是有限次数的,因
为至少最后会找到带有{{0}}的 Inserter.在迭代过程中,如果找到了满足条件的 Inserter,首先移除代码生成位置
表中所有该 Inserter 所在的分支之内的所有 Inserter;之后,在该 Inserter 位置创建多层的分支结构,并在每个分
支结构中添加对应的 Inserter.例如,迭代寻找前的分支路径为{0,0B,0F},最终找到了带有{{0}}的 Inserter,那么
就在这个 Inserter 的位置依次插入{0,0B}, {0,0B,0F},{0,0B,1F},{0,1B},{0,1B,0F},{0,1B,1F}这些分支和对应的
Inserter.然后,将当前组件的执行代码插入到{0,0B,0F}对应的 Inserter 位置.针对组件的分支路径存在冲突的情
况,我们直接在带有{{0}}的 Inserter 位置插入这些分支路径的全部组合的分支及对应的 Inserter,如第 18 行~第
20 行所示.这种情况适用于复杂的分支交叉的情况.比如某个组件的分支路径集合为{{0,1X},{0,0Y}}(其中,X 组
件的 1 分支和 Y 组件的 0 分支可能会同时执行),则创建{{0,1X} && {0,0Y}},{{0,1X}},{{0,0Y}}这 3 种分支及对
应的 Inserter,其中,“&&”表示逻辑与,意为两个分支都成立.这个例子最终会生成如代码片段 1 所示代码.在该算
法的最后,移除那些分支内容为空的 Branch 元素,并移除所有的 Inserter,因为它们并不会生成实际的代码.
代码片段 1. 多分支路径组件对应生成的代码示意.
1: if (1X && 0Y){ //Branch
2: Actor(1X,0Y) //ActorExe
3: }
4: else if (1X){ //Branch
5: Actor(1X) //ActorExe
6: }
7: else if (0Y){ //Branch
8: Actor(0Y) //ActorExe
9: }
接下来,我们仍然以图 2 中的模型为例,根据图 3 中计算好的分支标记来构建一个完整的 CGLT.由于受到篇
幅和排版的限制,我们没有办法将构造 CGLT 的每一步都罗列出来.代码片段 2 中展示了最终生成的 CGLT,其
中,用删除线划掉的代表最终被删掉的元素,在注释中注明了该元素是在什么时候创建和删除的.对于图 2 中的
模型,一开始我们构建一个空的 CGLT,并插入带有{{0}}分支路径的 Inserter,用于指定后续元素插入的位置,对
应到代码片段 2 的第 31 行.之后,逐层遍历模型.首先是 A 组件,A 组件具有{{0}}分支标记,所以直接将 A 组件的
执行语句插入到带有{{0}}的 Inserter 前面,对应到代码片段 2 的第 1 行.第 2 层的 B 组件也是同理,只不过 B 组
件的执行参数为 A 组件的输出,对应到代码片段 2 的第 2 行.虽然 B 组件是分支组件,但是它对应的分支不在这
里创建,而是在后面的组件遍历中按需要创建的.接下来,遍历到第 3 层的 C 组件,这时我们发现,C 组件的分支标
记{{0,0B}}在 CGLT 中查不到对应的 Inserter,而且 C 组件的分支标记中也只有一个分支路径{0,0B},所以,这里
按照算法 3 的第 9 行~第 15 行进行处理.去掉分支路径上的最后一个分支,继续查找子分支路径{0},找到带有
{{0}}的 Inserter,在该 Inserter 前插入 B 组件的所有分支,并将 C 组件的执行语句插入到新的带有{{0,0B}}的