BlueBasic Compiler

Kommentare anzeigen Worklog abonnieren
Gehe zu Seite 1, 2, 3  Weiter

Worklogs BlueBasic Compiler

Erste ausführbare Dateien

Samstag, 28. Juli 2012 von Thunder
Mit Freude kann ich endlich ausführbare Ergebnisse vorweisen. Arrays sind aber leider noch nicht mit von der Partie Sad Aber ich habe das Gefühl, das hier liest sowieso keiner Very Happy
Folgendes: Das Modulsystem funktioniert wie in BlitzMax - zweistufig (brl.blitz, pub.stdc - bzw. in BlueBasic gerade aktuell: blub.core). Imports funktionieren aber anders... Imports sollen im Endeffekt so funktionieren, dass sie nur die Symboltabelle importieren und nicht die Datei nochmal dem Linker übergeben. Wie Imports in BlitzMax implementiert sind, hat einige Störfaktoren provoziert. Aber darüber rede ich, wenn Imports komplett so funktionieren, wie sie es sollen.

Heute möchte ich über Finalize und das Select-Statement reden.

Finalize
Zu aller erst: Ich habe Blödsinn erzählt... Finalize wird nicht nur für Sprachen unterstützt werden, die Goto unterstützen. Goto ist nur notwendig, wenn man versucht, den erzeugten Objektcode klein zu halten. In Wirklichkeit kann man aber einfach das, was im Finalize-Block steht, an jeden If-Block anhängen.
Zur Verdeutlichung ein Beispiel in BlueBasic und zwei Wege, es zu kompilieren (beide in C):
BlitzMax: [AUSKLAPPEN]
If x = 2 Then
x = 0
ElseIf x = 3 Then
x = 500
ElseIf x = 4 Then
x = 1337
Else
x = 42
Finalize
y = 80
EndIf

Wenn die Zielsprache Goto erlaubt:
Code: [AUSKLAPPEN]
if(_x == 2){
    _x = 0;
}else if(_x == 3){
    _x = 500;
}else if(_x == 4){
   _x = 1337;
}else{
   _x = 42;
   goto _1;
}
_y = 80;
_1:;

Wenn sie es nicht tut:
Code: [AUSKLAPPEN]
if(_x == 2){
    _x = 0;
    _y = 80;
}else if(_x == 3){
    _x = 500;
    _y = 80;
}else if(_x == 4){
   _x = 1337;
   _y = 80;
}else{
   _x = 42;
}



So, habe den letzteren Weg jetzt auch implementiert... kann nicht erklären, wieso ich darauf nicht schon früher gekommen bin.

Ob Finalize auch für Schleifen kommen soll, das hat auch keinen interessiert - anscheinend. Also hab ich mir Mal die Arbeit gespart und ich überlege mir das später noch. Wichtig wäre, Mal mit den komplizierteren Sachen anzufangen: Arrays, Strings und Objekte.

Es gibt jetzt übrigens ein besseres Interface zwischen Frontend und Backend (Funktionspointer sind meine neuen besten Freunde). Das einerseits dem Frontend ermöglicht, Informationen über das Backend abzufragen (bool-Variablen: knows_goto, knows_continue, knows_exit - ob die Sprachen diese Funktionen unterstützt) und andererseits ermöglicht, Backendfunktionen über die Funktionspointer aufzurufen.
Wenn knows_goto true ist, wird die Goto-Methode verwendet, Finalize zu kompilieren, ansonsten die andere.

Select-Statement
Das Select-Statement in BlitzBasic und BlitzMax unterscheidet sich generell Mal vom Switch-Statement in C (und ein paar anderen Sprachen).
1. In den Blitz-Dialekten ist z.B. das Durchfallen von einem Case ins nächste (wenn kein break; vorhanden ist) nicht möglich (break bzw. exit ist nicht Mal notwendig, um aus dem Select auszusteigen). Das wurde in BlitzMax (weiß nicht, ob in BlitzBasic auch) mit dem Komma kompensiert.
2. Außerdem können in C die Case-Label nur konstante Ausdrücke abfragen (also keine Variablen). Da ist Basic also weiter, was Features angeht, kann aber nicht so gut optimieren, wie C das in dem Fall kann (<- meine Annahme).

Ich habe mich entschieden, Select noch durch das To-Schlüsselwort zu erweitern, damit man Bereiche angeben kann. Ein Beispiel:
BlitzMax: [AUSKLAPPEN]
Select x
Case 65 To 90, 97 To 122 ' ASCII Zeichen 'A' bis 'Z' oder 'a' bis 'z'
' ...
Case 13, 10 ' CR oder LF
' ...
Case 0
' ...
Case 48 To 57 ' '0' bis '9'
' ...
EndSelect


Das To hat natürlich eine höhere Priorität als das Komma. Ich finde es nützlich, denn wenn es um viele Bereichsabfragen geht, ist dieses Statement kürzer als If und trotzdem genauso prägnant.

Primitive
Die primitiven Datentypen Byte, Short, Int, Long, Float und Double sind bereits implementiert. Dazu kommen UByte, UShort, UInt und ULong.
So, jetzt kommt der BlitzMax-Kompatiblitätsmodus ins Spiel. Liest der Compiler eine "*.bmx" Datei, dann wird sie in diesem Modus kompiliert. Er wird auf einige BlitzMax-spezifische Sachen Rücksicht nehmen. Zum Beispiel, dass für BlitzMax Byte = UByte und Short = UShort ist. D.h. man kann in einer "*.bmx"-Datei Byte und Short nur ohne Vorzeichen verwenden. Das kann man jetzt als Nachteil sehen, aber ich sehe das so: Solange man nur seine BlitzMax-Projekte mit BlueBasic kompilieren will, ist das eh ok und wenn man BlueBasic als solches programmieren will, wird man sich nicht an die alte Dateiendung klammern (die neue ist übrigens "*.blub", falls ich das noch nicht irgendwie beiläufig erwähnt habe).

