Jump to content
IGNORED

Camel99 Forth Information goes here


TheBF

Recommended Posts

I have been trying to learn about higher-order-functions and programming without using loops.

 

I took look at REDUCE() in Python and took a run at emulating that.

Python's reduce(): From Functional to Pythonic Style – Real Python

 

>>> def my_add(a, b):
...     result = a + b
...     print(f"{a} + {b} = {result}")
...     return result

 

>>> from functools import reduce

>>> numbers = [0, 1, 2, 3, 4]

>>> reduce(my_add, numbers)
0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10

It's kind of neat to see this kind of high level stuff in lowly Forth.  Here we add REDUCE albeit for integer data only. 

Spoiler

\ REDUCE for cell data

\ INCLUDE DSK1.TOOLS

DECIMAL
VARIABLE REDUCER
: REDUCE ( inital addr size xt-- n)
         OVER 0= ABORT" REDUCE: size cannot be 0"
         REDUCER @ >R  \ allow this to be re-entrant
         REDUCER !
         CELLS BOUNDS
         DO
           I @  REDUCER PERFORM
         2 +LOOP
         R> REDUCER !
;

 

 

MyAdd is a little scarier. :)  but Python has a lot of implicit stuff baked in. 

 
: MyAdd ( n n -- n)
        CR OVER . ." + " DUP .  +  DUP ." = "  .  ;

This data has no length embedded so we have to provide it manually (that could be fixed) 

CREATE MYDATA  0 , 1 , 2 , 3 , 4 ,

And here is the result.

Classic99 QI399.046 2022-07-13 9_48_28 PM.png

 

Edit: This Forth REDUCE takes the initial value as an argument. (the zero in this case)

Python does that too but it is an extra parameter. If you don't put it in the argument list zero is assumed.

Variable number of parameters would take more effort in Forth and would be not worth the effort IMHO. 

 

 

 

 

 

  • Like 2
Link to comment
Share on other sites

So reading on in the Python page I see that SUM() is a standard Python function.

 

With REDUCE in our pocket that becomes simple.

Since '+' in Forth is a pure function where no global data is used, only stack data, we can use Forth's '+'  to "REDUCE" the array. :)

: SUM ( addr len ) 0 -ROT  ['] + REDUCE ; 

 

 

And of course once we have SUM we can create AVERAGE:

: AVERAGE ( addr len ) DUP>R SUM  R> / ;

 

In fact REDUCE can take any function with the stack diagram ( n1 n2 -- n3)  Two inputs, one output.

 

So find the maximum:

0 MYDATA 5 ' MAX REDUCE .

Find the minimum:

0 MYDATA 5 ' MIN REDUCE . 

 

Test if all the data are equal to 2:

2  MYDATA 5 ' = REDUCE . 

 

Multiply all the elements in an array requires that the initial argument be 1 since if it was zero the result would always be zero. :)

1  MYDATA 5  ' * REDUCE . 

This is fun.

  • Like 3
Link to comment
Share on other sites

Talking of neat stuff, have you looked at OOP? I may have the authors name wrong, but if I remember correctly, Dick Pountain wrote a book on object oriented programming in Forth. IIRC it's based on Forth 83, and relies heavily on vocabulary manipulation to manage private class methods etc. I imagine it would be really fun to port it across to your Forth system - or, just use the book as a guide to learned how he modelled the design, and put something similar together from scratch. 

 

Just had a quick check. The author is indeed Dick Pountain. The title is Object Oriented Forth - Implementation of Data StructuresObject-oriented forth Implementation of data structures by Pountain, Dick (z-lib.org).pdf

 

Source: https://archive.org/details/object-oriented-forth-implementation-of-data-structures-by-pountain-dick-z-lib.org

 

The book is fetching ridiculous money online - around $50. I bought it probably ten years ago for a few quid!

 

Not sure if you've seen this book before but I think you'll love it!

Link to comment
Share on other sites

Object Oriented programming was a mistake - and even Alan Kay who originally published the idea has said so.


The problem isn't really the basic concept of it. The problem is that, like all software paradigms, it's taken to ridiculous extremes.

 

Whatever software approach you use, remember that the software needs two things from you: (a) it needs to be readable, and (b) it needs to be predictable. These two things go hand in hand - a new person should be able to read your code and predict exactly what it's going to do.

 

