Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Compiling words and Forth programming style

469 views
Skip to first unread message

Howerd

unread,
Sep 3, 2014, 7:07:34 PM9/3/14
to
Hi All,

My attitude to compiling words was formed a long time ago, so I wanted to
see what I think today, and why.

I took a classic compiling word example from polyForth and tried some other styles.

I like to align the code vertically because it makes it easier to identify the strings.
The test word tthpAll displays a sample of the words' outputs so that
you can check by eye that they all work.

Votes please... I go for tthp5

Best regards,
Howerd

\ code starts here - copy and paste into SwiftForth to run

: (.) ( n -- ) 0 <# #s #> ; \ like . but with no trailing space

\ 1. Compiling words - the original, very clever, but I never liked it.
\ It is too hard to work out what is going on.
\ If you want to provide a library of powerful functions (in this case to create printer
\ control strings) this would be good, but you would have to document what ESC
\ actually does, which is not easy.

\ 58 Unit 0 Part 0 Abs 58
( 0) ( Enhanced HP LaserJet) DECIMAL
( 1) : ESC CREATE 32 STRING 0 C, DOES> BASE @ >R DECIMAL
( 2) 27 EMIT COUNT 2DUP TYPE + BEGIN COUNT ?DUP WHILE
( 3) ROT (.) TYPE 2DUP TYPE + REPEAT DROP R> BASE ! ;
( 4) : [#] -2 ALLOT 32 STRING 0 C, ; \ note the -2 to skip the trailing null in SwiftForth
( 5) ESC RESET E ESC PROP (s [#] P
( 6) ESC 18CPI &k6.67H ESC 16CPI &k7.2H
( 7) ESC PITCH (s [#] H ESC POINT (s [#] V
( 8) ESC FACE (s [#] T ESC (tab) &a [#] c [#] R
( 9) ESC LANDSCAPE &l1O ESC PORTRAIT &l0O
( 10) ESC margin &a [#] L ESC 66L/P &l14c1e7.64c66F
( 11) ESC U/L &dD ESC -U/L &d@
( 12) ESC CGTIMES (s4101T ESC COURIER (s3T
( 13) ESC LGOTHIC (s6T ESC +SCALE &k6W ESC -SCALE &k5W
( 14) ESC BOLD (s3B ESC NORM (s0B

: tthp1 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

\ 2. Simple approach
\ To avoid compiling words I usually go the simple route
\ Each word is defined using : and just does what it says
\ but it is a bit "wordy"
( Enhanced HP LaserJet) DECIMAL
: .dml ( n -- ) BASE @ >R DECIMAL (.) type R> BASE ! ; \ print in decimal
: .esc ( -- ) 27 EMIT ;
: RESET ( -- ) .esc ." E" ; : PROP ( n -- ) .esc ." (s" .dml ." P" ;
: 18CPI ( -- ) .esc ." &k6.67H" ; : 16CPI ( -- ) .esc ." &k7.2H" ;
: PITCH ( n -- ) .esc ." (s" .dml ." H" ; : POINT ( n -- ) .esc ." (s" .dml ." V" ;
: FACE ( n -- ) .esc ." (s" .dml ." T" ; : (tab) ( y x -- ) .esc ." &a" .dml ." c" .dml ." R" ;
: LANDSCAPE ( -- ) .esc ." &l1O" ; : PORTRAIT ( -- ) .esc ." &l0O" ;
: margin ( n -- ) .esc ." &a" .dml ." L" ; : 66L/P ( -- ) .esc ." &l14c1e7.64c66F" ;
: U/L ( -- ) .esc ." &dD" ; : -U/L ( -- ) .esc ." &d@" ;
\ : CGTIMES ( -- ) .esc ." (s4101T" ; : COURIER ( -- ) .esc ." (s3T" ;
: LGOTHIC ( -- ) .esc ." (s6T )" : +SCALE ( -- ) .esc ." &k6W" ;
: -SCALE ( -- ) .esc ." &k5W" ;
: BOLD ( -- ) .esc ." .(s3B" ; : NORM ( -- ) .esc ." .(s0B" ;

: tthp2 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

\ 3. Immediate words to reduce number of characters
\ The immediate words split the text flow, making it harder to read
\ You also have to explain what the immediate words are doing, and it forces
\ you to be aware of the compliation process, not the execution of the words.
\ I don't like this at all
( Enhanced HP LaserJet) DECIMAL
: .dml ( n -- ) BASE @ >R DECIMAL (.) type R> BASE ! ;
: .esc ( -- ) 27 EMIT ;
: .esc" ( -- ) postpone .esc postpone (s") [char] " string postpone type ; IMMEDIATE
: .dml" ( -- ) postpone .dml postpone (s") [char] " string postpone type ; IMMEDIATE
: RESET ( -- ) .esc" E" ; : PROP ( n -- ) .esc" (s" .dml ." P" ;
: 18CPI ( -- ) .esc" &k6.67H" ; : 16CPI ( -- ) .esc" &k7.2H" ;
: PITCH ( n -- ) .esc" (s" .dml" H" ; : POINT ( n -- ) .esc" (s" .dml" V" ;
: FACE ( n -- ) .esc" (s" .dml" T" ; : (tab) ( y x -- ) .esc" &a" .dml" c" .dml" R" ;
: LANDSCAPE ( -- ) .esc" &l1O" ; : PORTRAIT ( -- ) .esc" &l0O" ;
: margin ( n -- ) .esc" &a" .dml" L" ; : 66L/P ( -- ) .esc" &l14c1e7.64c66F" ;
: U/L ( -- ) .esc" &dD" ; : -U/L ( -- ) .esc" &d@" ;
: CGTIMES ( -- ) .esc" (s4101T" ; : COURIER ( -- ) .esc" (s3T" ;
: LGOTHIC ( -- ) .esc" (s6T )" ; : +SCALE ( -- ) .esc" &k6W" ;
: -SCALE ( -- ) .esc" &k5W" ;
: BOLD ( -- ) .esc" .(s3B" ; : NORM ( -- ) .esc" .(s0B" ;

: tthp3 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

\ A bit more factoring
\ I think this might be "pathalogical factoring" - just so you can say
\ that you factored it.
\ There are some patterns emerging, which could allow you to factor
\ into meaningful words such as : SetFont ( n -- ) .esc( [char] s .dml+ ." T" ;
( Enhanced HP LaserJet) DECIMAL
: .dml ( n -- ) BASE @ >R DECIMAL (.) type R> BASE ! ;
: .esc ( -- ) 27 EMIT ;
: .esc( ( -- ) .esc [char] ( emit ;
: .esc& ( -- ) .esc [char] & emit ;
: .dml+ ( n c -- ) emit .dml ;

: RESET ( -- ) .esc ." E" ; : PROP ( n -- ) .esc( [char] s .dml+ ." P" ;
: 18CPI ( -- ) .esc& ." k6.67H" ; : 16CPI ( -- ) .esc& ." k7.2H" ;
: PITCH ( n -- ) .esc( [char] s .dml+ ." H" ; : POINT ( n -- ) .esc( [char] s .dml+ ." V" ;
: FACE ( n -- ) .esc( [char] s .dml+ ." T" ; : (tab) ( y x -- ) .esc& [char] a .dml+ [char] c .dml+ ." R" ;
: LANDSCAPE ( -- ) .esc& ." l1O" ; : PORTRAIT ( -- ) .esc& ." l0O" ;
: margin ( n -- ) .esc& [char] a .dml+ ." L" ; : 66L/P ( -- ) .esc& ." l14c1e7.64c66F" ;
: U/L ( -- ) .esc& ." dD" ; : -U/L ( -- ) .esc& ." d@" ;
: CGTIMES ( -- ) .esc( ." s4101T" ; : COURIER ( -- ) .esc( ." s3T" ;
: LGOTHIC ( -- ) .esc( ." s6T )"; : +SCALE ( -- ) .esc& ." k6W" ;
: -SCALE ( -- ) .esc& ." k5W" ;
: BOLD ( -- ) .esc ." .(s3B" ; : NORM ( -- ) .esc ." .(s0B" ;

: tthp4 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

\ String approach
\ I like this one, because EscType is easy to explain
\ EscType allows you to specify the data content of each word precisely
\ If you need to send a # you are a bit stuck though...
\ SetFont is really neat now :-
\ : SetFont ( n -- ) s" (s#T" EscType ;

( Enhanced HP LaserJet) DECIMAL
\ Send an escape then the string but replace a # by the number on TOS
: EscType ( ... c-addr n -- )
#27 emit \ start with an escape character
over + swap ?do
i c@ dup [char] # = if \ send a number
drop BASE @ >R DECIMAL (.) type R> BASE !
else
emit \ send any other character
then
loop
;
: RESET ( -- ) s" E" EscType ; : PROP ( n -- ) s" (s#P" EscType ;
: 18CPI ( -- ) s" &k6.67H" EscType ; : 16CPI ( -- ) s" &k7.2H" EscType ;
: PITCH ( n -- ) s" (s#H" EscType ; : POINT ( n -- ) s" (s#V" EscType ;
: FACE ( n -- ) s" (s#T" EscType ; : (tab) ( y x -- ) s" &a#c#R" EscType ;
: LANDSCAPE ( -- ) s" &l1O" EscType ; : PORTRAIT ( -- ) s" &l0O" EscType ;
: margin ( n -- ) s" &a#L" EscType ; : 66L/P ( -- ) s" &l14c1e7.64c66F" EscType ;
: U/L ( -- ) s" &dD" EscType ; : -U/L ( -- ) s" &d@" EscType ;
: CGTIMES ( -- ) s" (s4101T" EscType ; : COURIER ( -- ) s" (s3T" EscType ;
: LGOTHIC ( -- ) s" (s6T )" EscType ; : +SCALE ( -- ) s" &k6W" EscType ;
: -SCALE ( -- ) s" &k5W" EscType ;
: BOLD ( -- ) s" .(s3B" EscType ; : NORM ( -- ) s" .(s0B" EscType ;

: tthp5 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

: tthpAll tthp1 tthp2 tthp3 tthp4 tthp5 ;

tthpAll

Howerd

unread,
Sep 3, 2014, 7:25:40 PM9/3/14
to
Hi All,

I just noticed that Google groups uses a variable font - here is the file so that you can open it in a text editor, and see the wonderfully neat vertical alignment of the code :
www.inventio.co.uk/ForthStyle.f
www.inventio.co.uk/ForthStyle.zip

Other style suggestions welcome ...

Best regards,
Howerd

Raimond Dragomir

unread,
Sep 4, 2014, 1:32:19 AM9/4/14
to
The first version is clever indeed, but with NO COMMENTS. I'm sure that you after tens of thousands of forth programming will be able to add one line or two of comments, describing what ESC does (not that complicated, isn't it?)

The last version has one line of comment to EscType word... How nice. And it's the most C-ish version of all.

So, if one knows his application a bit (in this case just printing controls) nothing is so hard in this sources for him. Even that a novice forth programmer will not understand the first version ESC word, if he is provided with the word and A BIT of comment, I'm sure that he will use it happily, and maybe he will even understand how it works some day (maybe day three? :)

BTW, the pathologigal factoring version is the worst.

Andrew Haley

unread,
Sep 4, 2014, 3:50:03 AM9/4/14
to
Howerd <how...@yahoo.co.uk> wrote:
> Hi All,
>
> My attitude to compiling words was formed a long time ago, so I wanted to
> see what I think today, and why.
>
> I took a classic compiling word example from polyForth and tried some other styles.
>
> I like to align the code vertically because it makes it easier to identify the strings.
> The test word tthpAll displays a sample of the words' outputs so that
> you can check by eye that they all work.
>
> Votes please... I go for tthp5

The classic version from polyFORTH, by a country mile. Although the
layout does need fixing. Sorry. :-)

I remember Chris Stephens' Law Of Boredom: if you ever do something
more than twice, it's time to do some factoring.

Andrew.

Howerd

unread,
Sep 4, 2014, 3:53:42 AM9/4/14
to
Hi Raimond,

> The first version is clever indeed, but with NO COMMENTS.
This is not my code - I always add comments.

> I'm sure that you after tens of thousands of forth programming
> will be able to add one line or two of comments,
This is Andrew's estimate of my Forth programming experience, not mine ;-)
But I have been using Forth since 1978, sometimes not very well, though.

> describing what ESC does (not that complicated, isn't it?)
But it is more complicated than EscType.

> The last version has one line of comment to EscType word... How nice. And it's > the most C-ish version of all.
C solved the general problem of how to embed non-printable characters into a printable string. Once you have got that language feature, you might as well use it.
I think it is no coincidence that the HP printer codes can be expressed easily in C.
Given C, Hewlett Packard naturally make a control code spec that fits C, but maybe there are better ways - once you are using C you are tied in...

> So, if one knows his application a bit (in this case just printing controls)
> nothing is so hard in this sources for him.
Indeed.

> BTW, the pathologigal factoring version is the worst.
Agreed.

Best regards,
Howerd

On Thursday, 4 September 2014 06:32:19 UTC+1, Raimond Dragomir wrote:
> joi, 4 septembrie 2014, 02:25:40 UTC+3, Howerd a scris:
>
> > Hi All,
>
> >
>
> >
>
> >
>
> > I just noticed that Google groups uses a variable font - here is the file so that you can open it in a text editor, and see the wonderfully neat vertical alignment of the code :
>
> >
>
> > www.inventio.co.uk/ForthStyle.f
>
> >
>
> > www.inventio.co.uk/ForthStyle.zip
>
> >
>
> >
>
> >
>
> > Other style suggestions welcome ...
>
> >
>
> >
>
> >
>
> > Best regards,
>
> >
>
> > Howerd
>
> > On Thursday, 4 September 2014 00:07:34 UTC+1, Howerd wrote:
[snip]

Mark Wills

unread,
Sep 4, 2014, 4:20:02 AM9/4/14
to
<snip>It is too hard to work out what is going on. </snip>

Why do you need to work out what is going on? If you know that the
code works, just use it. This does seem to be an issue amongst Forth
coders, much so than, say, Java or C programmers. If I download a Java
library to do something, I don't perform an autopsy on the code. I use
it, and if it works as advertised, great. If not, I curse the programmer
and move on!

The problem with ESC as posted above is not the complexity. It's that
it's not documented. Not even a stack comment. Fire the programmer :-)

Forth programmers just don't "do" libraries, do they? I think it has
something to do with forever trying to one-up other Forth coders.

1st Forth guy: "Hey, here's my library to do fixed point trig!"
2nd Forth guy: "Ugh! What ugly code. I wouldn't do it like *that*!"
1st Forth guy: "Have you ever written a fixed point trig library?"
2nd Forth guy: "Well, no, but if I did I wouldn't do.."
1st Forth guy: "Well STFU then!"

Andrew Haley

unread,
Sep 4, 2014, 4:25:06 AM9/4/14
to
Howerd <how...@yahoo.co.uk> wrote:
> Hi Raimond,
>
>> The first version is clever indeed, but with NO COMMENTS.
> This is not my code - I always add comments.
>
>> I'm sure that you after tens of thousands of forth programming
>> will be able to add one line or two of comments,
> This is Andrew's estimate of my Forth programming experience, not mine ;-)

It's almost certainly true, though.

>> BTW, the pathologigal factoring version is the worst.

Which one is that?

Andrew.

humptydumpty

unread,
Sep 4, 2014, 4:32:42 AM9/4/14
to
Hi!

A little try for ESC & [#], one|two things/line and commented:

: ESC ( escape-sequence definer; !SwiftForth append 0-char tail after a counted string! )
CREATE ( "esc-seq-name" -- )
BL STRING 0 C, \ parse&store counted-string, store null-counted-string
DOES> ( .. a -- ; .. means parameters )
BASE @ >R DECIMAL
27 EMIT
COUNT 2DUP TYPE + \ print start-esc-seq, walk to modifiers
BEGIN ( .. ca u )
COUNT ?DUP \ modifier?
WHILE
ROT (.) TYPE \ print parameter
2DUP TYPE + \ print modifier, walk to next one
REPEAT DROP
R> BASE !
;
: [#] ( append modifier to escape-sequence definition )
-2 ALLOT BL STRING 0 C, ; \ override 0-tail&null-string with modifier, append null-string


Have a nice day,
humptydumpty

Raimond Dragomir

unread,
Sep 4, 2014, 4:46:54 AM9/4/14
to
>
> A little try for ESC & [#], one|two things/line and commented:
>
>
>
> : ESC ( escape-sequence definer; !SwiftForth append 0-char tail after a counted string! )
>
> CREATE ( "esc-seq-name" -- )
>
> BL STRING 0 C, \ parse&store counted-string, store null-counted-string
>
> DOES> ( .. a -- ; .. means parameters )
>
> BASE @ >R DECIMAL
>
> 27 EMIT
>
> COUNT 2DUP TYPE + \ print start-esc-seq, walk to modifiers
>
> BEGIN ( .. ca u )
>
> COUNT ?DUP \ modifier?
>
> WHILE
>
> ROT (.) TYPE \ print parameter
>
> 2DUP TYPE + \ print modifier, walk to next one
>
> REPEAT DROP
>
> R> BASE !
>
> ;
>
> : [#] ( append modifier to escape-sequence definition )
>
> -2 ALLOT BL STRING 0 C, ; \ override 0-tail&null-string with modifier, append null-string
>
>
>
>
>
> Have a nice day,
>
> humptydumpty

I didn't complain about comments HOW it is implemented, I complained about comments WHAT IT DOES. In order for a user to use it :)
And the fact that there are no comments at all. This is the worst thing, in any languages. Maybe the comments are in a twin screen? It seems that the first source is in an old screen.

The sad truth is that Forth is not very good of self commenting. Compared to C, there are not explicit parameters, there is no explicit return value, usually there are no named local variables (forthers seems to abuse the stack instead of using locals). A good small C function, may not need any comment at all, especially if the names are well choosen. It's not the forth case. Forthers should comment more. But this is all about the quality of the coder, not the language.



Elizabeth D. Rather

unread,
Sep 4, 2014, 4:50:05 AM9/4/14
to
On 9/3/14 10:20 PM, Mark Wills wrote:
...
> Forth programmers just don't "do" libraries, do they? I think it has
> something to do with forever trying to one-up other Forth coders.

Well, in my experience it's more like this:

1st Forth guy: "Hey, here's my library to do fixed point trig!"
2nd Forth guy: "Wow, but why did you use only 14-bit fractions?"
1st Forth guy: "Because that's all the accuracy I had on my data in that
project."
2nd Forth guy: "Oh, dear, useless, then."
3rd Forth guy: "Hey, I used 31-bit fractions!"
2nd Forth guy: "Great, but why did you use that algorithm?"
3rd Forth guy: "Well, the chip I was using didn't have much RAM, so..."
2nd Forth guy: "But I need to optimize for speed."
4th Forth guy: "Well, I have a really fast one, but I'm afraid my
customer considers it proprietary..."
5th Forth guy: "Here, you can use my fast one."
2nd Forth guy: "But you wrote the inner code in assembler!"
5th Forth guy: "Well, you said you wanted fast..."

etc.

In other words, everything is customized for some particular set of
constraints in a particular project, and it ends up being easier just do
write what you need for your particular situation.

Back in the 70's, Chuck thought all his code would be reusable, and we
made microfiche of listings. Every time we needed a particular routine,
I'd try to find it in the fiche library. But when he read the code,
Chuck was always either sure that he could do it much better this time
or adamant that it didn't match the constraints of this situation.

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."
==================================================

Elizabeth D. Rather

unread,
Sep 4, 2014, 4:57:46 AM9/4/14
to
On 9/3/14 10:46 PM, Raimond Dragomir wrote:
...
>>
>> humptydumpty
>
> I didn't complain about comments HOW it is implemented, I complained about comments WHAT IT DOES. In order for a user to use it :)
> And the fact that there are no comments at all. This is the worst thing, in any languages. Maybe the comments are in a twin screen? It seems that the first source is in an old screen.
>
> The sad truth is that Forth is not very good of self commenting. Compared to C, there are not explicit parameters, there is no explicit return value, usually there are no named local variables (forthers seems to abuse the stack instead of using locals). A good small C function, may not need any comment at all, especially if the names are well choosen. It's not the forth case. Forthers should comment more. But this is all about the quality of the coder, not the language.

I'm all in favor of commenting more. But I really don't find using the
stack to be "abuse". Well-written Forth is rarely juggling many things,
and the definitions should be very short, compared with the number of
lines in a typical C program. I have seen a few cases (e.g. preparing a
Windows call with a zillion parameters) where locals are useful, but
rarely think they help in most code, except for Forth newbies who
haven't had enough practice to be comfortable with te stack.

There are certainly many situations in which regular VARIABLES and other
data structures are useful, though. I never understand why people are so
reluctant to use them when appropriate.

Andrew Haley

unread,
Sep 4, 2014, 5:12:33 AM9/4/14
to
Mark Wills <markwi...@gmail.com> wrote:
> <snip>It is too hard to work out what is going on. </snip>
>
> Why do you need to work out what is going on? If you know that the
> code works, just use it.

No, no, no! All code should be read, and reviewed, by as many people
as possible: it's the many eyes principle often described as "given
enough eyeballs, all bugs are shallow".

Andrew.


https://en.wikipedia.org/wiki/Linus%27s_Law

Mark Wills

unread,
Sep 4, 2014, 5:37:02 AM9/4/14
to
On Thursday, 4 September 2014 10:12:33 UTC+1, Andrew Haley wrote:
Nah. This is forth. You write a word. It's maybe three or four lines
long. You test the hell out of it. You let someone else test/break
it. You document it. You move on. Once its interfaces are defined
and it is known to work, it's a black-box. On to the next one!

humptydumpty

unread,
Sep 4, 2014, 5:45:54 AM9/4/14
to
On Thursday, September 4, 2014 11:46:54 AM UTC+3, Raimond Dragomir wrote:
> >
>
> > A little try for ESC & [#], one|two things/line and commented:
>
> >
>
> >
>
> >
>
> > : ESC ( escape-sequence definer; !SwiftForth append 0-char tail after a counted string! )
>
> >
>
> > CREATE ( "esc-seq-name" -- )
>
> >
>
> > BL STRING 0 C, \ parse&store counted-string, store null-counted-string
>
> >
>
> > DOES> ( .. a -- ; .. means parameters )
>
> >
>
> > BASE @ >R DECIMAL
>
> >
>
> > 27 EMIT
>
> >
>
> > COUNT 2DUP TYPE + \ print start-esc-seq, walk to modifiers
>
> >
>
> > BEGIN ( .. ca u )
>
> >
>
> > COUNT ?DUP \ modifier?
>
> >
>
> > WHILE
>
> >
>
> > ROT (.) TYPE \ print parameter
>
> >
>
> > 2DUP TYPE + \ print modifier, walk to next one
>
> >
>
> > REPEAT DROP
>
> >
>
> > R> BASE !
>
> >
>
> > ;
>
> >
>
> > : [#] ( append modifier to escape-sequence definition )
>
> >
>
> > -2 ALLOT BL STRING 0 C, ; \ override 0-tail&null-string with modifier, append null-string
>
> >
>
> >
>
> >
>
> >
>
> >
>
> > Have a nice day,
>
> >
>
> > humptydumpty
>
>
>

Hi!

> I didn't complain about comments HOW it is implemented, I complained about comments WHAT IT DOES. In order for a user to use it :)
>

I think that ESC definer and [#] modifier-compiler would deserve a lexicon, where usage should be described.

> And the fact that there are no comments at all. This is the worst thing, in any languages. Maybe the comments are in a twin screen? It seems that the first source is in an old screen.
>

Yes, I guess that comments could be in an shadow-screen.

>
>
> The sad truth is that Forth is not very good of self commenting. Compared to C, there are not explicit parameters, there is no explicit return value,

hmm, freedom of use have a cost too

usually there are no named local variables (forthers seems to abuse the stack instead of using locals).

data-stack is a bulls**t detector: if too much juggling it means
that I do not have a proper concept about what is going on
and must go factor concepts, then code, etc

A good small C function, may not need any comment at all, especially if the names are well choosen. It's not the forth case.

a minimum-commented word must expose in/out parameters,
a C-compiler additionally will do a type-checking of them.

Forthers should comment more. But this is all about the quality of the coder, not the language.

Paul E Bennett

unread,
Sep 4, 2014, 6:13:14 AM9/4/14
to
Lets get it in the right order

* Write the intent as a comment for the word (to be used as its glossary
entry) and thoroughly review the comments.
* Write the code to implement the intent
* Inspect and review the code thoroughly
* Test the word functions as expected
* Stress Test the word to try and make it behave outside the scope of the
intent stated in the comment you wrote to start with.
* DocGen the resultant source files to produce the documentation package
in very presentable form.

So, now you have the central essence of my paper for this years EuroForth.
See you guys in Palma de-Mallorca.

--
********************************************************************
Paul E. Bennett IEng MIET.....<email://Paul_E....@topmail.co.uk>
Forth based HIDECS Consultancy.............<http://www.hidecs.co.uk>
Mob: +44 (0)7811-639972
Tel: +44 (0)1235-510979
Going Forth Safely ..... EBA. www.electric-boat-association.org.uk..
********************************************************************

Anton Ertl

unread,
Sep 4, 2014, 6:58:39 AM9/4/14
to
Howerd <how...@yahoo.co.uk> writes:
>Hi All,
>
>My attitude to compiling words was formed a long time ago, so I wanted to
>see what I think today, and why.
>
>I took a classic compiling word example from polyForth and tried some other styles.
>
>I like to align the code vertically because it makes it easier to identify the strings.
>The test word tthpAll displays a sample of the words' outputs so that
>you can check by eye that they all work.
>
>Votes please... I go for tthp5

The second approach wins for me (but the lines are far too long). Any
of the "cleverer" versions buys relatively little, not enough to
justify the increased complexity. If there were more such words to
define, or if they were more uniform, then something cleverer would make sense.

- 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 2014: http://www.euroforth.org/ef14/

Raimond Dragomir

unread,
Sep 4, 2014, 7:13:29 AM9/4/14
to
> data-stack is a bulls**t detector: if too much juggling it means
>
> that I do not have a proper concept about what is going on
>
> and must go factor concepts, then code, etc

Ok, got it, do everything you can, but avoid locals.
I wonder how a locals section make it in the ANS standard...

Howerd

unread,
Sep 4, 2014, 7:20:35 AM9/4/14
to
Hi Mark,

> Why do you need to work out what is going on?
I have the type of personality that needs to know how everything works.

> If you know that the code works, just use it.
That's a big "if". The best way to see if the code works is to read it.
I think this is circular logic...

> This does seem to be an issue amongst Forth coders
Yes - it is a different mindset.

> The problem with ESC as posted above is not the complexity. It's that
> it's not documented. Not even a stack comment.
I think there are two problems - the code should be documented, but also it is too complicated.
Its level of complexity is such that it requires complex documentation.
A simpler implementation requires simpler documentation, is easier to understand, and perhaps most importantly does not put up a barrier to changes in the future.

Making a fancy compiling word, ESC , makes it very difficult (for me at least) to focus on what the problem really is.
Say you document ESC - "takes a space delimited string to send, preceded by an escape character, can be followed by [#] which sends a number followed by the next space delimited string".
Once you've got that in your mind you are no longer in a position to question whether the escape sequences that Hewlett Packard chose could be any better.
After all, you have got your ESC word to handle the complexity.
This is an example of the way in which complexity encourages more complexity.

Simplicity is difficult, complexity is easy. To quote Chuck Moore in his book http://www.colorforth.com/POL.htm :

"So to offer guidance when the trade-offs become obscure, I am going to define the Basic Principle:

Keep it Simple

As the number of capabilities you add to a program increases, the complexity of the program increases exponentially. The problem of maintaining compatibility among these capabilities, to say nothing of some sort of internal consistency in the program, can easily get out of hand. You can avoid this if you apply the Basic Principle. You may be acquainted with an operating system that ignored the Basic Principle.

It is very hard to apply. All the pressures, internal and external, conspire to add features to your program. After all, it only takes a half-dozen instructions; so why not? The only opposing pressure is the Basic Principle, and if you ignore it, there is no opposing pressure".

I love this quote :-)

Best regards,
Howerd

Howerd

unread,
Sep 4, 2014, 7:35:03 AM9/4/14
to
Hi Andrew,

OK, I just did a quick calculation : say 15 years @ 168 hours per month x 12 months = 30240 hours. Less a bit for teabreaks its still in the tens of thousands.
That's scary.

But I made the comment to try to diffuse what could have been seen as an "argument by authority" about the use of compiling words.

After all, you have seen some of my code, particularly in the early days ;-)

> >> BTW, the pathologigal factoring version is the worst.
> Which one is that?
Number 4 (sorry I missed the number in the comment. Look for tthp4 .
And I agree, it is bad.

One advantage of getting old is that other people's opinions seem to be of less interest.
I have found that trying to achieve simplicity in programming really works, even if it means re-inventing many wheels, and re-thinking solutions that were previously thought of as solved.

I have become aware of the "user illusion" - the smoke by which people make things complicated so that they can sell complicated solutions.

Sometimes I just have to remind myself that Chuck really did use a dialect of colorForth to create the GA144, and that even though the GA144 is so incredibly simple it can in fact be used to run RSA. It is truly a mysterious world :-)

Best regards,
Howerd



On Thursday, 4 September 2014 09:25:06 UTC+1, Andrew Haley wrote:

Raimond Dragomir

unread,
Sep 4, 2014, 7:53:13 AM9/4/14
to
> I have found that trying to achieve simplicity in programming really works, even if it means re-inventing many wheels, and re-thinking solutions that were previously thought of as solved.
>
>
>
> I have become aware of the "user illusion" - the smoke by which people make things complicated so that they can sell complicated solutions.
>
>
>
> Sometimes I just have to remind myself that Chuck really did use a dialect of colorForth to create the GA144, and that even though the GA144 is so incredibly simple it can in fact be used to run RSA. It is truly a mysterious world :-)
>

Nice said, maybe you're luckyier than others...

But many times you are not lucky. You just cannot achieve simplicity, others won't let you. Once I had a big piece of firmware to make, for a machine with 4 SD cards. It was quite obvious that at least one card was redundant. I asked the boss why not having 3 cards? The answer was: because the competition has three, we must have more.

Chuck is lucky because he makes whatever he likes, in his own way, no matter others say. But this has some drwabacks too: his chips are "a solution waiting for a problem" B-)


Andrew Haley

unread,
Sep 4, 2014, 10:05:23 AM9/4/14
to
Hi,

Howerd <how...@yahoo.co.uk> wrote:

> OK, I just did a quick calculation : say 15 years @ 168 hours per
> month x 12 months = 30240 hours. Less a bit for teabreaks its still
> in the tens of thousands. That's scary.
>
> But I made the comment to try to diffuse what could have been seen
> as an "argument by authority" about the use of compiling words.

I wasn't intended to be an argument from authority, it's just that
professionals' attitude to programming is different from amateurs.
Amateurs can be excellent programmers, and on a good day can produce
code as good or better than professionals, but their priorities are
inevitably different. Coding tricks are fun, but get tiresome when
they delay you from going home to be with your loved ones on a Friday
evening.

> After all, you have seen some of my code, particularly in the early
> days ;-)

And vice versa, of course.

> One advantage of getting old is that other people's opinions seem to
> be of less interest.

LOL! That's so great it might be a good .sig!

> I have found that trying to achieve simplicity in programming really
> works, even if it means re-inventing many wheels, and re-thinking
> solutions that were previously thought of as solved.
>
> I have become aware of the "user illusion" - the smoke by which
> people make things complicated so that they can sell complicated
> solutions.
>
> Sometimes I just have to remind myself that Chuck really did use a
> dialect of colorForth to create the GA144, and that even though the
> GA144 is so incredibly simple it can in fact be used to run RSA. It
> is truly a mysterious world :-)

Indeed it is.

Andrew.

rickman

unread,
Sep 4, 2014, 2:29:33 PM9/4/14
to
On 9/4/2014 7:35 AM, Howerd wrote:
> Hi Andrew,
>
> Sometimes I just have to remind myself that Chuck really did use a dialect of colorForth to create the GA144, and that even though the GA144 is so incredibly simple it can in fact be used to run RSA. It is truly a mysterious world :-)

If you find that "mysterious", consider that all of this can be
implemented with just NAND gates!

--

Rick

Gerry Jackson

unread,
Sep 4, 2014, 2:37:18 PM9/4/14
to
On 04/09/2014 00:07, Howerd wrote:
> Hi All,
>
> My attitude to compiling words was formed a long time ago, so I wanted to
> see what I think today, and why.
>
> I took a classic compiling word example from polyForth and tried some other styles.
>
> I like to align the code vertically because it makes it easier to identify the strings.
> The test word tthpAll displays a sample of the words' outputs so that
> you can check by eye that they all work.
>
> Votes please... I go for tthp5
>

I vote for the second one the simple approach, there's no reason to do
anything more complicated. Although I would use 0 .R in .dml instead of
(.) TYPE

The first one is a good example of how use of traditional Forth screens
often results in an unreadable mess of uncommented rectangular code.


--
Gerry

Albert van der Horst

unread,
Sep 4, 2014, 4:26:41 PM9/4/14
to
In article <a29bc76e-bcac-4eda...@googlegroups.com>,
This one gets my vote too.

Forth's may have formatting. I proposed %xxx embedded strings that are a
generalization of your EscType. xxx is in a special wordlist and executed
in a similar way to # above.

In ciforth it becomes

WANT FORMAT
\ Add %e as a formatter to add escape to the print string.
FORMAT-WID DEFINITIONS
: %e 27 CR$ $C+ ;
PREVIOUS DEFINITIONS

: RESET ( -- ) "%e E" FORMAT TYPE ;
: 18CPI ( -- ) "%e &k6.67H" FORMAT TYPE ;
: PITCH ( n -- ) "%e (s%d H" FORMAT TYPE ;
: FACE ( n -- ) "%e (s%d T" FORMAT TYPE ;
: LANDSCAPE ( -- ) "%e &l1O" FORMAT TYPE ;
: margin ( n -- ) "%e &a%d L" FORMAT TYPE ;
: U/L ( -- ) "%e &dD" FORMAT TYPE ;
: CGTIMES ( -- ) "%e (s4101T" FORMAT TYPE ;
: LGOTHIC ( -- ) "%e (s6T )" FORMAT TYPE ;
: -SCALE ( -- ) "%e &k5W" FORMAT TYPE ;
: BOLD ( -- ) "%e .(s3B" FORMAT TYPE ;

: PROP ( n -- ) "%e (s%d P" FORMAT TYPE ;
: 16CPI ( -- ) "%e &k7.2H" FORMAT TYPE ;
: POINT ( n -- ) "%e (s%d V" FORMAT TYPE ;
: (tab) ( y x -- ) SWAP "%e &a%d c%d R" FORMAT TYPE ;
: PORTRAIT ( -- ) "%e &l0O" FORMAT TYPE ;
: 66L/P ( -- ) "%e &l14c1e7.64c66F" FORMAT TYPE ;
: -U/L ( -- ) "%e &d@" FORMAT TYPE ;
: COURIER ( -- ) "%e (s3T" FORMAT TYPE ;
: +SCALE ( -- ) "%e &k6W" FORMAT TYPE ;

: NORM ( -- ) "%e .(s0B" FORMAT TYPE ;

: tthp6 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

( FORMAT is shamelessly ciforth specific, so I won't
show the code here. It certainly can be done in ISO-forth.)

If TYPE is more clever than EMIT (by outputting the string
as one system call), this approach is faster, even with the
run time lookup of %d and %e in a (very small) wordlist. 1]

Note the space behind each formatter and the SWAP in (tab) .

With this kind of application I hope my FORMAT word gets
some recognition, or even followers.

>
>: tthpAll tthp1 tthp2 tthp3 tthp4 tthp5 ;
>
>tthpAll

Groetjes Albert

1] And I have seen systems where escape was made a separate character
if there was a too long pause after it.
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

Albert van der Horst

unread,
Sep 4, 2014, 4:39:39 PM9/4/14
to
In article <R7adncJ5wtg3hJXJ...@supernews.com>,
Andrew Haley <andr...@littlepinkcloud.invalid> wrote:
>Howerd <how...@yahoo.co.uk> wrote:
>> Hi All,
>>
>> My attitude to compiling words was formed a long time ago, so I wanted to
>> see what I think today, and why.
>>
>> I took a classic compiling word example from polyForth and tried some other styles.
>>
>> I like to align the code vertically because it makes it easier to identify the strings.
>> The test word tthpAll displays a sample of the words' outputs so that
>> you can check by eye that they all work.
>>
>> Votes please... I go for tthp5
>
>The classic version from polyFORTH, by a country mile. Although the
>layout does need fixing. Sorry. :-)

I skipped the version immediately without even trying to understand it.
ESC was followed by several strings and not even the same number of them.
This kind of code requires a ton of comment. I can't be bothered to
reconstruct the comment by analyzing the code.

Compare that with my FORMAT solution. A tool is reused.

>
>I remember Chris Stephens' Law Of Boredom: if you ever do something
>more than twice, it's time to do some factoring.
>
>Andrew.

Albert van der Horst

unread,
Sep 4, 2014, 5:14:51 PM9/4/14
to
In article <6cec4e97-6f5b-4edf...@googlegroups.com>,
Howerd <how...@yahoo.co.uk> wrote:
>Hi Mark,
>
>> Why do you need to work out what is going on?
>I have the type of personality that needs to know how everything works.
>
>> If you know that the code works, just use it.
>That's a big "if". The best way to see if the code works is to read it.
>I think this is circular logic...

Read all code? No, impossible. Have you ever been on a 1M+ code base?
Most starting points for reading someone else's code is that it doesn't
work. And you can't just read "it". You first have to find the code
you should read for insight on the problem.

>
>> This does seem to be an issue amongst Forth coders
>Yes - it is a different mindset.
>
>> The problem with ESC as posted above is not the complexity. It's that
>> it's not documented. Not even a stack comment.
>I think there are two problems - the code should be documented, but also it is too complicated.
>Its level of complexity is such that it requires complex documentation.

Right! I guess the writer tried to document it, discovered that that was very
painful and thought "the hell with it! It works!" But in fact it doesn't work
for me. If I don't know what it is supposed to do, it doesn't work for me.

>A simpler implementation requires simpler documentation, is easier to understand, and perhaps
>most importantly does not put up a barrier to changes in the future.
>
>Making a fancy compiling word, ESC , makes it very difficult (for me at least) to focus on what
>the problem really is.
>Say you document ESC - "takes a space delimited string to send, preceded by an escape
>character, can be followed by [#] which sends a number followed by the next space delimited
>string".
>Once you've got that in your mind you are no longer in a position to question whether the
>escape sequences that Hewlett Packard chose could be any better.
>After all, you have got your ESC word to handle the complexity.
>This is an example of the way in which complexity encourages more complexity.
>
>Simplicity is difficult, complexity is easy. To quote Chuck Moore in his book
>http://www.colorforth.com/POL.htm :

So far I agree.

>
>"So to offer guidance when the trade-offs become obscure, I am going to define the Basic Principle:
>
>Keep it Simple
>
>As the number of capabilities you add to a program increases, the complexity of the program
>increases exponentially. The problem of maintaining compatibility among these capabilities, to
>say nothing of some sort of internal consistency in the program, can easily get out of hand.
>You can avoid this if you apply the Basic Principle. You may be acquainted with an operating
>system that ignored the Basic Principle.

This is simply not true. I'm currently working on the organ interface for manx.
Manx plays a number of notes together and with independant start and stop times on
metallophones, an internal speaker or any other instrument I come up with.
This problem has been solved.
Adding the organ means that I only need to solve the problem of starting and stopping
one note on the organ. (And then it can play notes together on the organ
as the piece requires).
This interface is pretty hard. The 24 magnets are in serial, bitbanging over a
clock and a data bit. But a magnet that must play a tone, must first be activated for a
certain period and then it must remain activated by a pulse width modulation.
Note, those 24 magnets each for a tone are quasi independant.
Meanwhile the other instruments are playing their part.

But in this well designed (ahem!) program it is not hard at all. I can concentrate on
this aspect, and discover it is even much less code then expected. Without the PWM
it would even be trivial.

I can (and will in due time) add playing over a midi interface and the
interference with other stuff will be zilch.

In a well designed program the complexity goes linear with the features, and the possibilities
go exponential in the features.

>
>It is very hard to apply. All the pressures, internal and external, conspire to add features to
>your program. After all, it only takes a half-dozen instructions; so why not? The only opposing
>pressure is the Basic Principle, and if you ignore it, there is no opposing pressure".

You must resist adding useless features. But in the end a program is not much more than
a conglomerate of features, so resisting features per se is pointless.
Oh, and you must keep balance and consistency in features, that too of course.

>
>I love this quote :-)

>
>Best regards,
>Howerd
>
>On Thursday, 4 September 2014 09:20:02 UTC+1, Mark Wills wrote:
>> <snip>It is too hard to work out what is going on. </snip>
>>
>>
>>
>> Why do you need to work out what is going on? If you know that the
>>
>> code works, just use it. This does seem to be an issue amongst Forth
>>
>> coders, much so than, say, Java or C programmers. If I download a Java
>>
>> library to do something, I don't perform an autopsy on the code. I use
>>
>> it, and if it works as advertised, great. If not, I curse the programmer
>>
>> and move on!
>>
>>
>>
>> The problem with ESC as posted above is not the complexity. It's that
>>
>> it's not documented. Not even a stack comment. Fire the programmer :-)
>>
>>
>>
>> Forth programmers just don't "do" libraries, do they? I think it has
>>
>> something to do with forever trying to one-up other Forth coders.
>>
>>
>>
>> 1st Forth guy: "Hey, here's my library to do fixed point trig!"
>>
>> 2nd Forth guy: "Ugh! What ugly code. I wouldn't do it like *that*!"
>>
>> 1st Forth guy: "Have you ever written a fixed point trig library?"
>>
>> 2nd Forth guy: "Well, no, but if I did I wouldn't do.."
>>
>> 1st Forth guy: "Well STFU then!"

Howerd

unread,
Sep 4, 2014, 5:41:43 PM9/4/14
to
Hi Raimond,

> Nice said, maybe you're luckier than others...
Thanks - I consider myself very lucky in many ways, including finding Forth :-)

> But many times you are not lucky.
Yes, I mostly program in C/C++ these days ;-)

> Chuck is lucky because he makes whatever he likes, in his own way, no matter
> others say.
Indeed :-)

> But this has some drawbacks too: his chips are "a solution waiting
> for a problem" B-)
I think the GA144 is not paying attention to the conventions of the day, and that this is a good thing.
I hope that GreenArrays improve the GA144 by allowing lower power "wire mode" operation, and maybe an external memory interface. It has a powerful synergy - everything just fits, and I know that this is not by accident.
It cannot be simpler without losing something significant.
But the pressure is there to make it bigger and more complicated - I hope Chuck and co. can resist ...

Best regards,
Howerd

Howerd

unread,
Sep 4, 2014, 5:43:26 PM9/4/14
to
Hi Rick,

> If you find that "mysterious", consider that all of this can be
> implemented with just NAND gates!
And everything is made of quarks , and still mysterious, to me at least ;-)

Best regards,
Howerd

rickman

unread,
Sep 4, 2014, 5:45:54 PM9/4/14
to
On 9/4/2014 5:41 PM, Howerd wrote:
> Hi Raimond,
>
>> Nice said, maybe you're luckier than others...
> Thanks - I consider myself very lucky in many ways, including finding Forth :-)
>
>> But many times you are not lucky.
> Yes, I mostly program in C/C++ these days ;-)
>
>> Chuck is lucky because he makes whatever he likes, in his own way, no matter
>> others say.
> Indeed :-)
>
>> But this has some drawbacks too: his chips are "a solution waiting
>> for a problem" B-)
> I think the GA144 is not paying attention to the conventions of the day, and that this is a good thing.
> I hope that GreenArrays improve the GA144 by allowing lower power "wire mode" operation, and maybe an external memory interface. It has a powerful synergy - everything just fits, and I know that this is not by accident.
> It cannot be simpler without losing something significant.
> But the pressure is there to make it bigger and more complicated - I hope Chuck and co. can resist ...

I don't know what "wire mode" operation is. Can you explain?

As to making the part more complex... well, they don't seem to have much
pressure at all at the moment... hardly anyone is buying the durn thing.

--

Rick

Howerd

unread,
Sep 4, 2014, 5:51:01 PM9/4/14
to
On Thursday, 4 September 2014 19:37:18 UTC+1, Gerry wrote:
Hi Gerry,

> I vote for the second one the simple approach, there's no reason to do
> anything more complicated.
Agreed.
> Although I would use 0 .R in .dml instead of (.) TYPE
OK.

> The first one is a good example of how use of traditional Forth screens
> often results in an unreadable mess of uncommented rectangular code.
I think the constraint here was the 80x25 text monitor, for which the old Forth blocks are an appropriate format. Comments should be in the shadow blocks - I couldn't find any for the printer code.

Best regards,
Howerd

Howerd

unread,
Sep 4, 2014, 6:00:00 PM9/4/14
to
Hi Albert,

Thanks for tthp6 - this seems to be even more C-like than tthp5.
Is this a good thing? I find the strings more difficult to read, because I am not used to your FORMAT tool. I think it is a matter of taste :-)

Best regards,
Howerd

On Thursday, 4 September 2014 21:26:41 UTC+1, Albert van der Horst wrote:
> In article <a29bc76e-bcac-4eda-9dcc-7....@googlegroups.com>,

Howerd

unread,
Sep 4, 2014, 6:03:25 PM9/4/14
to
Hi Albert,

A tool is reused, but that tool must be learned and remembered, and documented.
The tool also influences the type of code that you write - in C everything tends to be about data structures, etc.
There is room for both approaches, depending on the situation...

Best regards,
Howerd


On Thursday, 4 September 2014 21:39:39 UTC+1, Albert van der Horst wrote:
> In article <R7adncJ5wtg3hJXJnZ2dnUU7-eud....@supernews.com>,
>
> Andrew Haley <andrew29@littlepinkcl.....invalid> wrote:

Howerd

unread,
Sep 4, 2014, 7:01:45 PM9/4/14
to
Hi Rick,

"Wire mode" is when an F18 is setup to transfer data coming in on one port out to another. At the moment this has to be polled, so takes current.
I read that Chuck and Greg have a way to make it operate without polling.
This would mean that a GA144 could run polyForth at quiescent current, until some external event (RS232 or key press) occurred.

Best regards,
Howerd

rickman

unread,
Sep 4, 2014, 8:32:03 PM9/4/14
to
On 9/4/2014 7:01 PM, Howerd wrote:
> Hi Rick,
>
> "Wire mode" is when an F18 is setup to transfer data coming in on one port out to another. At the moment this has to be polled, so takes current.
> I read that Chuck and Greg have a way to make it operate without polling.
> This would mean that a GA144 could run polyForth at quiescent current, until some external event (RS232 or key press) occurred.

I think you have misunderstood something. One of the basic features of
the F18A processor in the GA144 is the ability to stop the CPU while
waiting for I/O, drawing no active current. I don't know what they have
done in the Polyforth implementation, but if the processor is simply
waiting for something to happen on the outside world there is no reason
I can think of why the processor would be running and drawing any
current other than quiescent.

Anyone here know why Polyforth on the GA144 keeps processors running
when there is nothing to do?

--

Rick

Elizabeth D. Rather

unread,
Sep 4, 2014, 8:58:10 PM9/4/14
to
I think GA did the implementation with our permission, so you'd have to
ask them.

Paul Rubin

unread,
Sep 4, 2014, 9:01:22 PM9/4/14
to
rickman <gnu...@gmail.com> writes:
> Anyone here know why Polyforth on the GA144 keeps processors running
> when there is nothing to do?

I had the impression that's just an artifact of the way Polyforth works.
It was written in the minicomputer era when cpu's didn't have power
control, so the Polyforth interpreter idle loop is a busy loop that
polls some things and never pauses the cpu. That's based on old posts
from this ng (i.e. I could be wrong) since I've never looked into the
issue directly. I don't know what if any obstacles exist to making
Polyforth pause the idle loop with occasional wakeups for polling.

Elizabeth D. Rather

unread,
Sep 4, 2014, 9:43:40 PM9/4/14
to
I agree that is probably what's going on. It's a very small, simple
piece of code, and it shouldn't be too hard to change it. A little
harder to decide what behavior you really want, especially as they don't
seem to have clocks.

francoi...@wanadoo.fr

unread,
Sep 5, 2014, 1:16:15 AM9/5/14
to
Hi,

I don't comment "in" words.
I comment only what the word do.
exemple :

: H.## ( c --- )
\ ----
\ Affiche un octets en Hexadecimal sur 2 digits
10h BASE !
0 <# # # #> TYPE
SPACE
10d BASE ! ;

(it's my own Forth, i don't use DECIMAL,HEX,OCTAL and BIN, it use extension "d","h","o", or "b" for base )

Like this, i know what the word do.

regards,
François

rickman

unread,
Sep 5, 2014, 1:17:54 AM9/5/14
to
On 9/4/2014 9:43 PM, Elizabeth D. Rather wrote:
> On 9/4/14 3:01 PM, Paul Rubin wrote:
>> rickman <gnu...@gmail.com> writes:
>>> Anyone here know why Polyforth on the GA144 keeps processors running
>>> when there is nothing to do?
>>
>> I had the impression that's just an artifact of the way Polyforth works.
>> It was written in the minicomputer era when cpu's didn't have power
>> control, so the Polyforth interpreter idle loop is a busy loop that
>> polls some things and never pauses the cpu. That's based on old posts
>> from this ng (i.e. I could be wrong) since I've never looked into the
>> issue directly. I don't know what if any obstacles exist to making
>> Polyforth pause the idle loop with occasional wakeups for polling.
>>
>
> I agree that is probably what's going on. It's a very small, simple
> piece of code, and it shouldn't be too hard to change it. A little
> harder to decide what behavior you really want, especially as they don't
> seem to have clocks.

I'm not sure clocks are needed. This code would have nothing else to do
except respond to the keyboard, right? In the original machine there
may have been other things going on having to do with the peripherals.
But on the GA144 what could Polyforth be doing?

The serial port driver is a node which I believe is triggered from the
edge of the start bit on the Rx line. The Tx would be free running
while transmitting then idle until another char is handed to it to
transmit. When no data is in process the comms is idle. I expect the
Polyforth could be idle anytime nothing is running and there are no
comms in action.

--

Rick

Elizabeth D. Rather

unread,
Sep 5, 2014, 2:57:03 AM9/5/14
to
Given that this polyFORTH seems to exist solely as a development
environmet, that would probably work. When it's running an entire
application it has to be able to support lots of things going on.

Andrew Haley

unread,
Sep 5, 2014, 3:30:43 AM9/5/14
to
It could be done, but it wouldn't be a trivial change. The low
latency of polyFORTH is due to its lightweight scheduler. To sleep
the CPU you'd have to wait for all tasks to be quiet and determine
that none are polling for an event; if some tasks are waiting for a
time delay, you have to manage timer queues. Of course this is
perfectly doable, but it's quite a lot more complicated. I expect
that SwiftX handles all this, of course.

Andrew.

Paul Rubin

unread,
Sep 5, 2014, 3:50:21 AM9/5/14
to
rickman <gnu...@gmail.com> writes:
> But on the GA144 what could Polyforth be doing?...
> Polyforth could be idle anytime nothing is running and there are no
> comms in action.

I think may have been solvable but didn't matter very much. So a few
of the GA144 cores on the development board's host-side processor are
spinning all the time. They use a few milliwatts each. In an embedded
circuit that would matter, but this is a dev board powered from a wall
socket, so nobody will likely care about the few milliwatts being lost.

Elizabeth D. Rather

unread,
Sep 5, 2014, 4:20:16 AM9/5/14
to
Yes, that's exactly how SwiftX works. On systems with a 'sleep' mode, it
automatically sleeps when no tasks are active. This means some parts can
have duty cycles in microseconds.

Howerd

unread,
Sep 5, 2014, 4:38:46 AM9/5/14
to
Hi Rick,

Its a while, but I believe the problem is when an F18 has to deal with its own "interrupts" and also transfer data across it's ports. It can do either one or the other without polling, but must poll (i.e. run a continuous loop and consume dynamic power) to do both.
I believe that Chuck and Greg had worked out how to fix this, involving additional latches, but I don't know how far they have got with this.

There is nothing about polyForth that requires polling - this is IMO a shortcoming of the GA144/F18 architecture that means that the GA144 consumes about 50% of the power of most other micros, when it could be 0.05%...
I hope the GreenArray guys will forgive me for saying that the GA144 is not perfect - it is still an amazing chip, even more so when you consider that it was developed by a small team on a shoestring budget, and yet outperforms the big boys.

Best regards,
Howerd

Albert van der Horst

unread,
Sep 5, 2014, 6:46:51 AM9/5/14
to
As far as I remember there was the following issue:
If a processor has the job to pass on data from N to S,
*and* from W to E simultaneously, it has to poll, irrespective of
whether there is something going on at all.
This kind of processors will be there on most non-trivial applications.

>
>--
>
>Rick


Groetjes Albert

Albert van der Horst

unread,
Sep 5, 2014, 6:56:42 AM9/5/14
to
In article <18816e1a-c13c-4d8c...@googlegroups.com>,
That's the spirit. However, I tend to put that kind of comment
above the definition, to convey the notion that you don't need
to look inside the word to use it.
Side effects must be clear from the comment. In this case "affiche"
implies that there is output on the screen.
This is vastly superior to a ( c -- ) stack diagram.

\ ----
\ Affiche un OCTET en Hexadecimal sur 2 digits
: H.##
10h BASE !
0 <# # # #> TYPE
SPACE
10d BASE ! ;

The word in capitals (OCTET) indicates the input.
In this way the stack diagram can be derived from the comment.

This is my style: (stack diagrams considered harmful).
\ Do this to THAT and THATOTHER, return the THING done.
implies a stack diagram of ( that thatother -- thing )

>regards,
>François

Howerd

unread,
Sep 5, 2014, 7:18:49 AM9/5/14
to
Hi All,

I have reformatted and commented the original clever code tthp1 to make it a fairer comparison with the others.
I've also changed ESC to ESC: to make it clearer that it is a compiling word.
I also changed STRING to bl word count string, because STRING uses PARSE which does not skip leading spaces, but word does.

Andrew : comparing tthp1 and tthp5, ESC: and [#] are definitely more complicated that EscType, but the data readability of the child words is similar.
But, I would say that
: PITCH ( n -- ) s" (s#H" EscType ;
is easier to follow than
ESC: PITCH ( n -- ) (s [#] H
because the former uses well-known constructs : s" ..." ;
whereas the latter requires an understanding of the parsing and compilation process to see what is going on.
Also the fact that ESC: and [#] use a space delimiter means that the string is broken up into pieces (s_[#]_H , which I find less intuitive than (s#H .

I think we may have to agree to differ - this is after all a matter of personal taste :-)

Maybe this discussion is an example of why Forth is not popular in a business environment - in C there would be no such discussion, since C has already solved this particular problem in a generic way :
void PITCH ( int size )
{
printf( "\0x1B(s%nH", size ); // untested...
}

It is not going to make team working efficient if every detail has to be discussed in this way, it is better just do do it in a fixed way even if its worse...

Best regards,
Howerd

I will post the new file when I get time :
www.inventio.co.uk/ForthStyle2.f
www.inventio.co.uk/ForthStyle2.zip


\ ForthStyle2.f 2014 Sep 05
\ code starts here - copy and paste into SwiftForth

: (.) ( n -- ) 0 <# #s #> ; \ like . but with no trailing space

\ 1. Compiling words - the original, very clever, but I never liked it.
\ It is too hard to work out what is going on.
\ If you want to provide a library of powerful functions (in this case to create printer
\ control strings) this would be good, but you would have to document what ESC:
\ actually does, which is not easy.

( Enhanced HP LaserJet) DECIMAL
: ESC: ( -- )
create \ create our new word using the name following in the input stream
[char] ) word \ skip over the stack comment
bl word count string, \ compile the next space-delimited word into the new words PFA field
0 c, \ add a 0 to mark the end of the data to send
does>
BASE @ >r decimal
#27 emit \ start with an escape character
count 2dup type + \ send the first string compiled into the child word's PFA
begin count ?dup while rot (.) type 2dup type + repeat drop \ for each additional string found, send the TOS then the string
r> BASE !
;
: [#] ( -- ) \ append a new string. The run-time code of ESC: always sends a number first if it finds a non-zero string after the first string.
-2 ALLOT \ note the -2 to skip back past the trailing null and 0 c, in SwiftForth, was -1 in polyForth to skip past the 0 c,
bl word count string, \ compile the next space-delimited word into the new words PFA field
0 c, \ add a 0 to mark the end of the data to send
;
ESC: RESET ( -- ) E ESC: PROP ( n -- ) (s [#] P
ESC: 18CPI ( -- ) &k6.67H ESC: 16CPI ( -- ) &k7.2H
ESC: PITCH ( n -- ) (s [#] H ESC: POINT ( n -- ) (s [#] V
ESC: FACE ( n -- ) (s [#] T ESC: (tab) ( y x -- ) &a [#] c [#] R
ESC: LANDSCAPE ( -- ) &l1O ESC: PORTRAIT ( -- ) &l0O
ESC: margin ( n -- ) &a [#] L ESC: 66L/P ( -- ) &l14c1e7.64c66F
ESC: U/L ( -- ) &dD ESC: -U/L ( -- ) &d@
ESC: CGTIMES ( -- ) (s4101T ESC: COURIER ( -- ) (s3T
ESC: LGOTHIC ( -- ) (s6T ESC: +SCALE ( -- ) &k6W
ESC: -SCALE ( -- ) &k5W
ESC: BOLD ( -- ) (s3B ESC: NORM ( -- ) (s0B
\ 4. A bit more factoring
\ I think this might be "pathalogical factoring" - just so you can say
\ that you factored it.
\ There are some patterns emerging, which could allow you to factor
\ into meaningful words such as : SetFont ( n -- ) .esc( [char] s .dml+ ." T" ;
( Enhanced HP LaserJet) DECIMAL
: .dml ( n -- ) BASE @ >R DECIMAL (.) type R> BASE ! ;
: .esc ( -- ) 27 EMIT ;
: .esc( ( -- ) .esc [char] ( emit ;
: .esc& ( -- ) .esc [char] & emit ;
: .dml+ ( n c -- ) emit .dml ;

: RESET ( -- ) .esc ." E" ; : PROP ( n -- ) .esc( [char] s .dml+ ." P" ;
: 18CPI ( -- ) .esc& ." k6.67H" ; : 16CPI ( -- ) .esc& ." k7.2H" ;
: PITCH ( n -- ) .esc( [char] s .dml+ ." H" ; : POINT ( n -- ) .esc( [char] s .dml+ ." V" ;
: FACE ( n -- ) .esc( [char] s .dml+ ." T" ; : (tab) ( y x -- ) .esc& [char] a .dml+ [char] c .dml+ ." R" ;
: LANDSCAPE ( -- ) .esc& ." l1O" ; : PORTRAIT ( -- ) .esc& ." l0O" ;
: margin ( n -- ) .esc& [char] a .dml+ ." L" ; : 66L/P ( -- ) .esc& ." l14c1e7.64c66F" ;
: U/L ( -- ) .esc& ." dD" ; : -U/L ( -- ) .esc& ." d@" ;
: CGTIMES ( -- ) .esc( ." s4101T" ; : COURIER ( -- ) .esc( ." s3T" ;
: LGOTHIC ( -- ) .esc( ." s6T )"; : +SCALE ( -- ) .esc& ." k6W" ;
: -SCALE ( -- ) .esc& ." k5W" ;
: BOLD ( -- ) .esc ." .(s3B" ; : NORM ( -- ) .esc ." .(s0B" ;

: tthp4 cr RESET space 123 PROP space 18CPI space 1 2 (tab) ;

\ 5. String approach
: tthpAll tthp1 tthp2 tthp3 tthp4 tthp5 ;

tthpAll

On Thursday, 4 September 2014 08:50:03 UTC+1, Andrew Haley wrote:
> Howerd <how....@yahoo.co.uk> wrote:
>
> > Hi All,
>
> >
>
> > My attitude to compiling words was formed a long time ago, so I wanted to
>
> > see what I think today, and why.
>
> >
>
> > I took a classic compiling word example from polyForth and tried some other styles.
>
> >
>
> > I like to align the code vertically because it makes it easier to identify the strings.
>
> > The test word tthpAll displays a sample of the words' outputs so that
>
> > you can check by eye that they all work.
>
> >
>
> > Votes please... I go for tthp5
>
>
>
> The classic version from polyFORTH, by a country mile. Although the
>
> layout does need fixing. Sorry. :-)
>
>
>

Andrew Haley

unread,
Sep 5, 2014, 7:47:10 AM9/5/14
to
Howerd <how...@yahoo.co.uk> wrote:
> Hi All,
>
> I have reformatted and commented the original clever code tthp1 to
> make it a fairer comparison with the others.
> I've also changed ESC to ESC: to make it clearer that it is a
> compiling word.
> I also changed STRING to bl word count string, because STRING uses
> PARSE which does not skip leading spaces, but word does.
>
> Andrew : comparing tthp1 and tthp5, ESC: and [#] are definitely more
> complicated that EscType, but the data readability of the child
> words is similar.

True. And also it's EMITting one character at a time, rather than
TYPEing them n a bunch. Some people at this point might ask "why does
that mater?" but real Forths at the time were very careful about
things like this, and it did make a difference when many people were
sharing the system.

> But, I would say that
> : PITCH ( n -- ) s" (s#H" EscType ;
> is easier to follow than
> ESC: PITCH ( n -- ) (s [#] H
> because the former uses well-known constructs : s" ..." ;
> whereas the latter requires an understanding of the parsing and
> compilation process to see what is going on.

Indeed. So the first version is smaller, more efficient, and harder
to read.

> Maybe this discussion is an example of why Forth is not popular in a
> business environment - in C there would be no such discussion, since
> C has already solved this particular problem in a generic way :

Not really: that involves runtime scanning, and if this gets used a
lot people will consider using lower-level functions.

Andrew.

rickman

unread,
Sep 5, 2014, 1:34:28 PM9/5/14
to
Yes, and sometimes a clock is needed to be able to measure time. But
that is application dependent. In synchronous devices the clock is
required for them to work at all and so we get used to having them. We
even use the defined time of instruction execution to measure time in
some cases. In an async machine the only accurate way to measure time
is to use a clock. The timing of instruction execution varies a great
deal with variations in process, voltage and temperature making it hard
to use the instruction timing of the async CPU.

In the F18 it makes sense to suspend the CPU while waiting for something
to do. If a timer is needed it should be done like the other
peripherals using an edge node controlled by an external clock, again
asleep other than on clock edges.

--

Rick

rickman

unread,
Sep 5, 2014, 1:35:24 PM9/5/14
to
Yes, that may be true. It could have been an example of how to do
things "right" on a GA144 however.

--

Rick

rickman

unread,
Sep 5, 2014, 1:40:42 PM9/5/14
to
So Polyforth is multitasking? That seems very odd on a multiprocessor
to use one processor (or a small handfull) to run multiple tasks.

Even so, the only reason to spin rather the halting is because there is
no clock to use to measure time so the CPU has to burn clock cycles to
measure time. I guess they needed to get the thing out the door and as
Paul pointed out there is not a lot of need to minimize power on the
development CPU.

--

Rick

rickman

unread,
Sep 5, 2014, 1:43:04 PM9/5/14
to
On 9/5/2014 6:46 AM, Albert van der Horst wrote:
> In article <lub0a1$526$1...@dont-email.me>, rickman <gnu...@gmail.com> wrote:
>> On 9/4/2014 7:01 PM, Howerd wrote:
>>> Hi Rick,
>>>
>>> "Wire mode" is when an F18 is setup to transfer data coming in on one
>> port out to another. At the moment this has to be polled, so takes
>> current.
>>> I read that Chuck and Greg have a way to make it operate without polling.
>>> This would mean that a GA144 could run polyForth at quiescent current,
>> until some external event (RS232 or key press) occurred.
>>
>> I think you have misunderstood something. One of the basic features of
>> the F18A processor in the GA144 is the ability to stop the CPU while
>> waiting for I/O, drawing no active current. I don't know what they have
>> done in the Polyforth implementation, but if the processor is simply
>> waiting for something to happen on the outside world there is no reason
>> I can think of why the processor would be running and drawing any
>> current other than quiescent.
>>
>> Anyone here know why Polyforth on the GA144 keeps processors running
>> when there is nothing to do?
>
> As far as I remember there was the following issue:
> If a processor has the job to pass on data from N to S,
> *and* from W to E simultaneously, it has to poll, irrespective of
> whether there is something going on at all.
> This kind of processors will be there on most non-trivial applications.

It has been awhile since I looked at this in detail, but I am pretty
sure the F18A can suspend waiting for more than one event. It does not
automatically "know" which event woke it up and it has to do some I/O
port reading to determine that. But I don't think this is the reason
why Polyforth would be polling.

--

Rick

Paul Rubin

unread,
Sep 5, 2014, 2:00:41 PM9/5/14
to
rickman <gnu...@gmail.com> writes:
> So Polyforth is multitasking? That seems very odd on a multiprocessor
> to use one processor (or a small handfull) to run multiple tasks.

No it's very normal to handle asynchronous events that way. In the PC
world you might have an 8-core cpu, that has 100's of network
connections open. For embedded, you might have timer tasks, i/o tasks,
the scheduler itself, etc.

> Even so, the only reason to spin rather the halting is because there
> is no clock to use to measure time so the CPU has to burn clock cycles
> to measure time.

Andrew explained it would complicate the software to not spin. Suppose
you have an external clock capable of waking the cpu at a time you
specify. You have N sleeping tasks that are scheduled to wake up at
given times T1,T2,...TN. The "right" way to do that is figure out which
of those times is the earliest, then suspend the CPU until that time;
then wake up, handle the task, then sleep til the next wakeup is due,
etc. Ideally you should use a priority queue data structure to quickly
identify the next task, instead of scanning through the whole (maybe
large) task list to find the next pending wakeup. This is what a
serious OS does, but it implies some implementation complexity.

The traditional Forth approach is to do the simplest thing possible. In
Polyforth's case, that might be to run all the tasks in round-robin
fashion non-stop. Each task gets a timeslice where it checks whether
it's time to wake up, and goes back to sleep if it's not time yet. That
burns some extra CPU cycles but is very simple to code.

Andrew Haley

unread,
Sep 5, 2014, 2:05:27 PM9/5/14
to
rickman <gnu...@gmail.com> wrote:
> On 9/5/2014 3:30 AM, Andrew Haley wrote:
>> Paul Rubin <no.e...@nospam.invalid> wrote:
>>> rickman <gnu...@gmail.com> writes:
>>>> Anyone here know why Polyforth on the GA144 keeps processors running
>>>> when there is nothing to do?
>>>
>>> I had the impression that's just an artifact of the way Polyforth
>>> works. It was written in the minicomputer era when cpu's didn't
>>> have power control, so the Polyforth interpreter idle loop is a busy
>>> loop that polls some things and never pauses the cpu. That's based
>>> on old posts from this ng (i.e. I could be wrong) since I've never
>>> looked into the issue directly. I don't know what if any obstacles
>>> exist to making Polyforth pause the idle loop with occasional
>>> wakeups for polling.
>>
>> It could be done, but it wouldn't be a trivial change. The low
>> latency of polyFORTH is due to its lightweight scheduler. To sleep
>> the CPU you'd have to wait for all tasks to be quiet and determine
>> that none are polling for an event; if some tasks are waiting for a
>> time delay, you have to manage timer queues. Of course this is
>> perfectly doable, but it's quite a lot more complicated. I expect
>> that SwiftX handles all this, of course.
>
> So Polyforth is multitasking?

What do you suppose "poly" means? :-)

Andrew.

rickman

unread,
Sep 5, 2014, 2:10:57 PM9/5/14
to
On 9/5/2014 2:00 PM, Paul Rubin wrote:
> rickman <gnu...@gmail.com> writes:
>> So Polyforth is multitasking? That seems very odd on a multiprocessor
>> to use one processor (or a small handfull) to run multiple tasks.
>
> No it's very normal to handle asynchronous events that way. In the PC
> world you might have an 8-core cpu, that has 100's of network
> connections open. For embedded, you might have timer tasks, i/o tasks,
> the scheduler itself, etc.
>
>> Even so, the only reason to spin rather the halting is because there
>> is no clock to use to measure time so the CPU has to burn clock cycles
>> to measure time.
>
> Andrew explained it would complicate the software to not spin. Suppose
> you have an external clock capable of waking the cpu at a time you
> specify. You have N sleeping tasks that are scheduled to wake up at
> given times T1,T2,...TN. The "right" way to do that is figure out which
> of those times is the earliest, then suspend the CPU until that time;
> then wake up, handle the task, then sleep til the next wakeup is due,
> etc. Ideally you should use a priority queue data structure to quickly
> identify the next task, instead of scanning through the whole (maybe
> large) task list to find the next pending wakeup. This is what a
> serious OS does, but it implies some implementation complexity.

You can make it as complex as you wish.


> The traditional Forth approach is to do the simplest thing possible. In
> Polyforth's case, that might be to run all the tasks in round-robin
> fashion non-stop. Each task gets a timeslice where it checks whether
> it's time to wake up, and goes back to sleep if it's not time yet. That
> burns some extra CPU cycles but is very simple to code.

And why can't you do that without wasting time in a spin loop? Again, a
timer "interrupt" would make all this possible without wasting CPU
cycles (and power) in a spin loop. When you say each task "gets a
timeslice" you mean the loop runs checking to see if a task is ready to
run or if it is still waiting for some event. Or are you saying this
checking is done in the task and it then returns to the OS when no ready?

Why would a task have to be waiting for a specific time? Wouldn't it be
waiting for some event to happen? Only one of those possible events is
a timer expiration.

I suppose it all comes down to the fact that they didn't want to put
effort into converting the code to something that would run efficiently
on the GA144 regardless of our speculation on just what the details are.

--

Rick

Elizabeth D. Rather

unread,
Sep 5, 2014, 2:12:46 PM9/5/14
to
On 9/5/14 8:00 AM, Paul Rubin wrote:
...
> The traditional Forth approach is to do the simplest thing possible. In
> Polyforth's case, that might be to run all the tasks in round-robin
> fashion non-stop. Each task gets a timeslice where it checks whether
> it's time to wake up, and goes back to sleep if it's not time yet. That
> burns some extra CPU cycles but is very simple to code.


It is, in fact, even simpler than that. Each task is represented by a
"status" cell, which contains either a jump to the next task in the
round-robin or a jump to wake-up code for this task. So "checking
whether it's time to wake up" takes exactly one instruction, and nobody
has to wake up to do it.

Ideally, tasks are asleep waiting for an interrupt to wake them up, by
changing the status cell appropriately. If a task has to poll something,
of course, it has to wake up, check whatever it's checking, and go back
to sleep, but we try to avoid that.

So continuous running in the round-robin is normal for polyFORTH. As I
noted elsewhere, SwiftX's multitasker is similar, but on
microcontrollers with sleep modes it will sleep when no task is active.

Coos Haak

unread,
Sep 5, 2014, 2:32:26 PM9/5/14
to
Op Thu, 4 Sep 2014 22:16:12 -0700 (PDT) schreef francoi...@wanadoo.fr:
As you seem not to care about side effects on BASE, why not simply:
: H.## 10h BASE ! 0 <# # # #> TYPE SPACE ;

I would like this:
: H.## BASE @ SWAP 10h BASE ! 0 <# # # #> TYPE SPACE BASE ! ;
That way my BASE is not suddenly changed to a value I would not expect.

--
Coos

CHForth, 16 bit DOS applications
http://home.hccnet.nl/j.j.haak/forth.html

Paul Rubin

unread,
Sep 5, 2014, 3:30:35 PM9/5/14
to
rickman <gnu...@gmail.com> writes:
> And why can't you do that without wasting time in a spin loop? Again,
> a timer "interrupt" would make all this possible without wasting CPU
> cycles (and power) in a spin loop.

If you want to control the timeouts with microsecond resolution, you
either need a 1 mhz interrupt (almost as bad as a spin loop) or you need
relatively complicated scheduling.

> Why would a task have to be waiting for a specific time? Wouldn't it
> be waiting for some event to happen?

Imagine something like bit-banged serial i/o, so at 9600 bps on the
receive side, you get an interrupt on the leading edge of a character
start bit, and then you want to sleep for 156 microseconds (to be in the
middle of the next bit cell), check the status of the i/o pin, and then
check again every 104 microseconds for the next 7 bits. You want to do
this on several ports at the same time and they are not synchronized
with each other, so you can't just have a 9600 hz master clock where you
check all the ports on each tick.

I actually don't know if this approach was ever feasible with Polyforth
even at 1200 bps, but at least it's a conceptual example.

> I suppose it all comes down to the fact that they didn't want to put
> effort into converting the code to something that would run
> efficiently on the GA144 regardless of our speculation on just what
> the details are.

Well, Elizabeth has just explained post how Polyforth multitasking
actually works. I haven't looked at Polyforth but I remember now
that eForth does something similar.

Elizabeth D. Rather

unread,
Sep 5, 2014, 3:47:48 PM9/5/14
to
On 9/5/14 9:30 AM, Paul Rubin wrote:
> rickman <gnu...@gmail.com> writes:
>> And why can't you do that without wasting time in a spin loop? Again,
>> a timer "interrupt" would make all this possible without wasting CPU
>> cycles (and power) in a spin loop.
>
> If you want to control the timeouts with microsecond resolution, you
> either need a 1 mhz interrupt (almost as bad as a spin loop) or you need
> relatively complicated scheduling.
>
>> Why would a task have to be waiting for a specific time? Wouldn't it
>> be waiting for some event to happen?
>
> Imagine something like bit-banged serial i/o, so at 9600 bps on the
> receive side, you get an interrupt on the leading edge of a character
> start bit, and then you want to sleep for 156 microseconds (to be in the
> middle of the next bit cell), check the status of the i/o pin, and then
> check again every 104 microseconds for the next 7 bits. You want to do
> this on several ports at the same time and they are not synchronized
> with each other, so you can't just have a 9600 hz master clock where you
> check all the ports on each tick.
>
> I actually don't know if this approach was ever feasible with Polyforth
> even at 1200 bps, but at least it's a conceptual example.

polyFORTH has done some very fast I/O, the fastest being various custom
high-speed data acquisition devices. Even in the 70's, pF's predecessor
could do 12-bit A/D sampling at kHz rates with concurrent scaling while
supporting multiple terminals and other devices.

rickman

unread,
Sep 5, 2014, 8:10:40 PM9/5/14
to
On 9/5/2014 3:30 PM, Paul Rubin wrote:
> rickman <gnu...@gmail.com> writes:
>> And why can't you do that without wasting time in a spin loop? Again,
>> a timer "interrupt" would make all this possible without wasting CPU
>> cycles (and power) in a spin loop.
>
> If you want to control the timeouts with microsecond resolution, you
> either need a 1 mhz interrupt (almost as bad as a spin loop) or you need
> relatively complicated scheduling.

No, you don't need a 1 MHz (not 1 milliHertz for sure) interrupt. You
need a timer with 1 us resolution. Is the math that complicated, even
"relatively"?

0 timer variable
\ Find the next timeout setting from list of N times
\ If timeout changes reset interrupt timer
: Interrupt_Timer_Val_Get ( n -- )
timer SWAP 0 DO I runtime NOW - timer min to timer LOOP
timer - IF timer ( code to set interrupt timer ) THEN ;

Is that really complex? runtime is an array that works like a variable
returning the value after using the index to get the address. NOW gets
the current time in the appropriate units.

Actually, you have to do all this even if you are running a spin loop.


>> Why would a task have to be waiting for a specific time? Wouldn't it
>> be waiting for some event to happen?
>
> Imagine something like bit-banged serial i/o, so at 9600 bps on the
> receive side, you get an interrupt on the leading edge of a character
> start bit, and then you want to sleep for 156 microseconds (to be in the
> middle of the next bit cell), check the status of the i/o pin, and then
> check again every 104 microseconds for the next 7 bits. You want to do
> this on several ports at the same time and they are not synchronized
> with each other, so you can't just have a 9600 hz master clock where you
> check all the ports on each tick.

Ok, so sleep for 156 us, then sleep for 104 us. None of this requires 1
MHz interrupts.


> I actually don't know if this approach was ever feasible with Polyforth
> even at 1200 bps, but at least it's a conceptual example.
>
>> I suppose it all comes down to the fact that they didn't want to put
>> effort into converting the code to something that would run
>> efficiently on the GA144 regardless of our speculation on just what
>> the details are.
>
> Well, Elizabeth has just explained post how Polyforth multitasking
> actually works. I haven't looked at Polyforth but I remember now
> that eForth does something similar.

She also said, "Yes, that's exactly how SwiftX works. On systems with a
'sleep' mode, it automatically sleeps when no tasks are active."

So why can't Polyforth do the same on the GA144? Some piece of code has
to set the "status cells". This piece of code can keep track of how
many status cells are set to "run". When that number is zero, the CPU
can stop. The only question is what are the tasks waiting for? The CPU
needs to have a signal from something to get it going again and adjust
the status cells to the "run" state.

--

Rick

Elizabeth D. Rather

unread,
Sep 5, 2014, 8:35:38 PM9/5/14
to
On 9/5/14 2:10 PM, rickman wrote:
> On 9/5/2014 3:30 PM, Paul Rubin wrote:
>> rickman <gnu...@gmail.com> writes:
...
>>> I suppose it all comes down to the fact that they didn't want to put
>>> effort into converting the code to something that would run
>>> efficiently on the GA144 regardless of our speculation on just what
>>> the details are.
>>
>> Well, Elizabeth has just explained post how Polyforth multitasking
>> actually works. I haven't looked at Polyforth but I remember now
>> that eForth does something similar.
>
> She also said, "Yes, that's exactly how SwiftX works. On systems with a
> 'sleep' mode, it automatically sleeps when no tasks are active."
>
> So why can't Polyforth do the same on the GA144? Some piece of code has
> to set the "status cells". This piece of code can keep track of how
> many status cells are set to "run". When that number is zero, the CPU
> can stop. The only question is what are the tasks waiting for? The CPU
> needs to have a signal from something to get it going again and adjust
> the status cells to the "run" state.
>

There's not "a piece of code" that sets status cells except what each
task does in the course of waking up and going back to sleep (one
instruction at each end). I'm not actually sure how it knows it got
around the loop once with no one active, but it's probably a counter or
something else simple. As for what they're waiting for, it obviously
depends on what the task's job is. If it's monitoring a device, it could
be waiting for an interrupt. Or a task could be waiting for another task
(that was awakened by an interrupt) to ask it to do something.

In any case, I agree that if GA is just looking at polyFORTH as the host
for a development environment, it isn't worth it to try to optimize it
for power management.

Paul Rubin

unread,
Sep 6, 2014, 3:48:55 AM9/6/14
to
rickman <gnu...@gmail.com> writes:
> Is that really complex? runtime is an array that works like a
> variable returning the value after using the index to get the address.

We're talking about Polyforth, which used 3+1 symbol names and a linear
linked list as a dictionary. It went for the absolutely minimal
approach to everything. A linear scan like what you posted is no slower
than looping through the tasks, but more fancier implementations use
more complicated approaches for higher efficiency.

Actually Elizabeth's explanation didn't mention how the issue of how
Polyforth tracked pending timeouts to wake up the affected tasks. The
max # of tasks must have been pretty small compared to what we deal with
now, but on the other hand, CPU's were slow back then.

Elizabeth D. Rather

unread,
Sep 6, 2014, 4:12:51 AM9/6/14
to
LOL, I remember a couple of projects that involved >100 tasks on an 8051
in the early 80's. I know of one customer who supported >1,000 on a PC
throughout the 80's.

The easy way to manage, say, an <n> MS timeout is this: The task stayed
awake. Each time around the loop, it read the millisecond circular timer
(incremented by the clock interrupt), and compared it against the
reading when it set the timer. When it passed, the task moved on with
its business.

If we really needed minimum overhead, it was done in the clock interrupt
(all code). When the timer value was exceeded, it waked the task.

Andrew Haley

unread,
Sep 6, 2014, 4:14:04 AM9/6/14
to
Paul Rubin <no.e...@nospam.invalid> wrote:
> rickman <gnu...@gmail.com> writes:
>> Is that really complex? runtime is an array that works like a
>> variable returning the value after using the index to get the address.
>
> We're talking about Polyforth, which used 3+1 symbol names and a linear
> linked list as a dictionary.

I suspect that this is polyFORTH II. The dictionary was hashed, and
it had variable-length names.

> It went for the absolutely minimal approach to everything. A linear
> scan like what you posted is no slower than looping through the
> tasks

It is: the "looping" is only one instruction per task.

> but more fancier implementations use more complicated approaches for
> higher efficiency.

No, lower efficiency IME. The fantastic thing about polyFORTH was how
well it could scale even on low-powered machines, and how good its
response time could be. In theory, going without timer queues and
polling each one is slow; in practice, unless you have a lot of tasks
all waiting on timers, it isn't.

> Actually Elizabeth's explanation didn't mention how the issue of how
> Polyforth tracked pending timeouts to wake up the affected tasks.

begin pause dup counter - 0< until

Conventional OSs would go to heroic lengths to avoid this "busy
waiting".

Andrew.

Andrew Haley

unread,
Sep 6, 2014, 4:31:36 AM9/6/14
to
Elizabeth D. Rather <era...@forth.com> wrote:
>
> If we really needed minimum overhead, it was done in the clock interrupt
> (all code). When the timer value was exceeded, it waked the task.

It's worth describing this a bit more. You do very little in
interrupt routines, in order to minimize the time when other
interrupts are blocked, but if you can do something quickly you do.
Stopping a task, and waking a task, is a very cheap operation.
Compare and contrast with a UNIXish operating system today, where
blocking and restarting a thread can take many hundreds of cycles.

Andrew.

rickman

unread,
Sep 6, 2014, 4:56:43 AM9/6/14
to
On 9/6/2014 3:48 AM, Paul Rubin wrote:
> rickman <gnu...@gmail.com> writes:
>> Is that really complex? runtime is an array that works like a
>> variable returning the value after using the index to get the address.
>
> We're talking about Polyforth, which used 3+1 symbol names and a linear
> linked list as a dictionary. It went for the absolutely minimal
> approach to everything. A linear scan like what you posted is no slower
> than looping through the tasks, but more fancier implementations use
> more complicated approaches for higher efficiency.

I don't want to get into a protracted debate with you over this,
especially since you don't seem to understand what is being discussed.
The code I provided was not a process you needed to run over and over.
That is the whole point, when there are *no* tasks that need to run now
but rather each task is waiting for a time based execution, this will
let you use a hardware based timer to schedule the *next* task and let
the processor sleep. Who cares how fast a processor sleeps?


> Actually Elizabeth's explanation didn't mention how the issue of how
> Polyforth tracked pending timeouts to wake up the affected tasks. The
> max # of tasks must have been pretty small compared to what we deal with
> now, but on the other hand, CPU's were slow back then.

Yup, she didn't say. The point is not how Polyforth did it on some
processor a long time ago, but rather how it could be done in the GA144.
More importantly, I was responding to a specific statement you made
(shown here) about the "complexity" of the code to do this.

> If you want to control the timeouts with microsecond resolution, you
> either need a 1 mhz interrupt (almost as bad as a spin loop) or you need
> relatively complicated scheduling.

So a 1 MHz interrupt is not needed and the code to do this is not complex.

In the real world tasks are usually pending some other operation, like
I/O for example. In the GA144 there would be an event associated with
these operations and any processor not involved in those operations
would be suspended until awakened by the event.

As others have pointed out there was no need to spend any effort on
saving this smallish amount of power in a development system. But I
think it is just not so terribly difficult to do so.

--

Rick

Paul Rubin

unread,
Sep 6, 2014, 5:10:07 AM9/6/14
to
Andrew Haley <andr...@littlepinkcloud.invalid> writes:
> I suspect that this is polyFORTH II. The dictionary was hashed, and
> it had variable-length names.

Hmm, ok, I didn't know about that.

> In theory, going without timer queues and polling each one is slow; in
> practice, unless you have a lot of tasks all waiting on timers, it
> isn't.

I know that GHC uses a heap structure to manage timeouts and that this
got a considerable performance boost over the older linear scan. Linux
purportedly has an O(1) scheduler but I don't know how it works. I'm
not sure what Erlang does. As you say, these are all intended for lots
of tasks. For highly concurrent network servers you might have 100K's
of tasks waiting on timers. This wasn't an issue in the Polyforth era
though.

> begin pause dup counter - 0< until
> Conventional OSs would go to heroic lengths to avoid this "busy
> waiting".

Do you mean each task is awake, running that loop? That's sort of what
I had guessed in the first place. But it means you can't pause the cpu
if there's nothing to do. For that, you have to be able to compute when
the next wakeup is due.

Elizabeth D. Rather

unread,
Sep 6, 2014, 2:07:51 PM9/6/14
to
No. It's really very rare that a task has to stop and wait on a timer.
Most of the things tasks wait for are I/O events (keyboard, disk, ... ),
and they do that asleep. Most tasks are asleep most of the time.

Jason Damisch

unread,
Sep 6, 2014, 4:08:42 PM9/6/14
to

> and we made microfiche of listings.

Which is why I won't ask you to post all of the microfiche of listings onto your website at Forth.com

But then again, somebody may want to see it!

:^/

( the older I get the more I realize the less I know )

Andrew Haley

unread,
Sep 6, 2014, 4:22:23 PM9/6/14
to
Paul Rubin <no.e...@nospam.invalid> wrote:
> Andrew Haley <andr...@littlepinkcloud.invalid> writes:
>
>> In theory, going without timer queues and polling each one is slow; in
>> practice, unless you have a lot of tasks all waiting on timers, it
>> isn't.
>
> I know that GHC uses a heap structure to manage timeouts and that
> this got a considerable performance boost over the older linear
> scan. Linux purportedly has an O(1) scheduler but I don't know how
> it works. I'm not sure what Erlang does. As you say, these are all
> intended for lots of tasks. For highly concurrent network servers
> you might have 100K's of tasks waiting on timers.

Umm, why are they waiting on timers?

> This wasn't an issue in the Polyforth era though.
>
>> begin pause dup counter - 0< until
>> Conventional OSs would go to heroic lengths to avoid this "busy
>> waiting".
>
> Do you mean each task is awake, running that loop?

No. Most tasks are stopped most of the time; this loop only applies
to tasks waiting for a timer. If you only have a small number of
tasks waiting for timers polling will be faster than messing with
priority queues. If if there are thousands of tasks all waiting for
timers you'll do something more elaborate.

O(N) versus O(1) doesn't tell you anything useful if N is small.

Andrew.

Paul Rubin

unread,
Sep 6, 2014, 4:55:04 PM9/6/14
to
Andrew Haley <andr...@littlepinkcloud.invalid> writes:
>> For highly concurrent network servers you might have 100K's of tasks
>> waiting on timers.
> Umm, why are they waiting on timers?

TCP timeouts comes to mind. Something else might wake them up first of
course.

> O(N) versus O(1) doesn't tell you anything useful if N is small.

O(1) became important once N got above a few hundred or maybe a few
thousand.

Rod Pemberton

unread,
Sep 6, 2014, 5:11:32 PM9/6/14
to
On Sat, 06 Sep 2014 04:12:49 -0400, Elizabeth D. Rather
<era...@forth.com> wrote:
> On 9/5/14 9:48 PM, Paul Rubin wrote:
>> rickman <gnu...@gmail.com> writes:

> LOL, I remember a couple of projects that involved >100 tasks on an 8051
> in the early 80's. I know of one customer who supported >1,000 on a PC
> throughout the 80's.

LOL, there's nothing like a computer task thrashing, by executing one or
two or a few instructions then switching to another task. With 1000 tasks
on an underpowered PC, not much would get done.

I remember the ability to handle many tasks as being a selling point
of Forth in BYTE magazine advertisements in the 80's.


Rod Pemberton
--
In a world full of paranoid and violent people, everyone will be tracked.
It means that no one is free, and everyone will be held accountable, for
minor transgressions too. The entire world becomes a prison without bars.

Andrew Haley

unread,
Sep 6, 2014, 6:12:50 PM9/6/14
to
Paul Rubin <no.e...@nospam.invalid> wrote:
> Andrew Haley <andr...@littlepinkcloud.invalid> writes:
>>> For highly concurrent network servers you might have 100K's of tasks
>>> waiting on timers.
>> Umm, why are they waiting on timers?
>
> TCP timeouts comes to mind. Something else might wake them up first of
> course.

In which case they aren't waiting on a timer, at least not in the
sense I wrote about: the code I provided is for tasks that are only
waiting on a timer, not for anything else. Timeouts waiting for I/O
will want some other mechanism.

But TCP timeouts don't require each task to have its own timer. All
they need is a single task which wakes up every few minutes and wakes
every task which hasn't made progress since last time. (Or maybe
better, on each timer tick an interrupt handler can wake a subset of
the tasks and let the tasks themselves decide if they should time out.
I imagine there are other possibilities.) So, you still don't need
timer queues.

>> O(N) versus O(1) doesn't tell you anything useful if N is small.
>
> O(1) became important once N got above a few hundred or maybe a few
> thousand.

Or maybe a few million. :-)

Andrew.

Elizabeth D. Rather

unread,
Sep 6, 2014, 6:19:18 PM9/6/14
to
On 9/6/14 11:15 AM, Rod Pemberton wrote:
> On Sat, 06 Sep 2014 04:12:49 -0400, Elizabeth D. Rather
> <era...@forth.com> wrote:
>> On 9/5/14 9:48 PM, Paul Rubin wrote:
>>> rickman <gnu...@gmail.com> writes:
>
>> LOL, I remember a couple of projects that involved >100 tasks on an
>> 8051 in the early 80's. I know of one customer who supported >1,000 on
>> a PC throughout the 80's.
>
> LOL, there's nothing like a computer task thrashing, by executing one or
> two or a few instructions then switching to another task. With 1000 tasks
> on an underpowered PC, not much would get done.

That's why we don't do it that way.

> I remember the ability to handle many tasks as being a selling point
> of Forth in BYTE magazine advertisements in the 80's.

Indeed.

humptydumpty

unread,
Sep 7, 2014, 12:42:33 PM9/7/14
to
On Thursday, September 4, 2014 2:07:34 AM UTC+3, Howerd wrote:
> Hi All,
>
>
>
> My attitude to compiling words was formed a long time ago, so I wanted to
>
> see what I think today, and why.
>
>
>
> I took a classic compiling word example from polyForth and tried some other styles.
>
>
>
> I like to align the code vertically because it makes it easier to identify the strings.
>
> The test word tthpAll displays a sample of the words' outputs so that
>
> you can check by eye that they all work.
>
>
>
> Votes please... I go for tthp5
>
>
>
> Best regards,
>
> Howerd
>
>

Hi!

First version kicked me into thinking ...
I abstracted from it maybe a useful concept, string multiplexer:


\ String multiplexer
\ GForth specific words: ]] [[

: R'@ ]] 2R@ drop [[ ; immediate

\ Multiplexes strings from PARAMETERS produced by XT
\ ( all strings are consumed by XT )
\ with strings from ARRAY of counted-strings terminated by null-string
: $muxer ( .. producer$-xt consumer$-xt csa -- ; string-multiplexer )
BEGIN
count ?dup
WHILE
2>R
2>R R'@ execute R@ execute 2R>
dup 2R@ rot execute
2R> +
REPEAT drop 2drop
;

\ Compiling-macro version
' , is compile, \ comment out if not gforth-itc
: comp,
['] noop over =
IF drop
ELSE compile, THEN
;
variable producer variable consumer
: producer: ' producer ! ;
: consumer: ' consumer ! ;
: $muxer,
]] BEGIN count ?dup WHILE 2>R [[
producer @ comp, consumer @ comp,
]] 2R@ [[ consumer @ comp,
]] 2R> + REPEAT drop [[
;

\
\ Example of use:
\
: (.) s>d <# #s #> ;
: ESC ( escape-sequences definer )
CREATE
bl parse string, 0 c,
DOES> ( .. a )
27 emit count 2dup type +
[ producer: (.) consumer: type $muxer, ]
;
: [#] ( -- ; compile modifier )
-1 allot bl parse string, 0 c,
;


'comp,' should not compile anything if has parameter 'noop,
that means it has pairs 'ca u' on stack.


Have a nice day,
humptydumpty

humptydumpty

unread,
Sep 10, 2014, 2:28:47 AM9/10/14
to
On Sunday, September 7, 2014 7:42:33 PM UTC+3, humptydumpty wrote:

Remove completely the following line:
> ' , is compile, \ comment out if not gforth-itc

Here was about producer:
> 'comp,' should not compile anything if has parameter 'noop,
> that means it has pairs 'ca u' on stack.

So, a correct version of string multiplexer is:

\ String multiplexer
\ GForth specific words: ]] [[

: R'@ ]] 2R@ drop [[ ; immediate

\ Multiplexes strings from PARAMETERS produced by XT
\ ( all strings are consumed by XT )
\ with strings from ARRAY of counted-strings terminated by null-string
: $muxer ( .. producer$-xt consumer$-xt csa -- ; string-multiplexer )
BEGIN
count ?dup
WHILE
2>R
2>R R'@ execute R@ execute 2R>
dup 2R@ rot execute
2R> +
REPEAT drop 2drop
;

\ Compiling-macro version
: comp,
['] noop over =
IF drop
ELSE compile, THEN
;
variable producer variable consumer
: producer: ' producer ! ;
: consumer: ' consumer ! ;
: $muxer,
]] BEGIN count ?dup WHILE 2>R [[
producer @ comp, consumer @ comp,

]] 2R@ [[ consumer @ comp,
]] 2R> + REPEAT drop [[
;

\
\ Example of use:
\
: (.) s>d <# #s #> ;
: ESC ( escape-sequences definer )
CREATE
bl parse string, 0 c,
DOES> ( .. a )
27 emit count 2dup type +
[ producer: (.) consumer: type $muxer, ]
;
: [#] ( -- ; compile modifier )
-1 allot bl parse string, 0 c,
;
: MACRO
CREATE
char parse string, 0 c, IMMEDIATE
DOES>
count 2dup 2>R evaluate 2R> +
[ producer: parse-name consumer: evaluate $muxer, ]
;
: +m
-1 allot char parse string, 0 c,
;

MACRO ?? | IF| +m | THEN|
: test ?? exit ." bye" ;
see test

Andrew Haley

unread,
Sep 10, 2014, 3:56:25 AM9/10/14
to
humptydumpty <oua...@gmail.com> wrote:
> On Sunday, September 7, 2014 7:42:33 PM UTC+3, humptydumpty wrote:
>
> Remove completely the following line:
>> ' , is compile, \ comment out if not gforth-itc
>
> Here was about producer:
>> 'comp,' should not compile anything if has parameter 'noop,
>> that means it has pairs 'ca u' on stack.
>
> So, a correct version of string multiplexer is:
>
> \ String multiplexer
> \ GForth specific words: ]] [[
>
> : R'@ ]] 2R@ drop [[ ; immediate
>
> \ Multiplexes strings from PARAMETERS produced by XT
> \ ( all strings are consumed by XT )
> \ with strings from ARRAY of counted-strings terminated by null-string

But what does it actually do? Can you describe it?

Andrew.

humptydumpty

unread,
Sep 10, 2014, 4:23:00 AM9/10/14
to
On Wednesday, September 10, 2014 10:56:25 AM UTC+3, Andrew Haley wrote:
Hi!

The above comment should be considered together with stack-comment of '$muxer'.

In other words, it takes a word produced from parameters on data stack
or by other meanings, feed it to consumer,
take a word from an array of counted-strings feed it to consumer
and so on, till the terminator of counted-strings array(null counted-string).

A solution of the classic problem of separating
what is changing of what is unchanged.

Maybe I named it wrong 'string multiplexer' ...

Andrew Haley

unread,
Sep 10, 2014, 4:34:11 AM9/10/14
to
Hi,

humptydumpty <oua...@gmail.com> wrote:
> On Wednesday, September 10, 2014 10:56:25 AM UTC+3, Andrew Haley wrote:
>> humptydumpty wrote:
>>
>> But what does it actually do? Can you describe it?
>

> The above comment should be considered together with stack-comment
> of '$muxer'.
>
> In other words, it takes a word produced from parameters on data
> stack or by other meanings, feed it to consumer, take a word from an
> array of counted-strings feed it to consumer and so on, till the
> terminator of counted-strings array(null counted-string).
>
> A solution of the classic problem of separating what is changing of
> what is unchanged.
>
> Maybe I named it wrong 'string multiplexer' ...

How is a word "produced from parameters on the data stack"?

I think an easier to undertand example would help.

Andrew.

humptydumpty

unread,
Sep 10, 2014, 4:49:59 AM9/10/14
to
On Wednesday, September 10, 2014 11:34:11 AM UTC+3, Andrew Haley wrote:
> Hi,
Hi!

An example is word '(.)' of stack-effect ( n -- ca u )
used in 'ESC' word:

: (.) 0 s>d <# #s #> ;

It could be every word that returns a valid 'ca u' pair,
whatever parameters take from stack (could be any,
like 'parse-name' used for 'MACRO').
It could be even a word that returns 'ca u' pairs
from another array of strings.

If we have pairs 'ca u'on stack then producer is 'noop',
that simply would not be compiled by '$muxer,' into target-word.

humptydumpty :)

humptydumpty

unread,
Sep 10, 2014, 4:55:25 AM9/10/14
to
On Wednesday, September 10, 2014 11:34:11 AM UTC+3, Andrew Haley wrote:
> Hi,
Oops!

Should be used 'a string produced from parameters...'

humptydumpty (maybe I deserve that nick-name :)

Andrew Haley

unread,
Sep 10, 2014, 6:14:13 AM9/10/14
to
humptydumpty <oua...@gmail.com> wrote:
> On Wednesday, September 10, 2014 11:34:11 AM UTC+3, Andrew Haley wrote:
>> Hi,
>> humptydumpty wrote:
>> > On Wednesday, September 10, 2014 10:56:25 AM UTC+3, Andrew Haley wrote:
>> >> humptydumpty wrote:
>> >> >> But what does it actually do? Can you describe it?
>> >
>> > The above comment should be considered together with stack-comment
>> > of '$muxer'.
>> >
>> > In other words, it takes a word produced from parameters on data
>> > stack or by other meanings, feed it to consumer, take a word from an
>> > array of counted-strings feed it to consumer and so on, till the
>> > terminator of counted-strings array(null counted-string).
>> >
>> > A solution of the classic problem of separating what is changing of
>> > what is unchanged.
>> >
>> > Maybe I named it wrong 'string multiplexer' ...
>> How is a word "produced from parameters on the data stack"?
>> I think an easier to undertand example would help.
>
> Hi!
>
> An example is word '(.)' of stack-effect ( n -- ca u )
> used in 'ESC' word:
>
> : (.) 0 s>d <# #s #> ;
>
> It could be every word that returns a valid 'ca u' pair,
> whatever parameters take from stack (could be any,
> like 'parse-name' used for 'MACRO').
> It could be even a word that returns 'ca u' pairs
> from another array of strings.
>
> If we have pairs 'ca u'on stack then producer is 'noop',
> that simply would not be compiled by '$muxer,' into target-word.

But why would anyone want to do that? That's the part I don't get.
What is this code for?

Andrew.

Paul E Bennett

unread,
Sep 10, 2014, 7:11:47 AM9/10/14
to
I am with Andrew in this sentiment. I also failed to see the central purpose
of this code. It looks like it should be part of a lexical set. However, the
purpose of the whole set of words, and each word individually, has not been
adequately explained preventing the rest of us from following what you are
trying to achieve. This is not a comment on the code itself but on the fact
that the aims and intents for it have not been made clear.

--
********************************************************************
Paul E. Bennett IEng MIET.....<email://Paul_E....@topmail.co.uk>
Forth based HIDECS Consultancy.............<http://www.hidecs.co.uk>
Mob: +44 (0)7811-639972
Tel: +44 (0)1235-510979
Going Forth Safely ..... EBA. www.electric-boat-association.org.uk..
********************************************************************

humptydumpty

unread,
Sep 10, 2014, 8:22:37 AM9/10/14
to
Hi!

It is an exploratory concept&code. I'll try to speak my mind as better
I can.

It started as an alternative to classic string formatter: substitution of
substrings in a template-string(unchanging-part) with variable-strings
(changing part) to obtain a result-string.

In exploring that new way, I observed that I can do string-formatting
in the 'string-multiplexer' way using a consumer word like
'$!+' (ca u a -- a+u) without need of escape-chars or the like.

So I think that new concept is more basic than string-formatting.

If normally I would see string-formatting as an indivisible step,
now I think I can broke that step in 'string-multiplexer' way
whenever the word that consume result-string is concatenative.

If is a better way I'm not convinced. But is something new for me.
Any comments are welcomed!

Thanks everyone,
humptydumpty

Andrew Haley

unread,
Sep 10, 2014, 9:00:13 AM9/10/14
to
humptydumpty <oua...@gmail.com> wrote:
> On Wednesday, September 10, 2014 2:11:47 PM UTC+3, Paul E Bennett wrote:
>>
>> I am with Andrew in this sentiment. I also failed to see the
>> central purpose of this code. It looks like it should be part of a
>> lexical set. However, the purpose of the whole set of words, and
>> each word individually, has not been adequately explained
>> preventing the rest of us from following what you are trying to
>> achieve. This is not a comment on the code itself but on the fact
>> that the aims and intents for it have not been made clear.
>
> It is an exploratory concept&code. I'll try to speak my mind as better
> I can.
>
> It started as an alternative to classic string formatter: substitution of
> substrings in a template-string(unchanging-part) with variable-strings
> (changing part) to obtain a result-string.
>
> In exploring that new way, I observed that I can do string-formatting
> in the 'string-multiplexer' way using a consumer word like
> '$!+' (ca u a -- a+u) without need of escape-chars or the like.
>
> So I think that new concept is more basic than string-formatting.
>
> If normally I would see string-formatting as an indivisible step,
> now I think I can broke that step in 'string-multiplexer' way
> whenever the word that consume result-string is concatenative.

Okay. Please tell us how you would use this to write something like
C's

printf(" 0x%d 0x%d\n", 1, 2);

Which should print " 1 2
"

Then we might understand.

Andrew.

humptydumpty

unread,
Sep 10, 2014, 2:58:44 PM9/10/14
to
On Wednesday, September 10, 2014 4:00:13 PM UTC+3, Andrew Haley wrote:
Hi!

Why did you shift discussion into C-land?
It was about Forth. Ok, I'll bite that.


\ Just to have everything at one place
: comp,
['] noop over =
IF drop
ELSE compile, THEN
;
variable producer variable consumer
: producer: ' producer ! ;
: consumer: ' consumer ! ;
: $muxer,
]] BEGIN count ?dup WHILE 2>R [[
producer @ comp, consumer @ comp,
]] 2R@ [[ consumer @ comp,
]] 2R> + REPEAT drop [[
;

: (.) s>d <# #s #> ;

\ Post-it Fix-up. 'HERE' marks fix-up place
: CR, 10 swap c! ; \ Fix-up with CR
: TAB, 9 swap c! ; \ Fix-up with TAB
: LEN, here over - 1- swap c! ; \ Fix-up string length
: s.. char parse ;

CREATE fmt$
s.. " \\\\\\\\\t 0x0%d" string,
HERE s.. " 0x0%d \\\\\n" string, 10 c,
HERE s.. " \\trest." string, TAB,
LEN,
0 c,

: fmt ( n2 n1 -- )
." alfa 0x0%d" fmt$
[ producer: (.) consumer: type $muxer, ]
;
2 1 fmt
cr .s cr bye

At command line:

$ gforth fmtst.fs
alfa 0x0%d1\\\\\\\\\t 0x0%d20x0%d \\\\\n
\\trest.
<0>
$

Now, please write a 'printf' statement that will print that above.

You see the difference? What a 'printf' should do, to obtain
the same output?

Albert van der Horst

unread,
Sep 10, 2014, 5:53:23 PM9/10/14
to
In article <1a023a6a-5e7d-4f27...@googlegroups.com>,
Oops. I still like my solution better.
This is what it does for Andrew's 0x challenge:
\ ------------ doit.frt --------------- 8< -------------------

WANT HEX: \ HEX: switches to hex for the duration of the definition.
WANT FORMAT

FORMAT-WID DEFINITIONS
: n ^J CRS$ $C+ ;
: % &% CRS$ $C+ ;
: x HEX: S>D <# #S &x HOLD &0 HOLD #> CRS$ $+! ;
WORDS "\ Were the words that may appear after an %" TYPE CR
PREVIOUS DEFINITIONS

1 2 SWAP ( !) " 0x%d 0x%d %n to make %% n clear%n" FORMAT TYPE CR CR

\ The equivalent of "%x %x\n" :


1 2 SWAP "%x %x %n" FORMAT TYPE CR CR

: test 1 2 SWAP "%x %x %n" FORMAT TYPE CR CR ;

test

\ --------------------------- 8< -------------------

lina -a < doit.frt
gives the following output:
"
d s n % x \ Were the words that may appear after an %
0x1 0x2
to make %n clear


0x1 0x2


0x1 0x2



"

Note that having "-recognizer available there is no difference
between the interpreted and compiled code.
Come to think of it. Maybe the space before 0 in " 0x..
was not intended to appear in the output but is an artifact of
old fashioned recognizers.

I added a %n in the middle of a format to show what it does:
a new line.

FORMAT is so simple that it is silly:
repeat until string empty
add part before % to output-string
execute word before blank
leave output-string

With my $@ $! $+! $C+ $/ string handling it's a snap.

>
>Have a nice day,
>humptydumpty

Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

humptydumpty

unread,
Sep 11, 2014, 1:58:29 AM9/11/14
to
On Thursday, September 11, 2014 12:53:23 AM UTC+3, Albert van der Horst wrote:
> In article <1a023a6a-5e7d-4f27...@googlegroups.com>,
>
Hi!

I knew ciforth string-handling suit and I think it is a well-factored suite.

I have done formatting-string words, multiline text-expanding macros,
macros that inside text have macros that extract words from text environment,
what 'FORMAT' do is one of the ways that can be done.

But every version use scanning format-string for 'key-words' or 'key-chars'.

What I posted is something without scanning. It is exploratory code(concept).

humptydumpty

unread,
Sep 11, 2014, 5:15:27 PM9/11/14
to
On Thursday, September 11, 2014 12:53:23 AM UTC+3, Albert van der Horst wrote:
> In article <1a023a6a-5e7d-4f27...@googlegroups.com>,
Hi!

Here a little more polished version of muxer with explanations:

\ String Multiplexer
\
\ Feed a CONSUMER word alternatively
\ with strings from a Template(format-string)
\ and
\ with strings PRODUCED from 0 or more parameters from stack.
\
\ Template is an array of counted-strings terminated by
\ a null-counted-string(char 0), that marks end of multiplexing.
\
\ OBS.: Nothing will stop you to use such a consumer
\ that will leave on stack as parameters a string that
\ will be fed ahead as input for multiplexer.
\
\ One of use is to offers an alternative to printf-like
\ string-formatting that relies on scanning Template for
\ 'key-chars' or 'key-words'.
\

: comp, ( xt -- ; compile, but not a 'noop')
['] noop over =
IF drop
ELSE compile, THEN
;
variable producer variable consumer
: producer: ' producer ! ;
: consumer: ' consumer ! ;
: $muxer, ( -- ; compile a string-multiplexer )
]] count 2dup 2>R [[ consumer @ comp,
]] 2R> + BEGIN count ?dup WHILE 2>R [[
producer @ comp, consumer @ comp,
]] 2R@ [[ consumer @ comp, ]] 2R> + REPEAT drop [[
;

\ Post-it Fix-up Template Editor. 'HERE' marks a fix-up place.
: CR, 10 swap c! ; \ Fix-up with CR
: TAB, 9 swap c! ; \ Fix-up with TAB
: LEN, here over - 1- swap c! ; \ Fix-up string length
: s.. char parse ;

\
\ Examples:
\
CREATE fmt$
s.. " alfa 0x0%d" string,
s.. " \\\\\\\\\t 0x0%d" string,
HERE s.. " 0x0%d \\\\\n" string, 10 c,
HERE s.. " \\trest." string, TAB,
LEN,
0 c,

: (.) s>d <# #s #> ;
: fmt ( n2 n1 -- )
fmt$ [ producer: (.) consumer: type $muxer, ]
;
2 1 fmt


[IFUNDEF] parse-name : parse-name bl word count ; [THEN]

CREATE ??$
s.. " IF" string, s.. " THEN" string,
0 c,

: ?? ( "word" -- ; compile a sequence "IF word THEN" )
??$ [ producer: parse-name consumer: evaluate $muxer, ]
; IMMEDIATE

: ??test ?? EXIT ." end." ;

see ??test

cr .s cr bye



Output is:

$ gforth stringmultiplexer.fs
alfa 0x0%d1\\\\\\\\\t 0x0%d20x0%d \\\\\n
\\trest.
: ??test
IF EXIT
THEN
.\" end." ;
<0>
$

Hmm, source is 20 lines and with word-comments is less than 700 bytes ...
0 new messages