# Beware: Forth in here

43 replies to this topic

### #1 WillsyOFFLINE

Willsy

River Patroller

• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Tue Mar 21, 2017 10:25 AM

All ye faint of heart turn back and be gone, for here lies The Forth, and The Forth is The Way.

Calculate the day of the week for any date since 1/1/1700.

Note: BRITISH date format (day month year) is required. Ha!

```variable year
variable month
variable day

create years      0 c, 3 c, 3 c, 6 c, 1 c, 4 c, 6 c, 2 c, 5 c, 0 c, 3 c, 5 c,
create leap-years 6 c, 2 c, 3 c, 6 c, 1 c, 4 c, 6 c, 2 c, 5 c, 0 c, 3 c, 5 c,
create centuries  4 c, 2 c, 0 c, 6 c,

: century ( year - n )
1700 /mod drop 100 / 4 mod centuries + c@ ;

: leapYear? ( year - flag )
dup >r 400 mod 0=  r@ 100 mod 0<> or  r> 4 mod 0= and ;

: dow ( day month year -- n )
dup century >r  100 mod year !  1- month !  day !
r>  year @ dup 4 /  day @
year @ leapYear? if leap-years else years then month @ + c@
+ + + +  7 mod ;

: ? ( day month year -- )
dow case
0 of ." Sunday" endof
1 of ." Monday" endof
2 of ." Tuesday" endof
3 of ." Wednesday" endof
4 of ." Thursday" endof
5 of ." Friday" endof
6 of ." Saturday" endof
endcase space ;

31 1 1883 ?
3 12 1970 ?
25 12 2017 ?
1 1 2018 ?
```

Edited by Willsy, Tue Mar 21, 2017 12:52 PM.

### #2 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Tue Mar 21, 2017 10:33 AM

Source for the algorithm: http://www.stoimen.c...ay-of-the-week/

### #3 VorticonOFFLINE

Vorticon

River Patroller

• 2,623 posts
• Location:Eagan, MN, USA

Posted Tue Mar 21, 2017 6:44 PM

Cool

### #4 sometimes99erOFFLINE

sometimes99er

River Patroller

• 3,832 posts
• Location:Denmark

Posted Wed Mar 22, 2017 5:00 AM

http://atariage.com/...date-like-this/

### #5 VorticonOFFLINE

Vorticon

River Patroller

• 2,623 posts
• Location:Eagan, MN, USA

Posted Wed Mar 22, 2017 9:26 AM

You've got to love Forth

Now I'm itching to do a pure software project. I have something in mind, and I think this time around I'm going to use FbForth so I can test it out.

### #6 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 11:36 AM

The calendar code is pretty nice.

Something I have been playing with is creating a Forth layer that is TI-BASIC user friendly.

Forth is a chameleon so it not too hard to do, but there would still be the RPN aspect of Forth to contend with

and spaces between each token in the source code but it would overlap better with TI-BASIC users

knowledge base and expectations.

One easy to fix sticky point is the colours in Forth use the machine values 0 to 15 where TI-BASIC uses 1 to 16.

One could also do this kind of thing pretty simply in Forth:

VARIABLE A\$  32 DIM

A\$ =" This is a Forth string"

A\$ PRINT

This is a Forth String  ok

If I created such layer on top of Forth with documentation, would it have some value to the 99ers here?

BF

### #7 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 11:44 AM

I am fascinated by the semantic density between Forth version and the BASIC program. The creator of BASIC found a way to create a pretty high level language.

Consider the PHP example Willsy gave us.