A lot of modern software paradigms break the predictable part by hiding as much of the operation as possible, and abstracting things away so far that when you read a line of code, you might be able to guess what it was intended to do, but without digging it's impossible to predict what it /will/ do. Avoid that. ;)

 

I suspect Forth object oriented code won't take things that far, since words will be words, and not some random abstraction of words. ;)

 

  • Like 3
Link to comment
Share on other sites

34 minutes ago, Tursi said:

The problem isn't really the basic concept of it. The problem is that, like all software paradigms, it's taken to ridiculous extremes.

Agree 100%. I'm currently developing a serial comms driver for old RTUs (like a PLC) in C#. The object model I've come up with is simple, and reflects how the RTU works. A very simple class structure. I'm also writing an emulator for these RTUs as the hardware is obsolete. Again, C#, but running in Linux. Lots of fun.

 

I've already been asked why I'm not using abstract classes and methods etc. The answer? I don't need them.

 

At the company I consult for, the developers are young and VERY sharp. Really clever guys. They laugh at me because I tell them to STOP WRITING CODE! Try and spend an hour or so every day REMOVING CODE if you can! Do not add unnecessary layers of abstraction. This isn't a dick-measuring contest. Just stop it. Write what you need to get the job done ONLY.

 

That's probably why I liked Forth so much. That's the entire ethos behind the language.;)

  • Like 3
Link to comment
Share on other sites

5 hours ago, Tursi said:

(Well, at least, he burned what it has become, hehe. ;)

 

 

This is an interesting conversation you guys have set up.

 

I watched this video a while back too.  His statement "... I did not have C++ in mind..."  reminds  me that Chuck Moore also did not like the idea of ANS/ISO Forth.  

He says "Standards are great! Everybody should have one." :)   (He's one of a kind himself) :) 

 

In both cases other people took their language concepts and did what ordinary people do. They "added" to it.

 

 As Willsy was saying taking things out is usually a productive effort. However I find that you have to be a bit smarter to remove stuff. 

Ordinary people seem to be adept at adding but the the Wizards among us are the ones who see the superfluous cruft. :)

It's interesting that in the arts it is well understood by the masters that you are not finished until you have gone through the work and removed things.

Elegance is valued.

 

There is video of Alan exploring his Mac Classic interactively in Smalltalk. He is doing things that would take people hours in conventional languages. It shows how he envisioned OOP to be used.

Coincidentally that's how Chuck envisioned using Forth. Have a conversation with the computer, interactively.  This is not possible in the edit/compile/link/load/run/fail loop that we inherited from Fortran. :) 

 

For @D-Type   I have a copy of Mini-OOP that will compile on Camel Forth but I have never written anything with it but it would work. And ya, Bernd is one smart cookie.

 

So I guess like all great ideas OOP, like any tool, should be used where it is the right tool. 

Used incorrectly it seems to have really messed up a lot of stuff.

I have read about horrible things in Java requiring endless duplication of Classes just to let different parts of the program get access to get the methods they need. Yuk!

 

 

 

 

 

 

 

  • Like 1
Link to comment
Share on other sites

5 hours ago, Tursi said:

The problem isn't really the basic concept of it. The problem is that, like all software paradigms, it's taken to ridiculous extremes.

Quite right.

5 hours ago, Tursi said:

Whatever software approach you use, remember that the software needs two things from you: (a) it needs to be readable, and (b) it needs to be predictable. These two things go hand in hand - a new person should be able to read your code and predict exactly what it's going to do.

 

A lot of modern software paradigms break the predictable part by hiding as much of the operation as possible, and abstracting things away so far that when you read a line of code, you might be able to guess what it was intended to do, but without digging it's impossible to predict what it /will/ do. Avoid that. ;)

This is the classic problem of the "programmable" languages I think.  LISP and Forth live in this world. You are writing the "language", in the first page that is then used in the rest of the project.

This is fantastic for the team on one developer, but requires docs and discipline for bigger teams. 

Chuck writes very short definitions (one line typically) that are "readable" and "predictable" at a glance and one more important thing... they are testable, immediately, at the console, for validation.

