Forth für blutige Anfänger

1 Infos zur Video-Konferenz

2 Die virtuelle Forth Umgebung

  • modernen Browser (Firefox, Chrome, Brave etc) benutzen
  • URL: https://free.forth-schulung.de:9090/system/terminal
  • Benutzer: forth
  • Passwort: forth
  • eigenen Namen für die Forth-Umgebung vergeben. Soll nach einem Re-Load der Webseite die gleiche Forth-Umgebung wieder aufgerufen werden, so muss der selbe Name wieder eingegeben werden

3 Einführung in Forth

3.1 Warum Forth?

Forth ist interaktiv

3.2 Warum Forth?

Forth ist leichtgewichtig

3.3 Warum Forth?

  • Forth ist auf sehr vielen Rechnern und Mikrokontrollern verfügbar
  • … einmal Gelerntes kann weiterverwendet werden
  • … und das seit mehr als 30 Jahren

3.4 Warum Forth?

  • Forth ist erweiterbar und veraltet nicht
  • … fehlende Funktionen werden 'nachgerüstet'
  • … Multitasking, Objekte, funktionale Programmierung, …

4 Forth Crashkurs

4.1 Eine kurze Einfuehrung in Forth

  • Syntax:
    • Ein Wort ist eine Sequenz von beliebigen Zeichen (außer Leerzeichen).
    • Ein "WORT" ist die Funktionseinheit eines Forth Systems (vergleichbar mit Prozeduren oder Funktionen)
    • Wörter werden durch Leerzeichen voneinander getrennt.
  • gültige Forth Wörter:
    • wort
    • !@#$%^&*()
    • 1234567890
    • BLIT

4.2 Forth-Syntax

  • Forth Syntaxregeln sind einfach
    • Eingaben werden durch Leerzeichen getrennt …
    • … und von links nach rechts ausgewertet (immer)
  • Eingaben sind entweder Forth-Worte, oder Zahlen
    • … das sind schon die kompletten Syntaxregeln :)

4.3 Sicherheit

  • Wichtig: in Forth gibt es nur die Sicherheitsnetze, welche der Programmierer selbst anbringt
    • d.h. normalerweise KEINE!
  • Forth steht dem Programmierer nicht im Weg

4.4 Was ist Forth

  • Ein ultimativ erweiterbares Programmiersystem
  • wenig Regeln, keine Syntax
  • der Programmierer hat volle Kontrolle über Forth …. und das System

4.5 Stack

  • Das auffälligste Merkmal von Forth ist der Stack (Stapelspeicher). Wenn man eine Zahl eintippt, wird diese auf den Stack geschoben.
  • Der Stack ist ein LIFO (Last In, First Out) Speicher.
  • Mit .s kann man sich den Inhalt des Stacks anschauen.
1 2 3 .s

4.6 "." (Dot)

  • Mit . kann man die Zahl des jeweils obersten Stackelements (TOS, Top of Stack) ausdrucken
1 2 3 . . . [RTN]

4.7 Arithmetik

  • Die arithmetischen Operatoren +, -, *, / und mod arbeiten immer auf den obersten beiden Stack-Elementen (Postfix-Schreibweise):
2 2 + . [RTN]
2 1 - . [RTN]
7 3 mod . [RTN]

4.8 Arithmetik 2

  • Klammern sind überflüssig, die Reihenfolge der Ausführung und die Operanden ergeben sich eindeutig durch die Anordnung der W<F6>rter:
3 4 + 5 * .
3 4 5 * + .

4.9 Stackkommentare

  • Bei der Dokumentation von Forth W<F6>rtern werden die <C4>nderungen des Stacks in Klammern angedeutet
  • Beispiele:
. ( n -- )
+ ( n n - n )

4.10 SWAP

  • swap vertauscht die beiden obersten Elemente des Stacks
SWAP ( x y -- y x )

4.11 DUP

  • dup erstellt vom obersten Stack-Element eine Kopie
DUP ( x -- x x )

