Annotation of gforth/glocals-1.60.fs, revision 1.1

1.1     ! anton       1: \ A powerful locals implementation
        !             2: 
        !             3: \ Copyright (C) 1995,1996,1997,1998,2000,2003,2004,2005,2007 Free Software Foundation, Inc.
        !             4: 
        !             5: \ This file is part of Gforth.
        !             6: 
        !             7: \ Gforth is free software; you can redistribute it and/or
        !             8: \ modify it under the terms of the GNU General Public License
        !             9: \ as published by the Free Software Foundation, either version 3
        !            10: \ of the License, or (at your option) any later version.
        !            11: 
        !            12: \ This program is distributed in the hope that it will be useful,
        !            13: \ but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            14: \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            15: \ GNU General Public License for more details.
        !            16: 
        !            17: \ You should have received a copy of the GNU General Public License
        !            18: \ along with this program. If not, see http://www.gnu.org/licenses/.
        !            19: 
        !            20: 
        !            21: \ More documentation can be found in the manual and in
        !            22: \ http://www.complang.tuwien.ac.at/papers/ertl94l.ps.gz
        !            23: 
        !            24: \ Local variables are quite important for writing readable programs, but
        !            25: \ IMO (anton) they are the worst part of the standard. There they are very
        !            26: \ restricted and have an ugly interface.
        !            27: 
        !            28: \ So, we implement the locals wordset, but do not recommend using
        !            29: \ locals-ext (which is a really bad user interface for locals).
        !            30: 
        !            31: \ We also have a nice and powerful user-interface for locals: locals are
        !            32: \ defined with
        !            33: 
        !            34: \ { local1 local2 ... }
        !            35: \ or
        !            36: \ { local1 local2 ... -- ... }
        !            37: \ (anything after the -- is just a comment)
        !            38: 
        !            39: \ Every local in this list consists of an optional type specification
        !            40: \ and a name. If there is only the name, it stands for a cell-sized
        !            41: \ value (i.e., you get the value of the local variable, not it's
        !            42: \ address). The following type specifiers stand before the name:
        !            43: 
        !            44: \ Specifier    Type    Access
        !            45: \ W:           Cell    value
        !            46: \ W^           Cell    address
        !            47: \ D:           Double  value
        !            48: \ D^           Double  address
        !            49: \ F:           Float   value
        !            50: \ F^           Float   address
        !            51: \ C:           Char    value
        !            52: \ C^           Char    address
        !            53: 
        !            54: \ The local variables are initialized with values from the appropriate
        !            55: \ stack. In contrast to the examples in the standard document our locals
        !            56: \ take the arguments in the expected way: The last local gets the top of
        !            57: \ stack, the second last gets the second stack item etc. An example:
        !            58: 
        !            59: \ : CX* { F: Ar  F: Ai  F: Br  F: Bi -- Cr Ci }
        !            60: \ \ complex multiplication
        !            61: \  Ar Br f* Ai Bi f* f-
        !            62: \  Ar Bi f* Ai Br f* f+ ;
        !            63: 
        !            64: \ There will also be a way to add user types, but it is not yet decided,
        !            65: \ how. Ideas are welcome.
        !            66: 
        !            67: \ Locals defined in this manner live until (!! see below). 
        !            68: \ Their names can be used during this time to get
        !            69: \ their value or address; The addresses produced in this way become
        !            70: \ invalid at the end of the lifetime.
        !            71: 
        !            72: \ Values can be changed with TO, but this is not recomended (TO is a
        !            73: \ kludge and words lose the single-assignment property, which makes them
        !            74: \ harder to analyse).
        !            75: 
        !            76: \ As for the internals, we use a special locals stack. This eliminates
        !            77: \ the problems and restrictions of reusing the return stack and allows
        !            78: \ to store floats as locals: the return stack is not guaranteed to be
        !            79: \ aligned correctly, but our locals stack must be float-aligned between
        !            80: \ words.
        !            81: 
        !            82: \ Other things about the internals are pretty unclear now.
        !            83: 
        !            84: \ Currently locals may only be
        !            85: \ defined at the outer level and TO is not supported.
        !            86: 
        !            87: require search.fs
        !            88: require float.fs
        !            89: require extend.fs \ for case
        !            90: 
        !            91: : compile-@local ( n -- ) \ gforth compile-fetch-local
        !            92:  case
        !            93:     0       of postpone @local0 endof
        !            94:     1 cells of postpone @local1 endof
        !            95:     2 cells of postpone @local2 endof
        !            96:     3 cells of postpone @local3 endof
        !            97:    ( otherwise ) dup postpone @local# ,
        !            98:  endcase ;
        !            99: 
        !           100: : compile-f@local ( n -- ) \ gforth compile-f-fetch-local
        !           101:  case
        !           102:     0        of postpone f@local0 endof
        !           103:     1 floats of postpone f@local1 endof
        !           104:    ( otherwise ) dup postpone f@local# ,
        !           105:  endcase ;
        !           106: 
        !           107: \ locals stuff needed for control structures
        !           108: 
        !           109: : compile-lp+! ( n -- ) \ gforth       compile-l-p-plus-store
        !           110:     dup negate locals-size +!
        !           111:     0 over = if
        !           112:     else -1 cells  over = if postpone lp-
        !           113:     else  1 floats over = if postpone lp+
        !           114:     else  2 floats over = if postpone lp+2
        !           115:     else postpone lp+!# dup ,
        !           116:     then then then then drop ;
        !           117: 
        !           118: : adjust-locals-size ( n -- ) \ gforth
        !           119:     \ sets locals-size to n and generates an appropriate lp+!
        !           120:     locals-size @ swap - compile-lp+! ;
        !           121: 
        !           122: \ the locals stack grows downwards (see primitives)
        !           123: \ of the local variables of a group (in braces) the leftmost is on top,
        !           124: \ i.e. by going onto the locals stack the order is reversed.
        !           125: \ there are alignment gaps if necessary.
        !           126: \ lp must have the strictest alignment (usually float) across calls;
        !           127: \ for simplicity we align it strictly for every group.
        !           128: 
        !           129: slowvoc @
        !           130: slowvoc on \ we want a linked list for the vocabulary locals
        !           131: vocabulary locals \ this contains the local variables
        !           132: ' locals >body wordlist-id ' locals-list >body !
        !           133: slowvoc !
        !           134: 
        !           135: create locals-buffer 1000 allot \ !! limited and unsafe
        !           136:     \ here the names of the local variables are stored
        !           137:     \ we would have problems storing them at the normal dp
        !           138: 
        !           139: variable locals-dp \ so here's the special dp for locals.
        !           140: 
        !           141: : alignlp-w ( n1 -- n2 )
        !           142:     \ cell-align size and generate the corresponding code for aligning lp
        !           143:     aligned dup adjust-locals-size ;
        !           144: 
        !           145: : alignlp-f ( n1 -- n2 )
        !           146:     faligned dup adjust-locals-size ;
        !           147: 
        !           148: \ a local declaration group (the braces stuff) is compiled by calling
        !           149: \ the appropriate compile-pushlocal for the locals, starting with the
        !           150: \ righmost local; the names are already created earlier, the
        !           151: \ compile-pushlocal just inserts the offsets from the frame base.
        !           152: 
        !           153: : compile-pushlocal-w ( a-addr -- ) ( run-time: w -- )
        !           154: \ compiles a push of a local variable, and adjusts locals-size
        !           155: \ stores the offset of the local variable to a-addr
        !           156:     locals-size @ alignlp-w cell+ dup locals-size !
        !           157:     swap !
        !           158:     postpone >l ;
        !           159: 
        !           160: \ locals list operations
        !           161: 
        !           162: : common-list ( list1 list2 -- list3 ) \ gforth-internal
        !           163: \ list1 and list2 are lists, where the heads are at higher addresses than
        !           164: \ the tail. list3 is the largest sublist of both lists.
        !           165:  begin
        !           166:    2dup u<>
        !           167:  while
        !           168:    2dup u>
        !           169:    if
        !           170:      swap
        !           171:    then
        !           172:    @
        !           173:  repeat
        !           174:  drop ;
        !           175: 
        !           176: : sub-list? ( list1 list2 -- f ) \ gforth-internal
        !           177: \ true iff list1 is a sublist of list2
        !           178:  begin
        !           179:    2dup u<
        !           180:  while
        !           181:    @
        !           182:  repeat
        !           183:  = ;
        !           184: 
        !           185: : list-size ( list -- u ) \ gforth-internal
        !           186:     \ size of the locals frame represented by list
        !           187:     0 ( list n )
        !           188:     begin
        !           189:        over 0<>
        !           190:     while
        !           191:        over
        !           192:        ((name>)) >body @ max
        !           193:        swap @ swap ( get next )
        !           194:     repeat
        !           195:     faligned nip ;
        !           196: 
        !           197: : set-locals-size-list ( list -- )
        !           198:     dup locals-list !
        !           199:     list-size locals-size ! ;
        !           200: 
        !           201: : check-begin ( list -- )
        !           202: \ warn if list is not a sublist of locals-list
        !           203:  locals-list @ sub-list? 0= if
        !           204:    \ !! print current position
        !           205:    ." compiler was overly optimistic about locals at a BEGIN" cr
        !           206:    \ !! print assumption and reality
        !           207:  then ;
        !           208: 
        !           209: : compile-pushlocal-f ( a-addr -- ) ( run-time: f -- )
        !           210:     locals-size @ alignlp-f float+ dup locals-size !
        !           211:     swap !
        !           212:     postpone f>l ;
        !           213: 
        !           214: : compile-pushlocal-d ( a-addr -- ) ( run-time: w1 w2 -- )
        !           215:     locals-size @ alignlp-w cell+ cell+ dup locals-size !
        !           216:     swap !
        !           217:     postpone swap postpone >l postpone >l ;
        !           218: 
        !           219: : compile-pushlocal-c ( a-addr -- ) ( run-time: w -- )
        !           220:     -1 chars compile-lp+!
        !           221:     locals-size @ swap !
        !           222:     postpone lp@ postpone c! ;
        !           223: 
        !           224: : create-local ( " name" -- a-addr )
        !           225:     \ defines the local "name"; the offset of the local shall be
        !           226:     \ stored in a-addr
        !           227:     create
        !           228:        immediate restrict
        !           229:        here 0 , ( place for the offset ) ;
        !           230: 
        !           231: : lp-offset ( n1 -- n2 )
        !           232: \ converts the offset from the frame start to an offset from lp and
        !           233: \ i.e., the address of the local is lp+locals_size-offset
        !           234:   locals-size @ swap - ;
        !           235: 
        !           236: : lp-offset, ( n -- )
        !           237: \ converts the offset from the frame start to an offset from lp and
        !           238: \ adds it as inline argument to a preceding locals primitive
        !           239:   lp-offset , ;
        !           240: 
        !           241: vocabulary locals-types \ this contains all the type specifyers, -- and }
        !           242: locals-types definitions
        !           243: 
        !           244: : W: ( "name" -- a-addr xt ) \ gforth w-colon
        !           245:     create-local
        !           246:        \ xt produces the appropriate locals pushing code when executed
        !           247:        ['] compile-pushlocal-w
        !           248:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           249:         \ compiles a local variable access
        !           250:        @ lp-offset compile-@local ;
        !           251: 
        !           252: : W^ ( "name" -- a-addr xt ) \ gforth w-caret
        !           253:     create-local
        !           254:        ['] compile-pushlocal-w
        !           255:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           256:        postpone laddr# @ lp-offset, ;
        !           257: 
        !           258: : F: ( "name" -- a-addr xt ) \ gforth f-colon
        !           259:     create-local
        !           260:        ['] compile-pushlocal-f
        !           261:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           262:        @ lp-offset compile-f@local ;
        !           263: 
        !           264: : F^ ( "name" -- a-addr xt ) \ gforth f-caret
        !           265:     create-local
        !           266:        ['] compile-pushlocal-f
        !           267:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           268:        postpone laddr# @ lp-offset, ;
        !           269: 
        !           270: : D: ( "name" -- a-addr xt ) \ gforth d-colon
        !           271:     create-local
        !           272:        ['] compile-pushlocal-d
        !           273:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           274:        postpone laddr# @ lp-offset, postpone 2@ ;
        !           275: 
        !           276: : D^ ( "name" -- a-addr xt ) \ gforth d-caret
        !           277:     create-local
        !           278:        ['] compile-pushlocal-d
        !           279:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           280:        postpone laddr# @ lp-offset, ;
        !           281: 
        !           282: : C: ( "name" -- a-addr xt ) \ gforth c-colon
        !           283:     create-local
        !           284:        ['] compile-pushlocal-c
        !           285:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           286:        postpone laddr# @ lp-offset, postpone c@ ;
        !           287: 
        !           288: : C^ ( "name" -- a-addr xt ) \ gforth c-caret
        !           289:     create-local
        !           290:        ['] compile-pushlocal-c
        !           291:     does> ( Compilation: -- ) ( Run-time: -- w )
        !           292:        postpone laddr# @ lp-offset, ;
        !           293: 
        !           294: \ you may want to make comments in a locals definitions group:
        !           295: ' \ alias \ ( compilation 'ccc<newline>' -- ; run-time -- ) \ core-ext,block-ext backslash
        !           296: \G Comment till the end of the line if @code{BLK} contains 0 (i.e.,
        !           297: \G while not loading a block), parse and discard the remainder of the
        !           298: \G parse area. Otherwise, parse and discard all subsequent characters
        !           299: \G in the parse area corresponding to the current line.
        !           300: immediate
        !           301: 
        !           302: ' ( alias ( ( compilation 'ccc<close-paren>' -- ; run-time -- ) \ core,file    paren
        !           303: \G Comment, usually till the next @code{)}: parse and discard all
        !           304: \G subsequent characters in the parse area until ")" is
        !           305: \G encountered. During interactive input, an end-of-line also acts as
        !           306: \G a comment terminator. For file input, it does not; if the
        !           307: \G end-of-file is encountered whilst parsing for the ")" delimiter,
        !           308: \G Gforth will generate a warning.
        !           309: immediate
        !           310: 
        !           311: forth definitions
        !           312: also locals-types
        !           313:     
        !           314: \ these "locals" are used for comparison in TO
        !           315: 
        !           316: c: some-clocal 2drop
        !           317: d: some-dlocal 2drop
        !           318: f: some-flocal 2drop
        !           319: w: some-wlocal 2drop
        !           320:     
        !           321: \ the following gymnastics are for declaring locals without type specifier.
        !           322: \ we exploit a feature of our dictionary: every wordlist
        !           323: \ has it's own methods for finding words etc.
        !           324: \ So we create a vocabulary new-locals, that creates a 'w:' local named x
        !           325: \ when it is asked if it contains x.
        !           326: 
        !           327: : new-locals-find ( caddr u w -- nfa )
        !           328: \ this is the find method of the new-locals vocabulary
        !           329: \ make a new local with name caddr u; w is ignored
        !           330: \ the returned nfa denotes a word that produces what W: produces
        !           331: \ !! do the whole thing without nextname
        !           332:     drop nextname
        !           333:     ['] W: >head-noprim ;
        !           334: 
        !           335: previous
        !           336: 
        !           337: : new-locals-reveal ( -- )
        !           338:   true abort" this should not happen: new-locals-reveal" ;
        !           339: 
        !           340: create new-locals-map ( -- wordlist-map )
        !           341: ' new-locals-find A,
        !           342: ' new-locals-reveal A,
        !           343: ' drop A, \ rehash method
        !           344: ' drop A,
        !           345: 
        !           346: new-locals-map mappedwordlist Constant new-locals-wl
        !           347: 
        !           348: \ slowvoc @
        !           349: \ slowvoc on
        !           350: \ vocabulary new-locals
        !           351: \ slowvoc !
        !           352: \ new-locals-map ' new-locals >body wordlist-map A! \ !! use special access words
        !           353: 
        !           354: variable old-dpp
        !           355: 
        !           356: \ and now, finally, the user interface words
        !           357: : { ( -- latestxt wid 0 ) \ gforth open-brace
        !           358:     dp old-dpp !
        !           359:     locals-dp dpp !
        !           360:     latestxt get-current
        !           361:     get-order new-locals-wl swap 1+ set-order
        !           362:     also locals definitions locals-types
        !           363:     0 TO locals-wordlist
        !           364:     0 postpone [ ; immediate
        !           365: 
        !           366: locals-types definitions
        !           367: 
        !           368: : } ( latestxt wid 0 a-addr1 xt1 ... -- ) \ gforth close-brace
        !           369:     \ ends locals definitions
        !           370:     ] old-dpp @ dpp !
        !           371:     begin
        !           372:        dup
        !           373:     while
        !           374:        execute
        !           375:     repeat
        !           376:     drop
        !           377:     locals-size @ alignlp-f locals-size ! \ the strictest alignment
        !           378:     previous previous
        !           379:     set-current lastcfa !
        !           380:     locals-list 0 wordlist-id - TO locals-wordlist ;
        !           381: 
        !           382: : -- ( addr wid 0 ... -- ) \ gforth dash-dash
        !           383:     }
        !           384:     [char] } parse 2drop ;
        !           385: 
        !           386: forth definitions
        !           387: 
        !           388: \ A few thoughts on automatic scopes for locals and how they can be
        !           389: \ implemented:
        !           390: 
        !           391: \ We have to combine locals with the control structures. My basic idea
        !           392: \ was to start the life of a local at the declaration point. The life
        !           393: \ would end at any control flow join (THEN, BEGIN etc.) where the local
        !           394: \ is lot live on both input flows (note that the local can still live in
        !           395: \ other, later parts of the control flow). This would make a local live
        !           396: \ as long as you expected and sometimes longer (e.g. a local declared in
        !           397: \ a BEGIN..UNTIL loop would still live after the UNTIL).
        !           398: 
        !           399: \ The following example illustrates the problems of this approach:
        !           400: 
        !           401: \ { z }
        !           402: \ if
        !           403: \   { x }
        !           404: \ begin
        !           405: \   { y }
        !           406: \ [ 1 cs-roll ] then
        !           407: \   ...
        !           408: \ until
        !           409: 
        !           410: \ x lives only until the BEGIN, but the compiler does not know this
        !           411: \ until it compiles the UNTIL (it can deduce it at the THEN, because at
        !           412: \ that point x lives in no thread, but that does not help much). This is
        !           413: \ solved by optimistically assuming at the BEGIN that x lives, but
        !           414: \ warning at the UNTIL that it does not. The user is then responsible
        !           415: \ for checking that x is only used where it lives.
        !           416: 
        !           417: \ The produced code might look like this (leaving out alignment code):
        !           418: 
        !           419: \ >l ( z )
        !           420: \ ?branch <then>
        !           421: \ >l ( x )
        !           422: \ <begin>:
        !           423: \ >l ( y )
        !           424: \ lp+!# 8 ( RIP: x,y )
        !           425: \ <then>:
        !           426: \ ...
        !           427: \ lp+!# -4 ( adjust lp to <begin> state )
        !           428: \ ?branch <begin>
        !           429: \ lp+!# 4 ( undo adjust )
        !           430: 
        !           431: \ The BEGIN problem also has another incarnation:
        !           432: 
        !           433: \ AHEAD
        !           434: \ BEGIN
        !           435: \   x
        !           436: \ [ 1 CS-ROLL ] THEN
        !           437: \   { x }
        !           438: \   ...
        !           439: \ UNTIL
        !           440: 
        !           441: \ should be legal: The BEGIN is not a control flow join in this case,
        !           442: \ since it cannot be entered from the top; therefore the definition of x
        !           443: \ dominates the use. But the compiler processes the use first, and since
        !           444: \ it does not look ahead to notice the definition, it will complain
        !           445: \ about it. Here's another variation of this problem:
        !           446: 
        !           447: \ IF
        !           448: \   { x }
        !           449: \ ELSE
        !           450: \   ...
        !           451: \ AHEAD
        !           452: \ BEGIN
        !           453: \   x
        !           454: \ [ 2 CS-ROLL ] THEN
        !           455: \   ...
        !           456: \ UNTIL
        !           457: 
        !           458: \ In this case x is defined before the use, and the definition dominates
        !           459: \ the use, but the compiler does not know this until it processes the
        !           460: \ UNTIL. So what should the compiler assume does live at the BEGIN, if
        !           461: \ the BEGIN is not a control flow join? The safest assumption would be
        !           462: \ the intersection of all locals lists on the control flow
        !           463: \ stack. However, our compiler assumes that the same variables are live
        !           464: \ as on the top of the control flow stack. This covers the following case:
        !           465: 
        !           466: \ { x }
        !           467: \ AHEAD
        !           468: \ BEGIN
        !           469: \   x
        !           470: \ [ 1 CS-ROLL ] THEN
        !           471: \   ...
        !           472: \ UNTIL
        !           473: 
        !           474: \ If this assumption is too optimistic, the compiler will warn the user.
        !           475: 
        !           476: \ Implementation:
        !           477: 
        !           478: \ explicit scoping
        !           479: 
        !           480: : scope ( compilation  -- scope ; run-time  -- ) \ gforth
        !           481:     cs-push-part scopestart ; immediate
        !           482: 
        !           483: : adjust-locals-list ( wid -- )
        !           484:     locals-list @ common-list
        !           485:     dup list-size adjust-locals-size
        !           486:     locals-list ! ;
        !           487: 
        !           488: : endscope ( compilation scope -- ; run-time  -- ) \ gforth
        !           489:     scope?
        !           490:     drop  adjust-locals-list ; immediate
        !           491: 
        !           492: \ adapt the hooks
        !           493: 
        !           494: : locals-:-hook ( sys -- sys addr xt n )
        !           495:     \ addr is the nfa of the defined word, xt its xt
        !           496:     DEFERS :-hook
        !           497:     latest latestxt
        !           498:     clear-leave-stack
        !           499:     0 locals-size !
        !           500:     locals-buffer locals-dp !
        !           501:     0 locals-list !
        !           502:     dead-code off
        !           503:     defstart ;
        !           504: 
        !           505: : locals-;-hook ( sys addr xt sys -- sys )
        !           506:     def?
        !           507:     0 TO locals-wordlist
        !           508:     0 adjust-locals-size ( not every def ends with an exit )
        !           509:     lastcfa ! last !
        !           510:     DEFERS ;-hook ;
        !           511: 
        !           512: \ THEN (another control flow from before joins the current one):
        !           513: \ The new locals-list is the intersection of the current locals-list and
        !           514: \ the orig-local-list. The new locals-size is the (alignment-adjusted)
        !           515: \ size of the new locals-list. The following code is generated:
        !           516: \ lp+!# (current-locals-size - orig-locals-size)
        !           517: \ <then>:
        !           518: \ lp+!# (orig-locals-size - new-locals-size)
        !           519: 
        !           520: \ Of course "lp+!# 0" is not generated. Still this is admittedly a bit
        !           521: \ inefficient, e.g. if there is a locals declaration between IF and
        !           522: \ ELSE. However, if ELSE generates an appropriate "lp+!#" before the
        !           523: \ branch, there will be none after the target <then>.
        !           524: 
        !           525: : (then-like) ( orig -- )
        !           526:     dead-orig =
        !           527:     if
        !           528:        >resolve drop
        !           529:     else
        !           530:         dead-code @
        !           531:         if
        !           532:            >resolve set-locals-size-list dead-code off
        !           533:        else \ both live
        !           534:            over list-size adjust-locals-size
        !           535:            >resolve
        !           536:            adjust-locals-list
        !           537:        then
        !           538:     then ;
        !           539: 
        !           540: : (begin-like) ( -- )
        !           541:     dead-code @ if
        !           542:        \ set up an assumption of the locals visible here.  if the
        !           543:        \ users want something to be visible, they have to declare
        !           544:        \ that using ASSUME-LIVE
        !           545:        backedge-locals @ set-locals-size-list
        !           546:     then
        !           547:     dead-code off ;
        !           548: 
        !           549: \ AGAIN (the current control flow joins another, earlier one):
        !           550: \ If the dest-locals-list is not a subset of the current locals-list,
        !           551: \ issue a warning (see below). The following code is generated:
        !           552: \ lp+!# (current-local-size - dest-locals-size)
        !           553: \ branch <begin>
        !           554: 
        !           555: : (again-like) ( dest -- addr )
        !           556:     over list-size adjust-locals-size
        !           557:     swap check-begin  POSTPONE unreachable ;
        !           558: 
        !           559: \ UNTIL (the current control flow may join an earlier one or continue):
        !           560: \ Similar to AGAIN. The new locals-list and locals-size are the current
        !           561: \ ones. The following code is generated:
        !           562: \ ?branch-lp+!# <begin> (current-local-size - dest-locals-size)
        !           563: 
        !           564: : (until-like) ( list addr xt1 xt2 -- )
        !           565:     \ list and addr are a fragment of a cs-item
        !           566:     \ xt1 is the conditional branch without lp adjustment, xt2 is with
        !           567:     >r >r
        !           568:     locals-size @ 2 pick list-size - dup if ( list dest-addr adjustment )
        !           569:        r> drop r> compile,
        !           570:        swap <resolve ( list adjustment ) ,
        !           571:     else ( list dest-addr adjustment )
        !           572:        drop
        !           573:        r> compile, <resolve
        !           574:        r> drop
        !           575:     then ( list )
        !           576:     check-begin ;
        !           577: 
        !           578: : (exit-like) ( -- )
        !           579:     0 adjust-locals-size ;
        !           580: 
        !           581: ' locals-:-hook IS :-hook
        !           582: ' locals-;-hook IS ;-hook
        !           583: 
        !           584: ' (then-like)  IS then-like
        !           585: ' (begin-like) IS begin-like
        !           586: ' (again-like) IS again-like
        !           587: ' (until-like) IS until-like
        !           588: ' (exit-like)  IS exit-like
        !           589: 
        !           590: \ The words in the locals dictionary space are not deleted until the end
        !           591: \ of the current word. This is a bit too conservative, but very simple.
        !           592: 
        !           593: \ There are a few cases to consider: (see above)
        !           594: 
        !           595: \ after AGAIN, AHEAD, EXIT (the current control flow is dead):
        !           596: \ We have to special-case the above cases against that. In this case the
        !           597: \ things above are not control flow joins. Everything should be taken
        !           598: \ over from the live flow. No lp+!# is generated.
        !           599: 
        !           600: \ About warning against uses of dead locals. There are several options:
        !           601: 
        !           602: \ 1) Do not complain (After all, this is Forth;-)
        !           603: 
        !           604: \ 2) Additional restrictions can be imposed so that the situation cannot
        !           605: \ arise; the programmer would have to introduce explicit scoping
        !           606: \ declarations in cases like the above one. I.e., complain if there are
        !           607: \ locals that are live before the BEGIN but not before the corresponding
        !           608: \ AGAIN (replace DO etc. for BEGIN and UNTIL etc. for AGAIN).
        !           609: 
        !           610: \ 3) The real thing: i.e. complain, iff a local lives at a BEGIN, is
        !           611: \ used on a path starting at the BEGIN, and does not live at the
        !           612: \ corresponding AGAIN. This is somewhat hard to implement. a) How does
        !           613: \ the compiler know when it is working on a path starting at a BEGIN
        !           614: \ (consider "{ x } if begin [ 1 cs-roll ] else x endif again")? b) How
        !           615: \ is the usage info stored?
        !           616: 
        !           617: \ For now I'll resort to alternative 2. When it produces warnings they
        !           618: \ will often be spurious, but warnings should be rare. And better
        !           619: \ spurious warnings now and then than days of bug-searching.
        !           620: 
        !           621: \ Explicit scoping of locals is implemented by cs-pushing the current
        !           622: \ locals-list and -size (and an unused cell, to make the size equal to
        !           623: \ the other entries) at the start of the scope, and restoring them at
        !           624: \ the end of the scope to the intersection, like THEN does.
        !           625: 
        !           626: 
        !           627: \ And here's finally the ANS standard stuff
        !           628: 
        !           629: : (local) ( addr u -- ) \ local paren-local-paren
        !           630:     \ a little space-inefficient, but well deserved ;-)
        !           631:     \ In exchange, there are no restrictions whatsoever on using (local)
        !           632:     \ as long as you use it in a definition
        !           633:     dup
        !           634:     if
        !           635:        nextname POSTPONE { [ also locals-types ] W: } [ previous ]
        !           636:     else
        !           637:        2drop
        !           638:     endif ;
        !           639: 
        !           640: : >definer ( xt -- definer ) \ gforth
        !           641:     \G @var{Definer} is a unique identifier for the way the @var{xt}
        !           642:     \G was defined.  Words defined with different @code{does>}-codes
        !           643:     \G have different definers.  The definer can be used for
        !           644:     \G comparison and in @code{definer!}.
        !           645:     dup >does-code
        !           646:     ?dup-if
        !           647:        nip 1 or
        !           648:     else
        !           649:        >code-address
        !           650:     then ;
        !           651: 
        !           652: : definer! ( definer xt -- ) \ gforth
        !           653:     \G The word represented by @var{xt} changes its behaviour to the
        !           654:     \G behaviour associated with @var{definer}.
        !           655:     over 1 and if
        !           656:        swap [ 1 invert ] literal and does-code!
        !           657:     else
        !           658:        code-address!
        !           659:     then ;
        !           660: 
        !           661: :noname
        !           662:     ' dup >definer [ ' locals-wordlist ] literal >definer =
        !           663:     if
        !           664:        >body !
        !           665:     else
        !           666:        -&32 throw
        !           667:     endif ;
        !           668: :noname
        !           669:     comp' drop dup >definer
        !           670:     case
        !           671:        [ ' locals-wordlist ] literal >definer \ value
        !           672:        OF >body POSTPONE Aliteral POSTPONE ! ENDOF
        !           673:        \ !! dependent on c: etc. being does>-defining words
        !           674:        \ this works, because >definer uses >does-code in this case,
        !           675:        \ which produces a relocatable address
        !           676:        [ comp' some-clocal drop ] literal >definer
        !           677:        OF POSTPONE laddr# >body @ lp-offset, POSTPONE c! ENDOF
        !           678:        [ comp' some-wlocal drop ] literal >definer
        !           679:        OF POSTPONE laddr# >body @ lp-offset, POSTPONE ! ENDOF
        !           680:        [ comp' some-dlocal drop ] literal >definer
        !           681:        OF POSTPONE laddr# >body @ lp-offset, POSTPONE 2! ENDOF
        !           682:        [ comp' some-flocal drop ] literal >definer
        !           683:        OF POSTPONE laddr# >body @ lp-offset, POSTPONE f! ENDOF
        !           684:        -&32 throw
        !           685:     endcase ;
        !           686: interpret/compile: TO ( c|w|d|r "name" -- ) \ core-ext,local
        !           687: 
        !           688: : locals| ( ... "name ..." -- ) \ local-ext locals-bar
        !           689:     \ don't use 'locals|'! use '{'! A portable and free '{'
        !           690:     \ implementation is compat/anslocals.fs
        !           691:     BEGIN
        !           692:        name 2dup s" |" str= 0=
        !           693:     WHILE
        !           694:        (local)
        !           695:     REPEAT
        !           696:     drop 0 (local) ; immediate restrict

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