Spracherweiterungen
Die Erfahrung zeigt, dass sich viele konkrete Design-Entscheidungen bei der Entwicklung einer Programmiersprache irgendwann in irgend einer Form als falsch herausstellen.
Deshalb wurde Perl 6 so entwickelt, dass möglichst vieles möglichst leicht änderbar ist und auch erweiterbar ist.
Hier werden einige Möglichkeiten vorgestellt, wie man Perl 6 verändern und erweitern kann.
Neue Operatoren
Operatoren sind in Perl 6 nur Funktionen mit komischen Namen. In dem
Ausdruck 1 + 2 steht das + für
infix:<+>.
Im Allgemeinen hat ein Operator einen Typ (im obigen Beispiel
infix), ein Symbol (+), einen Vorrang (Englisch
precedence), eine Assoziativität und eine Signatur.
Operatortypen
Perl 6 kennt folgende Operatortypen:
Name Beispiel Beschreibung
infix 1 + 2 steht zwischen zwei Ausdrücken
postfix $a++ steht hinter einem Ausdruck
prefix +3 steht vor einem Ausdruck
circumfix <a b c> umschliesst einen Ausdruck
postcircumfix uc($str) steht hinter einem Ausdruck und um-
schliesst einen weiteren Ausdruck
Zwischen einem Ausdruck und dem entpsrechenden postfix- prefix- oder postcircumfix-Operator darf kein Leerzeichen stehen.
Die ersten vier Operatoren sind immer subs, postcircumfix-Operatoren sind Methoden (d.h. gehören zu einer Klasse).
Symbole
Symbole für Operatoren sind nicht auf Sonderzeichen beschränkt, und auch
die Länge ist nicht beschränkt. So könnte man sich einen Operator mit Namen
kaffee definieren (was aber nicht besonders sinnvoll wäre).
Beim Parsen gilt das sogenannte "Longest Token Matching", d.h. wenn an
einer Stelle mehrere Operatoren passen, wird der längste gewählt. Deshalb wird
++$a als prefix:'++'($a) geparsed und nicht als
prefix:'+'(prefix:'+'($a)).
Operatorvorrang
Operatorvorrang entscheidet, wie "gierig" ein Operator ist, und in welcher Reihenfolge Operatoren ausgeführt werden.
In dem Ausdruck 1 + 2 * 3 wird erst das 2 * 3
ausgeführt, danach wird 1 addiert, weil infix:'*' einen höheren
Vorrang als infix:'+' hat.
Wenn man neue Operatoren definiert, kann man ihnen eine bestimmten Vorrang mitgeben. Dieser wird relativ zu bisherigen Operatoren angegeben.
multi sub postfix:<!> is equiv(&postfix:<++>) (Int $a) { return [*] 1..$a; }
Mit is equiv gibt man an, dass der neue Operator den gleichen
Vorrang (und die gleiche Assoziativität) wie der danach genannte Operator
haben soll. Alternativen sind is tighter und is
looser, die jeweils neue Vorrangebenen einführen.
Assoziativität
Die Assoziatitvität eines Infixoperators bestimmt, wie ein Konstrukt aus mehreren Operatoren mit der gleichen Vorrangebene gehandhabt wird.
Wenn man z.B. 1 op 2 op 3 hat, und op
links-assoziativ ist, wird das ganze genauso wie (1 op 2) op 3
gehandhabt. Wenn die Assoziativität allerdings Rechts ist, wird das Beispiel
als 1 op (2 op 3) verstanden. Ein Beispiel, wo das einen
Unterschied macht, ist die Division. 1 / (2 / 2) == 1, während
(1 / 2) / 2 == 1/4 ist. Daher ist der Ausdruck 1 / 2 /
2 nur dann eindeutig definiert, wenn man die Assozitivität von
infix:'/' kennt.
Neben den gerade vorgestellten Assozitivitäten left (links) und
right (rechts) gibt es auch noch non (keine
Assoziativität; damit wäre das Beispiel 1 op 2 op 3 ein
Syntaxfehler), chain (was "chained comparisons" wie in
if 0 <= $angle < 2 * pi { ... }
erlaubt)
und list (wie es z.B. der Komma-Operator zum konstruieren von
Listen hat).
Ist die Assoziativität eines Operators nicht angegeben, wird
Linksassoziativität angenommen, ausser wenn der Vorrang mit is
equiv spezifiert wurde; dann wird die Assoziativität des als äquivalent
angegeben Operators übernommen.
Signatur
Die Signatur einer Funktion, einer Methode oder eines Operators gibt an, wie viele Argumente sie oder er bekommt, und optional zu welchen Typs sie sind.
Dabei gilt, dass Pre- und Postfixoperatoren immer ein Argument
bekommen, Infixoperatoren der Assoziativitäten left, right, non,
chain zwei Argument und Infix-Operatoren der Assozitivität
list sowei Circumfix- und Postcircumfixoperatoren Listen
erhalten. Postfixoperatoren bekommen ausserdem noch den Ausdruck auf der
linken Seite als self übergeben.
Mit den Typangaben kann man, in Verbindung mit Multi Dispatch,
Verfeinerungen bestehender Operatoren definieren. Wenn man zum Beispiel eine
eigene Subklasse von Str schreibt, kann man sich die
String-Konkatenation für Objekte dieser Klasse überschreiben:
class MyStr is Str { multi sub *infix:<~>(MyStr $a, Str $b) { ... } multi sub *infix:<~>(Str $a, MyStr $b) { ... } # oder als eine Subroutine mit zwei möglichen # Signaturen geschrieben: multi sub *infix:<~>(MyStr $a, Str $b)|(Str $a, MyStr $b) { ... } }
Scoping
Per Default sind neue Operatoren nur in dem lexikalischen Scope gültig, in
dem sie definiert wurden. Um sie global zu Verfügung zu stellen, kann man
ihrem Namen vorne ein Stern * anhängen.
Zusammenfassung
Mit den hier vorgestellten Techniken hat man volle Kontrolle über neue Operatoren, und kann alte verändern. Tatsächlich benutzen alle in Perl 6 eingebaute Operatoren auch diese Techniken, um ihr Verhalten zu definieren.
Für viele neue Operatoren sollte es jedoch reichen, die Vorrangebene anzugeben.
Ein Wort der Warnung ist noch angebracht: neue Operatoren sollten spärlich verwendet werden, und sollten wohl durchdacht sein. Auch muss man vorher den bisherigen Sprachumfang von Perl 6 gut kennen, um keine schwer lesbaren oder unerwarteten Effekte durch das versehentliche Recyclen bestehender Operatoren oder syntaktischer Elemente zu erzeugen.
Macros
Während neue Operatoren die Syntax nur ein Stückchen verändern, kann mit Macros deutlich stärker eingreifen (und, wenn man es falsch macht, auch mehr kaputt machen).
Im einfachsten Fall sind Macros Textersetzungen, die während der syntaktischen Analyse des Quellcodes durchgeführt werden.
Wer zum Beispiel die interpolierende Form von qx/.../
(Ausführen eines externen Kommandos und zurückgeben der Ausgabe) von Perl 5
vermisst, und zu faul ist qq:x/.../ stattdessen zu schreiben,
kann sich zwei Buchstaben sparen, indem er ein Macro dafür definiert:
macro qx { 'qq:x ' }
my $result = qx/ls $dir|grep pl\$/;
Das führt erst eine Textersetzung durch, und parst dann das Ergebnis dieser Textersetzung als ganz normalen Perl 6 Quellcode.
is parsed
Macros können die is parsed-Eigenschaft haben, die angibt,
welche Regex oder Funktion den Text erkennt, der von einem Macro gematched
wird.
Auf diese Art kann man zum Beispiel HTML-Kommentare in Perl 6 verfügbar machen:
macro circumfix:« <!-- --> » is parsed rx/ .*? / { '' }
say 2 + 3; <!-- Ergebnis: 5 -->
Hier wird ein Circumfix-Operator definiert, der mit <!--
beginnt und mit --> endet. Alles dazwischen wird von der regex
.*? geparst, d.h. dort wird kein Perl 6-Code erwartet, sondern
nur noch das, was diese Regex matcht.
Das Ergebnis ist in diesem Fall der leere String, da Kommentare keine Funktion im Programmcode übernehmen.