Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #4541 > unrolled thread
| Started by | Michael L Gassanenko <m_l_g3@yahoo.com> |
|---|---|
| First post | 2011-08-02 02:01 -0700 |
| Last post | 2011-08-03 03:53 -0500 |
| Articles | 20 on this page of 75 — 18 participants |
Back to article view | Back to comp.lang.forth
multi-threading in Forth? Michael L Gassanenko <m_l_g3@yahoo.com> - 2011-08-02 02:01 -0700
Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-02 11:22 +0200
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-02 15:26 +0200
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-04 01:06 +0400
Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-04 06:36 +0200
Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 08:25 +0100
Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-04 13:42 +0200
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 14:35 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-04 08:30 -0500
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 15:38 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-04 09:12 -0500
Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:50 +0100
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-04 23:52 +0400
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-05 08:50 -0500
Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:49 +0100
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 19:23 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 10:57 -0500
Re: multi-threading in Forth? stephenXXX@mpeforth.com (Stephen Pelc) - 2011-08-04 14:16 +0000
Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:51 +0100
Re: multi-threading in Forth? stephenXXX@mpeforth.com (Stephen Pelc) - 2011-08-04 17:14 +0000
Re: multi-threading in Forth? "Rod Pemberton" <do_not_have@noavailemail.cmm> - 2011-08-06 03:46 -0400
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-04 19:11 +0200
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-05 08:43 -0500
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-06 01:49 +0400
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 03:13 -0500
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 04:41 +0400
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 11:01 +0400
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-10 09:02 -0500
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-11 01:10 +0400
Re: multi-threading in Forth? Ouatu Bogdan <ouatubi@gmail.com> - 2011-08-10 13:22 +0000
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-06 01:12 +0200
Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-06 02:37 +0200
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-06 12:54 +0200
Re: multi-threading in Forth? alextangent <blog@rivadpm.com> - 2011-08-06 12:15 +0100
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 22:23 -0500
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 03:17 -0500
Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-05 19:02 -0500
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-06 04:01 -0500
return-address manipulation (was: multi-threading in Forth?) anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2011-08-06 16:35 +0000
Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-07 11:45 +0100
Re: return-address manipulation Josh Grams <josh@qualdan.com> - 2011-08-08 09:40 +0000
Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-08 12:17 +0100
Re: return-address manipulation Josh Grams <josh@qualdan.com> - 2011-08-08 12:29 +0000
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-09 03:54 +0400
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-09 02:52 +0400
Re: return-address manipulation Gerry Jackson <gerry@jackson9000.fsnet.co.uk> - 2011-08-09 20:54 +0100
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:00 +0400
Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-09 14:49 -0500
Re: return-address manipulation anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2011-08-10 09:03 +0000
Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-10 05:13 -0500
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:24 +0400
Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-11 04:09 -0500
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:36 +0400
Re: return-address manipulation mlg3 <m_l_g3@yahoo.com> - 2011-08-11 09:54 +0400
Re: return-address manipulation Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-11 04:16 -0500
Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-07 01:43 -0500
Re: multi-threading in Forth? Spam@ControlQ.com - 2011-08-06 12:36 -0400
Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-07 01:13 -0500
Re: multi-threading in Forth? mlg3 <m_l_g3@yahoo.com> - 2011-08-09 02:35 +0400
Re: multi-threading in Forth? Albert van der Horst <albert@spenarnc.xs4all.nl> - 2011-08-07 10:08 +0000
Re: multi-threading in Forth? Josh Grams <josh@qualdan.com> - 2011-08-07 13:45 +0000
Re: multi-threading in Forth? mhx@iae.nl (Marcel Hendrix) - 2011-08-05 17:17 +0200
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-05 19:23 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-05 10:53 -0500
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-05 19:18 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-06 09:59 -0500
Re: multi-threading in Forth? Spam@ControlQ.com - 2011-08-06 12:28 -0400
Re: multi-threading in Forth? Albert van der Horst <albert@spenarnc.xs4all.nl> - 2011-08-06 11:29 +0000
Re: multi-threading in Forth? Mark Wills <markrobertwills@yahoo.co.uk> - 2011-08-04 16:47 +0100
Re: multi-threading in Forth? Julian Fondren <ayrnieu@gmail.com> - 2011-08-04 13:54 -0500
Re: multi-threading in Forth? coos haak <chforth@hccnet.nl> - 2011-08-04 22:02 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-02 08:23 -0500
Re: multi-threading in Forth? Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> - 2011-08-02 15:30 +0200
Re: multi-threading in Forth? Elizabeth D Rather <erather@forth.com> - 2011-08-02 08:53 -0500
Re: multi-threading in Forth? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2011-08-03 03:53 -0500
Page 2 of 4 — ← Prev page 1 [2] 3 4 Next page →
| From | "Rod Pemberton" <do_not_have@noavailemail.cmm> |
|---|---|
| Date | 2011-08-06 03:46 -0400 |
| Message-ID | <j1irhq$gd5$1@speranza.aioe.org> |
| In reply to | #4583 |
"Mark Wills" <markrobertwills@yahoo.co.uk> wrote in message news:j1ef6s$n41$4@dont-email.me... > > Perhaps the return stack should be > obsoleted? > If the return stack is obsoleted, how do you intend to ... a) implement a Forth for a threaded interpreter without it? E.g., a threaded interpreter saves and restores interpreter addresses using the return stack via ENTER/DOCOL and EXIT/NEXT. Historically, some Forth words start with R> due to threaded interpreters, e.g.: LIT BRANCH COMPILE DOES RP! b) implement a temporary register without it? An 'A' register ala MachineForth? E.g., a temporary may be needed for SWAP or for words using >R and R> . c) resolve the fact that parameter stack items must be reorganized and extra space is required to do that? d) keep control flow addresses off of the parameter stack? I.e., a single stack is commonly used in C, but not required. This leads to many of the problems C has with various exploits. This would be the same as merging the return stack and parameter/data stack of Forth into a single stack. Rod Pemberton
[toc] | [prev] | [next] | [standalone]
| From | Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> |
|---|---|
| Date | 2011-08-04 19:11 +0200 |
| Message-ID | <slrnj3ls85.27m.zbigniew2011REMOVE@Tichy.myhome.org> |
| In reply to | #4578 |
In comp.lang.forth, Stephen Pelc wrote:
> In practice, embedded Forths using 8051s may keep return addresses on
> a third stack. However, what you see more often is that a return
> address may be larger than a cell size (DSP, PIC ...) or may not be
> formatted as you expect (e.g. Cortex-M has bit 0 always set).
>
> Hence, there are occasion when you *need* to isolate the return
> address words because they are not just R@ and friends. Yes, this
> sort of problem is seen regularly.
Of course, it's better to create Forth compiler with "strange return stack",
than to not make it at all. But I understand the described situation rather
as a rationale, why ANS-standard doesn't impose the strict rules about stack:
"...because sometimes can be not possible to fulfil such requirement" - than
as a statement: "actually, return stack can be implemented in any way". Yes:
formally it can - it isn't forbidden by law, therefore no Forth creator will
be punished - but the question is: should it really be treated so blithely?
Nevertheless I'm somewhat amazed, that it is done in situations, when there
can be implemented "proper" return stack. It can make more problems, than
e.g. changing the word name from "<MOVE" to "MOVE>" - because it is the
violation of quite basic rule. What is return stack, is described in every
Forth primer. And then we can see, that the rule works "sometimes".
I think, that "rule" doesn't necessarily mean formal requirement of any
standard committee - it can be just "traditional solution", present and known
since 30 years, if it still does its work with no problems. But of course it
can be changed, when it will mean some obvious profits for both compiler
creator and user, for example: Andy Valencia, ForthOS creator, proposed to
use larger blocks - 4096 instead of "traditional" 1024 bytes - and it does
make sense, because meanwhile the HDD vendors changed the size of basic
sector unit to... 4096 bytes (from 512). So this is an example, how the
change of standard can follow "the life". Using blocks of the same size as
HDD sector means better performance (of course, if one's willing to use
blocks at all).
So if I'm trying to use a compiler written for some "exotic" hardware, yes:
I can expect the presence of some non-standard solution. But why in the case
of compiler written for most popular hardware? There surely is some
rationale behind it, although IMHO if such change won't give any tremendous
profits (much greater efficiency?), perhaps it's better to _not_ introduce
it, because most programmers will expect to find the things working "old way".
Well, of course I don't have so much Forth-programming experience as
everyone of you here (if my exercises can be taken as any "experience" at
all), but this is as I see it.
--
...but I'm ready to learn ('bout) of the power of Forth
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2011-08-05 08:43 -0500 |
| Message-ID | <b5SdnSwRyaUTbqbTnZ2dnUVZ8sidnZ2d@supernews.com> |
| In reply to | #4585 |
Zbiggy <zbigniew2011REMOVE@gmail.remove.com> wrote: > In comp.lang.forth, Stephen Pelc wrote: > >> In practice, embedded Forths using 8051s may keep return addresses on >> a third stack. However, what you see more often is that a return >> address may be larger than a cell size (DSP, PIC ...) or may not be >> formatted as you expect (e.g. Cortex-M has bit 0 always set). >> >> Hence, there are occasion when you *need* to isolate the return >> address words because they are not just R@ and friends. Yes, this >> sort of problem is seen regularly. > > Of course, it's better to create Forth compiler with "strange return > stack", than to not make it at all. But I understand the described > situation rather as a rationale, why ANS-standard doesn't impose the > strict rules about stack: "...because sometimes can be not possible > to fulfil such requirement" - than as a statement: "actually, return > stack can be implemented in any way". Yes: formally it can - it > isn't forbidden by law, therefore no Forth creator will be punished > - but the question is: should it really be treated so blithely? I think so. There is the matter of whether, say, return stack manipulation is more useful than tail call optimization. I'd choose tail call optimization every time, but IME code that (ab)uses the return stack for control flow is hard to understand and maintain. Whatever the problem, there's almost always a better way to do it than fiddling with the R-stack. > Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096 > instead of "traditional" 1024 bytes - and it does make sense, > because meanwhile the HDD vendors changed the size of basic sector > unit to... 4096 bytes (from 512). Yes, it makes very good sense to do this. However, given a 4096-byte block buffer, you can easily get a compatible BLOCK with : block ( n - a) 4 /mod big-block swap 1024 * + ; So, there is no need for application code to be affected by the use of big blocks. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | mlg3 <m_l_g3@yahoo.com> |
|---|---|
| Date | 2011-08-06 01:49 +0400 |
| Message-ID | <j1hofk$g8o$1@dont-email.me> |
| In reply to | #4595 |
Andrew Haley wrote:
> Whatever the problem, there's almost always a better way to do it than
> fiddling with the R-stack.
Fascinating. A person who apparently has no problems with
the use of data stack, discourages the use of return stack,
although both lead to equally untraditional, from an average
specialist's viewpoint, code, and both are backed by
solid mathematical background.
Probably, the word "fiddling" attempts to characterize it
as something illogical. It's not. But to demonstrate that,
I will introduce a new notation.
== means functional equivalence (*)
[footnote]
(*) that is, we assume that the code is not accessed as data (**).
(**) In fact, some elements of code, such as branch
destinations, _are_ accessed as data, but they are not
executed as code.
[/footnote]
For FOO defined as
: foo bar ;
we write:
foo == nest[ bar exit ]
analogously, for
: baz foo ;
we have
baz == nest[ foo exit ] == nest[ nest[ bar exit ] exit ]
The next thing to realize is that a call is just
placement of an address onto the return stack.
r[ qux ] stands for a code fragment that places the address of the code
fragment qux onto the return stack.
CREATE quux ] qux exit [
quux >R == r[ qux exit ]
And now, the most important property:
nest[ x ] y ... == r[ y ... ] x
The ellipsis above shows what happens with the code that follows y. The
ellipsis at the end of an expression may be omitted, for example, we may
rewrite the above as:
nest[ x ] y == r[ y ... ] x
or even
nest[ x ] == r[ ... ] x
Now,
foo == nest[ bar exit ] == r[ ... ] bar exit
(
Note that
exit x == exit y == exit
because exit does not pass control to the code that follows it.
Looking at
nest[ x ] == r[ ... ] x
you might wonder what about the code that follows x. The answer
is: in practice x usually ends with exit, so whatever follows
that exit is irrelevant.
)
Analogously, for baz defined above:
baz == nest[ foo exit ] == nest[ nest[ bar exit ] exit ]
== r[ ... ] r[ exit ] bar exit
Let us consider a more complicated example.
[example1]
: foo ." foo " ;
: qux ." qux " ;
CREATE quux ] qux exit [
: test quux >R foo ;
test == nest[ quux >R foo exit ] == nest[ r[ qux exit ] foo exit ]
It is evident that for our definition of foo
r[ anything ] foo == foo r[ anything ]
because foo defined above does not depend on and does not
affect the return stack state. This is a very important
property, it is written as foo:R.
(You may notice that I resort to common sense in the middle
of formal transformations. I do not try to try to prove
what we all already know is true, because it is not
important how we prove that. This knowledge is one level
of abstraction below our maths.)
So, continuing the chain,
nest[ r[ qux exit ] foo exit ]
== nest[ foo r[ qux exit ] exit ]
== nest[ foo qux exit ]
because
r[ x ] exit == x
(This is an axiom, and it means that exit transfers control
to the code whose address is taken off the return stack.)
So, test will print "foo qux ". Let's try it in Gforth:
: foo ." foo " ; ok
: qux ." qux " ; ok
CREATE quux ] qux exit [ ok
: test quux >r foo ; ok
ok
test foo qux ok
[/example1]
[example2]
And what about
: co r> r> swap >r >r ;
?
: x a co b ;
: y c y d ;
y == nest[ c nest[ a nest[ r> r> swap >r >r exit ] b exit] d exit ]
== r[ ... ] c r[ d exit ] a r[ b exit ] r> r> swap >r >r exit
== r[ ... ] c r[ b exit ] a r[ d exit ] exit
== r[ ... ] c r[ b exit ] a d exit
it would be tempting to continue the transformation, replacing
[ r[ b exit ] a d exit ] with [ a d b exit ] but to do that, we
must know that [a d]:R (the code fragment [a d] does not depend
on or affect the return stack).
(You likely have already figured out that we use brackets
to group two code fragments into one code fragment.)
[/example2]
By the way, the notation for
r[ something ] R>
is
'[ something ]
We could use it above, but did not. Of course,
'[ x ] >R == r[ x ]
And now, some important theorem.
But first, a definition.
Definition
S1( x ) is such a code fragment that
ForAll N: x N == N S1( x )
In other words, it does what x does, but leaves the stack top intact.
Examples:
S1( NOOP ) == NOOP
S1( DROP ) == NIP
S1( DUP ) == OVER SWAP
S1( EXIT ) == DROP EXIT
Indeed, N S1( EXIT ) == EXIT N, and N DROP EXIT == EXIT
Analogously,
Definition
R1( x ) is such a code fragment that
ForAll N: x N >R == N >R R1( x )
that is, R1( x ) acts like x but leaves the return stack top intact.
Examples:
R1( DUP ) == DUP
R1( DROP ) == DROP
R1( RDROP ) == R> RDROP >R
R1( EXIT ) == RDROP EXIT
Indeed, N >R R1( EXIT ) == EXIT N >R, and N >R RDROP EXIT == EXIT
Definition
x:R if x == R1( x )
(x:R means that x does not affect or depend on the return stack)
Examples:
DUP:R
DROP:R
And now, the theorem of call equivalence.
Theorem
x == nest[ R1( x ) exit ]
Proof
nest[ R1( x ) exit ] == r[ ... ] R1( x ) exit
== x r[ ... ] exit == x ... == x
Consequence
For x:R,
x == nest[ x exit ]
(that is, if words in a colon definition do not
read or modify the return stack, a call of the definition
is equivalent to inline substitution of the definition's body).
Are you still with me? ;-)
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2011-08-06 03:13 -0500 |
| Message-ID | <t_6dndJzyNwCaqHTnZ2dnUVZ7oudnZ2d@supernews.com> |
| In reply to | #4607 |
mlg3 <m_l_g3@yahoo.com> wrote: > Andrew Haley wrote: > > Whatever the problem, there's almost always a better way to do it than > > fiddling with the R-stack. > > Fascinating. A person who apparently has no problems with > the use of data stack, discourages the use of return stack, I don't discourage the use of return stack. It's very useful for saving items from the data stack. It's not useful for changing control flow, IME; that's the Forth equivalent of GOTO. > Probably, the word "fiddling" attempts to characterize it > as something illogical. It's not. But to demonstrate that, > I will introduce a new notation. <formalism elided> What is the point of this? It can't be in response to what I wrote, since I never claimed that fiddling with the return stack was "illogical". I did claim that it was, ah, fiddly. :-) > (that is, if words in a colon definition do not read or modify the > return stack, a call of the definition is equivalent to inline > substitution of the definition's body). Quite so: this also means that fiddling with the return stack may destroy this property. For example, : bletch foo bar baz ; may execute FOO and BAR, but not BAZ, because BAR has the side-effect of exiting from BLETCH . Throwing an exception does that too, but exceptions are constrained in a way that R-stack fiddling is not. You can, at least, see the site to which an exception returns. > Are you still with me? ;-) Andrew.
[toc] | [prev] | [next] | [standalone]
| From | mlg3 <m_l_g3@yahoo.com> |
|---|---|
| Date | 2011-08-09 04:41 +0400 |
| Message-ID | <j1pvnb$1n8$1@dont-email.me> |
| In reply to | #4616 |
Andrew Haley wrote: > mlg3 <m_l_g3@yahoo.com> wrote: >> Andrew Haley wrote: > <formalism elided> :-( > >> (that is, if words in a colon definition do not read or modify the >> return stack, a call of the definition is equivalent to inline >> substitution of the definition's body). > > Quite so: this also means that fiddling with the return stack may > destroy this property. For example, [sigh] The context was important. >> Theorem >> >> x == nest[ R1( x ) exit ] It shows that in the general case there's a transformation. For example, if we have R> and DROP and want to define RDROP, the body of the colon definition will contain R1( R> DROP ) : RDROP R> R> DROP >R ; If we want to extract body from a colon definition (that is, optimize out the call), it is not possible in some cases. For example, : lit& R@ @ AND R> CELL+ >R ; is better left called because it reads and changes the interpretation stack (return stack+ip). And in one particular but important case we get the consequence, which (consequence) is true but not new. >> Consequence >> >> For x:R, >> x == nest[ x exit ] >> >> (that is, if words in a colon definition do not >> read or modify the return stack, a call of the definition >> is equivalent to inline substitution of the definition's body). Evidently, in some particular case we had to get what the standard says. > ... For example, > > : bletch foo bar baz ; > > may execute FOO and BAR, but not BAZ, because BAR has the side-effect > of exiting from BLETCH . If BAR causes an exit from BLETCH, BAR/:R (it's not true that BAR:R) -- it needs to read the return stack to exit, so the consequence does not apply. In fact, : MAX 2DUP > IF DROP EXIT THEN NIP ; is a standard definition that may not be inlined because EXIT/:R (it is not true that EXIT:R) > Throwing an exception does that too, but > exceptions are constrained in a way that R-stack fiddling is not. You > can, at least, see the site to which an exception returns. Let us not mix the flies with the cutlets. Exceptions, of course, require a special set of axioms, but they are not relevant now. *** The result of return address manipulations may be predicted at compile-time *** And this is why the formalism is possible.
[toc] | [prev] | [next] | [standalone]
| From | mlg3 <m_l_g3@yahoo.com> |
|---|---|
| Date | 2011-08-09 11:01 +0400 |
| Message-ID | <j1qlvu$cem$1@dont-email.me> |
| In reply to | #4651 |
mlg3 wrote: > Andrew Haley wrote: >> mlg3 <m_l_g3@yahoo.com> wrote: >>> Andrew Haley wrote: > >> <formalism elided> > > :-( > >> >>> (that is, if words in a colon definition do not read or modify the >>> return stack, a call of the definition is equivalent to inline >>> substitution of the definition's body). >> >> Quite so: this also means that fiddling with the return stack may >> destroy this property. For example, > > [sigh] > > The context was important. > >>> Theorem >>> >>> x == nest[ R1( x ) exit ] > > It shows that in the general case there's a transformation. > For example, if we have R> and DROP and want to define RDROP, > the body of the colon definition will contain R1( R> DROP ) > > : RDROP R> R> DROP >R ; > > If we want to extract body from a colon definition (that is, > optimize out the call), it is not possible in some cases. > > For example, > : lit& R@ @ AND R> CELL+ >R ; > is better left called because it reads and changes the > interpretation stack (return stack+ip). > Sorry, I glitched. This should read "because it reads and changes the _return_ stack". (Any word that does not hang/halt the system reads and/or modifies IP). > And in one particular but important case we get the > consequence, which (consequence) is true but not new. > >>> Consequence >>> >>> For x:R, >>> x == nest[ x exit ] >>> >>> (that is, if words in a colon definition do not >>> read or modify the return stack, a call of the definition >>> is equivalent to inline substitution of the definition's body). > > Evidently, in some particular case we had to get what the > standard says. > > > > ... For example, >> >> : bletch foo bar baz ; >> >> may execute FOO and BAR, but not BAZ, because BAR has the side-effect >> of exiting from BLETCH . > > If BAR causes an exit from BLETCH, BAR/:R (it's not true that BAR:R) > -- it needs to read the return stack to exit, so the consequence > does not apply. > > In fact, > > : MAX 2DUP > IF DROP EXIT THEN NIP ; > > is a standard definition that may not be inlined because EXIT/:R > (it is not true that EXIT:R) > >> Throwing an exception does that too, but >> exceptions are constrained in a way that R-stack fiddling is not. You >> can, at least, see the site to which an exception returns. > > Let us not mix the flies with the cutlets. > > Exceptions, of course, require a special set of axioms, but they > are not relevant now. > > *** The result of return address manipulations may be predicted > at compile-time *** > > And this is why the formalism is possible. >
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2011-08-10 09:02 -0500 |
| Message-ID | <uMmdncpD8awdEt_TnZ2dnUVZ8uCdnZ2d@supernews.com> |
| In reply to | #4651 |
mlg3 <m_l_g3@yahoo.com> wrote: > Andrew Haley wrote: >> mlg3 <m_l_g3@yahoo.com> wrote: >>> Andrew Haley wrote: > >> <formalism elided> > > :-( > >> >>> (that is, if words in a colon definition do not read or modify the >>> return stack, a call of the definition is equivalent to inline >>> substitution of the definition's body). >> >> Quite so: this also means that fiddling with the return stack may >> destroy this property. For example, > > [sigh] > > The context was important. As far as I could see the formalism told us in a formal way what we already knew. Perhaps I missed something, though. Were there some uncommon insights? > > *** The result of return address manipulations may be predicted > at compile-time *** > > And this is why the formalism is possible. But what is it for? I don't understand where this is going. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | mlg3 <m_l_g3@yahoo.com> |
|---|---|
| Date | 2011-08-11 01:10 +0400 |
| Message-ID | <j1us4b$api$1@dont-email.me> |
| In reply to | #4689 |
Andrew Haley wrote:
> mlg3 <m_l_g3@yahoo.com> wrote:
>> Andrew Haley wrote:
>>> mlg3 <m_l_g3@yahoo.com> wrote:
>>>> Andrew Haley wrote:
>>> <formalism elided>
>> :-(
>>
>>>> (that is, if words in a colon definition do not read or modify the
>>>> return stack, a call of the definition is equivalent to inline
>>>> substitution of the definition's body).
>>> Quite so: this also means that fiddling with the return stack may
>>> destroy this property. For example,
>> [sigh]
>>
>> The context was important.
>
> As far as I could see the formalism told us in a formal way what we
> already knew. Perhaps I missed something, though. Were there some
> uncommon insights?
>
>>> *** The result of return address manipulations may be predicted
>> at compile-time ***
>>
>> And this is why the formalism is possible.
>
> But what is it for? I don't understand where this is going.
>
> Andrew.
ok,
>>> Theorem
>>>
>>> x == nest[ R1( x ) exit ]
(again that theorem)
Reminder.
ForAll N:
N >R R1( x ) == x N >R
Consequence 2: the correct algorithm of inlining
It requires that you track the return stack values
inside the colon definition's body.
The colon definition looks as
: foo z ;
The following cases are possible:
1. The return stack is left in the original state,
the return address and the values beneath it are not
read or written. It is ok to move the definition's
body into the calling definition as is.
It is the case that z == R1( z )
2. The return stack top is moved off the return stack and
later placed back, after something is done with the return
stack items beneath it. (Finally, the return stack top is
consumed by EXIT compiled by ; (semi-colon).)
It is easy to generate code for such y that
R1( y ) == z
(This y is the code that must be placed in the calling
definition's body in place of foo.
Indeed,
y == nest[ R1( y ) exit ] == nest[ z exit ] == foo
)
For r-stack primitives that access (that is, move)
the return address (the top return stack item),
no code is generated.
For r-stack primitives that access other return stack
items, the code is generated as if the top return stack
item did not exist.
For example,
: rswap r> r> r> swap >r >r >r ;
rswap == r> r> swap >r >r
One mode example.
( x -- y ) ( r: y -- x )
: >r> r> swap r> swap >r swap >r ;
The generated code will be:
( r> ) ( swap ) r> swap >r ( swap ) ( >r )
The removed words are shown in parens; they are removed
because they had to manipulate the return address that
is eliminated. The effect of R> was ( x -- x ra ) but
without ra it is just ( x -- x ), that is, NOOP.
The same applies to SWAP ( x ra -- ra x ), without
ra it becomes NOOP.
Alternative definition of the same word:
( x -- y ) ( r: y -- x )
: >r> r> r> swap rot >r >r ;
The generated code will be:
( r> ) r> ( swap ) swap >r ( >r )
The first SWAP disappears because it has to swap
the return address with another value, and the
return address disappears, so SWAP becomes a NOOP.
ROT becomes SWAP because it originally did
( x y ra -- y ra x ) and after ra disappears,
what must be done becomes ( x y -- y x ).
I think you already have mentioned that
>r> == nest[ r> r> swap rot >r >r ; ] ==
r[ ... ] r> r> swap rot >r >r ; ==
r> r[ ... ] rot >r >r ; ==
r> swap >r r[ ... ] >r ; == r> swap >r
3. The return stack is left in the original state
up to the moment that the return address is passed
to EXIT inside z. You may either not inline such
definition or transform it into an equivalent
single-exit form. (And the transformed code will
fall into category 1.)
Example.
: MAX 2dup > if drop exit then nip ;
To inline MAX, you first need to transform it into
an equivalent of
: max 2dup > if drop else nip then ;
4. All other cases. That is, the return address
is not what is passed to EXIT. Or it is passed,
but not to EXIT (and EXIT may never receive control).
There is a control transfer. You may generate optimized
code even for this case, but you will have to do code
transformations. Note that in many cases it is not
possible to completely eliminate the call (for example,
you may eliminate control transfer, but will still
have to push an address onto the return stack).
In this text, I recommend not to inline such
definitions. (Because else I would have to
give an exhaustive explanation of how to
generate control transfers for such stuff.)
Example.
: branch r> @ >r ;
You may either leave this definition as it is (that
is, call it rather than inline), or transform it into
a control transfer.
For subroutine-threaded code, the result might be
something like
jmp [address]
address: dw destination
Example.
: give> r> ;
I think, now you see why I am not willing to give
recommendations for the general case.
Better leave this definition as it is.
The generated native code might look as:
\ give> bar baz quz
spush address
exit
address:
bar
baz
qux
Example.
: enter >r ;
The easiest thing is to leave it as it is.
If you nevertheless want to generate optimized code,
you will have to generate an equivalent of a call
(the 'nest' action), and then transfer control to the
address at the stack top.
\ foo enter bar
foo
rpush address
spop R1
jump R1
address:
bar
Note that in the last two examples you can generate
inline code, but you cannot completely eliminate
the call: the RPUSH ADDRESS command is a piece
of that call.
Are definitions that use return address manipulations
worth generating optimized code? Such words likely
implement control structures, and the control structures
tend to be executed in loops. On the other hand, in
many cases custom control structures are not needed.
(As least, until we begin to implement custom control
structures for parallel execution on multi-core
processors.)
Anyway, whether or not you choose to generate optimized
code, the return address manipulations _do_ deserve
generation of _correct_ code.
[toc] | [prev] | [next] | [standalone]
| From | Ouatu Bogdan <ouatubi@gmail.com> |
|---|---|
| Date | 2011-08-10 13:22 +0000 |
| Message-ID | <j1u0mi$c4n$2@dont-email.me> |
| In reply to | #4607 |
Hi! On Sat, 06 Aug 2011 01:49:07 +0400, mlg3 wrote: > although both lead to equally untraditional, from an average > specialist's viewpoint, code, and both are backed by solid mathematical > background. And that convinced me that 'return stack manipulation of addresses' is not just a technique but knowledge. I think that knowledgeable use of return addresses should not be underestimated. > Are you still with me? ;-) Yes. Thanks for post, theory presented is backed by examples and more understandable than presented euroforth papers that sometimes are a bit too academic. Have a nice day, humptydumpty
[toc] | [prev] | [next] | [standalone]
| From | Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> |
|---|---|
| Date | 2011-08-06 01:12 +0200 |
| Message-ID | <slrnj3p5pu.cho.zbigniew2011REMOVE@Tichy.myhome.org> |
| In reply to | #4595 |
In comp.lang.forth, Andrew Haley wrote:
> I think so. There is the matter of whether, say, return stack
> manipulation is more useful than tail call optimization. I'd choose
> tail call optimization every time, but IME code that (ab)uses the
> return stack for control flow is hard to understand and maintain.
> Whatever the problem, there's almost always a better way to do it than
> fiddling with the R-stack.
Maybe you're right - I understand, that the examples in "Starting Forth"
weren't recommendations for "how this should be done", but rather the
illustrations of return stack functionality and of structure of the words.
But my point was rather: if possible, I would rather expect return addresses
still on "traditional" return stack - as on "standard place" in case of most
Forth compilers - instead of searching "where is it this time?".
>> Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096
>> instead of "traditional" 1024 bytes - and it does make sense,
>> because meanwhile the HDD vendors changed the size of basic sector
>> unit to... 4096 bytes (from 512).
>
> Yes, it makes very good sense to do this. However, given a 4096-byte
> block buffer, you can easily get a compatible BLOCK with
>
>: block ( n - a) 4 /mod big-block swap 1024 * + ;
>
> So, there is no need for application code to be affected by the use of
> big blocks.
Right, but this is a "workaround for today"; and since during following
years we simply won't be able to buy an HDD with 512 byte sector, the
mentioned change seems to be logical consequence.
Of course it won't have any real meaning to compilers (most Forth compilers
of today), that aren't using "native" blocks, but "block files" instead.
--
...but I'm ready to learn ('bout) of the power of Forth
[toc] | [prev] | [next] | [standalone]
| From | coos haak <chforth@hccnet.nl> |
|---|---|
| Date | 2011-08-06 02:37 +0200 |
| Message-ID | <f8hafrkn3qwu.1g2y8vgp00gbv$.dlg@40tude.net> |
| In reply to | #4608 |
Op 6 Aug 2011 01:12:34 +0200 schreef Zbiggy: <snip> > Right, but this is a "workaround for today"; and since during following > years we simply won't be able to buy an HDD with 512 byte sector, the > mentioned change seems to be logical consequence. All disks I have used till now had 512 byte *physical* sectors, an OS might use them in chunks of 8 or something, it still does not matter for a program. The 1024 size of a Forth block is a *logical* size. Yes, it's possible to access physical sectors, e.g. on a floppy disk, but I think it would not be sensible to do that on a terabyte disk. -- Coos CHForth, 16 bit DOS applications http://home.hccnet.nl/j.j.haak/forth.html
[toc] | [prev] | [next] | [standalone]
| From | Zbiggy <zbigniew2011REMOVE@gmail.REMOVE.com> |
|---|---|
| Date | 2011-08-06 12:54 +0200 |
| Message-ID | <slrnj3qet8.2sb.zbigniew2011REMOVE@Tichy.myhome.org> |
| In reply to | #4612 |
In comp.lang.forth, coos haak wrote:
> All disks I have used till now had 512 byte *physical* sectors,
AFAIK presently (all? Or just "most"?) HDDs still have some "translation
chip" built-in, to be seen by older hardware as having 512 bytes per sector.
Can't examine it, as I'm using - like for today - quite small disks.
> an OS might
> use them in chunks of 8 or something, it still does not matter for a
> program. The 1024 size of a Forth block is a *logical* size.
Why exactly this size of a block you see as "logical"?
> Yes, it's possible to access physical sectors, e.g. on a floppy disk, but
> I think it would not be sensible to do that on a terabyte disk.
Why not, actually? If we mean e.g. using blocks as kind of "virtual memory",
direct use of physical sectors means faster access.
--
...but I'm ready to learn ('bout) of the power of Forth
[toc] | [prev] | [next] | [standalone]
| From | alextangent <blog@rivadpm.com> |
|---|---|
| Date | 2011-08-06 12:15 +0100 |
| Message-ID | <vbadnQqVlPvAv6DTnZ2dnUVZ8sudnZ2d@bt.com> |
| In reply to | #4620 |
On 2011-08-06 11:54, Zbiggy wrote: > In comp.lang.forth, coos haak wrote: > >> All disks I have used till now had 512 byte *physical* sectors, > > AFAIK presently (all? Or just "most"?) HDDs still have some "translation > chip" built-in, to be seen by older hardware as having 512 bytes per sector. > > Can't examine it, as I'm using - like for today - quite small disks. > >> an OS might >> use them in chunks of 8 or something, it still does not matter for a >> program. The 1024 size of a Forth block is a *logical* size. > > Why exactly this size of a block you see as "logical"? > >> Yes, it's possible to access physical sectors, e.g. on a floppy disk, but >> I think it would not be sensible to do that on a terabyte disk. > > Why not, actually? If we mean e.g. using blocks as kind of "virtual memory", > direct use of physical sectors means faster access. Forget physical sectors. 4K will become the norm, and not all disks are 512 byte sector size; some are 520 bytes. Flash memory will also become the predominant technology, and that needs a completely new way of think about writing to "disk", since the block erase overhead is considerable.
[toc] | [prev] | [next] | [standalone]
| From | Elizabeth D Rather <erather@forth.com> |
|---|---|
| Date | 2011-08-05 22:23 -0500 |
| Message-ID | <EaednVOh3bw-LqHTnZ2dnUVZ_qednZ2d@supernews.com> |
| In reply to | #4608 |
On 8/5/11 6:12 PM, Zbiggy wrote: > In comp.lang.forth, Andrew Haley wrote: > >> I think so. There is the matter of whether, say, return stack >> manipulation is more useful than tail call optimization. I'd choose >> tail call optimization every time, but IME code that (ab)uses the >> return stack for control flow is hard to understand and maintain. >> Whatever the problem, there's almost always a better way to do it than >> fiddling with the R-stack. > > Maybe you're right - I understand, that the examples in "Starting Forth" > weren't recommendations for "how this should be done", but rather the > illustrations of return stack functionality and of structure of the words. > > But my point was rather: if possible, I would rather expect return addresses > still on "traditional" return stack - as on "standard place" in case of most > Forth compilers - instead of searching "where is it this time?". As a user, you don't need to search, because real return addresses are not intended to be user-accessible. They are a system facility, as are others (such as code space and name fields). It inappropriate to think that you can manipulate return addresses in any way that is predictably the same from one implementation to another. FORTH83 mandated implementation details such as return addresses, stack widths (only 16 bits!), layout of definitions, etc. In the Forth94 process, we realized that this was a terrible mistake, and took the firm step towards specifying behavior, not implementation. The result has been significantly faster and more powerful Forth systems. The cost, of course, is that users can't write code that depends on implementation details. IMO it's a win. Forth offers plenty of ways to control program flow. You don't need to play cute tricks with return addresses. Cheers, Elizabeth -- ================================================== Elizabeth D. Rather (US & Canada) 800-55-FORTH FORTH Inc. +1 310.999.6784 5959 West Century Blvd. Suite 700 Los Angeles, CA 90045 http://www.forth.com "Forth-based products and Services for real-time applications since 1973." ==================================================
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2011-08-06 03:17 -0500 |
| Message-ID | <t_6dnc1zyNw-ZaHTnZ2dnUVZ7oudnZ2d@supernews.com> |
| In reply to | #4608 |
Zbiggy <zbigniew2011REMOVE@gmail.remove.com> wrote: > In comp.lang.forth, Andrew Haley wrote: > >>> Andy Valencia, ForthOS creator, proposed to use larger blocks - 4096 >>> instead of "traditional" 1024 bytes - and it does make sense, >>> because meanwhile the HDD vendors changed the size of basic sector >>> unit to... 4096 bytes (from 512). >> >> Yes, it makes very good sense to do this. However, given a 4096-byte >> block buffer, you can easily get a compatible BLOCK with >> >>: block ( n - a) 4 /mod big-block swap 1024 * + ; >> >> So, there is no need for application code to be affected by the use of >> big blocks. > > Right, but this is a "workaround for today"; and since during > following years we simply won't be able to buy an HDD with 512 byte > sector, the mentioned change seems to be logical consequence. It's not a workaround at all. In the past, Forths used to have different sized blocks, and there was a constant B/BUF that was used. Changing BLOCKs to a standard size across all implementations, no matter what their physical block size, was an improvement. To go back to the old days, where you had to cope with variable block sizes, is a regression. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Julian Fondren <ayrnieu@gmail.com> |
|---|---|
| Date | 2011-08-05 19:02 -0500 |
| Message-ID | <8662mb5rw8.fsf@gmail.com> |
| In reply to | #4595 |
Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
> I think so. There is the matter of whether, say, return stack
> manipulation is more useful than tail call optimization. I'd choose
> tail call optimization every time, but IME code that (ab)uses the
> return stack for control flow is hard to understand and maintain.
> Whatever the problem, there's almost always a better way to do it than
> fiddling with the R-stack.
Can you elaborate on your IME? I'm not being flippant; I'm privately
becoming more and more confident that fiddling with the return stack is
a huge readability and therefore maintainability win, so I'd appreciate
a course correction.
I recall that you prefer immediate words for a good subset of what
return-stack words can do. E.g.,
: with-base ( u -- )
r> BASE @ >r swap BASE !
['] call catch r> BASE ! throw ;
: base[ ( u -- )
postpone BASE postpone @ postpone >r
postpone BASE postpone ! ; immediate
: ]base ( -- )
postpone r> postpone BASE postpone ! ; immediate
: x1 ( ... "to end of line" -- ... )
16 with-base 0 parse evaluate ;
: x2 ( ... "to end of line" -- ... )
16 base[ 0 parse evaluate ]base ;
Return-stack access must be balanced after WITH-BASE and between BASE[
]BASE ; only WITH-BASE will reset the BASE if there's an exception
(maybe not desirable, but when it isn't WITH-BASE can be easily defined
to not do this). In the presence of tail-call optimization, WITH-BASE
_can_ be used to do perverse things that only sometimes work
: in-hex 16 with-base ;
: x3 ( c-addr u -- )
in-hex evaluate ; \ only works if WITH-BASE was JMP'd to.
, but I oppose that. You can take any feature too far. You can have a
Forth word that accepts twenty arguments on the data stack.
But, these are so similar; how is only one of them hard to understand
and maintain?
Another class of return-stack words are words that validate some input:
: -too-small ( c-addr u -- c-addr u | *returns from caller )
dup 2 > ?exit 2drop r>drop ;
: -not-forth ( c-addr u -- c-addr u | *returns from caller )
2dup 2 - + 2 s" .f" compare(nc) 0= ?exit 2drop r>drop ;
: .forth-file ( dirent-addr -- )
USING dirent filename zcount
-too-small -not-forth type space ;
"If it's not too small, and if it's not not forth, TYPE SPACE it."
Maybe S" .f" HAS-EXT; would be a better name, with the semicolon to
suggest the potential exit.
With immediate wordS:
: -too-small ( c-addr u -- c-addr u | *exit )
postpone dup 2 postpone literal postpone <=
postpone if postpone 2drop postpone exit postpone then ; immediate
And so on. It's used in exactly the same way, it has exactly the same
issue of "to test this, I need to use it within a temporary definition",
and it could be neater with a macro to replace the POSTPONEs.
So these are _exactly the same_. How is the only of them hard to
understand and maintain?
I could have also "not done that" - have written
: .forth-file ( dirent-addr -- )
USING dirent filename zcount
dup 2 <= if 2drop exit then
2dup 2 - + 2 s" .f" compare(nc) if 2drop exit then
type space ;
Or:
: .forth-file ( dirent-addr -- )
USING dirent filename zcount ( c-addr u )
dup 2 > if 2dup 2 - + 2 s" .f" compare(nc) 0= if
type space exit
then then 2drop ;
Or:
: .forth-file ( dirent-addr -- )
USING dirent filename zcount
2dup long-enough? if 2dup is-forth? if
type space exit
then then 2drop ;
The last is nice and conventional, but is the return-stack version
really any harder to read?
Which classes of return-stack-fiddling word do you object to?
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2011-08-06 04:01 -0500 |
| Message-ID | <Mq2dnfXtqqWanqDTnZ2dnUVZ8v-dnZ2d@supernews.com> |
| In reply to | #4609 |
Julian Fondren <ayrnieu@gmail.com> wrote: > Andrew Haley <andrew29@littlepinkcloud.invalid> writes: >> I think so. There is the matter of whether, say, return stack >> manipulation is more useful than tail call optimization. I'd choose >> tail call optimization every time, but IME code that (ab)uses the >> return stack for control flow is hard to understand and maintain. >> Whatever the problem, there's almost always a better way to do it than >> fiddling with the R-stack. > > Can you elaborate on your IME? I'm not being flippant; I'm privately > becoming more and more confident that fiddling with the return stack is > a huge readability and therefore maintainability win, so I'd appreciate > a course correction. > > I recall that you prefer immediate words for a good subset of what > return-stack words can do. E.g., > > : with-base ( u -- ) > r> BASE @ >r swap BASE ! > ['] call catch r> BASE ! throw ; What is CALL ? > : base[ ( u -- ) > postpone BASE postpone @ postpone >r > postpone BASE postpone ! ; immediate > : ]base ( -- ) > postpone r> postpone BASE postpone ! ; immediate > > : x1 ( ... "to end of line" -- ... ) > 16 with-base 0 parse evaluate ; > > : x2 ( ... "to end of line" -- ... ) > 16 base[ 0 parse evaluate ]base ; This doesn't do anything nonstandard with the R-stack, as far as I can see. It seems very elaborate for what it does, but I suppose if you have a very great deal of BASE-changing it might be worthwhile. On the other hand, it might be clearer simply to use WITH-BASE wthout all the syntactic sugar. > Return-stack access must be balanced after WITH-BASE and between BASE[ > ]BASE ; only WITH-BASE will reset the BASE if there's an exception > (maybe not desirable, but when it isn't WITH-BASE can be easily defined > to not do this). In the presence of tail-call optimization, WITH-BASE > _can_ be used to do perverse things that only sometimes work > > : in-hex 16 with-base ; > : x3 ( c-addr u -- ) > in-hex evaluate ; \ only works if WITH-BASE was JMP'd to. > > , but I oppose that. You can take any feature too far. You can have a > Forth word that accepts twenty arguments on the data stack. > > But, these are so similar; how is only one of them hard to understand > and maintain? I didn't say that. Fiddling with the R-stack is just one way to confuse the reader. There are many more. :-) > Another class of return-stack words are words that validate some input: > > : -too-small ( c-addr u -- c-addr u | *returns from caller ) > dup 2 > ?exit 2drop r>drop ; > : -not-forth ( c-addr u -- c-addr u | *returns from caller ) > 2dup 2 - + 2 s" .f" compare(nc) 0= ?exit 2drop r>drop ; > > : .forth-file ( dirent-addr -- ) > USING dirent filename zcount > -too-small -not-forth type space ; > > "If it's not too small, and if it's not not forth, TYPE SPACE it." > Maybe S" .f" HAS-EXT; would be a better name, with the semicolon to > suggest the potential exit. > > With immediate wordS: > > : -too-small ( c-addr u -- c-addr u | *exit ) > postpone dup 2 postpone literal postpone <= > postpone if postpone 2drop postpone exit postpone then ; immediate > > And so on. It's used in exactly the same way, it has exactly the same > issue of "to test this, I need to use it within a temporary definition", > and it could be neater with a macro to replace the POSTPONEs. > > So these are _exactly the same_. How is the only of them hard to > understand and maintain? They're both hard to understand and maintain. What's wrong with -too-small if -not-forth if type space then then ? Golly, even a Forth beginner could understand what that does . We can't have that! :-) > I could have also "not done that" - have written > > : .forth-file ( dirent-addr -- ) > USING dirent filename zcount > dup 2 <= if 2drop exit then > 2dup 2 - + 2 s" .f" compare(nc) if 2drop exit then > type space ; > > Or: > > : .forth-file ( dirent-addr -- ) > USING dirent filename zcount ( c-addr u ) > dup 2 > if 2dup 2 - + 2 s" .f" compare(nc) 0= if > type space exit > then then 2drop ; > > Or: > > : .forth-file ( dirent-addr -- ) > USING dirent filename zcount > 2dup long-enough? if 2dup is-forth? if > type space exit > then then 2drop ; > > The last is nice and conventional, but is the return-stack version > really any harder to read? Yes. Conventional is good. The maintenance programmer's time is valuable. Think about it for a moment: on first sight, how long would it take the reader to figure out what these programs do? Remember, you're writing code to be read. There's an old saying that appears in many forms, one of which is "Don't use a ten dollar word when a nickel one will do." This is good advice when writing English, and terrific advice when programming. People learning English often use heavyweight synonyms and complex structures they've learned. That's good while learning, but it's also a sure sign of someone not very fluent with the language. A maintenance programmer has a reasonable expectation that a heavyweight technique will only be used when demanded by a heavyweight problem. When they find otherwise there's a kind of cognitive dissonance (talk about 10-dollar words!) that forces a break in concentration. It also forces them to step away from the problem they were trying to solve in order to learn how your custom control structures work. This is not general advice against custom control structures, which are one of Forth's strengths. But it's important to realize that, while they can simplify code and make it more readable, there is a cost. A wise programmer balances costs and benefits, and uses powerful techniques sparingly. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2011-08-06 16:35 +0000 |
| Subject | return-address manipulation (was: multi-threading in Forth?) |
| Message-ID | <2011Aug6.183535@mips.complang.tuwien.ac.at> |
| In reply to | #4618 |
Andrew Haley <andrew29@littlepinkcloud.invalid> writes:
>Julian Fondren <ayrnieu@gmail.com> wrote:
>> : with-base ( u -- )
>> r> BASE @ >r swap BASE !
>> ['] call catch r> BASE ! throw ;
>
>What is CALL ?
>
>> : base[ ( u -- )
>> postpone BASE postpone @ postpone >r
>> postpone BASE postpone ! ; immediate
>> : ]base ( -- )
>> postpone r> postpone BASE postpone ! ; immediate
>>
>> : x1 ( ... "to end of line" -- ... )
>> 16 with-base 0 parse evaluate ;
>>
>> : x2 ( ... "to end of line" -- ... )
>> 16 base[ 0 parse evaluate ]base ;
[...]
>On
>the other hand, it might be clearer simply to use WITH-BASE wthout all
>the syntactic sugar.
But WITH-BASE is the word that uses return address manipulation.
Another alternative is to write
: eval-rest ( ... -- ... )
0 parse evaluate ;
: x3 ( ... "to end of line" -- ... )
['] eval-rest 16 base-execute ;
And of course, if we had nested colon defs (called quotations, blocks,
or lambda expressions in other languages). we could write this as follows:
: x4 ( ... "to end of line" -- ... )
[: 0 parse evaluate :] 16 base-execute ;
One disadvantage I see for the WITH-BASE variant here is that you have
to wrap it in another colon definition if you want to do anything
after restoring BASE.
>> : .forth-file ( dirent-addr -- )
>> USING dirent filename zcount
>> -too-small -not-forth type space ;
[...]
>They're both hard to understand and maintain. What's wrong with
>
> -too-small if -not-forth if type space then then
It's longer.
>> : .forth-file ( dirent-addr -- )
>> USING dirent filename zcount
>> 2dup long-enough? if 2dup is-forth? if
>> type space exit
>> then then 2drop ;
>>
>> The last is nice and conventional, but is the return-stack version
>> really any harder to read?
>
>Yes. Conventional is good. The maintenance programmer's time is
>valuable. Think about it for a moment: on first sight, how long would
>it take the reader to figure out what these programs do?
That's a good question. If the maintenance programmer is familiar
with the idiom, does it take the maintenance programmer longer to
understand the shorter code?
>A maintenance programmer has a reasonable expectation that a
>heavyweight technique will only be used when demanded by a heavyweight
>problem. When they find otherwise there's a kind of cognitive
>dissonance (talk about 10-dollar words!) that forces a break in
>concentration. It also forces them to step away from the problem they
>were trying to solve in order to learn how your custom control
>structures work.
You are assuming that the maintenance programmer is not familiar with
the idiom. The same argument could be used against any programming
technique and any programming language: Just assume that the
programmer has to learn the technique/language first, and your
argument will eliminate the technique/language from consideration. I
don't think that this argument is useful.
I am not convinced that return address manipulation offers enough over
"conventional" to make it worthwhile to teach and learn the idioms
that use it, or whether we should better concentrate on adding
features like nested colon defs. But in any case, I would like to see
more useful arguments in the discussion.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2011: http://www.euroforth.org/ef11/
[toc] | [prev] | [next] | [standalone]
| From | Gerry Jackson <gerry@jackson9000.fsnet.co.uk> |
|---|---|
| Date | 2011-08-07 11:45 +0100 |
| Subject | Re: return-address manipulation |
| Message-ID | <j1lqbi$u2h$1@dont-email.me> |
| In reply to | #4625 |
On 06/08/2011 17:35, Anton Ertl wrote:
[...]
> I am not convinced that return address manipulation offers enough over
> "conventional" to make it worthwhile to teach and learn the idioms
> that use it, or whether we should better concentrate on adding
> features like nested colon defs. But in any case, I would like to see
> more useful arguments in the discussion.
>
A few months ago I made an effort to understand some return stack
manipulation techniques, specifically those described in the writings of
mlg3 at:
https://groups.google.com/group/comp.lang.forth/browse_frm/thread/b8a35c3014a40d40/8854a1b225d5bace?hl=en&lnk=gst&q=foreachopcode#8854a1b225d5bace
and http://www.forth.org.ru/~mlg/ef94/ef94-2-paper.txt
To apply the technique I used the Spykerman Sudoku solver ( available
at:
http://www-personal.umich.edu/~williams/archive/forth/utilities/sudoku.fs)
as a testbed where I recoded a few words to see if it was easy, more
readable etc.
To give an example, I recoded the word that displays the Sudoku grid.
The original code was:
: .sudokugrid
CR CR
sudokugrid
81 0 do
dup i + c@ . ." "
i 1+
dup 3 mod 0= if
dup 9 mod 0= if
CR
dup 27 mod 0= if
dup 81 < if ." ------+-------+------" CR then
then
else
." ! "
then
then
drop
loop
drop
CR
;
which, to me least, is not a good example of well-written Forth as it is
difficult to read, but could probably be rewritten to be more readable
in conventional Forth (perhaps someone would like to try?). My rewrite was:
: for-each-square pro 81 0 do i cont loop ;
: .square ( grid sq -- grid sq ) 2dup + c@ . ;
: boxright ( sq -- sq | ) dup 3 mod if drop fail then ;
: gridright ( sq -- sq | ) dup 9 mod if drop ." ! " fail then cr ;
: boxbase ( sq -- ) 80 min 27 mod 0= if ." ------+-------+------" cr
then ;
: .sudokugrid ( -- )
cr cr sudokugrid ( -- grid )
start for-each-square ( -- grid sq )
.square 1+
boxright gridright boxbase ( -- grid )
emerge drop cr ( -- )
;
where the words PRO CONT FAIL START and EMERGE are all described in the
above references. If anyone is interested I can provide definitions for
these that work with GForth.
This could be improved e.g. by renaming START as FOR-EACH and EMERGE as
END-FOR and better word names for the factors.
As a generator of square numbers, FOR-EACH-SQUARE was re-used in the
recoding of the other words that I completed.
How easy was it? Of the four words I recoded, three were easy, the
fourth, called SOLVER, took several attempts to get right. That may have
been a candidate for mlg3's AMONG ... EACH ... ITERATE ... control
structure (but I never got that far - I'm not sure that structure can be
implemented in Forth alone).
--
Gerry
[toc] | [prev] | [next] | [standalone]
Page 2 of 4 — ← Prev page 1 [2] 3 4 Next page →
Back to top | Article view | comp.lang.forth
csiph-web