blub.core
Blub.Core wird das Standardmodul von BlueBasic in dem die wichtigsten Funktionen eingebaut sind. Im Moment enthält es _main (also den Punkt, wo das C-Programm die Kontrolle bekommt - _main ruft dann bb_main auf), _PutInteger (da ich noch keine Strings habe, konnte ich noch kein Print verwenden, also habe ich eine Funktion geschrieben, die Integer ausgibt) und _End (das einfach exit(0) ausführt).

Einfache Programme (Sachen, die man normalerweise mit esoterischen Programmiersprachen lösen würde Very Happy) wie die Ausgabe der Fibonacci-Zahlen, sind daher schon möglich und in den meisten Fällen schreibt man sogar gültige BlitzMax-Programme:
BlitzMax: [AUSKLAPPEN]
Import blub.core

fibo 0, 1

Function fibo(a:Int, b:Int)
Local tmp = a
a :+ b
PutInteger a
If a < 500 Then fibo a, tmp
EndFunction


Ich hoffe, dass mir die nächsten Ziele keine argen Probleme machen.
Dann liest man sich wieder nächstes Mal,

Christian

Kommando retour

Sonntag, 17. Juni 2012 von Thunder
Hallo miteinander,

nach zwei Monaten und drei Tagen melde ich mich nun wieder und ich habe gute Neuigkeiten parat. Dafür, dass ich nur gelegentlich an BlueBasic arbeiten konnte, ist es in dieser Zeit gut gewachsen. Es ist immer noch unbenutzbar, aber es hat einige Konstrukte dazugelernt.

Bevor ich mit Blub (die offizielle Abkürzung Wink) beginne: Freundlicherweise habe ich die Erlaubnis bekommen, obwohl ich mein Projekt nicht in BlitzMax programmiere (sondern es nur was mit BMax zu tun hat), hier hin und wieder ein paar Updates zu machen. Danke dafür! Zusätzlich werde ich ein, zwei Sachen erklären (Syntax-Unterschiede ...), im Großen und Ganzen ist die Syntax aber sehr gleich der von BlitzMax.

Quelltextsicherung passiert jetzt auf Google Code: http://code.google.com/p/bluebasic Als Versionsverwaltungssystem verwende ich Mercurial und der Code ist jetzt unter GPL v3 lizenziert - da muss ich noch schauen, ob die nicht in Konflikt mit meinen Zielen steht (nämlich, ob man die mit BlueBasic programmierten Programme dann unter einer anderen Lizenz veröffentlichen könnte).

Zum alten Stand dazugekommen sind: If-ElseIf-Else-Finalize, If-Else (einzeilig), Select-Case-Default-Finalize, For-To-Step, For-Until-Step, Repeat-Until, Repeat-Forever, While-Wend, Goto und Labels, zuweisungsähnliche Operationen (so etwas wie a:+2, a:Mod 4)
Notwendigerweise habe ich dazu einen Formelparser gebraucht, der auch zu funktionieren scheint, und außerdem habe ich das Basissystem und die Caseinsensitivity verändert.

Finalize
So, jetzt wird dem BMax-Programmierer schon dieses merkwürdige "Finalize" aufgefallen sein. Was ist das?
Es ist ein weiterer optionaler Teil der If-Abfrage oder des Select-Ausdrucks und eigentlich ganz einfach zu verstehen:
Für If-Bedingungen: Der Finalize-Block wird aufgerufen, nachdem ein If- oder ElseIf-Block erfolgreich abgeschlossen wurde und nicht wenn kein einziger If- oder ElseIf-Zweig betreten wurde (dann kommt, wenn es da ist, Else ins Spiel). Else bleibt übrigens weiter optional, d.h. es kann auch If-Abfragen ohne Else, aber mit Finalize geben.
Für Case-Bedingungen: Selbes Prinzip. Der Finalize-Block wird aufgerufen, nachdem ein Case-Block erfolgreich durchlaufen wurde und nicht wenn kein einziger Case-Zweig betreten wurde (dann kommt, wenn es da ist, Default ins Spiel). Default bleibt auch weiterhin optional.

Internes Compilerdetail: Finalize wird mithilfe von unbedingten Sprüngen (besser bekannt als goto) umgesetzt. Ergo können Finalize-Ausdrücke nur in eine Sprache übersetzt werden, die auch goto oder eine Art unbedingter Sprünge unterstützt. Das betrifft aber BlueBasic für's Erste gar nicht, denn das Backend, welches ich schreibe, kompiliert nach C und da gibt's goto.

For-Schleife
Wie ich finde eine sehr interessante Schleife, weil sie sich wesentlich von For-Schleifen in anderen Sprachen unterscheidet. Ich bin dabei zu versuchen, sie in BlueBasic genau so umzusetzen, wie sie in BlitzMax funktioniert. Im Prinzip ist sie ganz einfach. Man hat eine Variable, gibt an von wo und bis wohin sie zählen soll und wenn man besonders gut gelaunt ist, auch noch, in welchen Abständen.
Interessanterweise lassen sich aber die Variablen, die man bei von-wo und bei bis-wohin angegeben hat, innerhalb der Schleife ändern, ohne dass sich BlitzMax davon gestört fühlt. Beispiel:
BlitzMax: [AUSKLAPPEN]
Local a:Int, i:Int
a := 5
For i := 1 To a ' Endlosschleife?
a:+1
Next ' Denkste!


