OP 似乎对处理树的语言感兴趣并且有一些并行性支持。
我们的DMS Software Reengineering Toolkit 是一个通用的程序分析和转换引擎。它将编程语言源文本解析为树(OP 的第一个兴趣),能够分析和处理这些树,并从这些树中重新生成源文本。
DMS 由描述正在处理的语言的显式语法驱动,并且它有许多可用的生产质量语言定义。
DMS 的树处理机制在两个不同级别提供并行支持,一个由 DMS 的底层并行编程语言 PARLANSE 直接支持,该语言受 LISP 启发,但几乎没有那么动态。
PARLANSE 提供了称为“谷物”的并行活动的“团队”(就像在沙滩上的沙子中,想法是有很多谷物)。团队可以通过(经典)分叉新颗粒并将它们添加到(动态)团队来动态构建;这很有用,但速度不是特别快。团队可能是静态结构的,包括“将这个固定大小的颗粒集分叉为纯并行”:
(|| a b c)
和“创建一组执行顺序由指定的部分顺序控制的颗粒”(!| ...)。你直接在fork调用中写偏序:
(!| step1 a
step2 b
step3 (>> step1 step2) c
step4 (>> step2) d )
表示动作 c 发生在动作 a 和 b 之后(后来 >> )强>完整。静态形成的团队由 PARLANSE 编译器预编译成非常有效的结构,因为可以非常快速地启动和杀死颗粒,从而允许非常小的颗粒大小(几百条指令)。标准并行库的开销比这要高得多。
处理树的基本方法非常传统:有一个 PARLANSE 库,用于检查树节点、在树上上下移动、创建新节点并将它们拼接到位。递归过程通常用于访问/更新树。
这里的关键观察是,访问某些子集的树访问可以按顺序编码,也可以轻松编码为静态并行团队。因此,手动编写并行树访问代码非常容易,但代价是为每种树节点类型编写大量特殊情况到并行分叉。 OP似乎对“分而治之”感兴趣。 (您当然可以枚举节点的子节点,并使用动态团队为每个子节点分叉,但这并不那么快)。这是用于在 DMS 中处理树的第一级并行性。
第二级并行性来自 DMS 提供的 DSL,该 DSL 实现了属性语法 (AG)。
AG 是用一组计算来装饰 BNF 的函数式语言。可以用 DMS 中的 AG 编写一个简单的计算器:
sum = sum + product;
<<Calculator>>: sum[0].result = sum[1].result + product.result;
这会通过组合第一个孩子 (sum[1]) 和第二个孩子 (product.result) 的结果属性来为根 (sum[0]) 计算一个属性“结果”。
所谓的综合属性将信息从叶子向上传播;继承的属性从父母那里传播信息。一般的属性文法和 DMS 的 AG 允许混合这些,因此信息可以以任意顺序在树上上下流动。
大多数 AG 都是纯功能性的,例如,没有副作用; DMS 允许出现使符号复杂化但在实践中非常有用的副作用。一个很好的例子是通过属性评估构建符号表。 current-scope 向下传递树,local-declaration 块创建新的 current scope 并将它们向下传递,并且各个声明将符号表数据存储到从 parent 接收到的符号表条目中。
DMS 的 AG 编译器分析属性计算,确定信息如何在整个树中流动,然后生成并行 PARLANSE 程序来实现每个树节点类型的信息流。它可以对每个 AG 规则进行数据流分析,以确定信息流以及先发生、后发生和最后发生的事情。对于我们上面的简单求和规则,应该清楚的是,必须先计算子节点的属性,然后才能计算根的属性。事实证明,PARLANSE 的偏序构造是对这些信息进行编码的完美方式,甚至可以很好地处理我们添加到 AG 的副作用。
结果是 DMS 将 AG 规范编译成高度并行的 PARLANSE 程序。我们的 C++ 前端名称/类型解析器被实现为大约 150K 行 DMS AG(是的,描述如何使用 C++ 类型信息需要这么多),它编译
到大约 700K SLOC 的并行 PARLANSE 代码。而且它工作(并在 x86 多核机器上并行运行),无需任何思考或调试,而且它似乎可以很好地扩展。