Hilfe:Lua/Programmierung
{{Wikipedia:Lua/Linkbox}}
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
mw
und Hilfsbibliotheken - Links
- Umgebung – aktueller Wiki-Server und seine Ausstattung
- Internationalisierung
- Tutorial
Bibliotheken
- 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
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
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
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
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
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
#invoke
wenigstens immer vom Typ string.
Hilfsfunktionen
- pcall()
- Absturzsicheres Ausführen einer Funktion
fun
mit Parameterlisteargs
. e, v = pcall( fun, args )
- Dabei ist
e
im Fehlerfallfalse
undv
die Fehlermeldung. - Wenn kein Problem auftrat, hat
e
den Werttrue
undv
nebst allen folgenden Variablen sind die üblichen Rückgabewerte vonfun
. pcall
steht für protected call.- 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 (alsonil
oderfalse
ist). - Als
message
sollte eine spezifische Fehlermeldung mitgegeben werden; es wäre sonst ein lapidares assertion failed! – zumindest wird aber immer die Zeilennummer desassert
in der Meldung gezeigt, nebst dem Namen des Moduls. - Wenn v weder
nil
nochfalse
ist, 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 Fehlermeldungmessage
zurückgegeben. - Der optionale Parameter
level
ist 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
allowEnvFuncs
nicht zugelassen wird. - Wird
level
nicht angegeben oder ist Null oder zu groß, so wird keine Information zum Ort gezeigt. - Es empfiehlt sich momentan,
level
immer mit1
oder 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
Anders als bei anderen Programmiersprachen gilt:
- Sowohl die Zahl
0
wie auch die leere Zeichenkette""
sind „etwas“. - Nur
nil
undfalse
sind „nichts“. - Eine leere table
{}
ist gleichbedeutend mitnil
.
Das ist vor allem für if
-Abfragen wichtig.
Table und Objekt
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.
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
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
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
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
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 )
Benutzerdefinierte Objekte
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
Mittels der Funktion mw.addWarning( wikitext )
können im Kopfbereich der Seitenvorschau Warnungen angezeigt werden.
wikitext
wird dabei noch geparset.- Mehrfache identische Meldungen werden nur einmal gezeigt.
- 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-54381975
generieren, der im Seitenkopf verlinkt und an der Einbindung verankert wird. - Die unterschiedlichen Einbindungen in der Seite haben keine Kenntnis voneinander.