Also speichert BlitzMax die Ergebnisse bevor die Schleife betreten wird? Nach einem kurzen Blick in den Asm-Code kann ich das nur bestätigen; oft sogar sehr effizient in einem Prozessorregister. Was BlitzMax kann, kann BlueBasic zwar noch lange nicht, aber das funktioniert bereits genau so in BlueBasic - dazu legt der Compiler lokale Variablen in der Funktion an, die diese Werte speichern, und zwar nur, wenn die angegebenen Werte nicht konstant sind.
Kompiliere ich das obige Beispiel mit BlueBasic, bekomme ich folgenden Code (auf das Wesentliche reduziert):
Code: [AUSKLAPPEN]
int _a;
int _i;
int temp0;
_a = 5;
temp0 = _a;
for(_i = 1 ; _i <= temp0 ; _i += 1){
   _a = _a + 1;
}


Wie man sieht löst der Compiler das Problem mit der temp0-Variablen, die den Wert von a übernimmt. Man könnte anstelle von a auch eine Formel hinschreiben, der Compiler würde genauso handeln. Das ganze funktioniert übrigens auch mit dem von-wo-Wert der For-Schleife.
Ich verweise außerdem stolz darauf, dass a:+1 zu _a = _a + 1 kompiliert wird. D.h. das Frontend formt die Formel so um, damit das Backend, falls die Zielsprache kein :+ oder += unterstützt, keine Probleme kriegt.

Es ist zwar nicht an diesem Beispiel erkennbar, aber die For-Schleife ist noch nicht ganz fertig, ich muss noch an der Ausstiegsbedingung arbeiten.

Wer sich die Syntaxdefinitionen genauer anschauen will, kann Mal einen Blick in die mcommand.d werfen. Dort ist in Kommentaren sehr schön erklärt, wie man welches Konstrukt gebrauchen kann und worauf man bei der Entwicklung eines Backends achten sollte.

Wer BlueBasic ausprobieren will, muss (weil es noch immer in einem sehr frühen Stadium ist) es sich selber bauen. Dazu braucht man MSYS (Teil von MinGW) und Mercurial und DMD (Digital Mars D Compiler). Ihr müsst euch mit Mercurial das Repository klonen und dann make drüberlaufen lassen. Blub funktioniert bisher sowohl auf Linux als auch auf Windows - ich lege Wert darauf, dass es von Anfang an auf beiden Systemen funktioniert.
Würde mich btw immer noch über Helfer freuen.

Außerdem eine Frage an die BlitzMax-er und vielleicht zukünftig BlueBasicler (Very Happy): Ich habe überlegt Finalize auch auf Schleifen zu übertragen. Für den Fall, dass die Schleife korrekt (mit der Bedingung) beendet wurde, springt sie noch in Finalize, wenn es da ist:
BlitzMax: [AUSKLAPPEN]
Repeat
...
If x=5 Then Exit ' steigt direkt aus - ohne Finalize
Finalize
x = 5
Until a>=50 ' wenn diese Bedingung erfüllt wurde, geht der Programmfluss zuerst noch in den Finalize-Block


Unschön oder verwendbar?

So, beim nächsten Mal folgen weitere Beschreibungen und hoffentlich mehr Features. Was mich in nächster Zeit wahrscheinlich beschäftigen wird sind zusätzliche primitive Datentypen (z.B. Float), Continue/Exit das Modulsystem und vielleicht schon Arrays.

Man liest sich,

Christian

Eine Nachricht

Samstag, 14. April 2012 von Thunder
Hallo,

ich schreibe euch heute, weil gestern, an Freitag dem Dreizehnten, die vermutlich letzte Kopie vom damaligen BlueBasic-Code wahrscheinlich zerstört wurde, als meine externe HDD den Geist aufgab. Ich glaube nicht, dass es Chance auf Rettung gibt.

Schweigeminute.

Aber viel wichtiger: Ich habe gar nicht geplant an dem Code weiterzuarbeiten, der ist schon seit fast zwei Jahren (so lang ist es schon her, dass ich das letzte Mal geschrieben habe) einfach über diverse Geräte verstreut gelegen. Ich hatte eigentlich vor für eine nächste Version flex und GNU Bison zu verwenden, was sich dann irgendwie auch erledigt hat, als ich gemerkt habe, dass die wahrscheinlich suboptimal sind.

Vor einem Monat in etwa, habe ich begonnen, die Arbeiten in mäßigem Schwung wieder aufzunehmen, aber auf einer neuen Codebasis und mit neuen Hintergedanken. Es soll eine Teilkompatiblität mit BlitzMax erreicht werden, damit BlitzMax-Programme mit wenig Aufwand nach BlueBasic portiert werden können und dort eventuell sogar von Zusatzfunktionen profitieren.

Die Codebasis ist nicht mehr in BlitzMax geschrieben (auch nicht BB), sondern in D. Was mich zweifeln lässt, ob ich den Worklog hier weiterführen darf. Einerseits ist das Projekt nicht mehr in einem Blitz-Dialekt geschrieben, aber es hängt direkt mit BlitzMax zusammen. Würde mich freuen, wenn mir das ein Zuständiger sagen könnte Smile
Ich werde nichtsdestoweniger diesen (Schluss-)Eintrag zumindest noch zu einem Erklärungseintrag verwenden, was ich machen möchte:

Ziele
- BlueBasic-Code soll sich fast immer so verhalten wie BlitzMax-Code. Das heißt nicht, dass derselbe Code produziert werden soll oder, dass die Module einfach von BlitzMax übernommen werden (dann könnte ich BlueBasic eventuell nicht OpenSource machen, weil die Module nicht mir gehören).
- Das Projekt auch online speichern, damit es 1. nicht verloren gehen kann und 2. dass andere daran mitarbeiten können. Geplant ist Google Code.
- Ein einfaches Backendsystem, sodass man relativ unkompliziert Backends für verschiedene Programmiersprachen (und damit auch Plattformen) schaffen kann. Im Moment programmiere ich nur ein kleines C-Backend nebenher, damit ich sehe, ob der Code richtig aus dem Abstract Syntax Tree (AST) generiert wird - ich halte das, was ich programmiere zumindest für einen AST Very Happy . Das C-Backend wird wahrscheinlich auch das erste richtige Backend für BlueBasic sein, weil ich C als portablen Assembler missbrauchen werde.
- BlueBasic soll Konstrukte bieten, die BlitzMax nicht oder nur eingeschränkt bietet. Ich hoffe, dass ich zumindest Funktionsüberladung und New() mit Parametern umsetzen kann. Aber auch kleinere Sachen, wie Conditional Compiling.

