条件表达式
如果设计如下的类生成 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);
}));
}
consequence
与 alternative
至少一者包含正规异步调用时, 情况会发生质变. 如编译以下节点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);
});
}
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)
{
(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));
}));
}
}
consequence
与 predicate
至少一者含异步调用), 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 实现
短路
如果与 / 或算符右侧的表达式含有正规异步成分, 那么同条件表达式一样, 不能等到两边结果都出来了再计算表达式, 否则短路就短不成了.最简单的方法是把短路变换成等价的条件表达式
a || b
等价于a ? a : b
a && b
等价于a ? b : false
最后广告: Stekinscript, 以更同步的方式编写异步代码.