python - 如何将 python 比较 ast 节点转换为 c?

标签 python c compiler-construction abstract-syntax-tree transpiler

让我们首先考虑 python3.8.5's grammar ,在这种情况下,我有兴趣弄清楚如何将 python 比较转换为 c。
为了简单起见,我们假设我们正在处理一个非常小的 Python 平凡子集,我们只想转译平凡的 Compare 表达式:

expr = Compare(expr left, cmpop* ops, expr* comparators)
如果我没记错的话,python 中的表达式如 a<b<c被转换成类似 a<b && b<c 的东西其中 b 只计算一次...所以我想在 c 中你应该做类似 bool v0=a<b; bool v1=v0<c 的事情为了防止在第一个子句为真的情况下对 b 进行多次评估。
不幸的是,我不知道如何将其放入代码中,到目前为止,这就是我所拥有的:
import ast
import shutil
import textwrap
from subprocess import PIPE
from subprocess import Popen


class Visitor(ast.NodeVisitor):
    def visit(self, node):
        ret = super().visit(node)
        if ret is None:
            raise Exception("Unsupported node")
        return ret

    def visit_Expr(self, node):
        return f"{self.visit(node.value)};"

    def visit_Eq(self, node):
        return "=="

    def visit_Lt(self, node):
        return "<"

    def visit_LtE(self, node):
        return "<="

    def visit_Load(self, node):
        return "//load"

    def visit_Name(self, node):
        return f"{node.id}"

    def visit_Compare(self, node):
        left = self.visit(node.left)
        ops = [self.visit(x) for x in node.ops]
        comparators = [self.visit(x) for x in node.comparators]

        if len(ops) == 1 and len(comparators) == 1:
            return f"({left} {ops[0]} {comparators[0]})"
        else:
            lhs = ",".join([f"'{v}'" for v in ops])
            rhs = ",".join([f"{v}" for v in comparators])
            return f"cmp<{lhs}>({rhs})"

    def visit_Call(self, node):
        func = self.visit(node.func)
        args = [self.visit(x) for x in node.args]
        # keywords = [self.visit(x) for x in node.keywords]
        return f"{func}({','.join(args)})"

    def visit_Module(self, node):
        return f"{''.join([self.visit(x) for x in node.body])}"

    def visit_Num(self, node):
        return node.n


if __name__ == "__main__":
    out = Visitor().visit(
        ast.parse(
            textwrap.dedent(
                """
            1 == 1<3
            1 == (1<3)
            1 == (0 < foo(0 <= bar() < 3, baz())) < (4 < 5)
            foo(0 <= bar() < 3, baz())
        """
            )
        )
    )

    if shutil.which("clang-format"):
        cmd = "clang-format -style webkit -offset 0 -length {} -assume-filename None"
        p = Popen(
            cmd.format(len(out)), stdout=PIPE, stdin=PIPE, stderr=PIPE, shell=True
        )
        out = p.communicate(input=out.encode("utf-8"))[0].decode("utf-8")
        print(out)
    else:
        print(out)
如您所见,输出将是某种不可编译的 c 输出:
cmp<'==', '<'>(1, 3);
(1 == (1 < 3));
cmp<'==', '<'>((0 < foo(cmp<'<=', '<'>(bar(), 3), baz())), (4 < 5));
foo(cmp<'<=', '<'>(bar(), 3), baz());
问题,什么是算法(python 工作示例在这里是理想的,但只是一些允许我改进提供的片段的通用伪代码也可以)允许我将 python 比较表达式转换为 c?

最佳答案

正确翻译为:

if 1 == 2 < 3:
是:
int i1 = 1;
int i2 = 2;
int i3 = 3;
if(i1 == i2 && i2 < i3) {
(编辑:这仍然不正确,因为它不会短路)
或者,最后一个不必是临时变量:
int i1 = 1;
int i2 = 2;
if(i1 == i2 && i2 < 3) {
或者:(此版本在比较表达式中保留要比较的表达式)
int i1;
if(1 == (i1 = 2) && i2 < 3) {
你的编译器需要知道被比较的值是 int s 以便它可以声明临时变量。而且它还需要选择每次都不同的临时变量名,所以如果你有两个这样的比较,那么它不会尝试生成多个具有相同名称的变量。
您可能会意识到可以对表达式 2 求值。不止一次,所以编译器可以生成以下代码:
if(1 == 2 && 2 < 3) {
但这是一个可选的额外项目。
请注意,在同一个表达式中可能有多个这些:
if 1 < (2 if (6 < 7 < 8) else 3) < 4:
翻译成这样:
int i1 = 1;
    int i2 = 6;
    int i3 = 7;
    int i4 = 8;
int i5 = (i2 < i3 && i3 < i4 ? 2 : 3);
int i6 = 4;
if(i1 < i5 && i5 < i6) {
或者:
int i1;
int i2;
if(1 < (i1 = (6 < (i2 = 7) && i2 < 8 ? 2 : 3)) && i1 < 4) {
//            ^^^^^^^^^^^^^^^^^^^^^^ inside
// ^^^^^^^^^^^                               ^^^^^^^^^^^ outside

关于python - 如何将 python 比较 ast 节点转换为 c?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63069595/

相关文章:

python - 如何为 xgboost 实现增量训练?

python - 如何将 GMT 时间转换为字符串时间

python - 通过对 pandas 进行采样,用不同的元素填充数据框中的空值

c - C语言中如何将带有终止符的字符串复制到另一个字符串中?

c - 在快速排序(hoare)中遇到无限循环,但我似乎没有发现问题

dll - 如何构建一个不需要 DLL 的程序

python - 如果条件满足,Pandas Dataframe 找到第一个出现的位置

c - 为什么要多重定义?为什么没有定义其他引用?这真的很基本,我错过了什么?

unix - 重新编译二进制文件时出现总线错误

C++ builder 如何配置编译器只输出 exe?