4.12 OVER

  • =over legt eine Kopie des 2ten Stackeintrages auf den Stack
OVER ( x1 x2 - x1 x2 x1 )

4.13 ROT

  • rot rotiert die obersten 3 Stackelemente
ROT ( x1 x2 x3 -- x2 x3 x1 )

4.14 -ROT

  • -rot rotiert die obersten 3 Stackelemente (andersherum)
-ROT ( x1 x2 x3 -- x3 x1 x2 )

4.15 Kommentare

\ das ist ein Kommentar bis zum Zeilenende
( das ist ein Kommentar bis zur schliessenden Klammer ) .s
.( Dieser Kommentar wird bei der Ausführung ausgegeben )
  • \, ( und .( sind normale Forth-Wörter und müssen daher durch Leerzeichen vom nachfolgenden Text getrennt werden.

4.16 Marker

  • marker ist ein einfacher Weg, um definierte Funktionen zu löschen
  • der Marker wird an beliebiger Stelle definiert
  • wird der Marker ausgeführt, so werden alle Funktionen die nach dem Marker definiert wurden, gelöscht
marker --mymarker--

4.17 Wörter definieren

  • Definitionen sind das äquivalent zu Prozeduren in anderen Programmiersprachen:
: quadrat ( n -- n^2 ) dup * ;

5 quadrat .

7 quadrat .

4.18 Wörter definieren (2)

  • : startet die Definition,
  • quadrat ist der Name des neuen Forth Wortes.
  • Der Kommentar beschreibt den Stack-Effekt
  • Die Worte dup * werden nicht ausgeführt, sondern in die Definition hineinkompiliert.
: quadrat ( n -- n^2 ) dup * ;

4.19 Wörter definieren (3)

  • Das neu definierte Wort kann man genauso verwenden wie ein "eingebautes" Wort, man kann es natürlich auch in weitere Definitionen einbauen:
: zur-dritten ( n -- n^3 ) dup quadrat * ;

-5 zur-dritten .

: zur-vierten ( n -- n^4 ) quadrat quadrat ;

3 zur-vierten .

4.20 Wörter umdefinieren

  • Neue Wörter mit Namen von bestehenden Wörtern überlagern die bestehenden.
  • Neue Definitionen benutzen die neuen Wörter, schon eingebaute benutzen weiterhin die alte Definition.
: * + ;

5 quadrat . [RTN] 25

5 5 * . [RTN] 10

4.21 WORDS

  • listet alle Wörter, die Forth kennt, sortiert von jüngsten zu den ältesten Definitionen
WORDS ( -- )

4.22 Variable

VARIABLE ccc ( -- )
  • definiert eine Variable mit dem Namen ccc, oder

4.23 Variable (2)

VARIABLE BETRAG [RTN]
BETRAG ( -- addr )
  • legt die Speicheradresse der Variable auf den Stack

4.24 ! (STORE)

! ( x addr -- )
  • schreibe den Wert x in die Speicherstelle mit Adresse "addr"
200 BETRAG !
  • speichert den Wert 200 in die Variable BETRAG

4.25 @ (FETCH)

@ ( addr -- x )
  • legt den in der Speicherstelle "addr" gepeicherten Wert auf den Stack
BETRAG @ . [RTN] 200
  • liest den Wert der Variable BETRAG und gibt den Wert am Bildschirm aus

4.26 Speicherzugriff

  • @, !, +! funktionieren mit Variablen aber auch mit jeder anderen Speicherstelle sie sind die Forth-Entsprechung für (Basic) "PEEK" und "POKE" oder Pointer (C)

4.27 Kontrollstrukturen

Kontrollstrukturen können in Forth nur in Definitionen verwendet werden (nicht im interaktiven Modus). Eine IF-Struktur sieht so aus:

: abs ( n1 -- +n2 )
	dup 0 < if
		negate then ;
5 abs .
-5 abs .

4.28 Kontrollstrukturen (2)

  • if nimmt ein Flag vom Stack. Ist es ungleich 0 ("wahr"), wird der folgende Code ausgeführt, ansonsten zum zugehörigen then (oder else) gesprungen.
  • < vergleicht die beiden obersten Stackelemente und produziert ein Flag:
1 2 < .
2 1 < .
1 1 < .

4.29 Kontrollstrukturen (3)

  • weitere Vergleichsworte sind =, >, <>, <=, >= (nicht alle Kombinationen aus Typen und Vergleichsworten sind Standard Forth Wörter, können aber bei Bedarf einfach erzeugt werden).

4.30 Kontrollstrukturen (4)

Optional kann man auch einen ELSE-Abschnitt verwenden:

: min ( n1 n2 -- n )
	2dup < if
		drop
	else
		nip
	then ;
  • Anmerkung: Der Stackkommentar von 2dup ist ( n1 n2 -- n1 n2 n1 n2 )

4.31 Logische Operatoren

  • Ein true-Flag hat alle Bits gesetzt, ein false keines (F83/ANSI Forth):
hex true u. decimal true . false .
  • Anmerkung: jede Zahl ungleich 0 wird als logisch true interpretiert

4.32 Logische Operatoren (2)

  • Zum Verknüpfen von Flags kann man die Wörter AND, OR, XOR und 0= sowie INVERT verwenden. Dabei arbeiten AND, OR, XOR und INVERT bitweise, produzieren also nur aus wohlgeformten Flags wieder neue gültige Flags:
1 2 and .
1 2 or .
1 3 xor .
1 invert .

4.33 Logische Operatoren (3)

  • 0= dagegen nimmt beliebige Zahlen als Eingabe und produziert ein Flag.
1 0= .
  • Um aus einer (beliebigen) Zahl ein wohlgeformtes Flag zu machen, verwendet man 0<>:
1 0<> .

4.34 Logische Operatoren (4)

  • Die Eigenschaft alle Bits gesetzt kann verwendet werden, um =IF=s zu vermeiden:
: foo ( n1 -- n2 ) 0= if 14 else 0 then ;
0 foo .
1 foo .
  • das gleiche ohne IF
: foo ( n1 -- n2 ) 0= 14 and ;
0 foo .
1 foo .

4.35 Schleifen

  • Die einfachste Schleife ist die Endlosschleife:
: endless ( -- )
  0 begin
    dup . 1+
again ;

endless
  • BEGIN macht nichts (zur Laufzeit), AGAIN springt zurueck zum BEGIN.

4.36 Schleifen (2)

  • Eine Schleife mit einer Ausstiegsbedingung an beliebiger Stelle sieht so aus:
: log2 ( +n1 -- n2 )
 2 / 0 begin
    over 0>
 while
    1+ swap 2 / swap
repeat nip ;

7 log2 .
8 log2 .
  • WHILE konsumiert ein Flag; ist es gleich 0 (falsch), wird hinter dem REPEAT fortgesetzt. REPEAT springt zurück zum BEGIN, wie AGAIN.

4.37 Schleifen (4)

?DO nimmt zwei Zahlen vom stack ( n1 n2 -- ), und die Schleife zwischen ?DO und LOOP wird dann n1-n2 mal ausgeführt. Auf den Zähler der Schleife kann man mit dem Wort I zugreifen:

: fac ( n -- n! )
  1 swap 1+ 1 ?do
    i *
  loop ;

5 fac .

7 fac .

4.38 Ausgabe

Ein Zeichen wird ausgegeben mit 'emit'

97 emit
char A emit

'char' liest den nästen Buchstaben im Eingabestrom und legt dessen ASCII-Wert auf den Stack

4.39 Speicher

Speicher kann reserviert werden mit:

variable speicher 20 cells allot

Dies erzeugt ein Wort speicher, welches bei der Ausführung eine Adresse liefert, und reserviert an dieser Adresse Platz für 20+1 Zellen.

4.40 Speicher (2)

Mittels Adressarithmetik können wir auf einzelne Elemente zugreifen:

3 speicher 5 cells + !

Das Wort cells definiert die Wortgrösse der CPU und des Forth Systems

: cells 2 * ;  ( 8/16 bit CPU )
: cells 4 * ;  ( 32bit CPU )
: cells 8 * ;  ( 64bit CPU )

4.41 ' (TICK)

` ccc ( -- xt )

sucht Forth-Wort ccc und legt das execution token (xt) auf den Stapel

execute ( xt -- ?? )

führt das Wort, auf das der "xt" zeigt, aus. In Wort-Definitionen wird das Wort ['] verwendet.

4.42 Verzeichnis Wechseln

Im GNU/Forth kann das aktuelle Verzeichnis mit dem Wort set-dir geändert werden

s" ~" set-dir

4.43 Shell aufrufen

Im GNU/Forth können mit dem Wort sh Befehle an das darunterliegende Betriebssystem (hier Linux) gegeben werden

sh sh

ruft eine Shell auf

4.44 Editor aufrufen

Einige Forth-Systeme haben eingeaute Quelltext-Editoren. Bei GNU/Forth kann ein Forth-Editor nachgeladen werden, oder es kann ein Editor des Betriebssystems aufgerufen werden

: vi s" vi" system ;

definiert ein neues Wort vi, welches des VI-Editor aufruft. Das Wort system übergibt eine Zeichenkette an eine Shell des Betriebssystems.

4.45 Quelldatei einladen

Forth-Quelltext wird mit dem Wort include in das GNU/Forth geladen

include mysource.4th

5 Beispiel Anwendungen

5.1 Zahlen faktorieren

// print factors
:  factors  
   dup  2/  1+ 1 do 
      dup i mod 0= if 
       i swap then 
      loop ;

: .factors 
  factors begin 
     dup dup . 1 <> while 
	 drop 
     repeat drop cr ;

64 .factors
100 .factors
333 .factors

5.2 Fizz Buzz

// fizz buzz in Forth https://codereview.stackexchange.com/questions/56839/fizzbuzz-in-forth
// others: https://rosettacode.org/wiki/General_FizzBuzz#Forth
// more: https://www.reddit.com/r/Forth/comments/druotn/fizzbuzz_in_forth/
// Fizz Buzz https://en.wikipedia.org/wiki/Fizz_buzz

hex
1249 constant 3vec
0421 constant 5vec
4000 constant probe
3vec 5vec or constant 3or5vec
decimal

: bang
   cr 0 101 1
   do
      ?dup 0= if probe then
      3or5vec over and 0= if i . then
      3vec over and if ." fizz" then
      5vec over and if ." buzz" then
      1 rshift cr
   loop
   drop
;

5.3 Funktionen / Wörter als Eingabedaten

siehe Joel Spolsky: Can Your Programming Language Do This?

If your programming language requires you to use functors, you’re not getting all the benefits of a modern programming environment. See if you can get some of your money back.

Der Programmierer kann Forth (den Compiler, die Sprache) beliebig erweitern.

5.3.1 Java Script Version

function reduce(fn, a, init) 
{ 
    var s = init; 
    for (i = 0; i < a.length; i++) 
	s = fn( s, a[i] ); 

    return s; 
} 

function sum(a) 
{ 
    return reduce( function(a, b){ return a + b; },  
		   a, 0 ); 
}

5.3.2 Forth Version

// words (xt) as arguments on the stack
// Donated  to the public domain, J.L. Bezemer 2014
// response to Joel's  article  "Can  Your  Programming   Language   Do   This?"
// https://www.joelonsoftware.com/2006/08/01/can-your-programming-language-do-this/

: reduce >r over r> execute
	 rot 0 ?do
	    >r r@  execute  r>
	 loop drop  ;

: sum  [: dup >r +! r> ;]
       [: 0 swap ! ;] reduce ;

variable  A
12 34 45 3 A sum ? cr