Hilfe:Lua/Programmierung
Diese Seite stellt für Lua-Programmierer einige Besonderheiten der Sprache zusammen, die im Scribunto-Tutorial nicht oder nur verborgen dargestellt sind.
Außerdem gibt es eigene Hilfeseiten zu Sonderthemen:
- Modul im Wiki – Integration des Lua-Codes in den Wiki-Kontext
- frame-Objekt – Schnittstelle zur Vorlagenprogrammierung
- Modul für eine bestimmte Vorlage – Einzelne Vorlage unterstützen
- Zeichenketten – String-Funktionen, Pattern (regulärer Ausdruck) und Text-Elemente
- mw – Objekt
mwund Hilfsbibliotheken - Links
- Umgebung – aktueller Wiki-Server und seine Ausstattung
- Internationalisierung
- Tutorial
Bibliotheken
[Bearbeiten | Quelltext bearbeiten]- Bestandteil der Sprache Lua sind einige Standardbibliotheken. Soweit sie im Kontext der Wiki-Seiten sinnvoll sind, werden sie unterstützt.
- Außerdem bieten Scribunto als Objekt
mw(für MediaWiki) einige Wiki-spezifische Bibliotheken an, die auch gut mit der sonstigen Architektur eines Wiki-Servers kooperieren.
- Lua-Basissprache
- debug
- debug.traceback
- math – Mathematische Funktionen → Scribunto
- os
- package – Laden von Modulen → Scribunto
- string – Zeichenketten
- table → Scribunto
- mw (für „MediaWiki“) – Scribunto-Bibliotheken
-
mw.addWarning()- mw.allToString()
- mw.clone()
- mw.getCurrentFrame()
- mw.incrementExpensiveFunctionCount()
- mw.isSubsting()
- mw.loadData()
- mw.log()
- mw.logObject()
- frame-Objekt – Schnittstelle zur Vorlagenprogrammierung
- mw.html – HTML-Element
- mw.language – Formatieren in der aktuellen menschlichen Sprache
- mw.message – Systemnachrichten
- mw.site – aktuelles Wiki-Projekt
- mw.text – Zeichenketten
- mw.title – Wiki-Seiten
- mw.uri – URL
- mw.ustring – Zeichenketten in Unicode
- mw.wikibase → mw:Extension:Wikibase Client/Lua
- Ladbare Bibliotheken
-
- Eher seltener erforderlich → Scribunto
- bit32
- libraryUtil
- luabit
- ustring
Fehlermeldungen
[Bearbeiten | Quelltext bearbeiten]Es gibt vier Ursachen für Skriptfehler:
- Syntaxfehler
- Programmierfehler zur Einbindung
- Fehlende Seite
- Anwenderfehler bei Einbindung
Von sich aus gab Lua dabei bis zum Herbst 2014 immer nur eine einzige Fehlermeldung von sich: „Skriptfehler“. Das war langweilig und half niemandem bei der Beseitigung. Inzwischen wird der Name des Moduls, die Zeilennummer und eine konkrete Situationsanalyse in jeder einbindenden Seite angezeigt. Die Fehlermeldung ist verlinkt mit einer Popup-Box, die die Aufruffolge mit einzelnen Modulnamen und Zeilennummer wiedergibt.
Außerdem wird die Kategorie:Wikipedia:Seite mit Skriptfehlern mit allen einbindenden Seiten gefüllt – das sollte vermieden werden, sondern Laufzeitfehler sollten abgefangen und in Modul-spezifische Wartungskategorien geleitet werden.
Syntaxfehler
[Bearbeiten | Quelltext bearbeiten]Ein Syntaxfehler ist oft ein trivialer Schreibfehler. Dieser müsste eigentlich zur Folge haben, dass das Abspeichern der Modul-Seite unterbunden wird; es sei denn, man hätte bis zum Herbst 2014 dem Feld „⧼scribunto-ignore-errors⧽“ ein Häkchen gegeben. Das ist nicht länger möglich.
Beim Anzeigen der Seitenvorschau des Moduls erhält man eine möglichst qualifizierte Fehlermeldung mitsamt Zeilennummer. Bei schweren Syntaxproblemen kann das jedoch nicht geleistet werden und es kommt der lakonische „Skriptfehler“.
In der „Fehlerbereinigungskonsole“ lassen sich beim Bearbeiten differenziertere Hinweise zur Fehlersuche gewinnen.
Ein typischer qualifizierbarer Fehler wäre ein einzelner Punkt statt zwei zur Verkettung von Zeichenketten (PHP-Stil). Ohne konkrete Hilfestellung bleibt jedoch fehlendes then oder end und auch eine nicht geschlossene Zeichenkette.
Programmierfehler zur Einbindung
[Bearbeiten | Quelltext bearbeiten]Wenn es durch die Programmstruktur in einigen Fällen zu ungeeigneten Datentypen kommt, in manchen nicht, so ist das schwer zu finden. Wird dann etwa versucht, auf ein nil eine Zeichenketten-Funktion anzuwenden, gibt das den bekannten undefinierten Fehler. Für den Einbinder einer Vorlage ist das nicht aufzulösen. Möglichst alle Pfade sind deshalb auf Testseiten durchzuspielen, und die Voraussetzungen hinsichtlich Datentyp und Wertebereich sind in den Funktionen zu überprüfen.
Fehlende Seite
[Bearbeiten | Quelltext bearbeiten]Wenn eine Seite transcludiert wird, kann es sein, dass dieser Seitenname nicht existiert; insbesondere die erwartete Seite verschoben wurde oder beim Komponieren eines zusammengesetzten Namens ein Fehler auftrat. Um diese Situation auflösen zu können, wird eine präzise Information benötigt, welche Seite fehlt. LuaWiki.transclude() ermöglicht eine sichere Einbindung und sorgt für eine präzise Fehlermeldung.
Anwenderfehler bei Einbindung
[Bearbeiten | Quelltext bearbeiten]Was immer die Vorlagenprogrammierer und Einbinder von Vorlagen an Parametern eingeben, muss analysiert und bei Problemen mit einer konkreten Nachricht versehen werden, damit eine Berichtigung möglich wird.
Defensives Programmieren ist ratsam.
- Alle Eingabewerte sind als möglicherweise fehlend oder syntaktisch falsch anzusehen, bis das Gegenteil festgestellt wurde.
- Allerdings sind vorhandene Parameter im
#invokewenigstens immer vom Typ string.
Hilfsfunktionen
[Bearbeiten | Quelltext bearbeiten]- pcall()
- Absturzsicheres Ausführen einer Funktion
funmit Parameterlisteargs. e, v = pcall( fun, args )- Dabei ist
eim Fehlerfallfalseundvdie Fehlermeldung. - Wenn kein Problem auftrat, hat
eden Werttrueundvnebst allen folgenden Variablen sind die üblichen Rückgabewerte vonfun. pcallsteht für protected call.argsist eine Komma-getrennte Aufzählung von null bis beliebig vielen Parameterwerten.- Das entspricht einem catch.
- assert()
assert( v, message, ... )- Löse einen Skript-Abbruch mit einer qualifizierten Fehlermeldung aus, wenn eine Bedingung nicht eingehalten wird.
- Entspricht in etwa der von
error(), jedoch nur, wenn die Bedingung v nicht erfüllt ist (alsoniloderfalseist). - Als
messagesollte eine spezifische Fehlermeldung mitgegeben werden; es wäre sonst ein lapidares assertion failed! – zumindest wird aber immer die Zeilennummer desassertin der Meldung gezeigt, nebst dem Namen des Moduls. - Wenn v weder
nilnochfalseist, werden alle Argumente einschließlich v und message zurückgegeben. - Der Aufruf ist nur innerhalb einer durch
pcall()geschützten Umgebung sinnvoll; ansonsten wird bloß ein allgemeiner „Skriptfehler“ ohne nähere Einzelheiten ausgelöst. - Sinn der Angelegenheit ist, in einer tieferen Ebene der Funktions-Hierarchie für v eine Funktion anzugeben, die die Gültigkeit der ihr übergebenen Werte prüft. Beim Versagen wird direkt in die Ebene mit
pcall()gesprungen; etwa die Schnittstelle zum#invoke. Damit erspart man sich das Durchreichen von besonderen Rückgabewerten von Funktion zu Funktion. - Die Funktion entspricht einem throw.
- error()
- Diese Funktion soll einen Skript-Abbruch mit einer qualifizierten Fehlermeldung ermöglichen.
error( message, level )- Der Aufruf ist nur innerhalb einer durch
pcall()geschützten Umgebung sinnvoll; ansonsten wird bloß ein allgemeiner „Skriptfehler“ ohne nähere Einzelheiten ausgelöst. - Innerhalb der von
pcall()ausgehenden Funktionsfolge wird die Fehlermeldungmessagezurückgegeben. - Der optionale Parameter
levelist in der Original-Sprache Lua eine Aufruf-Folge oder stack, dessen anzuzeigende Tiefe vorgegeben werden kann.- Zurzeit werden in Scribunto nur der Name des Moduls und die Zeilennummer angezeigt, und dies nur auf einer Ebene. Das ist möglicherweise eine noch unvollendete Situation. Der Aufbau von Wiki-Seiten und die sonstige Handhabung der Lua-Dateien unterscheiden sich etwas. Es hängt offenbar damit zusammen, dass in Scribunto
allowEnvFuncsnicht zugelassen wird. - Wird
levelnicht angegeben oder ist Null oder zu groß, so wird keine Information zum Ort gezeigt. - Es empfiehlt sich momentan,
levelimmer mit1oder gar nicht anzugeben.
- Zurzeit werden in Scribunto nur der Name des Moduls und die Zeilennummer angezeigt, und dies nur auf einer Ebene. Das ist möglicherweise eine noch unvollendete Situation. Der Aufbau von Wiki-Seiten und die sonstige Handhabung der Lua-Dateien unterscheiden sich etwas. Es hängt offenbar damit zusammen, dass in Scribunto
- Die Grundidee ähnelt der von
assert(). - Die Funktion entspricht einem throw.
- LuaWiki.*
- Aufrufe der Wiki-Umgebung.
- Diese Funktionen analysieren die übergebenen Parameter und sorgen für eine präzise Fehlermeldung.
Leere Werte
[Bearbeiten | Quelltext bearbeiten]Anders als bei anderen Programmiersprachen gilt:
- Sowohl die Zahl
0wie auch die leere Zeichenkette""sind „etwas“. - Nur
nilundfalsesind „nichts“. - Eine leere table
{}ist gleichbedeutend mitnil.
Das ist vor allem für if-Abfragen wichtig.
Table und Objekt
[Bearbeiten | Quelltext bearbeiten]Auf Werte und Funktionen als Komponenten einer table wird mit einem . als t.k zugegriffen, neben der Notation t["k+"] auch für Namen von Komponenten, die nicht rein alphanumerisch sind.
Die Elemente einer Tabelle können mit verschiedenen Iteratoren erfasst werden. Typisch sind folgende Universaliteratoren:
local Beispieltabelle = {"eins", "zwei", 3, "Quattro stagioni"}
for meinIndex, meinWert in ipairs(Beispieltabelle) do
...
end
iteriert durch die unbenannten Elemente einer Tabelle, während
local Beispieltabelle = {erster="eins", zweiter="zwei", dritter=3, vierter="Quattro stagioni"}
for meinSchlüssel, meinWert in pairs(Beispieltabelle) do
...
end
durch die benannten Werte der Tabelle iteriert.
Bei einem Objekt (einer Instanz, durch eine von Lua oder Scribunto angeboten Bibliothek) wird auf Eigenschaften ebenfalls mit einem . zugegriffen. Für Funktionen (Methoden) ist hingegen ein : zu verwenden. Beispiele siehe String-Funktionen.
Die Notation mit Doppelpunkt gilt nicht nur für Lua- und Scribunto-Objekte, sondern auch für benutzerdefinierte Objekte. Dabei erhält die Methode implizit einen zusätzlichen Parameter self, auf den innerhalb der Methode zugegrffen werden kann. Dieser repräsentiert die Instanz des Objekt-table, während die table einer „Klasse“ entspräche. Die beiden Anweisungen
function meinObjekt:f ( params ) body end
und
meinObjekt.f = function ( self, params ) body end
sind äquivalent.
Bei Funktionen in Objekten (Methoden) gibt es außerdem die Möglichkeit, benannte Argumente zu verwenden. Statt runder Klammern sind dann geschweifte Klammern {} zu benutzen. Dieses Format ist meist eine zusätzliche Option; in manchen Fällen wie bei frame:expandTemplate{} aber der einzige unterstützte Zugriff.
or: Von links nach rechts
[Bearbeiten | Quelltext bearbeiten]Die Vorlagenprogrammierung wird dadurch unterstützt, dass der or-Operator den links stehenden Disjunktionsausdruck zurückgibt, wenn dieser weder nil noch false ist, und sonst den Ausdruck auf der rechten Seite.
Damit lassen sich auch Datentypen bei fehlenden Angaben sichern:
saveString = frame.args.optional or ""
saveTable = getTable() or {}
Wenn der Wert leer und/oder dabei vom falschen Datentyp ist, wird mit dem richtigen Datentyp initialisiert.
Weil oft Bibliotheksfunktionen in bestimmten Situationen nil oder false zurückgeben, diese Zeichenketten aber nicht als Ergebnis der Vorlageneinbindung auftreten sollen, kann man etwa einen wirklich „leeren“ Wert mit der folgenden Anweisung sicherstellen:
return fun(x) or ""
Gibt fun(x) einen echten Wert zurück, wird dieser an das #invoke zurückgegeben; ist das nichts, greift das "".
Analog kann man den Wert 1 bei „nichts“ oder „etwas“ sicherstellen:
return fun(x) and "1" or ""
Weil diese Notation nicht sehr übersichtlich ist, sollte man es aber bei diesem Kniff bewenden lassen.
Reihenfolge lokaler Funktionen
[Bearbeiten | Quelltext bearbeiten]Die Reihenfolge lokaler Funktionen ist signifikant. Eine lokale Funktion muss in der physischen Abfolge der Definitionen definiert gewesen sein, bevor sie verwendet werden darf.
Zählschleife
[Bearbeiten | Quelltext bearbeiten]Bei einer Zählschleife
for v = e1, e2 do
werden die Argumente nur einmal zu Beginn ausgewertet; danach in interne Variablen überführt.
Es ist nicht möglich, innerhalb der Schleife die Endbedingung auszulösen, etwa durch e1=e2. Vielmehr ist eine break-Anweisung auszuführen.
Zeichenketten-Parameter trimmen
[Bearbeiten | Quelltext bearbeiten]Unbenannte Zeichenketten-Parameter sollten stets getrimmt werden, wenn sie aus Vorlagen stammen und Leerzeichen nicht signifikant wären. In der Vorlagenprogrammierung war dies nur mit Tricks möglich gewesen:
{{#if:trim|{{{1}}}}}
In Lua wäre der Weg eine Bibliotheksfunktion, wenn gesichert ist, dass s eine Zeichenkette ist:
s = mw.text.trim( s )
Strikte Syntax
[Bearbeiten | Quelltext bearbeiten]Insbesondere während der Entwicklungsphase kann mit dem folgenden Statement möglichst früh in den ausführbaren Zeilen eine verschärfte Syntaxkontrolle angefordert werden.
require( "strict" )
Zurzeit bewirkt dies lediglich, dass gesichert wird, dass alle Variablenbezeichner auch explizit als local deklariert sein müssen.
- Damit können sie nicht mit Zuweisungen in anderen Modulen oder Gültigkeitsbereichen kollidieren und unerklärliche Nebeneffekte bewirken.
- Variablenbezeichner müssen entweder auf Ebene des Moduls oder einer Funktion oder in einem Block als
localvereinbart sein (ausgenommen automatische lokale Variablen in Schleifen).
Weil dies im produktiven Stadium Performance und Ressourcen kostet, sollte es in fertigen Versionen nicht mehr aktiv vorhanden sein.
Benutzerdefinierte Objekte
[Bearbeiten | Quelltext bearbeiten]Es wäre vermessen, Lua als objektorientierte Sprache anzusehen. Gleichwohl lassen sich über eine metatable einer table einige objektartige Eigenschaften geben.
Damit lassen sich spezifische Methoden definieren für:
- Zugriff auf Element, neues Element, Vereinigung, Länge, Vergleich, arithmetische Operationen.
Weil die table ohnehin Funktionen als Elemente enthalten kann, lassen sich nach Art eines selbstdefinierbaren Objekts Methoden und Eigenschaften darstellen und über eine Generierungsfunktion auch allen Instanzen einer „Klasse“ zuweisen; diese sogar geeignet vererben und überschreiben. Das traut man der schlichten Sprache auf den ersten Blick nicht zu.
Fehlermeldungen im Kopf der Seitenvorschau
[Bearbeiten | Quelltext bearbeiten]Mittels der Funktion mw.addWarning( wikitext ) können im Kopfbereich der Seitenvorschau Warnungen angezeigt werden.
wikitextwird dabei noch geparset.- Mehrfache (identische?) Meldungen werden nur einmal gezeigt. Überhaupt wurde bis August 2025 immer nur eine einzige Meldung dargestellt.
- Problem: Zunächst besteht kein Zusammenhang zwischen der Warnmeldung im Seitenkopf und der aufrufenden Stelle in der Seite, wo das Problem auftritt. Bei sehr langen Seiten – und wenn dann auch noch keine Fehlermeldung in der Seitenvorschau sichtbar ist – wird die Meldung sinnfrei, weil die Autoren sie nicht der Ursache zuordnen können.
- Mit dem Zufallszahlengenerator math.random() ließe sich ein temporärer Fragmentbezeichner wie
error-54381975generieren, der im Seitenkopf verlinkt und an der Einbindung verankert wird. - Die unterschiedlichen Einbindungen in der Seite haben keine Kenntnis voneinander.