Was ist noch davor nötig
- Ich weiß nicht, wieviele Interesse hätten, sich zu beteiligen. Wenn es keinen interessiert, dann brauche ich das Projekt nicht Mal auf Google Code stellen (dann backupe ich es einfach nur mit Docs oder so), aber wenn doch sollte ich schon eine Art Dokumentation der geplanten Syntax ausarbeiten. Dann fällt die Zusammenarbeit wahrscheinlich wesentlich leichter.
- Die Probleme lösen, die ich gerade jetzt habe. Nämlich, wie man eine Formel in eine geeignete abstrakte Form umwandelt, sodass sie - ohne komplizierte Parsingvorgänge - vom Backend in eine Formel der Zielsprache umgewandelt werden kann. Dazu werde ich mir in der nächsten Zeit einige Gedanken machen müssen.

Wieviel vom Altbekannten steht wieder?
Da muss ich euch enttäuschen. Nicht viel. Das Problem mit den Formeln hämmt zu sehr, als dass ich z.B. Zuweisungen implementieren könnte, die ich längst einbauen wollte.
Der Funktionsumfang ist etwa der, den die nächsten zwei Programme abdecken:
BlueBasicCode: [AUSKLAPPEN]
Rem
   
   A test file.
   Test comment.
   ' Test comment in test comment.
   
EndRem

Const zee = 43 ' Test constant
Global a,b,c ' Test variables ...
Local d,e

Function f() ' test function
   Const ij = 42
   Local i
   Global j ' test global in test function
EndFunction

Function zee:Int(PARAMETER1:Int, parameter2, parameter3 = 5)
   Const style ' test void constant in test int function
   Local x,y
   Global z
EndFunction

Wird kompiliert zu:
Code: [AUSKLAPPEN]
/* GLOBAL CONSTANT DEFINITIONS */
const int __zee = 43

/* GLOBAL VARIABLES */
int __a;
int __b;
int __c;

/* FUNCTION FORWARD DECLERATIONS */
void bb_main(void);
void __f(void);
int __zee(int __parameter1,int __parameter2,int __parameter3);

/* FUNCTION DEFINITIONS */
void bb_main(void){
        int __d;
        int __e;
}
void __f(void){
        const int __ij = 42
        static  int __j;
        int __i;
}
int __zee(int __parameter1,int __parameter2,int __parameter3){
#define __style
        static  int __z;
        int __x;
        int __y;
#undef __style
}


Und dieser Code (der 3 Fehler enthält):
BlueBasicCode: [AUSKLAPPEN]
Rem
   
   A test file.
   Test comment.
   ' Test comment in test comment.
   
EndRem

Const zee = 43 ' Test constant
Global a,b,c ' Test variables ...
Local d,e,c

Function f(x:Object) ' test function
   Const ij = 42
   Local i
   Global j ' test global in test function
EndFunction

Function zee:Int(PARAMETER1:Int, parameter2, parameter3 = 5,)
   Const style ' test void constant in test int function
   Local x,y
   Global z
EndFunction


gibt diese Fehlermeldungen:

Zitat:
[11] error: Variable "c" is defined (at least) twice

[13] error: type "object" (of var "x") does not exist

[19] error: name of parameter (in function definition of "zee") expected, instead got ')' [41]


Vielleicht ist es schon jemandem aufgefallen, dass bei keiner der Variablen ein Typ-Bezeichner steht. Den gibt es, so wie in BlitzMax, aber wenn kein Typ neben einer Variablen steht, wird sie ein Integer (wie auch in BlitzMax bis in Strict, aber nicht mehr in SuperStrict) und ich habe mir einfach das :Int erspart.

Hier noch ein paar Plänchen kurz erläutert:
(vielleicht sind ein, zwei Sachen gleich geblieben und vielleicht habe ich sie schon Mal erklärt)

(Super)Strict wird es btw nicht geben, weil von Haus aus mit den strikten Regeln programmiert werden sollte. Dass Variablen ohne Typbezeichner aber direkt Integer werden, finde ich praktisch und sollte behalten werden. Auch das Ausschalten einzelner Funktion (z.B. Goto) im SuperStrict ist eine Qual.

Und wem die Void-Konstante aufgefallen ist, die sollen zum Conditional Compiling benutzt werden können.

Außerdem möchte ich zwei Istgleich-Zeichen. Dass in verschiedenen BASIC-Formen = sowohl für Zuweisungen als auch für Abfragen verwendet wird, halte ich für etwas inkonsistent. Um dem abzuhelfen, ohne aber zu forcieren, dass man diese Variante nicht mehr verwenden darf (man soll weiterhin beides mit = machen können), soll ein zusätzlicher Zuweisungsoperator erstellt werden ( ":=" schien mir sinnvoll, weil BlitzMax auch die Schreibweisen ":+" ":*" etc. kennt ), der explizit auf eine Zuweisung hinweisen soll. Die weitere Unterstützung des einfachen Istgleich für Zuweisungen ist eigentlich hauptsächlich der Kompatiblität geschuldet.

In-Operator. Er soll etwas Arbeit abnehmen und überprüfen, ob ein Element in einem Array enthalten ist:
Code: [AUSKLAPPEN]
Local c:Int, array:Int[20]
If c In array Then ...

Das erspart eine Schleife (intern wird sie natürlich schon gebraucht). Demnach soll dann auch die Syntax von For-Schleifen angepasst werden:
Code: [AUSKLAPPEN]
For c = EachIn array ' soll Bluebasic als deprecated kennzeichnen, aber kennen
ForEach c In array ' soll die präferierte Schreibweise sein