Fail to use Forth as designed and you will be un-happy IMHO. :( 

 

5 hours ago, Tursi said:

I suspect Forth object oriented code won't take things that far, since words will be words, and not some random abstraction of words. ;)

Indeed. OOP Forth has a lot of flavours but they mostly enforce encapsulation to limit the brain space required for complex stuff.

I understand it was VERY helpful to use OOP Forth for Windows since it was built with an OOP API.

 

This is a mini-OOP example of a Class. No surprises.  

object class
  cell var text
  cell var len
  cell var x
  cell var y
  method init
  method draw
end-class button

Methods are Forth words assigned to the Class methods that seem to take  the object's base address from the return stack (R@)  so that's a bit ugly.

(:noname  is  the ANS weird name for Lamda) :) 

:noname ( o -- ) >r
 r@ x @ r@ y @ at-xy  r@ text @ r> len @ type ;
 button defines draw

:noname ( addr u o -- ) >r
 0 r@ x ! 0 r@ y ! r@ len ! r> text ! ;
 button defines init

:)  I would be tempted here to rename "defines" to   's  so the code would read:

 

button 's draw

button 's init 

 

Maybe that's just too cute.

 

 

  • Like 1
Link to comment
Share on other sites

5 hours ago, Willsy said:

Agree 100%. I'm currently developing a serial comms driver for old RTUs (like a PLC) in C#. The object model I've come up with is simple, and reflects how the RTU works. A very simple class structure. I'm also writing an emulator for these RTUs as the hardware is obsolete. Again, C#, but running in Linux. Lots of fun.

 

I've already been asked why I'm not using abstract classes and methods etc. The answer? I don't need them.

 

At the company I consult for, the developers are young and VERY sharp. Really clever guys. They laugh at me because I tell them to STOP WRITING CODE! Try and spend an hour or so every day REMOVING CODE if you can! Do not add unnecessary layers of abstraction. This isn't a dick-measuring contest. Just stop it. Write what you need to get the job done ONLY.

 

That's probably why I liked Forth so much. That's the entire ethos behind the language.;)

To your point on simplicity, over on REDDIT Forth, a guy asked if there was a library like ncurses for Forth.

I gave him my code for VT100 which makes a little mark-up language to control the terminal.

https://github.com/bfox9900/CAMEL99-ITC/blob/master/LIB.ITC/VT100.FTH

https://github.com/bfox9900/CAMEL99-ITC/blob/master/LIB.ITC/VT100COLR.FTH

 

Another guy jumped in and said Forth was a control language and you shouldn't be used for such things. He should link to ncurses and blah blah.

Meanwhile the original OP had already taken my stuff and made simple windowing system for his terminal! :)  :) 

Forth for the win.

 

I remember reading about NEON which I think Dick had a hand in writing. It was brilliant. It came out when OOP was the rage.

It gave rise to Yerks Forth for Mac on 68000 which was used at the Yerks observatory and later that was re-written to a native code compiler called MOPS Forth which I understand has come back for Mac on ARM.

The author Mike Hore had no taste for Intel code. 

 

There is also the FMS library by Doug Hoffman, for VFX, which brings a kind of SmallTalk to Forth.

http://soton.mpeforth.com/flag/fms/index.html

  • Like 2
Link to comment
Share on other sites

Down the Rabbit Hole and through the looking glass

 

I went down the rabbit hole on this one. I have never done anything like this before in Forth. 

I continued down the road of "iterators" adding MAP and FILTER to Forth for integer data.

These two are interesting because of this:

"map() loops over the items of an input iterable (or iterables) and returns an iterator that results from applying a transformation function to every item in the original input iterable"

 

map() in Python does not change the original data but makes a new "iterable" with a one to one relationship to the original data but each cell is changed.

filter() produces a new iterable data that could be a different size than the original.  Uhoh!  Dynamic data required.

 

I wanted to make this work without digging into the complexity of ALLOCATE FREE RESIZE etc. so I just used HERE and comma for testing. :)

I created a new kind of array that writes into unused dictionary space at HERE, by using the comma operator.

The first cell in the array is a link to the previous array or zero. I don't use that field yet.

The 2nd cell is the number of bytes used by the array. These seem to be interesting in their own right. 

The word DATA: is a "namer" for one of these arrays and when the name is invoked it returns an (addr,size) pair.

 

Spoiler

DECIMAL
\  ** EXPERIMENTAL DATA STRUCTURE ***
\    COUNTED ARRAYS IN FREE MEMORY

