# Die Sprache Volang
Willkommen bei der Dokumentation zu Volang, der proprietären Programmiersprache von Voldeno. Sie ist das zentrale Werkzeug, um die Logik über Bausteine hinweg in unserem Smart-Home-Ökosystem umzusetzen.
# Einführung
Volang ist eine spezialisierte Programmiersprache, entwickelt von Voldeno, eigens dafür ausgelegt, die Logik von Bausteinen innerhalb unseres Smart-Home-Ökosystems umzusetzen. Sie überbrückt die Lücke zwischen Hardwarefähigkeiten und übergeordneter Automatisierung und erlaubt Entwicklern, fortgeschrittene Steuerungsszenarien effizient und sicher zu erstellen.
Der Hauptzweck von Volang ist es, das Verhalten von Bausteinen zu definieren. Im Voldeno-System stellt ein Baustein eine autonome Funktionseinheit dar (z. B. eine Rollladensteuerung, ein Thermostat oder einen Energiezähler). Volang erlaubt Entwicklern zu definieren, wie diese Bausteine auf Eingangssignale reagieren, internen Zustand verarbeiten und Aktionen aufgrund von Ereignissen auslösen.
# Architektur: das kompilierte Modell
Anders als einfache Skriptsprachen, die direkt aus dem Text interpretiert werden, nutzt Volang eine Architektur mit einer virtuellen Maschine (VM). Der Ausführungsprozess besteht aus zwei klar getrennten Stufen:
- Kompilierung: Der vom Entwickler geschriebene Quellcode wird vom Volang-Compiler verarbeitet. Dieser Schritt prüft Syntax, Datentypen und logische Integrität und wandelt den Code in ein optimiertes Binärformat um, den sogenannten Bytecode.
- Ausführung: Der entstandene Bytecode wird in die virtuelle Maschine Volang (Volang VM) geladen, eine dedizierte Laufzeitumgebung, die auf Voldeno-Steuerungen läuft. Die VM ist dafür zuständig, die Bytecode-Anweisungen zu interpretieren und sie sicher auf der physischen Hardware auszuführen.
# Warum Volang VM?
Eine Architektur mit virtueller Maschine bietet erhebliche Vorteile für Smart-Home-Umgebungen, vereinheitlicht den Entwicklungsablauf und erweitert die Fähigkeiten der Hardware:
- Sicherheit (Sandboxing): Nutzercode läuft in einer isolierten Umgebung, getrennt vom Kernbetriebssystem. Ein Laufzeitfehler (z. B. eine Division durch null) stoppt nur das betreffende Skript innerhalb der VM, sodass die Gesamtstabilität der Steuerung unangetastet bleibt.
- Leistung: Das Ausführen kompakten Binär-Bytecodes ist deutlich schneller und ressourcenschonender als das Parsen von Textdateien zur Laufzeit, was für eingebettete Systeme und Mikrocontroller entscheidend ist.
- Portabilität: In Volang geschriebene Logik ist hardwareunabhängig. Sie kann auf jedem Gerät ausgeführt werden, das die Volang VM betreiben kann, unabhängig von der zugrunde liegenden Prozessorarchitektur.
- Identische Simulation: Da die Volang VM plattformübergreifend konsistent läuft, kann exakt dieselbe Bausteinlogik innerhalb von Voldeno Studio ausgeführt werden. So können Entwickler ihren Code in einer Desktop-Umgebung testen und debuggen, mit der Garantie, dass sich das Verhalten identisch zum tatsächlichen Produktivsystem verhält.
- Verteilte Ausführung: Die Effizienz der VM erlaubt es, sie nicht nur auf dem zentralen Voldeno Hub, sondern auch auf peripheren Erweiterungsmodulen laufen zu lassen. Das ermöglicht echte verteilte Logik, bei der komplexe Verarbeitung lokal auf dem Gerät (am Rand) stattfinden kann, statt auf einfache Ereignismeldungen beschränkt zu sein.
# Lexikalische Konventionen
# Schlüsselwörter
Die folgenden Wörter sind in Volang reserviert und können nicht als Variablen- oder Funktionsnamen verwendet werden. Sie bilden das strukturelle Fundament der Sprache.
and break else
extern false fn
if or shl
shr return true
while xor
# Literale
In Volang ist ein Literal eine Notation, um einen festen Wert direkt im Quellcode darzustellen. Es sind die rohen Datenwerte, die Sie Variablen zuweisen oder an Funktionen übergeben. Volang unterstützt bestimmte Formate für boolesche, String-, Ganzzahl- und Gleitkomma-Literale.
# Boolesche Literale
Werden verwendet, um logische Zustände darzustellen. Es gibt genau zwei boolesche Literale:
true: stellt einen positiven oder aktiven Zustand dar (Ein).false: stellt einen negativen oder inaktiven Zustand dar (Aus).
# Ganzzahl-Literale
Ganzzahl-Literale stellen ganze Zahlen dar. Volang unterstützt zwei Notationen:
- Dezimal (Basis 10): Standard-Zahlendarstellung mit den Ziffern
0-9. Negative Zahlen werden mit einem Minuszeichen-versehen. - Hexadezimal (Basis 16): oft für Farbcodes, Bitmasken oder Hardwareadressen verwendet. Diese Literale müssen mit dem Präfix
0xbeginnen, gefolgt von Ziffern0-9und BuchstabenA-F(Groß-/Kleinschreibung ohne Bedeutung).
# Gleitkomma-Literale
Gleitkomma-Literale stellen Zahlen mit Nachkommateil dar.
- Syntax: Sie werden in Dezimalnotation mit einem Punkt
.als Dezimaltrennzeichen geschrieben. - Anforderung: Ein gültiges Gleitkomma-Literal erfordert typischerweise mindestens eine Ziffer vor oder nach dem Dezimalpunkt (z. B.
0.5,10.0,23.34).
# Text-Literale (Strings)
Text-Literale stellen Zeichenfolgen dar und werden für Nachrichten, Bezeichner oder Beschriftungen verwendet.
- Syntax: Text muss in doppelte Anführungszeichen
"eingeschlossen werden. - Mehrzeilige Unterstützung: Ein String-Literal in Volang kann sich über mehrere Zeilen erstrecken. Die Zeilenumbrüche innerhalb der Anführungszeichen bleiben erhalten, sodass sich Text für Anzeigetafeln oder Protokolle leicht formatieren lässt, ohne spezielle Escape-Codes wie
\nzu verwenden.
# Rohe Strings
Rohe Strings dienen dazu, Text genau so darzustellen, wie er geschrieben ist, und ignorieren Sonderzeichen oder Formatierungen, die der Compiler sonst interpretieren würde.
- Syntax: Rohe Strings müssen in dreifache doppelte Anführungszeichen
"""eingeschlossen werden. - Verwendung: Alles innerhalb der dreifachen Anführungszeichen wird als wörtlicher Text behandelt. Das ist besonders nützlich, um Codeblöcke, komplexe Regex-Muster oder vorformatierte Dokumentation zu speichern.
- Erhaltung: Wie bei standardmäßigen mehrzeiligen Strings bleiben alle Einrückungen und Zeilenumbrüche genau so erhalten, wie sie zwischen den
"""-Begrenzern erscheinen.
Beispiele:
// --- Boolesche Literale ---
is_light_on = true
is_door_locked = false
// --- Ganzzahl-Literale (dezimal) ---
counter = 10
negative_offset = -5
// --- Ganzzahl-Literale (hexadezimal) ---
status_mask = 0xFF // Dezimal 255
color_red = 0xFF0000 // Farbcode
// --- Gleitkomma-Literale ---
temperature = 23.34
calibration_factor = 0.95
voltage = 12.0 // .0 zeigt an, dass dies ein Float ist, keine Ganzzahl
// --- String-Literale ---
device_name = "Thermostat Wohnzimmer"
// Mehrzeiliger String (erhält die Formatierung)
notification_msg = "
Warnung: Hohe Temperatur!
Aktion: Kühlung aktiviert
Zeit: 12:00
"
// --- Rohes String-Literal ---
configJson = """
{
"status": "success",
"directory": "C:\users\volang\app",
"pattern": "\b[A-Z0-9._%+-]+",
"description": "No need to escape backslashes or "quotes" here!"
}
"""
# Werte und Typen
Volang arbeitet mit einem dynamischen Typsystem, das einen flexiblen Umgang mit Daten bietet. In Volang fungieren Variablen als generische Container statt als streng typisierte Speicher. Folglich müssen keine Typen (wie int oder float) im Code explizit deklariert werden. Die Typinformation ist dem Wert selbst inhärent, nicht der Variablen, die ihn hält.
# Erstklassige Werte
Alle Werte in Volang sind erstklassige Werte. Das bedeutet, dass jeder Wert, unabhängig von seinem Typ:
- in Variablen gespeichert werden kann,
- als Argument an Funktionen übergeben werden kann,
- als Ergebnis von Funktionen zurückgegeben werden kann.
# Typkonsistenz (strikte Typisierung)
Volang verlangt zwar keine Typdeklarationen, erzwingt aber Typstabilität. Sobald einer Variablen ein Wert eines bestimmten Typs (z. B. einer Ganzzahl) zugewiesen wurde, ist sie an diesen Typ gebunden. Sie können derselben Variablen anschließend keinen Wert eines anderen Typs (z. B. eines Strings) zuweisen. Dieser Mechanismus verhindert häufige Laufzeitfehler und sorgt für vorhersagbares Verhalten der Steuerung.
# Wertkategorien
Volang unterscheidet vier grundlegende Arten von Werten:
# 1. Numerisch (Ganzzahl und Float)
Werden für mathematische Berechnungen, Sensorwerte und Zähler verwendet.
- Ganzzahl: ganze Zahlen ohne Nachkommateil (z. B.
0,42,-15). - Float: Gleitkommazahlen, die Nachkommateile darstellen (z. B.
3.14,21.5,-0.001).
# 2. Textuell (String)
Werden für Statusmeldungen, Protokolle, Namen und Textverarbeitung verwendet. Strings sind Zeichenfolgen, eingeschlossen in doppelte Anführungszeichen ".
- Mehrzeilige Unterstützung: Volang unterstützt mehrzeilige Strings. Innerhalb der Anführungszeichen können Sie die Eingabetaste drücken, um einen Textblock über mehrere Zeilen zu erzeugen.
# 3. Logisch (Boolean)
Booleans stellen Wahrheitswerte dar und sind entscheidend für Entscheidungslogik (if, while).
- Werte:
trueoderfalse.
# 4. Undurchsichtig (Opaque)
Undurchsichtige Werte stellen komplexe Datenstrukturen dar, deren interne Umsetzung von der Volang VM verwaltet wird. Der Nutzer interagiert mit ihnen über dedizierte Funktionen der Standardbibliothek statt über direkte Syntaxoperatoren. Beispiele für undurchsichtige Typen sind:
- Array: eine geordnete, indizierte Sammlung von Werten.
- Map: eine Sammlung von Schlüssel-Wert-Paaren, bei der jeder Schlüssel eindeutig ist.
Dies sind nur einige der verfügbaren undurchsichtigen Typen, die Standardbibliothek kann weitere bereitstellen. Undurchsichtige Werte folgen denselben erstklassigen Regeln wie andere Typen: Sie können in Variablen gespeichert, als Argumente übergeben und aus Funktionen zurückgegeben werden. Sie können jedoch nicht mit den üblichen arithmetischen oder logischen Operatoren verwendet werden.
Beispiel:
// 1. Dynamische Typisierung
status = 25.5
// 2. Mehrzeilige Strings
config_info = "Gerät: Thermostat
Standort: Wohnzimmer
Firmware: v1.2"
// 3. Werte übergeben
fn logStatus(message) {
print(message)
}
logStatus(config_info)
# Variablen
Volang verfolgt einen schlanken Ansatz bei der Verwaltung von Variablen. Es gibt keine Deklarationsschlüsselwörter (wie var, let oder int). Eine Variable entsteht in dem Moment, in dem Sie einem Namen einen Wert zuweisen. Der Ort, an dem Sie die Variable definieren, bestimmt ihren Gültigkeitsbereich (Sichtbarkeit) im gesamten Skript.
# Variablen erstellen
Um eine Variable zu definieren, verwenden Sie einfach den Zuweisungsoperator =. Der Datentyp der Variablen wird aus dem zugewiesenen Wert abgeleitet und bleibt für die gesamte Lebensdauer dieser Variablen fest.
Syntax:
variable_name = value
# Gültigkeitsbereich von Variablen
Der Gültigkeitsbereich definiert, wo eine Variable in Ihrem Code zugänglich ist. Volang unterscheidet zwei Arten von Gültigkeitsbereichen:
# Globale Variablen
Variablen, die auf der obersten Ebene des Skripts definiert werden (außerhalb jeder Funktion oder jedes Steuerblocks), sind global.
- Sichtbarkeit: Sie sind von überall im Skript zugänglich, aber nicht innerhalb von Funktionen. Um einen globalen Wert innerhalb einer Funktion zu nutzen, übergeben Sie ihn als Argument.
- Anwendungsfall: Ideal, um den Gesamtzustand des Geräts zu speichern, etwa
target_temperature,system_modeoderalarm_status.
# Lokale Variablen
Variablen, die innerhalb einer Funktion (fn) oder eines Steuerblocks definiert werden, sind lokal.
- Sichtbarkeit: Sie existieren nur innerhalb des Blocks, in dem sie erstellt wurden. Sie werden zerstört, sobald die Ausführung diesen Block verlässt.
- Anwendungsfall: für temporäre Berechnungen, Schleifenzähler oder Zwischenschritte der Logik.
Beispiel:
// --- Globale Variablen ---
system_status = "IDLE"
setpoint = 21.5
// Globale Variablen sind NICHT innerhalb von Funktionen zugänglich.
// Übergeben Sie sie stattdessen als Argumente:
fn checkTemperature(current_temp, target) {
// --- Lokale Variable ---
// 'diff' existiert nur innerhalb dieser Funktion
diff = current_temp - target
if diff > 1.0 {
return "Kühlung erforderlich"
}
return "Stabil"
}
// Den globalen 'setpoint' als Argument übergeben
result = checkTemperature(22.8, setpoint)
// 'diff' ist hier nicht zugänglich
# Kommentare
Kommentare sind Codefragmente, die vom Volang-Compiler ignoriert werden. Sie dienen dazu, die Logik zu erklären, Notizen für andere Entwickler zu hinterlassen oder bestimmte Codeteile während des Tests vorübergehend zu deaktivieren.
# Einzeilige Kommentare
Einzeilige Kommentare beginnen mit zwei Schrägstrichen //. Jeder Text nach diesen Zeichen bis zum Zeilenende wird als Kommentar behandelt.
# Mehrzeilige Kommentare
Mehrzeilige (Block-)Kommentare beginnen mit /* und enden mit */. Alles zwischen diesen Markierungen wird ignoriert, unabhängig davon, über wie viele Zeilen es sich erstreckt.
Beispiel:
// Dies ist ein einzeiliger Kommentar
targetTemp = 21.5 // Kommentar am Ende einer Zeile
/*
Dies ist ein mehrzeiliger Kommentar.
Er eignet sich, um komplexe Logik
oder Blockköpfe zu beschreiben.
*/
/* oldLogic = 10
if (oldLogic > 5) {
// Dieser Code ist deaktiviert
}
*/
# Anweisungen
Volang ist darauf ausgelegt, visuelles Rauschen zu minimieren und die Lesbarkeit zu maximieren. Anders als Sprachen wie C++ oder Java verwendet Volang keine Semikolons (;), um das Ende einer Anweisung zu kennzeichnen.
Das Zeilenumbruchzeichen (Drücken der Eingabetaste) dient als endgültiges Ende einer Anweisung. Der Compiler interpretiert das Zeilenende als Abschluss des aktuellen Befehls.
# Zuweisungen
Zuweisungsoperatoren dienen dazu, einen Wert in einer Variablen zu speichern. Volang unterstützt den einfachen Standard-Zuweisungsoperator sowie zusammengesetzte Zuweisungsoperatoren.
# Einfache Zuweisung
Der Operator = weist den Wert auf der rechten Seite der Variablen auf der linken Seite zu.
target_temp = 21.0
event_counter = 0
# Zusammengesetzte Zuweisungsoperatoren
Zusammengesetzte Operatoren führen eine Operation auf dem aktuellen Wert der Variablen aus und aktualisieren sie anschließend mit dem neuen Ergebnis.
| Operator | Beschreibung |
|---|---|
+= | Addieren und zuweisen |
-= | Subtrahieren und zuweisen |
*= | Multiplizieren und zuweisen |
/= | Dividieren und zuweisen |
Wesentliche Verhaltensweisen:
- Initialisierungsanforderung: Bei zusammengesetzten Operatoren muss die Variable auf der linken Seite bereits existieren.
- Typheraufstufung: Die zusammengesetzte Zuweisung folgt denselben Regeln zur Typheraufstufung wie die normale Arithmetik.
Beispiele:
// Zähler erhöhen
event_counter += 1
// Sollwert verringern
target_temp -= 0.5
// Helligkeit auf 80 % skalieren
brightness = 100
brightness *= 0.8 // Ergebnis: 80.0
// Energie akkumulieren
total_energy_kwh = 0.0
current_usage = 1.5
total_energy_kwh += current_usage
# Ablaufsteuerung
Die if-Anweisung ist die grundlegende Entscheidungsstruktur in Volang. Der Bedingungsausdruck muss zu einem booleschen Wert (true oder false) ausgewertet werden. Nicht-boolesche Werte (wie Ganzzahlen, Floats oder Strings) werden nicht implizit umgewandelt, ihre Verwendung als Bedingung führt zu einem Kompilierungsfehler.
# Einfaches if
if (condition) {
// Code, der bei wahrer Bedingung ausgeführt wird
}
# if mit else
if (condition) {
// Code, der bei wahrer Bedingung ausgeführt wird
} else {
// Code, der bei falscher Bedingung ausgeführt wird
}
# Verkettete Bedingungen (else if)
if (condition_1) {
// Läuft, wenn condition_1 wahr ist
} else if (condition_2) {
// Läuft, wenn condition_1 falsch UND condition_2 wahr ist
} else {
// Läuft, wenn alle obigen Bedingungen falsch sind
}
Beispiel - Thermostatlogik:
current_temp = 19.0
target_temp = 21.0
hysteresis = 0.5
if (current_temp < (target_temp - hysteresis)) {
// Zu kalt -> Heizen starten
setHeating(true)
} else if (current_temp > (target_temp + hysteresis)) {
// Zu warm -> Kühlen starten
setCooling(true)
} else {
// Temperatur liegt im optimalen Bereich
setHeating(false)
setCooling(false)
}
# Schleifen
Volang unterstützt eine einzige Schleifenkonstruktion: die while-Schleife. Es gibt keine for- oder do-while-Schleifen in der Sprache. Wie bei if muss der Bedingungsausdruck zu einem booleschen Wert (true oder false) ausgewertet werden.
Syntax:
while (condition) {
// Code, der wiederholt ausgeführt wird
}
Volang unterstützt das Schlüsselwort break innerhalb von Schleifen. Trifft die Ausführung auf break, wird die Schleife sofort beendet.
Hinweis: In eingebetteten Systemen und Smart-Home-Steuerungen sind Endlosschleifen gefährlich. Eine
while-Schleife, deren Bedingung niefalsewird und in derbreaknie erreicht wird, blockiert die Skriptausführung, und der Baustein reagiert nicht mehr auf weitere Ereignisse. Es liegt in der Verantwortung des Skriptautors, sicherzustellen, dass die Schleife endet.
Beispiele:
// Einfacher Zähler
counter = 5
while (counter > 0) {
logValue(counter)
counter -= 1
}
// Warten mit Timeout (mit break)
attempts = 0
max_retries = 10
success = false
while (attempts < max_retries) {
if (checkConnection()) {
success = true
break
}
attempts += 1
}
if (success) {
startProcess()
}
# Funktionen
Funktionen sind wiederverwendbare Codeblöcke, die eine bestimmte Aufgabe erfüllen. In Volang werden Funktionen mit dem Schlüsselwort fn definiert.
Syntax:
fn functionName(argument1, argument2) {
// Auszuführender Code
}
Wesentliche Regeln:
- Argumente: Eine Funktion kann mehrere Argumente (durch Kommas getrennt) oder gar keine annehmen.
- Rückgabewerte: Verwenden Sie
return, um einen Wert zurückzugeben. Fehlt es, gibt die Funktion nichts zurück (void). - Namensbeschränkungen: Funktionsnamen müssen sich von den Namen der Funktionen der Standardbibliothek unterscheiden.
- Gültigkeitsbereich: Variablen, die innerhalb einer Funktion definiert werden, sind lokal zu dieser Funktion.
- Kein globaler Zugriff: Globale Variablen sind innerhalb von Funktionen nicht zugänglich. Alle externen Daten, die die Funktion benötigt, müssen als Argument übergeben werden.
Beispiel:
fn calculateAverage(a, b, c) {
total = a + b + c
return total / 3
}
avg = calculateAverage(20.5, 21.0, 19.5)
# Ausdrücke
# Binäre arithmetische Operatoren
| Operator | Beschreibung |
|---|---|
+ | Addition |
- | Subtraktion |
* | Multiplikation |
/ | Division |
% | Modulo |
Rangfolge: Multiplikation, Division und Modulo werden vor Addition und Subtraktion ausgewertet.
Beispiele:
// Sensorkalibrierung
raw_value = 50
scale_factor = 1.2
offset = 5.0
calibrated_value = raw_value * scale_factor + offset // 65.0
// Mittelwert mit Klammern
average_temp = (temp_1 + temp_2 + temp_3) / 3
// Modulo für zyklische Ereignisse
remainder = loop_counter % 10
# Binäre logische Operatoren
| Operator | Beschreibung |
|---|---|
and | Logisches UND, gibt nur true zurück, wenn beide Operanden true sind |
or | Logisches ODER, gibt true zurück, wenn mindestens ein Operand true ist. Beide Operanden werden stets ausgewertet (keine Kurzschlussauswertung). |
Beispiele:
// Bedingte Beleuchtung (UND)
if (motion_active and brightness_level < threshold) {
setLight(true)
}
// Redundantes Auslösen (ODER)
if (door_open or window_vibration) {
triggerAlarm()
}
// Komplexe Logik (Gruppierung)
heating_on = manual_override or (is_weekend and user_home)
# Binäre Bitoperationen
Bitoperationen erlauben die direkte Manipulation einzelner Bits innerhalb von Ganzzahlwerten.
| Operator | Beschreibung |
|---|---|
& | Bitweises UND |
| | Bitweises ODER |
xor | Bitweises XOR |
shl | Nach links verschieben |
shr | Nach rechts verschieben |
Beispiel - bitweises UND zum Maskieren:
sensor_value = 0xA7 // Binär: 1010 0111
mask = 0x0F // Binär: 0000 1111
// Nur die unteren 4 Bits extrahieren
lower_nibble = sensor_value & mask
// Ergebnis: 0x07 (Binär: 0000 0111)
