青语言开发文档

第四步:执行代码

创建于 2023-04-28 / 最近更新于 2023-04-28 / 1592
字体: [默认] [大] [更大]

经过上面的步骤,我们已经有了代码转换后的Expr序列,以及初始化后的语境,此时我们已经具备了执行代码所需的前提条件。

执行代码的过程就是对Expr序列里的每个Expr进行求解,对应的方法定义在Lang/Expr.cs中:

public Expr Eval(Ctx ctx, ref bool state) {
            if (!state) {
                return this;
            }

            /*类型小于圆括号类型的是值类型Expr,直接返回*/
            if(Tp < TP.Var) {
                return this;
            }

            /*元类型的Expr,返回内部包装的Expr*/
            if (Tp == TP.Meta) {
                return (Expr)Val;
            }

            /*取反类型,对内部的Expr转逻辑类型后再取反*/
            if (Tp == TP.Negate) {
                return new Expr(TP.Bool, !Raw().Eval(ctx).ToBool());
            }

            ……

这个方法的入参主要就是语境ctx,另外的state是用于中断程序执行的,这里不做重点介绍。

不难看出,这里的Eval方法内部其实就是根据当前Expr的类型的不同,采取不同的求解方式,例如类型Tp的值小于TP.Var,那么就说明是单个值,可以直接返回。如果是类型的Expr,那么其内部封装了另一个Expr,则将内部的Expr取出。如果是取反语句,那么对其内部的Expr求解后转为逻辑值后再取反。

当然,青语言还有更多类型的Expr,用于表示不同的值和语句,均在这个Eval方法中进行处理。


Eval方法用于处理单个Expr,而我们的代码会转换为Expr序列,所以对执行代码的过程只需要遍历Expr,逐个地对Expr进行求解即可。

public static Expr EvalExprs(List<Expr> es, Ctx ctx, ref bool state) {
            Expr ans = new Expr(TP.None, 0);
            if(es.Count == 0) {
                return ans;
            }
            /*遍历传入的Expr序列*/
            foreach(Expr e in es) {
                   ……

注意,其实分支语句、循环语句中出现的代码块,包括函数的代码块,其实本质上也是Expr序列,所以对它们的求解也使用到了这个EvalExprs方法,但是这些语句在执行过程中会出现一些中途跳出的情况,所以我们在遍历的过程中还要处理这些情况:

try {
                    /*对序列中的每一个Expr进行求解,求解依赖于指定语境Context*/
                    ans = e.Eval(ctx, ref state);
 }
……
if (ans.Tp == TP.Return) {               
                    return ans;
}
if (ans.Tp == TP.Break || ans.Tp == TP.Continue) {
                    return ans;
}

如果执行过程中出现了返回跳出继续关键字或语句,那么直接中断EvalExprs方法的执行,并直接返回。这些特殊的Expr会在循环或函数的执行代码中进行处理,以确定跳出的位置。

0 人点赞过