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

bugged code (I hope) about a calendar that identifies the day name of any date

225 views
Skip to first unread message

gobli...@gmail.com

unread,
Jun 8, 2021, 6:35:09 AM6/8/21
to
hello,

my goal was a toy to get day name from a user specified date,

there are no syntax errors currently (code runs on gforth 0.7.3)

code is here https://pastebin.com/gn7hdd06

but I get "random" results even for the same input date

main part of code is based on :(day + ((13*(m-1))/5) + k + k/4 + J/4 + 5J) %7 see code for details about it (in comments)

daynumber to use with a case statement calculated as :



so I may have troubles on stack management ? algorithm (zeller convergence) ? my code ? or elsewhere ?

I hope you can help me to solve this bugged toy

regards

Alexander Wegel

unread,
Jun 8, 2021, 8:00:56 AM6/8/21
to
gobli...@gmail.com <gobli...@gmail.com> wrote:

> so I may have troubles on stack management ?

Yes, there are invalid stack comments, there's superfluous/confusing
juggling.

> algorithm (zeller convergence) ?

Yes. You mis-copied it.

> my code ?

Yes. Lots. Basically it's backwards (BASIC program written in FORTH).

There are memory-corrupting idiomatic errors (with definition and use of
variables).

It's badly maintained (what's with the century??).

> or elsewhere ?

Well - you blame an out-of-range input on the stupidity of the user.
This might hint at your more serious troubles.

> I hope you can help me to solve this bugged toy

Sorry, no time.

gobli...@gmail.com

unread,
Jun 8, 2021, 8:14:58 AM6/8/21
to
Oh I saw where is the major issue I forgotten a "year" instead of "year @" twice
and I know I used to much variables instead of forth managing stack but it was first to understand the algo & to debug my code
next step will be to rewrite it in a more "forth way" :)

thanks.

dxforth

unread,
Jun 8, 2021, 8:51:49 AM6/8/21
to
On 8/06/2021 22:14, gobli...@gmail.com wrote:
>
> Oh I saw where is the major issue I forgotten a "year" instead of "year @" twice

Good that you spotted it. What about these:

mounth 13 !

mounth 14 !

> and I know I used to much variables instead of forth managing stack but it was first to understand the algo & to debug my code
> next step will be to rewrite it in a more "forth way" :)

You made debugging hard with all those BYE. Remove them and use ABORT
in your error handlers.

As for 'forth style' if you keep writing you'll discover for yourself
what is superfluous and clumsy.

HTH

NN

unread,
Jun 8, 2021, 10:54:24 AM6/8/21
to
I think its more useful for the user to know the range
of the expected input before he enters a number than
for the program to tell him afterwards and exit.

eg

day - between 1 and 31
month - between 1 and 12
etc

Jim Peterson

unread,
Jun 8, 2021, 12:43:15 PM6/8/21
to
I'm somewhat new to Forth, myself, but you could consider (at least) the "within" word, such as:

year @ 1 99 within

or, in your case, the inverted sense:

year @ 99 1 within

instead of your original:

year @ dup >r 99 > r> 1 < or

but I would prefer a more "readable"

year @ 1 99 within 0=

Also, I like using "value"s, instead of "variable"s. It avoids issues like what your bug was, in exchange for maybe accidentally doing "value year ... 42 year !", which gforth might catch as an error at least 7 times out of 8.

--Jim

Jim Peterson

