VisualBlitz - B3D & BMax in Visual Studio

Kommentare anzeigen Worklog abonnieren

Worklogs VisualBlitz - B3D & BMax in Visual Studio

Ein kleines Update über ANTLR

Mittwoch, 23. Juli 2014 von PacMani
Da die nächste Phase etwas länger dauert, gebe ich mal ein kurzes Update darüber woran ich gerade arbeite.

Ich habe nun die Sprache analysiert und aufgeschrieben, welche Dinge alle zu beachten sind, um BlitzBasic richtig parsen zu können. Dabei habe ich auch viele merkwürdige Schreibweisen ausprobiert um herauszufinden, wie BlitzBasic auch auf schludrige Programmierung reagiert (denn die ist leider nicht selten).
Im letzten Post hatte ich ja bereits einige Dinge angesprochen, die ich so nicht erwartet hätte, welche aber (größtenteils) glücklicherweise logische Hintergründe hatten.

Nun setze ich ANTLR ein, um BlitzBasic zu lexen und zu parsen. ANTLR erklärt sich am besten selbst; die Abkürzung steht für "Another Tool for Language Recognition" und besteht einfach gesagt aus zwei Dingen:

Zunächst ist da die ANTLR-"Grammar", eine Sprache, mit der Sprachen definiert werden. Textuell wird hier im ANTLR-Grammer-Format niedergeschrieben, was z.B. ein Keyword / Operator / Whitespace ist und wie Bedingungen aufgebaut sind. Es ist also die ANTLR-kompatible Abbildung meiner niedergeschriebenen Notizen, um BlitzBasic in seine einzelne Bestandteile auseinander nehmen und analysieren zu können.

Dann ist ANTLR selbst ein Tool, was aus der Grammar-Datei einen Parser und einen Lexer erstellt - also echte Klassen, die später im Code verwendet werden. Glücklicherweise gibt es von ANTLR eine C#-Version, die C#-Code erzeugt - da mein VS-Package auch in C# geschrieben ist, lässt sich dies wunderbar einbinden (neben C# erzeugt das ursprüngliche ANTLR Java-Code und ist auch in Java selber geschrieben).
Die Arbeit, selber das "Gerüst" für einen Parser zu schreiben, entfällt damit, denn die erledigt ANTLR für mich.

Mithilfe dieser Klassen kann ich dann die BB-Syntax von Anfang bis Ende abklopfen und mein Syntaxhighlighting darauf anwenden sowie intern die Liste der Variablen / Typen / etc. aktualisieren oder Gültigkeitsbereiche erkennen.

Hoffentlich gibt es bald wieder etwas zu sehen, dann gibt's auch interessantere Screenshots. Soviel erstmal zum Thema Parsen und Lexen. =)

Blitz3D-Merkwürdigkeiten

Sonntag, 22. Juni 2014 von PacMani
Ich wahr mitten beim Implementieren des Syntax Highlightings von Blitz3D, da fielen mir einige interessante Merkwürdigkeiten vom BlitzCC (dem Blitz3D Compiler) auf.

Die Syntax von Blitz3D scheint mir an manchen Stellen unausgereift zu sein. Zumindest sind einige Dinge möglich, die einfach nur verwirrend sind. Hier eine Auflistung einiger Mysteriositäten, die mir über den Weg gelaufen sind.

Fake Leerzeichen in Variablen-Namen
Es ist möglich, eine Variable mit Leerzeichen im Namen zu definieren.
Code: [AUSKLAPPEN]
Global Variable Mit Leerzeichen = 1
DebugLog(Variable Mit Leerzeichen)

Dieses Programm gibt brav 1 aus und erzeugt keinen Syntaxfehler. Wie kann das sein?
Okay, ich habe etwas gelogen. Der Variablenname besitzt keine Leerzeichen. Das sieht nur so aus. Tatsächlich handelt es sich hier allerdings um "Fake Leerzeichen", die mit der Tastenkombination Alt+255 (auf dem Numpad) erzeugbar sind (es wird ein Zeichen mit dem ASCII-Wert 255 erzeugt). Würde ich diese Zeichen gegen Unterstriche austauschen, sieht der Code schon viel logischer aus:
BlitzBasic: [AUSKLAPPEN]
Global Variable_Mit_Leerzeichen = 1
DebugLog(Variable_Mit_Leerzeichen)

