Nп/п : 22 из 93
 От   : none) (albert                       2:5075/128        17 июл 23 10:56:19
 К    : All                                                   17 июл 23 12:03:12
 Тема : tail call optimisation, lisp implemented in Forth
----------------------------------------------------------------------------------
                                                                                 
@MSGID:
93b6a1f6
@REPLYADDR none) (albert
@REPLYTO 2:5075/128 none) (albert
@CHRS: CP866 2
@RFC: 1 0
@RFC-Message-ID:

@TZUTC: 0200
@TID: FIDOGATE-5.12-ge4e8b94
I`m following the instructions of mal (clojure)
    https://github.com/kanaka/mal

The instruction in mal step 5 :
Several of the special forms that you have defined in EVAL end up
calling back into EVAL. For those forms that call EVAL as the last
thing that they do before returning (tail call) you will just loop
back to the beginning of eval rather than calling it again.

I come to realize that an implementation of a lisp without tail cal
optimisation is a joke. So this is all but mandatory.

In the context of my Forth implementation a function, say `` if ``
is looked up in the environment, it is special, so it leaves
evaluation of the rest of the list to the discretion of `` if ``.
The bottom line is that there is no way to construct an infinite
loop around special functions, as proposed,  in the vein of
"
  while true:    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    if not list?(ast): return eval_ast(ast, env)
    if empty?(ast): return ast
    switch ast[0]:
      `def!:        return env.set(ast[1], EVAL(ast[2], env))
      `let*:        env = ...; ast = ast[2] // TCO
      `do:          ast = eval_ast(ast[1..-1], env)[-1] // TCO
      `if:          EVAL(ast[1], env) ? ast = ast[2] : ast = ast[3] // TCO
      `fn*:         return new MalFunc(...)
      _default_:    f, args = eval_ast(ast, env)
                    if malfunc?(f): ast = f.fn; env = ... // TCO
                    else:           return apply(f, args)
"
In Forth `` eval `` is a colon definition , that is, a machine code
pointer and an array of tokens. The machine code takes care that
the tokens are interpreted via the SI register (x86)
Nesting is accomplished by storing SI in the return stack
pointed to by the EBP register.
These are the instructions.

DOCOL:  LEA     EBP,[EBP - (CW*(1))]                     ; *
        MOV     [EBP],ESI                                ; *
         MOV     ESI,[EAX+(CW*(D_HOFFSET - C_HOFFSET))]  ; *
        LODSD                  ; NEXT
        JMP     DWORD[EAX]

Nesting is  marked with *.
The NEXT code takes care that the following token is fetched an
executed. This changes SI.

TCO (tail call optimisation) is accomplished by leaving out the
nesting.
DOCOLPRIME:
        LODSD                  ; NEXT
        JMP     DWORD[EAX]

So we have
: eval  .... ;
and we can define an alias and we fix the code pointer CFA .
    `eval ALIAS eval-prime
    DOCOLPRIME  `eval-prime >CFA !

So we can change each terminating eval with eval-prime without
bothering with other functions that may or may not be special.

How cool is that!

Groetjes Albert
-- 
Don`t praise the day before the evening. One swallow doesn`t make spring.
You must not say "hey" before you have crossed the bridge. Don`t sell the
hide of the bear until you shot it. Better one bird in the hand than ten in
the air. First gain is a cat spinning.            - the Wise from Antrim -
--- trn 4.0-test77 (Sep 1, 2010)
 * Origin: KPN B.V. (2:5075/128)
SEEN-BY: 5001/100 5005/49 5015/255 5019/40 5020/715
848 1042 4441 12000
SEEN-BY: 5030/49 1081 5058/104 5075/128
@PATH: 5075/128 5020/1042 4441



   GoldED+ VK   │                                                 │   09:55:30    
                                                                                
В этой области больше нет сообщений.

Остаться здесь
Перейти к списку сообщений
Перейти к списку эх