JKForth's Utility PackageThe various *.fth files in the JKForth source contain genuine Forth source code that implements a number of utility functions, and even some core Forth words. I'll take the loops1.fth file as an example. ( Standard loop-control words. ) : begin here ; immediate : uloop compile abranch , ; immediate : while [compile] if ; immediate : repeat [compile] uloop [compile] then ; immediate : until compile not [compile] if [compile] uloop [compile] then ; immediate This file is a veritable frenzy of compiler-extension. To undertand it, you must first realize that the word "immediate" marks the last word in the dictionary (eg the word most recently compiled) as an immediate word; such words are executed at compile time rather than being compiled into the current definition. The "begin" word, for example, simply pushes the current dictionary address onto the stack, at compile time. Of course, when the word being compiled is run, that address will be the current virtual instruction pointer value. Thus, "begin" saves a bit of runtime information on the stack for future use by the compiler. The word "uloop" is an unconditional loop: it compiles an absolute branch to whatever address is on the stack (the word "," compiles the top of the stack into the dictionary, so the phrase "compile abranch ," compiles a branch to the address on the stack). Thus, the code : quackers begin quack uloop ;compiles into 10000: QUACK 10002: ABRANCH 10004: 10000(assuming the word-list portion of "quackers" happens to start at address 10000). At compile time, "begin" puts the current dictionary address (10000) on the stack; "quack" is then appended to the dictionary, and HERE incremented to 10002. Then "uloop" is called, which explicitly compiles the address of "abranch" into the dictionary using the phrase "compile abranch", then compiles the 10000 left on the stack by "begin" using the "," word. Isn't this fun? "So," you're wondering, "what's with the compile/[compile] stuff?" Good question. The "compile" word compiles code that compiles the next word in the input stream into the current definition. For example, when the definition of "uloop" uses the phrase "compile abranch", this causes "uloop", when executed, to compile a reference to "abranch" into whatever word is being compiled at the time. "[compile]", by contrast, immediately performs a normal compilation of the next word in the input stream. "Silly," you say, "because that's what the compiler does anyway." True. However, "[compile]" can be used to force normal compilation of immediate words. That is, if you want a word to contain a call to an immediate word, you use "[compile]" to achieve that. Therefore, the definition of "while", above, indicates that "while" is just being defined as a synonym for "if". Why would we want that? Well, it turns out that the semantics of "if" and "while" are identical in Forth, but it's still nice to be able to write : travel begin not_at_destination while take_step repeat ;rather than : travel begin not_at_destination if take_step uloop then ;Incidentally, Forth is a very aggressively postfix language! The correct way of "mentally parenthesizing" the above code would be: : travel (begin ((not_at_destination while) take_step) repeat)So you can think of C code:
while (!at_destination) { take_step(); }
Whew. I hope this little tour of JKForth has been as interesting for you as writing the code was for me! The End (For Now) |
Last changed: 03-02-06 23:56:38 |
Questions and comments to Joseph Knapka.