Coding-Schatzkästlein
Kleine Tips & Scripts zum Webdesign
Start > JavaScript > Library GfA2JS

JavaScript: GfA-Basic-Bibliothek "Library GfA2JS"

Zusammenfassung

Name:  Library GfA2JS
Aufgabe:  Stellt Funktionen der Programmiersprache GfA-Basic unter JavaScript zur Verfügung.
Benötigt:  JavaScript 1.1 (ungesichert), teilweise 1.2 (gesichert)
Beispiel:  fields.lst (GfA-Original) & fields.js (GfA2JS-"Konvertierung")
Download:  libgfa2.js

Nach nunmehr mehr als 20 Jahren GfA-Basic (zumal wenn man noch den 8-Bit-"Vorgänger" hinzurechnet) ist doch einiges an Programmen & Algorithmen zusammengekommen. Aus diesem Fundus mit vertretbarem Aufwand zu schöpfen, ist das Ziel dieser Bibliothek, die einige GfA-Befehle auch unter JavaScript zur Verfügung stellt. Aber auch für Programmierer anderer Basic-Dialekte, die anfangen, jetzt auch in JavaScript zu programmieren, dürfte interessant sein, wie die Funktionalität einzelner Basic-Befehle in JavaScript nachgebildet werden kann. Und auch reine JavaScript-Programmierer werden vielleicht Freude an der einen oder anderen Funktion haben, die es als native Methode nicht gibt (z.B. die kombinatorischen Funktionen, vereinzelt auch trigonometrische Funktionen).
Zwar stößt eine solche Umsetzung natürlich an die Grenzen, die JavaScript als Nicht-Universal-Programmiersprache begleiten, aber ich wollte versuchen, möglichst viel von der Syntax zu retten.

Alle GfA-Befehle sind in Großschrift und die Variablennamen in Kleinschrift (Deflist 6), da kleine Variablennamen eher üblich sind und die Funktionen auch nicht mit eventuell existierenden JavaScript-Befehlen kollidieren sollten (JavaScript arbeitet case-sensitive). Da zum einen der JavaScript-Befehl typeof Anwendung findet, zum anderen das $-Zeichen für Funktionen mit Strings als Ergebnis (und auch für String-Variablen selbst) aus Kompatibilitätsgründen beibehalten wurde ($ ist in JavaScript 1.0 ein unerlaubtes Zeichen), benötigt die Bibliothek JavaScript 1.1 und sollte deswegen nur als externe Datei von der HTML-Seite hinzugeladen werden (<script type="text/javascript" language="JavaScript" src="libgfa2.js"></script>).

Bislang umgesetzte Befehle (optionale Parameter in []):

  • Ein-/Ausgabebefehle
    • INPUT (keine identische Umsetzung: s.u.)
    • PRINT (keine identische Umsetzung: s.u.)
    • SPC(size)
  • Textoperationen
    • INSTR(text,string[,pos])
    • LEFT$(text[,num])
    • LEN(text)
    • MID$(text,pos[,num])
    • PRED (keine identische Umsetzung: s.u.)
    • RIGHT$(text[,num])
    • RINSTR(text,string[,pos])
    • SUCC (keine identische Umsetzung: s.u.)
  • String-Formatierung
    • SPACE$(size) (erweiterte Funktionalität: s.u.)
    • STRING$(size,data) (nur identisch wenn data ein String: s.u.)
    • TRIM$(text)
    • UPPER$(text)
  • Mathematische Operationen
    • ADD(num1,num2)
    • DIV(num1,num2)
    • MOD(num1,num2)
    • MUL(num1,num2)
    • SUB(num1,num2)
  • Numerische Funktionen
    • ABS(num)
    • EVEN(num)
    • FIX(num)
    • FRAC(num)
    • INT(num)
    • ODD(num)
    • PRED(data) (nur identisch wenn data eine Zahl: s.u.)
    • ROUND(num[,pos])
    • SGN(num)
    • SQR(num)
    • SUCC(data) (nur identisch wenn data eine Zahl: s.u.)
    • TRUNC(num)
  • Kombinatorische Funktionen
    • COMBIN(n,k)
    • FACT(num)
    • VARIAT(n,k)
  • Trigonometrische Funktionen
    • ACOS(radian)
    • ASIN(radian)
    • ATN(radian)
    • COS(radian)
    • COSQ(degree)
    • DEG(radian)
    • RAD(degree)
    • SIN(radian)
    • SINQ(degree)
    • TAN(radian)
  • Vergleichsoperationen
    • MAX(data1[,data2[,data3[, ...]]]) (nur Zahlenwerte!)
    • MIN(data1[,data2[,data3[, ...]]]) (nur Zahlenwerte!)
  • Zufallswert-Erzeugung
    • RAND(num)
    • RANDOM(num)
    • RND()
  • Datenumwandlung
    • ASC(text) (erst ab JavaScript 1.2)
    • BIN$(num[,size])
    • CFLOAT(num)
    • CINT(num)
    • CHR$(num) (erst ab JavaScript 1.2)
    • HEX$(num[,size])
    • OCT$(num[,size])
    • STR$(num[,size[,real]])
    • VAL(text)
  • Programmkontrolle
    • TIMER (keine identische Umsetzung: s.u.)
  • Interaktion
    • ALERT (keine identische Umsetzung: s.u.)
  • Konstante
    • FALSE
    • TRUE
    • PI

