Jump to content

Photo

Beware: Forth in here


43 replies to this topic

#1 Willsy OFFLINE  

Willsy

    River Patroller

  • 3,002 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 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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 Vorticon OFFLINE  

Vorticon

    River Patroller

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

Posted Tue Mar 21, 2017 6:44 PM

Cool :)



#4 sometimes99er OFFLINE  

sometimes99er

    River Patroller

  • 3,865 posts
  • Location:Denmark

Posted Wed Mar 22, 2017 5:00 AM

;)

 

Attached File  week.turbo.png   4.16KB   3 downloads

 

Attached File  week.xb.png   2.09KB   4 downloads

 

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

 

 



#5 Vorticon OFFLINE  

Vorticon

    River Patroller

  • 2,678 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 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 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 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 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 Opry99er OFFLINE  

Opry99er

    Quadrunner

  • 8,246 posts
  • Location:Cookeville, TN

Posted Wed Mar 22, 2017 1:00 PM

A BASIC compiler/translator written in Forth, eh?

Ears open...

#9 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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 mizapf OFFLINE  

mizapf

    River Patroller

  • 2,474 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  

--- Ω ---

    Kwisatz Haderach

  • 10,276 posts
  • TI-99/4A Fanatic
  • Location:In the den playing with my FinalGROM 99!

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 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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 :-D 

 

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 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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  :thumbsup:



#15 --- Ω --- OFFLINE  

--- Ω ---

    Kwisatz Haderach

  • 10,276 posts
  • TI-99/4A Fanatic
  • Location:In the den playing with my FinalGROM 99!

Posted Wed Mar 22, 2017 3:44 PM

 

You make 'em, I'll buy 'em  :thumbsup:

 

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



#16 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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 lucien2 OFFLINE  

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 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 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 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 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 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 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 :-D

 

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 Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,258 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.  :grin:

 

...lee



#22 TheBF OFFLINE  

TheBF

    Moonsweeper

  • 280 posts
  • Location:The Great White North

Posted Wed Mar 22, 2017 8:42 PM

The master has spoken. :) 

 

You have a gift Lee.



#23 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,258 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 Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,258 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 Willsy OFFLINE  

Willsy

    River Patroller

  • Topic Starter
  • 3,002 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