```function get_century_code(\$century)
{
// XVIII
if (1700 <= \$century && \$century <= 1799)
return 4;

// XIX
if (1800 <= \$century && \$century <= 1899)
return 2;

// XX
if (1900 <= \$century && \$century <= 1999)
return 0;

// XXI
if (2000 <= \$century && \$century <= 2099)
return 6;

// XXII
if (2100 <= \$century && \$century <= 2199)
return 4;

// XXIII
if (2200 <= \$century && \$century <= 2299)
return 2;

// XXIV
if (2300 <= \$century && \$century <= 2399)
return 0;

// XXV
if (2400 <= \$century && \$century <= 2499)
return 6;

// XXVI
if (2500 <= \$century && \$century <= 2599)
return 4;

// XXVII
if (2600 <= \$century && \$century <= 2699)
return 2;
}

/**
* Get the day of a given date
*
* @param \$date
*/
function get_day_from_date(\$date)
{
\$months = array(
1 => 0,		// January
2 => 3,		// February
3 => 3,		// March
4 => 6,		// April
5 => 1,		// May
6 => 4,		// June
7 => 6,		// July
8 => 2,		// August
9 => 5,		// September
10 => 0,	// October
11 => 3,	// November
12 => 5,	// December
);

\$days = array(
0 => 'Sunday',
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
);

// calculate the date
\$dateParts = explode('-', \$date);
\$century = substr(\$dateParts[2], 0, 2);
\$year = substr(\$dateParts[2], 2);

// 1. Get the number for the corresponding century from the centuries table
\$a = get_century_code(\$dateParts[2]);

// 2. Get the last two digits from the year
\$b = \$year;

// 3. Divide the number from step 2 by 4 and get it without the remainder
\$c = floor(\$year / 4);

// 4. Get the month number from the month table
\$d = \$months[\$dateParts[1]];

// 5. Sum the numbers from steps 1 to 4
\$e = \$a + \$b + \$c + \$d;

// 6. Divide it by 7 and take the remainder
\$f = \$e % 7;

// 7. Find the result of step 6 in the days table
return \$days[\$f];
}

// Sunday
echo get_day_from_date('31-1-1883');
```

Edited by TheBF, Wed Mar 22, 2017 11:49 AM.

### #8 Opry99erOFFLINE

Opry99er

• 8,221 posts
• Location:Cookeville, TN

Posted Wed Mar 22, 2017 1:00 PM

A BASIC compiler/translator written in Forth, eh?

Ears open...

### #9 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Wed Mar 22, 2017 1:40 PM

Wow! I Love that basic program. I'm going to have to translate it

### #10 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Wed Mar 22, 2017 1:49 PM

Something I have been playing with is creating a Forth layer that is TI-BASIC user friendly.

<snip>

The thing is, once you learn Forth, you realise it's actually EASIER than BASIC!

Especially when you have features such as local variables, which can eliminate stack thrashing completely; especially with my locals implementation, which do not have to be loaded from the stack - they are true local variables.

### #11 mizapfOFFLINE

mizapf

River Patroller

• 2,387 posts
• Location:Germany

Posted Wed Mar 22, 2017 2:32 PM

The thing is, once you learn Forth, you realise it's actually EASIER than BASIC!

Just being curious, as you are obviously skilled in Forth: Can you indeed understand a Forth program from looking at it (of course, if you are not the author)? In that sense, what you just said, is it easier to write a Forth program than a BASIC program, or easier to understand such a program?

My programming skills are certainly strongly influenced by object orientation for the last 22 years, so I admit I feel really lost when I look at a Forth program as shown above. (The same would be true for me for functional programming or logic programming.)

### #12 --- Ω ---OFFLINE

--- Ω ---

--- Ω ---

• 10,086 posts
• Location:Virgo Supercluster, Gould Belt in the Orion arm of Milky Way galaxy.

Posted Wed Mar 22, 2017 2:45 PM

...once you learn Forth, you realise it's actually EASIER than BASIC!

Now THAT would make a neat bumper sticker!

### #13 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Wed Mar 22, 2017 3:35 PM

Hi Michael,

Chuck Moore (the inventor of the Forth language) describes the Forth language as an "amplifier"; meaning that well designed Forth can look beautiful and almost read like english (or, at least, quite intuitive to understand) whereas badly written Forth can look terrible and be utterly incomprehensible.

Forth *can* be hard to understand. The reason is, in most languages you are only directing the flow of the *code* (when certain things are executed, and when they are not). In Forth, in addition to controlling code flow, you are also controlling the flow of *data* because it sits on a FIFO stack. You can of course use variables but the stack is used to manipulate everything.

Most of the Forth code that I see that isn't written by me I find hard to understand. There's essentially two reasons:

1) It's mostly from Forth nerds on comp.lang.forth and they actually seem to take delight in posting incredibly obtuse, non-idiomatic Forth code. It's like it's some sort of Forth coder cock-measuring context.

2) See 1

