Zum Inhalt springen

Fehlerbehandlung

Was ein Template tut, wenn ein Tag fehlschlägt: onerror=, default=, woher die Strategie kommt, und der eine Fehler, der niemals auffangbar ist.

Ein Prompt wird meist im unpassendsten Moment zusammengesetzt — während eine Anfrage wartet. Die interessante Frage ist deshalb nicht, ob ein Konstrukt fehlschlagen kann, sondern was der Renderer ausgibt, wenn es passiert.

Fehlerbehandlung greift zur Ausführungszeit. onerror= bestimmt, was anstelle eines fehlschlagenden Konstrukts erscheint, statt den Render abzubrechen. Lexer- und Parser-Fehler liegen vollständig außerhalb: Ein Konstrukt, das nicht parst, erreicht die Ausführung nie, es gibt also nichts zu ersetzen. Diese Grenze ist das Erste, was man verinnerlichen sollte — Fehlerbehandlung rettet keinen Syntax-Tippfehler, nur ein Fehlschlagen der Arbeit selbst.

Das Vokabular seit v0.24.0

onerror=Gibt ausAnmerkungen
thrownichts — der Fehler propagiert, der Render schlägt fehlDer Standard, und der Rückfall für jeden unbekannten Wert.
defaultdas default=-Attribut des KonstruktsLeer, wenn kein default= vorhanden ist, also remove mit Notausgang.
removeleerer String
keeprawden Originalquelltext des Konstrukts, wortgetreuLeer, wenn kein Quelltext erfasst wurde — es schlägt geschlossen fehl, statt einen falschen Ausschnitt auszugeben.
logleerer StringZusätzlich eine WARN-Zeile mit Tag und Fehler.
exons
{~exons.var name="tone" onerror="default" default="neutral" /~}

Woher die Strategie kommt

Ein Dokument gibt selten für jedes Tag seine eigene Fehlerbehandlung an. Das muss es auch nicht — die Auflösung geht drei Schritte und hält beim ersten an, der antwortet:

  1. Das eigene onerror= des fehlschlagenden Konstrukts (bei einem Zweig: das des Zweigs — siehe unten).
  2. Der Kontext-Standard: die Strategie, die der Ausführungskontext meldet, gesetzt aus WithErrorStrategy der Engine und vom Kind-Kontext eines Schleifenkörpers geerbt.
  3. throw.

Der Host bestimmt also die Haltung, und das Dokument überschreibt sie lokal. Ein Renderer für Entwürfe kann überall nachsichtig sein, ohne dass ein einziges Dokument mitmacht; ein Validator kann über denselben Dokumenten strikt sein.

Ein Schreibfehler fällt nicht durch

Die Auflösung hält an, sobald der Schlüssel vorhanden ist, und ein unbekannter Wert löst zu throw auf. onerror="remov" schlägt also hart fehl, unter einem Renderer, der nie hart fehlschlagen soll — und der Schreibfehler ist der einzige Hinweis darauf. Engine.Validate meldet ihn als Fehler auf jeder Form, die das Attribut beachtet; genau darum lohnt sich diese Prüfung, bevor du ein Template auslieferst.

Welche Fehler auffangbar sind seit v0.24.0

KonstruktFehlschlag
jedes Tagfür den Tag-Namen ist kein Resolver registriert
jedes Tagdas Validate des Resolvers weist die Attribute ab
jedes Tagdas Resolve des Resolvers gibt einen Fehler zurück
exons.if / exons.elseifdie Bedingung des Zweigs lässt sich nicht auswerten
exons.forder in=-Pfad ist im Kontext nicht vorhanden
exons.forder gefundene Wert ist nicht iterierbar
exons.forder Host-Kontext kann keinen Kind-Kontext erzeugen
exons.switchder eval=-Ausdruck der Verteilung lässt sich nicht auswerten
exons.caseder eval=-Ausdruck des Falls lässt sich nicht auswerten

Alles andere ist ein harter Fehlschlag. Parse-Zeit-Abweisungen — ein fehlendes eval=, item= oder in=, ein nicht-numerisches oder negatives limit=, ein ungeschlossenes oder falsch geschlossenes Konstrukt — passieren vor der Ausführung. Und ein Fehler innerhalb eines gewählten Zweigs oder Schleifenkörpers gehört zur Stelle dieses Knotens: Das onerror= des umgebenden Konstrukts fängt ihn nicht ab.

Die Versionsmarkierung dieses Abschnitts ist tragend. Vor v0.24.0 lasen exons.if, exons.for und exons.switch nur die Schlüssel, die sie brauchten, und ließen den Rest der Attribut-Map verfallen — onerror= und default= waren an einem Block-Konstrukt also stillschweigend wirkungslos, und jeder der obigen Fehlschläge war ein unbedingter Render-Fehler. Das Attribut zu schreiben war nicht falsch; es tat nur nichts.

Fehlerbehandlung pro Zweig seit v0.24.0

Ein exons.elseif und ein exons.case tragen jeweils ihr eigenes onerror=, beachtet an der Position des Zweigs statt an der des öffnenden Tags:

exons
{~exons.if eval="input.verbose"~}
  Langform
{~exons.elseif eval="input.terse" onerror="remove"~}
  Kurzform
{~/exons.if~}

Das ist wichtig, weil eine Bedingung das ist, was man am häufigsten falsch schreibt — und eine kaputte Bedingung im dritten Zweig einer Kette sollte nicht dem ersten zugeschrieben werden.

default= hat zwei Lesarten, die nie kollidieren

Bei exons.var und exons.input trägt derselbe Attributname zusätzlich eine Resolver-Bedeutung — ein Lookup-Fehlschlag oder ein leerer deklarierter Wert. Dieser Pfad gibt einen Wert zurück statt eines Fehlers, löst also auf, bevor die Fehlerbehandlung überhaupt befragt wird. default= als Fehlerbehandlung liest nur onerror="default".

Vererbung ist niemals auffangbar seit v0.25.0

{~exons.extends~} trägt kein onerror=, und keine Strategie — auch remove nicht — macht es überlebbar.

Vererbung wird aufgelöst, bevor irgendein Tag ausgeführt wird, denn die Elternkette entscheidet, welches Dokument überhaupt ausgeführt wird. Es gibt keinen Knoten, dessen Fehlschlag eine Strategie abfangen könnte. Ein nicht auflösbares extends lässt den Render fehlschlagen, unter jeder Konfiguration.

Das ist bewusst so und nicht unfertig. Es abzuschwächen würde die nackten Block-Körper des Kindes rendern — ein anderes Dokument als das, das der Autor geschrieben hat, zurückgegeben als Erfolg. Ein Aufrufer kann das nicht erkennen, was es strikt schlechter macht als einen Fehler.

Die beiden in einer Registry unterscheiden

Ein Konsument unterscheidet einen Vererbungsfehler von jedem anderen Ausführungsfehler an zwei Fakten: Der Fehler ist ein cuserr, und seine tag-Metadaten sind extends. Das gilt für alle drei Arten, auf die die Auflösung fehlschlagen kann — die Deklaration ist nicht lesbar, es gibt keine Engine zum Auflösen, oder die Kette lässt sich nicht gehen (fehlendes Eltern-Template, zirkulär, zu tief). Verzweige nicht über den Code des Fehlers: Er wird aus dem Wortlaut der zugrunde liegenden Meldung abgeleitet, nicht von der Bibliothek gesetzt.