Dennoch ist es fahrlässig, das der BlitzCC das zulässt. Moderne Programmiersprachen wie C# erlauben den ganzen Unicode-Block "Whitespaces" nicht und deuten diese als Trenner von Sprachelementen. Klar, Blitz3D-Dateien dürfen an sich nichtmal Unicode sein und können auf diese Methode nicht zurückgreifen - dennoch wäre ein einfacher Check von Alt+255 nicht sonderlich schwer gewesen. Zumal sonst auch alle anderen ASCII-Zeichen, die keine Buchstaben oder Zahlen sind, nicht erlaubt.
Übrigens rallt das weder IDEal noch der Syntax Highlighter hier. Der "Mit Leerzeichen"-Teil wird jeweils zu zwei weiteren Variablen gedeutet.
Ich sollte es dagegen so wie der Compiler parsen, für spätere Funktionalitäten wird das wichtig (z.B. einer einfachen Variablenliste aus der dann Intellisense erzeugt wird).

Altmodische Initialisierung mehrerer Variablen in einer Zeile
Leider nutzt BlitzBasic die altmodische Art, mehrere Variablen in einer Zeile zu initialisieren - oder eben nur die letzte davon, wie der folgende Code demonstiert:
BlitzBasic: [AUSKLAPPEN]
Global Variable1, Variable2 = 2
DebugLog(Variable1)
DebugLog(Variable2)

Dies gibt "0" und "2" aus.
Zunächst verstört es, dass die 2 nur an die zuletzt genannte Variable zugewiesen wird. In moderneren Sprachen bekommt jede der in der Zeile genannten Variablen den Wert 2 zugewiesen.
Damit der Effekt klarer wird, sollte man diese Art der Initialisierung als Programmierer lieber lassen und folgendes schreiben:
BlitzBasic: [AUSKLAPPEN]
Global Variable1 = 2
Global Variable2 = Variable1
DebugLog(Variable1)
DebugLog(Variable2)

Dies gibt beide Male 2 aus.
Nachtrag: Hier bin ich wohl tatsächlich mit etwas anderem durcheinander gekommen. Folgendes VB-Beispiel:
Code: [AUSKLAPPEN]
Dim a, b, c As String

Im alten VB6 hat dies zwei Integer a und b und einen String deklariert. c war ein String.
Im neuen VB.NET werden drei Strings deklariert. Das finde ich vernünftiger, weil man es auch so von der Leseart her erwartet. Sowas ist in BB aber ohnehin nicht möglich, da Typen direkt hinter dem Variablennamen stehen.
Das mit der Wertezuweisung bei der Deklaration ist natürlich eine ganz andere Sache. Ist die Frage, was schöner ist. Aber in C ist es bisher auch nicht anders abgelaufen wie in BB. Von daher nochmal ein Sorry für meine Verwirrtheit Wink

Komma vergessen? Let's mess!
Noch schlimmer wird die soeben genannte Geschichte, wenn man Kommata vergisst. Betrachten wir diesen Code:
BlitzBasic: [AUSKLAPPEN]
Global Variable1 Variable2 = 2
DebugLog(Variable1)
DebugLog(Variable2)

Dies gibt 0 und 2 aus. Es wurden also auf jeden Fall schonmal zwei Variablen definiert (wenn auch wieder verwirrend initialisiert). Dumm nur, dass Variable1 eine globale Variable geworden ist und Variable2 eine lokale. Verwirrung pur.
Was hier passiert, ist dass Variable2 keine explizit deklarierte Variable ist (man vergesse nicht, dass in Blitz Variablen zuvor leider nicht als Local / Global oder Dim deklariert werden müssen, sondern einfach "On-The-Fly" genutzt werden können). Sie wird demnach als Local definiert wie jede andere nicht zuvor deklarierte Variable.
Warum jetzt allerdings überhaupt eine zweite Variable nach einer anderen Definition genannt werden kann, ohne dass man ein Komma erwähnt, ist mir noch nicht ganz klar geworden, der Grund liegt ggf. woanders - oder es gibt garkeinen.
Nachtrag: Xeres hat hinzugefügt: Ein Statement kann auch durch ein Leerzeichen abgeschlossen werden, nicht nur durch einen Zeilenumbruch. Dadurch ergibt die Verhaltensweise von BB wieder einen Sinn. Der Compiler sieht im Grunde folgendes:
BlitzBasic: [AUSKLAPPEN]
Global Variable1
Variable2 = 2
DebugLog(Variable1)
DebugLog(Variable2)

