如何用玩具语言实现return语句?

时间:2012-09-21 15:41:07

标签: c return language-design bison flex-lexer

我最近使用C,Bison,Flex和this post作为起点制作了一种玩具编程语言。它看起来很像Python,除了没有冒号或空格规则。

代码是here,但它不如我坚持的概念/算法重要。

我设计了我的抽象语法树,就像Rudi在上面链接的帖子中所做的那样。

诀窍是,我无法想到从用户定义的函数返回或断开循环的好方法。如果我在用户定义的函数末尾只需要一个return语句,那么它是可行的(实际上这是当前用于用户定义函数的函数)。

示例:

i = 0
while 1 do
  if i > 15 then
     break
  end
done

示例2:

def mean(somelist)
  if len(list) == 0 then
    return 0  # throw error
  else
    return sum(somelist) / len(somelist)
  end
end

3 个答案:

答案 0 :(得分:0)

一些流行的基于堆栈的语言将值压入堆栈,然后由调用函数弹出。这可能对你有用。

当然,这依赖于具有已知返回类型的函数。如果函数是' void'那么Python返回PyNone。 (即什么都不返回)。 Python函数只返回一个值(可以是None,或者是对象或元组或其他)。

C类型语言也只返回函数中的一个值。

我认为重点是,在强类型语言中,您总是有一个返回值,您必须始终返回一个。在弱类型语言中,您可能或者可能返回一个值,但您仍然有一个。

答案 1 :(得分:0)

这个问题的答案很大程度上取决于代码如何运作的内部。

当我编写Mote Compiler(类似VB的编译器)时,我在函数启动时调用了MoteEngine.prototype.runFuncStart。我创建了一个新对象来保存所有局部变量,然后将其推送到我的堆栈中。

MoteEngine.prototype.runFuncEnd清理了堆栈。

答案 2 :(得分:0)

嗯,这很大程度上取决于你如何实现你的执行引擎。如果您遵循Rudi的建议,即使用递归调用彼此遍历AST的函数来实现执行引擎,那么要实现breakreturn,您需要返回几个级别的C调用堆栈。您可以使用setjmp / longjmp执行此操作。例如,您的代码可能类似于:

struct ExecEnviron {
    /* other contents of ExecEnviron, we're just adding a bit */
    jmp_buf *break_context;  /* where a 'break' or 'continue' should jump to */
    jmp_buf *return_context; /* where a 'return' should jump to */
};

void ExecWhile(ExecEnviron *e, AstElement *a) {
    jmp_buf local, *old = e->break_context;
    e->break_context = &local;

    if (setjmp(&local) != 1)
        while (dispatchExpression(a->whileStmt.cond))
            dispatchStatement(a->whileStmt.body);
    e->break_context = old;
}
void ExecBreak((ExecEnviron *e, AstElement *a) {
    longjmp(e->break_context, 1);
}
void ExecContinue((ExecEnviron *e, AstElement *a) {
    longjmp(e->break_context, 2);
}

Setjmp / longjmp可以很好地解决嵌套上下文的问题,但不能用于一般标签/ goto处理(因为它们允许跳转到循环中间)。如果你想处理它,你将不得不使用一个完全不同的执行引擎,并将AST转换为更像线性的字节码或线程代码。