uho / preForth

a minimalistic Forth kernel that can bootstrap
GNU General Public License v3.0
72 stars 9 forks source link

Less use of heads #15

Open nickd4 opened 2 years ago

nickd4 commented 2 years ago

Background: The heads table is like a cut-down version of the Forth dictionary, that only records the entry point of each word, rather than the entry point, name and backlink. Heads are needed for mapping token numbers in *.seed files to addresses.

As mentioned in the previous PR, I wanted to make it so that heads are not created during seedForthInteractive -- because now that execution tokens are simply addresses, heads are not needed once we have loaded and started the *.seed file.

To do this, I have modified the new and create words so that creation of heads is now a caller responsibility. The revised new and create words simply return the here address as it was before creating the code field of the newly created token.

For reference see, for example, /i386/seedForth-i386.pre where it defines the new and create words. The updated way:

: new ( -- xt )
   here  lit enter , ;

: create ( -- xt )
   0 , \ dummy does> field
   here lit dovar , ;

And then see /common/seedForth.pre where it refers to the new word. The updated way:

: fun ( -- )
   new h,  compiler ;

It's pleasing that we can now do something sensible with the address returned by new, rather than the old new drop.

Inserting a call to h, after create is harder. Consider the seedForth code in /common/seedForthRuntime.seedsource:

Definer Variable ( <name> -- )    create ( x ) drop 0 , ;

Compare with the corresponding textual Forth code in /common/runtime.forth which is used with seedForthInteractive:

: Variable ( <name> )
    Create 0 , ;

My analysis is that Definer is a seedForthism, which does not exist in standard Forth, and which behaves like :, but gives the tokenizer a hook so that it can capture the name of the new word being defined, here Variable, create a token for it, and thus keep the token numbering in sync. And we can see that the usage is identical, i.e. the two definitions are the same, except that : is replaced with Definer in the seedForth source. To preserve this equivalence, what I've done is to have the tokenizer insert the sequence here h, in the start of the Definer-body, somewhat as if the user had executed the following definition:

Definer Variable ( <name> -- )    here h,  create ( x ) drop 0 , ;

Of course we could make this a user responsibility, in which case the user would have to write this (somewhat more pleasing):

Definer Variable ( <name> -- )    create ( x ) h, 0 , ;

But, I considered it too difficult for the user if they have to deviate too much from standard Forth. What do you think about it?

Update: I have just noticed that the create ( x ) drop sequence is also a seedForthism, as compared with standard Forth where Create doesn't seem to return an execution token (see the /common/runtime.forth snippet I showed above). Because of this we might have some more latitude to change things, such as requiring the user to call create (x) h,. But I'm not sure.