I find my own code easy to understand, but that's probably because I understand my own style! Having said, I find any code that I didn't write hard to understand. I spend a lot of my day job (at the moment) modifying Structured Text code (similar to Pascal) written for control systems. I find it TERRIBLY difficult, and somewhat terrifying. I find that after a while, I begin to get "into the mind" of the programmer, and *that's* when I begin to understand the code. When I understand where the programmer is coming from, and where he's going. Then it begins to make sense.

I'm digressing. Forth is a language for writing other languages. So, normally, when you write a (major) program in Forth, maybe as much as 25-30% of it might be code that is associated with defining a domain specific language which the rest of the program would be written. This low-level, domain specific language definition code will typically be low-loevel nitty-gritty Forth code, and quite dense and difficult to understand. However, when the application is written using the domain specific language, the application is often very simple to understand. The word awesome is oft overused these days, but Forth's ability to extend itself, in terms of itself, is really quite awesome and totally mind blowing when you first experience and understand it.

For example, I might be writing a game (space invaders) and to put the invaders on the screen, I could define a DSL such that I can later write:

: displayAliens  on 20 rows 12 aliens per row ;

So, this is a word called displayAliens, and, well, you can understand exactly what it does, because it reads like English, but it isn't English. It's Forth. More specifically, the Forth language has been expanded with words such as rows, aliens, per and row, which are not mere subroutines/functions as they would be in other languages, but are now part of the language itself, just like DUP or SWAP or IF etc.

The Forth philosophy is very deep, and it takes time to fully appreciate it. It was the best thing I ever did in my personal journey with computing.

### #14 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Wed Mar 22, 2017 3:36 PM

Now THAT would make a neat bumper sticker!

You make 'em, I'll buy 'em

### #15 --- Ω ---OFFLINE

--- Ω ---

--- Ω ---

• 10,086 posts
• Location:Virgo Supercluster, Gould Belt in the Orion arm of Milky Way galaxy.

Posted Wed Mar 22, 2017 3:44 PM

You make 'em, I'll buy 'em

I think Chris' wife is the person you want to talk to!

### #16 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Wed Mar 22, 2017 3:55 PM

Harping on more about local variables, look how code can be simplified by using locals.

Here's a program to do exponentiation:

```: ^ ( i n -- i' )
begin
dup 1 > while
-rot
over
*
rot
1-
repeat
drop
nip ;
```

It's idiomatic Forth. It doesn't use variables at. It does everything on the stack. Because it does everything on the stack, there's a certain amount of stack-juggling (stacrobatics) going on, just moving things into the right place on the stack for the next operation, etc.

Being truly idiomatic, it would be written horizontally, like this:

```: ^ ( i n -- i' ) begin dup 1 > while -rot over * rot 1- repeat drop nip ;
```

But I find that the indentation helps with understanding. There are endless arguments between ninja black-belt snobby Forthers over this issue, which I won't go into here!

Let's have a look how it looks in TurboForth with locals (full disclosure: TurboForth's locals implementation is unique to TurboForth (I believe them to be superior)):

```: ^ ( n r -- n^r )
locals{ n r }  set r  set n
n n *
r 1-  0 do
n *
loop ;
```

No stack juggling. This version will actually execute faster than the idiomatic version because CPU cycles aren't being burned moving things around on the stack.

### #17 lucien2OFFLINE

lucien2

Moonsweeper

• 282 posts
• Location:Switzerland

Posted Wed Mar 22, 2017 6:09 PM

I am fascinated by the semantic density between Forth version and the BASIC program. The creator of BASIC found a way to create a pretty high level language.

Consider the PHP example Willsy gave us.

The PHP example is not exactly the same algorithm. It does not test leap years and the century codes are not in an array but in a switch/case statement.
Consider this JavaScript version.
Forth: 30 lines of code, 913 bytes
JavaScript: 26 lines of code, 778 bytes

```var years =      [ 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 ];
var leap_years = [ 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 ];
var centuries =  [ 4, 2, 0, 6 ];
var days = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ];

function century(year) {
return centuries[Math.floor(year % 1700 / 100) % 4];
}

function leapYear(year) {
return !(year % 400) || year % 100 && !(year % 4);
}

function dow(day, month, year) {
day = +day; month = +month; year = +year; // string to number
var yy = year % 100;
return days[
(century(year) + yy + Math.floor(yy/4) +
(leapYear(year)? leap_years[month-1] : years[month-1]) +
day) % 7
]
}

function dayOfWeek() {
output.innerHTML = dow.apply(null, input.value.split(' '));
}
```

