File:  [gforth] / gforth / glocals.fs
Revision 1.14: download - view: text, annotated - select for diffs
Mon Oct 16 18:33:10 1995 UTC (28 years, 6 months ago) by anton
Branches: MAIN
CVS tags: HEAD
added answords.fs and strsignal.c
added checking of documenetation of ANS Forth words
Fixed many documentation errors and added some documentation
signal handling now uses strsignal and can handle signals not present on all machines

    1: \ Local variables are quite important for writing readable programs, but
    2: \ IMO (anton) they are the worst part of the standard. There they are very
    3: \ restricted and have an ugly interface.
    4: 
    5: \ So, we implement the locals wordset, but do not recommend using
    6: \ locals-ext (which is a really bad user interface for locals).
    7: 
    8: \ We also have a nice and powerful user-interface for locals: locals are
    9: \ defined with
   10: 
   11: \ { local1 local2 ... }
   12: \ or
   13: \ { local1 local2 ... -- ... }
   14: \ (anything after the -- is just a comment)
   15: 
   16: \ Every local in this list consists of an optional type specification
   17: \ and a name. If there is only the name, it stands for a cell-sized
   18: \ value (i.e., you get the value of the local variable, not it's
   19: \ address). The following type specifiers stand before the name:
   20: 
   21: \ Specifier	Type	Access
   22: \ W:		Cell	value
   23: \ W^		Cell	address
   24: \ D:		Double	value
   25: \ D^		Double	address
   26: \ F:		Float	value
   27: \ F^		Float	address
   28: \ C:		Char	value
   29: \ C^		Char	address
   30: 
   31: \ The local variables are initialized with values from the appropriate
   32: \ stack. In contrast to the examples in the standard document our locals
   33: \ take the arguments in the expected way: The last local gets the top of
   34: \ stack, the second last gets the second stack item etc. An example:
   35: 
   36: \ : CX* { F: Ar  F: Ai  F: Br  F: Bi -- Cr Ci }
   37: \ \ complex multiplication
   38: \  Ar Br f* Ai Bi f* f-
   39: \  Ar Bi f* Ai Br f* f+ ;
   40: 
   41: \ There will also be a way to add user types, but it is not yet decided,
   42: \ how. Ideas are welcome.
   43: 
   44: \ Locals defined in this manner live until (!! see below). 
   45: \ Their names can be used during this time to get
   46: \ their value or address; The addresses produced in this way become
   47: \ invalid at the end of the lifetime.
   48: 
   49: \ Values can be changed with TO, but this is not recomended (TO is a
   50: \ kludge and words lose the single-assignment property, which makes them
   51: \ harder to analyse).
   52: 
   53: \ As for the internals, we use a special locals stack. This eliminates
   54: \ the problems and restrictions of reusing the return stack and allows
   55: \ to store floats as locals: the return stack is not guaranteed to be
   56: \ aligned correctly, but our locals stack must be float-aligned between
   57: \ words.
   58: 
   59: \ Other things about the internals are pretty unclear now.
   60: 
   61: \ Currently locals may only be
   62: \ defined at the outer level and TO is not supported.
   63: 
   64: require search-order.fs
   65: require float.fs
   66: 
   67: : compile-@local ( n -- ) \ gforth compile-fetch-local
   68:  case
   69:     0       of postpone @local0 endof
   70:     1 cells of postpone @local1 endof
   71:     2 cells of postpone @local2 endof
   72:     3 cells of postpone @local3 endof
   73:    ( otherwise ) dup postpone @local# ,
   74:  endcase ;
   75: 
   76: : compile-f@local ( n -- ) \ gforth compile-f-fetch-local
   77:  case
   78:     0        of postpone f@local0 endof
   79:     1 floats of postpone f@local1 endof
   80:    ( otherwise ) dup postpone f@local# ,
   81:  endcase ;
   82: 
   83: \ the locals stack grows downwards (see primitives)
   84: \ of the local variables of a group (in braces) the leftmost is on top,
   85: \ i.e. by going onto the locals stack the order is reversed.
   86: \ there are alignment gaps if necessary.
   87: \ lp must have the strictest alignment (usually float) across calls;
   88: \ for simplicity we align it strictly for every group.
   89: 
   90: slowvoc @
   91: slowvoc on \ we want a linked list for the vocabulary locals
   92: vocabulary locals \ this contains the local variables
   93: ' locals >body ' locals-list >body !
   94: slowvoc !
   95: 
   96: create locals-buffer 1000 allot \ !! limited and unsafe
   97:     \ here the names of the local variables are stored
   98:     \ we would have problems storing them at the normal dp
   99: 
  100: variable locals-dp \ so here's the special dp for locals.
  101: 
  102: : alignlp-w ( n1 -- n2 )
  103:     \ cell-align size and generate the corresponding code for aligning lp
  104:     aligned dup adjust-locals-size ;
  105: 
  106: : alignlp-f ( n1 -- n2 )
  107:     faligned dup adjust-locals-size ;
  108: 
  109: \ a local declaration group (the braces stuff) is compiled by calling
  110: \ the appropriate compile-pushlocal for the locals, starting with the
  111: \ righmost local; the names are already created earlier, the
  112: \ compile-pushlocal just inserts the offsets from the frame base.
  113: 
  114: : compile-pushlocal-w ( a-addr -- ) ( run-time: w -- )
  115: \ compiles a push of a local variable, and adjusts locals-size
  116: \ stores the offset of the local variable to a-addr
  117:     locals-size @ alignlp-w cell+ dup locals-size !
  118:     swap !
  119:     postpone >l ;
  120: 
  121: : compile-pushlocal-f ( a-addr -- ) ( run-time: f -- )
  122:     locals-size @ alignlp-f float+ dup locals-size !
  123:     swap !
  124:     postpone f>l ;
  125: 
  126: : compile-pushlocal-d ( a-addr -- ) ( run-time: w1 w2 -- )
  127:     locals-size @ alignlp-w cell+ cell+ dup locals-size !
  128:     swap !
  129:     postpone swap postpone >l postpone >l ;
  130: 
  131: : compile-pushlocal-c ( a-addr -- ) ( run-time: w -- )
  132:     -1 chars compile-lp+!
  133:     locals-size @ swap !
  134:     postpone lp@ postpone c! ;
  135: 
  136: : create-local ( " name" -- a-addr )
  137:     \ defines the local "name"; the offset of the local shall be
  138:     \ stored in a-addr
  139:     create
  140: 	immediate restrict
  141: 	here 0 , ( place for the offset ) ;
  142: 
  143: : lp-offset ( n1 -- n2 )
  144: \ converts the offset from the frame start to an offset from lp and
  145: \ i.e., the address of the local is lp+locals_size-offset
  146:   locals-size @ swap - ;
  147: 
  148: : lp-offset, ( n -- )
  149: \ converts the offset from the frame start to an offset from lp and
  150: \ adds it as inline argument to a preceding locals primitive
  151:   lp-offset , ;
  152: 
  153: vocabulary locals-types \ this contains all the type specifyers, -- and }
  154: locals-types definitions
  155: 
  156: : W: ( "name" -- a-addr xt ) \ gforth w-colon
  157:     create-local
  158: 	\ xt produces the appropriate locals pushing code when executed
  159: 	['] compile-pushlocal-w
  160:     does> ( Compilation: -- ) ( Run-time: -- w )
  161:         \ compiles a local variable access
  162: 	@ lp-offset compile-@local ;
  163: 
  164: : W^ ( "name" -- a-addr xt ) \ gforth w-caret
  165:     create-local
  166: 	['] compile-pushlocal-w
  167:     does> ( Compilation: -- ) ( Run-time: -- w )
  168: 	postpone laddr# @ lp-offset, ;
  169: 
  170: : F: ( "name" -- a-addr xt ) \ gforth f-colon
  171:     create-local
  172: 	['] compile-pushlocal-f
  173:     does> ( Compilation: -- ) ( Run-time: -- w )
  174: 	@ lp-offset compile-f@local ;
  175: 
  176: : F^ ( "name" -- a-addr xt ) \ gforth f-caret
  177:     create-local
  178: 	['] compile-pushlocal-f
  179:     does> ( Compilation: -- ) ( Run-time: -- w )
  180: 	postpone laddr# @ lp-offset, ;
  181: 
  182: : D: ( "name" -- a-addr xt ) \ gforth d-colon
  183:     create-local
  184: 	['] compile-pushlocal-d
  185:     does> ( Compilation: -- ) ( Run-time: -- w )
  186: 	postpone laddr# @ lp-offset, postpone 2@ ;
  187: 
  188: : D^ ( "name" -- a-addr xt ) \ gforth d-caret
  189:     create-local
  190: 	['] compile-pushlocal-d
  191:     does> ( Compilation: -- ) ( Run-time: -- w )
  192: 	postpone laddr# @ lp-offset, ;
  193: 
  194: : C: ( "name" -- a-addr xt ) \ gforth c-colon
  195:     create-local
  196: 	['] compile-pushlocal-c
  197:     does> ( Compilation: -- ) ( Run-time: -- w )
  198: 	postpone laddr# @ lp-offset, postpone c@ ;
  199: 
  200: : C^ ( "name" -- a-addr xt ) \ gforth c-caret
  201:     create-local
  202: 	['] compile-pushlocal-c
  203:     does> ( Compilation: -- ) ( Run-time: -- w )
  204: 	postpone laddr# @ lp-offset, ;
  205: 
  206: \ you may want to make comments in a locals definitions group:
  207: ' \ alias \ immediate
  208: ' ( alias ( immediate
  209: 
  210: forth definitions
  211: 
  212: \ the following gymnastics are for declaring locals without type specifier.
  213: \ we exploit a feature of our dictionary: every wordlist
  214: \ has it's own methods for finding words etc.
  215: \ So we create a vocabulary new-locals, that creates a 'w:' local named x
  216: \ when it is asked if it contains x.
  217: 
  218: also locals-types
  219: 
  220: : new-locals-find ( caddr u w -- nfa )
  221: \ this is the find method of the new-locals vocabulary
  222: \ make a new local with name caddr u; w is ignored
  223: \ the returned nfa denotes a word that produces what W: produces
  224: \ !! do the whole thing without nextname
  225:     drop nextname
  226:     ['] W: >name ;
  227: 
  228: previous
  229: 
  230: : new-locals-reveal ( -- )
  231:   true abort" this should not happen: new-locals-reveal" ;
  232: 
  233: create new-locals-map ' new-locals-find A, ' new-locals-reveal A,
  234: 
  235: vocabulary new-locals
  236: new-locals-map ' new-locals >body cell+ A! \ !! use special access words
  237: 
  238: variable old-dpp
  239: 
  240: \ and now, finally, the user interface words
  241: : { ( -- addr wid 0 ) \ gforth open-brace
  242:     dp old-dpp !
  243:     locals-dp dpp !
  244:     also new-locals
  245:     also get-current locals definitions  locals-types
  246:     0 TO locals-wordlist
  247:     0 postpone [ ; immediate
  248: 
  249: locals-types definitions
  250: 
  251: : } ( addr wid 0 a-addr1 xt1 ... -- ) \ gforth close-brace
  252:     \ ends locals definitions
  253:     ] old-dpp @ dpp !
  254:     begin
  255: 	dup
  256:     while
  257: 	execute
  258:     repeat
  259:     drop
  260:     locals-size @ alignlp-f locals-size ! \ the strictest alignment
  261:     set-current
  262:     previous previous
  263:     locals-list TO locals-wordlist ;
  264: 
  265: : -- ( addr wid 0 ... -- ) \ gforth dash-dash
  266:     }
  267:     [char] } parse 2drop ;
  268: 
  269: forth definitions
  270: 
  271: \ A few thoughts on automatic scopes for locals and how they can be
  272: \ implemented:
  273: 
  274: \ We have to combine locals with the control structures. My basic idea
  275: \ was to start the life of a local at the declaration point. The life
  276: \ would end at any control flow join (THEN, BEGIN etc.) where the local
  277: \ is lot live on both input flows (note that the local can still live in
  278: \ other, later parts of the control flow). This would make a local live
  279: \ as long as you expected and sometimes longer (e.g. a local declared in
  280: \ a BEGIN..UNTIL loop would still live after the UNTIL).
  281: 
  282: \ The following example illustrates the problems of this approach:
  283: 
  284: \ { z }
  285: \ if
  286: \   { x }
  287: \ begin
  288: \   { y }
  289: \ [ 1 cs-roll ] then
  290: \   ...
  291: \ until
  292: 
  293: \ x lives only until the BEGIN, but the compiler does not know this
  294: \ until it compiles the UNTIL (it can deduce it at the THEN, because at
  295: \ that point x lives in no thread, but that does not help much). This is
  296: \ solved by optimistically assuming at the BEGIN that x lives, but
  297: \ warning at the UNTIL that it does not. The user is then responsible
  298: \ for checking that x is only used where it lives.
  299: 
  300: \ The produced code might look like this (leaving out alignment code):
  301: 
  302: \ >l ( z )
  303: \ ?branch <then>
  304: \ >l ( x )
  305: \ <begin>:
  306: \ >l ( y )
  307: \ lp+!# 8 ( RIP: x,y )
  308: \ <then>:
  309: \ ...
  310: \ lp+!# -4 ( adjust lp to <begin> state )
  311: \ ?branch <begin>
  312: \ lp+!# 4 ( undo adjust )
  313: 
  314: \ The BEGIN problem also has another incarnation:
  315: 
  316: \ AHEAD
  317: \ BEGIN
  318: \   x
  319: \ [ 1 CS-ROLL ] THEN
  320: \   { x }
  321: \   ...
  322: \ UNTIL
  323: 
  324: \ should be legal: The BEGIN is not a control flow join in this case,
  325: \ since it cannot be entered from the top; therefore the definition of x
  326: \ dominates the use. But the compiler processes the use first, and since
  327: \ it does not look ahead to notice the definition, it will complain
  328: \ about it. Here's another variation of this problem:
  329: 
  330: \ IF
  331: \   { x }
  332: \ ELSE
  333: \   ...
  334: \ AHEAD
  335: \ BEGIN
  336: \   x
  337: \ [ 2 CS-ROLL ] THEN
  338: \   ...
  339: \ UNTIL
  340: 
  341: \ In this case x is defined before the use, and the definition dominates
  342: \ the use, but the compiler does not know this until it processes the
  343: \ UNTIL. So what should the compiler assume does live at the BEGIN, if
  344: \ the BEGIN is not a control flow join? The safest assumption would be
  345: \ the intersection of all locals lists on the control flow
  346: \ stack. However, our compiler assumes that the same variables are live
  347: \ as on the top of the control flow stack. This covers the following case:
  348: 
  349: \ { x }
  350: \ AHEAD
  351: \ BEGIN
  352: \   x
  353: \ [ 1 CS-ROLL ] THEN
  354: \   ...
  355: \ UNTIL
  356: 
  357: \ If this assumption is too optimistic, the compiler will warn the user.
  358: 
  359: \ Implementation: migrated to kernal.fs
  360: 
  361: \ THEN (another control flow from before joins the current one):
  362: \ The new locals-list is the intersection of the current locals-list and
  363: \ the orig-local-list. The new locals-size is the (alignment-adjusted)
  364: \ size of the new locals-list. The following code is generated:
  365: \ lp+!# (current-locals-size - orig-locals-size)
  366: \ <then>:
  367: \ lp+!# (orig-locals-size - new-locals-size)
  368: 
  369: \ Of course "lp+!# 0" is not generated. Still this is admittedly a bit
  370: \ inefficient, e.g. if there is a locals declaration between IF and
  371: \ ELSE. However, if ELSE generates an appropriate "lp+!#" before the
  372: \ branch, there will be none after the target <then>.
  373: 
  374: \ explicit scoping
  375: 
  376: : scope ( compilation  -- scope ; run-time  -- ) \ gforth
  377:  cs-push-part scopestart ; immediate
  378: 
  379: : endscope ( compilation scope -- ; run-time  -- ) \ gforth
  380:  scope?
  381:  drop
  382:  locals-list @ common-list
  383:  dup list-size adjust-locals-size
  384:  locals-list ! ; immediate
  385: 
  386: \ adapt the hooks
  387: 
  388: : locals-:-hook ( sys -- sys addr xt n )
  389:     \ addr is the nfa of the defined word, xt its xt
  390:     DEFERS :-hook
  391:     last @ lastcfa @
  392:     clear-leave-stack
  393:     0 locals-size !
  394:     locals-buffer locals-dp !
  395:     0 locals-list !
  396:     dead-code off
  397:     defstart ;
  398: 
  399: : locals-;-hook ( sys addr xt sys -- sys )
  400:     def?
  401:     0 TO locals-wordlist
  402:     0 adjust-locals-size ( not every def ends with an exit )
  403:     lastcfa ! last !
  404:     DEFERS ;-hook ;
  405: 
  406: ' locals-:-hook IS :-hook
  407: ' locals-;-hook IS ;-hook
  408: 
  409: \ The words in the locals dictionary space are not deleted until the end
  410: \ of the current word. This is a bit too conservative, but very simple.
  411: 
  412: \ There are a few cases to consider: (see above)
  413: 
  414: \ after AGAIN, AHEAD, EXIT (the current control flow is dead):
  415: \ We have to special-case the above cases against that. In this case the
  416: \ things above are not control flow joins. Everything should be taken
  417: \ over from the live flow. No lp+!# is generated.
  418: 
  419: \ !! The lp gymnastics for UNTIL are also a real problem: locals cannot be
  420: \ used in signal handlers (or anything else that may be called while
  421: \ locals live beyond the lp) without changing the locals stack.
  422: 
  423: \ About warning against uses of dead locals. There are several options:
  424: 
  425: \ 1) Do not complain (After all, this is Forth;-)
  426: 
  427: \ 2) Additional restrictions can be imposed so that the situation cannot
  428: \ arise; the programmer would have to introduce explicit scoping
  429: \ declarations in cases like the above one. I.e., complain if there are
  430: \ locals that are live before the BEGIN but not before the corresponding
  431: \ AGAIN (replace DO etc. for BEGIN and UNTIL etc. for AGAIN).
  432: 
  433: \ 3) The real thing: i.e. complain, iff a local lives at a BEGIN, is
  434: \ used on a path starting at the BEGIN, and does not live at the
  435: \ corresponding AGAIN. This is somewhat hard to implement. a) How does
  436: \ the compiler know when it is working on a path starting at a BEGIN
  437: \ (consider "{ x } if begin [ 1 cs-roll ] else x endif again")? b) How
  438: \ is the usage info stored?
  439: 
  440: \ For now I'll resort to alternative 2. When it produces warnings they
  441: \ will often be spurious, but warnings should be rare. And better
  442: \ spurious warnings now and then than days of bug-searching.
  443: 
  444: \ Explicit scoping of locals is implemented by cs-pushing the current
  445: \ locals-list and -size (and an unused cell, to make the size equal to
  446: \ the other entries) at the start of the scope, and restoring them at
  447: \ the end of the scope to the intersection, like THEN does.
  448: 
  449: 
  450: \ And here's finally the ANS standard stuff
  451: 
  452: : (local) ( addr u -- ) \ local paren-local-paren
  453:     \ a little space-inefficient, but well deserved ;-)
  454:     \ In exchange, there are no restrictions whatsoever on using (local)
  455:     \ as long as you use it in a definition
  456:     dup
  457:     if
  458: 	nextname POSTPONE { [ also locals-types ] W: } [ previous ]
  459:     else
  460: 	2drop
  461:     endif ;
  462: 
  463: : >definer ( xt -- definer )
  464:     \ this gives a unique identifier for the way the xt was defined
  465:     \ words defined with different does>-codes have different definers
  466:     \ the definer can be used for comparison and in definer!
  467:     dup >code-address [ ' bits >code-address ] Literal =
  468:     \ !! this definition will not work on some implementations for `bits'
  469:     if  \ if >code-address delivers the same value for all does>-def'd words
  470: 	>does-code 1 or \ bit 0 marks special treatment for does codes
  471:     else
  472: 	>code-address
  473:     then ;
  474: 
  475: : definer! ( definer xt -- )
  476:     \ gives the word represented by xt the behaviour associated with definer
  477:     over 1 and if
  478: 	swap [ 1 invert ] literal and does-code!
  479:     else
  480: 	code-address!
  481:     then ;
  482: 
  483: \ !! untested
  484: : TO ( c|w|d|r "name" -- ) \ core-ext,local
  485: \ !! state smart
  486:  0 0 0. 0.0e0 { c: clocal w: wlocal d: dlocal f: flocal }
  487:  ' dup >definer
  488:  state @ 
  489:  if
  490:    case
  491:      [ ' locals-wordlist >definer ] literal \ value
  492:      OF >body POSTPONE Aliteral POSTPONE ! ENDOF
  493:      [ ' clocal >definer ] literal
  494:      OF POSTPONE laddr# >body @ lp-offset, POSTPONE c! ENDOF
  495:      [ ' wlocal >definer ] literal
  496:      OF POSTPONE laddr# >body @ lp-offset, POSTPONE ! ENDOF
  497:      [ ' dlocal >definer ] literal
  498:      OF POSTPONE laddr# >body @ lp-offset, POSTPONE d! ENDOF
  499:      [ ' flocal >definer ] literal
  500:      OF POSTPONE laddr# >body @ lp-offset, POSTPONE f! ENDOF
  501:      -&32 throw
  502:    endcase
  503:  else
  504:    [ ' locals-wordlist >definer ] literal =
  505:    if
  506:      >body !
  507:    else
  508:      -&32 throw
  509:    endif
  510:  endif ; immediate
  511: 
  512: : locals|
  513:     \ don't use 'locals|'! use '{'! A portable and free '{'
  514:     \ implementation is anslocals.fs
  515:     BEGIN
  516: 	name 2dup s" |" compare 0<>
  517:     WHILE
  518: 	(local)
  519:     REPEAT
  520:     drop 0 (local) ; immediate restrict

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>