Flat evaluation of Lisp s-expressions

I am trying to figure out how I can implement a Lisp score that is non-recursive . My C-Based Estimator Minimal Lisp file l1.c. However, several functions there again return to eval: eval, apply, evargs, evlist, as well as Lisp Ops defineFunc, whileFunc, setqFunc, ifFunc ...

I am trying to find a flat rating. Some possible ways that I could come up with are:

  • Convert to byte code and execute in VM
  • Add a flat Forth evaluator and do a Lisp evaluation in Forth, which is what lf.f does.
  • Another possibility might be to combine all the recursion functions in l1.c into one large switch cycle. Local variables will be combined into a heap structure, calls to recursive subfunctions will be implemented using the heap-based return stack.

My question is: are there algorithms / documents / implementations that perform flat evaluations in different ways. I am looking for an implementation that does not convert to byte code, but something similar to "pass through depth without recursion" using an eject stack. I would like to use the original s-expression.

Answer: when implementing the evaluator in c, you need to implement the whole in a flat outline, implement the return stack and stack frames manually, model the control flow using goto and switch (). Here is an example: flat .

+4
source share
5 answers

When implementing the Lisp -evaluator in C, the C compiler uses the stack to generate control flow of subprogram calls. To implement an appraiser without a stack in C, you need to write the control thread manually using goto and Switch ():

v * evargs(ctx *cctx, v *l, v *env) { struct v *r = 0; if (l) { r = eval(cctx, car(l),env); r = mkCons(r,evargs(cctx, cdr(l),env)); } return r; } 

gets

 case EVARGS_0: S_SET(0,0); /* EVARGS_0: r = 0; */ if (!(v=S(2))) /* if (l) */ goto ret; RCALL_EVAL(EVARGS_1, car(v)); /* r = < eval(cctx, car(l),env); > */ break; case EVARGS_1: S_SET(3,S(1)); /* EVARGS_1: < r = ... > */ RCALL_EVARGS(EVARGS_2, cdr(S(2))); /* r = mkCons(r, < evargs(cctx, cdr(l),env) > ); */ break; case EVARGS_2: S_SET(0,mkCons(S(3),S(1))); /* EVARGS_2: < r = mkCons(r, evargs(cctx, cdr(l),env) ); > */ goto ret; 
+2
source

A very important aspect of Lisp, and in fact an important aspect of many of the functional languages ​​that followed it, is that it is compositional. This means that the value of an expression is determined using the values ​​of its subexpressions β€” or, in other words, the definition of an estimate is what is essentially recursive. In non-functional languages ​​there are some differences, as in expressions against operators, but even there, expressions are not limited in any way, so recursion is also baked in the definition. Probably the only cases where the recursiveness of a language definition is not so obvious are assembly languages. (Although even there the definition of meaning, of course, will require induction.)

So, the struggle with the definition of eval recursion is what you lose. If you go with compilation to machine code, this code will be recursive (and the generating code will also be recursive). If you make the assessment through the Forth evaluator, then that evaluator will still be recursive. Even if you go with the CPS offer in another answer, you just get another stack encoding.

So, the bottom line is that the best you can get is some kind of stack encoding that doesn't directly use the computer stack - there is no significant difference, but you usually lose performance (since processors process the stack very efficiently and it will be encoded on the heap slower).

+4
source

I think the key insight that you might be missing is that in the lisp interpreter, a minimal set of functions is implemented as primitives that are not recursive. The exact set of primitives varies, but includes cons, car, cdr and the "most primitive" version of the application that performs one of these real functions, rather than an interpreted version.

You should look for original articles by John McCarthy and / or John Allen lisp 1.5

0
source

I remember that I have a book about the HOPE programming language. It is a functional language similar to ML. He had a full conceptual description of his compiler and thoughts on compilers of functional languages ​​in general. One of his comments was an argument regarding the Y-combinator. The author suggested that one of the possible ways to solve recursive functions would be to implement Y-combinator and transform each recursive function into a non-recursive one that can be executed for repetition using Y-combinator.

This will be a similar trick that you have in a special if form that provides (and usually is enough) for all kinds of lazy evaluations that may be required in the language. Similarly, you can restrict all functions to recursive, but introduces a special function Y , which allows you to start recursion.

0
source

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


All Articles