Wiki Forth (WF) ist ein kleines CMS (Content Management System) in Forth. Der Name kommt daher, dass WF eine Spache benutzt, die denen der verschiedenen Wikis ähnlich sieht, also weitgehend normaler Text mit ein paar Steuerzeichen eingestreut. Bis auf ein paar historische Reste ist meine ganze Homepage in WF geschrieben. Die aktuelle Version von WF erzeugt W3C-konformes XHTML mit CSS. Es gibt zwar Browser, die das nicht so richtig können und trotzdem versuchen (Netzkappe 4 und Internet Exploder), die Seiten sind aber auch ohne CSS noch lesbar.
Ein CMS soll die Verwaltung von Web-Seiten erleichtern. Gerade wenn die Seiten etwas umfangreicher sind, und öfter hier und da etwas geändert wird, ist das sinnvoll. Natürlich sollen Inhalt und Form voneinander getrennt sein. Bei der Eingabe konzentriert man sich auf den Inhalt; die Seiten werden dann entsprechend dem gewählten Stil umgesetzt.
Zudem sollte ein CMS noch Verwaltungsfunktionen anbieten, etwa tote Links erkennen (innerhalb des verwalteten Bereichs), und andere automatisch generierbare Informationen zur Verfügung stellen. Eine sehr wichtige Funktion ist für mich z.B. die Größe einer verlinkten Datei anzugeben.
Die Steueranweisungen teilen sich in drei Gruppen auf:
Die globalen Informationen sind als Forth-Befehle am Anfang der Seite zu finden. Eine Seite wie diese enthält zumindest einige minimale Informationen über den Maintainer und dessen E-Mail-Adresse, das erste Dokumentdatum und das zugehörige Style-Sheet.
maintainer Bernd Paysan <bernd.paysan@gmx.de> created 11apr2004 css "wf.css"
Hat die Seite ein Menü, stellt man es hier zusammen. Menüs sind Links,
enthalten also einen Text und eine Datei, getrennt durch das
Pipe-Symbol |
. Aus dem Text kann man ein Icon generieren lassen.
up-toc Home|index.html top-toc 4stack|4stack.html top-toc b16|b16.html top-toc Gforth|gforth.html top-toc bigFORTH|bigforth.html this-toc Wiki Forth|wf.html sub-toc CMS|wf.html#CMS sub-toc Steueranweisungen|wf.html#ST
Die Icons generiert am besten der Gimp. Alle Icons werden im
Unterverzeichnis navigate
abgespeichert. Dort wird auch eine
Gimp-Batch-Datei erzeugt (nav.scm
), der nur noch das (gimp-quit 0)
als letzte Zeile fehlt. Den Gimp ruft also auf mit
echo '(gimp-quit 0)' >>navigate/nav.scm gimp -i --batch '(load "navigate/nav.scm")'
auf. Als Basis für die Generierung des Navigationsbuttons dient die
Datei navigation.scm
- die muss man in das scripts-Verzeichnis vom
Gimp kopieren. Als leeren Button verwendet die eine Datei
button.jpg
, deren Pfad in navigation.scm
drin steht. Als Font wird
der Blippo-Font aus dem Freefont-Packet verwendet. Man kann natürlich
auch einen der vielen bereits vorhandenen Button-Funktionen im Gimp
nehmen, aber Gimp-Programmierung ist jetzt ein anderes Thema.
Die eigentliche Seite wird mit dem Wort wf
eingeleitet. Dem folgt
die erzeugte HTML-Datei und der Seitentitel. wf scannt die
Eingangsdatei, bis es auf einen einzeln stehenden Punkt trifft; hier
endet die Seite.
wf wf.html "Wiki Forth"
Die Steueranweisungen für die Grobstruktur orientieren sich am
Emacs-Outline-Mode. Überschriften beginnen mit ein, zwei oder drei
Sternen (h1 bis h3 in HTML). Jedem Element kann man ein Label
zuordnen, indem man &&
label in die Zeile davor stellt. Horizonale
Leisten erzeugt man mit ---
. Untermenüs mit --
label, hier
werden die Sub-Menüs wieder erzeugt, und das aktuelle markiert.
Natürlich kann man auch Listen anlegen. <<
startet eine Liste, >>
beendet sie. Ob die Liste mit Punkten oder Nummern markiert wird,
entscheidet das Startzeichen: -
für Punkte, +
für Nummern. Die
Zeichen ?
und :
sind für descriptive Listen reserviert, wobei ?
einen Titel einleitet, und :
die eingerückte Erklärung.
Wenn die Struktur über mehr als einen Absatz gehen soll, hängt man
an's Startzeichen noch zwei <<
dran, und beendet das Ganze mit
>>
. Dabei sind natürlich wieder Unterstrukturen erlaubt.
Unformatierten ASCII-Text (für Listings) bindet man mit :code
.. :endcode
ein, oder mit :code-file
datei gleich eine ganze
Datei.
Implementiert werden werden diese Wörter alle in einer Wordlist namens
longtags
. Interpretiert werden sie, wenn sie am Anfang eines
Absatzes stehen. Steht kein verwertbares Wort am Anfang eines
Absatzes, wird er als ganz normaler Absatz gesetzt:
: section-par ( -- ) >in off bl sword longtags search-wordlist IF execute ELSE source nip IF >in off s" p" par THEN THEN ; : parse-section ( -- ) end-sec off BEGIN refill WHILE section-par end-sec @ UNTIL THEN ;
Innerhalb des Texts will man natürlich auch noch Variationen
verwenden. Etwa fette oder hervorgehobene Texte. Oder
Schreibmaschinenschrift
. Natürlich auch noch Links oder
. Oder im Link.
Diese Tags bestehen aus einzelnen Buchstaben am Anfang eines
Wortes. *
steht für fett, _
für unterstrichen, und # für
Schreibmaschinenschrift. Wer einen dieser reservierten Buchstaben
braucht, kann sie in eine Tilde ~
einfassen.
Bilder haben folgende Syntax: {
Text|
URL}
. Links sind fast
gleich aufgebaut: [
Text|
URL]
, allerdings darf der Text auch
Bilder enthalten. Ein paar Sonderzeichen direkt nach dem |
werden in
Optionen umgesetzt.
Links werden normalerweise automatisch mit einem Icon versehen. Icons
kann man für ganze URLS vergeben, oder für bestimmte Suffixe. Bei der
Suche nach dem Icon wird die URL stückweise nach .
durchsucht, und
der Rest als Dateiname im Unterverzeichnis icons
mit angehängtem
.*
gesucht. Das gefundene Icon (GIF, PNG oder JPEG) wird dann vor
den eigentlichen Link-Text gesetzt. Wer das verhindern will, setzt als
Options-Zeichen ein \
.
Längere Dateien (für Downloads) sollte man immer mit der Größe
versehen. Dafür ist die Link-Option %
vorgesehen. Die hängt die
Größe der Datei in Kilobytes (oder Megabytes, wenn's mehr als 2 sind)
hinter den Link (in Klammern). Natürlich kann man beide Link-Optionen
gleichzeitig verwenden, die Reihenfolge spielt dann keine Rolle.
Ich signiere Software-Downloads. Auch hier gibt es einen Automatismus:
Wenn es zum Link eine zugehörige .sig
-Datei gibt, wird der Link auf
diese Datei automatisch eingebunden.
Hier gibt es mehr Optionen, und die Reihenfolge spielt auch eine Rolle. Zunächst einmal kann man Bilder ausrichten.
l
, <
r
, >
c
, =
~
Normalerweise bekommen Bilder in Links immer einen Rahmen. Will man das verhindern (oder einen schmaleren Namen verwenden), muss man das natürlich auch deklarieren.
-
+
Allen Bildern wird automatisch die Größe mitgegeben, damit der Browser die Seiten schon komplett rendern kann, bevor die Bilder geladen sind.
Tabellen sind ein Thema für sich, schließlich erlaubt HTML sehr viel mit Tabellen anzustellen. Zur Zeit sind nur die wichtigsten Features implementiert. Die Formatierung lehnt sich dabei an LaTeX an.
Zunächst fangen Tabellen mit <|
Format [Border] an. Die
Format-Zeichen gelten für die jeweilige Spalte der Tabelle, und
bedeuten folgendes:
l , < | left alignment |
---|---|
r , > | left alignment |
c , = | left alignment |
Zeilen innerhalb der Tabelle sind eingerahmt, je nach Bedeutung:
+| .. |+ | Zeile enthält table headers (Kopfzeilen) |
---|---|
-| .. |- | Zeile enthält normale Tabelleneinträge |
=| .. |= | Zeile beginnt mit einer Kopfzeile, normale Tabelleneinträge folgen |
Einträge, die über mehrere Spalten gehen, benötigen eine eigene
Formatanweisung. Diese beginnt mit /
für Zellen, die mehrere Zeilen
überspannen, \
für mehrere Spalten. Die folgende optionale Ziffer
gibt die Zahl der Zeilen oder Spalten an (Default ist eine
Zeile/Spalte). Danach kann noch ein Formatzeichen kommen (siehe
oben). Es ist möglich, beide Formatanweisungen zu verwenden, wobei
zuerst /
und dann \
kommen muss.
Das kann man natürlich nur an einem Beispiel zeigen. Eine einfache Tabelle sieht etwa so aus:
<| clrr 1 +| Menge | Artikel | Preis | Summe |+ -| 3 | Äpfel | 0,30 | 0,90 |- -| 1 | Banane | 0,50 | 0,50 |- =| \3l Gesamtpreis | 1,40 |= |>
Menge | Artikel | Preis | Summe |
---|---|---|---|
3 | Äpfel | 0,30 | 0,90 |
1 | Banane | 0,50 | 0,50 |
Gesamtpreis | 1,40 |
Eine komplexe Tabelle zeigt alle Möglichkeiten:
<| rccl 2 +| /3 1 | 2 |\2c 40 |+ -| /3\2c center | 6 |- -| 8 |- -| /2 9 | 1 |- =| \2r 12 |/r 4 |= -| 1 | 2 | 3 | 40 |- -| 1234 | 1243 | 1234 | 1234 |- |>
gibt
1 | 2 | 40 | |
---|---|---|---|
center | 6 | ||
8 | |||
9 | 1 | ||
12 | 4 | ||
1 | 2 | 3 | 40 |
1234 | 1243 | 1234 | 1234 |
Man muss bei solchen komplexen Tabellen natürlich immer die ausgelassenen Tabellen im Kopf behalten.
Neben der eigentlichen Texteingabe braucht ein CMS natürlich noch mehr Komfort.
So sollen vielleicht bestimmte Schlüsselwörter immer als Link erscheinen (wie bei einem Wiki). Dazu gibt es autoreplaces. Diese Wörter werden zu Links expandiert, wann immer sie im Text auftauchen. Beispiel:
autoreplace ForthGesellschaft [Forth Gesellschaft|http://www.forth-ev.de/]
ersetzt jedes Vorkommen von "ForthGesellschaft" durch den entsprechenden Link Forth Gesellschaft .
Man kann sich natürlich noch komplexere Einträge vorstellen als einfache Links. Das braucht sowas wie eine einfache Datenbank. Zugriff hat man auf den Eintrag über das Schlüsselwort, eingefügt wird ein Text, der sich aus den einzelnen Feldern zusammensetzt. Ich verwende diese Datenbank an zwei Stellen: Für meine Changelog-Einträge in bigFORTH, und für Projektmitarbeiter auf den Intranetseiten in der Arbeit (die Mitarbeiter haben immer dieselben Daten, die man natürlich nicht für jedes Projekt nochmal eingeben will). Hier will ich nur die Changelog-Datenbank erklären. So ein Eintrag sieht etwa so aus:
change: 01sep2003 Version: 2.0.11 minos: 1.0.0 text: fixes a few bugs, like FORTH-WORDLIST being commented out, and problems with read only filesystems (like Knoppix). Also fixed OpenGL bindings, created a new setup script for Inno Setup 3.x, and a windows distribution. Fixed Makefile and configure process so that the windows version can be compiled from source with #./configure; make# when cygwin is installed. Bumped up MINOS version to 1.0.0 (there are little further changes to expect). .
Das Datum dient dabei als Schlüsselwort. Der Code dazu ist auch nicht so schwer. Zunächst einmal definiert man sich die Tabelle (aus Text-Feldern und Absätzen), die Ausgabe-Prozedur ist ein deferred Word, weil sie natürlich erst später definiert werden kann:
Defer .log ' .log 4 table: change: field: version: field: minos: par: text:
Das war noch einfach. Die Tabelle besteht aus vier Feldern mit den
Namen change:
, version:
, minos:
und text:
. Beim Aufruf wird
.log
ausgeführt. Was soll da passieren? Es sollen die Versionen mit
dem Wort ?rev ausgegeben werden, und der Text als solcher (ohne
besondere Methode; die Datenbank weiß, wie man Paragraphen
ausgibt). db-par
scannt durch den Text bis es an eine Punkt am
Zeilenanfang ankommt. Variablen stehen in %, werden sie nicht mit der
Standardmethode ausgegeben, muss der gewünschte Forth-Code mit |
abgetrennt werden (es ist auch mehr als ein Befehl erlaubt).
: .rev ( addr u -- ) s" b" tagged ; : ?rev ( addr u -- ) dup 0= IF 2drop EXIT THEN .rev ; : .entry-rest ( addr -- ) db-par The version %version:|?rev% from %change:|?rev% %text:% . ; : .entry-par ( addr -- ) LT -<< .entry-rest LT >> ; ' .entry-par is .log
Als Besonderheit gibt .last
noch den zuletzt definierten
Changelog-Eintrag etwas anders formatiert aus:
: .last ( -- ) last-entry @ db-par Current version is bigFORTH %version:|.rev%, Minos beta %minos:|.rev%, from %change:|.rev%. . last-entry @ .entry-rest ;