About
RSS

Bit Focus


移花接木 - 编译同步代码为异步代码 [条件表达式篇]

Posted at 2013-03-17 00:38:47 | Updated at 2024-04-24 15:40:11

上篇

条件表达式

    如果设计如下的类生成 Javascript 中条件表达式 predicate ? consequence : alternative
# semantic.py

class Conditional(Expression):
    def __init__(self, predicate, consequence, alternative):
        self.predicate = predicate
        self.consequence = consequence
        self.alternative = alternative

    def compile(self, context):
        return output.Conditional(self.predicate.compile(context),
                                  self.consequence.compile(context),
                                  self.alternative.compile(context))
# output.py

class Conditional(Expression):
    def __init__(self, predicate, consequence, alternative):
        self.predicate = predicate
        self.consequence = consequence
        self.alternative = alternative

    def str(self):
        return '({p} ? {c} : {a})'.format(p=self.predicate.str(),
                                          c=self.consequence.str(),
                                          a=self.alternative.str())
    这在条件表达式的三个子表达式均不包含异步调用时, 或仅 predicate 包含正规异步调用没问题, 如
root_context = ContextFlow()
root_flow = root_context.block

Block([
        Arithmetics(
            Binary(
                '=',
                Reference('m'),
                Conditional(
                    Reference('a'),
                    Reference('b'),
                    Reference('c')))),
        Arithmetics(
            Binary(
                '=',
                Reference('n'),
                Conditional(
                    RegularAsyncCall(
                        Reference('f'),
                        [ NumericLiteral(0) ]),
                    Reference('x'),
                    Reference('y')))),
    ]).compile(root_context)

print root_flow.str()
    这些节点编译后会生成类似如下代码
{
    m = (a ? b : c);
    (f(0, function (error, result$4000000000) {
        n = (result$4000000000 ? x : y);
    }));
}
    但如果 consequencealternative 至少一者包含正规异步调用时, 情况会发生质变. 如编译以下节点
Arithmetics(
    Binary(
        '=',
        Reference('p'),
        Conditional(
            Reference('z'),
            RegularAsyncCall(
                Reference('g'),
                [ NumericLiteral(1) ]),
            RegularAsyncCall(
                Reference('h'),
                [ NumericLiteral(2) ]))))
    若不加任何控制则生成类似如下代码
(g(1, function (error, result$4000000000) {
    (h(2, function (error, result$4111111000) {
        p = (z ? result$4000000000 : result$4111111000);
    }));
}));
    这代码运行起来当然是完全正确的, 唯一的问题就是无论条件 z 是真是假, 两个回调都会被求值, 并且在这之后再计算条件表达式的值. 这就没有任何意义了, 很明显任何正常的程序员都希望代码能够根据条件 z 的真假来决断该去对哪一个表达式进行求值.
    要达成这个目的, 思路是把条件表达式展开成一个分支语句插在上下文语句流中 (就像之前正规异步调用会生成一个算术语句插在上下文语句流中一样), 并且还要插入一个回调函数定义 (这个之前没有过), 然后在两个分支情况下, 将对应的表达式作为回调参数传入; 接下来就是轻车熟路地把剩下的语法节点撵到回调函数函数体内. 空口说没意思, 先给个概念蓝图, 到底要生成什么样的代码, 如下
conditionalTemporaryFunc = function (result) {
    p = result;
};

if (z) {
    g(1, function (error, result$4000000000) {
        conditionalTemporaryFunc(result$4000000000);
    });
} else {
    h(2, function (error, result$4111111000) {
        conditionalTemporaryFunc(result$4111111000);
    });
}
    那么下面就该动手改 semantic.py 中 Conditional 的编译方法实现了.
def compile(self, context):
    # 先编译条件
    pred_compl = self.predicate.compile(context)

    # 向当前上下文语句流中插入函数定义
    cb_body_context = ContextFlow() # 回调函数的上下文语句流
    cb_body_block = cb_body_context.block
    cb_body_id = str(id(cb_body_block)) # 同样地, 这时亦需要保证回调名唯一性
    cb_param = 'result$' + cb_body_id
    cb_lambda = output.Lambda([ cb_param ], cb_body_block)
    cb_ref_name = 'conditionalCallback$' + cb_body_id
    context.block.add(output.Arithmetics(output.Binary('=',
                                         output.Reference(cb_ref_name),
                                         cb_lambda))) # 放入回调函数定义

    # 向当前流中插入分支语句
    # 先分别编译两个分支
    def compile_one_branch(expression):
        sub_context = ContextFlow()
        sub_block = sub_context.block
        expr_compl = expression.compile(sub_context)
        sub_context.block.add(output.Arithmetics(output.Call(
                        output.Reference(cb_ref_name), [ expr_compl ])))
        return sub_block

    context.block.add(output.Branch(pred_compl,
                                    compile_one_branch(self.consequence),
                                    compile_one_branch(self.alternative)))

    # 替换当前语句流
    context.block = cb_lambda.body

    # 作为结果, 返回回调参数的引用
    return output.Reference(cb_param)
    现在重新运行之前的代码, 生成的 Javascript 会类似这个样子
{
    (conditionalCallback$3000000000 = function (result$3000000000) {
        (p = result$3000000000);
    });
    if (z) {
        (g(1, function (error, result$4000000000) {
            (conditionalCallback$3000000000(result$4000000000));
        }));
    }
    else {
        (h(2, function (error, result$4111111000) {
            (conditionalCallback$3000000000(result$4111111000));
        }));
    }
}
    不过似乎还有什么不对. 确实, 因为上面这种实现方案一出, 无论条件语句是否需要这样进行变换 (即是否 consequencepredicate 至少一者含异步调用), Conditional 都会把语句流搅得鸡飞狗跳. 那么, 必须要有个方法来验明一个表达式是否直接或间接包含正规异步成分, 那样就得在 Expression 类上开个接口了.
# semantic.py

class Expression:
    def is_async(self):
        return False

class Binary(Expression):
    def is_async(self):
        return self.left.is_async() or self.right.is_async()

class Conditional(Expression):
    def is_async(self):
        return self.predicate.is_async() or self.left.is_async(
                                       ) or self.right.is_async()

class Call(Expression):
    def is_async(self):
        for arg in self.arguments:
            if arg.is_async():
                return True
        return self.callee.is_async()

class RegularAsyncCall(Expression):
    def is_async(self):
        return True
    不一而足. 现在, 如果 Conditional 对象发现其分支均不包含正规异步成分, 则按照最不折腾的方式直译, 否则才去玩弄语句流.
class Conditional(Expression):
    def compile(self, context):
        if self.consequence.is_async() or self.alternative.is_async():
            return self.compile_async(context)
        return self.compile_sync(context)

    def compile_sync(self, context):
        return output.Conditional(self.predicate.compile(context),
                                  self.consequence.compile(context),
                                  self.alternative.compile(context))

    def compile_async(self, context):
        # 刚才的 compile 实现
    这部分完整代码请见这里.

短路

    如果与 / 或算符右侧的表达式含有正规异步成分, 那么同条件表达式一样, 不能等到两边结果都出来了再计算表达式, 否则短路就短不成了.
    最简单的方法是把短路变换成等价的条件表达式
    好了剩下就大家自行脑补加码补吧.

    最后广告: Stekinscript, 以更同步的方式编写异步代码.

Post tags:   Python  Javascript  Asynchronous Expression  Compiler Construction

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site