Das killt dann eine Überlappung der zwei For-Schleifen.

Ich weiß, ich hab mir wieder viel vorgenommen... Kann nicht anders.

Wie gesagt will ich, dass das Projekt OpenSource ist (zlib-Lizenz wahrscheinlich). Vorerst aber nicht, zuerst würde ich gerne schauen, ob sich der Aufwand lohnt es auf eine Plattform wie Google Code zu stellen - wäre nicht so begeistert, wenn ich mir die Mühe gemacht hätte und es dann niemand' interessiert.

Wie gesagt Programmiersprache ist D. Es ist ein Ableger von C/C++ aber macht doch einige Sachen entscheidend anders, d.h. eine geringe Einarbeitungszeit würde niemandem erspart bleiben, aber die hatte ich auch zu leisten. D ist kostenlos, objektorientiert, modulbasiert, konsistent...
Die Compiler dmd (Original von Digital Mars) bzw. gdc (Frontend für gcc) sind für Windows, Linux und afaik auch MacOS erhältlich. Durch Phobos (eine Lib, die auch auf zumindest diesen drei Systemen läuft) sind dann die D-Programme auch kompatibel und verhalten sich auf allen Systemen gleich.

Ich will nur darauf hinaus, dass ich gerne Rückmeldungen von denjenigen hätte, die Interesse hätten mehr oder weniger daran mitzuarbeiten ("mehr oder weniger" hier im wahrsten Sinne der Worte). Vielleicht manchmal den Code zu überfliegen, zu schauen, ob man ein Teilproblem lösen kann etc.
Wenn nicht probier ich's wieder alleine.


Ja, für's erste war's das jetzt. Ich hoffe es quälen sich einige durch den Eintrag, tut mir aber Leid, dass er so lang ausgefallen ist. Solange ich nicht weiß, ob ich hier den Worklog weiterführen darf, werde ich auch nichts neues mehr posten, aber hoffentlich umso mehr an BlueBasic arbeiten.

Man liest sich hier oder wo anders
- aber irgendwo sicher,

Christian

Erweiterte Konstrukte

Freitag, 16. Juli 2010 von Thunder
Hallo,

länger ist es her, dass hier ein Eintrag gemacht wurde - das liegt an meinem Parallelprojekt MaxNet. Vergessen habe
ich BlueBasic allerdings nicht.
Ich habe jetzt Select-Case-Default sowie Repeat-Until implementiert. Ersteres muss noch genauer auf Lauffähigkeit
geprüft werden.

Wie ich schon oft gesagt habe, wird man mit BlueBasic einige Einschränkungen vorfinden (zB: kein On-The-Fly Deklarieren
von Variablen, Select-Case-Default eingeschränkt ...). Daher will ich dem Nutzer dennoch einiges an nützlichen Funktionen
bieten, die man bei BlitzBasic doch vermisst.
Eine, neuimplementierte, dieser Art ist das aussteigen aus verschachtelten Schleifen. Ich gehe in meinen Beispielen in
dem Eintrag von folgendem Code aus:
BlitzBasic: [AUSKLAPPEN]
Global x%,y%=1
Repeat
x=1
Repeat
Print x+" x "+y+" = "+(x*y)
x=x+1
Until x>10
y=y+1
Until y>10
Print "Fertig!"
End


Dieser Code gibt mit Hilfe von zwei Variablen und zwei verschachtelten Repeat-Until-Schleifen das 1x1 aus.
Wenn ich das 1x1 allerdings nach der Rechnung 5x5 abbrechen will, muss ich einen unschönen Umweg gehen:
BlitzBasic: [AUSKLAPPEN]
Global x%,y%=1
Repeat
x=1
Repeat
Print x+" x "+y+" = "+(x*y)
If x=5 And y=5 Then x=100
x=x+1
Until x>10
If x=101 Then Exit
y=y+1
Until y>10
Print "Fertig!"
End

(Dies ist nur eine Möglichkeit von vielen)

Nicht besonders schön - besonders eklig wird es, wenn man die Schleifen erweitern will. Deshalb habe ich mir ein Feature
von der Programmiersprache Ada abgeschaut: Man kann Schleifen Namen geben.
An den Befehl Exit stellt man einfach den Schleifennamen hinten ran und man kann aus einer beliebigen Schleife (in der man
auch drinnen ist Wink) aussteigen:

BlueBasic und nicht BlitzMax: [AUSKLAPPEN]
Global x:Int,y%=1
Repeat schleifenname
x=1
Repeat
Print x+" x "+y+" = "+(x*y)
'nach 5x5 abbrechen:
If x=5 And y=5 Then Exit schleifenname
x=x+1
Until x>10
y=y+1
Until y>10
Print "Fertig!"
End


Wer genau hinschaut bemerkt, dass ich für Variablen auch die BlitzMax-Specifier zulasse (allerdings nur Int, String und Float).
Obiger BlueBasic-Code lässt sich bereits problemlos kompilieren und ausführen Smile


mfg Thunder

Riesenfortschritte II

Dienstag, 6. Juli 2010 von Thunder
Überraschung!

Auch BlueBasic habe ich erweitert. Ich habe einen Import-Befehl implementiert, der eine C-Datei mit einer IMP-Datei importiert.
Die IMP-Datei listet auf, welche Funktionen dem BlueBasic-Compiler bekannt sein sollen, also welche der Programmierer verwenden
kann.
Außerdem habe ich Strings implementiert, doch leider gibt es Probleme mit Stringvergleichen. Wie bei BlueBasic-1 sind folgende
Stringvergleiche möglich:
BlitzBasic: [AUSKLAPPEN]
Local s1$,s2$
If "jalsldf"="kdlsfjsld" Then ...
If "jklsdjf"=s1 Then ...
If s2="jkdlsf" Then ...
If s1=s2 Then ...