Folgende Befehle haben keine identische Syntax:

  • ALERT icon,text,button,buttontext,Variable -> Variable=ALERT(icon,text,button,buttontext)
  • INPUT text,Variable -> Variable=INPUT(text)
  • PRED(text) -> PRED(text,true) (erst ab JavaScript 1.2)
  • PRINT data; -> PRINT(data)
  • PRINT data -> PRINTLN(data)
  • PRINT #channel,data; -> PRINT(data,channel)
  • PRINT #channel,data -> PRINTLN(data,channel)
  • STRING$(size,num) -> STRING$(size,num,true) (erst ab JavaScript 1.2)
  • SUCC(text) -> SUCC(text,true) (erst ab JavaScript 1.2)
  • TIMER -> TIMER()

Folgender Befehl hat eine erweiterte Funktionalität:

  • SPACE$(size) (optional: SPACE$(size,true) für HTML-Leerzeichen)

Der zusätzliche Parameter true (z.B. in STRING$(size,num,true)) existiert in GfA-Basic nicht. Er muß gesetzt werden, wenn der Routine als Hauptparameter entweder eine Zahl oder eine Zeichenkette übergeben werden kann (JavaScript kann nur bedingt den Unterschied zwischen der Zahl 42, dem String "42" und dem Sinn des Lebens erkennen ;-) - überhaupt nicht, wenn es sich um eine GfA-Hexadezimal-, Binär- oder Oktalzahl handelt). Entscheidend bei welcher Konstruktion das true übergeben werden muß, war die - aus meiner Sicht - typischere Verwendung des Befehls (so wird bei STRING$() meistens eine Zeichenkette übergeben - also muß die Zahl gesondert ausgewiesen werden, bei SUCC() ist es umgekehrt).