Da ich mich darauf verlassen habe, dass nur Zeilenumbrüche das Statement abschließen, konnte ich das nicht richtig deuten. Die on-the-fly deklarierte Variable2 wird dementsprechend natürlich Local, und Variable1 bekommt keinen Wert zugewiesen.

Wissenschaftliche Schreibweise darf man nicht selber verwenden
Wir kennen ja, das Blitz gern extrem große oder kleine Zahlen in der wissenschaftlichen Schreibweise mit e ausgibt:
BlitzBasic: [AUSKLAPPEN]
Global zahl# = 0.000000000000000003
DebugLog(zahl)

Das gibt "3.e-018" aus.
Aber wehe dem, der versucht, das selber so im Code zu schreiben:
BlitzBasic: [AUSKLAPPEN]
Global zahl# = 3.e-018
DebugLog(zahl)

Das lässt sich garnicht erst compilieren und bricht mit der Fehlermeldung "Function e not found" ab.
Okay, das macht es für mich einfacher, Literale (also Zahlen) zu erkennen.
Dennoch merkwürdig, wieso der Compiler hier eine Funktion vermutet. Hinter einem Punkt hätte ich eher einen Type erwartet. Aber naja, mir ist das egal Wink

Unterstrich am Anfang eines Variablennamens ist verboten
Was mir jede andere bekannte und breiter verwendete Programmiersprache erlaubt, geht in Blitz3D nicht: Unterstriche am Anfang von Variablennamen:
BlitzBasic: [AUSKLAPPEN]
Global bla
Global _laber

Wegen der zweiten Zeile lässt sich das Programm nicht kompilieren. Schade. Gerade in C# nutze ich den Unterstrich so, um Felder einer Klasse zu kennzeichnen. Na gut, Klassen hat B3D ja eh nicht Smile
Ach ja, das Fake-Leerzeichen ist am Anfang allerdings erlaubt!
Code: [AUSKLAPPEN]
Global  bla
Global  bla

