john@aplcen.apl.jhu.edu (John Hayes) (09/07/90)
In an earlier posting, I described a word, START:, that was added to the Core Extensions Word Set of ANS Forth at the Vancouver meeting. The following is the discussion section of the proposal for START:; it offers further motivation for standardizing START:. I frequently want to compile Forth code fragments (code without a head), save the code in a data structure, and later extract it for execution. Here is a real world example: The Experiment Computer, a part of Spacelab (a module that can be flown on the Space Shuttle), sends commands to experiments in the shuttle's payload bay as a packet containing a two digit command number. In the Hopkins Ultraviolet Telescope experiment we process these commands using this syntax: 0 CMD: ... code for command 0 ... ;CMD 1 CMD: ... code for command 1 ... ;CMD 99 CMD: ... code for command 99 ... ;CMD We chose this syntax because we didn't want to strain our brains inventing 100 names and we didn't want to expend dictionary space storing names that would never be used. The nth command is executed with DOCMD which takes n as a stack argument. The problem described above is a specific instance of the general problem of constructing headerless code fragments. Attempting to implement CMD:, etc. will reveal whether we can solve the general problem. An implementation that works on many systems is: CREATE CMD-TABLE 100 CELLS ALLOT : CMD: ( N -- ) HERE SWAP CELLS CMD-TABLE + ! ] ; : DOCMD ( N -- ) CELLS CMD-TABLE + @ >R ; This implementation has several problems. In CMD: using HERE to find the address of the subsequently compiled code is non-portable; some systems separate code and data and HERE points to data. In DOCMD pushing the address of the code on the return stack and returning isn't guaranteed to work; some systems do not use the return stack for return addresses and some systems use a return address bigger than one cell. Despite there problems, this is the most portable solution I am able to find. What we really need is to construct an execution token in CMD: and pass this token to EXECUTE in DOCMD. Unfortunately, there is no standard way to construct an execution token except through :. That's where START: comes in. START: creates an execution token such that code subsequently compiled into the dictionary will be associated with the execution token. START: returns the execution token on the stack. With START: the definitions of CMD: and DOCMD become: : CMD: ( N -- ) START: SWAP CELLS CMD-TABLE + ! ] ; : DOCMD ( N -- ) CELLS CMD-TABLE + @ EXECUTE ; START: is a general solution to the problem of creating headless code fragments. One of Forth's great strengths is that it allows the programmer to create application specific languages. The application language is frequently a mixture of Forth code and application constructs. The language is implemented by storing Forth code as fragments in a data structure. To illustrate this, I have included examples of four problems that require the functionality of START: to implement portably. The first three examples are from my own programming experience and the last is a published example. I have recently been working with an object-oriented programming system implemented in Forth. My syntax for declaring a class is: parent-class SUB-CLASS> class LOCAL: X \ instance variables LOCAL: Y METHOD: :DOTHIS ... ;METHOD METHOD: :DOTHAT ... ;METHOD END> METHOD: not only compiles method code, but must store the code's execution token in a dispatch table in the class data structure. The dispatch table provides an efficient way to do late message/method binding. A few years ago I wrote a user-extensible text editor. The editor provided a few, very powerful, very general editing commands and a way to bind user-defined command to particular keystrokes. Here are three such commands ('d' deletes the current line, 'D' deletes to the end of the line, and '^W' deletes the current word): cmd d |<- mark scrollcursor deleterange endcmd cmd D drop mark ->| deleterange endcmd cntrl W mark ->w deleterange endcmd In my embedded systems programming work, I have found the following construct useful: do: monitor-that-device ... some code ... ;do monitor-that-device every 5 seconds doit Do: constructs a data structure that, among other things, contains the execution token of the following code. The data structure is passed to doit, which gives the structure to a time service process. The final example is FORPS [Matheus, C. J. "The Internals of FORPS: a FORth-based Production System, JFAR 4/1, pp.7-27] an expert system shell. The syntax for declaring a rule is: RULE: rule-name PRIORITY: n *IF* ... condition code ... *THEN* ... action code ... *END* The data structure for each rule contains the execution tokens for the condition code and the action code. The condition code for every rule is executed before deciding which rule's action code should be executed. In summary, Forth is at its best when designing application specific programming languages. This is frequently accomplished by constructing headless code fragments. Unfortunately, there is now no portable way of doing this. START: solves this problem. John R. Hayes Applied Physics Laboratory Johns Hopkins University