Bei SPACE$ sorgt ein zusätzlicher Parameter true dafür, daß kein normales Leerzeichen, sondern ein festes HTML-Leerzeichen (&#160;) ausgegeben wird (mehrere normale Leerzeichen werden in HTML ja zu einem zusammengefaßt) . Bei SPC() werden hingegen immer feste HTML-Leerzeichen ausgegeben, da dieser Befehl explizit für die Bildschirm-Verwendung gedacht ist.

Die Funktion PRINT(data,channel) entspricht einem PRINT #channel,data;, wobei der channel optional ist und die Nummer eines Frames bezeichnet, in den die Daten ausgegeben werden sollen. PRINTLN(data,channel) arbeitet genauso, gibt aber zum Schluß einen Zeilenabschluß aus (entspricht also dem PRINT ohne abschließendes Semikolon).

Der Aufruf TIMER() entspricht dem GfA-Befehl TIMER und gibt die Systemzeit ebenfalls in 1/200stel-Sekunden aus (nur liegt der Startpunkt des Systems auf dem 1.1.1970).

Die Benutzung von ALERT() ist zwar von der Syntax her ähnlich, aber die mangelnde Flexibilität der JavaScript-Alerts behebt sie natürlich nicht.

Achtung: Wenn GfA- und JavaScript-Syntax gemischt wird, ist zu beachten, daß Stringfunktionen in GfA-Basic (auch bei den hier vorgestellten Umsetzungen) ab 1 zählen (INSTR("ABC","A") ergibt 1), während JavaScript ab 0 zählt ("ABC".indexOf("A") ergibt 0)! Entsprechend wird auch bei nicht gefundener Zeichenkette der Rückgabewert verringert (INSTR("ABC","D") ergibt 0, "ABC".indexOf("D") ergibt -1).

Die Begrenzung der Zahlenbereiche, Integerbereich auf 8 Byte inkl. Vorzeichen, also einem Zahlenbereich von -2147483648 ($80000000) bis 2147483647 ($7FFFFFFF) sowie Realbereich auf 13 Stellen, habe ich nicht nachgebildet. Die Integerfunktionen arbeiten hier mit genauso großen Zahlen wie die Funktionen der Realzahlen, bis zur zulässigen Größe die die jeweilige JavaScript-Version erlaubt. HEX$(2147483648) ist also gültig und ergibt 80000000 (genau wie HEX$(-2147483648), was aus Kompatibilitätsgründen beibehalten wurde). Die optionale Beschränkung der Ausgabegröße (0-32 bei BIN$(), 0-8 bei HEX$(), bzw. 0-11 bei OCT$()) wurde jedoch nicht vergrößert. Größere Zahlen werden sowohl direkt, als auch über die GfA-Notation unterstützt (1.234567890123E+14 wird umgewandelt in 123456789012300).

Sollen Befehle verwendet werden, die eine höhere JavaScript-Version als 1.1 benötigen, so kann man ihre Lauffähigkeit mit isBefehlsname abfragen (z.B. hat isCHR$ den Wert true, wenn die Ausführung möglich ist und false, wenn nicht).

Aber auch wenn JavaScript und GfA-Basic von der Syntax her doch recht ähnlich sind (GfA-Basic war damals seiner Zeit ein wenig voraus ... 8-)), so gibt es doch im Detail einige Unterschiede.

Nicht-umgehbare Unterschiede

