JimBoyd wrote:
In solving this problem I came up with two different solutions which resulted in two versions of the metacompiler. They were the same except for what feature was added.
For reference I will refer to the version of the metacompiler without either of these solutions as the base metacompiler. It is this base upon which version A and version B are built.
First, some words in the base metacompiler were cleaned up.
The original source for PNAME and VHEADER .
Code:
: PNAME ( -- )
1 VALLOT 1 PNAMES +!
COLS ?CR ." PADDED: R"
HERE S? CR ;
: VHEADER ( >IN -- >IN )
VNAME TFIND NIP
ABORT" REDEFINITION IN TARGET"
HEAD @
IF
DUP >IN !
HERE C@ THERE + 4 +
SPLIT DROP 0=
IF PNAME THEN
WLINK VADD
VNAME C@ THERE $80 VTOGGLE
VALLOT THERE $80 VTOGGLE
1 VALLOT
EXIT
THEN
THERE 1+ SPLIT DROP 0=
IF PNAME THEN
1 HEADLESS +! ;
HOST
and the improved source.
Code:
: PNAME ( ADR -- )
1+ SPLIT DROP ?EXIT \ If the low byte of the address is not $FF then exit.
1 VALLOT 1 PADDED +! \ Pad the virtual header and increment the padded count.
COLS ?CR ." PADDED: R" \ If not at left column perform carriage return
HERE S? CR ; \ before displaying the name of the padded word.
: VHEADER ( -- )
>IN @ VNAME TFIND NIP \ Save >IN. If the parsed name already exists
ABORT" REDEFINITION IN TARGET" \ in the target vocabulary then abort.
HEAD @ \ If HEAD is on
IF \ create header.
>IN ! \ Restore >IN to parse the name again.
HERE C@ THERE + 2+ 1+ PNAME \ Avoid the indirect jump bug.
WLINK VADD \ Create the link field.
VNAME C@ THERE $80 VTOGGLE \ Create the name field with high bit set
VALLOT THERE $80 VTOGGLE \ for count and last character.
1 VALLOT \
EXIT
THEN \ If HEAD is off
DROP 1 HEADLESS +! \ increment the count of headerless words
THERE PNAME ; \ and avoid the indirect jump bug.
PNAME now takes an address and pads the header, if necessary, to avoid the code field straddling a page boundary.
VHEADER now no longer requires the value of >IN on the data stack and it returns nothing.
I will present the first solution, metacompiler A.
Here is the base metacompiler's vocabulary structure.
Code:
FORTH
META
SHADOW
FORTH
ASSEMBLER
EDITOR
ASSEMBLER
The vocabulary RUN is added to the META vocabulary.
Code:
FORTH
META
SHADOW
FORTH
ASSEMBLER
RUN
EDITOR
ASSEMBLER
The variable ALIASES is added to keep a count of how many words have an alias.
The new word ALIAS creates a header in virtual memory and a target word (a handle) in the RUN vocabulary. ALIAS also switches off header creation in virtual memory for the next word with NH .
Code:
: ALIAS ( ++ )
ORDER@ [META] RUN DEFINITIONS \ Save the search order and
HEADER NH \ create header in RUN vocabulary.
ORDER! \ Restore the search order.
TRUE HEADLESS +! 1 ALIASES +! ; \ Correct the headless count and
\ increment the count of aliases.
ALIAS is used like this:
Code:
ALIAS DO
CODE (DO) ( LIMIT START -- )
...
...
END-CODE
Here is the base metacompiler's MCOMPILE .
Code:
CODE MCOMPILE ( -- )
>FORTH \ Shift to high level Forth.
?COMP \ Abort if not compiling.
R> DUP 2+ >R @ \ Get the CFA of next word in Forth thread containing MCOMPILE
>NAME COUNT $1F AND >HERE CLIP \ and use the name field to create a search string at HERE .
TFIND ?TARGET M, ; \ Search the TARGET FORTH vocabulary and abort if not found.
\ If found, compile to virtual memory with M, .
and the modified MCOMPILE .
Code:
CODE MCOMPILE ( -- )
>FORTH \ Shift to high level Forth.
?COMP \ Abort if not compiling.
R> DUP 2+ >R @ \ Get the CFA of next word in Forth thread containing MCOMPILE
>NAME COUNT $1F AND >HERE CLIP \ and use the name field to create a search string at HERE .
TFIND TRUE <> \ If not found or if immediate
IF
DROP HERE [META] \ drop the result and use the string at HERE
[ ' RUN >BODY ] LITERAL \ to search the RUN vocabulary.
VFIND TRUE <> \ If still not found
ABORT" RUN-TIME NOT FOUND" \ abort.
THEN
M, ; \ If found, compile to virtual memory.
The modified MCOMPILE for metacompiler A still creates a search string from the name field of the next word in it's definition.
Code:
: TEST
MCOMPILE (TEST) ; IMMEDIATE
When TEST is executed, MCOMPILE will create the search string "(TEST)" at HERE .
The TARGET FORTH vocabulary is searched by TFIND .If the sought word is not found or if it is immediate, the result is dropped and the RUN vocabulary is searched. Once more, the result has to be for a word which is not immediate.
VFIND does not search parent vocabularies.
This version of the metacompiler allows changing the names of run-time words with ALIAS ; however, there is one downside.
Using DO and it's run-time as an example.
Code:
ALIAS DO
CODE (DO) ( LIMIT START -- )
...
...
END-CODE
There are now two handles on the host system for the same target word in virtual memory, this is not the problem. If the name of the run-time for DO on the host system does not match either one, a new alias is needed.
Code:
ALIAS DO \ Name of run-time for new kernel. Creates handle DO in RUN vocabulary and header DO in virtual memory.
ALIAS D \ Name of run-time in host system. Creates handle D in RUN vocabulary. header creation in virtual memory suppressed.
CODE (DO) \ Name of run-time used in kernel source. Creates handle (DO) in TARGET FORTH vocabulary. header creation in virtual memory suppressed.
...
...
END-CODE
This creates three handles for the same word in virtual memory, one in the TARGET FORTH vocabulary and two in the RUN vocabulary.
There is only one header created in virtual memory for this word since ALIAS suppresses header creation in the following word.