如何使用玩具语言实现返回语句?

时间:2022-03-31 09:42:21

I recently made a toy programming language using C, Bison, Flex, and this post as a starting point. It looks a lot like Python except there's no colons or whitespace rules.

我最近用C、Bison、Flex和这篇文章做了一个玩具编程语言。它看起来很像Python,只是没有冒号或空格规则。

The code is here, but it's less important than the concept/algorithm I'm stuck on.

代码在这里,但是它比我所使用的概念/算法更重要。

I designed my abstract syntax tree just like Rudi did in the post linked above.

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

The trick is, I can't think of a great way to return from user-defined functions, or break out of loops. If I require only a single return statement at the end of a user-defined function, it's doable (actually this is what currently works for user-defined functions).

诀窍是,我想不出一个从用户定义函数返回的好方法,或者跳出循环。如果在用户定义函数的末尾只需要一个返回语句,那么它是可行的(实际上,这是目前对用户定义函数有效的方法)。

Example:

例子:

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

Example 2:

示例2:

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

3 个解决方案

#1


0  

Some popular stack-based languages push a value onto the stack which is then popped by the calling function. That might work for you.

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

Of course, this relies on having a function with a known return type. Python returns PyNone if the function is 'void' (i.e. returns nothing). A Python function returns only one value (which could be None, or an object or a tuple or whatever).

当然,这依赖于具有已知返回类型的函数。如果函数为“void”,则Python返回PyNone(即不返回任何值)。Python函数只返回一个值(可能为None,或对象或tuple或其他值)。

C-type languages also return only one value from a function.

c类型语言也只从函数返回一个值。

I think the point is that in a strong-typed language you always have a return value and you must always return one. In a weak typed language where you might or might return a value, you have one nonetheless.

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

#2


0  

The answer to that one depends a lot on the internals of how your code works.

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

When I wrote my Mote Compiler (a VB-like compiler), I called MoteEngine.prototype.runFuncStart, when a function started. I created a new object to hold all local variables and then I pushed that onto my stack.

当我编写Mote编译器(类似vb的编译器)时,当函数启动时,我调用了MoteEngine.prototype.runFuncStart。我创建了一个新的对象来保存所有的局部变量,然后我把它放到堆栈上。

MoteEngine.prototype.runFuncEnd cleaned up the stack.

moteengineering .prototype. runfuncend清理堆栈。

#3


0  

Well, it depends heavily on HOW exactly you implement your execution engine. If you follow Rudi's suggestion of implementing the execution engine with functions that recursively call each other to traverse the AST, then to implement a break or return, you need to return through several levels of C call stack. You can do this with setjmp/longjmp. So for example your code might look something like:

这在很大程度上取决于你如何实现执行引擎。如果您遵循Rudi的建议,即使用递归调用彼此来遍历AST的函数来实现执行引擎,那么要实现一个中断或返回,您需要返回几个级别的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 work adequately well for breaking OUT of nested context, but won't work for general label/goto handling (as those allow for jumping INTO the middle of a loop). If you want to deal with that, you'll have to use a completely different execution engine, and transform the AST into something more linear like bytecode or threaded code.

Setjmp/longjmp可以很好地打破嵌套上下文,但不适用于一般的label/goto处理(因为这些操作允许跳转到循环的中间)。如果您想处理这个问题,您将不得不使用一个完全不同的执行引擎,并将AST转换为更线性的东西,如字节码或线程代码。

#1


0  

Some popular stack-based languages push a value onto the stack which is then popped by the calling function. That might work for you.

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

Of course, this relies on having a function with a known return type. Python returns PyNone if the function is 'void' (i.e. returns nothing). A Python function returns only one value (which could be None, or an object or a tuple or whatever).

当然,这依赖于具有已知返回类型的函数。如果函数为“void”,则Python返回PyNone(即不返回任何值)。Python函数只返回一个值(可能为None,或对象或tuple或其他值)。

C-type languages also return only one value from a function.

c类型语言也只从函数返回一个值。

I think the point is that in a strong-typed language you always have a return value and you must always return one. In a weak typed language where you might or might return a value, you have one nonetheless.

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

#2


0  

The answer to that one depends a lot on the internals of how your code works.

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

When I wrote my Mote Compiler (a VB-like compiler), I called MoteEngine.prototype.runFuncStart, when a function started. I created a new object to hold all local variables and then I pushed that onto my stack.

当我编写Mote编译器(类似vb的编译器)时,当函数启动时,我调用了MoteEngine.prototype.runFuncStart。我创建了一个新的对象来保存所有的局部变量,然后我把它放到堆栈上。

MoteEngine.prototype.runFuncEnd cleaned up the stack.

moteengineering .prototype. runfuncend清理堆栈。

#3


0  

Well, it depends heavily on HOW exactly you implement your execution engine. If you follow Rudi's suggestion of implementing the execution engine with functions that recursively call each other to traverse the AST, then to implement a break or return, you need to return through several levels of C call stack. You can do this with setjmp/longjmp. So for example your code might look something like:

这在很大程度上取决于你如何实现执行引擎。如果您遵循Rudi的建议,即使用递归调用彼此来遍历AST的函数来实现执行引擎,那么要实现一个中断或返回,您需要返回几个级别的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 work adequately well for breaking OUT of nested context, but won't work for general label/goto handling (as those allow for jumping INTO the middle of a loop). If you want to deal with that, you'll have to use a completely different execution engine, and transform the AST into something more linear like bytecode or threaded code.

Setjmp/longjmp可以很好地打破嵌套上下文,但不适用于一般的label/goto处理(因为这些操作允许跳转到循环的中间)。如果您想处理这个问题,您将不得不使用一个完全不同的执行引擎,并将AST转换为更线性的东西,如字节码或线程代码。