Das deklariert "bla" und " bla" als eigenständige Variablen. Die endgültige Methode meines Classifiers sieht demnach am Ende so aus, um den gültigen Start von Variablennamen zu erkennen (C#):
Code: [AUSKLAPPEN]
private static bool IsNameStart(char character)
{
    int asciiCode = (int)character;
    return (asciiCode >= 65 && asciiCode <= 90)  // Uppercase letters
        || (asciiCode >= 97 && asciiCode <= 122) // Lowercase letters
        || asciiCode == 255;                     // Non-breakable space
}





Sicherlich fallen mir noch mehr Dinge auf, welche ich in einem nachfolgenden Teil ansprechen werde.

Das man alle genannten "Features" dazu nutzen kann, um unglaublich verwirrenden und mülligen Code schreiben zu können (Stichwort Obfuscation?), sollte klar sein:
Code: [AUSKLAPPEN]
Global Pflaumen Apfel = 1
Global Pfirsiche Trauben = 2
Const Bananen = 3

Apfel = Apfel + 4
Trauben = Trauben + 3
Bananen = 5

DebugLog(Pflaumen)
DebugLog(Pfirsiche)
DebugLog(Trauben)
DebugLog(Apfel)
DebugLog(Bananen)


Ausgabe: 0, 0, 3, 5 und 5. Und ja, es lässt sich compilieren. Smile Wer erklärt, warum? Wink Achtung: Kann sein, dass der Code-Tag hier im Post Fake-Leerzeichen in normale umwandelt.

Aller Anfang ist Blitz3D

Sonntag, 15. Juni 2014 von PacMani
Hallo liebe BBler!

Wie vor einiger Zeit angekündigt, habe ich vor, Blitz3D und anschließend BlitzMax in Microsoft Visual Studio zu integrieren. Visual Studio ermöglicht eine sehr flexible und leistungsstarke Integration von eigenen und anderen Sprachen. So wurde bereits Python und PHP komplett mit Debugging-Funktionen, IntelliSense, Syntax-Hervorhebung und Tooltips implementiert, die dem Entwickler das Leben leichter machen. Interessant ist auch, dass man mit VS Integrated eine komplette Blitz-IDE zur Verfügung stellen kann, die Visual Studio-Technik unter der Haube benutzt und auch so aussieht - und das völlig kostenlos für andere. Die unterstützt dann halt nur B3D und im Nachhinein BMax, nicht die anderen Microsoft-Sprachen.

Nun bin ich mit der Integration von zunächst Blitz3D und anschließend BlitzMax dran. Aber, warum eigentlich? Ist Blitz3D nicht von vornerein veraltet und man sollte nicht gleich BlitzMax nehmen? Ich habe darüber lange nachgedacht und kam zu dem Entschluss: Geh' es langsam an.
  • Blitz3D ist deutlicher einfacher aufgebaut als BlitzMax. Kein "echtes" OOP, keine Zeilenumbrüche - das klingt banal aber macht die Implementierung der Sprache in VS deutlich leichter. Schließlich muss ich Parser, Lexer, Tokenizer, Colorizer etc. selber programmieren, um den Mist zu deuten, den der Programmierer verbockt hat Wink. Erst dann kann ich Syntax hervorheben oder IntelliSense anbieten.
  • BlitzMax hat es dagegen in sich. Echte Klassen und dazugehörige Methoden machen schon die Behandlung von sogenannten Scopes komplizierter - also wann eine Methode aufgerufen werden kann (nur von der Instanz der Klasse) und wann nicht.
  • Deswegen gehe ich BlitzMax erst danach an und benutze meine gewonnenen Kenntnisse in Blitz3D, um Support für BMax anzubieten.


Das tolle an den Visual Studio-Erweiterungen (sogenannte "Packages") ist, dass ich ein Feature nach dem anderen hinzufügen kann. Also erstmal die Syntax-Hervorhebung, und später dann, wenn ich Lust habe (Razz) z.B. IntelliSense. Im Grunde funktioniert so eine Erweiterung wie ein Eimer, in den man Features hineinwirft.

Welche Features habe ich in welcher Reihenfolge vor?
  • Syntaxhervorhebung. Der Tokenizer, der hierfür notwendig wird, kann von vielen nachfolgenden Komponenten genutzt werden.
  • Automatische Einrückung und Formatierung. Am besten vom Benutzer einstellbar.
  • Automatische Klammerergänzung.
  • B3D-Projektmappen und externen Kompiler anbinden, damit die Arbeit in Visual Studio erst richtig Sinn ergibt.
  • Outlining von Text, sprich Ein- und Ausklappen von Functions oder Types.
  • Anweisungsvervollständigung. Sowas wie IntelliSense.
  • Navigationsleiste (die Comboboxen über dem Visual Studio-Texteditor)
  • Tooltips. So kann ein über der Funktion stehender Kommentar als Tooltip angezeigt werden, wie javadoc oder XML-Summaries.
  • Syntaxfehler erkennen und unterstreichen. Das kommt erst so spät dran, weil ich hier noch nicht genau weiß, wie ich in VS vorzugehen habe.


Wenn sich der Compiler und Debugger von Blitz3D aufknacken lässt, würde ich auch gerne ein integriertes Debugging in Visual Studio ermöglichen. Nur leider wird der Blitz3D-Compiler da recht unzugänglich sein. Mal sehen, was sich machen lässt.

Die Arbeit am Syntax-Highlighting habe ich bereits begonnen. Die ersten Ergebnisse nach einer Stunde Arbeit: schrullig, aber Proof-Of-Concept geeignet Wink
user posted image