[net.lang.forth] Defining a structure in FORTH?

florman@randvax.UUCP (Bruce Florman) (06/13/86)

I am very much a novice FORTH programmer, and I don't even have a good
textbook to go by.  I recently purchased a FORTH for my Macintosh at home
(MACH1, distributed by the Palo Alto Shipping Co.), and would like some
advice.  Professionally I do a lot of work with LISP, and I would like to
implement something similar to a `DEFSTRUCT' package in FORTH.  In other
words, I'd like to be able to do something like:

	DEFSTRUCT[ RECTANGLE
		   TOP    2
		   LEFT   2
		   BOTTOM 2
		   RIGHT  2 ]ENDSTRUCT

Which would automatically define the following:

	8 CONSTANT RECTANGLE-SIZE
	: RECTANGLE-TOP@ ( a - n ) @ ;
	: RECTANGLE-TOP! ( n a - ) ! ;
	: RECTANGLE-LEFT@ ( a - n ) 2 + @ ;
	: RECTANGLE-LEFT! ( n a - ) 2 + ! ;
	: RECTANGLE-BOTTOM@ ( a - n ) 4 + @ ;
	: RECTANGLE-BOTTOM! ( n a - ) 4 + ! ;
	: RECTANGLE-RIGHT@ ( a - n ) 6 + @ ;
	: RECTANGLE-RIGHT! ( n a - ) 6 + ! ;
	: MAKE-RECTANGLE ( whatever code necessary to allocate 8 bytes
			   of variable storage and assign a dictionary
			   entry to the word which follows.  This I guess
			   would be implementation specific. ) ;

While I'm sure that this could be done by defining `DEFSTRUCT[' so that it
constructs all of the necessary dictionary headers etc. at the bit and byte
level, this would doubtless be complicated and not very portable.  I wonder
then, if there is a higher level method of defining such a beast?  Any help
(even "no that can't be done") would be appreciated.


Bruce Florman	-----------------------------	florman@rand-unix.ARPA

oster@ucblapis.berkeley.edu (David Phillip Oster) (06/14/86)

Two years ago I wrote such a beast in Forth-83 for the Mac. (The mac op
sys involves lots of Pascal structures.)  My version lets you say things
like
2 MakeRec: Integer:
BEGINREC
  Integer: .v
  Integer: .h
ENDREC: Point:
BEGINREC
  Point: .topLeft
  Point: .bottomRight
ENDREC: Rect:
Rect: aRect
Point: aPoint
4 aPoint.v !

The code is fairly portable, and about 2 screens of Forth-83
--- David Phillip Oster		-- "The goal of Computer Science is to
Arpa: oster@lapis.berkeley.edu  -- build something that will last at
Uucp: ucbvax!ucblapis!oster     -- least until we've finished building it."

jb@rti-sel.UUCP (Jeff Bartlett) (06/18/86)

> I am very much a novice FORTH programmer, and I don't even have a good
> textbook to go by.  I recently purchased a FORTH for my Macintosh at home
> (MACH1, distributed by the Palo Alto Shipping Co.), and would like some
> advice.  Professionally I do a lot of work with LISP, and I would like to
> implement something similar to a `DEFSTRUCT' package in FORTH.  In other
> words, I'd like to be able to do something like:
> 
> 	DEFSTRUCT[ RECTANGLE
> 		   TOP    2
> 		   LEFT   2
> 		   BOTTOM 2
> 		   RIGHT  2 ]ENDSTRUCT
> 
> Which would automatically define the following:
> 
> 	8 CONSTANT RECTANGLE-SIZE
> 	: RECTANGLE-TOP@ ( a - n ) @ ;
> 	: RECTANGLE-TOP! ( n a - ) ! ;
> 	: RECTANGLE-LEFT@ ( a - n ) 2 + @ ;
> 	: RECTANGLE-LEFT! ( n a - ) 2 + ! ;
> 	: RECTANGLE-BOTTOM@ ( a - n ) 4 + @ ;
> 	: RECTANGLE-BOTTOM! ( n a - ) 4 + ! ;
> 	: RECTANGLE-RIGHT@ ( a - n ) 6 + @ ;
> 	: RECTANGLE-RIGHT! ( n a - ) 6 + ! ;
> 	: MAKE-RECTANGLE ( whatever code necessary to allocate 8 bytes
> 			   of variable storage and assign a dictionary
> 			   entry to the word which follows.  This I guess
> 			   would be implementation specific. ) ;
> 
> While I'm sure that this could be done by defining `DEFSTRUCT[' so that it
> constructs all of the necessary dictionary headers etc. at the bit and byte
> level, this would doubtless be complicated and not very portable.  I wonder
> then, if there is a higher level method of defining such a beast?  Any help
> (even "no that can't be done") would be appreciated.
> 
> Bruce Florman	-----------------------------	florman@rand-unix.ARPA

This should do what you want to do.  The idea is to create a screen
on-the-fly that contains the definitions, then just load 'em.

    999 constant (DEFSTRUCT-SCREEN) ( screen scratch area, site-dependant)
    0 variable (DEFSTRUCT-STATE)
    0 variable (DEFSTRUCT-OFFSET)
    0 variable (DEFSTRUCT-BYTES)
    0 variable (DEFSTRUCT-LABEL) 32 allot

    : (DEFSTRUCT-EMIT)  ( c -- )
	(DEFSTRUCT-SCREEN) block (DEFSTRUCT-OFFSET) @ + c! update
	(DEFSTRUCT-OFFSET) @ 1 + 
		dup b/buf > if ." DEFSTRUCT[ output too large" abort endif
	(DEFSTRUCT-OFFSET) !
	;

    : (DEFSTRUCT-TYPE) ( addr -- )
		count 0 do dup c@ (DEFSTRUCT-EMIT) 1 + loop drop ;
	
    : (DEFSTRUCT-LEADIN) ( -- )
	bl (DEFSTRUCT-EMIT)
	" : " (DEFSTRUCT-TYPE)
	(DEFSTRUCT-LABEL) (DEFSTRUCT-TYPE)
        '-' (DEFSTRUCT-EMIT)
        here (DEFSTRUCT-TYPE)
       ;

    : (DEFSTRUCT-PROFFSET) ( -- )
	bl (DEFSTRUCT-EMIT)
	(DEFSTRUCT-OFFSET) @ <# #S #> (DEFSTRUCT-TYPE)
	bl (DEFSTRUCT-EMIT)
	;

    : (DEFSTRUCT) ( -- )
	(DEFSTRUCT-STATE) @
		dup 0 = if
			0 (DEFSTRUCT-OFFSET) !
			0 (DEFSTRUCT-BYTES) !
			here (DEFSTRUCT-LABEL) here c@ cmove
			1 (DEFSTRUCT-STATE) !
		else dup 1 = if
			(DEFSTRUCT-LEADIN)
			" @ "                (DEFSTRUCT-TYPE)
			(DEFSTRUCT-PROFFSET)
			" + @ ; "            (DEFSTRUCT-TYPE)

			(DEFSTRUCT-LEADIN)
			" ! "                (DEFSTRUCT-TYPE)
			(DEFSTRUCT-PROFFSET)
			" + ! ; "            (DEFSTRUCT-TYPE)

			2 (DEFSTRUCT-STATE) !
		else dup 2 = if

			0 0 here number drop (DEFSTRUCT-BYTES) !+

			1 (DEFSTRUCT-STATE) !
		endif endif endif
        drop
     ;

    : (DEFSTRUCT-CLOSEUP)
	(DEFSTRUCT-PROFFSET)
	" constant "            (DEFSTRUCT-TYPE)
	(DEFSTRUCT-LABEL)       (DEFSTRUCT-TYPE)
	" -SIZE "               (DEFSTRUCT-TYPE)

	" : MAKE-"              (DEFSTRUCT-TYPE)
	(DEFSTRUCT-LABEL)       (DEFSTRUCT-TYPE)
	BL                      (DEFSTRUCT-EMIT)
	" <builds "             (DEFSTRUCT-TYPE)
	(DEFSTRUCT-PROFFSET)
	BL                      (DEFSTRUCT-EMIT)
	" allot does> ; "       (DEFSTRUCT-TYPE)
	" ;s "                  (DEFSTRUCT-TYPE)
     ;

    0 variable (strequal-flag)

    : strequal ( s1 s2 -- f )
	1 (strequal-flag) !
	count drop swap count 0 do
		2dup c@ swap c@ = if
			1+ swap 1+ swap
		else
			0 (strequal-flag) ! leave
		endif
	loop 2drop (strequal-flag) @ ;

    : DEFSTRUCT[ 
         0 (DEFSTRUCT-STATE) !
         begin
	    BL WORD
	    HERE " ]ENDSTRUCT" strequal not
         while
            (DEFSTRUCT)
         again
	     (DEFSTRUCT-CLOSEUP) (DEFSTRUCT-SCREEN) load
	    ; immediate

It's based on the fig-forth model, or what I remember of it.

This was pounded into the editor and has not be tested.
Everything except the string literals should be in upper case.
I don't have a reference handy so check the usage of
	'DO' '<#','#S','#>', 'cmove', 'number' etc...
(no expressed or implied warranty, blah, blah ....)

the generated code should look like this (except that it will span
	lines randomly) :


   : RECTANGLE-TOP@ 0 + @ ; : RECTANGLE-TOP! 0 + ! ;
   : RECTANGLE-LEFT@ 2 + @ ; : RECTANGLE-LEFT! 2 + ! ;
   : RECTANGLE-BOTTOM@ 4 + @ ; : RECTANGLE-BOTTOM! 4 + ! ;
   : RECTANGLE-RIGHT@ 6 + @ ; : RECTANGLE-RIGHT! 6 + ! ;
   8 CONSTANT RECTANGLE-SIZE : MAKE-RECTANGLE <BUILDS 8 ALLOT DOES> ; ;S


Hope this will help.  If you use this, please post your improved version
to the net.

For the versions of forth that load from text files, create a tempfile
and input it.

Jeff Bartlett
Center for Digital Systems Research
Research Triangle Institute		mcnc!rti-sel!jb

with generic_disclaimer; use generic_disclaimer;