Hilfe:Lua/Programmierung

aus Wikipedia, der freien Enzyklopädie

{{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:

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
stringZeichenketten
table → Scribunto
mw (für „MediaWiki“) – Scribunto-Bibliotheken
frame-ObjektSchnittstelle zur Vorlagenprogrammierung
mw.htmlHTML-Element
mw.languageFormatieren in der aktuellen menschlichen Sprache
mw.messageSystemnachrichten
mw.siteaktuelles Wiki-Projekt
mw.textZeichenketten
mw.titleWiki-Seiten
mw.uriURL
mw.ustringZeichenketten 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:

  1. Syntaxfehler
  2. Programmierfehler zur Einbindung
  3. Fehlende Seite
  4. 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 Parameterliste args.
e, v = pcall( fun, args )
Dabei ist e im Fehlerfall false und v die Fehlermeldung.
Wenn kein Problem auftrat, hat e den Wert true und v nebst allen folgenden Variablen sind die üblichen Rückgabewerte von fun.
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 (also nil oder false ist).
Als message sollte eine spezifische Fehlermeldung mitgegeben werden; es wäre sonst ein lapidares assertion failed! – zumindest wird aber immer die Zeilennummer des assert in der Meldung gezeigt, nebst dem Namen des Moduls.
Wenn v weder nil noch false 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 Fehlermeldung message 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 mit 1 oder gar nicht anzugeben.
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 und false sind „nichts“.
  • Eine leere table {} ist gleichbedeutend mit nil.

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.