\ creator for counted arrays in empty memory
\ Data structure has a 2 cell header
\ Cell 1: LINK - to previous array or zero
\ Cell 2: size - SIZE in bytes of this array
\ Data .....

VARIABLE LAST[]
: DLINK,    HERE LAST[] @ , LAST[] ! ;

: CLEAN    LAST[] @ IF  LAST[] @ DP !  THEN ;

: [[   ( -- addr )
       DLINK,  HERE  0 ,  ; \ create header, return the address

: ]]   ( -- addr )
       HERE OVER - 2- OVER ! ; \ end array, fill in the size

: SIZE ( addr -- addr size) DUP CELL+ SWAP @ ; \ size is in BYTES
: LINK ( addr -- 'array[] | 0) 2- @ ;

\ a word to name a counted array and return the (addr,len) pair
: DATA:   CREATE
          ( addr --) ,
          DOES> @ SIZE ( -- addr size)  ;

 

 

Of course this cannot go on forever without some kind cleaner so I just used MARKER for testing purposes to just pull the memory back to a specific point. 

Since these arrays are not always named this is better than using FORGET.

 

Surprise

After version one I realized that I had duplicated the iteration code three times which was stupid so that is now factored out as FOREACH.

There is some complexity in the words ACTION, a variable that holds an XT and ACTOR a defer word that runs the ACTION and other code as needed inside the FOREACH loop.

The comments try to explain it. 

 

Using FOREACH and ACTION made the definitions of MAP and FILTER  much smaller. 

Using the iterator to make more iterators made a difference. :)


