I would like to add it to my hobby system, but it's a bit of a brain
mangler for me. It appears to be a case (ha case!) of compiling IFs
(and by inference, 0BRANCH/BRANCH) words.
I already have IF..ELSE..THEN done (immediate words), DO..LOOP etc.
Regards
Mark
What about the ANS Forth example near the end of this section:
http://www.complang.tuwien.ac.at/forth/dpans-html/dpansa3.htm#A.3.2.3.2
-- David
Thanks for the link David.
It looks complex, because the example CASE..OF uses POSTPONE, which I
don't have.
It is a little strange. For example, there is the phrase POSTPONE OVER
- strange - I thought POSTPONE was for forcing IMMEDIATE words to be
compiled? OVER isn't immediate, so I don't know what is going on
there! Of course, I could just be completely barking up the wrong
tree!
Mark
It's a question of when OVER executes. For example
: OF ( #of -- orig #of+1 / x -- )
1+ ( count OFs )
>R ( move off the stack in case the control-flow )
( stack is the data stack. )
POSTPONE OVER POSTPONE = ( copy and test case value)
POSTPONE IF ( add orig to control flow stack )
POSTPONE DROP ( discards case value if = )
R> ( we can bring count back now )
; IMMEDIATE
OF is IMMEDIATE, and executes in a definition by compiling OVER,
etc., into the definition. OVER executes when the definition
into which it was compiled is executed.
-- David
Thanks for the explanation David.
So, is POSTPONE basically a synonym for ['] ... , ?
I.e. POSTPONE OVER ---> ['] OVER ,
ANd for overriding immediate words, we have [COMPILE]
From my source code:
;[ [COMPILE] ( -- )
; *while compiling*, '[COMPILE] word' compiles 'word' if it would
otherwise be
; IMMEDIATE.
icomph data tick2h,>8009 ; note immediate flag
text '[COMPILE] '
icomp data docol
data word ; get a word from TIB
data find ; find it in the dictionary
data cfa ; convert dictionary address to CFA
data comma ; compile the CFA to HERE
data exit
Regards
Mark
Any word has "compilation semantics", the big word which means: "what
happens when the word is encountered while compiling". For an immediate
word, the word is executed. For non-immediate words, the "default
compilation semantics" are: "just add a call to that word into the
word being currently defined".
POSTPONE FOOBAR means "look at FOOBAR but do not execute its compilation
semantics; instead, compile into the current word the action of running
the compilation semantics".
So that if you do this:
: BLAH POSTPONE OVER ; IMMEDIATE
: HOP BLAH ;
then you define HOP as a kind of alias for OVER. Basically:
-- BLAH is defined to do, when executed, whatever the compiler would do
if it encountered a plain OVER while compiling.
-- BLAH is then made IMMEDIATE, so that, when encountered by the
compiler, it is itself executed immediately.
-- When compiling HOP, the compiler stumbles upon BLAH, which is immediate.
The compiler then runs BLAH, and BLAH performs its job, which is to
do what the compiler would have done when encountering OVER. In other
words, this behave as if I had written:
: HOP OVER ;
In pre-ANS Forth, you would have used COMPILE or [COMPILE], depending
on whether the target word is immediate or not. POSTPONE replaces both,
which avoids having to think about immediacy of such words; moreover,
it makes it somewhat easier for Forth systems which produce native
optimized code.
Unfortunately, POSTPONE does not replace LITERAL. You cannot write:
: BLUH POSTPONE 42 ; IMMEDIATE
although this would have made sense (this is like considering that
numerical constants are predefined words). ANS does not prevent a
Forth to have a POSTPONE which can handle numerical constants, but
it does not mandate it either, and at least Gforth does not implement
it that way. Hence, for portability, you have to write it as:
: BLUH 42 POSTPONE LITERAL ; IMMEDIATE
which I find somewhat inelegant and cumbersome.
--Thomas Pornin
For many words it is, but not in general: "POSTPONE FOO" compiles the
compilation semantics of FOO, whereas "['] FOO COMPILE," compiles the
compilation of the interpretation/execution semantics of FOO. For
many words, like OVER, the compilation semantics are to compile the
execution semantics, and then they are equivalent.
But for other words, e.g., IF, there are no interpretation/execution
semantics, so ['] IF may give an error or a different result from what
you expected.
And yet other words (e.g., S") have interpretation semantics, but the
compilation semantics are not to compile these interpretation
semantics; for these words it's not very advisable (and sometimes
non-standard) to tick or POSTPONE them anyway, because several systems
implement them in a STATE-smart way, resulting in wrong behaviour if
the STATE does not match the intended semantics.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2009: http://www.euroforth.org/ef09/
If I understand that correctly, if you have Forth-79/Forth-83 COMPILE
and [COMPILE] in the system, you'd [COMPILE] the immediate words and
COMPILE the regular words.
AFAIU, that's basically what POSTPONE does in a simple implementation.
If there is any magic while compiling in a more complex implementation
it also makes sure that it exports the magic connected with a word,
but if its a simple model compiler, checking immediacy after FIND and
doing the right thing for that status would seem to do it.
And an addition note from Thomas Pornin that POSTPONE may, though it
is not required to, POSTPONE a non-word as a literal, which would give
something to do for each of the three possible status returns from
FIND.
Something like that is in my SEE
SEE HERE works like you expect, but SEE 4711 works too, provided 4711
is a valid execution token. Otherwise it resorts to disassembly.
--
Coos
CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html
Sure:
SCR # 79
0 ( :NONAME CASE MARKER )
1 WANT POSTFIX
2 : :NONAME "NONAME" POSTFIX : LATEST DUP HIDDEN !CSP ; \ ISO
3
4 \ ISO
5 : CASE 0 ; IMMEDIATE
6 : OF POSTPONE OVER POSTPONE =
7 POSTPONE IF POSTPONE DROP ; IMMEDIATE
8 : ENDOF POSTPONE ELSE ; IMMEDIATE
9 : ENDCASE POSTPONE DROP BEGIN DUP WHILE POSTPONE THEN REPEAT
10 DROP ; IMMEDIATE
11
12 \ ISO
13 : MARKER HERE CREATE , DOES> @ 'FORGET-VOC FOR-VOCS DP ! ;
14
[ BTW nothing to do with ITC. Never mind the extra words,
it is better to paste in working code literally.]
>
>Regards
>
>Mark
Groetjes Albert
--
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
Here is the source for FIG-Forth published with the original
article
(Forth Dimensions, Vol. II, No. 3, pp. 37-40.). The ?PAIRS word
was
FIG-Forth's way of implementing a small amount of syntax checking.
: CASE ?COMP CSP @ !CSP 4 ; IMMEDIATE
: OF 4 ?PAIRS COMPILE OVER COMPILE = COMPILE OBRANCH
HERE 0 , COMPILE DROP 5 ; IMMEDIATE
: ENDOF 5 ?PAIRS COMPILE BRANCH HERE 0 ,
SWAP 2 [COMPILE] ENDIF 4 ; IMMEDIATE
: ENDCASE 4 ?PAIRS COMPILE DROP
BEGIN SP@ CSP @ = 0=
WHILE 2 [COMPILE] ENDIF REPEAT
CSP ! ; IMMEDIATE
Alberto
eforth does it nicely somehow, the word elseif
comes to mind
I think that approach makes it very easy to sort of optimize it
If your 16-bit ITC Forth is running on an 8086, then I would recommend
building an array of the literals that you are comparing against, and
an associated array of xt values for the words that handle each case.
Use the string lookup instruction to find the index of the matching
literal in the first array, and then call the associated xt of the
case word at the same index in the second array.
I have seen this done as an add-on to 16-bit UR/Forth, but I didn't
write the code myself. Those CISC string instructions can provide a
huge speed-up on the 8086, but I wouldn't recommend their use on the
Pentium because it is a RISC processor under the hood and doesn't
support those old CISC instructions very well.
Hi Albert, could you explain a couple of things:
Line 1: WANT POSTFIX - what does this do/mean?
Line 2: :NONAME .... - that is a complete mystery! What is going
on?
Line 13: A word MARKER is defined, but it isn't referenced in the code
above it...
Regards
Mark
Hi Alberto,
This version also has some 'dependencies'...
?COMP - ???
CSP and CSP! (are these something to do with data stack pointers? I
have S0 as a simple variable, which you can @ and ! int the normal
way)
?PAIRS we discussed a while. I believe ?PAIRS leaves a boolean on the
stack? ?PAIRS ( count -- boolean )
Regards
Mark
Hi Bruce,
Yes, it's a mish-mash, mainly 83. It's largely based on the Jones
Forth articles:
Although I only took the text descriptions of *how* things work (which
is very well written) - I largely ignored the Intel Assembler, as it's
horrid! The textual information is good enough.
Regards
Mark
: CASE IMMEDIATE 0 ; ( push 0 to mark the bottom of the stack )
: OF IMMEDIATE
' OVER , ( compile OVER )
' = , ( compile = )
[COMPILE] IF ( compile IF )
' DROP , ( compile DROP )
;
: ENDOF IMMEDIATE
[COMPILE] ELSE ( ENDOF is the same as ELSE )
;
: ENDCASE IMMEDIATE
' DROP , ( compile DROP )
( keep compiling THEN until we get to our zero marker )
BEGIN
?DUP
WHILE
[COMPILE] THEN
REPEAT
;
It also has :NONAME
: :NONAME
0 0 CREATE ( create a word with no name - we need a dictionary header
because ; expects it )
HERE @ ( current HERE value is the address of the codeword, ie. the
xt )
DOCOL , ( compile DOCOL (the codeword) )
] ( go into compile mode )
;
It's been a while since I read the article so I will have another
look.
Mark
: CASE 0 ; IMMEDIATE
: OF POSTPONE OVER POSTPONE =
POSTPONE IF POSTPONE DROP ; IMMEDIATE
: ENDOF POSTPONE ELSE ; IMMEDIATE
: ENDCASE POSTPONE DROP BEGIN DUP WHILE POSTPONE THEN REPEAT
DROP ; IMMEDIATE
This is the cleanest solution, but it has a couple of dependencies,
such as the data stack being the compilation stack and if-sys
etc. never being zero.
A digression: one thing that has always been hard for me to understand
about CASE is the point of
: ENDOF POSTPONE ELSE ; IMMEDIATE
After all
case
1 of ... else
2 of ... else
...
endof
is just as easy to read as
case
1 of ... endof
2 of ... endof
...
endof
I think it's just a hangover from the old fog-FORTH idea of "compiler
security" where conditionals had to be "correctly matched". And of
course you can mix things up and do
case
1 of ... else
2 of ... else
dup 0 10 within if drop ... else
...
endof
Andrew.
<SNIP>
>
>Unfortunately, POSTPONE does not replace LITERAL. You cannot write:
> : BLUH POSTPONE 42 ; IMMEDIATE
>although this would have made sense (this is like considering that
>numerical constants are predefined words). ANS does not prevent a
>Forth to have a POSTPONE which can handle numerical constants, but
>it does not mandate it either, and at least Gforth does not implement
>it that way. Hence, for portability, you have to write it as:
> : BLUH 42 POSTPONE LITERAL ; IMMEDIATE
>which I find somewhat inelegant and cumbersome.
I think it is a good thing to use an explicit literal.
And it is probably to be calculated anyway.
In non-portable ciforth code I'm moving away from any word that
combines parsing and some other action (except generating a constant).
." aap" --> "aap" TYPE
' aap / ['] aap --> 'aap
[ this is not smarter than being able to use 234 in both modes ]
: ..... ' .... ; -->
: ...... PARSE-WORD NAME-FIND .... ;
[ actually in ciforth NAME FOUND ]
NAME-FIND draws attention to the fact that during execution of
this word you are looking something up in the dictionary.
This is related to avoiding smart words.
Smart words results in even more problems if postponed.
> --Thomas Pornin
It is just extra code, that you don't need.
This is what I meant with
" Never mind the extra words ... "
(etc)
One thing to watch out for here.
Jones has defined IMMEDIATE to be itself an immediate word. In
Standard Forth it is not, and so must be placed after the end of the
definition it modifies. The difference is important when you come to
create new words to define other words (one of the chief delights of
Forth).
No, ?PAIRS expect a pair of numbers and aborts when they are not equal
( n1 n2 -- ), as was told you a few weeks ago.
?COMP ( -- ) aborts when STATE is not zero.
!CSP ( -- ) remembers the depth of the stack.
?CSP ( -- ) aborts when the depth of the stack is not the one
remembered.
Try http://www.forth.org/fig-forth/contents.html
It contains FigForth for a whole lot op processors: 8080 8086 6502
6800 68000 1802 PDP-11, 360 etc.
It's a very old implementation (1977/1978) but it contains the
definitions of the above words.
The canonical picture is
case
1 of endof
2 of endof
( default, dropped with endcase )
endcase ( not endof )
The compiler security never comes in the way when you follow the
rules. I've never found them intimidating.
And yes, endof is a synonym for else in some implementations.
> Hi Bruce,
> Yes, it's a mish-mash, mainly 83. It's largely based on the Jones
> Forth articles:
> Although I only took the text descriptions of *how* things work (which
> is very well written) - I largely ignored the Intel Assembler, as it's
> horrid! The textual information is good enough.
I mostly skipped Forth-83 and went from fig and Forth-79 to Forth-94,
but AFAIU, if its for local use, you can just swap POSTPONE with
COMPILE and [COMPILE] based on knowing whether or not the word is
immediate.
A local definition of POSTPONE that saves the input buffer index, BL
WORD FIND to see if it is immediate or not, restores the input buffer
index, and does COMPILE or [COMPILE] as appropriate seems like it
would be a workable POSTPONE for extending the range of Forth-94
scripts you can run directly. Or leave the input buffer index alone
and copy and paste from the definitions of COMPILE and [COMPILE] after
the point that they have found the word, using the immediate/regular/
not-found flag to pick the correct behavior.
Might be easiest to just ABORT at first if the word is not found, and
decide how to handle that case later after more experience.
ORIF might be a good test of whether your POSTPONE is working:
: ORIF \ p ORIF q THEN ... yields q when p=0, otherwise p
POSTPONE DUP POSTPONE 0= POSTPONE IF POSTPONE DROP
; IMMEDIATE
I implemented the Jones Forth version in my version.
It worked, but I had to change instances of ' in the Jones Forth
version to ['] in my version. I'm slightly puzzled as to the reason
why.
By my understanding, ' is used at the command line (invokes WORD FIND
>CFA) to tick a word. ['] ticks a word in a compiled word. To my mind,
['] seemed more appropriate, so I changed it and it works.
['] in my system just looks at PC+2 (the next 'word' (CFA) in the
thread) and places it on the stack. It then skips that CFA:
tick data $+2
inct stack ; make space on stack
mov *pc+,*stack ; get next word in code thread,skip it and put on
stack
b *next ; NEXT
Thanks for the comments, everyone.
Mark
> I implemented the Jones Forth version in my version.
> It worked, but I had to change instances of ' in the Jones Forth
> version to ['] in my version. I'm slightly puzzled as to the reason why.
This is the (informal standard) Forth-79 ' which has different actions
depending on the state. It turns out that giving distinct names to the
distinct actions wherever feasible is be a firmer foundation for
building on, and more flexible as an additional benefit, so (informal)
Forth-83 and (ANS) Forth-94 have ' for the action of finding an XT and
placing it on the stack - which may simply be compiled into a word if
you wish to do that when that word executes - and ['] for putting the
XT of the next word in the source on the stack when the word executes.
What I described above will provide the fastest execution speed on an
8088 or 8086. I've seen this done when simulating an alien machine-
language, with the opcodes being the literal values. There were
hundreds of opcodes to compare against, but execution speed was
reasonably fast.
If you don't want to use the string search instruction for some
reason, there are some alternatives:
1.) Sort your array of literal values at compile-time, and also
arrange the array of xt values to correspond. At run-time, do a binary
search to determine which literal value matches.
2.) Instead of arrays, use my symtab tree to store the literal values
(as the key) and the associated xt value. Search through the tree at
run-time to find the literal value that matches. My symtab is
currently written to use a string as the key, but it should be easy to
rewrite it to use a numeric key. Also, btw, you might want to
implement a CASE that uses strings rather than numeric values.
The #2 solution takes advantage of the fact that some of the literal
values are more likely than others. People typically try to take
advantage of this fact by putting the more likely values earlier in
the CASE statement. You get a lot faster search with a binary tree
though, rather than just comparing the values sequentially.
What you guys are describing, compiling a CASE statement using IF and
ELSE internally, is the way that Forth compilers were written in the
1970s. That kind of compiler-writing is long obsolete. This is the
same point that I was making in my "don't use CREATE DOES>" post ---
there seems to be a tendency for the Forth community to hang on to
1970s technology forever.
That's the way that COMPILE used to work in many systems (except that
it compiled the CFA instead of putting it on the stack). It's not
quite correct for ['], because it does not work correctly on immediate
words, e.g., "['] \".
>tick data $+2
> inct stack ; make space on stack
> mov *pc+,*stack ; get next word in code thread,skip it and put on
>stack
> b *next ; NEXT
That's normally called LIT and is not directly used by programmers;
instead, the system uses LIT in the implementation of LITERAL
Specifically, ['] is a word that acts at compile time to look up the
word and compile its execution token as a literal. In Forth79 (and
apparently Jones Forth) ' is a "state-smart" word that fetches the xt to
the stack when used on the command line, and compiles it as a literal
when used inside a definition. Modern Forth usage (as Bruce notes) uses
different names for these quite different behaviors. Generally, words
whose names are [...] act inside a colon definition to compile something.
> ['] in my system just looks at PC+2 (the next 'word' (CFA) in the
> thread) and places it on the stack. It then skips that CFA:
>
> tick data $+2
> inct stack ; make space on stack
> mov *pc+,*stack ; get next word in code thread,skip it and put on
> stack
> b *next ; NEXT
Yes, that's the behavior of a literal. The behavior of ['] was to
compile the xt as a literal.
> Thanks for the comments, everyone.
>
> Mark
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
http://www.forth.com
"Forth-based products and Services for real-time
applications since 1973."
==================================================
And Borland Pascal native-code compilers in the 90's.
> That kind of compiler-writing is long obsolete. This is the
> same point that I was making in my "don't use CREATE DOES>" post ---
> there seems to be a tendency for the Forth community to hang on to
> 1970s technology forever.
Surely which method is best depends on the application (how
many CASE clauses there are, frequency of execution) and
the target compiler (native code, DTC/ITC etc).
I can easily imagine a few in-line IF/ELSE translated to native
machine-code might prove faster/easier than an array solution.
If there were "one best" solution then everybody would be
using it - even Forth users :)
And in an umbilical embedded context, likely easier to patch with a
new word that handles a few new cases. Indeed, since the compilation
re-uses existing run-time actions, there's no added run-time support
code required to convert an IF-ELSE-THEN into a CASE if the
alternatives expand from two to five.
Surely a big system, big library Forth might usefully have an option
to select one of several versions of CASE - especially since it likely
has several execution table defining facilities already in its library
and just has to put some sugar coating on top to make them look like
CASE statements.
But the argument that EVERY forth implementation ought to implement
CASE according to some particular weighting of the trade-offs between
system size and execution speed and some particular stereotype about
the "normal" thing that CASE is used for, if all it wants to do is to
be able to load lf.f -- that's silly.
A binary search is almost always faster than a linear search. The
exception, as you noted, would be a very tiny CASE statement with only
a handful of elements. It is known at compile-time how many elements
there are though! The compiler can have a rule that if the number of
elements is below some threshold (5 perhaps, but this would be
determined from experimentation), then an IF/ELSE implementation would
be used, and otherwise a binary search. It is the large CASE
statements that are important, and these are the ones that benefit the
most from a binary search.
The IF/ELSE solution doesn't have any benefit except simplicity, and
it is not even very good in that respect. The solution with the binary
search isn't any more of a "brain mangler" than the IF/ELSE solution
in my estimation.
When I worked at Testra, my boss John Hart was an electrical engineer.
He tended to think about software using hardware-type of thinking ---
that is to say: state-machines. His programs often involved gigantic
CASE statements. Personally, I consider this to be a horrible way to
program because the software is extremely difficult to test and debug.
For example, he wrote several machine-code simulators that involved a
gigantic CASE statement, and he advised me to write my MiniForth
simulator in the same way. I didn't. I was assembling all of the code
myself, so I had my assembler not only generate the MiniForth machine-
code, but also generate a Forth program that ran on the desktop
machine and simulated the MiniForth program instruction-by-
instruction. That is my style of programming. A lot of programmers do
tend to think in terms of gigantic CASE statements though. Does
anybody remember the way that Windows used to involve a gigantic CASE
(actually SWITCH, since it was C) statement? That was pretty
horrible!
The use of symtab is a gamble because you are assuming that there is a
lot of variance in the frequency that the elements are accessed
relative to each other. In the case of a simulator, this is true, as
some opcodes (MOV and ADD) are more likely than others. In the case of
a old-style Windows state-machine, this is also true, as some events
are more likely than others. I can't think of any case in which a
gigantic CASE statement doesn't have variance like this, but there
likely are such examples. A binary search of an array (or a balanced
binary tree) is a safer solution because it doesn't make any
assumptions about variance of access frequency. If I were writing a
compiler, I would go with a binary search of an array. This uses a lot
less memory than a binary tree (the LEFT and RITE pointers are
expensive), and it provides good execution speed in every
circumstance.
I have stated several times that the bad design of the ANS-Forth
standard is the primary reason why Forth lost popularity. One of my
many complaints against ANS-Forth is that the CASE statement only
works with single-precision integers. We need CASE statements for all
of the data types, including doubles, strings and floats. Also, it
would be a good idea to have a CASE statement that works with pointers
to records and accepts the xt of a comparer function that the user
writes, and which tests the records for equality.
Thanks Elizabeth.
In my system, ['] is used normally in conjunction with , (comma) which
I believe is the correct 'nomenclature', yes?
e.g. ['] DROP , ( compile drop )
['] puts the XT on the stack and comma takes a value (obviously
doesn't care what it is) and compiles it to HERE.
It can sometimes get confusing to my novice brain. For example, string
constants, we can use LITERAL (or at least, I can ;-)
: STAR [ CHAR * ] LITERAL ;
It can sometimes be confusing to me as to which word is applicable,
and of course, there are probably a few ways to skin the same cat.
With respect to ' ['] [COMPILE] and LITERAL (all of which are present
and working in my hobby Forth) I sometimes find myself reaching for
Brodie, or your own book, which I recently treated myself to courtesy
of Amazon.
Regards
Mark
Hi Hugh
Well, my requirement for CASE..OF is in conjunction with a de-compiler
that I am writing for my hobby Forth (SEE). I already have it working,
but I have a word called SpecialCases that checks the XT (CFA in my
parlance) against a list of 'special' (from SEE's perspective) words.
For example, LIT, BRANCH and 0BRANCH are special cases, in as much as
SEE needs to also read the next word and simply *display* it, rather
than attempt to look it up in the dictionary.
Currently I have a few special cases done (in fact, the three above),
implemented as simple IF..THEN constructs. However, now that I have a
working CASE..OF suite of words, I will change the code to use it, as
it does improve the readability of the source.
As an aside, it does seem that CASE..OF offers a small performance
bonus, in as much as once a matching case has been found no further
cases are evaluated. So, like you say, putting more commonly found XTs
at the start will make a small improvment.
I was a little surprised and impressed that it worked at all, once I
changed the ' to ['] as it occured to me that CASE is compiling IF
which in turn compiles 0BRANCH/BRANCH - so I must be doing something
right!
The code is not intended for 8086 (can't stand Intel assembly) - it
runs on an ancient TMS9900 system. I know, I need to get out more!
Mark
Um, no. If you re-read my comments above, you will see that actually
['] not only does the lookup, but also *compiles* the resulting xt as a
literal.
Use of comma in this case would totally not work in a Standard Forth. In
the first place, comma is not immediate, and so would not be executed at
compile time. In the second place, even if your comma happened to be
immediate, it wouldn't compile a literal, but only the xt itself. You
want the runtime action to push the xt onto the stack, not execute it.
> It can sometimes get confusing to my novice brain. For example, string
> constants, we can use LITERAL (or at least, I can ;-)
>
> : STAR [ CHAR * ] LITERAL ;
>
> It can sometimes be confusing to me as to which word is applicable,
> and of course, there are probably a few ways to skin the same cat.
> With respect to ' ['] [COMPILE] and LITERAL (all of which are present
> and working in my hobby Forth) I sometimes find myself reaching for
> Brodie, or your own book, which I recently treated myself to courtesy
> of Amazon.
Well, it's important to get really clear in your mind what the words do,
and when (e.g. at compile time vs run time) they do it. Your definition
of STAR above would work, but there are several other ways to do it:
: STAR ( -- c ) [CHAR] * ;
is one. There's that [...] naming convention again: [CHAR] * will
*compile* the character as a literal. And the word LITERAL only
compiles a single value, not a string.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310-491-3356
5155 W. Rosecrans Ave. #1018 Fax: +1 310-978-9454
Hawthorne, CA 90250
> CSP and CSP! (are these something to do with data stack pointers? I
> have S0 as a simple variable, which you can @ and ! int the normal
> way)
It's a way to track the depth of a stack of references. Another way
common way is to have CASE put a 0 on the stack. ENDCASE resolves
references until 0 is encountered.
>
> ?PAIRS we discussed a while. I believe ?PAIRS leaves a boolean on the
> stack? ?PAIRS ( count -- boolean )
>
?PAIRS ( u1 u2 -- ) ABORTs if u1<>u2.
Hi Elizabeth. Are you sure ['] does a lookup? I'm under the impression
(and previous posts seem to elude to it also) that ['] doesn't do a
look up. It merely looks at the XT of the *next* word in the code-
thread, and compiles code that (at run time) causes that xt to be
placed on the stack. Because it looks at the next word in the code-
thread, it can only be used in colon definitions.
So:
: TEST ... ... ... ['] DROP ... ... ;
Will actually compile LIT 0xFACE into the definition* TEST (assuming
the address of DROP is 0xFACE)
*i.e. ['] cannot be used on the command line.
Would the above be about right? Have I understood you correctly? Sorry
to be a pain but I want to get it correct!
I have now implemented COMPILE. The following code works:
: DoDROP COMPILE DROP ; IMMEDIATE
: TEST 1 2 3 4 DoDROP ;
TEST .S
1 2 3 ok
*Previously*, I would have had to do it like this:
: DoDROP ['] DROP , ; IMMEDIATE
Which, as you (and others) have pointed out is very wrong!
Thanks
Mark
That's an implementation detail. :)
The ANS standard specifies that ['] parses a name, looks it up, and
compiles the XT as a literal.
But yes, older implementations let the compiler look up and compile the
word as usual, and basically all ['] does is translate from "call xt" to
"push xt".
Hrm...I suppose there is possibly some difference in behavior...dunno if
it's enough that you should worry about it.
--Josh
I guess. It would be nice to get it right though. It would appear '
and ['] are essentially analogues, with ' being used for immediate
mode (at the command line) and ['] being used inside a colon
definition.
I guess it comes down to if ['] is immediate or not (if my brain is
engaged properly today):
If it is not immediate, then at run time ['] would look ahead at the
xt in front of it (which has been compiled to memory in the normal
way), put it on the stack, and skip it. The xt would never be
executed.
If ['] is immediate, it looks in the TIB, does a WORD FIND >CFA then
compiles the CFA as a literal. This is how (I think ;-) Elizabeth was
describing it. Implying that ['] is immediate.
Interesting. It's not immediate in my version. I guess, like you say,
Josh, it's an implementation detail. Many ways to skin that cat!
Thanks
Mark
I pointed that difference out in
<2009Dec1...@mips.complang.tuwien.ac.at>. I have the impression
that MarkWillis does not read my postings lately.
6.1.2510 [']
bracket-tick CORE
Interpretation: Interpretation semantics for this word are
undefined.
Compilation: ( "<spaces>name" -- )
Skip leading space delimiters. Parse name delimited by a space. Find
name. Append the run-time semantics given below to the current
definition.
An ambiguous condition exists if name is not found.
Run-time: ( -- xt )
Place name's execution token xt on the stack. The execution token
returned by the compiled phrase ['] X is the same value returned by '
X outside of compilation state.
Like Anton Ertl, I'm not sure if you ever read responses.
Sorry, I somehow managed to completely miss that posting. My newsreader
seems to have receieved it just fine...can't imagine why I didn't see
it.
--Josh
Hi all,
Anton, I can assure you that I do read your posts! However, google
seemed to have a problem with your posting. I actually saw Coos post,
before yours, and was confused by his reference to you, since there
was no post from you to be seen!
However, it is now there.
Many thanks for all the information. Sorry if I am a little slow
sometimes. It took me a while, but I realised my code was fine as it
is, so I reverted back to the original code.
Elizabeth's post threw me. In her post she says that ['] compiles the
following word as a literal.
My version just puts the CFA of the following word on the stack.
The two actions are one and the same. That is, the end result is the
same.
So, it seems ['] is a compiling word that compiles LIT xxxx into the
definition. Mine doesn't do that, ['] just looks at the CFA in front
of it, and places it on the stack. So, they work differently, but the
end result is the same.
Thanks everyone, and a happy new year to you all.
Mark
In general it isn't. Apparently you still have not read and
understood <2009Dec1...@mips.complang.tuwien.ac.at>. For your
convenience, here's a weblink for that posting:
http://groups.google.com/group/comp.lang.forth/msg/7143f43b192271fd
You are stating at the level of implementation details, when the rule
for implementations is "if it delivers the required behavior, its
good".
Read the standard again. Compiletime: "Skip leading space delimiters.
Parse name delimited by a space. Find name. Append the run-time
semantics given below to the current definition."
Skip leading space delimiters, parse name delimited by space, find
name, that's all what the standard Forth compiler/interpreter does -
your implementation just allows the standard Forth compiler/
interpreter to do that work. There's nothing in the standard that says
that the ['] word has to be executed immediately and DO that itself -
just that one way or another, it has to happen when ['] is encountered
while compiling.
And the Runtime is: "Place name's execution token xt on the stack. The
execution token returned by the compiled phrase ['] X is the same
value returned by ' X outside of compilation state."
If the forth compiler compiles a sequence of xt's, then an action by
['] that just fetches the next xt in the list to the stack and skips
to the following xt is fine.
Its a difference that makes a difference in some implementation - eg,
a subroutine threaded forth with a lot of words compiled as machine
code macros, or a Forth with a pinhole optimizer that translates some
pairs of words into a single more efficient primitive, they would not
get by with that version of ['].
Mind, even on your system, it won't work for ``['] ;'', but any Forth
script that does that is asking for more than Forth-94 promises ...
Forth-94 does not specify what happens when you try to get the
execution token of a word with undefined interpret-time behavior ...
so ``['] ;'' is not required to work.
That's why ``POSTPONE'' is required - there are things that just can't
be standardized at any lower level than that. ``POSTPONE ;'' is one of
those things.
>
> You are stating at the level of implementation details, when the rule
> for implementations is "if it delivers the required behavior, its
> good".
>
Well, it does, so I agree, it's good!
> Read the standard again. Compiletime: "Skip leading space delimiters.
> Parse name delimited by a space. Find name. Append the run-time
> semantics given below to the current definition."
>
> Skip leading space delimiters, parse name delimited by space, find
> name, that's all what the standard Forth compiler/interpreter does -
> your implementation just allows the standard Forth compiler/
> interpreter to do that work. There's nothing in the standard that says
> that the ['] word has to be executed immediately and DO that itself -
> just that one way or another, it has to happen when ['] is encountered
> while compiling.
>
Well, mine gets the job done. I must admit, I didn't look at the
standard at all. It's written with language that I find hard to
understand. We've discussed that before on C.L.F so I won't go there
again!
The standard makes reference to "compile time". That to me says
immediate. It's a compiling word. Mine isn't. It doesn't need to be,
since it's a classic ITC Forth. The XT it needs is exactly one word
ahead. So the following code:
INCT STACK ; make space on stack
MOV *PC+,*STACK ; move next word to stack and skip the word
Does it nicely in 4 bytes.
>
> That's why ``POSTPONE'' is required - there are things that just can't
> be standardized at any lower level than that. ``POSTPONE ;'' is one of
> those things.
There you all go with your POSTPONE again ;-)
Meh. I'm probably setting myself up to be admonished, but I read
POSTPONE as COMPILE.
My version of COMPILE is (IIRC)
: COMPILE WORD FIND >CFA , ; IMMEDIATE
Something like that. It works at any rate!
Mark
Hi Anton,
I have read (and re-read ;-) that. It appears to me that there isn't
much difference.
['] places an XT on the stack. It is normally immediate and does it's
work at compile time, compiling 'LIT xt'
LIT in my version is pretty much the same. It takes the next word (my
system is a 16 bit system) and places it on the stack.
Like you mentioned (I think it was you) LIT is not used much (at all?)
in Forth source code. However, I find I use it a lot at the assembly
source level, when I am writing Forth with assembler (if you catch
what I mean).
For example:
bootmsg data lit,bootmsg,lit,len,type
This is a code 'thread' in assembly format. I'm sure sure you are
familiar with this. I use lit to push the symbolic values bootmsg and
len (determined at assembly time) to the stack. Then TYPE is called to
type the message.
Of course, all the above is elementary to you and many others here.
I'm only pointing it out to try to reassure you that I know what LIT
is for. And yes, there is a similarity.
Many thanks
Mark
That'd be a reason you can't read the standard. All it says is, "this
is what happens if you are compiling". *How* it happens is up to the
implementation.
> It's a compiling word.
> Mine isn't. It doesn't need to be, since it's a classic ITC Forth.
Of course yours is a *compiler* word. Think about it: what does it DO
if you use it from the command line? Its just that its a compiler word
that relies on the main compiler to do most of its work.
The same trick would work with a classic DTC Forth. A similar trick
would work with a simple enough subroutine threaded forth.
Its perfectly compliant in a simple-enough implementation. If you ever
added a pinhole optimizer, you'd want to make ['] an immediate
compiling word to avoid the trap of accidentally combining the word
getting [']'ed and the following word.
Bruce is correct that it doesn't matter how you deliver the required
behavior.
But, I think you're missing the distinction between words that act at
compile time to compile something, and others that *are compiled by*
compiling words and exist *only* at run-time. For example, IF compiles
a forward branch whose destination is unknown at the time, and leaves
the address of the destination place-holder to be filled in by ELSE or
THEN. Similarly, LITERAL compiles a number preceded by the address of
something that will push that number on the stack at runtime.
The names of these runtime functions are not standardized, nor are the
details of their behavior. For example, the branches in IF etc. might
be absolute jumps or relative branches. And there are a variety of
names for them, not to mention the fact that an optimizing compiler may
just lay down a few machine instructions in place of several Forth words.
As Bruce mentioned, you have to watch out for those IMMEDIATE compiler
words. If you want to compile a reference to IF, it won't work to say
['] IF because in your implementation you'll get a compiled branch
there, the xt of the branching word, and then the destination address,
which LIT won't know to skip over. In an implementation where ['] does
the lookup, you'll get the xt of the compiling behavior. That may or
may not be what you wanted. This is what POSTPONE is useful for: it is
used in a definition that is (usually) itself IMMEDIATE, and will
execute the compiling behavior of a word when the word in which it
appears is executed. For example:
: +IF ( -- dest ) POSTPONE DUP POSTPONE IF ; IMMEDIATE
: FOO ( n -- ) +IF . ELSE DROP ." ZERO" THEN ;
+IF is a word that preserves the value being tested. The POSTPONEs here
mean that a reference to DUP will be compiled and the compiling behavior
of IF will be executed when +IF is executed during the compilation of
FOO. It is similar to COMPILE, but is capable of handling IMMEDIATE
words as well as ordinary words, whereas COMPILE isn't.
> Like you mentioned (I think it was you) LIT is not used much (at all?)
> in Forth source code. However, I find I use it a lot at the assembly
> source level, when I am writing Forth with assembler (if you catch
> what I mean).
...
>
> Of course, all the above is elementary to you and many others here.
> I'm only pointing it out to try to reassure you that I know what LIT
> is for. And yes, there is a similarity.
If your ['] only pushes the content of the next cell on the stack and
advances the address interpreter over it, then it isn't "similar" to the
runtime behavior of LITERAL, it's identical to it. The reason you don't
see LIT in source code much is because it is a common name for the
runtime code for LITERAL, although there are variations. On some
systems, for example, LITERAL will examine the value to be compiled, and
if it's < 255 it may only occupy a byte; the runtime code has to be
different, because it has to fetch only a byte and advance the
interpreter pointer only one byte.
I think you will find all this a lot less confusing if you aren't trying
to make implementing a Forth your first experience with Forth. You need
to write more high-level Forth and get a good grounding in the behavior
of words. And Jones Forth is perhaps a poor choice, since it differs
from standard Forth in many ways.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
Literal compiles a value of as cell on the stack into the word being
compiled. To wit:
48 constant digitzero
: digitfive ( -- ) [ digitzero 5 + ] literal ;
digitfive emit
should produce 5 ok
> Like you mentioned (I think it was you) LIT is not used much (at all?)
> in Forth source code. However, I find I use it a lot at the assembly
> source level, when I am writing Forth with assembler (if you catch
> what I mean).
>
> For example:
>
> bootmsg data lit,bootmsg,lit,len,type
>
> This is a code 'thread' in assembly format. I'm sure sure you are
> familiar with this. I use lit to push the symbolic values bootmsg and
> len (determined at assembly time) to the stack. Then TYPE is called to
> type the message.
>
> Of course, all the above is elementary to you and many others here.
> I'm only pointing it out to try to reassure you that I know what LIT
> is for. And yes, there is a similarity.
Jerry
--
Engineering is the art of making what you want from things you can get.
�����������������������������������������������������������������������
You really need to wrap your head around it. Compile time is the time
when a word is being compiled; when the compiler is on. There are a few
ways to turn on the compiler Compiling words ( CREATE , VARIABLE ,
CONSTANT , VALUE ) do it, as well as : and ] . Immediate words *do not*
turn on the compiler. They assume that the compiler is on.
> INCT STACK ; make space on stack
> MOV *PC+,*STACK ; move next word to stack and skip the word
>
> Does it nicely in 4 bytes.
>
>> That's why ``POSTPONE'' is required - there are things that just can't
>> be standardized at any lower level than that. ``POSTPONE ;'' is one of
>> those things.
>
> There you all go with your POSTPONE again ;-)
>
> Meh. I'm probably setting myself up to be admonished, but I read
> POSTPONE as COMPILE.
If "admonished" is the same as "warned that you are confused", then yes.
> My version of COMPILE is (IIRC)
>
> : COMPILE WORD FIND >CFA , ; IMMEDIATE
>
> Something like that. It works at any rate!
Jerry
If we think of "compile time" as when references to words are compiled
into a definition rather than being executed directly, then only : and ]
actually "turn on the compiler". The other words modify the dictionary,
but aren't really "compiling" in the strict sense of executing IMMEDIATE
words and compiling references to everything else.
>> Meh. I'm probably setting myself up to be admonished, but I read
>> POSTPONE as COMPILE.
>
> If "admonished" is the same as "warned that you are confused", then yes.
>
>> My version of COMPILE is (IIRC)
>>
>> : COMPILE WORD FIND >CFA , ; IMMEDIATE
>>
>> Something like that. It works at any rate!
That is a non-standard word, but depending on the context in which it's
used it may produce ok results. The question is when you detect
IMMEDIATE words and how you handle them.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
I doubt that that helps. Mark doesn't seem to understand "compile time"
and "run time" as applied to Forth.
Hi Jerry,
Yeah, admonished was a little strong. I appreciate you all taking the
time to teach this newbie. Thank you to all that have contributed.
If may so though, I think I'm a little less confused than people may
think is the case. Perhaps I don't explain myself to well, but in
general I am managing to absorb and understand the concepts at play.
Of course, I do get confused sometimes, and that is when I turn to to
C.L.F - ont of the problems with learning Forth is that there is
nobody else learning it! It's not like C where you can just shout out
in the office and somebody will help you. So i'm grateful for C.L.F
and it's members.
> You really need to wrap your head around it. Compile time is the time
> when a word is being compiled; when the compiler is on. There are a few
> ways to turn on the compiler Compiling words ( CREATE , VARIABLE ,
> CONSTANT , VALUE ) do it, as well as : and ] . Immediate words *do not*
> turn on the compiler. They assume that the compiler is on.
Understood.
Thanks
Mark
Hi Elizabeth,
For immediate words, I have [COMPILE]
I use it like a POSTPONE (if my understanding is correct ;-)
For example, I use it in CASE...OF to defer IF
IF is immediate and compiles it's own stuff, 0BRANCH , BRANCH etc.
[COMPILE] is used to defer the execution of IF until the word into
which it has been compiled is executed.
Phew. That was a mouthful. Code should illustrate:
: CASE 0 ; IMMEDIATE
: OF COMPILE OVER COMPILE = [COMPILE] IF COMPILE DROP ; IMMEDIATE
: ENDOF [COMPILE] ELSE ; IMMEDIATE
: ENDCASE COMPILE DROP BEGIN ?DUP WHILE [COMPILE] THEN REPEAT ;
IMMEDIATE
And we've gone full circle - this is an improved version of the Jones
version, which used ['] ... ,
Despite my ramblings, I do (now ;-) understand how the code above
works. It took me a while (I had it working before I understood it)
but then there was a fairly large light-bulb moment.
Next I need to go back to CREATE..DOES>
We discussed it in the summer, but I didn't really get it. I *think* I
understand it better now, (I'm talking about how it is coded, not how
to use it, but how does it work). But I think I should let you guys
have a few days off ;-)
I've promised myself if I ever get to the USA I'm booking myself on a
course at Forth Inc as a present to myself. We'll see, so far my work
always takes me East (I'm working in Uzbekistan right now) - never
West :-(
Regards
Mark
Compile time: When the compiler is on and is sequentially compiling
xt's to memory. In doing so, certain immediate words may be
encountered. When encountered these words will be executed. They may
or may not influence what is compiled to memory.
Run time: More tricky to define. The simplest I can come up with would
be to say "when the compiler is not compiling". What I mean is, there
is ALWAYS *something* "running". When we are in immediate mode (just
typing the command line) QUIT INTERPRET WORD FIND etc are all
'running' (i.e. being invoked). But we may not actually be compiling
anything:
1 2 3 + +
Nothing compiled there.
If I start my program with
START
Are we in 'runtime'? Don't know. It's just another word. Maybe my
program will compile some words with CREATE..DOES> So we're jumping in
and out of 'runtime', but there is always 'something' running.
Chapter 11 of Brodie's Starting Forth (page 289 in my copy - looks
like mine is the 1st edition) is quite helpful:
"Just a question of Time
...We have used the term "runtime" when reffering to things that occur
when when a word is *executed* and "compile time" when referring to
things that happen when a word is *compiled*. So far so good. But
things get a little confusing when a single word has both a run-time
behaviour and a compile time behavior."
He's right, it does!
I'll re-visit the chapter again tonight. I suspect I am getting far
more from this than you guys are in trying to keep me on the straight
and narrow! Once again, I am grateful. If it's any consolation, this
(I hope) serves as a good example of which particular facets of Forth
pose the most difficulty for newbies.
Elizabeth is correct: I should have spent some time developing
applications before diving into a compiler project. I've checked out
the majority of PC Forths, they all leave me cold. Any Forth
development I do will be based on embedded systems, probably ARM
systems.
I've been looking at this device: http://andahammer.com/mini244
I'd really like to see a commercial Forth system that will run on it
and give the user access to the hardware.
Regards
Mark
Jerry
Confirmed. My system gives exactly the same results.
Regards
Mark
The combination of COMPILE and [COMPILE] was how these things were done
pre-Forth94. The problem was that this required users to know which
words were IMMEDIATE and which were not. As you have seen in this
discussion, implementors differ sometimes as to what words need to be
IMMEDIATE, so this was a problem. POSTPONE will defer the compilation
of a word regardless of whether it's IMMEDIATE.
> Next I need to go back to CREATE..DOES>
>
> We discussed it in the summer, but I didn't really get it. I *think* I
> understand it better now, (I'm talking about how it is coded, not how
> to use it, but how does it work). But I think I should let you guys
> have a few days off ;-)
Once again, there are many different ways to implement this. The
important thing is to get the behavior(s) right.
> I've promised myself if I ever get to the USA I'm booking myself on a
> course at Forth Inc as a present to myself. We'll see, so far my work
> always takes me East (I'm working in Uzbekistan right now) - never
> West :-(
That would be nice, but be sure and check with our Sales Dept before
committing to travel, as courses are not scheduled often and sometimes
have to be cancelled.
There are two senses in which the term "compile time" is used. Mainly,
it means what goes on between : and ; (unless there's a [ in which case
compilation resumes when a ] is encountered). Sometimes, though, the
term is used to describe what a defining word such as VARIABLE or CREATE
executes to construct some sort of data object. I prefer to use the
word "defining" for the latter case, and keep "compiling" to mean
strictly what goes on during the construction of a colon definition. I
describe the behavior of something defined by a defining word as its
"instance behavior".
> Run time: More tricky to define. The simplest I can come up with would
> be to say "when the compiler is not compiling". What I mean is, there
> is ALWAYS *something* "running". When we are in immediate mode (just
> typing the command line) QUIT INTERPRET WORD FIND etc are all
> 'running' (i.e. being invoked). But we may not actually be compiling
> anything:
>
> 1 2 3 + +
>
> Nothing compiled there.
"Run time" is simply the opposite of "compile time". Yes, when you're
typing on the command line that's runtime, *unless* you type in a colon
definition, in which case it's compile time during its construction.
> If I start my program with
>
> START
>
> Are we in 'runtime'? Don't know.
Yes. Why did you think you might not be?
> It's just another word. Maybe my
> program will compile some words with CREATE..DOES> So we're jumping in
> and out of 'runtime', but there is always 'something' running.
No, you're not "jumping into and out of" runtime. Consider:
: ARRAY ( n -- ) CREATE CELLS ALLOT
DOES> ( -- a ) ;
You're in compile time from the colon all the way to the semicolon.
1000 ARRAY STUFF
Here you're *executing* the defining part of ARRAY (the part before the
DOES>). That's all runtime. Executing ARRAY constructed an *instance*
of ARRAY named STUFF, with 1000 cells of allocated data space.
STUFF 1000 CELLS DUMP
Also all runtime, executing the instance behavior of ARRAY (whatever
appears after DOES>, which in this case is nothing, meaning the default
behavior of DOES> objects, which is to return the address of the data
space of the instance).
> Chapter 11 of Brodie's Starting Forth (page 289 in my copy - looks
> like mine is the 1st edition) is quite helpful:
>
> "Just a question of Time
> ...We have used the term "runtime" when reffering to things that occur
> when when a word is *executed* and "compile time" when referring to
> things that happen when a word is *compiled*. So far so good. But
> things get a little confusing when a single word has both a run-time
> behaviour and a compile time behavior."
>
> He's right, it does!
Those exist mainly for compiler directives such as IF. In the Standard,
these are referred to as "compilation semantics" and "runtime semantics".
> I'll re-visit the chapter again tonight. I suspect I am getting far
> more from this than you guys are in trying to keep me on the straight
> and narrow! Once again, I am grateful. If it's any consolation, this
> (I hope) serves as a good example of which particular facets of Forth
> pose the most difficulty for newbies.
>
> Elizabeth is correct: I should have spent some time developing
> applications before diving into a compiler project. I've checked out
> the majority of PC Forths, they all leave me cold. Any Forth
> development I do will be based on embedded systems, probably ARM
> systems.
>
> I've been looking at this device: http://andahammer.com/mini244
>
> I'd really like to see a commercial Forth system that will run on it
> and give the user access to the hardware.
Check out FORTH, Inc.'s SwiftX cross compilers. It's a lot easier to
develop for an embedded target with the kinds of tools a PC host can
provide. The free evaluation versions come with all source for the
target's kernel, so you can see how it's built, as well as extensive
documentation.
Cheers,
Elizabeth
--
==================================================
Elizabeth D. Rather (US & Canada) 800-55-FORTH
FORTH Inc. +1 310.999.6784
5959 West Century Blvd. Suite 700
Los Angeles, CA 90045
> Meh. I'm probably setting myself up to be admonished, but I read
> POSTPONE as COMPILE.
And you are reading COMPILE as [COMPILE] making a wonderful semantic
noodle salad.
> My version of COMPILE is (IIRC)
> : COMPILE WORD FIND >CFA , ; IMMEDIATE
> Something like that. It works at any rate!
It works as [COMPILE] ... that is the Forth-79 / Forth-83 word that
forces compilation of the following word whether it is immediate or
not. Quoting the Forth-79 standard for ``COMPILE'', "when the word
containing COMPILE executes, the 16-bit value following the
compilation address is copied (compiled) into the dictionary, i.e.,
COMPILE DUP will copy the compilation address of DUP".
So in your system, COMPILE should not have any WORD FIND in it at all.
It should be something like your ['], except then do a comma:
_compile
data $+2
inct stack ; make space on stack
mov *pc+,*stack ; get next word in code thread, skip it
; and put on stack
b _comma ; complete with comma primitive
Its [COMPILE] that would be:
: [COMPILE] WORD FIND >CFA , ; IMMEDIATE
I'm assuming from that you have WORD and FIND as ...
... WORD ( "token" -- counted-addr )
... FIND ( counted-addr -- PFA|0 )
If =IMMEDIATE= is the immediate bit in the PFA, and assuming that >CFA
aborts with an appropriate message if handed a ``0'' indicating no
word was found, your POSTPONE would be something like:
: ~IMMEDIATE? ( PFA -- flag=not-immediate? )
C@ =IMMEDIATE= AND 0= ;
: POSTPONE
WORD FIND DUP ~IMMEDIATE? IF
COMPILE COMPILE
THEN >CFA , ;
Of course you'll have a factor to test the immediate bit from your
compiler code, so swap that in for ~IMMEDIATE? and if your WORD and
FIND do not work exactly that way, adjust the definition to suit. A
Forth-79 system, for instance, would skip the leading ``WORD''.
But making ['] an alias for LIT does not deliver the required results,
e.g., for the following standard code:
: foo ['] \ ;
> : foo ['] \ ;
Aha, I was looking at words like IF and ; when I should have been
looking at \ and ( ... and even if \ is CORE EXT ( is CORE, so
Forth-94 ['] has to parse for itself (as, indeed, [COMPILE] misnamed
as COMPILE was specified to do).
> Although I only took the text descriptions of *how* things work (which
> is very well written) - I largely ignored the Intel Assembler, as it's
> horrid! The textual information is good enough.
Looking at the JonesForth articles more closely, one reason the
Forth-94 draft proposal standards document might be confusing would be
dialect interference. For example, Jones writes about interpret-mode
as "immediate-mode" - if he called it interpret-mode, then
"interpretation semantics" would not be so obscure.
You've already noted that his CREATE is not the Forth-94 CREATE and
that can be untangled by dubbing his header creation
HEADER ( ca u -- )
which leaves the way open for a CREATE that starts out with a code
field with DOVAR in it. Also note that while he says that DOES> is not
possible, for a standard CREATE it seems like it should be - when
CREATE executes, you need to stash the CFA of the most recently
created in some dedicated variable - given how old-school JonesForth
is, maybe CCFA for Created Code Field Address - and then use that when
DOES> is encountered to compile the { DOES EXIT DODOES } sequence so
that when the word executes, CCFA can be used to patch the code field
of the most recently created word to point to DODOES.
His WORD stores an up to 32 byte token in a fixed buffer, and returns
( ca u ) on the stack. This could be called something else, say
TOKEN ( ca u )
to avoid suggesting that Forth-94 WORD is available.
His FIND also does less than the Forth-94 FIND, which leaves the
execution token (CFA in this case) and a flag on whether its immediate
or not (and his flag has only one bit set) so his FIND might be:
LOOKUP ( ca u -- link-field-address )
leaving the way clear for a later FIND something like:
: FIND ( counted-addr - 0 | CFA 1 | CFA TRUE )
COUNT LOOKUP DUP IF
DUP CELL+ C@ F_IMMED AND IF 1 ELSE -1 THEN
\ or maybe the other way around, I'm neither fluent
\ in x386 nor in gas
>R >CFA R>
THEN ;
As Anton points out, his ' and ['] break in the case of, eg,
... ' ( ...
and in addition his ' is not ' at all, which is a regular word and if
included in an action definition is saying what you want to have
happen when the action takes place.
So to leave room for a later ' and ['] it'd need his gimmick kind-of
ticks to get their own name, maybe L' [L'] for "lazy tick" (most
likely found on a lazy dog).
Forth-94 IMMEDIATE is not IMMEDIATE (an action definition that had
``IMMEDIATE'' in it would be making the LATEST word IMMEDIATE when the
action happens), so his IMMEDIATE might be dubbed [IMMEDIATE] to,
again, leave the way clear for a Forth-94 IMMEDIATE.
The main actual change in actions of words I'd suggest is switching to
an all-bits-set TRUE.
( x boolean ) AND ( -- x | FALSE )
and
( x boolean ) OR ( -- x | TRUE )
... is handier for when a test for a special case has to be added to
already working code outside that case.
On Dec 17 2009, 3:20 am, MarkWills <markrobertwi...@yahoo.co.uk>
wrote:
> Does anyone have a version of CASE..OF (written in Forth) that is
> suitable for use with a 16 bit ITC Forth?
>
> I would like to add it to my hobby system, but it's a bit of a brain
> mangler for me. It appears to be a case (ha case!) of compiling IFs
> (and by inference, 0BRANCH/BRANCH) words.
>
> I already have IF..ELSE..THEN done (immediate words), DO..LOOP etc.
Well, I like this one:
: ` POSTPONE POSTPONE ; IMMEDIATE
: CASE TRUE ` LITERAL ` IF ; IMMEDIATE
: OF ` OVER ` = ` IF ` DROP ; IMMEDIATE
: ENDOF 1 CS-ROLL ` THEN ` ELSE ; IMMEDIATE
: ENDCASE ` DROP ` THEN ; IMMEDIATE
Regards,
-Helmar