### #18 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 7:36 PM

Yes I noticed that PHP was a very different algorithm. It was the first code I saw on the page Willsy  pointed to.

The Java code is much tighter and tracks more closely with the Forth version.

Thanks!

B

### #19 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 7:44 PM

<SNIP>

Let's have a look how it looks in TurboForth with locals (full disclosure: TurboForth's locals implementation is unique to TurboForth (I believe them to be superior)):

```: ^ ( n r -- n^r )
locals{ n r }  set r  set n
n n *
r 1-  0 do
n *
loop ;
```

No stack juggling. This version will actually execute faster than the idiomatic version because CPU cycles aren't being burned moving things around on the stack.

Now if those locals took spare registers instead of space on the stack what would happen?

(I am still jealous of GCC) :-)

Here was some code I thought code be used to allocate register variables in machine Forth

as long as you have the spare registers contiguous from 1 to n.

NEW.REGS would have to RUN by the Forth word LOCALS in order

to free up a new batch of registers for your sub-routine (WORD).

```variable  reg#                     \ holds the next available register

: new.regs    ( -- )  6 reg# !  ;  \ INIT the allocations: my system has 6 free registers from R0 to R5

: reg.allot   ( -- n)
reg# @ 1- dup 0<            \ check if have any left
abort" no more registers!"  \ abort if we don't
dup reg# ! ;                \ return the available register and store in REG#

: local       ( -- <text>)
create  reg.allot ,         \ compile time: create a named number that is allotted register
does> @  ;                  \ run time: fetch the register number when the name is used in a program
```

Edited by TheBF, Wed Mar 22, 2017 7:50 PM.

### #20 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 8:07 PM

Hi Michael,

Chuck Moore (the inventor of the Forth language) describes the Forth language as an "amplifier"; meaning that well designed Forth can look beautiful and almost read like english (or, at least, quite intuitive to understand) whereas badly written Forth can look terrible and be utterly incomprehensible.

Forth *can* be hard to understand. The reason is, in most languages you are only directing the flow of the *code* (when certain things are executed, and when they are not). In Forth, in addition to controlling code flow, you are also controlling the flow of *data* because it sits on a FIFO stack. You can of course use variables but the stack is used to manipulate everything.

Most of the Forth code that I see that isn't written by me I find hard to understand. There's essentially two reasons:

1) It's mostly from Forth nerds on comp.lang.forth and they actually seem to take delight in posting incredibly obtuse, non-idiomatic Forth code. It's like it's some sort of Forth coder cock-measuring context.

2) See 1

I find my own code easy to understand, but that's probably because I understand my own style! Having said, I find any code that I didn't write hard to understand. I spend a lot of my day job (at the moment) modifying Structured Text code (similar to Pascal) written for control systems. I find it TERRIBLY difficult, and somewhat terrifying. I find that after a while, I begin to get "into the mind" of the programmer, and *that's* when I begin to understand the code. When I understand where the programmer is coming from, and where he's going. Then it begins to make sense.

I'm digressing. Forth is a language for writing other languages. So, normally, when you write a (major) program in Forth, maybe as much as 25-30% of it might be code that is associated with defining a domain specific language which the rest of the program would be written. This low-level, domain specific language definition code will typically be low-loevel nitty-gritty Forth code, and quite dense and difficult to understand. However, when the application is written using the domain specific language, the application is often very simple to understand. The word awesome is oft overused these days, but Forth's ability to extend itself, in terms of itself, is really quite awesome and totally mind blowing when you first experience and understand it.

For example, I might be writing a game (space invaders) and to put the invaders on the screen, I could define a DSL such that I can later write:

: displayAliens  on 20 rows 12 aliens per row ;

So, this is a word called displayAliens, and, well, you can understand exactly what it does, because it reads like English, but it isn't English. It's Forth. More specifically, the Forth language has been expanded with words such as rows, aliens, per and row, which are not mere subroutines/functions as they would be in other languages, but are now part of the language itself, just like DUP or SWAP or IF etc.

