How to implement a return statement in a toy language?

I recently created a toy programming language using C, Bison, Flex and this post as a starting point. It is very similar to Python, except for colon or space rules.

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

I created my abstract syntax tree like Rudi in connection with the message above.

The trick is that I cannot come up with a great way to return from user functions or exit cycles. If I need only one return at the end of a user-defined function, it is executable (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:

 def mean(somelist) if len(list) == 0 then return 0 # throw error else return sum(somelist) / len(somelist) end end 
+4
source share
3 answers

Some popular stack-based languages โ€‹โ€‹push the value onto the stack, which is then called by the calling function. This might work for you.

Of course, this depends on the availability of a function with a known return type. Python returns PyNone if the function is "void" (ie, returns nothing). The Python function returns only one value (which may be None, or an object or a tuple, or something else).

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

I think the fact is that in a strongly typed language you always have a return value, and you should always return it. In a weak typed language, where you could or could return the value, you still have it.

0
source

The answer to this question depends a lot on how your code works.

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

MoteEngine.prototype.runFuncEnd cleared the stack.

0
source

Well, that depends a lot on how you implement your execution engine. If you follow Rudiโ€™s proposal to implement a execution mechanism with functions that recursively call each other to pass AST, then to implement break or return you need to return through several levels of the C call stack. You can do this with setjmp / longjmp. For example, your code might look something like this:

 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 works well enough to split OUT the nested context, but will not work for general processing of / goto tags (since they allow you to jump INTO in the middle of the loop). If you want to deal with this, you will have to use a completely different execution mechanism and convert the AST to something more linear, like a bytecode or a threaded code.

0
source

Source: https://habr.com/ru/post/1435527/


All Articles