====== Lyx – Syntax-Referenz ======
Diese Seite ist die vollständige Syntax-Referenz für Lyx. Sie beschreibt alle lexikalischen Regeln, Operatoren, Kontrollstrukturen und Deklarationsformen. Für konzeptuelle Erläuterungen und Code-Beispiele zu einzelnen Themen existieren eigene Unterseiten (verlinkt am Ende jedes Abschnitts).
===== 1. Lexikalische Regeln =====
==== Kommentare ====
// Einzeiliger Kommentar – bis zum Zeilenende
/* Mehrzeiliger Kommentar
Nicht verschachtelbar: /* kein weiterer /* */ hier */
==== Identifier ====
Bezeichner folgen dem Muster ''[A-Za-z_][A-Za-z0-9_]*''. Lyx ist **case-sensitive**: ''MyVar'', ''myVar'' und ''MYVAR'' sind drei verschiedene Bezeichner.
^ Gültig ^ Ungültig ^
| ''counter'', ''_tmp'', ''Vec2'' | ''2fast'' (beginnt mit Zahl) |
| ''MAX_SPEED'', ''isReady'' | ''my-var'' (Bindestrich) |
| ''x64'', ''uart0'' | ''for'' (Schlüsselwort) |
==== Schlüsselwörter ====
Reservierte Wörter, die nicht als Identifier verwendet werden dürfen:
abstract and array as assert
break case catch class co
con continue default dim dispose
do downto else enum extends
extern false finally fn for
if implements import in interface
is layout let Map match
new not null or override
panic parallel pool private protected
pub public repeat return RingBuffer
self Set signal static struct
super switch throw to true
try type unit until utype
var virtual where while widget
Zwei Sonderfälle:
* ''self'' steht nicht in der Keyword-Tabelle des Lexers, sondern wird im Parser gesondert behandelt — reserviert ist es dennoch.
* ''char'' steht umgekehrt in der Keyword-Tabelle, lässt sich aber als Bezeichner verwenden: es ist ein Typname, kein reserviertes Wort.
* ''to'' ist reserviert und fällt in der Praxis auf, sobald jemand ein Paar ''from''/''to'' als **Parameternamen** schreibt: ''%%fn f(to: int64)%%'' bricht mit ''Parse error: expected IDENT, got reserved word 'to''' ab. ''from'' ist frei — nur ''to'' nicht.
> **Nicht reserviert**, auch wenn ältere Fassungen dieser Seite das behaupteten: ''flat'', ''packed'', ''check'', ''limit'' und ''Self''. Alle fünf sind als Bezeichner verwendbar.
> ''default'' ist zwar reserviert, in ''match'' aber **nicht** verwendbar — der Wildcard-Fall lautet ''case _ =>''. Guards werden mit ''if'' geschrieben (''case p if cond =>''), nicht mit ''when''; ''when'' ist kein Schlüsselwort.
**Soft Keywords** — als Bezeichner tokenisiert und nur im jeweiligen Kontext wirksam: ''range'', ''wraps'', ''defer'', ''limit'', ''grant'', ''restrict'', ''ref''. ''limit'' wird nur unmittelbar hinter der Bedingung eines ''while'' und vor dem Block erkannt, gefolgt von ''(''; überall sonst ist es ein gewöhnlicher Bezeichner.
Die Liste der 70 reservierten Wörter steht seit lyxc 1.1.3H auch in der normativen Grammatik (''ebnf.md'', Abschnitt 2.1) und wird dort von ''tests/schluesselwoerter_test.sh'' bei jedem Testlauf gegen den Compiler geprüft — jedes harte Wort muss abgewiesen, jedes weiche angenommen werden ([[https://github.com/SEOLizer/LyX-Compiler/issues/1653|#1653]]). Der Parser nennt seitdem auch den Anlass:
Parse error at line 1: expected IDENT, got reserved word 'unit'
''ref'' gilt **nur unmittelbar vor einem Parameternamen**, auf den ein Doppelpunkt folgt. Überall sonst bleibt es ein gewöhnlicher Bezeichner — ''var ref := 5'' und ein Parameter namens ''ref: int64'' sind beide zulässig.
===== 2. Literale =====
==== Integer-Literale ====
^ Format ^ Beispiel ^ Typ ^
| Dezimal | ''42'', ''-7'' | int64 |
| Dezimal mit Ziffern-Trenner | ''1_000'', ''9_000_000_000'' | int64 |
| Hexadezimal | ''0xFF'', ''0x3FF44000'' | int64 |
| Binär | ''0b1010'' | int64 |
| Oktal | ''0o755'' | int64 |
> **Typsuffixe gibt es nicht.** Schreibweisen mit angehängtem Typkürzel (''42''+''i8'', ''255''+''u8'', ''1000''+''i32'') sind ein Syntaxfehler. Der Zieltyp wird über die Deklaration bestimmt: ''var b: uint8 := 255;''
==== Float-Literale ====
^ Format ^ Beispiel ^ Typ ^
| Dezimal | ''3.14'', ''-0.5'', ''1.0'' | f64 |
| Mit Ziffern-Trenner | ''3.14_159'', ''1_000.5'' | f64 |
| Wissenschaftlich | ''1.0e-9'', ''1.0e9'' | f64 |
> Auch hier kein Suffix: ''3.14f32'' ist ungültig. Für ''f32'' die Deklaration nutzen — ''var x: f32 := 3.14;''
> **Sehr kleine Literale werden korrekt übersetzt** — mit 1.1.0A nachgemessen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1569|#1569]] behoben):
>
> ^ Literal ^ Bitmuster ^
> | ''1.0e-25'' | 4233087679066701273 |
> | ''1.0e-27'' | 4202930039008934928 |
> | ''1.0e-28'' | 4188260752279232537 |
>
> Ältere Fassungen dieser Seite beschrieben eine Grenze bei Exponent −28, ab der solche Literale stillschweigend zu null wurden. Die gibt es nicht mehr; der dort empfohlene Umweg über eine Division ist unnötig.
>
> Bei ''1.0e-30'' liegt das Ergebnis noch **ein ULP** unter dem exakten Wert. Für Toleranzrechnungen belanglos — wer Bitmuster vergleicht, prüft es mit ''FeqBits'' aus [[lyx_-_programmiersprache:units:feq|std.feq]].
==== Sonstige Literale ====
^ Typ ^ Literal ^ Anmerkung ^
| bool | ''true'', ''false'' | |
| char | ''%%'a'%%'', ''%%'\n'%%'' | ASCII, ein Zeichen |
| pchar | ''"Hallo\n"'' | Nullterminierter String, Typ pchar |
| null | ''null'' | Entspricht der Adresse 0 |
==== String-Escape-Sequenzen ====
^ Sequenz ^ Bedeutung ^
| ''\n'' | Zeilenumbruch (LF) |
| ''\t'' | Tabulator |
| ''\r'' | Wagenrücklauf (CR) |
| ''\\'' | Backslash |
| ''\"'' | Anführungszeichen |
| ''\0'' | Null-Byte (String-Terminator) |
===== 3. Speicherklassen & Variablen =====
→ Ausführlich: [[lyx_-_programmiersprache:sprache:variablen|Variablen]] — Startwerte, Typableitung, Gültigkeitsbereich, Modulebene, Lebensdauer. Speicher und Lebensdauer vertieft [[lyx_-_programmiersprache:sprache:memory-management|Memory Management]].
Jede Variable in Lyx trägt eine explizite Speicherklasse. Die Klasse bestimmt Mutabilität und Lebenszeit.
^ Keyword ^ Mutabilität ^ Speicherort ^ Beschreibung ^
| ''var'' | Veränderlich | Stack / Data-Segment | Standard-Variable; Wert kann jederzeit geändert werden |
| ''let'' | Einmalig | Stack / Data-Segment | Wert wird einmalig zur Laufzeit gesetzt, danach read-only |
| ''co'' | Read-only | Stack | Schreibgeschützter Stack-Slot; ähnlich wie ''let'', aber ohne Heap-Zugriff |
| ''con'' | Compile-Zeit | Code (Immediate) | Compile-Zeit-Konstante; wird direkt in den Maschinencode eingebettet |
==== Syntax ====
var counter: int64 := 0; // Typ explizit, Wert zugewiesen
var name := "Lyx"; // Typ inferiert: pchar
let max_val: int64 := ReadConfig(); // Einmalige Zuweisung
co PI: f64 := 3.14159265358979; // Stack-Konstante
con MAX_BUFFER := 4096; // Compile-Zeit-Konstante (int64 inferiert)
* Der Zuweisungsoperator ist immer '':=''.
* Typangabe ist optional wenn der Typ aus dem Initialisierer eindeutig inferierbar ist — auch bei String-Literalen (''var name := "Lyx";'' ergibt ''pchar'') und bei ''new'' (''var p := new Point();'').
* ''pub'' vor der Speicherklasse macht die Variable aus anderen Units sichtbar: ''pub var x: int64 := 0;''
* ''let'' und ''co'' müssen **bei der Deklaration** belegt werden. Eine spätere Zuweisung ist keine „einmalige" mehr, sondern wird abgewiesen: ''assignment to let/co binding not allowed''. ''let x: int64;'' ohne Initialisierer übersetzt zwar, ergibt aber einen Slot, der nie einen Wert bekommen kann.
* ''con'' verlangt einen zur Compile-Zeit auswertbaren Ausdruck. Ein Funktionsaufruf wird abgelehnt (''function call not allowed in const expression''); die Ableitung aus anderen ''con''-Werten ist erlaubt (''%%con B: int64 := A * 2;%%''). Eine Zuweisung an ''con'' meldet ''assignment to con constant not allowed''.
===== 4. Typsystem =====
==== Primitive Typen ====
^ Typ ^ Bits ^ Wertebereich ^
| ''int8'' | 8 | −128 … 127 |
| ''int16'' | 16 | −32 768 … 32 767 |
| ''int32'' | 32 | −2 147 483 648 … 2 147 483 647 |
| ''int64'' / ''int'' | 64 | −9 223 372 036 854 775 808 … 9 223 372 036 854 775 807 |
| ''uint8'' | 8 | 0 … 255 |
| ''uint16'' | 16 | 0 … 65 535 |
| ''uint32'' | 32 | 0 … 4 294 967 295 |
| ''uint64'' | 64 | 0 … 18 446 744 073 709 551 615 |
| ''f32'' | 32 | IEEE 754 Single |
| ''f64'' | 64 | IEEE 754 Double |
| ''bool'' | 1 (8 im Speicher) | ''true'', ''false'' |
| ''char'' | 8 | Ein ASCII-Zeichen |
| ''pchar'' | 64 (Pointer) | Nullterminierter String (char*) |
| ''void'' | – | Kein Rückgabewert |
> ''isize'' und ''usize'' standen hier früher ebenfalls; der Compiler kennt sie nicht (''unknown type in var decl''). Für Indizes und Größenangaben ''int64'' bzw. ''uint64'' verwenden. Ebenso entfallen ist ''qbool'' — der Typ existiert nicht.
==== Pointer ====
Lyx hat **keinen Pointer-Typ** in der Typgrammatik. Es gibt weder ''%%^T%%'' als Typangabe noch einen Address-of-Operator (''%%^x%%'' bzw. ''&x'') oder einen Dereferenz-Operator (''%%p^%%''). Adressen werden als ''int64'' geführt, der Zugriff läuft über Builtins:
var cell: int64 := alloc(8); // 8 Byte anfordern
poke64(cell, 42); // schreiben
var v: int64 := peek64(cell); // lesen
free(cell, 8);
^ Builtin ^ Bedeutung ^
| ''alloc(n)'' / ''free(p, n)'' | Speicher anfordern / freigeben — die Größe ist bei ''free'' Pflicht |
| ''peek8/16/32/64(p)'' | Wert an Adresse lesen |
| ''poke8/16/32/64(p, v)'' | Wert an Adresse schreiben |
Ein Ausgabeparameter wird als Zelle übergeben: ''alloc(8)'', Adresse an die Funktion, danach ''peek64''.
==== Nullable Typen ====
Das ''?''-Suffix markiert einen Typ als nullable — er darf zusätzlich ''null'' annehmen:
var x: int64? := null;
var name: pchar? := null;
Für den Zugriff auf möglicherweise nicht gesetzte Werte gibt es den Safe-Access-Operator ''?.'' — er liefert den Feldwert, ohne bei ''null'' zu dereferenzieren:
var a: A := new A();
a.v := 3;
var x: int64 := a?.v; // 3
==== Range-Typen ====
type Altitude = int64 range -1000..60000;
type Speed = int64 range 0..300;
Die Grenzen werden auf zwei Ebenen durchgesetzt:
* **Compile-Zeit** — steht der Wert bereits beim Übersetzen fest (Literal, ''con'', konstanter Ausdruck), meldet der Compiler einen Fehler: ''Wert liegt ausserhalb des Bereichs des Typs''.
* **Laufzeit** — bei berechneten Werten prüft der erzeugte Code beim Zuweisen und bricht mit ''Bereichstyp <Name>: Wert ausserhalb'' ab.
Die Laufzeitprüfung greift überall dort, wo ein Wert den Bereichstyp annimmt — bei Zuweisung, als Parameter, als Rückgabewert und beim Schreiben in ein Strukturfeld:
type Alt = int64 range 0..100;
fn Setze(a: Alt): int64 { return a; }
fn Hole(): Alt { var x: int64 := 500; return x; } // bricht ab
type S = struct { a: Alt; };
fn main(): int64 {
var x: int64 := 500;
var a: Alt := x; // bricht ab
Setze(x); // bricht ab
var s: S;
s.a := x; // bricht ab
return 0;
}
===== 5. Operatoren =====
==== Arithmetik ====
^ Operator ^ Operation ^ Beispiel ^
| ''+'' | Addition | ''a + b'' |
| ''-'' | Subtraktion / Negation | ''a - b'', ''-x'' |
| ''*'' | Multiplikation | ''a * b'' |
| ''/'' | Division (ganzzahlig bei int) | ''a / b'' |
| ''%'' | Modulo (Rest) | ''a % b'' |
| ''++'' | Inkrement (postfix) | ''i++'' |
| ''--'' | Dekrement (postfix) | ''i--'' |
==== Vergleich ====
^ Operator ^ Bedeutung ^
| ''=='' | Gleich |
| ''!='' | Ungleich |
| ''<'' | Kleiner als |
| ''>'' | Größer als |
| ''<='' | Kleiner oder gleich |
| ''>='' | Größer oder gleich |
| ''in'' | Bereichsprüfung: ''%%x in 1..10%%'' — beide Grenzen zählen mit |
> **Wichtig:** Der **Vergleichs**operator ist ''=='', der **Zuweisungs**operator '':=''. In einer Bedingung wird ein einzelnes ''='' abgewiesen — ''%%if (x = 0)%%'' meldet ''Parse error at line N: expected ), got = %%'='%%''. Damit ist die in C klassische versehentliche Zuweisung in einer Bedingung strukturell ausgeschlossen.
>
> **Auch als eigenständige Anweisung wird ''='' abgewiesen** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1575|#1575]] behoben). ''%%x = 42;%%'' meldet mit 1.1.2E ''Parse error at line N: `=` ist kein Zuweisungsoperator — gemeint ist `:=`''. Ein ''='' im Code ist damit nur noch ein Parametervorgabewert.
==== Logisch & Bitweise ====
^ Operator ^ Bedeutung ^ Anwendung ^
| ''&&'' | Logisches AND mit Kurzschluss | Bedingungen; rechte Seite nur bei wahrer linker |
| ''%%||%%'' | Logisches OR mit Kurzschluss | Bedingungen; rechte Seite nur bei falscher linker |
| ''&'' | Logisches / Bitweises AND | bool-Ausdrücke und Integer-Maskierung; wertet immer beide Seiten |
| ''%%|%%'' | Logisches / Bitweises OR | bool-Ausdrücke und Integer-Flags; wertet immer beide Seiten |
| ''!'' | Logisches NOT | Nur auf bool |
| ''~'' | Bitweises NOT (Komplement) | Nur auf Integer |
| ''^'' | Bitweises XOR (Infix) | ''a ^ b'' |
| ''%%|~%%'' | Bitweises NOR | ''%%12 |~ 10%%'' = ''-15'' — gleichbedeutend mit ''%%~(12 | 10)%%'' |
| ''<<'' | Bitshift links | ''1 << 3'' = 8 |
| ''>>'' | Bitshift rechts, **arithmetisch** | ''0x80 >> 4'' = 8; ''-16 >> 2'' = ''-4'' |
> **Zwei Eigenheiten beim Schieben.**
>
> * **''%%>>%%'' zieht das Vorzeichenbit nach.** ''%%-16 >> 2%%'' ergibt ''-4'', ''%%-1 >> 1%%'' bleibt ''-1''. Ein logisches Rechtsschieben, bei dem Nullen nachrücken, erhält man über die Umdeutung: ''%%(wert as uint64) >> n%%''. Die Standardbibliothek nutzt genau diese Form, etwa in ''SwapEndian32''.
> * **Die Schiebeweite wird modulo 64 genommen.** ''1 << 64'' ergibt ''1'', ''1 << 65'' ergibt ''2'', ''1 << -1'' ergibt das Vorzeichenbit. Das entspricht dem Verhalten der Prozessorbefehle; eine Warnung gibt es nicht. Stammt die Weite aus einer Rechnung, ist sie vorher gegen ''0…63'' zu prüfen.
> **''%%^%%'' ist ausschließlich XOR.** Ältere Fassungen dieser Seite nannten zusätzlich einen Präfix-Gebrauch (''%%^x%%'' als Adresse von x) und einen Postfix-Gebrauch (''%%p^%%'' als Dereferenz). Beide gibt es nicht — ''%%^x%%'' ergibt ''expected expression'', und einen Zeigertyp führt die Typgrammatik ohnehin nicht. ''%%6 ^ 3%%'' ergibt ''5''.
==== Spezialoperatoren ====
^ Operator ^ Name ^ Beschreibung ^ Beispiel ^
| '':='' | Zuweisung | Weist einen Wert zu | ''x := 42'' |
| ''%%|%%>'' | Pipe | Ergebnis links als erstes Argument rechts | ''val %%|%%> Abs()'' |
| ''?.'' | Safe Call | Memberzugriff nur wenn nicht null | ''node?.Next'' |
| ''??'' | Null-Coalesce | Fallback wenn null | ''name ?? "Gast"'' |
| ''as'' | Cast | Explizite Typkonvertierung | ''x as f64'' |
| ''is'' | Typ-Test | Prüft Laufzeit-Typ | ''obj is Circle'' |
| ''..'' | Bereich | In match-Mustern und Range-Typen | ''100..199'' |
==== Operator-Präzedenz (absteigend) ====
^ Priorität ^ Operatoren ^ Assoziativität ^
| 1 (höchste) | Unär: ''!'', ''~'', ''-'' | Rechts |
| 2 | ''*'', ''/'', ''%'' | Links |
| 3 | ''+'', ''-'' | Links |
| 4 | ''<<'', ''>>'' | Links |
| 5 | ''&'' | Links |
| 6 | ''^'' (XOR) | Links |
| 7 | ''%%|%%'' | Links |
| 8 | ''='', ''!='', ''<'', ''>'', ''<='', ''>='' | Nicht-assoziativ |
| 9 | ''as'', ''is'' | Links |
| 10 | ''?.'' | Links |
| 11 | ''??'' | Rechts |
| 12 | ''%%|%%>'' | Links |
| 13 (niedrigste) | '':='' | Rechts |
===== 6. Kontrollfluss =====
==== if / else ====
if (Bedingung) {
// ...
} else if (andere_Bedingung) {
// ...
} else {
// ...
}
''if'' ist kein Ausdruck – es gibt keinen ternären Operator ''?:''. Bedingte Werte werden über ''match'' oder Hilfsfunktionen ausgedrückt.
==== while ====
while (Bedingung) {
// ...
}
// Bounded – garantierte Terminierung
while (Bedingung) limit(Konstante) {
// ...
}
==== for ====
// Aufwärts (inklusive Grenzen)
for i := Startwert to Endwert do {
// i läuft von Startwert bis einschließlich Endwert
}
// Abwärts
for i := Endwert downto Startwert do {
// i läuft von Endwert bis einschließlich Startwert
}
// C-Stil — ohne Klammern, ohne var
for i := 0; i < n; i++ {
// ...
}
// Bereichsform (seit 1.0.14), beide Grenzen einschließend
for i in 0..3 {
// i läuft 0, 1, 2, 3
}
''for ... in'' erwartet einen **Bereich**, keine Sammlung — ''for v in a'' über ein Array wird mit ''for ... in erwartet einen Bereich'' abgewiesen.
Die Laufvariable wird automatisch inkrementiert / dekrementiert. Ihr Typ wird vom Compiler inferiert. ''for''-Schleifen sind immer bounded – kein ''limit'' erforderlich.
==== repeat-until ====
repeat {
// Körper wird mindestens einmal ausgeführt
} until (Abbruchbedingung);
// Mit Bounded-Limit
repeat {
// ...
} until (Bedingung) limit(Konstante);
==== break & continue ====
while (true) {
if (DoneCondition()) { break; } // verlässt die innerste Schleife
if (SkipCondition()) { continue; } // springt zum nächsten Durchlauf
}
''break'' und ''continue'' wirken immer auf die **innerste** umgebende Schleife.
==== match ====
match (Ausdruck) {
case Wert => Anweisung;
case A | B => Anweisung; // OR-Muster
case X if (Guard) => Anweisung; // mit Guard-Bedingung
case _ => Anweisung;
}
> Bereichsmuster werden als ''case lo..hi =>'' geschrieben; beide Grenzen zählen mit. Die obere Grenze darf entfallen (''case lo.. =>'').
''match'' kann als Ausdruck verwendet werden (Ergebnis einer Case-Arm-Anweisung). Alle Fälle müssen abgedeckt sein (Exhaustivität). Details: [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching]].
===== 7. Funktionen =====
==== Deklaration ====
fn FunktionsName(param1: Typ1, param2: Typ2): RückgabeTyp {
// Körper
return Wert;
}
* Kein Rückgabewert: Rückgabetyp und ''return'' werden weggelassen (implizit ''void'').
* Mehrere Rückgabewerte: Tupel-Syntax ''(Typ1, Typ2)''.
* Öffentliche Sichtbarkeit: ''pub'' voranstellen.
==== Beispiele ====
// Einfache Funktion
fn Add(a: int64, b: int64): int64 {
return a + b;
}
// Keine Rückgabe (void)
fn Log(msg: pchar) {
Print(msg);
}
// Tupel-Rückgabe (mehrere Werte)
fn Divide(a: int64, b: int64): (int64, bool) {
if (b == 0) { return (0, false); }
return (a / b, true);
}
// Öffentlich
pub fn GetVersion(): pchar {
return "1.0.0";
}
==== Aufruf ====
var result := Add(3, 4);
var quotient, ok := Divide(10, 3);
if (ok) { Print(IntToStr(quotient)); }
// Pipe-Operator: Wert als erstes Argument
var v := -3.14 |> Abs() |> Round();
→ [[lyx_-_programmiersprache:sprache:funktionen|Funktionen (vertieft) — Tupel-Rückgabe, anonyme Funktionen, Higher-Order Functions, Pipe-Operator]]
===== 8. Units & Imports =====
Jede Lyx-Quelldatei beginnt mit einer ''unit''-Deklaration. Sie definiert den Namensraum der Datei.
unit my_module;
import std.io;
import std.math;
import my_project.utils;
* ''unit''-Name und Dateiname müssen übereinstimmen (''my_module.lyx'' → ''unit my_module;'').
* ''import'' macht alle ''pub''-Symbole der Ziel-Unit sichtbar.
* Standard-Bibliothek: ''std.io'', ''std.math'', ''std.string'', ''std.alloc'', ''std.result'', ''std.error'', …
===== 9. Typdefinitionen =====
==== struct ====
Stack-allozierte Wertetypen. Zuweisung kopiert den gesamten Wert.
type Vec2 = struct {
x: f64;
y: f64;
fn Length(): f64 {
return Sqrt(self.x * self.x + self.y * self.y);
}
fn Scale(factor: f64) {
self.x := self.x * factor;
self.y := self.y * factor;
}
};
==== class ====
Heap-allozierte Referenztypen. ''new'' erzeugt, ''dispose'' gibt frei.
type Animal = class {
name: pchar;
fn Create(n: pchar) {
self.name := n;
}
fn Destroy() {
// Aufräum-Logik
}
virtual fn Speak() {
Print(self.name);
}
};
type Dog = class extends Animal {
override fn Speak() {
Print("Wuff!");
}
};
var d := new Dog("Bello");
d.Speak(); // "Wuff!"
dispose d;
Schlüsselwörter für Klassen:
^ Keyword ^ Bedeutung ^
| ''extends'' | Einfachvererbung |
| ''virtual fn'' | Überschreibbare Methode (V-Table-Eintrag) |
| ''override fn'' | Überschreibt eine virtuelle Methode |
| ''abstract fn'' | Muss in abgeleiteten Klassen implementiert werden |
| ''super.Methode()'' | Aufruf der Eltern-Implementierung |
| ''self'' | Referenz auf das aktuelle Objekt |
| ''new T(args)'' | Heap-Allokation |
| ''dispose ptr'' | Heap-Freigabe |
==== enum ====
enum Richtung { Nord, Sued, Ost, West }
enum StatusCode {
Ok = 200,
NotFound = 404,
Error = 500
}
// Zugriff über ::
var d := Richtung.Nord;
var s := StatusCode.Ok;
''match'' über Enums prüft Exhaustivität: Fehlt ein Fall, erzeugt der Compiler einen Fehler.
==== interface & implements ====
type Druckbar = interface {
fn Zeige(): int64;
};
type Punkt = class implements Druckbar {
x: int64;
y: int64;
fn Zeige(): int64 { return self.x + self.y; }
};
Fehlt eine der Signaturen in der Klasse, meldet der Compiler ''class missing interface method''. ''trait'' und ''impl'' gibt es nicht.
Generics mit Trait-Schranken:
fn PrintAll(items: [16]T) {
for i := 0 to 15 do {
items[i].Print();
}
}
==== Einheitentypen — dim und utype ====
''dim'' benennt eine physikalische **Dimension**, ''utype'' eine **Einheit** darin, zusammen mit ihrem Faktor bezogen auf die Basiseinheit. Dimensionen lassen sich aus anderen ableiten:
dim Laenge;
dim Zeit;
dim Geschwindigkeit = Laenge / Zeit; // * und / sind erlaubt
utype Meter: Laenge = 1;
utype Kilometer: Laenge = 1000;
utype MeterProSekunde: Geschwindigkeit = 1.0; // Faktor auch als Float
fn main(): int64 {
var strecke: Kilometer := 2;
PrintLn(IntToStr(strecke as int64));
return 0;
}
Die Standardbibliothek bringt mit ''[[lyx_-_programmiersprache:units:units.lyx|std.units]]'' rund 40 fertige Einheiten mit.
==== Was Einheitentypen leisten ====
Die **Dimensionsprüfung** greift: Eine Länge lässt sich weder einer Zeit zuweisen noch mit ihr verrechnen.
var m: Meter := 5;
var s: Sekunde := m; // sema error: Zuweisung ueber Dimensionsgrenzen hinweg
var x: Meter := m + s; // sema error: Einheitentypen verschiedener Dimension verrechnet
**Umrechnung, Vergleich und abgeleitete Dimensionen greifen** — nachgemessen nach [[https://github.com/SEOLizer/LyX-Compiler/issues/1956|#1956]], [[https://github.com/SEOLizer/LyX-Compiler/issues/1955|#1955]] und [[https://github.com/SEOLizer/LyX-Compiler/issues/1964|#1964]]:
var a: Meter := 500;
var b: Kilometer := 1;
var summe: Meter := a + b; // 1500 — der Faktor wirkt
if (a > b) { } // falsch — 500 m sind weniger als 1 km
var c: Meter := b; // 1000
Auch die **abgeleitete Dimension** wird geprüft: aus ''%%dim Tempo = Laenge / Zeit;%%'' folgt, dass ''meter / sekunde'' ein Tempo ist — die Zuweisung an eine Länge wird mit //Zuweisung ueber Dimensionsgrenzen hinweg// abgewiesen. Ein ''as''-Cast über Dimensionsgrenzen ebenfalls; die Meldung verweist auf ''as f64'', wenn nur der Zahlenwert gemeint ist.
Was weiterhin nicht geht: ein Einheitentyp mit einer nackten Zahl verrechnet (''%%b + 30%%'' → //Einheitentyp mit dimensionsloser Zahl verrechnet//). Die Zahl braucht denselben Typ.
**''wraps'' rechnet zuverlässig** ([[https://github.com/SEOLizer/LyX-Compiler/issues/2004|#2004]] behoben, nachgemessen 2026-09-08). Der Bereich ''0..360'' umfasst 361 Werte, gewrappt wird darüber hinaus:
359 -> 359 361 -> 0 362 -> 1 370 -> 9 720 -> 359
Konstanten und Laufzeitwerte liefern jetzt dasselbe. Zuvor kam bei Laufzeitwerten ein f64-Bitmuster heraus.
Die Modifikatoren ''range'' und ''wraps'' begrenzen den Wertebereich. ''range'' bricht bei Überschreitung ab, ''wraps'' rechnet zyklisch:
utype Grad: Winkel = 1 range 0..360; // 400 -> Abbruch "unit value out of range"
utype GradW: Winkel = 1 wraps 0..360; // 370 -> 9
''range'' greift **zweimal**: steht der Wert schon beim Übersetzen fest, meldet der Compiler ''Wert liegt ausserhalb der Grenzen des Einheitentyps''; kommt er erst zur Laufzeit, bricht das Programm mit ''unit value out of range'' und Rückgabecode 1 ab.
Eine nackte ''int64''-Zahl lässt sich einem Einheitentyp **ohne Cast** zuweisen (''%%var m: Meter := raw;%%'') — im Gegensatz zum Verrechnen, das abgewiesen wird.
Der **Umrechnungsfaktor** wirkt bei der Zuweisung zwischen Einheiten derselben Dimension: 3 Meter, einer Millimeter-Variablen zugewiesen, ergeben 3000. Nur dort — siehe den Kasten oben.
Seit lyxc 1.0.15C wirken auch **Float-Faktoren**: Der Compiler führt sie als gekürzten Bruch (''1000.0'' → 1000/1, ''0.017453'' → 17453/1000000). Ein Faktor, den er nicht vollständig liest, wird gemeldet statt still als 1 gerechnet. Ebenfalls seit 1.0.15C wird ''pub'' an ''dim'' und ''utype'' exportiert — ''std.units'' ist damit importierbar.
> **Die ganzzahlige Umrechnung schneidet ab.** In die gröbere Einheit geht Genauigkeit verloren, ohne Hinweis: 2500 Meter werden zu 2 Kilometern, 500 Meter zu **0**. In die feinere Einheit ist die Umrechnung verlustfrei.
>
> ''wraps 0..360'' rechnet modulo **361** — beide Grenzen zählen mit, wie bei ''range''. Wer den Vollkreis meint, schreibt ''wraps 0..359''.
===== 10. Generics =====
// Generische Funktion
fn Max(a: T, b: T): T {
if (a > b) { return a; }
return b;
}
// Generischer Struct
type Stack = struct {
data: [64]T;
top: int64;
fn Push(val: T) {
self.data[self.top] := val;
self.top++;
}
fn Pop(): T {
self.top--;
return self.data[self.top];
}
};
// Mehrere Schranken mit +
fn Process(val: T) {
val.Print();
}
===== 11. Attribute & Pragmas =====
Attribute beginnen mit ''@'' und stehen unmittelbar vor dem annotierten Element (Unit, Funktion, Variable).
==== Sicherheit & Zertifizierung ====
^ Attribut ^ Ziel ^ Beschreibung ^
| ''@flight_crit'' | Unit, Fn | Strikte Luftfahrt-Regeln: kein Heap, keine unsafe-Blöcke |
| ''@dal(A)''..'@dal(E)'' | Unit, Fn | Design Assurance Level nach DO-178C |
| ''@stack_limit(N)'' | Fn | Hartes Stack-Limit in Bytes; wird bei jeder Übersetzung geprüft (kein Flag nötig) |
| ''@wcet(N)'' | Fn | WCET-Annotation in Nanosekunden |
| ''@redundant'' | Variable | Triple Modular Redundancy (TMR) |
| ''@integrity(mode: …)'' | Fn | ''mode: scrubbed'' (CRC32-Sweep über die geladenen Codeseiten) oder ''mode: software_lockstep'' (Rückgabeausdruck doppelt gerechnet). ''mode'' ist Pflicht; ''lockstep'' allein ist kein gültiger Wert |
==== Performance & Code-Generierung ====
^ Attribut ^ Ziel ^ Beschreibung ^
| ''@energy(1..5)'' | Fn | Steuert die Aggressivität des Backends; das Argument ist **Pflicht** |
==== Interoperabilität ====
^ Attribut ^ Ziel ^ Beschreibung ^
| ''@volatile'' | Var | Verhindert das Wegoptimieren von Zugriffen (MMIO) |
| ''@packed'' | Struct | Kein Padding — exaktes Hardware-Layout |
==== Was es nicht gibt ====
Diese Attribute standen in früheren Fassungen dieser Tabelle. Der Compiler weist sie seit #1099 mit ''unbekanntes Attribut'' ab — **jedes Beispiel, das sie verwendet, scheitert an der Übersetzung**:
^ Attribut ^ stattdessen ^
| ''@inline'', ''@noinline'' | nichts — über das Inlining entscheidet der Codegen |
| ''@no_opt'' | nichts je Funktion; ''%%--%%no-opt'' wirkt nur programmweit |
| ''@section("name")'' | das Linker-Skript der Zielplattform |
| ''@variadic'' | nichts — variadische C-Funktionen werden wie andere mit ''extern fn … link'' deklariert |
| ''@stdcall'' | nichts — die Aufrufkonvention folgt aus ''%%--%%target'' |
| ''@at(N)'' | nichts (→ [[lyx_-_programmiersprache:sprache:attributes-pragmas|Attribute & Pragmas]]) |
| ''@allow_unsafe'' | nichts — ''unsafe''-Blöcke gibt es ebenfalls nicht |
Drei Sonderfälle:
* **''@parallel''** wird zwar als Attributname erkannt, ist aber weder an einer Funktion noch an einer Schleife anwendbar — beides endet im Parser. Für SIMD ist ''@energy(4)'' oder ''@energy(5)'' der vorgesehene Weg.
* **''@extern''** und **''@export''** sind keine gewöhnlichen Attribute, sondern machen die folgende Deklaration zu einer externen. ''@extern'' allein genügt nicht — es fehlt dann die ''link''-Klausel. Die übliche Schreibweise ist ''extern fn … link "<bibliothek>"'' (→ [[lyx_-_programmiersprache:sprache:ffi|FFI]]).
* **''@critical''** wird angenommen und vermerkt, hat aber keine geprüfte Wirkung.
==== Mehrere Attribute kombinieren ====
@flight_crit
@dal(B)
@stack_limit(512)
@wcet(10000)
fn ProcessFlightData(val: f64): f64 {
@redundant
var heading: f64 := val;
return Clamp(heading, 0.0, 360.0);
}
===== 12. Berechnete Adressen =====
''unsafe''-Blöcke gibt es in Lyx **nicht** — ''unsafe { … }'' wird mit ''undefined symbol '\''unsafe'\'''' abgewiesen. Eine gesonderte Kennzeichnung braucht es auch nicht, weil Adressen ohnehin gewöhnliche ''int64''-Werte sind: Arithmetik darauf ist normale Ganzzahl-Arithmetik, und die Breite des Zugriffs bestimmt allein das gewählte Primitiv.
var adresse: int64 := basis + offset; // Adressrechnung
var b: int64 := peek8(adresse); // 1 Byte lesen
var w: int64 := peek32(adresse); // 4 Byte an derselben Stelle
poke32(adresse, 0xFF); // 4 Byte schreiben
Was in anderen Sprachen ein Reinterpret-Cast wäre, ist hier die Wahl zwischen ''peek8'', ''peek16'', ''peek32'' und ''peek64'' auf derselben Adresse.
→ [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]] · [[lyx_-_programmiersprache:sprache:pointer-inlining|Low-Level: Speicherzugriff & Pragmas]]
===== 13. Sonderzeichen & Strukturregeln =====
^ Zeichen ^ Bedeutung ^
| '';'' | Statement-Terminator (Pflicht am Zeilenende) |
| '':'' | Typ-Trenner in Deklarationen (''name: Typ'') |
| ''{ }'' | Block-Begrenzer für Funktionskörper, Schleifen, Bedingungen |
| ''( )'' | Ausdruck-Gruppierung; Pflicht in ''if'', ''while'', ''match'' |
| ''[ ]'' | Array-Index (''arr[i]'') und Array-Typ (''[N]T'') |
| ''.'' | Memberzugriff (''obj.field'', ''obj.Method()'') |
| ''::'' | Enum-Wert-Zugriff (''Status.Ok'') |
| '','' | Trennzeichen in Parameterlisten und Tupeln |
| ''^'' | Bitweises XOR (''%%a ^ b%%'') — kein Zeiger-Operator |
| ''@'' | Attribut-Präfix (''@dal(A)'', ''@volatile'') |
==== Semikolons ====
Semikolons sind in Lyx **Pflicht** als Statement-Abschluss. Sie können nicht weggelassen werden:
var x: int64 := 42; // ✅
var y: int64 := 10 // ✗ Compiler-Fehler: fehlendes ;
Ausnahme: Nach ''}''-Blöcken (Funktionskörper, if, while) wird kein Semikolon benötigt.
===== 14. Vollständige Grammatik-Übersicht (kompakt) =====
Program → (Unit Import* Decl*)?
Unit → 'unit' Ident ';'
Import → 'import' QualIdent ';'
Decl → VarDecl | FnDecl | TypeDecl | TraitDecl | ImplDecl
VarDecl → Modifier? StorageClass Ident (':' Type)? ':=' Expr ';'
StorageClass → 'var' | 'let' | 'co' | 'con'
Modifier → 'pub'
FnDecl → Attr* Modifier? 'fn' Ident GenericParams? '(' Params ')' (':' Type)? Block
Params → (Ident ':' Type (',' Ident ':' Type)*)?
TypeDecl → 'type' Ident GenericParams? '=' ('struct' | 'class' ('extends' Ident)?) '{' Member* '}'
EnumDecl → 'enum' Ident '{' (Ident ('=' IntLit)? (',' Ident ('=' IntLit)?)*)? '}'
InterfaceDecl → 'interface' '{' MethodSignature* '}'
ClassDecl → 'class' ['extends' Type] ['implements' Ident {',' Ident}] '{' … '}'
Block → '{' Stmt* '}'
Stmt → VarDecl | ExprStmt | IfStmt | WhileStmt | ForStmt | RepeatStmt
| MatchStmt | ReturnStmt | BreakStmt | ContinueStmt | UnsafeBlock
IfStmt → 'if' '(' Expr ')' Block ('else' 'if' '(' Expr ')' Block)* ('else' Block)?
WhileStmt → 'while' '(' Expr ')' ('limit' '(' IntLit ')')? Block
ForStmt → 'for' Ident ':=' Expr ('to' | 'downto') Expr 'do' Block
RepeatStmt → 'repeat' Block 'until' '(' Expr ')' ('limit' '(' IntLit ')')? ';'
MatchStmt → 'match' '(' Expr ')' '{' MatchArm* '}'
MatchArm → 'case' (Pattern | '_') ('if' Expr)? '=>' Expr ';'
Expr → ... (vollständige Präzedenz-Hierarchie; siehe Abschnitt 5)
**Weiterführende Seiten:**
* [[lyx_-_programmiersprache:sprache:datentypen|Datentypen – vollständige Typ-Referenz]]
* [[lyx_-_programmiersprache:erste-schritte|Erste Schritte – Einführung mit Beispielen]]
* [[lyx_-_programmiersprache:sprache:pattern-matching|Pattern Matching – match im Detail]]
* [[lyx_-_programmiersprache:sprache:enums|Enums – Aufzählungstypen]]
* [[lyx_-_programmiersprache:sprache:module-und-importe|Module und Import-System]]
* [[lyx_-_programmiersprache:sprache:typaliase-und-typumwandlung|Typ-Aliase und Typumwandlung (as, is)]]
* [[lyx_-_programmiersprache:sprache:oop|OOP – Klassen, Traits, Generics]]
* [[lyx_-_programmiersprache:sprache:pointer-inlining|Low-Level: Speicherzugriff & Pragmas]]
* [[lyx_-_programmiersprache:sprache:attributes-pragmas|Attribute & Pragmas – vollständige @-Referenz]]
* [[lyx_-_programmiersprache:sprache:ffi|FFI – @extern, @export, @packed]]
----
Letzte Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1956|#1956]], gemessen mit lyxc 1.1.18A) — Abschnitt „Was Einheitentypen leisten" um den gemessenen Stand ergänzt: der Umrechnungsfaktor wirkt nur bei der Zuweisung, nicht bei Arithmetik und Vergleich; abgeleitete Dimensionen (''*'', ''/'') tragen keine Prüfung; ''range'' greift zur Übersetzungs- und zur Laufzeit; ''int64'' → Einheitentyp ist ohne Cast zulässig.
Vorherige Aktualisierung: 2026-09-05 ([[https://github.com/SEOLizer/LyX-Compiler/issues/1908|#1908]], gemessen mit lyxc 1.1.18A) — ''@integrity''-Zeile der Attributtabelle berichtigt: ''mode:'' ist Pflicht, der Modus heißt ''software_lockstep'' (nicht ''lockstep''), und das Attribut gehört an eine Funktion.
Vorherige letzte Aktualisierung: 2026-08-19 — Schlüsselwortliste gegen ''ebnf.md'' 2.1 (lyxc 1.1.3H) abgeglichen: identisch; ''limit'' zu den Soft Keywords ergänzt und die neue Parsermeldung aufgenommen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1653|#1653]]).
Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).