: MAP ( initial addr size xt-- addr' size')
  [[ >R  ['] MAPPER IS ACTION  FOREACH  R> ]] SIZE ;

: FILTER ( initial addr size xt-- addr' size')
 [[  >R  ['] FILTRATION IS ACTION  FOREACH  DROP R> ]]  SIZE ;

The screen captures show how this works.  I like this concept but of course making a complete system to handle other data types would start to balloon in size.

It was eye opening to me and makes me appreciate the array languages like APL a lot more. 

 

 

The entire file with test code EDIT: Replaced with newer version

Spoiler

\ iterators.fth   MAP REDUCE FILTER for cell data  16JUL2022  B Fox

INCLUDE DSK1.TOOLS
INCLUDE DSK1.VALUES
INCLUDE DSK1.DEFER
INCLUDE DSK1.MARKER

DECIMAL
\  ** EXPERIMENTAL DATA STRUCTURE ***
\    COUNTED ARRAYS IN FREE MEMORY

\ creator for counted arrays in empty memory
\ Data structure has a 2 cell header
\ Cell 1: LINK - to previous array or zero
\ Cell 2: size - SIZE in bytes of this array
\ Data .....

VARIABLE LAST[]
: DLINK,    HERE LAST[] @ , LAST[] ! ;

: CLEAN    LAST[] @ IF  LAST[] @ DP !  THEN ;

: [[   ( -- addr )
       DLINK,  HERE  0 ,  ; \ create header, return the address

: ]]   ( -- addr )
       HERE OVER - 2- OVER ! ; \ end array, fill in the size

: SIZE ( addr -- addr size) DUP CELL+ SWAP @ ; \ size is in BYTES
: LINK ( addr -- 'array[] | 0) 2- @ ;

\ a word to name a counted array and return the (addr,len) pair
: DATA:   CREATE
          ( addr --) ,
          DOES> @ SIZE ( -- addr size)  ;

\ Explanation:
\ ACTOR holds the execution token (XT) for the operation that will be done
\ to each data element. The XT is passed to ACTOR by the programmer.
VARIABLE ACTOR

\ ACTION is vector that holds code that does the ACTOR XT  and it also
\ runs EXTRA code that makes the difference between REDUCE, MAP or FILTER
DEFER ACTION

\ this code is run in a loop by FOREACH
: REDUCER  ( initial n -- n)  ACTOR PERFORM ; \ reduce and return a value

: MAPPER   ( initial n -- )  REDUCER  ,  ; \ reduce and compile value

: FILTRATION ( initial n -- ) \ reduce with conditional value compilation
     2DUP SWAP REDUCER ( -- n ? )
     IF  ,       \ if true compile n into array
     ELSE DROP   \ otherwise throw it away
     THEN  ;

DECIMAL
\ * changed argument order to allow sequential calls to foreach
: FOREACH  ( addr size initial xt-- n) \ primary iterator
         >R -ROT R>  \ ugly but it works
         OVER 0= ABORT" FOREACH: size=0"
         ACTOR @ >R  \ allow variable to be re-entrant
         ACTOR !     \ set the XT of action on each cell
         BOUNDS DO  I @ ACTION   2 +LOOP
         R> ACTOR !
;

: REDUCE  ( addr size inital xt-- n)
         ['] REDUCER IS ACTION  FOREACH  ;

\ Use REDUCE to do something
: ..  ( addr size -- ) 0 ['] .  REDUCE DROP ; \ print array signed
: U.. ( addr size -- ) 0 ['] U. REDUCE DROP ; \ print array un-signed
: SUM     ( addr len -- n) 0 ['] + REDUCE ;
: AVERAGE ( addr len -- n) DUP 2/ >R  SUM  R> / ;

\ MAP returns a new array as output
: MAP  ( addr size xt-- addr' size)
  0 SWAP \ add the intial value. Not used but need by FOREACH
  [[ >R   ['] MAPPER IS ACTION  FOREACH DROP  R> ]] SIZE ;

\ filter returns a new array as output that might be a different size
: FILTER  ( addr size inital xt-- addr' size')
 [[  >R   ['] FILTRATION IS ACTION  FOREACH  DROP R> ]]  SIZE ;
\ ======================= ITERATORS ENDS ===========================

\ TEST DATA
[[  20 CELLS ALLOT  ]] DATA: B[] ( un-initialzed data example)
[[  1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , -1 , -2 , ]] DATA: A[]

\  TEST CODE
A[] ..
\ using REDUCE
A[] SUM .
A[] AVERAGE .

\ MAP * changed to require an initial value. '0' is most common.
A[]  ' 8* MAP ..
A[]  ' 2+  MAP ..

\ FILTER
\ filter functions must have a stack diagram ( n n -- ?)
\ The flag determines if the data will be added to the output or not.
 A[] 7 ' < FILTER ..
 A[] 4 ' > FILTER ..
 A[] 9 ' = FILTER ..

\ using the initial value 1, we can test for ODD/EVEN
: EVEN? ( initial n -- n ?)  AND 0= ;
: ODD?  ( initial n -- n ?)  AND 0> ;
 A[] 1 ' EVEN? FILTER ..

\ feed a filter into another filter :)
 A[] ' 2* MAP  3 ' > FILTER  16 ' < FILTER 2DUP CR .. CR SUM . 

 

 

 

Classic99 QI399.046 2022-07-16 6_14_59 PM.png

Classic99 QI399.046 2022-07-16 6_16_06 PM.png

Edited by TheBF
Update code file
  • Like 3
Link to comment
Share on other sites

One minor enhancement to these iterators and then I am done for now.

EDIT:  Latest version of the code with test examples is in the previous post.

 

I realized that I could adjust the order of the arguments in FOREACH so that it was cleaner to feed the results from one iterator to another one.

(no stack juggling at the programmer level)

 

The side effect is that that the "initial" value parameter must be in the ACTION word before the XT. 

So SUM becomes:

: SUM     ( addr len ) 0 ['] + REDUCE ;

I couldn't think of a reason for an initial value for MAP so I add a 0 in the definition of MAP (and DROP it later) so the programmer doesn't have to.

: MAP  ( addr size xt-- addr' size)
  0 SWAP \ add the intial value. Not used but need by FOREACH
  [[ >R   ['] MAPPER IS ACTION  FOREACH DROP  R> ]] SIZE ;

This makes MAP look the same as before

A[]  ' 8* MAP ..
A[]  ' 2+ MAP ..

 

The other benefit that changes is the syntax of FILTER becomes more Forth appropriate in terms of argument order. 

 A[] 7 ' < FILTER ..
 A[] 4 ' > FILTER ..
 A[] 9 ' = FILTER ..

And so now this is a VALID expression :) 

 A[] ' 2* MAP  3 ' > FILTER  16 ' < FILTER 2DUP CR .. CR SUM . 


 

 

Classic99 QI399.046 2022-07-17 5_30_02 PM.png

  • Like 2
Link to comment
Share on other sites

47 minutes ago, GDMike said:

BTW, what is the definition of include for how you load a routine from disk?

Good question.

 

So INCLUDE <file-path>  is based on the reversed polish word that works like this:    S" DSK1.MYFILE" INCLUDED

 

So INCLUDED is a bit complicated because it can be "nested" ( a file can include another file) but here is all the source code.

https://github.com/bfox9900/CAMEL99-ITC/blob/master/SRC.ITC/FILESYX2.HSF

 

The neat thing is to make a command that parses the file name and loads the file we just do this:

 

: INCLUDE     PARSE-NAME  INCLUDED ;   ( PARSE-NAME outputs an address,length pair ie: a string) 

 

With that you can just type INCLUDE DSK1.TOOLS  and compile the tools into the system.

 

In my system the definition for INCLUDE is compiled when the system boots. :) 

https://github.com/bfox9900/CAMEL99-ITC/blob/master/SRC.ITC/SYSTEM.FTH

 

 

 

 

  • Like 3
Link to comment
Share on other sites

What I did, let me know if I'm ok with it, but I Did this..in TF

I first created a blank image of 50 pages, blocks.

It's called DISK1 and located on DISK 4.

I added a line in my essentials boot block like this.

: INDEX1 S" DSK4.DISK1" USE 30 LIST ;

 I figured I'd set up a text page starting at

 block 30 that would display a listing of WORDS that are defined (or will be in the future) for loading..

 

 

 

Link to comment
Share on other sites

Important to remember that you working BLOCKS on TF. They live in a file but are traditional Forth BLOCKS just the same.

My system uses text files by default. Blocks can be added with... you guessed it... INCLUDE DSK1.BLOCKS  :) 

  • Like 3
Link to comment
Share on other sites

38 minutes ago, TheBF said:

Important to remember that you working BLOCKS on TF. They live in a file but are traditional Forth BLOCKS just the same.

My system uses text files by default. Blocks can be added with... you guessed it... INCLUDE DSK1.BLOCKS  :) 

Yes, I noticed that you're includes have been mostly text files. For what I see, for me in this case, I'll just use my blocks files for storing little routines for testing etc...

 

I'm sure the blocks take up more space than text files would.

 

I'm just limiting my blocks to 50 pages inside the block file.

That way, I can quickly take notice of what they are if I see them in a directory list..

 

Edited by GDMike
  • Like 2
Link to comment
Share on other sites

I read something on Hacker News and I had to try it. 

This made me laugh out loud.

 

Just when you get used to reverse polish notation... :)

This might hurt your Forth head.

 

image.png.27e8dc3d0626bc2a535b7aa665a599f8.png

 

 

Spoiler

INCLUDE DSK1.STACKS

20 LIFO: XSTK  \ a stack 

: (      ' XSTK PUSH ;
: )     XSTK POP EXECUTE ;

 

 

  • Like 1
Link to comment
Share on other sites

33 minutes ago, TheBF said:

I read something on Hacker News and I had to try it. 

This made me laugh out loud.

 

Just when you get used to reverse polish notation... :)

This might hurt your Forth head.

 

image.png.27e8dc3d0626bc2a535b7aa665a599f8.png

 

 

  Hide contents


INCLUDE DSK1.STACKS

20 LIFO: XSTK  \ a stack 

: (      ' XSTK PUSH ;
: )     XSTK POP EXECUTE ;

 

 

 

 Nah! It is precisely the Forth head that won’t hurt!

 

...lee

  • Like 1
Link to comment
Share on other sites

Strange coincidence after my posts about high level functions Forth

 

Over on Reddit Forth this article was posted that makes the case that Forth is useful in this world of functional programming written way back in 1993.

( I may be a member of the dis-functional programming group)  :-) 

 

I am pretty sure the REDDIT poster wasn't reading my posts here. 

 

Linear Logic and Permutation Stacks--The Forth Shall Be First

https://www.plover.com/~mjd/misc/hbaker-archive/ForthStack.html

 

---

On another note:

While watching a video on functional programming I learned that there is an operator called "after" that looks like a circle. 

 

It's used something like:

 x = func3() after func2 after func1    ( I am paraphrasing )

 

This made me laugh since if you just switched to RPN the "AFTER' operator is not even needed and everything reads left to right like English. :) 

func1()  func2()  func3()  TO X 

 

Oh well. I don't think I can convince the world to toss infix and prefix notation from Atariage.com

  • Haha 1
Link to comment
Share on other sites

I have never liked the name of the word :NONAME in Forth 94 but I just realized it could do something useful in my little system.

(LISP has LAMDA for this kind of thing and Forth could have stolen that name or maybe  :LAMDA  ? ) 

 

:NONAME lets you make a word definition that has of course no name. Instead it just leaves behind the "execution token" (the CFA) of the un-named word.

 

Here is :NONAME in Camel99 Forth: (I should probably combine these now that it is working) 

\ (:noname) from studying gforth
 : (:NONAME) ( -- ) ['] DOCOL @ COMPILE,  HIDE  ]  ;
 
 : :NONAME  ( -- xt) HERE  !CSP   (:NONAME)  ;

Here is what I just realized.

When you save the system as an executable program, with the SAVESYS command, you have to provide the EXECUTION token of some Forth code that will run when the system starts.

 

Typically you make a short definition that must have the WARM word in it to initialize the Forth system and then the word (or words) you want to run.

For example if the start word to run your program was called BRICKS this would work:

: START    WARM  BRICKS ;

Then do:

' START SAVESYS DSK1.MYCOOLPROG

 

You can do the same thing without creating the START word like this:

:NONAME   WARM  BRICKS ;  SAVESYS DSK1.MYCOOLPROG 

It removes the bytes used for the START definition header, that have no use in the program, so why not.

 

  • Like 2
Link to comment
Share on other sites

Just now, TheBF said:

Here is :NONAME in Camel99 Forth: (I should probably combine these now that it is working) 


\ (:noname) from studying gforth
 : (:NONAME) ( -- ) ['] DOCOL @ COMPILE,  HIDE  ]  ;
 
 : :NONAME  ( -- xt) HERE  !CSP   (:NONAME)  ;

 

Actually no I should not combine them because (:NONAME) is a nice common factor as I learned from GForth.

 

Here is the definition of colon :) 

( X:  and ;X  are aliases in the cross-compiler so I don't get to confused when I have to re-define the colon with a colon) ?

X: :         !CSP  HEADER (:NONAME)  ;X

(There is a colonoscopy joke in here somewhere but I can't find it right now) 

 

Or how about this:

Did you hear about the Forth programmer who redefined his colon and disappeared up the orifice of his own fundament? :) 

  • Haha 3
Link to comment
Share on other sites

  • 3 weeks later...

While looking around for some information on local variables in Forth I found this huge repository of Forth related information maintained by Anton Ertl I believe at the Technical University of Vienna 

 

Index of /forth (tuwien.ac.at)

 

One of the cool things an archive of Forth Dimensions magazines and there is a quicksort done as one definition.

Index of /forth/forth-dimensions (tuwien.ac.at)

 

I slightly modified it for Camel99 Forth.  This version sorts an array of characters. 

\ FORTH DIMENSIONS jAN/FEB 1984 Vol5,No.5  QUICK SORT BY MARK PERCEL
\ Ported to Camel99 Forth with small modification  Brian Fox

INCLUDE DSK1.TOOLS

\ harness for CAMEL99
: 2OVER    3 PICK 3 PICK ;

VARIABLE PIVOT \ changed from MIDDLE to PIVOT

\ Replaced ROT ROT with -ROT
\ Replaced NOT with 0=
\ Replaced MYSELF with RECURSE
: EXCHANGE  ( adr adr -- ) 2DUP C@ SWAP C@ ROT C! SWAP C! ;

: QSORT ( start len -- )
     OVER +  ( 'start 'end )
     2DUP 2DUP  OVER - 2/ + C@ PIVOT !
     BEGIN
        SWAP BEGIN DUP C@ PIVOT @ < WHILE 1+ REPEAT
        SWAP BEGIN DUP C@ PIVOT @ > WHILE 1- REPEAT
        2DUP > 0= IF 2DUP  EXCHANGE 1 -1  D+  THEN
        2DUP > ( loop until partitions cross)
     UNTIL SWAP ROT
     2OVER 2OVER - -ROT -  < IF 2SWAP THEN
     2DUP < IF RECURSE  ELSE 2DROP THEN
     2DUP < IF RECURSE  ELSE 2DROP THEN ;

 

  • Like 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...