ForthHub/forth

POSTPONE

drom opened this issue · 17 comments

drom commented

The following code:

T{ : GT1 123 ; -> }T
T{ : GT4 POSTPONE GT1 ; IMMEDIATE -> }T
T{ : GT5 GT4 ; -> }T
T{ GT5 -> 123 }T

Lead to the core test failure:

Error: in line: 647 T{ : GT5 GT4 ; -> }T  expected [ 291 ] to deeply equal [].
Error: in line: 648 T{ GT5 -> 123 }T  expected [] to deeply equal [ 291 ].

Other parts of the test with POSTPONE are passing.

T{ : GT6 345 ; IMMEDIATE -> }T
T{ : GT7 POSTPONE GT6 ; -> }T
T{ GT7 -> 345 }T

and

T{ : NOP : POSTPONE ; ; -> }T
T{ NOP NOP1 NOP NOP2 -> }T
T{ NOP1 -> }T
T{ NOP2 -> }T
drom commented

Here is some words from the standard:

image


That was brief!


image


image


image


image


image

If you have a traditional Forth with just default and immediate words, POSTPONE FOO should be the same as COMPILE FOO if FOO has default compilation semantics, and the same as [COMPILE] FOO if FOO is immediate.

It seems your problem is with POSTPONEing non-immediate words. If bar is not immediate

: foo   postpone bar ;

should have the same effect at run time as

: foo   ['] bar postpone literal  ['] compile, compile, ;

I.e. when foo runs, it compiles bar into the definition that is being compiled that time. (If bar were immediate, it would be compiled into foo instead.)

Is your head spinning yet? ;-)

drom commented

Here is 'very helpful' description of COMPILE, from the standard:

image

Note: COMPILE, is just the same as , in a traditional (indirect or direct) threaded Forth.

drom commented

Yes, that is my issue. I have JS-Threaded Forth 😏
Looks like I need to detect if the word is immediate or not.
Current code is too simple:
https://github.com/drom/forth/blob/master/lib/cfg.js#L65-L74

def('postpone', function () {
        var w = this.io.word('\\s');
        var wordLink = this.O[w.toLowerCase()];
        if (wordLink) {
           this.pos.push(ast.callword(wordLink.index));
        } else {
            cxt.log('word: ' + w + ' is not defined');
            throw new Error;
        }
    }, cxt, { immediate: true });

I see your compiler is building JavaScript code at run time. So COMPILE, should take an xt and generate code to call that xt.

POSTPONE IF should generate code to call IF.

POSTPONE + should generate code to generate code to call +. Simple!

drom commented

That is what I am doing. But as you can see in the failing case:

HEX
T{ : GT1 123 ; -> }T
T{ : GT4 POSTPONE GT1 ; IMMEDIATE -> }T
T{ : GT5 GT4 ; -> }T
T{ GT5 -> 123 }T
SEE GT4

will be:

gt4: function () {
    this[152]();
}

Lead to the core test failure:

Error: in line: 647 T{ : GT5 GT4 ; -> }T  expected [ 291 ] to deeply equal [].
Error: in line: 648 T{ GT5 -> 123 }T  expected [] to deeply equal [ 291 ].

You ca run it by yourself:
http://drom.io/forth

What does your GT5 do? It seems it doesn't leave 123 on the stack. Why doesn't it do that?

GT4 is an immediate word. When GT4 runs it should compile GT1 into the current definition. So : GT5 GT4 ; should have the same effect as : GT5 GT1 ;.

Also, where does 291 come from?

drom commented

123 HEX = 291 DEC

Well, I tried http://drom.io/forth, and typed in the test code.

see GT4 clearly shows the generated JavaScript code is wrong.

drom commented

Looks like POSTPONE has to check if the WORD, it is POSTPONing is immediate or not.

if (immediate) {
    compile the WORD into the current definition.
} else {
   compile the code that will compile the WORD into current definition when being executed.
}
drom commented

Thanks, fixing. 7 more issue to go ;)

drom commented
T{ : GT1 123 ; -> }T
T{ : GT4 POSTPONE GT1 ; IMMEDIATE -> }T
T{ : GT5 GT4 ; -> }T
T{ GT5 -> 123 }T

GT1 is a word with default compilation semantics (i.e., you get them from Section 3.4.3.3 cited above. POSTPONE GT1 appends the compilation semantics of GT1 to GT4, whereas just writing GT1 would append the execution semantics of GT1 to GT4. Or, in other words, POSTPONE GT1 appends the appending of the execution semantics of GT1 to GT4. When GT4 is performed in the next line, it appends the execution semantics of GT1 to GT5. And when GT5 is performed in the next line, it performs the execution semantics of GT1 and pushes 123 on the stack.

--- anton

from here: https://groups.google.com/d/msg/comp.lang.forth/BlA-81dmWow/j81maLLaAAAJ

drom commented
T{ : GT6 345 ; IMMEDIATE -> }T
T{ : GT7 POSTPONE GT6 ; -> }T
T{ GT7 -> 345 }T

GT6 is an immediate word with the following compilation semantic == execution semantic is to push 345 to the stack.
POSTPONE GT6 appends this compilation / execution semantic of GT6 to GT7 by just compiling call to GT6.
GT7 just executes this call.

T{ : NOP : POSTPONE ; ; -> }T
T{ NOP NOP1 NOP NOP2 -> }T
T{ NOP1 -> }T
T{ NOP2 -> }T

; is an immediate word with the following compilation semantic == execution semantic of finishing the colon definition.
POSTPONE ; appends this compilation / execution semantic of ; to NOP by just compiling call to ;.
NOP NOP1 just executes this call.