Planung B++ 2.0
- Lokale Klassen bzw. Datentypen
- Hier müssen Änderungen in der Serialisierung/Deserialisierung vorgenonommen werden, so dass beim Deserialisieren keine Namenssuche in globalen Dictionarys nötig ist. --> Ansatz: Auflösung wie bei gewöhnlichen Daten-Dictionarys.
- Im Prinzip sind lokale Datentypen nicht nur in Klassen, sondern auch in Funktionen denkbar.
- Virtuelle Methoden wie in C++, d.h. frühe Bindung, wenn eine Methode nicht explizit als virtual deklariert ist. Problem: Wenn der Aufrufer nicht statisch typisiert ist, ist auch keine frühe Funktionsbindung möglich --> Dann muss auch eine nicht virtuelle Methode wie eine virtuelle aufgerufen werden.
Allenfalls könnte man einen Laufzeitfehler generieren, falls der Versuch gemacht wird, einen virtuellen Aufruf einer nicht virtuellen Methode durchzuführen.
Alternativ könnte eine Option die statische Typisierung von Objekten verlangen oder der virtuelle Aufruf dann eben einfach akzeptiert werden. Letzteres ist wahrscheinlich das Vernünftigste:
Frühe Bindung wird einfach nur dann gemacht (als Mittel höheer Effizienz), wenn
- bei einem klassischen Aufruf mit -> von einem statisch typisierten Objekt eine nicht virtuelle methode aufgerufen wird oder
- der Aufruf mit der erweiterten Form -->(expr) erfolgt und expr einen Methodenzeiger liefert.
- Generische Datentypen/Klassen
- Standardcontainer können sich ihren Elementtyp merken
- typedef und using wie in C++: So entstehen benutzerdefinierte Typen, wobei die Gültigkeit lokal zu, jeweiligen Block ist.
- Coroutinen-Unterstützung
- +In CodeVec einer Bytecode-Funktion neuer Index IDX_FUNCSTATE (Vektor bei Coroutine nach yield, sonst null)
- Element vom Typ vector [uint nextopcode, vector locals, vector arguments]
- +neues Schlüsselwort yield und neuer Opcode OP_YIELD:
- Verhalten im Prinzip wie return
- +Compiler setzt für die Funktion das BVF_STATIC-Flag, damit clone eine Funktionskopie erzeugt
- +BppValue löscht beim Klonen einer Funktion immer den Wert bei IDX_FUNCSTATE im Resultat
- +Beim Serialisieren einer Funktion wird IDX_FUNCSTATE übergangen (als null ausgegeben)
- Interpreter:
- bei OP_YIELD wird der aktuelle Funktionszustand (nächster Opcode-Index, lokale Variablen, Argumente) als Vektor bei IDX_FUNCSTATE eingetragen
- Anzahl lokale Variablen ist Argument von OP_TSPACE (1. Opcode in Bytecode-Funktion), erste lokale Variable bei FP+1
- erstes Argument suf Stack bei BP+1
- bei OP_RETURN wird der Funktionszustand gelöscht.
- Beim Aufruf von call wird geprüft, ob ein Vector bei IDX_FUNCSTATE vorhanden ist.
- Falls ja:
- Initialisieren nicht gesetzter formaler Argumente mit den gespeicherten,
- Anlegen der lokalen Variablen auf dem Stack als Kopie der gespeicherten,
- Wert an IDX_FUNCSTATE auf null setzen
- Sprung zum gespeicheten Opcode-Index
- sonst: Verhalten wie bisher
- Zusätzliche Funktion bool done(function), die true liefert, wenn IDX_FUNCSTATE leer ist.
- Coroutionen werden wie normale Funktionen geschrieben und entstehen implizit, indem sie mit yielt statt return verlassen werden
- Der erste Aufruf erfolgt wie bei einer normalen Funktion; bei weiteren Aufrufen nach yield (also zur Fortsetzung) können formale Parameter weggelassen werden. Sie werden dann automatisch durch die gespeicherten Parameterwerte ersetzt. Lokale Variablen haben immer den Wert, den sie bei yield hatten.
- Coroutinen können nach einem yield nicht rekursiv aufgerufen werden - es käme Unfug heraus. Ein rekursiver Aufruf ist auf einer Kopie (mit clone erzeugt) möglich, allerdings bleiben Änderungen lokaler statischer Variablen dann auf die Kopie beschränkt.
- Coroutinen können im Prinzip auch Elementfunktionen von Objekten sein.
Änderungen nach Version 1.5
- Bugfix move fpr buffer, charbuffer und vector (Indexprüfung berichtigt)
- Bugfix in BppValue::asBool (Fehler bei Test auf BVT_STRONGOBJECT berichtigt)
- Bugfix in BppVector::rfind (Indexfehler berichtigt)
- Bugfix insert für buffer und charbuffer (size wurde nicht gesetzt)
- Bugfix: BppString(const BppChar* format,unsigned nargs, ValPtr& args) akteptiert auch BVT_UINTPTR, Gleiches gilt für Funktion newstring(string fmt,...)
- Funktion clear akzeptiert auch string als Argumenttyp
- Funktion find akzeptiert als Container-Argument auch den Typ string
- Funktion getclass wirft keine Ausnahme, wenn einer per Name angegebene Klasse nicht gefunden wurde. Stattdessen ist das Ergebnis null.
Änderungen nach Version 1.4
- Datentyp bool (hat noch Eingang in Version 1.4 gefunden)
- Datentyp uintptr
- vordefinierte Literale CHARSIZE (interne Zeichengröße) und WCHARSIZE (Typgröße von wchar_t)
- CStruct unterstützt neue Datentypen (bool und Zeichentypen), Packing, Unions, dynamische Strukturen, anonyme Strukturen
- echte Character-Datentypen (char, uchar wchar_t, char8_t, char16_t, char32_t - char8_t hat 32 Bit und speichert ein UTF-8-Zeichen
- Funktion insert (für string und charbuffer) sowie operator+ für string konvertieren übergebene einzelne Zeichen in TCHAR
- dictio ünterstützt Zeichentypen und Strings mit 32-Bit-Zeichen (mit Konvertierung von/nach internem Zeichensatz)
- Konvertierungsfunktionen (cast) für Character-Datentypen
- Zeichentypen explizit in string konvertierbar (ggf. Umwandlung in internen Zeichensatz)
- BppBuffer in char8_t konvertierbar
- Zeichentypen von/nach integralen Typen konvertierbar
- Scanner::readString interpretiert Präfixe u und U als Unicode mit anschließender Konvertierung in internen Zeichensatz
- Scanner::readChar interpretiert Präfixe u und U als Unicode und speichert Zeichentyp in _numType
- Scanner::getNumberValue kann (unter Berücksichtigung von _numbertype) auch Zeichen zurückgeben
- Scanner erkennt Quellcode-Zeichensatz und führt Konvertierung in internen Zeichensatz durch
- Hilfsprojekt collectcodepages - generiert Konvertierungstabellen zwischen utf16 (eigentlich UCS-2) und Singlebyte-Zeichensätzen für alle bekannten Windows-Codepages (codetab.inc)
- Lokale Klassen
- Noch offen ist Sichtbarkeit (besonders in Bäumen geschachtelter Klassen), vorerst ist alles public
- Namensräume (rudimentär)
- Schlüsselwort namespace erzeugt Klasse mit Einschränkungen:
- kein Ctor, kein Dtor
- keine Vererbung
- keine Zugriffsspezifizierer (alles public)
- Keine Instanzvariablen
- Methoden immer static
- #define ist Textersetzung wie in C; auch 'Funktionsmakros' einschließlich variadischer Makros möglich; Sonderfunktion von ## als Begrenzer leerer __VA_ARGS__ (entfernt benachbartes Komma, falls __VA_ARGS__ leer, wie beim gcc; __VA_OPT__ wird nicht unterstützt)
- neue Präprozessor-Direktiven #error und #warning
- signed und unsigned wie in C, zusätzlich auch für byte möglich
- litexpr funktioniert auch für +-Operator mit Strings
- #pragma-Direktive mit
- #pragma message <litexpr> als string
- #pragma cdefine on|off|push|pop (Umschaltung zwischen define in C-Manier und deflit-Alias
- #pragma codepage <litexpr> als string oder uint setzt Codepage des Quelldatei (direkt als Zahl oder indirekt über Name)
- neue Funktionen getDataBuffer und getDataSize in CStruct
- Callback-Unterstützung (standardmäßig nicht für DOS)
- neue Funktion GetCallbackFunc(uint nparams) mit 1 bis zu 8 Parametern, letzter muss Funktor füt Rückruf sein
- neue Funktionen GetStaticCallbackFunc und SetStaticCallbackHandler für Callbacks ohne 'UserData'-Parameter: 0 bis 4 Parameter, je Callbacktyp bis zu 5 mögliche Instanzen; Instanzen in globalem Array.
- Schlüsselwort const
- zur Konstantendefinition:
- Konstanten sind global, als (implizit statische) Member von Klassen, als Funktionsargumente sowie statische oder temporäre 'Variable' in Funktionen möglich.
- Konstanten müssen initialisiert werden. Die Initialisierung entspricht der von Variablen, d.h. temporäre Konstanten werden zur Laufzeit, alle anderen statisch (mit Literalen) initialisiert.
- Zuweisungen an Konstanten (=, []=, .=) werden sowohl vom Compiler als zur Laufzeit abgefangen
- Bei Zuweisungen von Konstanten an eine Variable geht die const-Eigenschaft verloren; im Fall von Referenztypen wird eine tiefe Kopie (clone) erzeugt.
- Innerhalb von konstanten Objekten sind keine Zuweisungen am Member erlaubt (Laufzeitfehler, der Compiler kann das nicht herausfinden).
- als Modifizierer eines Rückgabetyps, syntaktisch wie C++
- als Spezifizierer einer Elementfunktion, syntaktisch wie C++, (macht innerhalb der Funktion den this-Zeiger const), Prüfung vom Compiler und zur Laufzeit
- Abweichung von C++: Der Aufruf nicht mit const deklerierter Funktionen ist auch von const-Objekten aus möglich. Fehler werden erst bei der Modifikation nicht als mutable gekennzeichneter Elementvariablen erzeugt. Das ist notwendig, um bestehende Methoden aus älteren Versionen (ohne const und mutable) uns native-Methoden weiter verwenden zu können,
- Im Zusammenhang mit const gibt es die neuen Opcodes OP_MKCONST (temporäre Vaileble const machen) und OP_SPCONST (Wert bei SP const machen), letzterer wird für konstante return-Werte verwendet.
- Neue Funktionen isconst und mkconst für Abfrage unf Modifikation des const-Flags eines Wertes
- Schlüsselwort mutable:
- Wird im Prinzip gebraucht wie in C++, lässt Schreibzugriff auf Elementvariablen in const-Methoden oder Methoden auf const-Objekten zu.
- Abweichung vom Verhalten in C++ beim direkten Variablenzugriff über den Punktoperator: Hier ist mutable nicht wirksam, für den Operator zur Übersetzungszeit nicht zwingend klar ist, worauf er aufgerufen wird (Dictionary oder Objekt - und wenn Objekt, dann von welchem Typ).
Würde man für die Property-Operatoren zur Übersetzungszeit das const-Flag ignorieren, wäreein Abfangen unzulässiger Zugriffe generell nur zur Laufzeit möglich. Will man eine mutable-Variable auf einem const-Objekt ändern, muss dazu eine Methode geschrieben werden (das kann auch eine OP_PSET-Operatorfunktion sein).
- Die bisherigen vordefinierten Variablen __OSTYPE__, __UNICODE__ und __PTRSIZE__ sind jetzt Konstanten.
- Namensräume ähnlich C++ mit namespace identifier { ... }
- spezielle Klassen mit nur statischen Elementen; nicht instantiierbar, nicht vererbbar, alle Elemente public
- keun using namespace
- keine anonymen Namensräume
Änderungen nach Version 1.3
- Sortierte Dictionary-Literale mit {! ... }
- Buffer-Literale (Ressourcenliterale mit << litexpr, wobei litexpr einen String ergeben muss, der einen Dateinamen bzw. relativen oder absoluten Pfad repräsentiert. Die Suche nach der Datei erfolgt zur Kompilierzeit nach denselben Regeln wie für in Anführungszeichen eingeschlossene include-Dateien.
- Korrektur Returnwert bei Returntype null und Verbot von Variablen des Typs null
- Korrektur: Typ und Standardwert können in Argumentlisten, nicht aber in der Variablenliste im Funktionskopf angegeben werden.
- Korrektur: Zur Initialisierung statischer (einschließlich globaler) Variablen verwendte Literale werden kopiert (bei lokalen Variablen war das schon immer so).
- Verallgemeinerte Containerfunktionen: clear, erase, insert, reserve, resize, switch, move, slice, find, rfind
- neue Containerfunktionen lbound, ubound, bsearch,insrange
- vordefiniertes Literal NPOS = uint(-1)
- Containerfunktionen verwenden uint für Längenangaben
- #defVar tut nichts, wenn bereits eine entsprechende Variable existiert
- static auto ist auch in Funktionen möglich sein (in Klassendeklarationen ging es schon immer)
- neue Funktion strftime - Im Prinzip analog zu C, jedoch kann Parameter tm entweder ein vector (wie das Ergebnis von localtime oder ein Unix-Timestaamp (wie Ergebnis von time() ) sein.
- Längere Zeichenkettenliterale sind in bppmsg.h/.cpp ausgelagert und als Zeichenkettenkonstanten definiert (und so nicht mehr als Literale im Code verstreut).
Änderungen nach Version 1.2
- +Erweiterte Suffixe s und L0..n an Integer-Literalen: s erzwingt Wert mit Vorzeichen, Ln legt Größe in Bytes fest. S darf nicht mit U, Ln nicht mit L kombiniert werden. L0 bedeutet kleinstmögliche Größe, weitere gültige Werte für L: 1,2,4,8
- +Korrektur OP_PINC/DEC, Opcodes OP_STOS und OP_LDTOS (Speichern und Laden des SP-Inhalts in/aus temporärer Speicherzelle in ProcInfo-Struktur
- Korrektur OP_REF, OP_MREF, OP_AREF, OP_VREF und OP_PREF: Im Zuweisungsziel wird das STRONGTYPED-Flag explizit zurückgesetzt. Dies verhindert, dass (so war es bisher) der zugewiesene Wert streng typisiert ist, auf der betreffenden Stackpositionzuvor ein streng typisierter Wert stand, was bisher zu Fehlern führte, wenn in einer Schleife Containerelemente unterschiedlicher Typen ausgelesen oder Fuktionen mit variablen Rückgabetypen aufgerufen wurden.
- Korrektur OP_TSET: Der Operator tut nichts, falls der zuszweisende Wert vom Typ AUTO und das Ziel null; ist. Das kommt bei im Code eingefügten Initialisierungsanweisungen für mit auto deklarierte temporäre Variablen vor und führte bisher zu Fehlern, falls eine auto-Variable innerhalb einer Schleife definiert wurde.
Generell ist es ungünstig, streng typisierte Variablen in einer Schleife zu definieren, da die zusätzliche Initialisierungsanweisung (TREF) Zeit kostet.
- In bppdef.h, Zeile 7 wird die B++-Versionsnummer als #define _BPP_VER 130 definiert
- +Neue benannte Literale PTRSIZE (uint, Zeigergröße 4 oder 8) und __bplusplus mit Versionsnummer als Wert
- +Neue vordefinierte globale Variable __PTRSIZE__, ebenfalls mit Zeigergröße in Bytes
- Bugfix Typkonvertierung buffer->Gleitkommazahl
- +Bei explizit angegebenem Rückgabetyp einer Funktion wird bei Bedarf (eigentlicher Rückgabetyp stimmt nicht mit dem geforderten überein) eine implizite Konvertierung des Ergebnisses vorgenommen. Das entspricht dem Verhalten von C++.
- +Neue vordefinierte Funktionen Class getclass(var arg) --> liefert Klasse von Objekt oder Zeichenkette (Klassenname) und object newobject(Class classtype, ...) --> erzeugt Objekt über Klassentyp.
- +Neues Schlüsselwort 'litexp' zur expliziten Kennzeichnung literaler Ausdrücke (ersetzt #, # bleibt zur Abwärtskompatibilität benutzbar, es darf jedoch kein Leerzeichen zwischen '#' und '(' stehen).
- preproc-Aufrufe als Callback aus Scanner::getToken() --> Damit können Verarbeitungsanweisungen an beliebiger Stelle stehen.
- Emulation einfacher C-Strukturen mit vordefinierter abstrakter Klasse CStruct
- +Neue vordefinierte I/O-Funktionen freopen, fflush, ftell und fseek
- +neue vordefinierte Literale SEEK_SET (0), SEEK_CUR (1) und SEEK_END (2)
Änderungen nach B++1.1
- Modifizierte null-Vergleiche:
Der Wert null (Typ BVT_NULL ist auch gleich Werten von Nicht-Value-Typen, sofern sie auf die Adresse NULL verweisen, also keine gütigen Daten besitzen. Dies kann nach der Zuweisung von null an eine statisch typisierte Variable sowie bei Weak-Referenzen auf eine solche Variable vorkommen.
- Move-Semantik für VS > 2005 an manchen Stellen
- Korrektur Implementierung operator++/--(int) von ValPtr
- aktualisiertes (separates) Setup für 32Bit mit VS2005 (läuft auf XP)
- Bp2Dict mit bidirektionalen Iteratoren und reverse iterator
- Dictionary-Implementierung mit doppelt verketteter Liste für schnelleres Einfügen
- kleinere Optimierungen zur Laufzeitverbesserung
- Opcode-Behandlung im Interpreter in Einzelfunktionen aufgeteilt
- Debugger-Callbacks haben zusätzlichen Parameter zur Übergabe der VM, was das Debuggen mehrerer VMs erlaubt
- Bedingte Übersetzung ähnlich C-Präprozessor
- Kommandozeilenversion mit Option -D für vordefinierte Symbole
- Kommandozeilenversionen mit Option -L zum Linken gegen eine beliebige Runtime
- BppRegEx::operator .= gibt zugewiesenen Wert (statt Objekt) zurück
- Funktion litexpr zur Auswertung eines literalen Ausdrucks zur Laufzeit, insbesondere zum Lesen von JSON-Daten
- Funktionen strlwr und strupr
- Ellipse zur (optionalen) Kennzeichnung variabler Parameterlisten
- elementare Unix/Linux-Unterstützung
Änderungen von B++ 1.1 gegenüber 1.0
- Neu: Native-Klasse RegEx zur Unterstützung Regularer Ausdrücke (basiert auf T-REX)
- Neu: Zusatzbibliothek BppXML (B++-Bibliothek) - XML-DOM, Parser, Serialisierung
- Änderung: Funktion call akzeptiert als ersten Parameter neben Funktionen auch Objekte, die operator() definieren.
- Bugfix: Bug bei der Division vorzeichenloser 64Bit-Ints in der emulierten 64-Bit-Arithmetik (trat auf, wenn höchstwertiges Bit tatsächlich gesetzt war)
- Bugfix: Bug bei der Serialisierung/Deserialisierung von Bytecode-Modulen mit Klassenstrukturen, die Vorwärtsdeklarationen von Klassen enthalten.
Konsequenz ist geändertes Serialisierungsformat (Klassen schreiben die Anzahl ihrer Datenmember) -> Neuübersetzen von Modulen nötig.
- Bugfix: Bug im Interpreter bei OP_NOT auf 64-Bit-Plattformen: Hier konnte 0 nicht 0 sein, wenn für den betreffenden Typ ungenutzte Bits 63-32 gesetzt waren.
Änderungen von BOB+2 gegenüber BOB+
Allgemeines
- Speicherverwaltung mit Referenzzählung
- Stack und Länge von Symbolen sind nur durch den Speicherplatz begrenzt
- Es gibt keine explizite Begrenzung der Schachtelungstiefe von Schleifen mehr.
- BOB+2 ist eine gekapselte virtuelle Maschine, von der in einem Prozess mehrere
unabhängige Instanzen existieren können. Die Kommandozeilenversion bp2.exe ist lediglich
eine Minimalanwendung, die Startparameter von der Kommandozeile übernimmt und Ein-
und Ausgaben über die Konsole abwickelt.
- Alle Textausgaben erfolgen in BOB+2 über eine abstrakte Print-Schnittstelle, die
von jeder Applikation nach Belieben implementiert werden kann. Die BOB+2-Bibliothek
bringt vordefinierte Implementierungen für Konsolenausgaben und - in der 32-Bit-Version
- für Ausgaben über einen installierbaren Callback mit. Normale Ausgaben sind von
Debug- und Fehlerausgaben unterscheidbar, was die Einbindung in Entwicklungsumgbungen
erleichtern soll.
- zusätzlicher Zeilenende-Kommentar, mit #! eingeleitet
- Pseudo-Schlüsselwörter TRON, TROFF und TRSTEP für Debugging (eingeführt mit BOB+
1.1a),
Wartefunktion für Step ist definier- und installierbar (z.B. für Umgebungen ohne
Konsole)
- Neues Schlüsselwort var
... dient der Definition lokaler Variablen;
ersetzt (oder ergänzt) Variablen-Definitionsliste im Funktionskopf (alte Form weiter
möglich)
Syntax: <varstatement> := "var" <identifier>
[ "=" <expr> ] { "," <identifier>} [ "=" <expr> ]} ";"
.
Das var-Schlüsselwort leitet eine Definitions-Anweisung ein und kann stehen, wo
eine normale Anweisung stehen kann.
<varstatement> ist auch an Stelle des Initialisierungsausdrucks einer for-Anweisung
zulässig.
Der Gültigkeitsbereich ist immer der der definierenden Funktion (nicht der Block!!).
- var und
null und void vor Deklarationen:
Dort, wo in C++ der Rückgabetyp einer Funktion (auch Member-Funktion) steht, kann
optional var oder
null stehen. In Member-Variablen-Deklarationen ist nur
var zulässig. Die Verwendung beider Schlüsselwörter ist optional
und ohne tatsächliche Bedeutung. Sie können aber die Lesbarkeit verbessern und sind
im Zusammenhang mit Doxygen von Vorteil.
- --> Neu: Auch globale Variablen können mit var, null oder function
bzw. einem Typnamen deklariert werden. Zur Initialisierung kann ein Literalwert
verwendet werden.
Wie bei liokalen Variablen oder Variablendefinitionen in Klassen, sind auch Listen
möglich, wobei alle Elemente vom gleichen Typ sind.
- --> Neu: Es ist nicht mehr möglich, globale Variablen implizit
durch Verwendung im Quellkode anzulegen - sie müssen stets deklariert werden.
- --> Neu: Es gibt eine optionale statische Typisierung. Neben
var, null, void und
function können als Typangaben auch
die Bezeichner der vordefinierten Typen int,
bool (Synonym für int), float, double, string, vector, dictionary, buffer,
charbuffer, object und FILE
verwendet werden. Außerdem kann als Typ der Bezeichner einer zum Zeitpunkt der Übersetzung
bereits deklarierten Klasse stehen, was aber letztlich nur der besseren Lesbarkeit
dient und streng genommen einfach den Typ object
meint.
Klassen- und Referenzvariablen können nicht statisch typisiert werden, dafür gibt
es schlicht kein syntaktisches Ausdrucksmittel.
Typisierungen sind überall möglich, wo bisher 'var' verwendet werden konnte - also
bei der Deklaration von Variablen (global, lokal, in Objekten, statisch oder nicht).
'Echte' Typnamen bewirken eine Überprüfung der Gültigkeit bei der Zuweisung - der
Compiler erledigt das bei der Initialisierung mit Literalen, sonst erfolgt die Prüfung
zur Laufzeit.
- --> Neu: Nicht-statische Variablen in Objektdefinitionen können
mit Literalwerten initialisiert werden (wie die statischen Variablen).
- Für Funktionen (und Operatordefinitionen) kann ein Rückgabetyp festgelegt werden, indem man
bei der Definition statt des bisher schon möglichen (optionalen) var bzw. null einen
Typ (einschließlich void und function) angibt. Für den Rückgabetyp void beschwert
sich der Compiler, falls explizit etwas zurückgegeben wird (Argument hinter return),
bei anderen Rückgabetypen beschwert er sich bei einem return ohne Argument.
- Neues Schlüsselwort function:
... erlaubt die Definition einer Funktion oder Methode als Literal. Damit können insbesondere
anonyme Funktionen als Rechtswert eines (Zuweisungs-)Ausdrucks definiert werden.
Syntax
"function" [ "[" <classid>
"]" ] [<returntype>] "(" [<argumentlist>]
[ ";" <localvarlist>] ")" "{" { <statement>} "}" .
Achtung: Das Konstrukt wird wie ein Literal behandelt und kann überall dort
vorkommen, wo ein Literal zulässig ist. Somit kann - sofern es als Abschluss einer
Anweisung notiert wird - ein nachfolgendes Semikolon erforderlich sein (trotz der
schließenden Klammer des Anweisungsblocks).
Durch die optionale Angabe einer Klasse (Identifier oder Klassenname als String),
eingeschlossen in eckige Klammern, lässt sich eine Objektmethode als Literal definieren.
Eine solche Methode kann mit Hilfe des Operators ->(expr)() im Kontext
einer Objektinstanz aufgerufen werden und ähnelt so einem Elementfunktionszeiger
in C++.
function kann auch als Typbezeichner
zur Deklaration einer Variablen oder als Returntype einer Funktion verwendet werden.
- Reservierter Bezeichner self:
Eine Funktion (oder auch Methode eines Objekts) kann sich mit "self" "(" [<argumentlist>] ")" selbst
aufrufen. Dies ist vor allem für anonyme Funktionen gedacht, die sonst keinen Möglichkeit
zum rekursiven Aufruf hätten.
self kann auch innerhalb von Member-Funktionen verwendet werden - im Unterschied
zu "normalen" Aufruden wird hier keine Suche über den Methodennamen durchgeführt,
sondern es erfolgt eine direkte Bindung des Codes. Dadurch können bei rekursiven
Aufrufen Geschwindigkeitsvorteile erzielt werden.
- Schlüsselwort static kann auch im
Rumpf einer Funktion verwendet werden und definiert dann eine oder mehrere lokale
statische Variable(n).
Syntax:
"static" [<typedefinition>] <identifier> [ "=" <litexpr>
] { "," <identifier>}
[ "=" <litexpr> ]} ";"
- Erweiterungsschnittstelle für in C++-DLLs implementierte Funktionen und Klassen
- VM hat C-API zur Verwendung in Nicht-C++-Umgebungen
- Ausnahmebehandlung (try-catch) --> neue Schlüsselwörter try, catch, throw
- Fallunterscheidung (switch-case-default) --> neue Schlüsselwörter switch, case,
default
- Unterstützung für Quelltext-Debugging:
- VM hat Funktion setGenerateLineInfo zum Einschalten der Zeileninformationen
- Wenn eingeschaltet generiert der Compiler Zeileninformationen (eigener Opcode)
- Interpreter ruft auf VM Funktion notifySourceLineChanged zurück, die ihrerseits
einen installierbaren Callback auftuft
- Kommandozeilenversion hat Option -l zum Einschalten
Literale
- Array-Literale:
Arrays bzw. Vektoren können als Literale initialisiert werden, z.B:
myArray = [1,2,3];
otherArray=["ich","du", [1,2,3],"er",'\n',0x23]; // gemischte Typen, eingebettetes
Array
- Dictionary-Literale:
myDict = { "Name" : "Müller", "Alter" : 52, Kinder
: ["Anne","Emil"]};
allgemein: { <key> : <value> [, ... ] }; <key> ist immer ein String oder Identifier,
<value> beliebig -->
entspricht JSON-Syntax
- Funktions-Literale: siehe oben (Schlüsselwort
function)
- Escape-Codes:
Zusätzlich zu den bereits in BOB+ unterstützten Escape-Codes werden beliebige Zeichencodes
erkannt, die nach einem Backslash als normale Integer-Literale notiert werden. Hexadezimale
Zeichencodes können auch nur mit x (statt 0x) beginnen.
Beispiele: "Die Zeichenkette \7kann piepen.", 'Zeilenumbruch:\0xD\xa", '\0xFF'
- Zeichenkettenliterale können wie in C++verkettet werden:
s = "Dieser String "
"gehört zusammen."
Außerdem sind mehrzeilige Strings mit Backslash am Zeilenende zugelassen.
- Literale Ausdrücke:
Mit Literalen können Ausdrücke gebildet werden (im Prinzip wie auch sonst), wobei
die Operanden selbst Literale sein müssen.
Ausdrücke rechts von Initialisierungszuweisungen (static var, static member), #defvar und
#deflit-Instruktionen werden automatisch als Literalausdrücke erkannt. An anderen
Stellen kann ein Ausdruck mit #(<litexpr>)
explizit als Literal gekennzeichnet werden.
- Vordefinierte Literale:
true = 1, false = 0
Es gibt die vordefinierten Literale UNICODE, WIN32, WINCE und MSDOS, die jeweils
den Wert 1 haben, wenn beim Kompilieren die jeweilige Gegebenheit zutrifft und 0,
wenn nicht.
Darüber hinaus existieren in Anlehnung an C99 vordefinierte Literale __func__ (string),
__FILE__ (string) und __LINE__ (int) zur Angabe von aktuellem Methoden- oder Funktionsnamen,
Dateinamen bzw. aktueller Zeilennummer während der Übersetzung. Die werte dieser
Literale weden vom Scanner gebildet.
- Außerdem gibt es die Datentypen BVT_XXX als vordefinierte Literale
- Literale vom Typ string, vector und dictionary werden beim Zugriff darauf kopiert
(damit sie nicht zur Laufzeit geändert werden können); außerdem ist die direkte
Anwendung des []-Operators auf Literale verboten (Compilerfehler). , Funktionsliterale
werden kopiert, falls sie static-Definitionen enthalten
- Ganzzahlige Literale können auch binär, in der Form 0b1001 angegeben werden und
sind 32Bit-Integer.
Verarbeitungsanweisungen
- können auch in Klassendeklarationen und Blöcken innerhalb einer Funktion vorkommen
- sinnvoll für #include
- #include ist neu und funktioniert im Prinzip wie in C/C++
- Datei wird zur Übersetzungszeit an Stelle der #include-Anweisung eingebunden
- #include <myincfile.inc> ... sucht Datei im aktuellenVerzeichnis sowie Pfad
aus BPPINC-Umgebungsvariable
- #include "myincfile.inc" ... sucht Datei im aktuellen Verzeichnis
- In beiden Varianten sind relative Pfade zugelassen
- #use "mylib.bpm" sucht im durch BPPLIB
angegebenen Pfad, nicht mehr in PATH
- #import "mydll.dll"
sucht nach DLL mit"Native"-Modul in PATH
- #deflit <identifier> <litexpr>
definiert ein benanntes Literal (alias: #define)
- #literal <identifier> <litexpr>
definiert ein benanntes Literal, sofern es nicht bereits definiert ist
- #undeflit <identifier>
hebt die Definition eines benannten Literals auf (alias: #undef)
-
Eingebaute
Datentypen
- Fließkommazahlen können den internen Typ double (8 Bytes) oder float (4 Bytes)
haben. Standard ist double.
- Zusätzliche Referenz-Datentypen:
- Buffer : Puffer für beliebige Binärdaten
- CharBuffer : Puffer für eine Menge von Textzeichen - für schnelle Zeichenkettenmanipulationen
- Dictionary : Container für Schlüssel-Wert-Paare
- Referenz-Datentypen werden normalerweise automatisch - eben per Referenzzählung
- verwaltet. Sie können explizit davon ausgenommen werden, um sie "zu Fuß" wieder
wegzuräumen.
Vordefinierte Operatoren
- Es gibt einen speziellen Referenz-Zuweisungsoperator
=>
Er kann mit allen Referenztypen verwendet werden schließt die zugewiesene Objektreferenz
von automatischen Verwaltung über die Referenzzählung aus. Er dient in erster Linie
der Lösung des Problems von zyklischen Referenzen - dazu ein einfaches Beispiel:
Es sei eine Hierarchie von gleichartigen Objekten gegeben. Ein Objekt (Element,
Knoten im Hierarchiebaum) hat eine Menge (z.B. Liste oder Vektor) von Kindelementen.
Fügt man ein neues Element als Kind eines anderen ein, so wird der Referenzzähler
des erhöht (auf eins gesetzt, falls das Element neu erzeugt wurde). Wird nun der
Wurzelknoten gelöscht, so gibt er auch seine Kindknoten frei, d.h. vermindert deren
Referenzzähler um eins. Werden sie nirgendwo sonst verwendet, sterben auch
sie usw.
Anders sähe es aus, wenn die Kindknoten immer einen Rückwärtsverweis auf den Elternknoten
hätten. Dann würde beim Setzen dieses Verweises mittels einer normalen Zuweisung
der Referenzzähler des Elternobjekts erhöht und es könnte niemals freigegeben werden,
weil ja immer mindestens noch eine Referenz darauf (nämlich die des Kindknotens)
existiert. Mit zusätzlichen Operator wird dieses Problem umgangen, indem bei der
Zuweisung der Referenzzähler eben nicht erhöht wird.
Eine weitere - wohl wesentlich seltenere - Anwendung des Operators ist es, ein Objekt
von vornherein von der Referenzzählung auszunehmen. Das kann dann nötig sein, wenn
es knappe Systemressourcen beansprucht, die unbedingt zu einem bestimmten Zeitpunkt
freigegeben werden müssen. Ein von vornherein (uns ausschließlich) mit => zugewiesenes
Objekt kann mit dem delete-Operator
explizit gelöscht werden.
- Neu ist der .-Operator für den Zugriff auf Dictionary-Elemente sowie zur Property-Implementierung
für Objekte. Dafür kann der Operator überladen werden. In der Operatorenhierarchie
liegt er auf derselben Ebene wie [].
- Der Operator delete dient ausschließlich
der Freigabe von Objekten, die explizit von der Referenzzählung ausgeschlossen wurden.
Für alle anderen Objekte ist er zwar syntaktisch zugelassen aber wirkungslos. Anders
als in BOB+ kann er auf alle dynamischen Objekte - nicht nur auf Klasseninstanzen
angewendet werden. Er ersetzt damit die Freigabefunktion
free.
- Alle arithmetischen Operatoren können mit Operanden der Typen int, float oder double
verwendet werden. Bei unterschiedlichen Typen erfolgt eine implizite Umwandlung
in den größten Typ. Dies gilt auch für den Modulo-Operator (%), der bisher nur für
Ganze Zahlen verwendbar war.
- Hinzugekommen sind kombinierte Zuweisungsoperatoren für Bit-Operationen. also
|=, &=, ~=, ^=, <<= und
>>=. Diese Operatoren sind -
wie alle Bit-Operatoren - auf Integer-Operanden anwendbar
- Operator []: Der Operator ist auf Vektoren, Strings, Buffer, CharBuffer, Dictionarys
und Objekte anwendbar.
Bei Dictionarys ist das Argument vom Typ String, bei Objekten hängt der Argumenttyp
von der Implementierung des überladenen Operators ab, für alle anderen Typen wird
ein Index vom Typ Int verwendet.
- Operator ->: Der Operator dient wie bisher zum Methodenaufruf, statt eines Bezeichners
(Methodenname) kann jedoch auf den Operator ein geklammerter Ausdruck folgen, dessen
Ergebnis der Methodenname oder eine Funktion (genauer: Methode) sein kann., z.B.:
a->("foo")(); // Methodenaufruf über Namen
a->(b)(); // Methodenname oder Methode aus Inhalt von Variable b
Funktionen
- Funktionen können wie bisher mit mehr Parametern aufgerufen werden, als formale
Parameter definiert wurden. Anders als bisher (d.h. in BOB und BOB+) werden in BOB+2
nicht die letzten sondern die ersten übergebenen Parameter an die formalen Parameter
zugewiesen.
- Vorgabeparameter, z.B. myFunc(par1, par2 = 17);
Als Vorgabeparameter kann ein literaler Ausdruck stehen.
Vorgabeparameter können - zur Dokumentation - auch bei vorwärtsdeklarationen
verwendet werden, sind dort aber bedeutungslos.
Hat eine Funktion Vorgabeparameter, so wird sie implizit immer mit mindestans allen
definierten Argumenten aufgerufen (mehr sind möglich).
Deklaration von Klassen
- BOB+2 gestattet Vorwärtsdeklarationen von Klassen im C++-Stil, z.B.
class MyClass;
- Bei Klassendeklarationen ist hinter der schließenden Klammer (}) ein optionales
Semikolon zulässig.
Es sollte dann notiert werden, wenn eine Referenz mit Doxygen erzeugt werden soll
- sonst erkennt Doxygen das Ende der Deklaration nicht.
- Auch Methoden von Klassen können Vorgabeparameter haben
- Zur Vermeidung undefinierter Zustände ist der Compiler strenger:
- Ist eine Klasse von einer Basisklasse abgeleitet, so kann sie bei einer Neudefinition
(Redefinition, Erweiterung) nicht von einer anderen Basisklasse abgeleitet werden.
- Einer Klasse, die von einer anderen Klasse als Basisklasse verwendet wird, können keine zusätzlichen nicht-statischen Member-Variablen
hinzugefügt werden.
- Bereits definierte Member-Variablen können nicht nachträglich als static umdefiniert
werden.
Überladen von Operatoren
- OP_CALL, OP_VREF, OP_VSET wie bisher
- neu OP_PREF(key), OP_PSET(key,val) zum Lesen und Schreiben von Properties. Das Überladen
erfolgt analog zu OP_VREF/OP_VSET. key ist immer ein String (Identifier
bei der Verwendung im Quelltext)
- neu - unäres Minus: OP_NEG
- neu - unäres logisches Not: OP_NOT
- neu - Inkrement,Dekrement: OP_INC, OP_DEC (pre/post-inkrement funktioniert für
alle Datentypen)
neue Operatorfunktionen OP_PINC und OP_PDEC für explizize Definition der post-Varianten
Wird für eine Klasse OP_INC bzw. OP_DEC definiert, ohne auch OP_PINC bzw. OP_PDEC
zu definieren,
so fügt der Compiler entsprechende Definitionen ein, die den Code von OP_INC/OP_DEC
mitbenutzen.
Achtung: umgekehrt gilt das nicht! Definiert man z.B. nur OP_PINC, so führt der
Präfix-Aufruf zu einem Fehler.
Gennerell ist es ratsam, stets beide Varianten zu definieren.
Die post-Varianten Operatorfunktionen sollten immer ein neues Objekt (anders als
in C++ das modifizierte, wobei das aktuelle unverändett bleibt!), die Prefix-Varianten dagegen eine Referenz auf das eigene
Objekt zurückliefern.
class A
{
A();
OP_INC();
OP_PINC();
OP_DEC();
OP_PDEC();
setI(val);
getI();
toString();
i;
}
A::A() { i = 0; }
A::setI(val) { i=val; }
A::getI() { return i; }
A::OP_INC()
{
++i;
return this;
}
A::OP_PINC()
{
var result = new A();
result->setI(i+1);
return result;
}
A::OP_DEC()
{
--i;
return this;
}
A::OP_PDEC()
{
var result = new A();
result->setI(i-1);
return result;
}
Achtung: Das zurückgegebene Objekt ERSETZT das ursprüngliche. Dies lässt sich vermeiden,
wenn man statt eines neuen Objekts das aktuelle - also this zurückgibt. Dann arbeitet
allerdings die Postfix-Variante nicht mehr korrekt, genauer: sie verhält sich wie
die Präfix-Variante.
- binäre Operatoren (+ - * / % & | ^ << >>) können für linken und
rechten Operanden überladen werden:
OP_ADD_R OP_ADD OP_SUB_R OP_SUB OP_MUL_R OP_MUL OP_DIV_R OP_DIV OP_REM_R OP_REM
OP_BAND_R OP_BAND OP_BOR_R OP_BOR OP_XOR_R OP_XOR OP_SHL_R OP_SHL OP_SHR_R OP_SHR
- neu - unäres bitweises NOT (~): OP_BNOT
- neu - compare-Funktion zum Überladen der Vergleichsoperatoren für Objekte
- neu - equals-Funktion zum Überladen der Operatoren == und !=. Sind sowohl equals
als auch compare
implementiert gewinnt equals. equals sollte 1 für Gleichheit und 0 für Ungleichheit
zurückgeben.
- neues Schlüsselwort operator zur syntaktischen
Vereinfachung der Operatordefinition:
opdef ::= [var] "operator" opspec "("argumentlist")".
opspec ::= (["L" | "R"] binop) | indexop | propop | callop | unaryop.
binop ::= "+" | "-" | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>".
indexop ::= "[" ["var"] "]"[ "="].
propop ::= "."["="] .
callop := "(" ")" .
unaryop := "-" | "~" | "++" | "--".
Ähnlich C++ werden Pre- und Postfix-Variante der Increment/Dekrement-Operatoren
durch die Argumentliste unterschieden. Für die Postfix-Varianten enthält die Liste
das Schlüsselwort "var", für die Prefix-Varianten bleibt sie leer.
Der unäre Minus-Operator wird vom binären dadurch unterschieden, dass bei ersterem
die Argumentliste leer ist.
Alle Operatordefinitionen werden vom Compiler in die korrespondierenden reservierten
Funktionsnamen umgesetzt.
Beim Indexoperator und beim Property-Operator werden Get- und Set-Varianten dadurch unterschieden, dass bei letzteren hinter dem Operator ein Gleichheitszeichen (=)
notiert wird. Für den Indexoperator kann stattdessen auch zwischen den eckigen Klammern das Schlüsselwort "var" stehen.
Das Präfix"R" kennzeichnet bei binären Operatoren die Variante für den rechtsseitigen
Operanden, "L" oder kein Präfix die für den linksseitigen.
Ausnahmebehandlung
- Prinzip und Syntax analog C++/JavaScript
- Da es keine strenge Typisierung gibt, gibt es zu jedem try nur einen catch-Block.
- Fest implementierte ("native") Funktionen führen bei Fehlern nicht mehr zwangsweise
zum Programmabbruch sondern lösen eine Ausnahme vom String-Typ aus, die im Code
behandelt werden kann.
Beispiel:
try
{
myVar = anyFunc();
if (myVar) < 0)
throw "myVar must not be negative";
// do something else
}
catch (e)
{
print(e, "catched\n");
throw; // rethrow exception
}
Fallunterscheidung (switch-case)
- syntaktisch wie C/C++, Java(Script) u.ä.
- Anders als in C++ kann hinter case
nicht nur eine Konstante sondern ein beliebiger Ausdruck stehen. Der Ausdruck hinter
switch kann ebenfalls beliebigen Typs
sein, sofern ein Vergleich mit den Werten hinter
case definiert ist.
- Wie in C/C++ gibt es ein "fall through", d.h. wird ein
case-Zweig nicht mit break
verlassen, werden die Anweisungen des folgenden Zweiges mit ausgeführt.
Beispiel:
switch (myVariable)
{
case 1:
// do something
break;
case 2:
// do any other
// fall through
default:
// do something else
}
Neue vordefinierte globale Variablen
Die Variablen (die eigentlich als Konstanten zu verstehen sind) sollen helfen, plattformunabhängige
Programme zu schreiben. Anders als die vordefiniertten Literale bekommen sie ihren
Wert nicht zur Kompilier- sondern zur Laufzeit. Ein Programm kann so beispielsweise
prüfen, ob es in der selben Umgebung abläuft, in der es übersetzt wurde.
- __OSTYPE__ für den Typ des Betriebssystems, mögliche Werte "WIN32", "WINCE", "DOS"
oder eine leere Zeichenkette für andere Plattformen (tritt praktisch nicht auf)
- __UNICODE__ (long) mit Wert 1, wenn das Programm auf einem UNICODE-Build läuft,
sonst 0.
Vordefinierte Funktionen
- newbuffer, newcbuffer, newdict zum Erzeugen von Binär- und Zeichenpuffern
sowie Dictionarys
- newstring kennt drei Aufrufvarianten:
- newstring(string|buffer|charbuffer) ... neuer String aus einfachem Wert
- newstring(int size, fillchar=' ') ... string mit Anfangsgröße und Initialisierung
- newstring(formatStr, ...) ... String aus Formatangabe und Werteliste (analog sprintf)
- size - liefert Anzahl der Elemente eines Containers (Binär- oder Zeichenpuffer,
Vektor, Dictionary oder string)
- cb = CBuf(...) // argtypes: int, buffer, charbuffer, string; other ignored
... konstruiert einen Zeichenpuffer aus einer Argumentliste
- dictcontains, dicterase, dictclear,dictgetkeys, dictgetvalues ... Dictionary-Funktionen
- buffer(var) ... Typumwandlung in Buffer
- bool(var) ... Typumwandlung in Booleschen Wert, liefert 0 für numerische Werte ==0,
null und alle Null-Zeiger, sonst 1
- clone(var) ... erzeugt eine (tiefe) Kopie von Objekten, Strings, Vektoren, Dictionaries;
für alle anderen Typen wird einfach der Wert kopiert.
- LoadLibrary, FreeLibrary, GetProcAddress, CallLibFunc ... DLL-Funktionen ;
in Windows-Builds wird ? im Funktionsnamen bei GetProcAddress durch A (Ansi) bzw.
W (Unicode) ersetzt, unter WinCE darf die aufgerufene DLL-Funktion max. 16 Argumente
haben
- In allen Windows-Builds neue Funktionen GetUser32Func, GetKernel32Func, GetShell32Func,
GetGdi32Func und GetOle32Func. Alle diese Funktionen liefern einen Zeiger auf eine
Funktion aus der entsprechenden DLL (ähnlich GetProcAddress), wobei aber Laden und
Verwalten der DLLs intern erfolgt. Außerdem gibt es einen Alias GetCoreFunc, der
GerUser32Func entspricht. Unter WindowsCE bezoehen sich alle Aufrufe an GetUser32Func,
GetKernel32Func, GetShell32Func und GetGdi32Func auf die coredll-Bibliothek (daher
auch der Alias-Name).
- Ebenfalls in Windows-Builds: neue Funktionen GetLastError (Wrapper für die gleichnamige
Windows-Funktion) sowie GetSysErrorMessage([errcode]) zum Holen der Text-Fehlermeldung
zu einem Code. Fehlt errcode, so wird implizit GetLastError aufgerufen.
- neue Systemfunktionen getenv(string name) und setenv(string name, string value);
unter WinCE wird Environment in HKCU\Environment nachgebildet
- neue I/O-Funktionen
- long fread(buffer,[[unsigned size=1,]unsigned cnt,]FILE* fp) ... aus Datei in
Puffer lesen
- long fwrite(buffer,[[unsigned size=1,]unsigned cnt,]FILE* fp) ... aus Puffer in
Datei schreiben
- in Windows-Builds: getwc, putwc, fgetws, fputws ... Unicode-File-I/O
- neue String-Funktionen (* .. Quellstring str wird modifiziert)
- strfind(str,searchstr) ... suchen, liefert Position oder -1
- substr(str,startpos,length=-1) .. Teilstring bilden
- strreplace (str,search,replace) ... Teilstring oder Zeichen ersetzen (*)
- strerase(str,startpos,cnt=-1) ... Teilstring löschen (*)
- strinsert(str,pos,insertstr) oder strinsert(str,pos,insertchar,cnt=1) .. Einfügen
(*)
- Die Typumwandlungsfunktion string hat (wie in Version 1.0) nur ein Argument und
akzeptiert die Typen int, double, float, string ,cbuffer bzw.object. Objekte müssen
zur Umwandlung in eine Zeichenkette eine Methode toString implementieren. Für die
Formatierung von Strings ist newstring verwendbar.
- Die Typumwandlungsfunktion int versteht als Argument auch Strings mit Zahlen in
Binärdarstellung (siehe Literale).
- Die print-Funktion ruft für Objekte deren toString-Methode auf (falls es eine gibt)
Aufbau des Bytecode-Vektors einer Funktion
Index | Symbol | Beschreibung |
0 | IDX_CODE | Bytecode-Puffer (BppBuffer) |
1 |
IDX_CLASS |
Verweis auf Klasse (bei Mehoden), sonst null |
2 |
IDX_NAME |
Funktionsname
|
2 |
IDX_FIRSTLITERAL |
erstes Literal (=Funktionsname) |
3 |
IDX_DEFAULTARGS |
Vektor mit default-Argumentwerten |
4 |
IDX_SELFREFERENCE |
Zeige auf eigenen Code-Vektor (self-Referenz) |
... |
|
Werte von Literalen und lokalen statischen Variablen |
(last) |
|
Vector(Vector(paramnames),Vector(localvarnames)) - nur wenn _genLocalVarInfo in
Compiler gesetzt; wenn statische lokale Variablen existieren, enthält localvarnames
ein zusätzliches Element (letztes, Dictionary) mit Zuordnungen der Namen der lokalen
statischen Variablen zu Indizes im Vektor. |
Stack-Frame bei Funktionsaufruf
Index |
Beschreibung |
BP |
Objektverweis bei Methodenaufrufen |
BP+1 |
1. Argument |
BP+nargs |
letztes Argument) |
FP (= BP+nargs) |
vor erster lokalen Variablen (falls vorhanden) |
FP+1 |
erste lokale Variable (reserviert für throw Exception) |
... |
lokale Variablen (mit erstem opcode eingefügt) |
SP |
aktueller Stack-Pointer |
Weitere Vorhaben
- einheitliche Integer-Datentypen / uint-Typen für alle Plattformen und explizite
Zeichentypen
char, uchar, wchar, short, ushort, int, uint, long, ulong, longlong, ulonglong,
size_t, ptrdiff_t (Voraussetzung für 64Bit-Version)
int8, uint8, int16, int16, int32, uint32, int64, uint64
--> sbyte, byte, short, ushort, int, uint, long, ulong,
intptr
- Konvertierungsfunktionen und Konvertierungsoperatoren
(für Klassen) für diese Typen
- expliziter Zeigertyp void*
- statische Typisierung für Verweistypen BVT_XXXREF
- Zeiger- und Referenztypen in der Sprache (Notation wie in C++), Referenzparameter
für Funktionen
--> bisherige *REF-Typen werden Zeiger, Referenzen gibt es extra
Referenzen speichern intern einen Zeiger auf das 'richtige' Datum
--> es wird ein const& gebraucht, nicht konstante Referenzen
müssen immer auf L-Werte verweisen.
- statische Initialisierungsabschnitte für jedes Modul - werden beim Laden ausgeführt
- wie in C++
- Globaler Variablenbereich ist selbst eine Klasse (kann im Prinzip auch Member-Funktionen
und -Variablen beinhalten?)
--> Klassen müssen innerhalb anderer Klassen (hierarchisch) definierbar sein
- Namensräume - using-Anweisung
- Echte Sichtbarkeitsstufen (public, protected, private)
--> Problem: es ändert sich die Semantik der Operatoren . und ->. Beide müssen
eigentlich einen Verweis auf das angesprochene Datum liefern; -> dient also nicht
mehr zwingend dem Methodenaufruf
--> Es bleibt bei der alten Semantik. operator. sucht aber zuerst nach zugänglichen
daten-Membern (:: für statische Elemente), der benutzerdefinierte Operator wird
auch verwendet, wenn nichts Passendes gefunden wurde.
- "Vernünftige" Implementierung von operator++(int) - wie in C++
- Inline-Definition von Methoden in Klassen
- Statt operator++/--(var) bzw. [var] soll auch
int als Argument möglich sein
- Definition von Klassen in Klassen
--> Speichern/Lesen der Symboltabellen muss verallgemeinert werden
- echter Strukturtyp - Einzelheiten sind noch zu klären (vielleicht eine Art Klasse
mit einem buffer als Datenträger) --> Klasse CStruct emuliert Strukturen ab Version 1.3
- Iteratoren für alle Container als Sprachmittel
- Wiederverwenden gleichartiger Literale in Codevektoren
für alle Typen (nicht nur
Strings und Variablenverweise)
--> Vektoren brauchen ein allgemeines find/rfind
- kann dann auch in der Sprache
verfügbar sein
--> besser noch: Vergleichoperatoren für BppValue, find in allen containerartigen
Typen)
- Standardklassen Vector, String, Dictionary ... als fester Sprachbestandteil
- Literale mit Endungen f/F-float, l/L-long oder long , ll/LL-longlong, u/U-unsigned,
Zifferngruppierung 24'345'123
ecplizite Unicode-Unterstützung mit L und \uxxxx bzw. \Uxxxxxxxx in Zeichen(ketten)literalen
- auto-Typ (behält nach Zuweisung statisch den Typ des zugewiesenen
Ausdrucks)
- $-Zeichen als gültiges Zeichen
in Identifiern
- NaN-Unterstützung (Problem in DOS-Umgebungen: Borland C++ interpretiert das als Fehler und bricht Programm ab
- Aufzählungstypen --> Realisierung noch unklar
- virtual nicht als Standard, sondern explizit zu deklarieren
--> VMTs statt Namenssuche (konkrete Realisierung noch offen)
- complex-Datentyp
- Compileroptionen: -g lässt automatische globale Variablen zu (zur
Bob+1-Kompatibilität)
- Move-Semantik für moderne Compiler
- 64-Bit-Version (erfordert mindestens expliziten Adresstyp, außerdem 64Bit-Int, Beachtung
der long-Unterschiede zwischen GCC/MSVC)
- Globale Operatordefinitionen: Alle (überladbaren) Operatoren werden als normale
Funktionen implementiert, wie macht man dann die Unterscheidung bei Operatoren für
Objekte? Oder: es können Funktionen installiert werden, die die Standardoperatoren
ersetzen. Wie geht das effizient und wie kommt man wieder an die Originalimplementierung
heran?
- DOS-kompatible Grafikfunktionen auch unter Windows --> Konsole verbiegen
- Kindprozesse: spawn (DOS/Win), shell->DOS: system, Win->ShellExecute oder
exec
--> system und spawn für DOS/Win (nicht WinCE)
- Komfortable Schnittstelle zur Anbindung eines Sourcecode-Debuggers