GfA-CodeJavaScript-Code
Kommentare
REM oder ' oder ! oder // oder /*
// oder /* (...) */
Namensraum
Variablen-/Funktionsnamen mit "." erlaubt (Test.var - nicht alle GfA-Versionen)
Variablen-/Funktionsnamen mit "." nicht erlaubt (TestVar oder Test_var)
Variable/Feldvariable
Variablentypdefinitionen $#%&|
Keine Definition - nur $ wird als Namensbestandteil akzeptiert, ist aber ohne Wirkung
LOCAL
var
CLR Variable
Variable=null
DIM Feld(100)
Feld=new Array() (keine Dimensionierung notwendig)
Variable=DIM?(Feld())
Variable=Feld.length
Variable=Feld(0)
Variable=Feld[0]
OPTION BASE 1
Keine Entsprechung in JavaScript
Der Index beginnt immer bei 0 (OPTION BASE 0)
DATA / RESTORE
Keine Entsprechung in JavaScript
Zahlensysteme
$, & oder &H (Prefix für Hexadezimalzahlen) Keine Entsprechung in JavaScript: GfA &H0A entspricht parseInt("0A",16)
% oder &X (Prefix für Binärzahlen) Keine Entsprechung in JavaScript: GfA &X1001 entspricht parseInt("1001",2)
&O (Prefix für Oktalzahlen) 0: GfA &O10 entspricht 010 oder parseInt("10",8)
0010 entspricht 10 (führende Nullen werden ignoriert) 0010 interpretiert JS als 8 (führende 0 ist ja Prefix für Oktalzahlen) - GfA 0010 entspricht somit 10 oder parseInt("0010",10)
Die Befehle der GFA2JS-Bibliothek wandeln bereits alle GfA-Zahlensysteme mit der internen Funktion parseGfA() in Dezimalzahlen um (parseGfA("$0A") entspricht parseInt("0A",16)). Nicht-Dezimalzahlen, die einer Funktion nicht in einer Variablen sondern direkt übergeben werden, müssen aber als String geschrieben werden (also statt MUL($A,%1001) MUL("$A","%1001")!
Arithmetik
INC Variable
Variable++
DEC Variable
Variable--
Variable=num1^num2
Variable=Math.pow(num1,num2)
Funktionen/Prozeduren
FUNCTION Name(Parameter)
 (...)
 RETURN Ergebnis
ENDFUNC
function Name(Parameter) {
 (...)
 return Ergebnis
}
PROCEDURE Name(Parameter)
 (...)
RETURN
function Name(Parameter) {
 (...)
}
Funktionsaufruf: @Funktion
Funktionsaufruf: Funktion() (Klammern auch bei Funktionen ohne Parameter)
Funktionsergebnis ignorieren: VOID @Funktion
Funktionsergebnis ignorieren: void (Funktion()) oder void Funktion()
Verzweigungen
SELECT Variable
 CASE Konstante1 [TO Konstante2, ...]
 [CONT]
 DEFAULT
ENDSELECT
switch(Variable) { // (erst ab JavaScript 1.2)
 case Konstante:
 [break]
 default:
}
IF Bedingung
 (...)
ELSE IF Bedingung
 (...)
ELSE
 (...)
ENDIF
if(Bedingung) {
 (...) }
else if(Bedingung) {
 (...) }
else {
 (...)
}
GOTO
Keine Entsprechung in JavaScript
Schleifen
FOR Variable=Startwert TO Endwert
 (...)
NEXT Variable
for(Variable=Startwert;Variable<=Endwert;Variable++) {
 (...)
}
FOR Variable=Startwert TO Endwert STEP 3
 (...)
NEXT Variable
for(Variable=Startwert;Variable<=Endwert;Variable+=3) {
 (...)
}
FOR Variable=Startwert DOWNTO Endwert
 (...)
NEXT Variable
for(Variable=Startwert;Variable>=Endwert;Variable--) {
 (...)
}
Achtung: Bei GfA-Basic wird in FOR ... NEXT (und nur in diesem Schleifen-Typ) ein variabler Endwert einmalig zu Beginn festgelegt. Wurde als Endwert also eine Variable definiert die in der Schleife selbst geändert wird, so bleibt der Schleifen-Endwert unverändert! Bei JavaScript hingegen wird ein variabler Endwert bei jedem Durchlauf neu bestimmt. Ist der Endwert also durch eine Variable definiert die in der Schleife selbst geändert wird, so ändert sich auch der Schleifen-Endwert!
WHILE Bedingung
 (...)
WEND
while(Bedingung) {
 (...)
}
REPEAT
 (...)
UNTIL Bedingung
do { // (erst ab JavaScript 1.2)
 (...)
} while(Bedingung)
while(true) {
 (...)
 if(Bedingung) { break }
}
DO
 (...)
 EXIT IF Bedingung
 (...)
LOOP
while(true) {
 (...)
 if(Bedingung) { break }
 (...)
}
Vergleichsoperatoren
= <>
== !=
=< => ><
<= >= !=
==
Keine Entsprechung in JavaScript
Verknüpfungsoperatoren
AND OR NOT
&& || !
XOR IMP EQV
Keine Entsprechung in JavaScript
Interruptsteuerung
AFTER x GOSUB Procedure (x in 1/200 Sekunden)
Variable=window.setTimeout("Function()",x) (x in 1/1000 Sekunden)
AFTER STOP
window.clearTimeout(Variable)
AFTER CONT
Keine Entsprechung
EVERY x GOSUB Procedure (x in 1/200 Sekunden)
Variable=window.setInterval("Function()",x) (x in 1/1000 Sekunden)
EVERY STOP
window.clearInterval(Variable)
EVERY CONT
Keine Entsprechung

Auch müssen in GfA-Basic bei Proceduren/Funktionen genau die Parameter übergeben werden, die auch deklariert wurden. Bei JavaScript kann man hingegen mehr oder weniger Parameter übergeben. Dafür sind diese Parameter dann ausschließlich lokale Variable, während man bei GfA-Basic ja optional auch globale Variable übergeben kann (von den ganzen Möglichkeiten der Pointer mal ganz zu schweigen - JavaScript ist eben nur für einen sehr eng umrissenen Bereich entwickelt worden).

Beispiel-Umsetzung
(aus der Datenfeldverwaltung Fields)

GfA-CodeJavaScript-Code
FUNCTION getfield$(field$,nr%,div$) function getfield$(field$,nr,div$) {
 LOCAL entry$,q%,v%,w%,nr.old%  var entry$,q,v,w,nrOld
 IF div$=""  if(div$=="") {
  div$="|"   div$="|"
 ENDIF  }
 field$=field$+div$  field$=field$+div$
 nr.old%=nr%  nrOld=nr
 w%=0  w=0
 REPEAT   do {
  v%=SUCC(w%)   v=SUCC(w)
  w%=INSTR(field$,div$,v%)   w=INSTR(field$,div$,v)
  IF w%=0   if(w==0) {
   entry$=MID$(field$,v%, MAX(SUB(LEN(field$),PRED(v%)),0))    entry$=MID$(field$,v, MAX(SUB(LEN(field$),PRED(v)),0))
  ELSE   } else {
   DEC nr%    nr--
   IF nr%<=0    if(nr<=0) {
    entry$=MID$(field$,v%, MAX(SUB(w%,v%),0))     entry$=MID$(field$,v, MAX(SUB(w,v),0))
   ENDIF    }
  ENDIF   }
  EXIT IF w%=0   if(w==0) { break; }
 UNTIL nr%<=0  } while(nr>0)
 RETURN entry$  return entry$
ENDFUNC }
Die komplette konvertierte Fields-Bibliothek: fields.js

Bitte die veränderte Schleifenbedingung beim Wechsel von REPEAT (...) UNTIL nach do { (...) } while beachten! Auch sollte ggf. auf do { (...) } while verzichtet werden, da dieses Schleifenkonstrukt erst ab JavaScript 1.2 existiert.


Die externe Bibliothek: libgfa2.js
Zur interaktiven GfA-Befehlsübersicht

Updates:
10.01.2004: 
Korrektur: Bei irgendeiner Copy'n'Paste-Aktion war RANDOM() total verhunzt worden.
20.12.2003: 
Neue Funktionen: CFLOAT(), CINT(), COMBIN(), FACT() und VARIAT(). Update: MAX() und MIN() verarbeiten jetzt beliebig viele Zahlenwerte.
17.12.2003: 
Neue Funktionen: BIN$(), HEX$() und OCT$(). Update: Arbeitet eine GfA-Funktion nur mit Integerzahlen, so werden die übergebenen Werte jetzt zwangsintegriert. Bei CHR$() werden ungültige ASCII-Werte jetzt wie bei GfA umgewandelt (CHR$(-65) entspricht nun CHR$(256-65)).
12.12.2003: 
Update: Es werden jetzt nicht nur Dezimalzahlen, sondern alle Zahlensysteme (GfA-Notation) bei der Übergabe akzeptiert. Außerdem unterstützt STR$() jetzt auch die optionalen Parameter.
03.12.2003: 
Neue Funktionen: FRAC() und VAL().
01.11.2003: 
Korrektur: INSTR() bei leerer Suche, sowie LEFT$(), RIGHT$() und MID$() bei alten JS-Versionen.


Kurz-URL dieses Artikels: http://Coding.binon.net/JSGfALib