unread,
Jun 8, 2021, 2:19:19 PM6/8/21
to
(slight mistake: you'd want to use "100" instead of "99" when using "within")
(also, isn't "0" a valid year? therefore: "year @ 0 100 within 0=" is my final answer)

none albert

unread,
Jun 8, 2021, 3:20:03 PM6/8/21
to
In article <f59a8a29-79bc-4c2c...@googlegroups.com>,
I would start with assuming it is the start of the computer era.
Th jan 1st 1970. So the day number is 4.
Now ask the year. For each 4 years after 1970 add 1461 to the day
number. Take the remainder mod 7.
For each other year add 365 to the day number.
Take the remainder mod 7.
Now ask the month. It it is past januari add 31 to the day number.
Take the remainder mod 7.
Then go on:
"is it januari" question? NOT IF 31 + 7 MOD ELSE
"is it februari" question? NOT IF 30 + 7 MOD ELSE

In this way you can check what you're doing half way.

Groetjes Albert
--
"in our communism country Viet Nam, people are forced to be
alive and in the western country like US, people are free to
die from Covid 19 lol" duc ha
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst

P Falth

unread,
Jun 8, 2021, 3:23:32 PM6/8/21
to
Here is a version done in a "Forth" way. This version breaks up the problem in several words
that can be tested individually. It also uses the forth command line as the interface.
Checks and other interfaces can be build on top of these words.
This is using the simplified formula taken from wikipedia

: fixyear ( M Y -- M' Y') \ jan, feb becomes 13 and 14
over 3 < if swap 12 + swap 1- then ;

: Y>days ( Y' --days )
dup 4 /
over 100 / -
over 400 / + + ;

: M>days ( M' -- days )
1+ 13 * 5 / ;

: date>day# ( D M Y -- n)
fixyear
Y>days
swap M>days + +
7 mod ;

create daytable
s" Saturday Sunday Monday Tuesday WednesdayThursday Friday "
dup allot daytable swap move

: day>name ( day# -- )
9 * daytable + 9 ;

\ usage:
\ 8 6 2021 date>day# day>name type
\ Tuesday ok

BR
Peter Fälth

Hugh Aguilar

unread,
Jun 8, 2021, 7:21:45 PM6/8/21
to
On Tuesday, June 8, 2021 at 3:35:09 AM UTC-7, gobli...@gmail.com wrote:
> hello,
>
> my goal was a toy to get day name from a user specified date,

This is my code in the novice package:
--------------------------------------------------------------------------------------------
\ ******
\ ****** This is the day of the week calculator.
\ ******

align here ," Saturday"
align here ," Friday"
align here ," Thursday"
align here ," Wednesday"
align here ," Tuesday"
align here ," Monday"
align here ," Sunday"

create <days> , , , , , , , \ Sunday=0, Monday=1, ... Saturday=6

: days ( n -- str )
dup 0< over 7 >= or abort" *** DAYS given invalid value ***"
w * <days> + @ ;

\ See TOY.4TH for two more implementations, one of which is my own invention.

\ DATE>N and N>DATE both come from: https://alcor.concordia.ca//~gpkatch/gdate-algorithm.html
\ This is mostly useful when you need add some number of days to a date and get a new date.

: date>n { month day year -- n }
month 9 + 12 mod to month
year month 10 / - to year
year 365 *
year 4 / +
year 100 / -
year 400 / +
month 306 * 5 + 10 / +
day 1 - + ;

: n>dow ( n -- str ) \ N comes from DATE>N
3 + 7 mod days ;

: n>date { n | y mm dd ddd mi -- month day year } \ N comes from DATE>N
n 10000 um* 14780. d+ 3652425 um/mod nip to y
365 y * y 4 / + y 100 / - y 400 / + n swap - to ddd
ddd 0< if
y 1 - to y
365 y * y 4 / + y 100 / - y 400 / + n swap - to ddd then
ddd 100 * 52 + 3060 / to mi
mi 2 + 12 mod 1 + to mm
mi 2 + 12 / y + to y
mi 306 * 5 + 10 / ddd swap - 1 + to dd
mm dd y ;

: valid-date? ( month day year -- flag )
3dup date>n n>date \ -- m1 d1 y1 m2 d2 y2
fourth = >r \ -- m1 d1 y1 m2 d2
fourth = >r \ -- m1 d1 y1 m2
fourth = >r \ -- m1 d1 y1
3drop
r> r> and r> and ;

: month+ ( month n -- relative-year new-month ) \ MONTH and NEW-MONTH are in the range [1,12]
+ 1- s>d \ the 1- is because we need months in the range [0,11]
12 fm/mod
swap 1+ ;

\ MONTH+ was inspired by Paul Rubin in this thread: https://groups.google.com/forum/#!topic/comp.lang.forth/i_F7GtPxjE4
--------------------------------------------------------------------------------------------


Jim Peterson

unread,
Jun 8, 2021, 7:46:54 PM6/8/21
to
On Tuesday, June 8, 2021 at 7:21:45 PM UTC-4, Hugh Aguilar wrote:
> On Tuesday, June 8, 2021 at 3:35:09 AM UTC-7, gobli...@gmail.com wrote:
> > hello,
> >
> > my goal was a toy to get day name from a user specified date,

I feel as though his original question/goal was not so much "What's a good algorithm for converting date to day of week?", but rather "Given this algorithm, what is a good way to implement it in Forth?", as though the real interest is to learn Forth... not a day-of-week algorithm.

That being said, looking at the first two functions, I find myself wondering why input$ is defined to take a number of characters as input, rather than maybe saying (using underscores for indention in an NNTP post):

_ : input$ ( -- addr n )
_ _ pad dup 16 accept
_ ;

What's more, the input# function should probably just check that the entered value was a number, and abort if not:

_ \ check input type
_ : input# ( -- u )
_ _ 0. input$ dup >r
_ _ >number nip nip
_ _ r> = abort" Must be a number"
_ ;

You can even create an input function that checks the range and aborts if the range is exceeded:

_ \ read number between a and b
_ : input_range ( a b -- n )
_ _ 2>r input# dup
_ _ 2r@ 1+ within if
_ _ _ 2r> 2drop exit
_ _ then
_ _ 2r> swap ." Must be between " . ." and " . cr
_ _ abort
_ ;

Given the input_range function, you could then get year month and day rather easily:

_ \ get all year mounth and day to check
_ : readyear
_ _ cr ." Year ? " 1753 9999 input_range
_ ;
_
_ : readday
_ _ cr ." Day ? " 1 31 input_range
_ ;
_
_ : readmonth
_ _ cr ." Month ? " 1 12 input_range
_ ;

I notice he implements readcentury, but never uses century anywhere. I'm surprised the algorithm works in this case, except that a century of 20 means jj/4 + 5*jj = 105, which is exactly divisible by 7. The year 1999 doesn't work (also, months 1 and 2 don't work either because "mounth 13 !" should be "13 mounth !", etc.)

Of course, then we come to implementing the actual algorithm. I understand that he said he would use variables less in his next revision, but I saw doing so as a personal challenge, and attempted to implement the algorithm myself, as well. I came up with this (extensive commenting):

_ \ Convert yyyy/mm/dd into day of week. Year must be greater than 1752.
_ \ Day of week (dow) is one of:
_ \
_ \ 0 - Sunday
_ \ 1 - Monday
_ \ 2 - Tuesday
_ \ 3 - Wednesday
_ \ 4 - Thursday
_ \ 5 - Friday
_ \ 6 - Saturday
_ \
_ \ Month (mm) and day of month (dd) are 1-based, with 1=January,
_ \ 2=February, ... 12=December.
_ : date>dow ( yyyy mm dd -- dow )
_ _ rot rot
_ _
_ _ ( dd yyyy mm )
_ _ \ ll = mm < 3
_ _ dup 3 <
_ _
_ _ ( dd yyyy mm ll )
_ _ \ Adjust year and month for January and February:
_ _ \ m2 = ll ? mm + 12 : mm
_ _ \ y2 = ll ? y2-1 : y2
_ _ if
_ _ _ 12 + swap 1- swap
_ _ then
_ _
_ _ ( dd y2 m2 )
_ _ \ e = (13*(m2+1))/5
_ _ 1+ 13 * 5 /

_ _ ( dd y2 e )
_ _ \ kk = y2 % 100
_ _ \ jj = y2 / 100
_ _ swap
_ _ 100 /mod
_ _
_ _ ( dd e kk jj )
_ _ \ a = jj/4 + 5*jj
_ _ dup 4 / swap 5 * +
_ _
_ _ ( dd e kk a )
_ _ \ b = kk + a + kk/4
_ _ over 4 / + +
_ _
_ _ ( dd e b )
_ _ \ dow = (dd + e + b + 6) % 7
_ _ + + 6 + 7 mod
_ ;

I like to have this as a stand-alone function, rather than OP's all-in-one find-the-day-and-print-it-out technique. I feel like doing so allows for the function to live on for other uses in later coding adventures.

When using the year as a 4-digit (or more?!) year, the values for jj and kk from the algorithm could be acquired using the "/mod" operator. Also, I adjusted the day-of-week numbering so that 0 was Sunday by doing "6 +" right before the "7 mod" operation.

Gforth reports "see date>dow" as:

_ see date>dow
_ : date>dow_
_ _ rot rot dup 3 <
_ _ IF_ _ 12 + swap 1- swap
_ _ THEN
_ _ 1+ 13 * 5 / swap 100 /mod dup 4 / swap 5 * + over 4 / + + + + 6 + 7 mod ; ok

It uses 38 words to get a result. Maybe that's a measure of performance? Who knows?

I like P Falth's day>name function. I just need to adjust it since I made day 0=Sunday.

One final observation: the "case ... endcase" structure in OP's original code has a lot of repetition. The "cr"s and "bye"s could be removed to either side of the case, and arguably the "bye"s shouldn't even be in this function.

--Jim

Hugh Aguilar

unread,
Jun 8, 2021, 8:22:18 PM6/8/21
to
On Tuesday, June 8, 2021 at 4:46:54 PM UTC-7, elk...@gmail.com wrote:
> On Tuesday, June 8, 2021 at 7:21:45 PM UTC-4, Hugh Aguilar wrote:
> > On Tuesday, June 8, 2021 at 3:35:09 AM UTC-7, gobli...@gmail.com wrote:
> > > hello,
> > >
> > > my goal was a toy to get day name from a user specified date,
> I feel as though his original question/goal was not so much
> "What's a good algorithm for converting date to day of week?", but rather
> "Given this algorithm, what is a good way to implement it in Forth?",
> as though the real interest is to learn Forth... not a day-of-week algorithm.

A day-of-week program isn't a good way to learn Forth. Any algorithm for it is non-intuitive.
The program is full of arithmetic operations that don't really make any sense without
understanding the algorithm, which requires a lot of effort unrelated to Forth programming.

In my novice package I have these easy programs:
* parallel-resistance calculator
* fizz-buzz (classic job-interview question)
These are slightly harder:
* Towers of Hanoi
* currency change-making (actually is useful)
This is slightly harder:
* Nqueens
I have some others too --- I don't remember everything that I've written off the top of my head.

I would recommend that the OP work through these from easy to hard. All of these
have the advantage that the algorithm is intuitive and doesn't involve a lot of
arithmetic that appears to be arbitrary if you don't understand the algorithm.

I applaud the OP for actually writing code, even if it has bugs and is badly designed.
Writing programs is the only way to learn programming. This is what I tried to tell Gavino
and Elizabeth Rather, but to no avail. Implementing a linked-list data-structure is also a
good way to learn Forth, and it provides a useful means to work with data in myriad programs.
Peter Knaggs would do well to try implementing a linked -list --- he might learn some Forth.

If the OP writes the above listed programs, I can critique his code.
I can't critique his day-of-week program too much without first figuring out the algorithm.
He is doing some things wrong, such as using BYE in the program and not factoring
his functions adequately. Learning to factor functions into sub-functions that are useful
in their own right and/or can be tested easily, is a big step toward learning Forth.

dxforth

unread,
Jun 8, 2021, 9:29:24 PM6/8/21
to
On 9/06/2021 04:19, Jim Peterson wrote:
>>
>> year @ 1 99 within
>>
>> or, in your case, the inverted sense:
>>
>> year @ 99 1 within
>>
>> instead of your original:
>>
>> year @ dup >r 99 > r> 1 < or
>>
>> but I would prefer a more "readable"
>>
>> year @ 1 99 within 0=
>
> (slight mistake: you'd want to use "100" instead of "99" when using "within")
> (also, isn't "0" a valid year? therefore: "year @ 0 100 within 0=" is my final answer)
>

It wasn't unusual for forths to have several 'within' words e.g. F-PC
had WITHIN (not ANS), BETWEEN, UBETWEEN.

ANS' decision has led to definitions such as:

: BETWEEN 1+ WITHIN ;

But it turns out there's another way:

http://dxforth.mirrors.minimaltype.com/between.html

For convenience I include both WITHIN and BETWEEN in my forth kernel.

gobli...@gmail.com

unread,
Jun 9, 2021, 4:50:41 AM6/9/21
to
thanks :)

Jim Peterson

unread,
Jun 9, 2021, 11:55:54 AM6/9/21
to
On Tuesday, June 8, 2021 at 8:22:18 PM UTC-4, Hugh Aguilar wrote:
> > I feel as though his original question/goal was not so much
> > "What's a good algorithm for converting date to day of week?", but rather
> > "Given this algorithm, what is a good way to implement it in Forth?",
> > as though the real interest is to learn Forth... not a day-of-week algorithm.
> A day-of-week program isn't a good way to learn Forth. Any algorithm for it is non-intuitive.
> The program is full of arithmetic operations that don't really make any sense without
> understanding the algorithm, which requires a lot of effort unrelated to Forth programming.

I tend to disagree on this point. Zeller's convergence algorithm is sufficiently complex but not overly so, having a good mix of arithmetic operations (+,/,mod) to try as well as a bit of if/then flow control. Implementing it is a good intro to Forth's stack-based, RPN computations. I suspect OP was simply given the algorithm (honestly, it feels like a homework assignment) without the need to understand how it works, and OP's implementation is far more readable than my "date>dow", that relies solely on stack manipulations (maybe using locals would have been a good compromise).

Besides the algorithm, OP's code had plenty more programming aspects than just the simple formula/algorithm that some other posts have presented. There is user input and output (including fancy screen formatting), range checking, variable usage (which was arguably overdone), and a case/endcase structure (though P Falth's "day>name" is likely preferred). Being new to Forth, examining/debugging OP's code exposed me to aspects and words I had only read about previously ("page", "pad", "accept", ">number"), so I have definitely learned more Forth by association.

From Hugh Aguilar (you), I've learned about the ," word, which is not in the standard. I'm saddened to see that gforth's definition has "align" at the end of S,:

see ,"
: ,"
34 parse S, ; ok
see S,
: S,
here over char+ allot place align ; ok
see place
: place
over >r rot over 1+ r> move c! ; ok

It feels out of place, for a character-based word, and opposes the ability to pack multiple counted strings back-to-back:

create daytable
," Sunday"
," Monday"
," Tuesday"
," Thursday"
," Friday"
," Saturday"

: day>name ( dow -- c-addr u )
daytable swap
0 ?do
count chars + \ <= doesn't work... needs "count chars + align", instead... WHY!!!?
loop
;

It would be really nice and useful to have ," in the standard, though I would lobby for a version that doesn't call "align". Also, I think the word "place" would be better standardized as something that operates the opposite of "pick", rather than placing a counted string in memory.

--Jim

P Falth

unread,
Jun 9, 2021, 12:55:45 PM6/9/21
to
Either you are missing one day or you have shorter weeks where you live!

> : day>name ( dow -- c-addr u )
> daytable swap
> 0 ?do
> count chars + \ <= doesn't work... needs "count chars + align", instead... WHY!!!?
> loop
> ;

Align in S, will align the dataspace pointer (HERE) on a cell boundary allocating more space for the string so the next one start on an aligned address.
In your day>name function you need to take care of this extra space as you are adding up the lenghts to get to the right string. For this you
need ALIGNED after count +.

My version avoids this by making all strings 9 bytes wide. I can immediately calculate the start address of the string.
Unfortunately if you read CLF in Google Groups, Google will remove extra spaces so my string if copied into a forth system
from Google will be wrong.

BR
Peter

NN

unread,
Jun 9, 2021, 1:28:51 PM6/9/21
to
Ignoring the calculation part which I think you have worked out ;
the first part which is getting the user input was of interest to me as
it comes up often, so I took a stab at writing something


\ ----------------

variable day
variable month
variable year

: getinput ( -- )
pad 1+ 20 accept pad c! ;

: conv ( -- )
0. pad count >number
dup 0= if 2drop
else abort then ;

: small ( -- )
dup 0= if d>s
else abort then ;

: input# ( -- )
getinput conv small ;

: input$ ( -- n )
['] input# catch if
cr ." Was expecting a number got something else" abort then ;

: between ( n1 n2 n3 -- ) 1+ within ;

: readday ( -- )
cr ." Enter day ( 1 -- 31 ) : "
input$ dup day ! 1 31 between 0= abort" Outside range" ;

: readmonth ( -- )
cr ." Enter month ( 1 -- 12 ). : "
input$ dup month ! 1 12 between 0= abort" Outside range" ;

: readyear ( -- )
cr ." Enter year ( 1752 -- 9999 ) : "
input$ dup year ! 1752 9999 between 0= abort" Outside range" ;

: read-dmy ( -- )
cr ." Insert a decomposed date : " cr
cr readday
cr readmonth
cr readyear
cr
;

: leap-year? ( y -- f )
dup 400 mod 0= swap 4 mod 0= and ;


: validdate1 ( -- f )
\ 2 sep 1752
year @ 1752 < if false else
year @ 1752 = if
month @ 9 >= day @ 2 >= and
else true then
then ;

: validdate2 ( -- f )
\ jan mar may jul aug oct dec
\ 31 days so no check

\ apr jun sep nov
month @
dup 4 =
over 6 = or
over 9 = or
swap 11 = or if
day @ 30 <=
else true then ;

: validdate3 ( -- f )
month @ 2 = if
year @ leap-year? if
day @ 29 <=
else
day @ 28 <=
then
else true then ;

: validdate?
validdate1
validdate2 and
validdate3 and ;

: dmy
read-dmy
cr validdate? if ." ok"
else ." NOT ok " then ;


To test type 'dmy' which will ask for a date and check its > 2 sept 1752
and for leap years feb has the right number of days etc.

It will still abort on a bad entry but the user was told and so cant complain
about the fragility.


dxforth

unread,
Jun 9, 2021, 9:02:30 PM6/9/21
to
On 10/06/2021 01:55, Jim Peterson wrote:
>
> It would be really nice and useful to have ," in the standard, though I would lobby for a version that doesn't call "align".

A nice idea but that boat has sailed as there are versions of ," that not
only ALIGN but null terminate as well, thanks to the internal word that
places the string.

> Also, I think the word "place" would be better standardized as something that operates the opposite of "pick", rather than placing a counted string in memory.

More divergence? :) I've increasingly come to view 'Standard Forth' as more a
vehicle for ideas than an organized language - something akin to the old FORML,
with the exception one can vote on it.

dxforth

unread,
Jun 10, 2021, 8:09:45 AM6/10/21
to
On 10/06/2021 02:55, P Falth wrote:
> On Wednesday, 9 June 2021 at 17:55:54 UTC+2, elk...@gmail.com wrote:
>> ...
>> It feels out of place, for a character-based word, and opposes the ability to pack multiple counted strings back-to-back:
>>
>> create daytable
>> ," Sunday"
>> ," Monday"
>> ," Tuesday"
>> ," Thursday"
>> ," Friday"
>> ," Saturday"
>
> Either you are missing one day or you have shorter weeks where you live!
>
>> : day>name ( dow -- c-addr u )
>> daytable swap
>> 0 ?do
>> count chars + \ <= doesn't work... needs "count chars + align", instead... WHY!!!?
>> loop
>> ;
>
> Align in S, will align the dataspace pointer (HERE) on a cell boundary allocating more space for the string so the next one start on an aligned address.
> In your day>name function you need to take care of this extra space as you are adding up the lenghts to get to the right string. For this you
> need ALIGNED after count +.
>
> My version avoids this by making all strings 9 bytes wide. I can immediately calculate the start address of the string.

Here's a disambigufier for aberrant implementations :)

: ," here ," count chars + here - allot ;

There's no good reason I can see why ," should align or null append its
string. That's properly the role of an application should it be needed.
A disambigufier shouldn't be necessary.

none albert

unread,
Jun 10, 2021, 8:45:02 AM6/10/21
to
A reasonable factor seems to be ,, (multicomma).
Reference implementation
1 \ Allot sc .
2 : ,, HERE SWAP DUP ALLOT MOVE ;

This is neutral and can be handy in a great many circumstances.

: ," &" PARSE HERE >R DUP , ,, R> ;
If you prefer brain damaged strings
: ," &" PARSE HERE >R DUP C, ,, R> ;
If you prefer C- strings
: ," &" PARSE HERE >R ,, 0 C, R> ;

I would promote `,, rather than trying everybody to adopt the
same strings that I prefer.

Jim Peterson

unread,
Jun 10, 2021, 3:49:41 PM6/10/21
to
On Wednesday, June 9, 2021 at 12:55:45 PM UTC-4, P Falth wrote:
> Either you are missing one day or you have shorter weeks where you live!

meh... nobody likes Wednesdays, anyhow ;) (sorry... had to retype the code by hand)

> My version avoids this by making all strings 9 bytes wide. I can immediately calculate the start address of the string.
> Unfortunately if you read CLF in Google Groups, Google will remove extra spaces so my string if copied into a forth system
> from Google will be wrong.

I was noticing this (and got caught by Google Groups' troubles). It makes for simple indexing ("9 *"), but then it enforces space padding where that might not be desirable. I suppose you could have another table for string length (note: interpretation semantics for S" are not standardized):

create daytable
s" Saturday Sunday Monday Tuesday WednesdayThursday Friday "
dup allot daytable swap move

create daylength
8 c,
6 c,
6 c,
7 c,
9 c,
8 c,
6 c,
: day>name ( day# -- c-addr u )
dup 9 * daytable + swap daylength + c@ ;



Then I saw Hugh Aguilar's version, which used ," but also had to define them in reverse order and put a pointer for each, which I thought used more memory than should be necessary and was not as easily understood (due to the reverse ordering).

I was torn between execution speed and memory usage, so I opted for reducing memory in the extreme. There might be a better tradeoff:

create daytable
," Monday"
," Tuesday"
," Wednesday" \ Yay!
," Thursday"
," Friday"
," Saturday"

create dayoffset 7 allot

marker undo
:noname
daytable 7 0 do
dup daytable -
dayoffset i + c!
count chars + \ add "aligned", if necessary
loop
drop
; execute
undo

: day>name ( day# -- c-addr u )
dayoffset + c@ daytable + count ; \ "count" not strictly a necessary step

This would be rather compact and fast, but I worry that the :noname definition will linger in the dataspace (or some space). I tried to remove it with "marker undo"/"undo". Not sure if that's legitimate. Of course, the above only works if ," does not ALIGN. The code for initializing dayoffset isn't exactly intuitive, either.



On Wednesday, June 9, 2021 at 1:28:51 PM UTC-4, NN wrote:
> : leap-year? ( y -- f )
> dup 400 mod 0= swap 4 mod 0= and ;

I think leap-year? might be wrong, as it returns false most of the time (e.g., for 2004).



On Wednesday, June 9, 2021 at 9:02:30 PM UTC-4, dxforth wrote:
> On 10/06/2021 01:55, Jim Peterson wrote:
> >
> > It would be really nice and useful to have ," in the standard, though I would lobby for a version that doesn't call "align".
> A nice idea but that boat has sailed as there are versions of ," that not
> only ALIGN but null terminate as well, thanks to the internal word that
> places the string.

I think the mentality of "that boat has sailed" just because some Forth system somewhere defines ," or "place" as something different might artificially bind the hands of a standards committee. Such a committee has enough work to do without worrying that newly proposed words break an existing system's non-standard additions, and users of non-standard elements should already be keenly aware that they're tying their code to a particular system... even a particular *version* of that system. If your code's not standard, it's not portable (even if it is standard, it might not be 100% portable, but that would be the target system's issue, not the code's).

While the committee might make an effort not to needlessly break systems, I think their primary drive should be for the utility, clarity, and promotion of the language. I think that having a "place" command that does this:

PLACE ( x[u] x[u-1] ... x[0] y u -- y x[u-1] ... x[0] )

would increase the utility of the language, as would having a standard ," word. Additionally, not having the ," word do ALIGN at the end would increase its clarity, as it would feel more like expected behavior.

I might also argue for:

NSWAP ( x[u] x[u-1] ... x[0] u -- x[0] x[u-1] ... x[u] )

Given PICK, PLACE, NSWAP, and DROP, a user could convert some arbitrarily arranged data stack into a different desired order in what seems to be a relatively straight-forward technique, placing top-of-stack where desired, or dropping it, or swapping it with the deepest word needing adjustment.

Of course, short-cut words like 1PLACE, 2PLACE, 3PLACE and 2PICK, 3PICK, 4PICK, etc. could probably be more efficiently implemented by the system, leaving the user, or some standard header, to do:

[UNDEFINED] 2pick [IF] : 2pick 2 pick ; [THEN]

etc.


Here's another thought: if Forth systems wish to provide non-standard words, they should likely also provide a wordlist for their current (and past) versions, so that users could make forward-compatible code by surrounding their code in "ALSO GFORTH_0.7.3" and "PREVIOUS". This way, their code's use of ," or PLACE would pick out the versions from the GFORTH_0.7.3 wordlist, while other code is not bound to that. A developer could even use [DEFINED]/[IF]/[THEN] structures to pick out the most compatible wordlist versions from each supported Forth system, as well. I feel like this would be a strong win for forward compatiblity in any system that implements it.

The wordlist for the current version could always be empty, so as not to bother overriding the latest default wordset, anyway. Wordlists for previous versions would only need definitions for words that have changed in the current version (and could somehow "link in" wordlists from the version before them? Is that a thing that can be done?).

--Jim

P Falth

unread,
Jun 10, 2021, 4:54:07 PM6/10/21
to
On Thursday, 10 June 2021 at 21:49:41 UTC+2, elk...@gmail.com wrote:
> On Wednesday, June 9, 2021 at 12:55:45 PM UTC-4, P Falth wrote:
> > Either you are missing one day or you have shorter weeks where you live!
> meh... nobody likes Wednesdays, anyhow ;) (sorry... had to retype the code by hand)
> > My version avoids this by making all strings 9 bytes wide. I can immediately calculate the start address of the string.
> > Unfortunately if you read CLF in Google Groups, Google will remove extra spaces so my string if copied into a forth system
> > from Google will be wrong.
> I was noticing this (and got caught by Google Groups' troubles). It makes for simple indexing ("9 *"), but then it enforces space padding where that might not be desirable. I suppose you could have another table for string length (note: interpretation semantics for S" are not standardized):

It is if the file wordset is loaded.

> create daytable
> s" Saturday Sunday Monday Tuesday WednesdayThursday Friday "
> dup allot daytable swap move
> create daylength
> 8 c,
> 6 c,
> 6 c,
> 7 c,
> 9 c,
> 8 c,
> 6 c,
> : day>name ( day# -- c-addr u )
> dup 9 * daytable + swap daylength + c@ ;

Yes this is a great improvement!

> Then I saw Hugh Aguilar's version, which used ," but also had to define them in reverse order and put a pointer for each, which I thought used more memory than should be necessary and was not as easily understood (due to the reverse ordering).
>
> I was torn between execution speed and memory usage, so I opted for reducing memory in the extreme. There might be a better tradeoff:
>
> create daytable
> ," Monday"
> ," Tuesday"
> ," Wednesday" \ Yay!
> ," Thursday"
> ," Friday"
> ," Saturday"
> create dayoffset 7 allot
>
> marker undo
> :noname
> daytable 7 0 do
> dup daytable -
> dayoffset i + c!
> count chars + \ add "aligned", if necessary
> loop
> drop
> ; execute
> undo
>
> : day>name ( day# -- c-addr u )
> dayoffset + c@ daytable + count ; \ "count" not strictly a necessary step
>
> This would be rather compact and fast, but I worry that the :noname definition will linger in the dataspace (or some space). I tried to remove it with "marker undo"/"undo". Not sure if that's legitimate. Of course, the above only works if ," does not ALIGN. The code for initializing dayoffset isn't exactly intuitive, either.

:noname definitions will go to code space. In many systems code and data space are the same.

I would simply calculate the offset by hand and put them in the dayoffset table,
just like you did with daylenght.

If you are into minimizing space usage you repeat "day" 7 times. You could save 6*3 18 bytes by
removing it and typing that separately!

BR
Peter

NN

unread,
Jun 10, 2021, 9:43:38 PM6/10/21
to
> On Wednesday, June 9, 2021 at 1:28:51 PM UTC-4, NN wrote:
> > : leap-year? ( y -- f )
> > dup 400 mod 0= swap 4 mod 0= and ;
> I think leap-year? might be wrong, as it returns false most of the time (e.g., for 2004).

Yeah its wrong.

How about

: leap-year? ( y -- f )
dup 100 mod 0= if
400 mod 0=
else 4 mod 0= then ;

dxforth

unread,
Jun 10, 2021, 9:48:14 PM6/10/21
to
On 10/06/2021 22:31, albert wrote:
>
> I would promote `,, rather than trying everybody to adopt the
> same strings that I prefer.

It's about thoughtful implementation. In this case a primitive was
complicated with things that ought to have been handled separately,
and it came back to bite. What affected systems do about it is
their business. I feel no obligation to find work-arounds for them.
Learning from their mistake is enough for me.

none albert

unread,
Jun 11, 2021, 6:35:03 AM6/11/21
to
In article <385833cd-a1ce-4e0f...@googlegroups.com>,
P Falth <peter....@gmail.com> wrote:
<SNIP>
>
>:noname definitions will go to code space. In many systems code and data
>space are the same.

I doubt that definitions will go to the code space always.
In a straightforward threaded system, the threaded
code is data to the system in e.g. the ELF sense.
With the headers it could be in a writable data segment.
Only code definitions would add to the ELF code segment.
In such systems you can have a non-writable code segment and
Forth is fine. Expect less problems with virus checkers.

Jim Peterson

unread,
Jun 11, 2021, 7:37:50 AM6/11/21
to
On Thursday, June 10, 2021 at 4:54:07 PM UTC-4, P Falth wrote:
> On Thursday, 10 June 2021 at 21:49:41 UTC+2, elk...@gmail.com wrote:
> > ... (note: interpretation semantics for S" are not standardized):
> It is if the file wordset is loaded.

Sometimes the standard bugs me. Why did they choose to redefine words in a wordset like that rather than just adjusting the base level word?

> If you are into minimizing space usage you repeat "day" 7 times. You could save 6*3 18 bytes by
> removing it and typing that separately!

You know, I had thought about that for some time. I feel like it violates the spirit of the function, though. I could have done something with "SunMonTueWedThuFriSat", too.


On Thursday, June 10, 2021 at 9:43:38 PM UTC-4, NN wrote:
> How about
> : leap-year? ( y -- f )
> dup 100 mod 0= if
> 400 mod 0=
> else 4 mod 0= then ;

Ah, yes... that works better.

--Jim

Hugh Aguilar

unread,
Jun 11, 2021, 10:11:58 PM6/11/21
to
On Thursday, June 10, 2021 at 5:09:45 AM UTC-7, dxforth wrote:
> Here's a disambigufier for aberrant implementations :)
>
> : ," here ," count chars + here - allot ;
>
> There's no good reason I can see why ," should align or null append its
> string. That's properly the role of an application should it be needed.
> A disambigufier shouldn't be necessary.

You are purposely using my word 'disambiguifier' incorrectly.
You are getting to be really irksome --- piss off!

dxforth

unread,
Jun 12, 2021, 12:12:17 AM6/12/21
to
On 12/06/2021 12:11, Hugh Aguilar wrote:
> On Thursday, June 10, 2021 at 5:09:45 AM UTC-7, dxforth wrote:
>> Here's a disambigufier for aberrant implementations :)
>>
>> : ," here ," count chars + here - allot ;
>>
>> There's no good reason I can see why ," should align or null append its
>> string. That's properly the role of an application should it be needed.
>> A disambigufier shouldn't be necessary.
>
> You are purposely using my word 'disambiguifier' incorrectly.

I don't promote disambigufiers if that's what you mean. Appeasement
is no solution.

Jim Peterson

unread,
Jun 17, 2021, 4:05:33 PM6/17/21
to
On Thursday, June 10, 2021 at 8:09:45 AM UTC-4, dxforth wrote:
> Here's a disambigufier for aberrant implementations :)
>
> : ," here ," count chars + here - allot ;

This is absolutely what I do not want to do, though. If I
feel compelled to override a standard word (though it's not
standard yet) with a slightly different implementation, won't
others be, as well? If they do so, and multiple source files
are included, suddenly we have several different overrides of
the word, and a bunch of dead/redundant code hanging around.
And hopefully no one includes source that was actually
expecting alignment.

I'd rather kill the problem at the source by deciding on a
good implementation before the word gets standardized with a
sub-par one.

Like "literal"... why on earth does it not have interpretation
semantics? Interpretation mode, given the existence of "["
and "]", is clearly not the same thing as "not compiling a
word" mode. I could certainly see a specification for
"literal" having a problem when there is no word definition
being built, but it seems far more intuitive for code to
say:

[ char Z char A - literal ]

rather than:

[ char Z char A - ] literal

The first implies that the literal value was something that
was computed on the data stack at compilation time, while
the second mode is misleading unless you understand "literal"
to be immediate, and yet it's required.

It really feels like, beyond STATE, there is another mode of
interest stating whether or not a word definition is currently
being built. I can see "literal" throwing an error if that
mode isn't "yes", and "," throwing an error if the mode isn't
"no" (unless you're using some internal version of "," because
you're the system developer and you know what data should be
encoded into a word definition).

It is this other mode which the standard seems to confuse with
STATE in terms of what words should be allowed where...
whether they should have compilation and/or interpretation
semantics. Why is there both ." and .( for instance? I mean,
sure you'd have to [ ." <something>" ] if you wanted to emit
something during the compilation of a word, but while that is a
lot more characters, it feels far more intuitive to me as to what
the developer wanted done right there ("pause word definition,
emit some text, resume definition"). What's more, abort" should
work the same in interpretation and compilation modes (I think
I've ranted about this before). I want to do things like:

[UNDEFINED] b@ abort" need a byte-access method"

or:

here ," " here - -1 <> [IF]
." WARNING!: ,\" wastes space"
[THEN]

(but then, ,\" doesn't work either, does it?)


It feels like half the standard is stuck in a 1980's mindset
(case insensitivity... single, flat namespace... modal BASE...
no escaped characters or native strings... every byte or
machine cycle counts... read blocks from disk) while the other
half is trying to just work around the problems and not correct
them (search order wordset, but no standard wordlists defined...
some words support character escaping... ," can burn 7 bytes for no
reason... file I/O exists).

It almost feels like we should make a clean break from the
past standard, either ditching backward compatibility
altogether or having it a mode by which old code can be
compiled (no... not a mode variable, just some "compat-include"
notion, or something), and rebuilding the standard to be
a bit more modern while retaining the interesting aspects
from the original.

--Jim

dxforth

unread,
Jun 18, 2021, 12:07:53 AM6/18/21
to
The former has bugged me ever since I saw such constructs being
used in examples on c.l.f. Here's why I see it as wrong, even if
it happens to work :)

[ ] implies compilation is being suspended and we're computing
something immediately on the data stack. Including LITERAL among
the calculations re-invokes compilation. Semantically it's
equivalent to writing:

[ char Z char A - ] literal [ ]

Which is a lot of bother when one can simply write:

[ char Z char A - ] literal

That LITERAL works in interpret state on some systems is a quirk
of implementation. It won't work on classic systems due to COMPILE
nor would I wish it to for semantic reasons.

Jim Peterson

unread,
Jun 18, 2021, 11:05:24 AM6/18/21
to
On Friday, June 18, 2021 at 12:07:53 AM UTC-4, dxforth wrote:
> The former has bugged me ever since I saw such constructs being
> used in examples on c.l.f. Here's why I see it as wrong, even if
> it happens to work :)
>
> [ ] implies compilation is being suspended and we're computing
> something immediately on the data stack. Including LITERAL among
> the calculations re-invokes compilation. Semantically it's
> equivalent to writing:

Interesting. This may just be a difference in what we view that "LITERAL"
actually means. In my mind, I view it much like "COMPILE," and think that
it should really be named "LITERAL," to take its place with other words
having "," in their names. I see it as saying "take top-of-stack and encode
it into the current definition so that, at that place in the definition, that
number is pushed on to the stack". I see it as a word-building word from the
outside of the definition... one of the collection of external processes that
is creating a new word definition by appending stuff to the dictionary.

You seem to view it as a word inside a definition, which is a placeholder
saying something like "this is a number being pushed on the stack (like
123 or -48), but I'm not actually specifying what that number is. It would
appear right here in the code. Its actual value is whatever's on the data
stack during the definition of the word." If the control stack was guaranteed
to be different from the data stack, one could even compute several such
numbers on the data stack before the definition of the word even began, thus
having a sort of templatized word definition which, if it was compiled several
times (into different wordlists) could serve to make a whole family of words
(perhaps also using [IF]/[THEN]). Like:

: addN LITERAL + ;

which you would evaluate/compile/whatever after computing N on the data
stack.

I see that point of view as well.

--Jim
0 new messages