Die Strings dürfen nicht in der If manipuliert werden, weil es dann zu folgendem Problem kommen würde:
Code: [AUSKLAPPEN]
;Basic-Code:
If Lower(s1)=Lower(s2) Then ...
;C-Code:
if(strcmp(lower(s1),lower(s2))==0){ ...


Lower muss, um nicht den echten String zu manipulieren, eine Kopie anlegen und einen Zeiger auf diese Kopie (die danach auch verändert
wurde) zurückgeben. Dieser Zeiger zeigt (logischerweise) auf einen allokierten Speicherbereich. Der Vergleich würde funktionieren, doch
würde ich die Adressen der Speicherbereiche, und damit 8 KB Speicher, verlieren. Wenn diese Vergleiche in einer Schleife (die z.B. 10000
Mal ausgeführt wird) vorgenommen werden, hat man in den Speicher ein Loch von 78 MiB gebohrt. Schlimmer wird es, wenn diese Funktionen
verschachtelt aufgerufen werden: Trim(Mid(Upper(s$),5,-1))
Daher habe ich das rausgelassen. Man darf Strings manipulieren, aber nicht in Vergleichen.

Außerdem gibt es einen von BlitzBasic abweichenden Befehl Clr, der einen String "freigibt". Man könnte es mit str="" vergleichen, es ist
aber besser, weil auch der Speicher freigegeben wird. Ich werde trotzdem versuchen, das auf eine Zuweisung von "" zu übertragen.

Weiters sind jetzt Print und Write einigermaßen implementiert. Folgende Syntax:
Code: [AUSKLAPPEN]
BNF:
<print-syntax> = Print String | Variable | ( Rechenausdruck )

Wenn man innerhalb von Print rechnen will, muss man den Rechenausdruck in Klammern schreiben:
BlitzBasic: [AUSKLAPPEN]
Global daumen=5
;Nicht zulässig:
Print "Hier ist Pi mal Daumen: "+3.14*daumen
;Zulässig:
Print "Hier ist Pi mal Daumen: "+(3.14*daumen)
;Außerdem unzulässig:
Print "In Klammern dürfen nur Rechenausdrücke stehen"+(" es ist leichter zu parsen und außerdem schreibt kein Mensch einen String in Klammern^^")



mfg Thunder

PS: Ja, "Riesenfortschritte I" war nicht in dem Worklog, sondern hier: https://www.blitzforum.de/worklogs/369/

Ifs

Mittwoch, 9. Juni 2010 von Thunder
Hallo,

ich habe den Vorschlag von Noobody umgesetzt und die Entfernung der Kommentare in den Compiler selbst verlegt damit
keine Probleme entstehen. Bis jetzt funktioniert es so, ich könnte mir allerdings vorstellen, dass ich irgendwas
vergessen habe...

Das nächste wären If-Bedingungen. Auch diese wurden fürs erste nur für Ints und Floats eingebaut. Ich habe einige
Tests gemacht; dieser Quelltext:
BlitzMax: [AUSKLAPPEN]
Local x%=2,y%=1 'Kommentar
If x=y Then 'Kommentar
inlinec printf("x=y\n"); 'Kommentar
ElseIf x=y+1'Kommentar
If y=x-1 Then inlinec printf("x=y+1 und y=x-1\n"); 'Kommentar
Else 'Kommentar
inlinec printf("else\n");'Kommentar
EndIf 'Kommentar

... wird zu ...
Code: [AUSKLAPPEN]
#include "C:/BlitzMax/BlueBasic2/inc/standard.c"
#include "test.h"
int main(int argc, char** argv){
BB_INT x = 2;
BB_INT y = 1;
if(x==y){
printf("x=y\n");
}else if(x==y+1){
if(y==x-1){
printf("x=y+1 und y=x-1\n");
}

}else{
printf("else\n");
}
return 0;
}


Es scheint also zu funktionieren und es kostete mich nur 30 Zeilen Code (am Anfang mehr, ein bisschen Optimierung und
alles wird besser; apropos First make it run, then make it run fast (Brian Kernighan) passt gut dazu).

Im Moment sind einzeilige If-Verschachtelungen oder If-Elseif oder If-Else oder If-Elseif-Else Kombinationen nicht möglich:
BlitzMax: [AUSKLAPPEN]
'Wir sind nicht möglich:
If x=0 Then If y=0 Then ...
If x=0 Then x=1 Else x=0
'...


Außerdem wird wahrscheinlich eine weitere Regel bleiben: Mehrzeilige If -> 'Then' ist optional ; Einzeilige If -> 'Then'
ist verpflichtend. Das ist geplant (oder auch nicht) um soetwas zu vermeiden:
BlitzMax: [AUSKLAPPEN]
If x=0 If y=2 y=3 'nicht cool


Ich weiß, ich habe euch wahrscheinlich nicht mit großartigen Neuigkeiten überschwemmt, aber jetzt ist einmal der Schulstreß
vorbei (heute war der letzte Test gewesen) und ich werde versuchen mehr zu machen.


Bis zum nächsten Eintrag,

Thunder

rekursive Includes

Montag, 7. Juni 2010 von Thunder
Hallo,

ich melde mich wieder mit einer neuen Version BlueBasic, die jetzt Includes unterstützt. Diese werden durch
rekursive Funktionsaufrufe meiner Präprozessorfunktion (die auch Kommentare entfernt etc.) interpretiert. Es ist
erstaunlich wie gut sich das mit Hilfe von Rekursion in 37 effektiven Zeilen (=Zeilen in denen was steht) lösen lässt.
(sauber - nicht gepfuscht! ok, ich gebs zu; zuerst gepfuscht und dann sauber neugeschrieben Wink )

Die Rekursivität der Includes im Titel betrifft natürlich nicht die Funktion, mit der diese interpretiert werden,
sondern, dass sie auch rekursiv inkludiert werden können, zB test.bb inkludiert test2.bb, das test3.bb inkludiert.
Übrigens gibt es eine Warnung, wenn versucht wird eine Datei doppelt zu inkludieren.

Info:

  • Note heißt, es wird ein Text ausgegeben, der dem Benutzer etwas sagt, was aber nicht unbedingt
    wichtig ist. Wird nur in einem der Verbose-Modes des Compilers angezeigt:
    -v ... be verbose
    -V ... be very verbose

  • Warnung heißt, es wird ein Text ausgegeben, aber der Kompiliervorgang wird fortgesetzt.
  • Error heißt, es wird ein Text ausgegeben und die Kompilation beendet.


Außerdem habe ich das Kommentarzeichen von ; auf ' geändert, jedoch sehr modular, zwei Schläge auf die Tastatur und
es ist wieder beim Alten.
Der Grund für die Änderung war die Einführung der Funktion InlineC. Diese kann man auf 2 Möglichkeiten aufrufen:
BlitzBasic: [AUSKLAPPEN]
InlineC printf("Hallo Welt!\n"); 'Einzeilig
InlineC ' wenn nach dem Befehl eine neue Zeile anf&auml;ngt...
printf("Hallo Welt!\n");
'... wird er als mehrzeilig interpretiert.
EndInlineC

Der Strichpunkt am Ende der C-Befehle hat es ausgemacht. Dieser würde nämlich schon vom Präprozessor entfernt und der
Compiler bekäme ihn gar nicht zu Gesicht. Der Strichpunkt hätte automatisch eingefügt werden können, doch das ist 1. unpraktisch
und 2. höherer Aufwand (nicht unbedingt viel höher, aber schon um einiges und ich mag keine vollgestopften Funktionen, die ich
später nicht mehr lesen kann).
Nicht dass ihr jetzt denkt ich sei faul^^, aber InlineC ist für mich eine ganz wichtige Funktion, weil ich theoretisch Teile
der BlueBasic-Funktionsbibliothek in BlueBasic selbst schreiben könnte (mit einigen InlineC-Aufrufen).

Wie ich schon letztes Mal sagte erstellt der Compiler die Datei irgendwas.prep.bb die dann kompiliert wird. Diese beinhaltet schon
alle Includes und keine Kommentare mehr.
Mal sehen, was ich bis zum nächsten Mal mache...


Bis dahin,

Thunder

Funktionsaufrufe und optionale Parameter

Sonntag, 6. Juni 2010 von Thunder
Hallo,

ich habe jetzt einen einfachen Funktionsaufruf aus dem Programm implementiert (noch nicht in mathematischen Ausdrücken).
Auch optionale Parameter durften nicht fehlen, doch da sie von ANSI-C nicht unterstützt werden, musste ich da nachhelfen.
Mein System mit optionalen Parametern ist nicht so komfortabel wie das von BlitzBasic, aber es ist definitiv einfacher zu
programmieren und braucht in meinem Quelltext etwa 30 Zeilen. Ich weiß noch nicht ob ich es umstellen werde. Mein System
sieht so aus:

BlitzBasic: [AUSKLAPPEN]
Function EineFunktion(a%,b%=20,c%=30)
End Function ; in BlueBasic ist auch EndFunction erlaubt: BlitzMax-Style Wink
;Aufrufe:
EineFunktion(30,40,50)
EineFunktion(40,,20)
EineFunktion(10,1,) ;Das ganze geht natürlich auch ohne Klammern
EineFunktion(10,,)


Wie wird das verwirklicht? Der Compiler geht die Datei zweimal von oben bis unten durch. Beim ersten Mal wird alles mögliche
zusammengetragen, woran mich C hindern würde (Funktionen, die weiter oben verwendet werden, müssen vordeklariert sein ...).

In diesem Durchlauf werden auch die Funktionen im Speicher des Compilers vordeklariert, damit er weiß, dass es sie gibt. Der
Compiler erstellt dann einen String mit den Parametern der Funktion. Ein Beispiel:
BlitzBasic: [AUSKLAPPEN]
Function IrgendeineFunktion(s$,i%=1337,k%,f#=3.14,s1$="Hallo")

Bei solch einer Funktion generiert der Compiler diesen Parameterstring: $%=1337%#=3.14$="Hallo"
Aus dem kann er bei Funktionsaufrufen lesen, welcher Parameter welchen Typs ist und ob er optional ist und was eingesetzt werden
soll, falls er optional ist und nicht angegeben wird.

Ein kompiliertes Beispiel:
BlitzBasic: [AUSKLAPPEN]
Local POSx=22,POSy%=11
Local x%,y#=55.5,w=100.2,h%=23
Global isinside=(posx>=x And posx<=x+w) And (posy>=y And posy<=y+h)
add50(,,)
add50(50,40,30)
add50(,20,)
add50(,20,30)
add50(60,,)
add50(70,50,)
add50(70,,54)

Function Add50(X=20,Y=30,Z=40)
Local z%=(x+y+z) Mod 50
End Function


Sieht in C so aus:

Code: [AUSKLAPPEN]
#include "C:/BlitzMax/BlueBasic2/inc/standard.c"
#include "test.h"
BB_INT add50(BB_INT x, BB_INT y, BB_INT z){
// BlueBasic macht alle Variablen- und Funktionsnamen klein.
BB_INT z = (x+y+z) % 50;
}
int main(int argc, char** argv){
BB_INT posx = 22;
BB_INT posy = 11;
BB_INT x = 0;
BB_FLOAT y = 55.5;
BB_INT w = 100.2;
BB_INT h = 23;
isinside = (posx>=x && posx<=x+w) && (posy>=y && posy<=y+h);
add50(20,30,40);
add50(50,40,30);
add50(20,20,40);
add50(20,20,30);
add50(60,30,40);
add50(70,50,40);
add50(70,30,54);

return 0;
}
(test.h):

BB_INT isinside;



Bis zum nächsten Mal,

Thunder

Matheconverter & Modularität

Samstag, 5. Juni 2010 von Thunder
Hallo,

glaubt nicht, was ich sage! Es ist kein Mathe-Parser - ich würde es eher als Mathematik-Syntax-Converter
bezeichnen; jedenfalls ist es so ziemlich fertig, was auch immer es jetzt ist. Schwer war es nicht, weil ich
nur einiges zu konvertieren hatte ( => zu >=, Shr zu >> ...); zwar fehlen noch ein paar Elemente, diese sind
aber nicht grundlegend und können später ohne weiteres eingefügt werden.

Außerdem habe ich eine Theorie aufgestellt und zwar darüber, wieso BB Variablen, die zum ersten Mal benutzt werden,
deklariert. Zumindest, wenn man nach C kompiliert (ich habe keine Ahnung was blitzcc macht - ich bleibe bei meiner
Theorie, die besagt, dass BlitzBasic NICHT kompiliert wird) ist es viel einfacher wenn man einfach alles deklariert,
was einem in den Weg kommt. Ich hätte es viel leichter gehabt, aber ich wollte eine strikte Deklaration von Variablen.

Übrigens versuche ich alles so modular wie möglich zu halten, um Änderungen schnell an vielen Elementen vornehmen zu
können. Ein Beispiel: Früher hatte ich 3 Funktionen für Deklaration von Variablen/Konstanten: GlobalDec, LocalDec, ConstDec.
Jetzt ist es eine (Declaration), die einen Parameter übergeben bekommt, um welche Deklaration es sich handelt. Das ist
um einiges praktischer, wenn man nach C kompiliert. Diese 3 Funktionen waren ein Überbleibsel aus der Kompilierung nach
Assembler.

Es ist immernoch nicht mehr möglich, als Variablen zu deklarieren und Funktionen. Jetzt eine Beispieldatei:
BlitzBasic: [AUSKLAPPEN]
Local posx=22,posy%=11
Local x%,y#=55.5,w=100.2,h%=23
Global isinside=(posx>=x And posx<=x+w) And (posy>=y And posy<=y+h)

Function Add(x%,y)
Local z%=x+y
End Function

...wird zu...
Code: [AUSKLAPPEN]
#include "C:/BlitzMax/BlueBasic2/inc/standard.c"
#include "test.h"
BB_FLOAT Add(BB_INT x, BB_INT y){
BB_INT z = x+y;
}
int main(int argc, char** argv){
BB_INT posx = 22;
BB_INT posy = 11;
BB_INT x = 0;
BB_FLOAT y = 55.5;
BB_INT w = 100.2;
BB_INT h = 23;
isinside = (posx>=x && posx<=x+w) && (posy>=y && posy<=y+h);
return 0;
}


Sollte also alles klappen. Diese Datei hieß beim kompilieren test.bb
Zu den Includedateien: Es werden 2 Dateien inkludiert nämlich test.h und standard.c

test.h: Der Compiler erstellt 3 Dateien (Beispiel: test.bb): test.prep.bb (wird nachher gelöscht), test.c, test.h
test.h enthält die Deklarationen der globalen Variablen - so war es einfacher diese in den Griff zu bekommen.

standard.c: Das ist die Library die bei jeder BlueBasic-generierten C-Datei hinzugefügt wird. Sie enthält alle
Deklarationen etc. unter Anderem die von BB_INT, BB_FLOAT und BB_STRING (auch ein Teil des Modularitätsprinzips)


Bis zum nächsten Eintrag,

Thunder

Edit: ich habe gerade bemerkt, dass die Beispieldatei (Rückgabetyp der Funktion) falsch kompiliert wird. Ich werde das beheben. - funktioniert jetzt!

Es geht weiter und der Code ist recht schön

Freitag, 4. Juni 2010 von Thunder
Hallo,

wie gesagt: es geht weiter! Ich habe nun Funktionsdeklarationen so gut wie fertig (eigentlich fertig, aber ich
weiß noch nicht, ob sie fehlerlos funktionieren). Globale Deklarationen müssen noch ein wenig abgeändert werden,
weil in BB folgendes möglich ist:
BlitzBasic: [AUSKLAPPEN]
Local x=55,y=22
Global z=x*2-y

Das sähe in C bei Übernahme des Codes so aus:
Code: [AUSKLAPPEN]
int z=x*2-y; // Fehler! da x und y noch nicht aufgetaucht sind...
int main(int argc, char **argv){
   int x=55,y=22;
   return 0;
}

Dieser Schritt der Initialisierung bei globalen Variablen wird in die main-Funktion verlegt:
Code: [AUSKLAPPEN]
int z;
int main(int argc, char **argv){
   int x=55,y=22;
   z=x*2-y;
   return 0;
}


Eigentlich ist es recht einfach das umzusetzen, doch ich möchte zuerst den Matheparser schreiben, damit ich den auch
gleich einfügen kann. Das heißt, wenn ich Glück habe, seht ihr nächstes Mal eine funktionierende Global Deklaration,
Local Deklaration, Const Deklaration (alles vorerst nur für Int und Float) und eine funktionierende Funktions Deklaration.

Übrigens habe ich (von mir so genannte) Befehlsattribute eingeführt; auf ein Global(zum Beispiel) darf ein Doppelpunkt folgen,
dem ein bestimmtes Attribut folgt (zB Static):
BlitzBasic: [AUSKLAPPEN]
Global:Static x,y ;auf diese Variablen kann nur in dieser Datei zugegriffen werden


Ich hoffe ihr verliert trotz des Neuschreibens nicht die Hoffnung/das Interesse, ich gebe mein Bestes!


Bis zum nächsten Mal,

Thunder

Gehe zu Seite 1, 2, 3  Weiter