The Forth philosophy is very deep, and it takes time to fully appreciate it. It was the best thing I ever did in my personal journey with computing.

I will echo the language creation concept.

I always say that I stop programming in Forth after this first page of code.

Years ago I read an article where a guy wrote code to "Menu Drive the 8250 UART" in Forth.

It was a menu on the screen where you pushed 1 to do X, 2 to do Y,3 to do Z etc.

All this so you could configure the \$!%!#\$ RS232 ports on a PC.

I kind of lost it when I saw it but it prompted me to write a reply article that was published.

I created the following Forth words:

COM1:   COM2:    BAUD  BITS PARITY ODD EVEN NO  STOP-BIT

Ya you get it.  So instead of pile of BS menus, you could now type at the console -OR-  use in your program:

COM1: 9600 BAUD   NO PARITY  1 STOP-BIT

etc...

You don't actually program in Forth. It's too stupid. :-)  (primitive)

You make what you need and then program in that.

B

Edited by TheBF, Wed Mar 22, 2017 8:42 PM.

### #21 Lee StewartOFFLINE

Lee Stewart

River Patroller

• 3,196 posts
• Location:Silver Run, Maryland

Posted Wed Mar 22, 2017 8:07 PM

... In Forth, in addition to controlling code flow, you are also controlling the flow of *data* because it sits on a FIFO stack. ...

Not to get in the way of your most excellent dissertation, but the data stack is LIFO.

...lee

### #22 TheBFOFFLINE

TheBF

Chopper Commander

• 217 posts
• Location:The Great White North

Posted Wed Mar 22, 2017 8:42 PM

The master has spoken.

### #23 Lee StewartOFFLINE

Lee Stewart

River Patroller

• 3,196 posts
• Location:Silver Run, Maryland

Posted Wed Mar 22, 2017 10:53 PM

Thanks.  Not everyone appreciates nitpicking, but I do it anyway.

...lee

### #24 Lee StewartOFFLINE

Lee Stewart

River Patroller

• 3,196 posts
• Location:Silver Run, Maryland

Posted Wed Mar 22, 2017 11:19 PM

Harping on more about local variables, look how code can be simplified by using locals.

Here's a program to do exponentiation:

```: ^ ( i n -- i' )
begin
dup 1 > while
-rot
over
*
rot
1-
repeat
drop
nip ;
```
It's idiomatic Forth. It doesn't use variables at. It does everything on the stack. Because it does everything on the stack, there's a certain amount of stack-juggling (stacrobatics) going on, just moving things into the right place on the stack for the next operation, etc.

Being truly idiomatic, it would be written horizontally, like this:
```: ^ ( i n -- i' ) begin dup 1 > while -rot over * rot 1- repeat drop nip ;
```
But I find that the indentation helps with understanding. There are endless arguments between ninja black-belt snobby Forthers over this issue, which I won't go into here!

Let's have a look how it looks in TurboForth with locals (full disclosure: TurboForth's locals implementation is unique to TurboForth (I believe them to be superior)):
```: ^ ( n r -- n^r )
locals{ n r }  set r  set n
n n *
r 1-  0 do
n *
loop ;
```
No stack juggling. This version will actually execute faster than the idiomatic version because CPU cycles aren't being burned moving things around on the stack.

These ideas are great, Mark, but your code will not work as is.  The “stackrobatic” word is missing over swap before begin .  The “locals” example gives n^(r+1) instead of the desired n^r.  It can be fixed by removing n * before the loop and enclosing the loop in r 1 > if ... then .

Neither example handles r = 0, which can be managed with

Spoiler

...lee, the nitpicker!

### #25 WillsyOFFLINE

Willsy

River Patroller

• Topic Starter
• 2,980 posts
• Location:Uzbekistan (no, really!)

Posted Thu Mar 23, 2017 1:58 AM

Ah I should have checked more closely! I did have code for checking for 0 and 1 but removed it when posting for brevity.

Of all nits worth picking this was surely one of them! Thanks :-)

Is good to be talking about Forth again.

By the way, I made some significant improvements to the locals implementation in TF last night. Have a look at the locals page on the website. Also updated the online help. Need to update the blocks disk.

#### 0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users