[net.lang.forth] Oster's Forth Structure Package

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

Here's what you've been asking for, here is the source code to define
structures in Forth. I first wrote this code almost two years ago, when I
was in the process of writing my own implementation of Forth-83 for the
Macintosh.  Menu Clock is written in that Forth.
( Pascal Record Package,I ( David Phillip Oster 3/4/85 )
\ Structures in Forth are almost as simple as arrays in Forth.
\ Here it is. (Note that the actual code, without the comments, is only 16
\ lines long.)

\ This structure package is 
\ Copyright (c) 1985, 1986 David Phillip Oster. All Rights Reserved
\ You may use it as you see fit except:
\ 1 ) all copies of the source code of the package must contain 
\ this copyright message.
\ 2 ) If you describe this idea you must give David Oster
\ full credit for its invention.


\ ****************** General Comments *********************
\ Structure:
\   This file consists of:
\   General Coments: this overview
\   Implementation: the underlying implementation of the record package
\   Example 1: Declaring some macintosh types.
\   Example 2: Using those types to do some mac programming
\ 
\ Use:
\ This package defines two defining words, 
\ "RECORD"	( starts a pascal-like record-type definition)
\ "ENDREC:"	( ends, and gives a name to the record-type)
\ 
\ and also
\ 
\ "SIZEOF("	( takes a type name and returns the size fo that type.)
\ 
\ and lastly:
\ 
\ "MAKEREC:"	( record types are defined in terms of other 
\ 			record types.
\ 				To start the system off, you need a more primitive way of 
\ 			creating record types. )
\ 
\ Features:
\ 	Record-types can be used inside other record-types to declare
\ field names.  Record-types can also be used to declare variables in the
\ dictionary of that type.
\ 
\ 	The code generated by the package is optimized: field names that
\ merely add 0 to the record base address generate no run-time code!
\ 
\ Record-types, created with this package can be used in the definition of
\ yet other records. Record-types can also be used to declare variables
\ of that type.
\ Field names are also defined by this package.  Field names can be applied
\ to record variables to give the address of the beginning of that field.
\ 
\ Naming Conventions:
\   field names start with a period.  Record-Types, like all defining words,
\ end with a colon. I pretty print against the right-edge, since the verbs
\ in forth are on the right.
\ 
\ Limitations:
\ The Forth-83 vocabulary structure is NOT used, so you can apply field 
\ names for type A to a record of type B, and no-one but you will ever 
\ catch the mistake (this is no worse than most of Forth.) 
\ The underlying implementation of the record package should really be in
\ a hidden vocabulary.
\ Field names are just words in the vocabulary, so they should be unique.
\ Some-one should extend this so that all the field names of a record
\ are grouped in that record's "record-type vocabulary"
\ using a record variable sets the "record-type vocabulary"
\ this would allow a record type A to have a field named ".foo" and a 
\ second record-type B to have a field name ".foo" without B's definition
\ overriding A's.  (This package overrides.)
\
\ ****************** The Structure package ******************
VARIABLE INRECORD  ( Holds a flag - are we defining a record or a variable?)

( WALIGN - if you need it align to word boundary )
\ In Mac Pascal, successive byte sized fields are packed, but you
\ skip a byte if you have an odd number of byte sized fields and the next 
\ field is larger than a byte.  
: WALIGN ( AmtUsed MySiz - AdjustUse MySiz )  DUP 1 = NOT IF
              SWAP 1+ [ HEX ] FFFE [ DECIMAL ] AND SWAP THEN ;

( DOOFFSETR - Runtime of a .Field name )
: DOOFFSETR DOES> ( adrRecord adrOffset - adrRecord+Offset )
      @  + ;

( DOPASREC - run time actions for PascalType: )
\ two major cases: declaring a .Field, or declaring a var
\ If first field, make .PascalField a compileTime noop
: DOPASREC  ( -- ) DOES> @ CREATE  INRECORD @ IF
                           WALIGN OVER ?DUP IF
                              , DOOFFSETR ELSE
          -2 DP +! ['] NOOP @ , IMMEDIATE THEN
                                         +  ELSE
                                      ALLOT THEN ;

\ ****************** Stucture package top level ******************
\ begin a record definition
: RECORD ( - 0 | intialize vars ) INRECORD ON  0 ;

\ takes number of bytes, which is the size of the field
: MAKEREC: ( n - ; name )  CREATE , DOPASREC ;

\ interested in fields, not in the final record, so just end
: ENDREC   ( n - |Used when just defining fields)
                                  INRECORD OFF DROP ;
\ create the record
: ENDREC:  ( n - ; name )         INRECORD OFF MAKEREC: ;

\ takes a type-name from the input
: SIZEOF(  ( - n ;typename) 
	' >BODY @ STATE @ IF LITERAL THEN ; IMMEDIATE

\ ****************** An example, some Macintosh structures ******************
( Mac FontInfoRec Point record definitions    ( 12/26/84 dpo)
4 	MAKEREC: LINT:		\ LongInt
2 	MAKEREC: INT:		\ Integer  
1 	MAKEREC: BOOL:		\ Boolean
8 	MAKEREC: Pattern:   
32	MAKEREC: Bits16:
4 	MAKEREC: RgnHandle:
4 	MAKEREC: Handle:      
4 	MAKEREC: PTR:		\ Ptr

  RECORD
    INT: .ASCENT
    INT: .DESCENT
    INT: .WIDMAX
    INT: .LEADING
  ENDREC: FontInfoRec:

  RECORD
    INT: .V
    INT: .H
  ENDREC: Point:

  RECORD
    Point: .topLeft
    Point: .botRight
  ENDREC: Rect:

\ Other names for fields of rect:
: .bottom  .botright   .v ;
: .top     .topLeft    .v ;
: .right   .botright   .h ;
: .left    .topleft    .h ;

  RECORD
    PTR:   .BASEADDR
    INT:   .ROWBYTES
    RECT:  .BOUNDS
  ENDREC: BitMap:

\ ****************** Example, continued ******************
Point: SavePen
SavePen GetPen
 100 100 MoveTo
 SavePen .x @ SavePen .y @ LineTo

Rect: 4Square

: CopyPoint ( &Point &Point - ) SIZEOF( Point: CMOVE ;

SavePen		4Square .topLeft	CopyPoint
SavePen		4Square .botRight	CopyPoint

4Square .bottom 4 +!
4Square .right  4 +!

4Square FrameRect

--- 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."
U.S.Mail:
David Oster
Mosaic Codes
Suite 1036
2000 Center St.
Berkeley, Ca. 94704

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

Clarification of some funky forth:
replace DP +! by  ALLOT .  (ALLOT is usually defined as : ALLOT DP +! ;)
some of you asked about the defs of ON and OFF:
: ON TRUE !;
: OFF FALSE ! ;
in  Forth-83
0 CONSTANT FALSE
-1 CONSTANT TRUE
but any reasonable definition of true and false should work.
--- David Phillip Oster		-- "We live in a Global Village."
Arpa: oster@lapis.berkeley.edu  --
Uucp: ucbvax!ucblapis!oster     -- "You are Number Six."