| |
\ A powerful locals implementation |
| |
|
| |
\ Copyright (C) 1995,1996,1997,1998,2000,2003 Free Software Foundation, Inc. |
| |
|
| |
\ This file is part of Gforth. |
| |
|
| |
\ Gforth is free software; you can redistribute it and/or |
| |
\ modify it under the terms of the GNU General Public License |
| |
\ as published by the Free Software Foundation; either version 2 |
| |
\ of the License, or (at your option) any later version. |
| |
|
| |
\ This program is distributed in the hope that it will be useful, |
| |
\ but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
\ GNU General Public License for more details. |
| |
|
| |
\ You should have received a copy of the GNU General Public License |
| |
\ along with this program; if not, write to the Free Software |
| |
\ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
| |
|
| |
|
| |
\ More documentation can be found in the manual and in |
| |
\ http://www.complang.tuwien.ac.at/papers/ertl94l.ps.gz |
| |
|
| \ Local variables are quite important for writing readable programs, but |
\ Local variables are quite important for writing readable programs, but |
| \ IMO (anton) they are the worst part of the standard. There they are very |
\ IMO (anton) they are the worst part of the standard. There they are very |
| \ restricted and have an ugly interface. |
\ restricted and have an ugly interface. |
| \ Currently locals may only be |
\ Currently locals may only be |
| \ defined at the outer level and TO is not supported. |
\ defined at the outer level and TO is not supported. |
| |
|
| include float.fs |
require search.fs |
| include search-order.fs |
require float.fs |
| |
require extend.fs \ for case |
| |
|
| |
: compile-@local ( n -- ) \ gforth compile-fetch-local |
| |
case |
| |
0 of postpone @local0 endof |
| |
1 cells of postpone @local1 endof |
| |
2 cells of postpone @local2 endof |
| |
3 cells of postpone @local3 endof |
| |
( otherwise ) dup postpone @local# , |
| |
endcase ; |
| |
|
| |
: compile-f@local ( n -- ) \ gforth compile-f-fetch-local |
| |
case |
| |
0 of postpone f@local0 endof |
| |
1 floats of postpone f@local1 endof |
| |
( otherwise ) dup postpone f@local# , |
| |
endcase ; |
| |
|
| |
\ locals stuff needed for control structures |
| |
|
| |
: compile-lp+! ( n -- ) \ gforth compile-l-p-plus-store |
| |
dup negate locals-size +! |
| |
0 over = if |
| |
else -1 cells over = if postpone lp- |
| |
else 1 floats over = if postpone lp+ |
| |
else 2 floats over = if postpone lp+2 |
| |
else postpone lp+!# dup , |
| |
then then then then drop ; |
| |
|
| |
: adjust-locals-size ( n -- ) \ gforth |
| |
\ sets locals-size to n and generates an appropriate lp+! |
| |
locals-size @ swap - compile-lp+! ; |
| |
|
| \ the locals stack grows downwards (see primitives) |
\ the locals stack grows downwards (see primitives) |
| \ of the local variables of a group (in braces) the leftmost is on top, |
\ of the local variables of a group (in braces) the leftmost is on top, |
| \ lp must have the strictest alignment (usually float) across calls; |
\ lp must have the strictest alignment (usually float) across calls; |
| \ for simplicity we align it strictly for every group. |
\ for simplicity we align it strictly for every group. |
| |
|
| |
slowvoc @ |
| |
slowvoc on \ we want a linked list for the vocabulary locals |
| vocabulary locals \ this contains the local variables |
vocabulary locals \ this contains the local variables |
| ' locals >body Constant locals-list \ acts like a variable that contains |
' locals >body wordlist-id ' locals-list >body ! |
| \ a linear list of locals names |
slowvoc ! |
| |
|
| create locals-buffer 1000 allot \ !! limited and unsafe |
create locals-buffer 1000 allot \ !! limited and unsafe |
| \ here the names of the local variables are stored |
\ here the names of the local variables are stored |
| |
|
| : alignlp-w ( n1 -- n2 ) |
: alignlp-w ( n1 -- n2 ) |
| \ cell-align size and generate the corresponding code for aligning lp |
\ cell-align size and generate the corresponding code for aligning lp |
| dup aligned tuck - compile-lp+!# ; |
aligned dup adjust-locals-size ; |
| |
|
| : alignlp-f ( n1 -- n2 ) |
: alignlp-f ( n1 -- n2 ) |
| dup faligned tuck - compile-lp+!# ; |
faligned dup adjust-locals-size ; |
| |
|
| \ a local declaration group (the braces stuff) is compiled by calling |
\ a local declaration group (the braces stuff) is compiled by calling |
| \ the appropriate compile-pushlocal for the locals, starting with the |
\ the appropriate compile-pushlocal for the locals, starting with the |
| swap ! |
swap ! |
| postpone >l ; |
postpone >l ; |
| |
|
| |
\ locals list operations |
| |
|
| |
: common-list ( list1 list2 -- list3 ) \ gforth-internal |
| |
\ list1 and list2 are lists, where the heads are at higher addresses than |
| |
\ the tail. list3 is the largest sublist of both lists. |
| |
begin |
| |
2dup u<> |
| |
while |
| |
2dup u> |
| |
if |
| |
swap |
| |
then |
| |
@ |
| |
repeat |
| |
drop ; |
| |
|
| |
: sub-list? ( list1 list2 -- f ) \ gforth-internal |
| |
\ true iff list1 is a sublist of list2 |
| |
begin |
| |
2dup u< |
| |
while |
| |
@ |
| |
repeat |
| |
= ; |
| |
|
| |
: list-size ( list -- u ) \ gforth-internal |
| |
\ size of the locals frame represented by list |
| |
0 ( list n ) |
| |
begin |
| |
over 0<> |
| |
while |
| |
over |
| |
((name>)) >body @ max |
| |
swap @ swap ( get next ) |
| |
repeat |
| |
faligned nip ; |
| |
|
| |
: set-locals-size-list ( list -- ) |
| |
dup locals-list ! |
| |
list-size locals-size ! ; |
| |
|
| |
: check-begin ( list -- ) |
| |
\ warn if list is not a sublist of locals-list |
| |
locals-list @ sub-list? 0= if |
| |
\ !! print current position |
| |
." compiler was overly optimistic about locals at a BEGIN" cr |
| |
\ !! print assumption and reality |
| |
then ; |
| |
|
| : compile-pushlocal-f ( a-addr -- ) ( run-time: f -- ) |
: compile-pushlocal-f ( a-addr -- ) ( run-time: f -- ) |
| locals-size @ alignlp-f float+ dup locals-size ! |
locals-size @ alignlp-f float+ dup locals-size ! |
| swap ! |
swap ! |
| postpone swap postpone >l postpone >l ; |
postpone swap postpone >l postpone >l ; |
| |
|
| : compile-pushlocal-c ( a-addr -- ) ( run-time: w -- ) |
: compile-pushlocal-c ( a-addr -- ) ( run-time: w -- ) |
| -1 chars compile-lp+!# |
-1 chars compile-lp+! |
| locals-size @ swap ! |
locals-size @ swap ! |
| postpone lp@ postpone c! ; |
postpone lp@ postpone c! ; |
| |
|
| : create-local ( " name" -- a-addr ) |
: create-local ( " name" -- a-addr ) |
| \ defines the local "name"; the offset of the local shall be stored in a-addr |
\ defines the local "name"; the offset of the local shall be |
| |
\ stored in a-addr |
| create |
create |
| immediate |
immediate restrict |
| here 0 , ( place for the offset ) ; |
here 0 , ( place for the offset ) ; |
| |
|
| |
: lp-offset ( n1 -- n2 ) |
| |
\ converts the offset from the frame start to an offset from lp and |
| |
\ i.e., the address of the local is lp+locals_size-offset |
| |
locals-size @ swap - ; |
| |
|
| : lp-offset, ( n -- ) |
: lp-offset, ( n -- ) |
| \ converts the offset from the frame start to an offset from lp and |
\ converts the offset from the frame start to an offset from lp and |
| \ adds it as inline argument to a preceding locals primitive |
\ adds it as inline argument to a preceding locals primitive |
| \ i.e., the address of the local is lp+locals_size-offset |
lp-offset , ; |
| locals-size @ swap - , ; |
|
| |
|
| vocabulary locals-types \ this contains all the type specifyers, -- and } |
vocabulary locals-types \ this contains all the type specifyers, -- and } |
| locals-types definitions |
locals-types definitions |
| |
|
| : W: |
: W: ( "name" -- a-addr xt ) \ gforth w-colon |
| create-local ( "name" -- a-addr xt ) |
create-local |
| \ xt produces the appropriate locals pushing code when executed |
\ xt produces the appropriate locals pushing code when executed |
| ['] compile-pushlocal-w |
['] compile-pushlocal-w |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| \ compiles a local variable access |
\ compiles a local variable access |
| postpone @local# @ lp-offset, ; |
@ lp-offset compile-@local ; |
| |
|
| : W^ |
: W^ ( "name" -- a-addr xt ) \ gforth w-caret |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-w |
['] compile-pushlocal-w |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, ; |
postpone laddr# @ lp-offset, ; |
| |
|
| : F: |
: F: ( "name" -- a-addr xt ) \ gforth f-colon |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-f |
['] compile-pushlocal-f |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone f@local# @ lp-offset, ; |
@ lp-offset compile-f@local ; |
| |
|
| : F^ |
: F^ ( "name" -- a-addr xt ) \ gforth f-caret |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-f |
['] compile-pushlocal-f |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, ; |
postpone laddr# @ lp-offset, ; |
| |
|
| : D: |
: D: ( "name" -- a-addr xt ) \ gforth d-colon |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-d |
['] compile-pushlocal-d |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, postpone 2@ ; |
postpone laddr# @ lp-offset, postpone 2@ ; |
| |
|
| : D^ |
: D^ ( "name" -- a-addr xt ) \ gforth d-caret |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-d |
['] compile-pushlocal-d |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, ; |
postpone laddr# @ lp-offset, ; |
| |
|
| : C: |
: C: ( "name" -- a-addr xt ) \ gforth c-colon |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-c |
['] compile-pushlocal-c |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, postpone c@ ; |
postpone laddr# @ lp-offset, postpone c@ ; |
| |
|
| : C^ |
: C^ ( "name" -- a-addr xt ) \ gforth c-caret |
| create-local ( "name" -- a-addr xt ) |
create-local |
| ['] compile-pushlocal-c |
['] compile-pushlocal-c |
| does> ( Compilation: -- ) ( Run-time: -- w ) |
does> ( Compilation: -- ) ( Run-time: -- w ) |
| postpone laddr# @ lp-offset, ; |
postpone laddr# @ lp-offset, ; |
| |
|
| \ you may want to make comments in a locals definitions group: |
\ you may want to make comments in a locals definitions group: |
| ' \ alias \ immediate |
' \ alias \ ( compilation 'ccc<newline>' -- ; run-time -- ) \ core-ext,block-ext backslash |
| ' ( alias ( immediate |
\G Comment till the end of the line if @code{BLK} contains 0 (i.e., |
| |
\G while not loading a block), parse and discard the remainder of the |
| |
\G parse area. Otherwise, parse and discard all subsequent characters |
| |
\G in the parse area corresponding to the current line. |
| |
immediate |
| |
|
| |
' ( alias ( ( compilation 'ccc<close-paren>' -- ; run-time -- ) \ core,file paren |
| |
\G Comment, usually till the next @code{)}: parse and discard all |
| |
\G subsequent characters in the parse area until ")" is |
| |
\G encountered. During interactive input, an end-of-line also acts as |
| |
\G a comment terminator. For file input, it does not; if the |
| |
\G end-of-file is encountered whilst parsing for the ")" delimiter, |
| |
\G Gforth will generate a warning. |
| |
immediate |
| |
|
| forth definitions |
forth definitions |
| |
|
| \ So we create a vocabulary new-locals, that creates a 'w:' local named x |
\ So we create a vocabulary new-locals, that creates a 'w:' local named x |
| \ when it is asked if it contains x. |
\ when it is asked if it contains x. |
| |
|
| 0. 2constant last-local \ !! actually a 2value |
|
| |
|
| also locals-types |
also locals-types |
| |
|
| : new-locals-find ( caddr u w -- nfa ) |
: new-locals-find ( caddr u w -- nfa ) |
| \ make a new local with name caddr u; w is ignored |
\ make a new local with name caddr u; w is ignored |
| \ the returned nfa denotes a word that produces what W: produces |
\ the returned nfa denotes a word that produces what W: produces |
| \ !! do the whole thing without nextname |
\ !! do the whole thing without nextname |
| drop nextname W: \ we don't want the thing that W: produces, |
drop nextname |
| ['] last-local >body 2! \ but the nfa of a word that produces that value: last-local |
['] W: >head-noprim ; |
| [ ' last-local >name ] Aliteral ; |
|
| |
|
| previous |
previous |
| |
|
| : new-locals-reveal ( -- ) |
: new-locals-reveal ( -- ) |
| true abort" this should not happen: new-locals-reveal" ; |
true abort" this should not happen: new-locals-reveal" ; |
| |
|
| create new-locals-map ' new-locals-find A, ' new-locals-reveal A, |
create new-locals-map ( -- wordlist-map ) |
| |
' new-locals-find A, |
| vocabulary new-locals |
' new-locals-reveal A, |
| new-locals-map ' new-locals >body cell+ A! \ !! use special access words |
' drop A, \ rehash method |
| |
' drop A, |
| |
|
| |
new-locals-map mappedwordlist Constant new-locals-wl |
| |
|
| |
\ slowvoc @ |
| |
\ slowvoc on |
| |
\ vocabulary new-locals |
| |
\ slowvoc ! |
| |
\ new-locals-map ' new-locals >body wordlist-map A! \ !! use special access words |
| |
|
| variable old-dpp |
variable old-dpp |
| |
|
| \ and now, finally, the user interface words |
\ and now, finally, the user interface words |
| : { ( -- addr wid 0 ) |
: { ( -- lastxt wid 0 ) \ gforth open-brace |
| dp old-dpp ! |
dp old-dpp ! |
| locals-dp dpp ! |
locals-dp dpp ! |
| also new-locals |
lastxt get-current |
| also get-current locals definitions locals-types |
get-order new-locals-wl swap 1+ set-order |
| |
also locals definitions locals-types |
| 0 TO locals-wordlist |
0 TO locals-wordlist |
| 0 postpone [ ; immediate |
0 postpone [ ; immediate |
| |
|
| locals-types definitions |
locals-types definitions |
| |
|
| : } ( addr wid 0 a-addr1 xt1 ... -- ) |
: } ( lastxt wid 0 a-addr1 xt1 ... -- ) \ gforth close-brace |
| \ ends locals definitions |
\ ends locals definitions |
| ] old-dpp @ dpp ! |
] old-dpp @ dpp ! |
| begin |
begin |
| repeat |
repeat |
| drop |
drop |
| locals-size @ alignlp-f locals-size ! \ the strictest alignment |
locals-size @ alignlp-f locals-size ! \ the strictest alignment |
| set-current |
|
| previous previous |
previous previous |
| locals-list TO locals-wordlist ; |
set-current lastcfa ! |
| |
locals-list 0 wordlist-id - TO locals-wordlist ; |
| |
|
| : -- ( addr wid 0 ... -- ) |
: -- ( addr wid 0 ... -- ) \ gforth dash-dash |
| } |
} |
| [char] } word drop ; |
[char] } parse 2drop ; |
| |
|
| forth definitions |
forth definitions |
| |
|
| |
|
| \ Implementation: |
\ Implementation: |
| |
|
| \ orig, dest and do-sys have the following structure: |
\ explicit scoping |
| \ address (of the branch or the instruction to be branched to) (TOS) |
|
| \ locals-list (valid at address) (second) |
|
| \ locals-size (at address; this could be computed from locals-list, but so what) (third) |
|
| |
|
| 3 constant cs-item-size |
|
| |
|
| : CS-PICK ( ... u -- ... destu ) |
|
| 1+ cs-item-size * 1- >r |
|
| r@ pick r@ pick r@ pick |
|
| rdrop ; |
|
| |
|
| : CS-ROLL ( destu/origu .. dest0/orig0 u -- .. dest0/orig0 destu/origu ) |
|
| 1+ cs-item-size * 1- >r |
|
| r@ roll r@ roll r@ roll |
|
| rdrop ; |
|
| |
|
| : CS-PUSH ( -- dest/orig ) |
|
| locals-size @ |
|
| locals-list @ |
|
| here ; |
|
| |
|
| : BUT sys? 1 cs-roll ; immediate restrict |
|
| : YET sys? 0 cs-pick ; immediate restrict |
|
| |
|
| : common-list ( list1 list2 -- list3 ) |
|
| \ list1 and list2 are lists, where the heads are at higher addresses than |
|
| \ the tail. list3 is the largest sublist of both lists. |
|
| begin |
|
| 2dup u<> |
|
| while |
|
| 2dup u> |
|
| if |
|
| swap |
|
| endif |
|
| @ |
|
| repeat |
|
| drop ; |
|
| |
|
| : sub-list? ( list1 list2 -- f ) |
|
| \ true iff list1 is a sublist of list2 |
|
| begin |
|
| 2dup u< |
|
| while |
|
| @ |
|
| repeat |
|
| = ; |
|
| |
|
| : list-size ( list -- u ) |
|
| \ size of the locals frame represented by list |
|
| 0 ( list n ) |
|
| begin |
|
| over 0<> |
|
| while |
|
| over |
|
| cell+ name> >body @ max |
|
| swap @ swap ( get next ) |
|
| repeat |
|
| faligned nip ; |
|
| |
|
| : x>mark ( -- orig ) |
: scope ( compilation -- scope ; run-time -- ) \ gforth |
| cs-push 0 , ; |
cs-push-part scopestart ; immediate |
| |
|
| variable dead-code \ true if normal code at "here" would be dead |
: adjust-locals-list ( wid -- ) |
| |
locals-list @ common-list |
| |
dup list-size adjust-locals-size |
| |
locals-list ! ; |
| |
|
| : unreachable ( -- ) |
: endscope ( compilation scope -- ; run-time -- ) \ gforth |
| \ declares the current point of execution as unreachable and |
scope? |
| \ prepares the assumptions for a possible upcoming BEGIN |
drop adjust-locals-list ; immediate |
| dead-code on |
|
| dup 0<> if |
|
| 2 pick 2 pick |
|
| else |
|
| 0 0 |
|
| endif |
|
| locals-list ! |
|
| locals-size ! ; |
|
| |
|
| : check-begin ( list -- ) |
\ adapt the hooks |
| \ warn if list is not a sublist of locals-list |
|
| locals-list @ sub-list? 0= if |
|
| \ !! print current position |
|
| ." compiler was overly optimistic about locals at a BEGIN" cr |
|
| \ !! print assumption and reality |
|
| endif ; |
|
| |
|
| : xahead ( -- orig ) |
: locals-:-hook ( sys -- sys addr xt n ) |
| POSTPONE branch x>mark unreachable ; immediate |
\ addr is the nfa of the defined word, xt its xt |
| |
DEFERS :-hook |
| |
last @ lastcfa @ |
| |
clear-leave-stack |
| |
0 locals-size ! |
| |
locals-buffer locals-dp ! |
| |
0 locals-list ! |
| |
dead-code off |
| |
defstart ; |
| |
|
| : xif ( -- orig ) |
: locals-;-hook ( sys addr xt sys -- sys ) |
| POSTPONE ?branch x>mark ; immediate |
def? |
| |
0 TO locals-wordlist |
| |
0 adjust-locals-size ( not every def ends with an exit ) |
| |
lastcfa ! last ! |
| |
DEFERS ;-hook ; |
| |
|
| \ THEN (another control flow from before joins the current one): |
\ THEN (another control flow from before joins the current one): |
| \ The new locals-list is the intersection of the current locals-list and |
\ The new locals-list is the intersection of the current locals-list and |
| \ inefficient, e.g. if there is a locals declaration between IF and |
\ inefficient, e.g. if there is a locals declaration between IF and |
| \ ELSE. However, if ELSE generates an appropriate "lp+!#" before the |
\ ELSE. However, if ELSE generates an appropriate "lp+!#" before the |
| \ branch, there will be none after the target <then>. |
\ branch, there will be none after the target <then>. |
| : xthen ( orig -- ) |
|
| sys? dup @ ?struc |
: (then-like) ( orig -- ) |
| dead-code @ |
dead-orig = |
| if |
if |
| >resolve |
>resolve drop |
| locals-list ! |
|
| locals-size ! |
|
| else |
else |
| locals-size @ 3 roll - compile-lp+!# |
dead-code @ |
| |
if |
| |
>resolve set-locals-size-list dead-code off |
| |
else \ both live |
| |
over list-size adjust-locals-size |
| >resolve |
>resolve |
| locals-list @ common-list locals-list ! |
adjust-locals-list |
| locals-size @ locals-list @ list-size - compile-lp+!# |
then |
| endif |
then ; |
| dead-code off ; immediate |
|
| |
: (begin-like) ( -- ) |
| : scope ( -- dest ) |
dead-code @ if |
| cs-push ; immediate |
\ set up an assumption of the locals visible here. if the |
| |
\ users want something to be visible, they have to declare |
| : endscope ( dest -- ) |
\ that using ASSUME-LIVE |
| drop |
backedge-locals @ set-locals-size-list |
| locals-list @ common-list locals-list ! |
then |
| locals-size @ locals-list @ list-size - compile-lp+!# |
dead-code off ; |
| drop ; immediate |
|
| |
|
| : xexit ( -- ) |
|
| locals-size @ compile-lp+!# POSTPONE exit unreachable ; immediate |
|
| |
|
| : x?exit ( -- ) |
|
| POSTPONE xif POSTPONE xexit POSTPONE xthen ; immediate |
|
| |
|
| : xelse ( orig1 -- orig2 ) |
|
| sys? |
|
| POSTPONE xahead |
|
| 1 cs-roll |
|
| POSTPONE xthen ; immediate |
|
| |
|
| : xbegin ( -- dest ) |
|
| cs-push dead-code off ; immediate |
|
| |
|
| : xwhile ( dest -- orig dest ) |
|
| sys? |
|
| POSTPONE xif |
|
| 1 cs-roll ; immediate |
|
| |
|
| \ AGAIN (the current control flow joins another, earlier one): |
\ AGAIN (the current control flow joins another, earlier one): |
| \ If the dest-locals-list is not a subset of the current locals-list, |
\ If the dest-locals-list is not a subset of the current locals-list, |
| \ issue a warning (see below). The following code is generated: |
\ issue a warning (see below). The following code is generated: |
| \ lp+!# (current-local-size - dest-locals-size) |
\ lp+!# (current-local-size - dest-locals-size) |
| \ branch <begin> |
\ branch <begin> |
| : xagain ( dest -- ) |
|
| sys? |
: (again-like) ( dest -- addr ) |
| locals-size @ 3 roll - compile-lp+!# |
over list-size adjust-locals-size |
| POSTPONE branch |
swap check-begin POSTPONE unreachable ; |
| <resolve |
|
| check-begin |
|
| unreachable ; immediate |
|
| |
|
| \ UNTIL (the current control flow may join an earlier one or continue): |
\ UNTIL (the current control flow may join an earlier one or continue): |
| \ Similar to AGAIN. The new locals-list and locals-size are the current |
\ Similar to AGAIN. The new locals-list and locals-size are the current |
| \ ones. The following code is generated: |
\ ones. The following code is generated: |
| \ lp+!# (current-local-size - dest-locals-size) |
\ ?branch-lp+!# <begin> (current-local-size - dest-locals-size) |
| \ ?branch <begin> |
|
| \ lp+!# (dest-local-size - current-locals-size) |
|
| \ (Another inefficiency. Maybe we should introduce a ?branch-lp+!# |
|
| \ primitive. This would also solve the interrupt problem) |
|
| : until-like ( dest xt -- ) |
|
| >r |
|
| sys? |
|
| locals-size @ dup 4 roll - compile-lp+!# ( list dest-addr old-locals-size ) |
|
| r> compile, |
|
| >r <resolve |
|
| check-begin |
|
| locals-size @ r> - compile-lp+!# ; |
|
| |
|
| : xuntil ( dest -- ) |
|
| ['] ?branch until-like ; immediate |
|
| |
|
| : xrepeat ( orig dest -- ) |
|
| 3 pick 0= ?struc |
|
| postpone xagain |
|
| postpone xthen ; immediate |
|
| |
|
| \ counted loops |
|
| |
|
| \ leave poses a little problem here |
|
| \ we have to store more than just the address of the branch, so the |
|
| \ traditional linked list approach is no longer viable. |
|
| \ This is solved by storing the information about the leavings in a |
|
| \ special stack. The leavings of different DO-LOOPs are separated |
|
| \ by a 0 entry |
|
| |
|
| \ !! remove the fixed size limit. 'Tis easy. |
|
| 20 constant leave-stack-size |
|
| create leave-stack leave-stack-size cs-item-size * cells allot |
|
| variable leave-sp leave-stack leave-sp ! |
|
| |
|
| : clear-leave-stack ( -- ) |
|
| leave-stack leave-sp ! ; |
|
| |
|
| \ : leave-empty? ( -- f ) |
|
| \ leave-sp @ leave-stack = ; |
|
| |
|
| : >leave ( orig -- ) |
|
| \ push on leave-stack |
|
| leave-sp @ |
|
| dup [ leave-stack leave-stack-size cs-item-size * cells + ] Aliteral >= |
|
| if |
|
| abort" leave-stack full" |
|
| endif |
|
| tuck ! cell+ |
|
| tuck ! cell+ |
|
| tuck ! cell+ |
|
| leave-sp ! ; |
|
| |
|
| : leave> ( -- orig ) |
|
| \ pop from leave-stack |
|
| leave-sp @ |
|
| dup leave-stack <= if |
|
| abort" leave-stack empty" |
|
| endif |
|
| cell - dup @ swap |
|
| cell - dup @ swap |
|
| cell - dup @ swap |
|
| leave-sp ! ; |
|
| |
|
| : done ( -- ) |
|
| \ !! the original done had ( addr -- ) |
|
| begin |
|
| leave> |
|
| dup |
|
| while |
|
| POSTPONE xthen |
|
| repeat |
|
| 2drop drop ; immediate |
|
| |
|
| : xleave ( -- ) |
|
| POSTPONE xahead |
|
| >leave ; immediate |
|
| |
|
| : x?leave ( -- ) |
|
| POSTPONE 0= POSTPONE xif |
|
| >leave ; immediate |
|
| |
|
| : xdo ( -- do-sys ) |
: (until-like) ( list addr xt1 xt2 -- ) |
| POSTPONE (do) |
\ list and addr are a fragment of a cs-item |
| POSTPONE xbegin |
\ xt1 is the conditional branch without lp adjustment, xt2 is with |
| 0 0 0 >leave ; immediate |
>r >r |
| |
locals-size @ 2 pick list-size - dup if ( list dest-addr adjustment ) |
| : x?do ( -- do-sys ) |
r> drop r> compile, |
| 0 0 0 >leave |
swap <resolve ( list adjustment ) , |
| POSTPONE (?do) |
else ( list dest-addr adjustment ) |
| x>mark >leave |
drop |
| POSTPONE xbegin ; immediate |
r> compile, <resolve |
| |
r> drop |
| : xfor ( -- do-sys ) |
then ( list ) |
| POSTPONE (for) |
check-begin ; |
| POSTPONE xbegin |
|
| 0 0 0 >leave ; immediate |
|
| |
|
| \ LOOP etc. are just like UNTIL |
|
| \ the generated code for ?DO ... LOOP with locals is inefficient, this |
|
| \ could be changed by introducing (loop)-lp+!# etc. |
|
| |
|
| : loop-like ( do-sys xt -- ) |
|
| until-like POSTPONE done POSTPONE unloop ; |
|
| |
|
| : xloop ( do-sys -- ) |
|
| ['] (loop) loop-like ; immediate |
|
| |
|
| : x+loop ( do-sys -- ) |
|
| ['] (+loop) loop-like ; immediate |
|
| |
|
| : xs+loop ( do-sys -- ) |
|
| ['] (s+loop) loop-like ; immediate |
|
| |
|
| : locals-:-hook ( sys -- sys addr xt ) |
|
| DEFERS :-hook |
|
| last @ lastcfa @ |
|
| clear-leave-stack |
|
| 0 locals-size ! |
|
| locals-buffer locals-dp ! |
|
| 0 locals-list ! ; ( clear locals vocabulary ) |
|
| |
|
| : locals-;-hook ( sys addr xt -- sys ) |
: (exit-like) ( -- ) |
| 0 TO locals-wordlist |
0 adjust-locals-size ; |
| locals-size @ compile-lp+!# |
|
| lastcfa ! last ! |
|
| DEFERS ;-hook ; |
|
| |
|
| ' locals-:-hook IS :-hook |
' locals-:-hook IS :-hook |
| ' locals-;-hook IS ;-hook |
' locals-;-hook IS ;-hook |
| |
|
| |
' (then-like) IS then-like |
| |
' (begin-like) IS begin-like |
| |
' (again-like) IS again-like |
| |
' (until-like) IS until-like |
| |
' (exit-like) IS exit-like |
| |
|
| \ The words in the locals dictionary space are not deleted until the end |
\ The words in the locals dictionary space are not deleted until the end |
| \ of the current word. This is a bit too conservative, but very simple. |
\ of the current word. This is a bit too conservative, but very simple. |
| |
|
| \ things above are not control flow joins. Everything should be taken |
\ things above are not control flow joins. Everything should be taken |
| \ over from the live flow. No lp+!# is generated. |
\ over from the live flow. No lp+!# is generated. |
| |
|
| \ !! The lp gymnastics for UNTIL are also a real problem: locals cannot be |
|
| \ used in signal handlers (or anything else that may be called while |
|
| \ locals live beyond the lp) without changing the locals stack. |
|
| |
|
| \ About warning against uses of dead locals. There are several options: |
\ About warning against uses of dead locals. There are several options: |
| |
|
| \ 1) Do not complain (After all, this is Forth;-) |
\ 1) Do not complain (After all, this is Forth;-) |
| |
|
| \ And here's finally the ANS standard stuff |
\ And here's finally the ANS standard stuff |
| |
|
| : (local) ( addr u -- ) |
: (local) ( addr u -- ) \ local paren-local-paren |
| \ a little space-inefficient, but well deserved ;-) |
\ a little space-inefficient, but well deserved ;-) |
| \ In exchange, there are no restrictions whatsoever on using (local) |
\ In exchange, there are no restrictions whatsoever on using (local) |
| |
\ as long as you use it in a definition |
| dup |
dup |
| if |
if |
| nextname POSTPONE { [ also locals-types ] W: } [ previous ] |
nextname POSTPONE { [ also locals-types ] W: } [ previous ] |
| 2drop |
2drop |
| endif ; |
endif ; |
| |
|
| \ \ !! untested |
: >definer ( xt -- definer ) |
| \ : TO ( c|w|d|r "name" -- ) |
\G @var{Definer} is a unique identifier for the way the @var{xt} |
| \ \ !! state smart |
\G was defined. Words defined with different @code{does>}-codes |
| \ 0 0 0. 0.0e0 { c: clocal w: wlocal d: dlocal f: flocal } |
\G have different definers. The definer can be used for |
| \ ' dup >definer |
\G comparison and in @code{definer!}. |
| \ state @ |
dup >does-code |
| \ if |
?dup-if |
| \ case |
nip 1 or |
| \ [ ' locals-wordlist >definer ] literal \ value |
else |
| \ OF >body POSTPONE Aliteral POSTPONE ! ENDOF |
>code-address |
| \ [ ' clocal >definer ] literal |
then ; |
| \ OF POSTPONE laddr# >body @ lp-offset, POSTPONE c! ENDOF |
|
| \ [ ' wlocal >definer ] literal |
|
| \ OF POSTPONE laddr# >body @ lp-offset, POSTPONE ! ENDOF |
|
| \ [ ' dlocal >definer ] literal |
|
| \ OF POSTPONE laddr# >body @ lp-offset, POSTPONE d! ENDOF |
|
| \ [ ' flocal >definer ] literal |
|
| \ OF POSTPONE laddr# >body @ lp-offset, POSTPONE f! ENDOF |
|
| \ abort" can only store TO value or local value" |
|
| \ endcase |
|
| \ else |
|
| \ [ ' locals-wordlist >definer ] literal = |
|
| \ if |
|
| \ >body ! |
|
| \ else |
|
| \ abort" can only store TO value" |
|
| \ endif |
|
| \ endif ; |
|
| |
|
| \ : locals| |
: definer! ( definer xt -- ) |
| \ !! should lie around somewhere |
\G The word represented by @var{xt} changes its behaviour to the |
| |
\G behaviour associated with @var{definer}. |
| |
over 1 and if |
| |
swap [ 1 invert ] literal and does-code! |
| |
else |
| |
code-address! |
| |
then ; |
| |
|
| |
:noname |
| |
' dup >definer [ ' locals-wordlist ] literal >definer = |
| |
if |
| |
>body ! |
| |
else |
| |
-&32 throw |
| |
endif ; |
| |
:noname |
| |
0 0 0. 0.0e0 { c: clocal w: wlocal d: dlocal f: flocal } |
| |
comp' drop dup >definer |
| |
case |
| |
[ ' locals-wordlist ] literal >definer \ value |
| |
OF >body POSTPONE Aliteral POSTPONE ! ENDOF |
| |
\ !! dependent on c: etc. being does>-defining words |
| |
\ this works, because >definer uses >does-code in this case, |
| |
\ which produces a relocatable address |
| |
[ comp' clocal drop >definer ] literal |
| |
OF POSTPONE laddr# >body @ lp-offset, POSTPONE c! ENDOF |
| |
[ comp' wlocal drop >definer ] literal |
| |
OF POSTPONE laddr# >body @ lp-offset, POSTPONE ! ENDOF |
| |
[ comp' dlocal drop >definer ] literal |
| |
OF POSTPONE laddr# >body @ lp-offset, POSTPONE 2! ENDOF |
| |
[ comp' flocal drop >definer ] literal |
| |
OF POSTPONE laddr# >body @ lp-offset, POSTPONE f! ENDOF |
| |
-&32 throw |
| |
endcase ; |
| |
interpret/compile: TO ( c|w|d|r "name" -- ) \ core-ext,local |
| |
|
| |
: locals| |
| |
\ don't use 'locals|'! use '{'! A portable and free '{' |
| |
\ implementation is compat/anslocals.fs |
| |
BEGIN |
| |
name 2dup s" |" str= 0= |
| |
WHILE |
| |
(local) |
| |
REPEAT |
| |
drop 0 (local) ; immediate restrict |