uho / preForth

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

Simplify bootstrapping #11

Open nickd4 opened 2 years ago

nickd4 commented 2 years ago

This change attempts to simplify bootstrapping from gForth/SwiftForth, noting that I don't have SwiftForth and only test gForth.

With the original bootstrapping method, we were creating a very authentic preForth environment within the host Forth, by creating a new wordlist and then loading the entire preForth runtime system into it, except for a few essential "borrowed" words.

The files that controlled this process were load-i386-preForth.fs, load-preForth.fs and borrow.fs. Collectively they were quite complicated, and not being an experienced Forth programmer I didn't understand the details of borrowing in particular.

There was also another thing that I found confusing, which is the way that load-preForth.fs was defining pre and code so they skip their body. Initially I thought, shouldn't it be echoing the body to the output, instead of skipping it? But after a bit of thought I worked out the reason: Under an interpretive Forth system, you would expect a word like pre or code to be defined and then executed. But since preForth is only compiled, it's not possible to execute a word that was defined in the same run. Instead the words like pre and code have to be already defined and part of the compiler that is compiling the source. So this leads to the unusual pattern where pre and code are first executed, and then later on, compiled to use in next stage. And this indirectly explains why pre and code are being ignored in the initial bootstrap, as we are only compiling them. (!)

Thinking about it some more, I realized that the more elegant way of handling this situation is not to include the pre and code definitions in the initial bootstrap at all (rather than defining them to no-ops and then including them). Or in other words, we can use the primitives supplied by the host Forth instead of the asm primitives, in the bootstrap compile run. So we can omit the file preForth-i386-rts.pre from the bootstrap compile run. And the same logic applies to most of the high-level Forth code in preForth-rts.pre, it's already available in the host Forth with a few exceptions like case?, tab, *10 and stack strings.

Therefore, my new approach to bootstrapping is to split out the nonstandard words from preForth-rts.pre into preForth-rts-nonstandard.pre so that they can be used for the bootstrap compile run, whilst preForth-i386-rts.pre and preForth-rts.pre are omitted. I also have an extremely cut down version of borrow.fs / load-preForth.fs which I call preForth-bootstrap.fs. All this does is to deal with the tail call syntax which is nonstandard. So all of the nonstandard parts are now in either preForth-bootstrap.fs or preForth-rts-nonstandard.pre, with the former being not needed under pure preForth and the latter needed.

I also have an extremely cut down version of load-i386-preForth.fs which I call preForth-cold.fs and all this does is to call cold and then bye (mirroring what the main function does in compiled preForth asm code). The actual including of sources for the bootstrap compile run is now done from the gForth command line (I assume / hope this will also work for SwiftForth).

Thus the bootstrap compile command is now:

        cat \
preForth-i386-rts.pre \
preForth-rts-nonstandard.pre \
preForth-rts.pre \
preForth-i386-backend.pre \
preForth.pre \
|$(HOSTFORTH) \
preForth-bootstrap.fs \
preForth-rts-nonstandard.pre \
preForth-i386-backend.pre \
preForth.pre \
preForth-cold.fs \
>$@

Note that redefinition warnings are generated for some words like cold that have different meanings under the host Forth than the preForth compiler. I could suppress these, as was done in the original load-preForth.fs, but I thought it not necessary. If the initial bootstrap process is a bit ugly it does not really matter, as once bootstrapped you can use the preForth way instead.

Once again, the changeset will look larger than it really is, so please merge the previous PRs then have github recalculate it.