PetaPerl
PetaPerl ist eine Perl 5 Laufzeitumgebung der nächsten Generation, geschrieben in Rust, mit Ziel-Kompatibilität zu Perl 5.42+.
Ziele
- Auto-Parallelisierung - Automatische parallele
map,grep,forSchleifen - JIT-Kompilierung - V8-Klasse Leistung via Cranelift
- Pure Perl Viabilität - Schnell genug, dass XS optional wird
Architektur Überblick
flowchart LR
subgraph Eingabe
A[Perl Quellcode]
end
subgraph Parser
B[Lexer] --> C[Parser]
C --> D[AST]
end
subgraph Compiler
D --> E[Codegen]
E --> F[OpArena]
end
subgraph Laufzeit
F --> G[Interpreter]
G --> H[PP Dispatch]
end
subgraph Ausgabe
H --> I[Ergebnis]
end
A --> B
Schnellstart
# Skript ausführen
pperl script.pl
# Einzeiler ausführen
pperl -e 'print "Hallo, Welt!\n"'
# Nur Syntax prüfen
pperl -c script.pl
Kompatibilität
PetaPerl strebt hohe Kompatibilität mit Perl 5.42+ an. Die Regex-Engine besteht 99,3 % der perl5 re_tests (1959 von 1972 Tests). Siehe Unterschiede zu Perl 5 für bekannte Inkompatibilitäten.
Plattform-Unterstützung
- Nur Linux (jede Architektur)
- Kein Windows, macOS oder andere Plattformen
Erste Schritte
Installation
Binärdateien werden über eine GitHub-Release-Seite verfügbar sein (noch offen). Dort können auch Fehlerberichte über den Issue-Tracker eingereicht werden.
Erstes Skript
#!/usr/bin/env pperl
print "Hello from PetaPerl!\n";
Kommandozeilenoptionen
| Option | Beschreibung |
|---|---|
-e 'code' | Code ausführen (moderne Features immer aktiv) |
-c | Nur Syntax prüfen |
-w | Warnungen aktivieren |
-v | Version anzeigen |
--no-jit | JIT-Kompilierung deaktivieren |
--no-parallel | Auto-Parallelisierung deaktivieren |
--p5 | Eigenständige P5-Laufzeit verwenden (experimentell) |
--cache | Bytecode-Caching aktivieren |
Siehe Skripte ausführen für die vollständige Optionsliste.
Hinweise
- Moderne Features (
say,state,//, Signaturen) sind immer aktiviert — keinuse featurenötig - XS-Module werden nicht unterstützt; für gängige Module gibt es native Rust-Reimplementierungen
-Iwird nicht unterstützt; die UmgebungsvariablePERL5LIBkann zum Hinzufügen von Include-Pfaden verwendet werden
Skripte ausführen
Der Befehl pperl von PetaPerl führt Perl-Skripte mit einer vertrauten Schnittstelle aus. Die meisten perl5-Optionen funktionieren identisch.
Grundlegende Ausführung
# Skript ausführen
pperl script.pl
# Mit Argumenten ausführen
pperl script.pl arg1 arg2
# Einzeiler ausführen
pperl -e 'say "Hello, World!"'
# Nur Syntax prüfen, ohne auszuführen
pperl -c script.pl
Kommandozeilenoptionen
Perl-kompatible Optionen
| Option | Beschreibung | Beispiel |
|---|---|---|
-e 'code' | Einzeiler ausführen (moderne Features immer aktiv) | pperl -e 'say "hi"' |
-c | Nur Syntax prüfen | pperl -c script.pl |
-w | Warnungen aktivieren | pperl -w script.pl |
-v | Version anzeigen | pperl -v |
-V | Ausführliche Konfiguration | pperl -V |
-h, -?, --help | Hilfe anzeigen | pperl --help |
Kombinierte Kurzoptionen:
pperl -wc script.pl # Warnungen aktivieren + Syntaxprüfung
PetaPerl-spezifische Optionen
| Option | Beschreibung | Standard |
|---|---|---|
--stats, -s | Leistungsstatistiken anzeigen (Zeit, Speicher, Ops) | Deaktiviert |
--trace, -t | Ausführungsverfolgung aktivieren | Deaktiviert |
--timeout=SECS | Zeitlimit für Ausführung in Sekunden | Keines |
--p5 | P5-Laufzeit verwenden (siehe unten) | Deaktiviert |
Auswahl der Laufzeitumgebung
PetaPerl verfügt über zwei Laufzeitumgebungen. Die standardmäßige PP-Laufzeit verwendet sicheres, idiomatisches Rust und unterstützt JIT-Kompilierung sowie Auto-Parallelisierung. Die P5-Laufzeit (--p5) ist eine zeilengetreue Rust-Übertragung des C-Interpreters von perl5 — sie erreicht unmittelbar perl5-Leistung, unterstützt aber weder JIT noch Parallelisierung.
--p5 eignet sich, wenn maximale Kompatibilität mit dem Randverhalten von perl5 oder Basisleistung ohne JIT benötigt wird. Die Standard-PP-Laufzeit (mit aktiviertem JIT) eignet sich für rechenintensive Aufgaben, bei denen Parallelisierung und JIT deutliche Geschwindigkeitsvorteile bringen.
Leistungsoptionen
| Option | Beschreibung | Standard |
|---|---|---|
--no-jit | JIT-Kompilierung deaktivieren | JIT aktiviert |
--no-parallel | Auto-Parallelisierung deaktivieren | Parallelisierung aktiv |
--threads=N | Anzahl der Threads für Parallelisierung | Alle CPUs |
--parallel-threshold=N | Minimale Iterationen für Parallelisierung | 100 |
Caching- und Analyseoptionen
| Option | Beschreibung |
|---|---|
--cache | Bytecode-Caching aktivieren (~/.pperl/cache/) |
--flush | Alle Bytecode-Caches leeren und beenden |
--dump-optree | Kanonischen Op-Tree ausgeben (nicht ausführen) |
--from-json, -j | Op-Tree von stdin lesen (B::PetaPerl-JSON-Format) |
--optree, -o | Op-Tree von stdin lesen (B::Concise-Format) |
--compare-bytecode | Op-Trees vergleichen: perl5-Backend vs. nativer Parser |
Nicht unterstützte perl5-Optionen
Folgende perl5-Flags sind nicht implementiert:
| Option | Beschreibung | Umgehung |
|---|---|---|
-I | Include-Pfad hinzufügen | PERL5LIB verwenden |
-E | Mit Features ausführen | -e verwenden (Features immer aktiv) |
-M / -m | Modul laden | use im Code verwenden |
-n / -p | Implizite Eingabeschleife | Explizite Schleife schreiben |
-l | Automatisches Zeilenende | chomp/say verwenden |
-a / -F | Autosplit | split explizit verwenden |
-i | In-Place-Bearbeitung | open/close verwenden |
-d | Debugger | - |
-x | Skript extrahieren | - |
-T | Taint-Modus | - |
Beispiele
Einzeiler
Moderne Features (say, state usw.) sind immer aktiviert.
# Hallo ausgeben
pperl -e 'say "Hello!"'
# Eingabe verarbeiten
echo "hello" | pperl -e 'while (<>) { say uc($_) }'
# Rechnen
pperl -e 'say 2 + 2'
# Daten erzeugen
pperl -e 'say join ",", 1..10'
Syntaxprüfung
# Einzelne Datei prüfen
pperl -c script.pl
# Ausgabe: script.pl syntax OK
# Mit aktivierten Warnungen prüfen
pperl -wc script.pl
Skript-Argumente
Argumente nach dem Skriptnamen befüllen @ARGV.
pperl process.pl file1.txt file2.txt
# process.pl
for my $file (@ARGV) {
say "Processing: $file";
}
Leistungsstatistiken
pperl --stats script.pl
Ausgabe:
--- Performance Statistics ---
Wall-clock time: 0.042 seconds
Peak memory (RSS): 12.34 MB (12640 KB)
Ops executed: 1523 (36261 ops/sec)
------------------------------
Ausführungsverfolgung
Jede ausgeführte Op wird angezeigt (Hilfsmittel zur Fehlersuche).
pperl --trace script.pl
Ausgabe:
[TRACE] NextState
[TRACE] Const("Hello")
[TRACE] Print
Parser-Vergleich
Ausgabe des nativen Rust-Parsers mit einem von perl5 erzeugten JSON-Op-Tree vergleichen (Analysewerkzeug).
pperl --compare-bytecode script.pl
Zeigt Unterschiede in den erzeugten Op-Trees. Nützlich zur Parser-Validierung.
Op-Tree-Inspektion
Kanonische Op-Tree-Darstellung ausgeben, ohne auszuführen.
pperl --dump-optree script.pl
Zeigt die interne Op-Tree-Struktur:
NextState(file="script.pl", line=1)
→ Const(sv="Hello")
→ Print
→ LeaveSub
Ausführungsmodell
Parser-Pipeline
Quellcode → Lexer → Parser → AST → Codegen → OpArena → Interpreter
Schnell, reines Rust. Keine perl5-Abhängigkeit zur Laufzeit.
JSON-Op-Tree-Laden (nur Analyse)
Quellcode → perl + B::PetaPerl → JSON → OpArena → Interpreter
Für Analyse und Parser-Validierung kann pperl einen von perl5 erzeugten Op-Tree über --from-json laden oder ihn über --compare-bytecode mit dem nativen Parser vergleichen. Dies wird bei normaler Ausführung nicht verwendet.
Zeitlimits
Standardmäßig hat pperl kein Zeitlimit. Mit --timeout=SECS kann eines gesetzt werden:
# Kein Zeitlimit (Standard)
pperl script.pl
# Zeitlimit setzen (nützlich für nicht vertrauenswürdigen Code oder Tests)
pperl --timeout=60 script.pl
pperl --timeout=5 script.pl
Das Test-Harness setzt ein eigenes Zeitlimit (standardmäßig 5 Sekunden pro Test). Dieses ist unabhängig vom --timeout-Flag.
Exit-Codes
| Code | Bedeutung |
|---|---|
| 0 | Erfolg |
| 1 | Laufzeitfehler oder Kompilierfehler |
| 2 | Fehler bei Kommandozeilenverwendung |
Umgebungsvariablen
| Variable | Wirkung | Standard |
|---|---|---|
PPERL_MAX_RECURSION | Maximale Rekursionstiefe für Subroutinen | 1000 |
PPERL_CACHE | Verzeichnis für Bytecode-Cache | - |
PERL5LIB | Modul-Suchpfad | - |
Beispiel:
export PPERL_MAX_RECURSION=5000
pperl deeply_recursive.pl
export PPERL_CACHE=/tmp/pperl-cache
pperl --cache script.pl
Warnungen
Warnungen mit -w oder -W aktivieren:
pperl -w script.pl
Warnungen erscheinen auf stderr. Derzeit implementiert:
- Verwendung eines nicht initialisierten Werts
- Aufruf einer undefinierten Subroutine
Standardströme
# stdin lesen
while (my $line = <STDIN>) {
print "Got: $line";
}
# stdout schreiben
print "Output\n";
say "Output with newline";
# stderr schreiben
warn "Warning message\n";
Umleitung funktioniert wie gewohnt:
pperl script.pl < input.txt > output.txt 2> errors.txt
Shebang-Unterstützung
PetaPerl-Skripte unterstützen Shebang für direkte Ausführung.
#!/usr/bin/env pperl
say "Hello from PetaPerl!";
Ausführbar machen und starten:
chmod +x script.pl
./script.pl
Modulladen
use strict;
use warnings;
use Data::Dumper;
my $data = { foo => 42 };
print Dumper($data);
Modul-Suchpfad: identisch mit perl5s @INC. Mit PERL5LIB können Verzeichnisse hinzugefügt werden.
Aktuelle Einschränkungen:
- XS-Module nicht unterstützt (nur native Module)
- Einige Pragmas (strict, warnings) werden geparst, aber nicht vollständig durchgesetzt
- Pure-Perl-Module funktionieren, sofern sie keine nicht unterstützten Features verwenden
Unterschiede zu perl5
Immer aktivierte Features
Moderne Perl-Features sind immer verfügbar (kein use feature nötig):
say- Ausgabe mit Zeilenumbruchstate- persistente lexikalische Variablen//- Defined-Or-Operator~~- Smart Match (teilweise)
Standard-Zeitlimit
pperl hat kein Standard-Zeitlimit (wie perl5). Mit --timeout=SECS kann für nicht vertrauenswürdige Skripte eines gesetzt werden.
Parser
pperl verwendet immer den nativen Rust-Parser. Es gibt keinen Laufzeit-Fallback auf perl5.
Fehlersuche
Syntaxfehler
pperl -c script.pl
Meldet Parse-Fehler mit Zeilennummern:
Parse error: Expected ';' after statement
at script.pl line 42
Laufzeitfehler
Fehler zeigen einen Stacktrace:
Runtime error: Undefined subroutine &main::foo
called at script.pl line 10
Ausführungsverfolgung
pperl --trace script.pl
Zeigt jede ausgeführte Op. Sehr ausführlich. Nützlich zum Verständnis des Ausführungsablaufs.
Leistungstipps
Benchmarking
time pperl script.pl
Oder --stats für detaillierte Metriken:
pperl --stats script.pl
Leistungscharakteristiken
Die Leistung von PetaPerl variiert je nach Arbeitslast:
- Interpreter-Fast-Paths: Häufige Operationen (Ganzzahlarithmetik, Zuweisung, Vergleich) laufen mit oder nahe perl5-Geschwindigkeit
- JIT-kompilierte Schleifen: Arithmetik-intensive Schleifen werden über Cranelift zu nativem Code kompiliert und erreichen bis zu 76-fache perl5-Geschwindigkeit
- JIT + Parallelisierung: Trivial parallelisierbare Aufgaben auf Mehrkern-Systemen erreichen bis zu 431-fache perl5-Geschwindigkeit (Mandelbrot 1000x1000)
- String-intensiver Code: Vergleichbar mit perl5 dank PvBuf-Optimierungen
- Modulladen: Ähnlich wie perl5 für Pure-Perl-Module
Mit --no-jit und --no-parallel kann die reine Interpreter-Leistung isoliert werden.
Übliche Arbeitsabläufe
Skript-Entwicklung
# Skript bearbeiten
vim script.pl
# Syntax prüfen
pperl -c script.pl
# Mit Warnungen ausführen
pperl -w script.pl
# Bei Bedarf mit Tracing debuggen
pperl --trace script.pl
Testen
# Syntax aller Skripte prüfen
find . -name '*.pl' -exec pperl -c {} \;
# Testsuite ausführen
pperl t/test.pl
Produktiv-Einsatz
# Produktionsskript ausführen (kein Zeitlimit standardmäßig)
pperl production.pl
# Leistung überwachen
pperl --stats production.pl
Fortgeschrittene Verwendung
Op-Tree aus JSON laden (Analyse)
Für Parser-Validierung und Analyse:
# JSON-Format (B::PetaPerl) — liest von stdin
perl -MO=PetaPerl script.pl | pperl --from-json
# Kurzform
perl -MO=PetaPerl script.pl | pperl -j
Dies lädt einen von perl5 erzeugten Op-Tree und führt ihn in der PetaPerl-Laufzeit aus, wobei der native Parser umgangen wird.
Parser vergleichen
Ausgabe des nativen Parsers mit einem von perl5 erzeugten Op-Tree vergleichen:
pperl --compare-bytecode script.pl
Zeigt strukturelle Unterschiede. Exit-Code 0 = äquivalent, 1 = verschieden.
Fehlerbehebung
“Can’t open perl script”
Datei existiert nicht oder falscher Pfad.
pperl script.pl # Muss existieren
“Undefined subroutine”
Subroutine nicht definiert oder im aktuellen Gültigkeitsbereich nicht sichtbar.
sub greet { say "hi" }
greet(); # OK
greeet(); # Fehler: Undefined subroutine
“Timeout expired”
Das Skript lief länger als der --timeout-Wert.
pperl --timeout=120 long_script.pl # Zeitlimit erhöhen
pperl long_script.pl # Kein Zeitlimit (Standard)
“Parse error”
Syntaxfehler im Skript. Mit -c prüfen:
pperl -c script.pl
“Is perl installed and in PATH?”
Die Analysewerkzeuge --from-json/-j und --compare-bytecode erfordern ein installiertes perl 5.42+ mit B::PetaPerl. Normale Ausführung benötigt kein perl.
Versionsinformationen
pperl -v
Zeigt Kurzversion und Copyright.
pperl -V
Zeigt ausführliche Konfiguration:
Summary of PetaPerl configuration:
PetaPerl:
version='0.1.0'
binary='pperl'
Runtime:
backend=Rust interpreter + function-pointer dispatch
jit=Cranelift (for/while loops)
parallelism=Rayon (auto-parallel map/grep/for/while)
Perl compatibility:
parser=native Rust
perl_required=no (5.42+ optional for --from-json/--compare-bytecode analysis)
platforms=Linux only
Plattform-Unterstützung
PetaPerl läuft ausschließlich unter Linux (jede Architektur).
Nicht unterstützt: Windows, macOS, BSD, VMS usw.
Diese Vereinfachung ermöglicht aggressive Optimierung für Linux-spezifische Funktionen.
Geplante Erweiterungen
Geplante Features:
- Profiling-Ausgabe (
--profile) - Erweiterte JIT-Abdeckung (String-Operationen, Subroutine-Inlining)
- Erweiterte Parallelisierung (weitere Schleifenmuster, Pipeline-Parallelismus)
Fortschritt verfolgen: https://gl.petatech.eu/petatech/peta-perl
perlvar - Vordefinierte Variablen in PetaPerl
BESCHREIBUNG
Dieses Dokument beschreibt die speziellen Variablen, die von PetaPerl unterstuetzt werden. Die vollstaendige Perl-5-Dokumentation findet sich unter perldoc perlvar.
SPEZIELLE VARIABLEN
Allgemeine Variablen
$_ - Die Standardvariable
Der voreingestellte Eingabe- und Mustersuchraum. Viele Funktionen operieren auf $_, wenn kein Argument angegeben wird.
for (1, 2, 3) {
print; # gibt $_ implizit aus
}
$_ = "hello";
print length; # gibt 5 aus
Implementierung: src/runtime/pp/globals.rs:95
@_ - Unterprogramm-Argumente
Innerhalb eines Unterprogramms enthaelt @_ die an dieses Unterprogramm uebergebenen Argumente.
Das Aendern von Elementen in @_ aendert die Variablen des Aufrufers (Aliasing).
sub double {
$_[0] *= 2; # aendert die Variable des Aufrufers
}
my $x = 5;
double($x);
print $x; # gibt 10 aus
Implementierung: src/runtime/pp/sub.rs
Globale Spezialvariablen
$0 - Programmname
Enthaelt den Namen des ausgefuehrten Programms.
print "Ausfuehrung: $0\n";
$$ - Prozess-ID
Die Prozess-ID des Perl-Prozesses, der dieses Skript ausfuehrt.
print "PID: $$\n";
$? - Fehlerstatus des Kindprozesses
Der Rueckgabestatus des letzten pipe-close-, Backtick-Befehls oder system()-Aufrufs.
system("false");
print "Exit-Status: ", $? >> 8, "\n";
$@ - Eval-Fehler
Die Fehlermeldung des letzten fehlgeschlagenen eval.
eval { die "oops" };
print "Fehler: $@" if $@;
$! - Betriebssystemfehler
Im numerischen Kontext liefert diese Variable den aktuellen Wert von errno.
Im Zeichenkettenkontext liefert sie die zugehoerige Fehlermeldung.
open(my $fh, '<', '/nonexistent') or print "Fehler: $!\n";
E/A-Variablen
$/ - Eingabe-Datensatztrennzeichen
Das Eingabe-Datensatztrennzeichen, standardmaessig ein Zeilenumbruch. Wird es auf undef gesetzt, wird der Slurp-Modus aktiviert.
{
local $/; # Slurp-Modus
my $content = <$fh>;
}
{
local $/ = ""; # Absatzmodus
while (<$fh>) { ... }
}
$\ - Ausgabe-Datensatztrennzeichen
Wird am Ende jedes print-Aufrufs angehaengt. Standardwert ist undef (nichts wird angehaengt).
{
local $\ = "\n";
print "line1"; # gibt "line1\n" aus
print "line2"; # gibt "line2\n" aus
}
$, - Ausgabe-Feldtrennzeichen
Wird zwischen den Argumenten von print ausgegeben. Standardwert ist undef.
{
local $, = ", ";
print "a", "b", "c"; # gibt "a, b, c" aus
}
$" - Listentrennzeichen
Wird verwendet, wenn Arrays in Zeichenketten interpoliert werden. Standardwert ist ein Leerzeichen.
my @arr = (1, 2, 3);
print "@arr\n"; # gibt "1 2 3" aus
{
local $" = ",";
print "@arr\n"; # gibt "1,2,3" aus
}
$| - Automatisches Leeren der Ausgabe
Wenn auf einen Wert ungleich Null gesetzt, wird nach jedem Schreibvorgang auf dem aktuell ausgewaehlten Ausgabekanal ein Flush erzwungen. Standardwert ist 0.
$| = 1; # Autoflush auf STDOUT aktivieren
$. - Aktuelle Zeilennummer
Die aktuelle Zeilennummer des zuletzt gelesenen Dateihandles.
while (<$fh>) {
print "$.: $_"; # gibt Zeilennummer und Inhalt aus
}
$; - Index-Trennzeichen
Das Index-Trennzeichen fuer die Emulation mehrdimensionaler Hashes. Standardwert ist "\034" (SUBSEP).
$hash{$x, $y} = $value; # aequivalent zu $hash{"$x\034$y"}
$^T - Startzeit
Der Zeitpunkt, zu dem das Programm gestartet wurde, in Sekunden seit der Epoche.
print "Gestartet um: ", scalar localtime($^T), "\n";
$^W - Warnungs-Flag
Der aktuelle Wert des Warnungsschalters. Wird durch -w gesetzt.
$^W = 1; # Warnungen aktivieren
Variablen fuer regulaere Ausdruecke
$& - Trefferzeichenkette
Die Zeichenkette, die vom letzten erfolgreichen Musterabgleich gefunden wurde.
"hello world" =~ /wo\w+/;
print $&; # gibt "world" aus
$` - Zeichenkette vor dem Treffer
Die Zeichenkette vor dem, was vom letzten erfolgreichen Musterabgleich gefunden wurde.
"hello world" =~ /wo\w+/;
print $`; # gibt "hello " aus
$' - Zeichenkette nach dem Treffer
Die Zeichenkette nach dem, was vom letzten erfolgreichen Musterabgleich gefunden wurde.
"hello world!" =~ /wo\w+/;
print $'; # gibt "!" aus
$1, $2, … - Erfassungsgruppen
Enthalten den Text, der von den Erfassungsgruppen des letzten Musterabgleichs gefunden wurde.
"hello world" =~ /(\w+) (\w+)/;
print "$1, $2\n"; # gibt "hello, world" aus
%+ - Benannte Erfassungen
Hash mit dem Text, der von benannten Erfassungsgruppen gefunden wurde.
"hello world" =~ /(?<first>\w+) (?<second>\w+)/;
print "$+{first}, $+{second}\n"; # gibt "hello, world" aus
Prozessvariablen
$< - Reale UID
Die reale Benutzer-ID dieses Prozesses.
print "Reale UID: $<\n";
$> - Effektive UID
Die effektive Benutzer-ID dieses Prozesses.
print "Effektive UID: $>\n";
$( - Reale GID
Die reale Gruppen-ID dieses Prozesses.
print "Reale GID: $(\n";
$) - Effektive GID
Die effektive Gruppen-ID dieses Prozesses, gefolgt von ergaenzenden Gruppen-IDs.
print "Effektive GID: $)\n";
Systemvariablen
%ENV - Umgebungsvariablen
Hash mit den aktuellen Umgebungsvariablen.
print $ENV{HOME};
$ENV{MY_VAR} = "value";
@ARGV - Kommandozeilenargumente
Enthaelt die an das Skript uebergebenen Kommandozeilenargumente.
# script.pl arg1 arg2
print "Argumente: @ARGV\n"; # gibt "Argumente: arg1 arg2" aus
@INC - Suchpfad fuer Module
Liste der Verzeichnisse, die bei use- und require-Anweisungen durchsucht werden.
push @INC, '/my/lib/path';
%SIG - Signal-Handler
Hash zum Setzen von Signal-Handlern.
$SIG{INT} = sub { die "SIGINT abgefangen" };
Versionsvariablen
$] - Perl-Version (numerisch)
Die Versionsnummer des Perl-Interpreters als Dezimalzahl.
print $]; # z.B. 5.042000
$^V - Perl-Version (Zeichenkette)
Die Perl-Version als Versionszeichenkette.
print $^V; # z.B. v5.42.0
$^O - Betriebssystem
Der Name des Betriebssystems.
print $^O; # "linux" fuer PetaPerl
$^X - Pfad zur Perl-Programmdatei
Der Pfad zur ausfuehrbaren Perl- (oder PetaPerl-)Datei.
print $^X; # z.B. /usr/local/bin/pperl
PetaPerl-spezifische Variablen
Reserviert fuer zukuenftige PetaPerl-spezifische Erweiterungen.
SIEHE AUCH
Eingebaute Funktionen
PetaPerl implementiert die eingebaute Funktionsbibliothek von Perl 5. Dieses Dokument listet alle implementierten Funktionen auf, nach Kategorien geordnet.
Legende:
- ✅ Vollständig implementiert
- ⚠️ Teilweise implementiert
- ❌ Noch nicht implementiert
E/A-Funktionen
Ausgabeoperationen
| Funktion | Status | Beschreibung |
|---|---|---|
print | ✅ | Ausgabe auf Dateihandle oder STDOUT |
say | ✅ | Ausgabe mit Zeilenumbruch |
printf | ✅ | Formatierte Ausgabe |
sprintf | ✅ | Formatierte Zeichenkette |
print "Hello\n";
print $fh "data\n";
say "Hello"; # Fuegt Zeilenumbruch hinzu
printf "%d Elemente\n", $count;
my $str = sprintf "%05d", $num;
Dateioperationen
| Funktion | Status | Beschreibung |
|---|---|---|
open | ✅ | Datei oder Pipe oeffnen |
close | ✅ | Dateihandle schliessen |
read | ✅ | Daten fester Laenge lesen |
sysread | ✅ | Lesen auf Systemebene |
syswrite | ✅ | Schreiben auf Systemebene |
readline | ✅ | Zeile(n) vom Dateihandle lesen |
getc | ✅ | Einzelnes Zeichen lesen |
eof | ✅ | Auf Dateiende pruefen |
seek | ✅ | Dateihandle positionieren |
tell | ✅ | Position des Dateihandles abfragen |
fileno | ✅ | Dateideskriptornummer abfragen |
binmode | ✅ | Binaermodus setzen |
select | ✅ | Standard-Dateihandle setzen |
write | ✅ | Formatierten Datensatz schreiben |
open my $fh, '<', $filename or die $!;
my $data;
read $fh, $data, 1024; # 1024 Bytes lesen
my $line = readline($fh); # oder <$fh>
my $char = getc($fh);
seek $fh, 0, 0; # Zum Anfang zurueckspulen
my $pos = tell $fh;
close $fh;
Verzeichnisoperationen
| Funktion | Status | Beschreibung |
|---|---|---|
opendir | ✅ | Verzeichnishandle oeffnen |
readdir | ✅ | Verzeichniseintrag lesen |
closedir | ✅ | Verzeichnishandle schliessen |
rewinddir | ✅ | Verzeichnishandle zuruecksetzen |
seekdir | ✅ | Verzeichnishandle positionieren |
telldir | ✅ | Verzeichnisposition abfragen |
mkdir | ✅ | Verzeichnis erstellen |
rmdir | ✅ | Verzeichnis entfernen |
chdir | ✅ | Arbeitsverzeichnis wechseln |
opendir my $dh, $dir or die $!;
my @entries = readdir $dh;
closedir $dh;
mkdir "newdir", 0755 or die $!;
rmdir "olddir";
chdir "/tmp";
Dateisystemoperationen
| Funktion | Status | Beschreibung |
|---|---|---|
unlink | ✅ | Dateien loeschen |
rename | ✅ | Datei umbenennen |
link | ✅ | Harten Link erstellen |
symlink | ✅ | Symbolischen Link erstellen |
readlink | ✅ | Symbolischen Link lesen |
chmod | ✅ | Dateiberechtigungen aendern |
chown | ✅ | Dateibesitzer/-gruppe aendern |
utime | ✅ | Zugriffs-/Aenderungszeiten aendern |
truncate | ✅ | Datei auf Laenge kuerzen |
stat | ✅ | Dateistatus abfragen |
lstat | ✅ | Dateistatus abfragen (ohne Link-Verfolgung) |
glob | ✅ | Dateinamenerweiterung |
unlink "file.txt" or die $!;
rename "old.txt", "new.txt";
link "source", "link";
symlink "target", "symlink";
my $target = readlink "symlink";
chmod 0644, @files;
chown $uid, $gid, @files;
utime $atime, $mtime, @files;
truncate $fh, $length;
my @info = stat $file;
my @info = lstat $symlink; # Folgt keinem Symlink
my @files = glob "*.txt";
Zeichenkettenfunktionen
| Funktion | Status | Beschreibung |
|---|---|---|
length | ✅ | Zeichenketten-/Array-Laenge |
substr | ✅ | Teilzeichenkette extrahieren/ersetzen |
index | ✅ | Position einer Teilzeichenkette finden |
rindex | ✅ | Teilzeichenkette finden (rueckwaerts) |
chr | ✅ | Zahl in Zeichen umwandeln |
ord | ✅ | Zeichen in Zahl umwandeln |
lc | ✅ | Zeichenkette in Kleinbuchstaben |
uc | ✅ | Zeichenkette in Grossbuchstaben |
lcfirst | ✅ | Erstes Zeichen in Kleinbuchstaben |
ucfirst | ✅ | Erstes Zeichen in Grossbuchstaben |
quotemeta | ✅ | Metazeichen quotieren |
chop | ✅ | Letztes Zeichen entfernen |
chomp | ✅ | Zeilenende entfernen |
hex | ✅ | Hexadezimale Zeichenkette in Zahl |
oct | ✅ | Oktale Zeichenkette in Zahl |
pack | ✅ | Werte in Binaerzeichenkette packen |
unpack | ✅ | Binaerzeichenkette entpacken |
vec | ✅ | Bitvektorzugriff |
my $len = length $str;
my $sub = substr $str, 0, 5; # Erste 5 Zeichen
substr($str, 7, 5) = "replace"; # Lvalue-substr
my $pos = index $str, "find";
my $rpos = rindex $str, "last";
my $char = chr(65); # "A"
my $code = ord("A"); # 65
my $lower = lc "HELLO"; # "hello"
my $upper = uc "hello"; # "HELLO"
my $cap = ucfirst "hello"; # "Hello"
chomp $line; # Entfernt \n falls vorhanden
chop $str; # Entfernt letztes Zeichen
my $quoted = quotemeta '.*'; # '\\.\\\*'
my $num = hex "FF"; # 255
my $num = oct "377"; # 255
Lvalue-substr: substr kann als Zuweisungsziel verwendet werden.
substr($str, 0, 3) = "New"; # Ersetzt die ersten 3 Zeichen
Array-Funktionen
| Funktion | Status | Beschreibung |
|---|---|---|
push | ✅ | An Array anhaengen |
pop | ✅ | Letztes Element entfernen |
shift | ✅ | Erstes Element entfernen |
unshift | ✅ | Vor Array stellen |
splice | ✅ | Array-Elemente entfernen/ersetzen |
join | ✅ | Array mit Trennzeichen verbinden |
split | ✅ | Zeichenkette in Array aufteilen |
reverse | ✅ | Liste/Zeichenkette umkehren |
sort | ✅ | Liste sortieren |
grep | ✅ | Liste filtern |
map | ✅ | Liste transformieren |
push @arr, $val;
push @arr, @more;
my $last = pop @arr;
my $first = shift @arr;
unshift @arr, $val;
splice @arr, $offset, $length, @replacement;
my $str = join ",", @arr;
my @parts = split /,/, $str;
my @reversed = reverse @arr;
my @sorted = sort @arr;
my @sorted = sort { $a <=> $b } @numbers;
my @filtered = grep { $_ > 10 } @numbers;
my @doubled = map { $_ * 2 } @numbers;
Parallele Ausfuehrung: map, grep und einfache for-Schleifen koennen bei Unbedenklichkeit parallel ausgefuehrt werden.
Hash-Funktionen
| Funktion | Status | Beschreibung |
|---|---|---|
keys | ✅ | Hash-Schluessel abfragen |
values | ✅ | Hash-Werte abfragen |
each | ✅ | Schluessel-Wert-Paare iterieren |
exists | ✅ | Existenz eines Schluessels pruefen |
delete | ✅ | Hash-Schluessel entfernen |
my @keys = keys %hash;
my @vals = values %hash;
while (my ($k, $v) = each %hash) { ... }
if (exists $hash{key}) { ... }
my $removed = delete $hash{key};
Kontextsensitivitaet: keys und values geben im Listenkontext eine Liste zurueck, im skalaren Kontext die Anzahl.
my $count = keys %hash; # Anzahl der Schluessel
my @all_keys = keys %hash; # Alle Schluessel
Mathematische Funktionen
| Funktion | Status | Beschreibung |
|---|---|---|
abs | ✅ | Absolutwert |
int | ✅ | Ganzzahlige Abschneidung |
sqrt | ✅ | Quadratwurzel |
sin | ✅ | Sinus |
cos | ✅ | Kosinus |
exp | ✅ | e hoch Potenz |
log | ✅ | Natuerlicher Logarithmus |
atan2 | ✅ | Arkustangens von y/x |
rand | ✅ | Zufallszahl |
srand | ✅ | Zufallsgenerator initialisieren |
my $abs = abs -5; # 5
my $int = int 3.7; # 3
my $sqrt = sqrt 16; # 4
my $sin = sin $angle;
my $cos = cos $angle;
my $exp = exp 1; # e
my $log = log 10;
my $atan = atan2 $y, $x;
my $rand = rand 10; # 0 bis <10
srand 42; # Mit bestimmtem Wert initialisieren
Typ- und Referenzfunktionen
| Funktion | Status | Beschreibung |
|---|---|---|
ref | ✅ | Referenztyp abfragen |
bless | ✅ | Referenz in Paket segnen |
defined | ✅ | Auf Definiertheit pruefen |
undef | ✅ | Variable undefinieren |
scalar | ✅ | Skalaren Kontext erzwingen |
my $type = ref $value; # 'ARRAY', 'HASH', 'CODE' usw.
my $obj = bless {}, 'MyClass';
my $obj = bless $ref, 'MyClass';
if (defined $var) { ... }
undef $var; # Auf undef setzen
my $count = scalar @arr; # Skalaren Kontext erzwingen
Steuerung und Introspektion
| Funktion | Status | Beschreibung |
|---|---|---|
caller | ✅ | Aufruferinformation abfragen |
wantarray | ✅ | Aufrufkontext abfragen |
prototype | ✅ | Unterprogramm-Prototyp abfragen |
pos | ✅ | Regex-Position abfragen/setzen |
study | ✅ | Regex-Abgleich optimieren |
reset | ✅ | Variablen zuruecksetzen |
my ($package, $file, $line) = caller;
my ($pkg, $file, $line, $sub) = caller(1);
if (wantarray) {
return @list;
} else {
return $scalar;
}
my $proto = prototype \&mysub;
my $pos = pos $str;
study $str; # Fuer wiederholten Regex-Abgleich optimieren
reset; # ??-Suchen zuruecksetzen
Prozess und System
| Funktion | Status | Beschreibung |
|---|---|---|
die | ✅ | Ausnahme ausloesen |
warn | ✅ | Warnung ausgeben |
exit | ✅ | Programm beenden |
system | ✅ | Externen Befehl ausfuehren |
exec | ✅ | Ausfuehren und Prozess ersetzen |
fork | ✅ | Kindprozess erzeugen |
wait | ✅ | Auf Kindprozess warten |
waitpid | ✅ | Auf bestimmten Kindprozess warten |
sleep | ✅ | Fuer Sekunden pausieren |
time | ✅ | Aktuelle Unix-Zeit abfragen |
times | ✅ | Prozesszeiten abfragen |
localtime | ✅ | Zeit in Ortszeit umwandeln |
gmtime | ✅ | Zeit in GMT umwandeln |
getlogin | ✅ | Anmeldenamen abfragen |
die "Fehler: $!" if $error;
warn "Warnmeldung";
exit 0;
my $status = system "ls", "-l";
exec "command", @args; # Kehrt bei Erfolg nicht zurueck
my $pid = fork;
if ($pid == 0) {
# Kindprozess
} else {
# Elternprozess
}
wait; # Auf beliebigen Kindprozess warten
waitpid $pid, 0; # Auf bestimmten Kindprozess warten
sleep 5; # 5 Sekunden pausieren
my $now = time;
my @times = times; # (user, system, cuser, csystem)
my @lt = localtime time;
my @gmt = gmtime time;
my $user = getlogin;
Benutzer- und Gruppendatenbank
Alle Benutzer-/Gruppendatenbankfunktionen sind implementiert:
| Funktion | Status | Beschreibung |
|---|---|---|
getpwnam | ✅ | Passworteintrag nach Name abfragen |
getpwuid | ✅ | Passworteintrag nach UID abfragen |
getpwent | ✅ | Naechsten Passworteintrag abfragen |
setpwent | ✅ | Passwort-Iteration zuruecksetzen |
endpwent | ✅ | Passwort-Iteration beenden |
getgrnam | ✅ | Gruppeneintrag nach Name abfragen |
getgrgid | ✅ | Gruppeneintrag nach GID abfragen |
getgrent | ✅ | Naechsten Gruppeneintrag abfragen |
setgrent | ✅ | Gruppen-Iteration zuruecksetzen |
endgrent | ✅ | Gruppen-Iteration beenden |
my @pwinfo = getpwnam "username";
my @pwinfo = getpwuid $uid;
while (my @entry = getpwent) { ... }
endpwent;
my @grinfo = getgrnam "groupname";
my @grinfo = getgrgid $gid;
Netzwerkdatenbank
Alle Netzwerkdatenbankfunktionen sind implementiert:
| Funktion | Status | Beschreibung |
|---|---|---|
gethostbyname | ✅ | Host nach Name abfragen |
gethostbyaddr | ✅ | Host nach Adresse abfragen |
gethostent | ✅ | Naechsten Hosteintrag abfragen |
sethostent | ✅ | Host-Iteration zuruecksetzen |
endhostent | ✅ | Host-Iteration beenden |
getnetbyname | ✅ | Netzwerk nach Name abfragen |
getnetbyaddr | ✅ | Netzwerk nach Adresse abfragen |
getnetent | ✅ | Naechsten Netzwerkeintrag abfragen |
setnetent | ✅ | Netzwerk-Iteration zuruecksetzen |
endnetent | ✅ | Netzwerk-Iteration beenden |
getprotobyname | ✅ | Protokoll nach Name abfragen |
getprotobynumber | ✅ | Protokoll nach Nummer abfragen |
getprotoent | ✅ | Naechsten Protokolleintrag abfragen |
setprotoent | ✅ | Protokoll-Iteration zuruecksetzen |
endprotoent | ✅ | Protokoll-Iteration beenden |
getservbyname | ✅ | Dienst nach Name abfragen |
getservbyport | ✅ | Dienst nach Port abfragen |
getservent | ✅ | Naechsten Diensteintrag abfragen |
setservent | ✅ | Dienst-Iteration zuruecksetzen |
endservent | ✅ | Dienst-Iteration beenden |
my @host = gethostbyname "example.com";
my @net = getnetbyname "loopback";
my @proto = getprotobyname "tcp";
my @serv = getservbyname "http", "tcp";
Tie-Mechanismus
| Funktion | Status | Beschreibung |
|---|---|---|
tie | ⚠️ | Variable an Objekt binden (Stub – gibt undef zurueck) |
tied | ⚠️ | Gebundenes Objekt abfragen (Stub – gibt undef zurueck) |
untie | ⚠️ | Variable entbinden (Stub – keine Wirkung) |
Die Funktionen werden erkannt und sind aufrufbar, leiten aber nicht an Tie-Klassenmethoden weiter. Module, die auf tie angewiesen sind (z.B. Tie::File), funktionieren nicht korrekt.
Formate (veraltet)
| Funktion | Status | Beschreibung |
|---|---|---|
formline | ⚠️ | Zeile formatieren (Stub) |
write | ⚠️ | Formatierte Ausgabe schreiben (Stub) |
Perl-Formate sind ein veraltetes Feature. Die Funktionen existieren, aber das Formatausgabesystem ist nicht implementiert. Verwende stattdessen printf/sprintf.
Socket-Funktionen
Alle Socket-Operationen sind implementiert:
| Funktion | Status | Beschreibung |
|---|---|---|
socket | ✅ | Socket erstellen |
socketpair | ✅ | Socket-Paar erstellen |
bind | ✅ | Socket an Adresse binden |
listen | ✅ | Auf Socket lauschen |
accept | ✅ | Verbindung annehmen |
connect | ✅ | Mit Gegenstelle verbinden |
shutdown | ✅ | Socket herunterfahren |
send | ✅ | Daten senden |
recv | ✅ | Daten empfangen |
getsockopt | ✅ | Socket-Option abfragen |
setsockopt | ✅ | Socket-Option setzen |
getsockname | ✅ | Lokale Adresse abfragen |
getpeername | ✅ | Entfernte Adresse abfragen |
IPC-Funktionen
| Funktion | Status | Beschreibung |
|---|---|---|
pipe | ✅ | Pipe erstellen |
msgctl | ✅ | Nachrichtenwarteschlangensteuerung |
msgget | ✅ | Nachrichtenwarteschlange abfragen |
msgsnd | ✅ | Nachricht senden |
msgrcv | ✅ | Nachricht empfangen |
semctl | ✅ | Semaphorsteuerung |
semget | ✅ | Semaphorsatz abfragen |
semop | ✅ | Semaphoroperationen |
shmctl | ✅ | Gemeinsamer-Speicher-Steuerung |
shmget | ✅ | Gemeinsamen Speicher abfragen |
shmread | ✅ | Gemeinsamen Speicher lesen |
shmwrite | ✅ | Gemeinsamen Speicher schreiben |
Bekannte Einschraenkungen
Regulaere Ausdruecke
qr//ist vollstaendig implementiert einschliesslich Wiederverwendung vorkompilierter Muster(?{code})eingebettete Codeausfuehrung ist implementiert- Possessive Quantifizierer (
*+,++,?+) sind implementiert - Siehe Regulaere Ausdruecke fuer verbleibende Randfaelle (selbstreferenzierende Erfassungen, Mehrzeichenfaltung bei Gross-/Kleinschreibung)
Modulsystem
useundrequirefunktionieren fuer Pure-Perl-Module- XS-Module werden nicht unterstuetzt (native Neuimplementierungen fuer gaengige Module vorhanden)
- Einige Pragma-Auswirkungen koennen von perl5 abweichen
Spezielle Variablen
- Die meisten speziellen Variablen werden befuellt (
$^O,$^X,$],$^Vusw.) Internals::SvREADONLYist nicht implementiert
PetaPerl-spezifische Hinweise
Konstantenfaltung
PetaPerl fuehrt Konstantenfaltung zur Uebersetzungszeit durch fuer:
length("hello") # Wird zur Uebersetzungszeit zu 5 gefaltet
substr("text", 0, 2) # Wird zu "te" gefaltet
Parallele Ausfuehrung
Diese Funktionen koennen bei Unbedenklichkeit parallel ausgefuehrt werden:
map- Parallele Elementtransformationgrep- Parallele Filterungsortmit reiner Vergleichsfunktion
Parallelisierung erfordert:
- Keine Seiteneffekte im Callback
- Keinen gemeinsam veraenderbaren Zustand
- Array-Groesse ueber der Parallelisierungsschwelle
Leistung
- Zeichenkettenfunktionen verwenden UTF-8-bewusste Operationen
- Array-Operationen minimieren Kopien
- Hash-Operationen verwenden optimierte Hashtabellen
- Mathematische Funktionen werden wenn moeglich auf CPU-Befehle abgebildet
Operatoren
PetaPerl implementiert den vollstaendigen Satz von Perl-5-Operatoren mit identischer Semantik. Alle Operatoren wahren Perls Kontextsensitivitaet und Vorrangregeln.
Binaere Operatoren
Arithmetik
| Operator | Operation | Beispiel |
|---|---|---|
+ | Addition | $a + $b |
- | Subtraktion | $a - $b |
* | Multiplikation | $a * $b |
/ | Division | $a / $b |
% | Modulo | $a % $b |
** | Potenzierung | $a ** $b |
Alle arithmetischen Operatoren wandeln Operanden in den numerischen Kontext um. Division durch Null erzeugt eine Warnung und gibt Unendlich oder NaN zurueck.
my $x = 5 + 3; # 8
my $y = 10 / 3; # 3.333...
my $z = 2 ** 10; # 1024
my $m = 17 % 5; # 2
Zeichenketten
| Operator | Operation | Beispiel |
|---|---|---|
. | Verkettung | $a . $b |
x | Wiederholung | $str x $count |
Zeichenkettenoperatoren wandeln Operanden in den Zeichenkettenkontext um.
my $str = "Hello" . " " . "World"; # "Hello World"
my $rep = "X" x 5; # "XXXXX"
my $pad = " " x 10; # 10 Leerzeichen
Numerischer Vergleich
| Operator | Operation | Gibt zurueck |
|---|---|---|
== | Gleich | Wahr bei Gleichheit |
!= | Ungleich | Wahr bei Ungleichheit |
< | Kleiner als | Wahr wenn kleiner |
> | Groesser als | Wahr wenn groesser |
<= | Kleiner oder gleich | Wahr wenn kleiner oder gleich |
>= | Groesser oder gleich | Wahr wenn groesser oder gleich |
<=> | Dreiwegevergleich | -1, 0 oder 1 |
Numerische Vergleiche wandeln Operanden in Zahlen um.
if ($age >= 18) { ... }
my $cmp = $a <=> $b; # -1 wenn $a < $b, 0 bei Gleichheit, 1 wenn $a > $b
Zeichenkettenvergleich
| Operator | Operation | Gibt zurueck |
|---|---|---|
eq | Gleich | Wahr bei Gleichheit |
ne | Ungleich | Wahr bei Ungleichheit |
lt | Kleiner als | Wahr wenn kleiner |
gt | Groesser als | Wahr wenn groesser |
le | Kleiner oder gleich | Wahr wenn kleiner oder gleich |
ge | Groesser oder gleich | Wahr wenn groesser oder gleich |
cmp | Dreiwegevergleich | -1, 0 oder 1 |
Zeichenkettenvergleiche verwenden lexikographische (Woerterbuch-)Ordnung.
if ($name eq "John") { ... }
my $cmp = $a cmp $b; # -1, 0 oder 1
Logisch
| Operator | Operation | Kurzschluss | Vorrang |
|---|---|---|---|
&& | Und | Ja | Hoch |
| ` | ` | Oder | |
// | Definiert-oder | Ja | Hoch |
and | Und | Ja | Niedrig |
or | Oder | Ja | Niedrig |
xor | Exklusiv-oder | Nein | Niedrig |
Logische Operatoren geben den zuletzt ausgewerteten Wert zurueck, nicht nur wahr/falsch.
my $result = $x && $y; # Gibt $y zurueck wenn $x wahr, sonst $x
my $default = $user || "guest"; # Gibt "guest" zurueck wenn $user falsch
my $value = $config // 0; # Gibt 0 zurueck nur wenn $config undefiniert
Definiert-oder (//): Anders als || prueft dieser Operator nur, ob die linke Seite definiert ist, nicht ob sie wahr ist. 0 und "" sind beide falsch, aber definiert.
my $x = 0;
my $a = $x || 10; # 10 (0 ist falsch)
my $b = $x // 10; # 0 (0 ist definiert)
Bitweise
| Operator | Operation | Beispiel |
|---|---|---|
& | Bitweises UND | $a & $b |
| ` | ` | Bitweises ODER |
^ | Bitweises XOR | $a ^ $b |
<< | Linksverschiebung | $a << $n |
>> | Rechtsverschiebung | $a >> $n |
Bitweise Operatoren arbeiten auf Ganzzahlen. Operanden werden in vorzeichenlose Ganzzahlen umgewandelt.
my $mask = 0xFF & $value;
my $flags = $READ | $WRITE;
my $double = $x << 1;
Bereich
| Operator | Operation | Kontext |
|---|---|---|
.. | Inklusiver Bereich | Erzeugt im Listenkontext eine Liste |
... | Flip-Flop | Ist im skalaren Kontext zustandsbehaftet |
my @digits = (0..9); # (0, 1, 2, ..., 9)
my @letters = ('a'..'z'); # ('a', 'b', ..., 'z')
for my $i (1..100) { ... } # Schleife von 1 bis 100
Im skalaren Kontext sind .. und ... Flip-Flop-Operatoren (zustandsbehafteter boolescher Bereich).
Bindung
| Operator | Operation | Beispiel |
|---|---|---|
=~ | Abgleich | $str =~ /pattern/ |
!~ | Negativer Abgleich | $str !~ /pattern/ |
Bindungsoperatoren verbinden Zeichenketten mit Regex-Operationen.
if ($email =~ /\@/) { ... } # Enthaelt @
if ($name !~ /^\d/) { ... } # Beginnt nicht mit Ziffer
Unaere Operatoren
Arithmetik
| Operator | Operation | Beispiel |
|---|---|---|
- | Negation | -$x |
+ | Unaeres Plus | +$x |
my $neg = -5;
my $pos = +$x; # Numerischer Kontext
Logisch
| Operator | Operation | Beispiel |
|---|---|---|
! | Nicht | !$x |
not | Nicht (niedriger Vorrang) | not $x |
if (!$error) { ... }
die "Fehlgeschlagen" if not $ok;
Bitweise
| Operator | Operation | Beispiel |
|---|---|---|
~ | Bitweises Komplement | ~$x |
my $inverted = ~$bits;
Referenz
| Operator | Operation | Beispiel |
|---|---|---|
\ | Referenz erzeugen | \$x, \@arr, \%hash |
my $scalar_ref = \$value;
my $array_ref = \@data;
my $hash_ref = \%config;
Inkrement/Dekrement
| Operator | Operation | Wann ausgewertet |
|---|---|---|
++$x | Praeinkrement | Nach dem Erhoehen |
--$x | Praedekrement | Nach dem Verringern |
$x++ | Postinkrement | Vor dem Erhoehen |
$x-- | Postdekrement | Vor dem Verringern |
my $x = 5;
my $a = ++$x; # $x ist 6, $a ist 6
my $b = $x++; # $x ist 7, $b ist 6
Zeichenketteninkrement: ++ auf Zeichenketten fuehrt ein “magisches Inkrement” durch (Perl-Stil).
my $s = "aa";
$s++; # "ab"
$s++; # "ac"
Dateitestoperatoren
Dateitestoperatoren pruefen Eigenschaften von Dateien und Dateihandles. Alle geben wahr/falsch zurueck, ausser -s, das die Dateigroesse liefert.
| Operator | Test | Gibt zurueck |
|---|---|---|
-e | Existiert | Boolesch |
-r | Lesbar | Boolesch |
-w | Schreibbar | Boolesch |
-x | Ausfuehrbar | Boolesch |
-o | Gehoert effektiver UID | Boolesch |
-R | Lesbar fuer reale UID | Boolesch |
-W | Schreibbar fuer reale UID | Boolesch |
-X | Ausfuehrbar fuer reale UID | Boolesch |
-O | Gehoert realer UID | Boolesch |
-z | Groesse Null | Boolesch |
-s | Groesse ungleich Null | Groesse in Bytes oder falsch |
-f | Regulaere Datei | Boolesch |
-d | Verzeichnis | Boolesch |
-l | Symbolischer Link | Boolesch |
-p | Benannte Pipe (FIFO) | Boolesch |
-S | Socket | Boolesch |
-b | Blockspezialdatei | Boolesch |
-c | Zeichenspezialdatei | Boolesch |
-t | TTY (Terminal) | Boolesch |
-u | Setuid-Bit gesetzt | Boolesch |
-g | Setgid-Bit gesetzt | Boolesch |
-k | Sticky-Bit gesetzt | Boolesch |
-T | Textdatei | Boolesch |
-B | Binaerdatei | Boolesch |
-M | Aenderungszeit (Tage) | Zahl |
-A | Zugriffszeit (Tage) | Zahl |
-C | Inode-Aenderungszeit (Tage) | Zahl |
if (-e $file) { ... } # Datei existiert
if (-f $path && -r $path) { ... } # Regulaere Datei und lesbar
my $size = -s $file; # Groesse in Bytes
if (-d $path) { ... } # Ist Verzeichnis
Gestapelte Dateitests: -f -w -r $file prueft alle drei Bedingungen.
Weitere unaere Operatoren
| Operator | Operation | Beispiel |
|---|---|---|
defined | Auf Definiertheit pruefen | defined $x |
if (defined $value) { ... }
Zuweisungsoperatoren
Einfache Zuweisung
| Operator | Operation | Beispiel |
|---|---|---|
= | Zuweisung | $x = 5 |
my $x = 10;
my ($a, $b, $c) = (1, 2, 3); # Listenzuweisung
Zusammengesetzte Zuweisung
Alle binaeren Operatoren haben zusammengesetzte Zuweisungsformen:
| Operator | Aequivalent | Beispiel |
|---|---|---|
+= | $x = $x + $y | $x += 5 |
-= | $x = $x - $y | $x -= 3 |
*= | $x = $x * $y | $x *= 2 |
/= | $x = $x / $y | $x /= 10 |
%= | $x = $x % $y | $x %= 7 |
**= | $x = $x ** $y | $x **= 2 |
.= | $x = $x . $y | $str .= "more" |
x= | $x = $x x $y | $str x= 3 |
&= | $x = $x & $y | $bits &= $mask |
| ` | =` | `$x = $x |
^= | $x = $x ^ $y | $x ^= $y |
<<= | $x = $x << $y | $x <<= 2 |
>>= | $x = $x >> $y | $x >>= 1 |
&&= | $x = $x && $y | $x &&= $default |
| ` | =` | |
//= | $x = $x // $y | $x //= 0 |
my $count = 10;
$count += 5; # 15
$count *= 2; # 30
my $path = "/home";
$path .= "/user"; # "/home/user"
$cache ||= load_data(); # Nur laden wenn $cache falsch ist
$config //= {}; # Nur zuweisen wenn $config undef ist
Ternaerer Operator
| Operator | Syntax | Beispiel |
|---|---|---|
? : | Bedingung | $cond ? $then : $else |
my $result = $x > 0 ? "positiv" : "nicht positiv";
my $max = $a > $b ? $a : $b;
Ternaerer Operator als Lvalue: Der ternaere Operator kann als Zuweisungsziel verwendet werden.
($x > 0 ? $pos : $neg) = 10; # Weist $pos oder $neg zu
Array-/Hash-Indizes
| Syntax | Operation | Beispiel |
|---|---|---|
$arr[index] | Array-Elementzugriff | $arr[0] |
$hash{key} | Hash-Elementzugriff | $hash{name} |
@arr[indices] | Array-Scheibe | @arr[1, 3, 5] |
@hash{keys} | Hash-Scheibe | @hash{qw(a b c)} |
my $first = $arr[0];
my $value = $hash{key};
my @subset = @arr[0, 2, 4]; # Elemente 0, 2, 4
my @values = @hash{'a', 'b'}; # Werte fuer Schluessel 'a', 'b'
Pfeiloperator
| Syntax | Operation | Beispiel |
|---|---|---|
->[] | Array-Dereferenzierungsindex | $aref->[0] |
->{} | Hash-Dereferenzierungsindex | $href->{key} |
->() | Methodenaufruf | $obj->method() |
my $element = $array_ref->[5];
my $value = $hash_ref->{name};
my $result = $object->method(@args);
Kommaoperatoren
| Operator | Operation | Verwendung |
|---|---|---|
, | Listentrennzeichen | (1, 2, 3) |
=> | Fettes Komma | key => value |
Das fette Komma (=>) quotiert Barewords auf seiner linken Seite automatisch.
my %hash = (
name => "John", # 'name' wird automatisch quotiert
age => 30,
);
Operatorvorrang
Hoechster bis niedrigster Vorrang (identisch zu Perl 5):
- Terme und Listenoperatoren (links)
->(links)++--(kein)**(rechts)!~\unaeres+unaeres-(rechts)=~!~(links)*/%x(links)+-.(links)<<>>(links)- Benannte unaere Operatoren
<><=>=ltgtlege(kein)==!=<=>eqnecmp~~(kein) –~~Smart Match ist teilweise implementiert&(links)|^(links)&&(links)||//(links).....(kein)?:(rechts)=+=-=usw. (rechts),=>(links)- Listenoperatoren (rechts)
not(rechts)and(links)orxor(links)
Verwende Klammern, wenn der Vorrang unklar ist.
PetaPerl-spezifische Hinweise
Parallelisierung
Operatoren werden standardmaessig sequenziell ausgefuehrt. PetaPerls Parallelisierung greift auf Ebene von Schleifen/map/grep, nicht bei einzelnen Operatoren.
Leistung
- Bitweise Operationen werden zu nativen Maschinenbefehlen kompiliert
- Zeichenkettenverkettung kann neuen Speicher allokieren (
.=fuer Effizienz verwenden) - Numerische Operationen werden getrennt fuer Ganzzahlen und Gleitkommazahlen optimiert
Regulaere Ausdruecke
PetaPerl implementiert eine Perl-5-kompatible Regex-Engine mit Unterstuetzung fuer das gesamte Spektrum der Perl-Regex-Funktionalitaeten.
Mustersyntax
Literale
/hello/ # Literales "hello" abgleichen
/foo bar/ # "foo bar" abgleichen
Metazeichen
| Zeichen | Bedeutung |
|---|---|
. | Beliebiges Zeichen ausser Zeilenumbruch |
^ | Anfang der Zeichenkette |
$ | Ende der Zeichenkette |
\A | Anfang der Zeichenkette (absolut) |
\z | Ende der Zeichenkette (absolut) |
\Z | Ende der Zeichenkette oder vor abschliessendem Zeilenumbruch |
\b | Wortgrenze |
\B | Keine Wortgrenze |
\G | Position des letzten Treffers |
/^start/ # Muss am Anfang stehen
/end$/ # Muss am Ende stehen
/\bword\b/ # Ganzes Wort abgleichen
/\Abegin/ # Absoluter Anfang
/finish\z/ # Absolutes Ende
Zeichenklassen
[abc] # a, b oder c abgleichen
[^abc] # Alles ausser a, b, c abgleichen
[a-z] # Kleinbuchstabe abgleichen
[A-Z0-9] # Grossbuchstabe oder Ziffer abgleichen
[a-zA-Z_] # Wortzeichen abgleichen
Vordefinierte Zeichenklassen
| Klasse | Trifft auf | Negiert |
|---|---|---|
\d | Ziffer [0-9] | \D (Nicht-Ziffer) |
\w | Wortzeichen [a-zA-Z0-9_] | \W (Nicht-Wortzeichen) |
\s | Leerraum [ \t\n\r\f] | \S (Nicht-Leerraum) |
\h | Horizontaler Leerraum | \H |
\v | Vertikaler Leerraum | \V |
/\d+/ # Eine oder mehrere Ziffern
/\w+/ # Ein oder mehrere Wortzeichen
/\s*/ # Null oder mehr Leerraum
POSIX-Zeichenklassen
[:alnum:] # Alphanumerisch [a-zA-Z0-9]
[:alpha:] # Alphabetisch [a-zA-Z]
[:ascii:] # ASCII-Zeichen [0-127]
[:blank:] # Leerzeichen und Tabulator
[:cntrl:] # Steuerzeichen
[:digit:] # Ziffern [0-9]
[:graph:] # Sichtbare Zeichen (kein Leerzeichen)
[:lower:] # Kleinbuchstaben
[:print:] # Druckbare Zeichen
[:punct:] # Satzzeichen
[:space:] # Leerraum
[:upper:] # Grossbuchstaben
[:word:] # Wortzeichen [a-zA-Z0-9_]
[:xdigit:] # Hexadezimalziffern [0-9A-Fa-f]
Verwendung: [[:digit:]] oder [[:alpha:][:digit:]]
Quantifizierer
| Quantifizierer | Bedeutung | Gierig | Nicht-gierig | Possessiv |
|---|---|---|---|---|
* | 0 oder mehr | Ja | *? | *+ |
+ | 1 oder mehr | Ja | +? | ++ |
? | 0 oder 1 | Ja | ?? | ?+ |
{n} | Genau n | Ja | N/A | N/A |
{n,} | n oder mehr | Ja | {n,}? | N/A |
{n,m} | n bis m | Ja | {n,m}? | N/A |
/a*/ # 0 oder mehr 'a' (gierig)
/a*?/ # 0 oder mehr 'a' (nicht-gierig)
/a+/ # 1 oder mehr 'a'
/a?/ # 0 oder 1 'a'
/a{3}/ # Genau 3 'a'
/a{3,}/ # 3 oder mehr 'a'
/a{3,5}/ # 3 bis 5 'a'
Gierig vs. nicht-gierig: Gierige Quantifizierer treffen so viel wie moeglich, nicht-gierige treffen so wenig wie moeglich.
# Gegeben: "foo123bar"
/\d+/ # Trifft "123" (gierig)
/\d+?/ # Trifft "1" (nicht-gierig, aber das gesamte Muster muss treffen)
Gruppen und Erfassungen
Erfassungsgruppen
/(foo)/ # "foo" in $1 erfassen
/(foo)(bar)/ # In $1 und $2 erfassen
Zugriff auf Erfassungen ueber $1, $2 usw. oder das @+-Array.
Nicht-erfassende Gruppen
/(?:foo)/ # Gruppieren ohne Erfassung
Verwende diese, wenn Gruppierung noetig ist, aber kein Erfassungsaufwand.
Benannte Erfassungen
/(?<name>\w+)/ # Benannte Erfassung "name"
/(?'name'\w+)/ # Alternative Syntax
Zugriff ueber den $+{name}-Hash.
Alternation
/foo|bar/ # "foo" oder "bar" abgleichen
/(red|green|blue)/ # Farbe erfassen
Anker und Zusicherungen
Zusicherungen mit Nullbreite
| Zusicherung | Bedeutung |
|---|---|
(?=pattern) | Positiver Vorausblick |
(?!pattern) | Negativer Vorausblick |
(?<=pattern) | Positiver Rueckblick |
(?<!pattern) | Negativer Rueckblick |
/foo(?=bar)/ # "foo" gefolgt von "bar" (bar wird nicht verbraucht)
/foo(?!bar)/ # "foo" nicht gefolgt von "bar"
/(?<=foo)bar/ # "bar" dem "foo" vorausgeht
/(?<!foo)bar/ # "bar" dem nicht "foo" vorausgeht
Atomare Gruppen
/(?>pattern)/ # Atomare Gruppe (kein Backtracking)
Einmal getroffen, wird der Inhalt der Gruppe fixiert. Wird zur Leistungsoptimierung verwendet.
Rueckverweise
/(foo)\1/ # Trifft "foofoo" - \1 verweist auf erste Erfassung
/(['"]).*?\1/ # Trifft quotierte Zeichenkette (gleicher Quotierungstyp)
Benannte Rueckverweise
/(?<tag>\w+)...\k<tag>/ # Benannter Rueckverweis
Bedingungen
/(?(condition)yes|no)/ # Wenn Bedingung trifft, versuche "yes", sonst "no"
Bedingungen koennen sein:
- Erfassungsgruppennummer:
(?(1)yes|no)- wenn Gruppe 1 getroffen hat - Benannte Erfassung:
(?(<name>)yes|no)- wenn benannte Gruppe getroffen hat - Vorausblick:
(?(?=test)yes|no)- wenn Vorausblick erfolgreich
Modifizierer
Modifizierer aendern das Verhalten des regulaeren Ausdrucks. Sie werden nach dem schliessenden Begrenzer angegeben:
| Modifizierer | Bedeutung |
|---|---|
i | Gross-/Kleinschreibung ignorieren |
m | Mehrzeilig (^/$ treffen auf Zeilengrenzen) |
s | Einzeilig (. trifft auf Zeilenumbruch) |
x | Erweitert (Leerraum ignorieren, Kommentare erlaubt) |
g | Global (alle Treffer finden) |
c | Suche nach fehlgeschlagenem Abgleich fortsetzen |
o | Einmal kompilieren (veraltet, in PetaPerl nicht noetig) |
e | Ersetzung als Code auswerten (in s///) |
/pattern/i # Gross-/Kleinschreibung ignorieren
/pattern/ms # Mehrzeilig + einzeilig
/pattern/x # Erweitert (lesbar)
/pattern/g # Globaler Abgleich
Beispiele
# Gross-/Kleinschreibung ignorieren
if ($str =~ /hello/i) { ... }
# Mehrzeilig: ^ und $ treffen auf Zeilenanfaenge/-enden
while ($text =~ /^Line: (.+)$/mg) {
print "Gefunden: $1\n";
}
# Erweitert: Leerraum und Kommentare werden ignoriert
my $email_re = qr{
(\w+) # Benutzername
@ # At-Zeichen
([\w.]+) # Domain
}x;
# Global: alle Treffer finden
my @words = $text =~ /\w+/g;
Abgleich und Ersetzung
Abgleichoperator
$str =~ /pattern/ # Wahr wenn Treffer
$str =~ /pattern/g # Global, gibt alle Treffer zurueck
Im Listenkontext mit Erfassungen:
my ($user, $domain) = $email =~ /(\w+)@([\w.]+)/;
Im Listenkontext mit global:
my @numbers = $text =~ /\d+/g; # Alle Zahlen
Ersetzung
$str =~ s/old/new/ # Erstes Vorkommen ersetzen
$str =~ s/old/new/g # Alle Vorkommen ersetzen
$str =~ s/old/new/i # Ersetzen ohne Beachtung der Gross-/Kleinschreibung
$str =~ s/old/new/gi # Global + ohne Beachtung der Gross-/Kleinschreibung
Ersetzung mit Erfassungen:
$str =~ s/(\w+)@(\w+)/$2\@$1/; # user@domain umkehren
Ausgewertete Ersetzung:
$str =~ s/(\d+)/$1 * 2/e; # Alle Zahlen verdoppeln
Transliteration
$str =~ tr/abc/xyz/ # a durch x, b durch y, c durch z ersetzen
$str =~ y/abc/xyz/ # Identisch mit tr
$str =~ tr/a-z/A-Z/ # In Grossbuchstaben umwandeln
$str =~ tr/ //d # Leerzeichen loeschen
$str =~ tr/a-z//c # Nicht-Kleinbuchstaben zaehlen
Spezielle Variablen
Nach einem erfolgreichen Abgleich:
| Variable | Enthaelt |
|---|---|
$& | Gesamte getroffene Zeichenkette |
$` | Zeichenkette vor dem Treffer |
$' | Zeichenkette nach dem Treffer |
$1, $2, … | Erfassungsgruppen |
$+ | Letzte getroffene Erfassung |
@+ | Endpositionen der Erfassungen |
@- | Anfangspositionen der Erfassungen |
%+ | Benannte Erfassungen |
if ($str =~ /(foo)(bar)/) {
print "Gesamttreffer: $&\n"; # "foobar"
print "Gruppe 1: $1\n"; # "foo"
print "Gruppe 2: $2\n"; # "bar"
print "Davor: $`\n";
print "Danach: $'\n";
}
Regex-Kompilierung
qr//-Operator
Regulaeren Ausdruck zur Wiederverwendung kompilieren:
my $word = qr/\w+/;
my $email = qr/\w+@\w+\.\w+/;
if ($str =~ $word) { ... }
if ($str =~ /$word@$word/) { ... } # Interpolieren
Vorteile:
- Einmal kompilieren, vielfach verwenden
- Lesbare Regex-Komposition
- Leistungsoptimierung
Leistungsueberlegungen
Verankerte Muster
Mit ^ oder \A verankerte Muster sind schneller:
/^pattern/ # Schnell: prueft nur den Anfang
/pattern/ # Langsamer: durchsucht die gesamte Zeichenkette
Atomare Gruppen
Verwende atomare Gruppen (?>...) um Backtracking zu verhindern:
# Langsam: Backtracking bei Fehlschlag
/\d+\w+/
# Schnell: kein Backtracking in \d+
/(?>\d+)\w+/
Nicht-erfassende Gruppen
Verwende (?:...) wenn Erfassungen nicht benoetigt werden:
/(?:foo|bar)/ # Schneller als /(foo|bar)/ wenn Erfassung nicht noetig
PetaPerl-spezifische Funktionalitaeten
Bytecode-Kompilierung
Regex-Muster werden zu Bytecode kompiliert fuer effiziente Ausfuehrung. PetaPerl verwendet:
- Bitmap-Zeichenklassen fuer schnellen ASCII-Abgleich
- Literal-Praefix-Extraktion zum Ueberspringen unmoeglicher Positionen
- Ankerungserkennung zur Vermeidung unnuetzen Durchsuchens
Possessive Quantifizierer
Possessive Quantifizierer verhindern Backtracking vollstaendig (effizienter als atomare Gruppen fuer einfache Faelle):
/a++/ # 1 oder mehr 'a', kein Backtracking
/a*+/ # 0 oder mehr 'a', kein Backtracking
/a?+/ # 0 oder 1 'a', kein Backtracking
Eingebetteter Code
/pattern(?{ code })/ # Code waehrend des Abgleichs ausfuehren
/(??{ code })/ # Verzoegerter Regex (Code gibt Muster zurueck)
(?{code}) fuehrt Perl-Code an der Stelle im Muster aus, an der er erscheint. Der Code kann auf $1, $2 usw. aus bisherigen Erfassungen zugreifen.
Aktuelle Einschraenkungen
PetaPerls Regex-Engine besteht 99,3% der re_tests-Testsuite von perl5 (1959/1972 Tests). Verbleibende Luecken:
- Selbstreferenzierende Erfassungen – Muster wie
(a\1)(3 Tests) localin Codebloecken –(?{ local $x = ... })(2 Tests)- Mehrzeichenfaltung bei Gross-/Kleinschreibung – Unicode-Zeichen, die zu mehreren Zeichen gefaltet werden (2 Tests)
- Branch-Reset-Rueckverweise – komplexe
(?|...)-Muster mit Rueckverweisen (5 Tests) - Randfaelle bei Zeichenketteninterpolation – 1 Test
Unicode-Eigenschaftsunterstuetzung (\p{Letter}, \p{Digit} usw.) ist vollstaendig implementiert fuer standardmaessige Unicode-Kategorien.
Beispiele
E-Mail-Validierung
my $email_re = qr/^[\w.+-]+@[\w.-]+\.[a-zA-Z]{2,}$/;
if ($email =~ $email_re) {
print "Gueltige E-Mail\n";
}
URL-Analyse
my ($protocol, $host, $path) = $url =~
m{^(https?)://([^/]+)(/.*)$};
Protokolldatei-Analyse
while ($line =~ /\[(\d{4}-\d{2}-\d{2})\] (\w+): (.+)/g) {
my ($date, $level, $msg) = ($1, $2, $3);
# Protokolleintrag verarbeiten
}
Zeichenkettenbereinigung
# Mehrfache Leerzeichen entfernen
$text =~ s/\s+/ /g;
# Fuehrenden/nachfolgenden Leerraum entfernen
$text =~ s/^\s+|\s+$//g;
# Oder mit zwei Ersetzungen
$text =~ s/^\s+//;
$text =~ s/\s+$//;
Vorlagenersetzung
my %vars = (name => "John", age => 30);
my $template = "Hallo {{name}}, du bist {{age}} Jahre alt.";
$template =~ s/\{\{(\w+)\}\}/$vars{$1}/ge;
Siehe auch
Datentypen
Perl hat drei grundlegende Datentypen: Skalare, Arrays und Hashes. PetaPerl implementiert diese mit identischer Semantik zu Perl 5.
Skalare
Skalare sind der grundlegende Datentyp in Perl und enthalten einen einzelnen Wert. Der Variablenname beginnt mit $.
my $number = 42;
my $string = "hello";
my $float = 3.14;
my $ref = \@array;
Skalartypen
Intern stellt PetaPerl Skalare als Enum mit folgenden Varianten dar:
| Typ | Beschreibung | Beispiel |
|---|---|---|
Undef | Undefinierter Wert | undef |
Iv | Ganzzahl (i64) | 42, -17 |
Uv | Vorzeichenlose Ganzzahl (u64) | Grosse positive Zahlen |
Nv | Gleitkommazahl (f64) | 3.14, 1.5e10 |
Pv | Unveraenderliche Zeichenkette (Arc<str>) | Konstanten, Literale |
PvBuf | Veraenderliche Zeichenkette (Arc<String>) | .=-Ziele, $_ |
Rv | Referenz | \$x, \@arr, \%hash |
Av | Array | Erzeugt durch [] |
Hv | Hash | Erzeugt durch {} |
Dynamische Typisierung: Ein Skalar kann seinen Typ waehrend der Ausfuehrung aendern.
my $x = 42; # Ganzzahl
$x = "hello"; # Jetzt eine Zeichenkette
$x = 3.14; # Jetzt eine Gleitkommazahl
Skalarer Kontext
Operationen, die einen einzelnen Wert erwarten, erzwingen skalaren Kontext:
my $count = @array; # Array-Laenge
my $last = (1, 2, 3); # Letztes Element (3)
if (@array) { ... } # Wahr wenn nicht leer
Spezielle Skalare
| Wert | Beschreibung |
|---|---|
undef | Undefinierter Wert |
0 | Numerische Null, Zeichenkette “0” |
"" | Leere Zeichenkette |
Wahrheitswerte: Diese sind im booleschen Kontext falsch: undef, 0, "0", "". Alles andere ist wahr.
if ($value) { ... } # Falsch bei undef, 0, "0" oder ""
if (defined $value) { ... } # Falsch nur bei undef
Arrays
Arrays sind geordnete Listen von Skalaren. Variablennamen beginnen mit @.
my @numbers = (1, 2, 3, 4, 5);
my @words = qw(foo bar baz);
my @empty = ();
Array-Zugriff
my $first = $numbers[0]; # Erstes Element (Index 0)
my $last = $numbers[-1]; # Letztes Element
$numbers[5] = 6; # Element setzen
Beachte den Sigilwechsel: Verwende $ fuer den Zugriff auf ein einzelnes Element, da ein Skalar zurueckgegeben wird.
Array-Laenge
my $length = @array; # Skalarer Kontext
my $length = scalar @array; # Explizit skalarer Kontext
my $max_index = $#array; # Hoechster Index (Laenge - 1)
Array-Operationen
push @arr, $value; # Anhaengen
my $value = pop @arr; # Letztes entfernen
my $value = shift @arr; # Erstes entfernen
unshift @arr, $value; # Voranstellen
splice @arr, $offset, $len, @replacement; # Allgemeines Entfernen/Einfuegen
Array-Scheiben
Mehrere Elemente auf einmal extrahieren:
my @subset = @arr[0, 2, 4]; # Elemente 0, 2, 4
my @range = @arr[0..5]; # Elemente 0 bis 5
@arr[1, 3] = (10, 20); # Mehreren Elementen zuweisen
Listenkontext
Operationen, die mehrere Werte erwarten, erzwingen Listenkontext:
my @copy = @original; # Array-Kopie
my ($a, $b, $c) = (1, 2, 3); # Listenzuweisung
my @results = function(); # Funktion gibt Liste zurueck
Array-Referenzen
Referenzen auf Arrays erzeugen:
my $aref = \@array; # Referenz auf bestehendes Array
my $aref = [1, 2, 3]; # Anonyme Array-Referenz
Zugriff ueber Referenz:
my $elem = $aref->[0]; # Erstes Element
my @copy = @$aref; # Dereferenzierung zum Array
push @$aref, $value; # Push ueber Referenz
Hashes
Hashes sind ungeordnete Schluessel-Wert-Paare. Variablennamen beginnen mit %.
my %user = (
name => "John",
age => 30,
email => "john@example.com",
);
Hash-Zugriff
my $name = $user{name}; # Wert abrufen
$user{city} = "NYC"; # Wert setzen
Sigilwechsel: Verwende $ fuer den Zugriff auf ein einzelnes Element (es wird ein Skalar zurueckgegeben).
Hash-Operationen
my @keys = keys %hash; # Alle Schluessel
my @values = values %hash; # Alle Werte
while (my ($k, $v) = each %hash) { ... } # Iterieren
if (exists $hash{key}) { ... } # Existenz pruefen
my $val = delete $hash{key}; # Entfernen und zurueckgeben
Hash-Scheiben
Mehrere Werte auf einmal extrahieren:
my @vals = @hash{qw(name age)}; # Werte fuer Schluessel
@hash{qw(x y)} = (10, 20); # Mehrfachzuweisung
Hinweis: Hash-Scheiben verwenden das @-Sigil, da sie eine Liste zurueckgeben.
Hash-Referenzen
Referenzen auf Hashes erzeugen:
my $href = \%hash; # Referenz auf bestehenden Hash
my $href = { key => "val" }; # Anonyme Hash-Referenz
Zugriff ueber Referenz:
my $val = $href->{key}; # Wert abrufen
$href->{new} = "value"; # Wert setzen
my @keys = keys %$href; # Dereferenzierung zum Hash
Referenzen
Referenzen sind Skalare, die auf andere Daten verweisen.
Referenzen erzeugen
my $scalar_ref = \$scalar;
my $array_ref = \@array;
my $hash_ref = \%hash;
my $code_ref = \⊂
my $anon_array = [1, 2, 3];
my $anon_hash = { a => 1, b => 2 };
my $anon_sub = sub { ... };
Dereferenzierung
my $value = $$scalar_ref; # Skalar-Dereferenzierung
my @array = @$array_ref; # Array-Dereferenzierung
my %hash = %$hash_ref; # Hash-Dereferenzierung
my $result = &$code_ref(); # Code-Dereferenzierung
Pfeilnotation (bevorzugt fuer Klarheit):
my $elem = $array_ref->[0];
my $val = $hash_ref->{key};
my $result = $code_ref->(@args);
Referenztypen
Referenztyp mit ref pruefen:
my $type = ref $ref;
# Gibt zurueck: 'SCALAR', 'ARRAY', 'HASH', 'CODE', 'REF' oder '' (keine Referenz)
Verschachtelte Datenstrukturen
Referenzen ermoeglichen komplexe Datenstrukturen:
my $data = {
users => [
{ name => "John", age => 30 },
{ name => "Jane", age => 25 },
],
config => {
debug => 1,
timeout => 30,
},
};
my $name = $data->{users}->[0]->{name}; # "John"
Autovivifikation
Perl erzeugt automatisch Zwischenreferenzen:
my %hash;
$hash{a}{b}{c} = 1; # Erzeugt verschachtelte Hashes automatisch
my $val = $hash{x}[0]; # Erzeugt Array-Referenz unter $hash{x}
Typumwandlungen
Perl fuehrt automatische Typumwandlungen kontextabhaengig durch.
Zeichenkette zu Zahl
my $x = "42";
my $y = $x + 10; # 52 (Zeichenkette -> Zahl)
Zahl zu Zeichenkette
my $x = 42;
my $s = "Wert: $x"; # "Wert: 42" (Zahl -> Zeichenkette)
Zeichenkettenverkettung
my $result = 10 . 20; # "1020" (beide -> Zeichenkette)
Boolescher Kontext
if ("0") { ... } # Falsch
if ("00") { ... } # Wahr
if (0) { ... } # Falsch
if (0.0) { ... } # Falsch
if ("") { ... } # Falsch
if (undef) { ... } # Falsch
Typeglobs
Typeglobs sind ein spezieller Typ, der Eintraege fuer alle Variablentypen mit demselben Namen enthalten kann.
*name = \$scalar; # Glob auf Skalar aliasieren
*name = \⊂ # Glob auf Unterprogramm aliasieren
Werden hauptsaechlich fuer Symboltabellenmanipulation und Importvorgaenge verwendet.
PetaPerl-spezifische Implementierung
Speicherdarstellung
PetaPerl verwendet effiziente interne Darstellungen:
Skalare: Rust-Enum mit Varianten fuer jeden Typ. Zwei Zeichenkettendarstellungen optimieren fuer verschiedene Zugriffsmuster.
#![allow(unused)]
fn main() {
pub enum Sv {
Undef,
Iv(i64), // Ganzzahl
Uv(u64), // Vorzeichenlos
Nv(f64), // Gleitkomma
Pv(Arc<str>, u32), // Unveraenderliche Zeichenkette + virtuelle Laenge (O(1) chomp)
PvBuf(Arc<String>), // Veraenderliche Zeichenkette (COW ueber Arc::make_mut)
Rv(RvInner), // Referenz
Av(Av), // Array
Hv(Hv), // Hash
// ... weitere Typen
}
}
Duale Zeichenkettendarstellung: Pv wird fuer Konstanten und Literale verwendet – die virtuelle Laenge u32 ermoeglicht O(1)-chomp ohne Aenderung der gemeinsam genutzten Zeichenkette. PvBuf wird fuer veraenderliche Zeichenketten verwendet (Standard fuer new_string()) – Arc::make_mut() bietet Copy-on-Write-Semantik ohne Allokation bei nicht geteiltem Besitz.
Arrays: Dynamische Vektoren mit effizienten push/pop-Operationen.
Hashes: Optimierte Hashtabellen mit schneller Schluesselsuche.
Gemeinsamer Besitz
Zeichenketten-Skalare verwenden Arc<str> (atomare Referenzzaehlung):
- Guenstiges Klonen (nur Zaehler erhoehen)
- Thread-sicheres Teilen fuer parallele Ausfuehrung
- Haeufig verwendete Zeichenketten koennen Speicher teilen
Aliasing-Unterstuetzung
PetaPerl implementiert @_-Aliasing korrekt:
- Argumente sind Aliase auf die Variablen des Aufrufers
- Aenderungen schreiben direkt in das Original
- Verwendet
SvCellfuer veraenderliche Indirektion
sub modify {
$_[0] = "changed"; # Aendert die Variable des Aufrufers
}
my $x = "original";
modify($x);
print $x; # "changed"
Leistungsmerkmale
| Operation | Komplexitaet | Anmerkungen |
|---|---|---|
| Skalarzuweisung | O(1) | Zeichenkette nutzt Arc (keine Kopie) |
| Array push/pop | O(1) amortisiert | Dynamisches Wachstum |
| Array shift/unshift | O(n) | Elemente muessen verschoben werden |
| Hash-Zugriff | O(1) durchschnittlich | Optimierte Hash-Funktion |
| Hash-Einfuegen | O(1) durchschnittlich | Mit Wachstum |
Parallele Ausfuehrung
PetaPerls Parallelisierungsmodell:
- Jeder Thread erhaelt seinen eigenen lexikalischen Pad (keine gemeinsame Mutation)
- Array- und Hash-Operationen sind thread-sicher
Arc<str>-Zeichenketten koennen sicher zwischen Threads geteilt werden- Parallelitaet auf Schleifenebene erfordert keine globalen Sperren
Kontextsensitivitaet
Perls Kontextsystem bestimmt, wie Ausdruecke ausgewertet werden.
Skalarer Kontext
Erzwingt Auswertung als Einzelwert:
my $count = @array; # Laenge
my $last = (1, 2, 3); # Letztes Element
my $concat = (1, 2, 3); # 3
Listenkontext
Erzwingt Auswertung als Mehrfachwert:
my @copy = @array; # Alle Elemente
my @results = func(); # Alle Rueckgabewerte
my ($a, $b) = (1, 2, 3); # Erste zwei Elemente
Void-Kontext
Das Ergebnis wird verworfen:
func(); # Rueckgabewert ignoriert
print "hello"; # Keine Zuweisung
Kontextweitergabe
my @arr = (1, 2, 3);
# Skalarer Kontext
my $x = keys @arr; # keys im skalaren Kontext -> Anzahl
# Listenkontext
my @k = keys @arr; # keys im Listenkontext -> alle Schluessel
# Funktionsargument-Kontext
func(@arr); # Listenkontext
func(scalar @arr); # Skalarer Kontext (explizit)
Konstanten
Konstanten sind unveraenderliche Werte.
Literalkonstanten
42 # Ganzzahl
3.14 # Gleitkommazahl
"string" # Zeichenkette
qw(a b c) # Liste von Zeichenketten
Benannte Konstanten
use constant PI => 3.14159;
use constant MAX => 100;
use constant {
RED => 0xFF0000,
GREEN => 0x00FF00,
BLUE => 0x0000FF,
};
Konstantenfaltung zur Uebersetzungszeit
PetaPerl fuehrt Konstantenfaltung zur Uebersetzungszeit durch:
my $x = 2 + 3; # Wird zu 5 gefaltet
my $len = length("hello"); # Wird zu 5 gefaltet
my $sub = substr("text", 0, 2); # Wird zu "te" gefaltet
Diese Optimierung eliminiert Laufzeitberechnungen fuer konstante Ausdruecke.
Spezielle Typen
Code-Referenzen
Unterprogramme koennen referenziert und dynamisch aufgerufen werden:
my $coderef = sub { return $_[0] * 2 };
my $result = $coderef->(21); # 42
my $coderef = \&existing_sub;
$coderef->(@args);
Dateihandles
Dateihandles sind spezielle Skalare:
open my $fh, '<', $file or die $!;
my $line = <$fh>;
close $fh;
Globs
Typeglobs verweisen auf Symboltabelleneintraege:
*alias = *original; # Alle Typen aliasieren
*func = sub { ... }; # Unterprogramm installieren
Siehe auch
- perlop - Operatoren, die mit diesen Typen arbeiten
- perlfunc - Funktionen zur Datenmanipulation
- perlref - Mehr zu Referenzen (Perl-5-Dokumentation)
Unterprogramme
Unterprogramme sind wiederverwendbare Codebloecke in Perl. PetaPerl implementiert Perl5-kompatible Unterprogramme mit vollstaendiger Unterstuetzung fuer lexikalische Variablen, Closures und Signaturen.
Deklaration
sub greet {
print "Hello, World!\n";
}
# Benanntes Unterprogramm
sub add {
my ($a, $b) = @_;
return $a + $b;
}
# Anonymes Unterprogramm
my $sub = sub {
my $x = shift;
return $x * 2;
};
Argumente (@_)
Argumente werden ueber das spezielle @_-Array uebergeben. Jedes Element ist ein Alias auf die Variable des Aufrufers.
sub modify {
$_[0] = "modified"; # Aendert die Variable des Aufrufers
}
my $x = "original";
modify($x);
say $x; # "modified"
Argumente kopieren:
sub safe_modify {
my ($val) = @_; # Kopie, kein Alias
$val = "modified"; # Beeinflusst den Aufrufer nicht
}
Direkter Zugriff:
sub first { $_[0] }
sub second { $_[1] }
Rueckgabewerte
Explizite Rueckgabe
sub max {
my ($a, $b) = @_;
return $a if $a > $b;
return $b;
}
Implizite Rueckgabe
Der Wert des letzten Ausdrucks wird automatisch zurueckgegeben.
sub square {
my $x = shift;
$x * $x # Implizite Rueckgabe
}
Kontextabhaengige Rueckgabe
sub get_data {
if (wantarray) {
return (1, 2, 3); # Listenkontext
} else {
return "scalar data"; # Skalarer Kontext
}
}
my @list = get_data(); # (1, 2, 3)
my $scalar = get_data(); # "scalar data"
Hinweis: PetaPerls wantarray funktioniert fuer direkte Aufrufe und die meisten gaengigen Muster. Bei komplexer Verschachtelung kann es Randfaelle geben.
Prototypen
Prototypen bieten Argumentpruefung zur Uebersetzungszeit und Kontexterzwingung.
sub scalar_arg ($) {
my $x = shift;
}
sub array_arg (@) {
my @vals = @_;
}
sub no_args () {
return 42;
}
Gaengige Prototypen:
| Prototyp | Bedeutung |
|---|---|
$ | Einzelnes Skalarargument |
@ | Array von Argumenten (schluerfen) |
% | Hash (Liste gerader Laenge) |
& | Unterprogramm-Referenz |
* | Bareword oder Typeglob |
\$ | Skalar-Referenz |
\@ | Array-Referenz |
\% | Hash-Referenz |
() | Keine Argumente (konstantes Unterprogramm) |
PetaPerl-Status: Grundlegendes Prototyp-Parsing implementiert. Laufzeitdurchsetzung teilweise.
Signaturen (experimentell)
Perl-5.20+-Signaturen bieten benannte Parameter mit optionalen Typbeschraenkungen und Standardwerten.
sub greet ($name, $greeting = "Hello") {
say "$greeting, $name!";
}
greet("Alice"); # Hello, Alice!
greet("Bob", "Hi"); # Hi, Bob!
Schluerfen-Parameter:
sub sum ($first, @rest) {
my $total = $first;
$total += $_ for @rest;
return $total;
}
sum(1, 2, 3, 4); # 10
PetaPerl-Status: Signatur-Parsing im AST implementiert. Codegen senkt zu Pad-Allokation ab. Laufzeit-Parameterzuweisung funktioniert. Standardwerte und Schluerfen-Parameter funktionsfaehig.
Closures
Anonyme Unterprogramme erfassen lexikalische Variablen aus dem umschliessenden Gueltigkeitsbereich.
sub make_counter {
my $count = 0;
return sub {
return ++$count;
};
}
my $c1 = make_counter();
say $c1->(); # 1
say $c1->(); # 2
my $c2 = make_counter();
say $c2->(); # 1 (unabhaengiger Zaehler)
Closure-Erfassungsmechanismus:
- Parser identifiziert aeussere lexikalische Variablen, die im Unterprogrammkoerper referenziert werden
- Codegen zeichnet
outer_refs-Zuordnung auf (sub_pad_slot, outer_pad_slot) pp_anoncodeerfasst Zellen zum Instanziierungszeitpunktpp_entersubteilt erfasste Zellen in den Pad des Unterprogramms
Beispiel: Closure-Fabrik
sub make_adder {
my $offset = shift;
return sub {
my $x = shift;
return $x + $offset; # Erfasst $offset
};
}
my $add5 = make_adder(5);
my $add10 = make_adder(10);
say $add5->(3); # 8
say $add10->(3); # 13
Jede Closure erfasst ihre eigene $offset-Zelle. Aenderungen betreffen nur die Instanz dieser Closure.
Methodenaufrufe
my $obj = Some::Class->new();
$obj->method($arg);
# Indirekte Objektnotation (nicht empfohlen)
method $obj $arg;
PetaPerl-Status: Methodendispatch ueber pp_method, pp_method_named. ISA-Suche implementiert. AUTOLOAD unterstuetzt.
Spezielle Unterprogramme
AUTOLOAD
Wird aufgerufen, wenn ein undefiniertes Unterprogramm aufgerufen wird.
package Foo;
our $AUTOLOAD;
sub AUTOLOAD {
my $method = $AUTOLOAD;
$method =~ s/.*:://; # Paket entfernen
print "Undefinierte Methode aufgerufen: $method\n";
}
Foo->undefined_method(); # Loest AUTOLOAD aus
PetaPerl-Status: AUTOLOAD-Dispatch in pp_entersub implementiert. Setzt $Package::AUTOLOAD korrekt.
BEGIN, END, INIT, CHECK, UNITCHECK
PetaPerl-Status: BEGIN-Bloecke werden waehrend der Kompilierung ausgefuehrt. END-Bloecke werden beim Programmende ausgefuehrt. INIT, CHECK und UNITCHECK haben teilweise Unterstuetzung.
Rekursion
Rekursive Aufrufe werden mit Tiefenlimitierung unterstuetzt.
sub factorial {
my $n = shift;
return 1 if $n <= 1;
return $n * factorial($n - 1);
}
Rekursionslimit: Standardmaessig 1000 Ebenen (konfigurierbar ueber PPERL_MAX_RECURSION). Verhindert Stapelueberlauf.
Aufrufkonventionen
Mit Klammern
foo(); # Keine Argumente
foo($a); # Ein Argument
foo($a, $b); # Mehrere Argumente
Ohne Klammern (Kaufmanns-Und-Form)
&foo; # Aktuelles @_ an foo uebergeben
Bei Aufruf als &foo (ohne Klammern) wird das aktuelle @_ an das aufgerufene Unterprogramm uebergeben. Dies ermoeglicht Wrapper-Funktionen:
sub wrapper {
log_call();
goto &real_function; # Tail-Call mit aktuellem @_
}
Goto und Tail-Calls
sub outer {
# ... Vorbereitung ...
goto &inner; # Aktuellen Aufrufrahmen ersetzen
}
goto &sub fuehrt eine Tail-Call-Optimierung durch: ersetzt den aktuellen Rahmen, anstatt einen neuen hinzuzufuegen.
PetaPerl-Status: goto &sub in pp_goto implementiert. Tail-Rekursions-Optimierung funktionsfaehig.
Erweiterte Funktionalitaeten
Konstante Unterprogramme
use constant PI => 3.14159;
use constant DEBUG => 0;
# Aequivalent zu:
sub PI () { 3.14159 }
Konstante Unterprogramme haben einen leeren Prototyp () und geben einen festen Wert zurueck. Perl kann sie zur Uebersetzungszeit einbetten.
PetaPerl-Status: use constant erzeugt konstante Unterprogramme. pp_entersub gibt den konstanten Wert direkt zurueck, ohne das Unterprogramm zu betreten.
Lvalue-Unterprogramme
sub get_value : lvalue {
$global_var;
}
get_value() = 42; # Weist $global_var zu
PetaPerl-Status: :lvalue-Attribut nicht implementiert. pp_leavesublv-Stub existiert.
State-Variablen
sub counter {
state $count = 0;
return ++$count;
}
state-Variablen bleiben ueber Aufrufe hinweg erhalten (werden einmal initialisiert).
PetaPerl-Status: State-Variablen sind implementiert. Variablen werden einmal initialisiert und bleiben ueber Aufrufe hinweg erhalten.
Eingebaute Funktionen
wantarray
Gibt den Aufrufkontext zurueck: undef (void), 0 (skalar), 1 (Liste).
sub context_aware {
if (!defined wantarray) {
say "Void-Kontext";
} elsif (wantarray) {
say "Listenkontext";
return (1, 2, 3);
} else {
say "Skalarer Kontext";
return 42;
}
}
PetaPerl-Status: Teilweise Implementierung. Funktioniert fuer direkte Aufrufe. Kann bei komplexer Verschachtelung fehlschlagen.
caller
Gibt Informationen ueber den aufrufenden Stapelrahmen zurueck.
# Skalarer Kontext: Boolesch (gibt es einen Aufrufer?)
if (caller) {
# Von einem anderen Unterprogramm aufgerufen
}
# Listenkontext: Detaillierte Informationen
my ($package, $filename, $line) = caller(0);
my ($pkg, $file, $line, $sub) = caller(1); # Einen Rahmen hoeher
PetaPerl-Status: In pp_caller implementiert. Gibt (package, filename, line, subroutine, ...) im Listenkontext zurueck.
PetaPerl-Implementierungshinweise
Op-Tree-Struktur
Benannte Unterprogramme registrieren CV (Code Value) in der Arena:
SubDef -> lower_sub() -> NextState -> Body-Ops -> LeaveSub
|
Register CV(name, start_op, pad_size, outer_refs)
Anonyme Unterprogramme geben eine AnonCode-Operation aus:
AnonSub -> lower_anon_sub() -> NextState -> Body-Ops -> LeaveSub
|
AnonCode (legt CV auf den Stapel)
Aufrufsequenz
- Aufrufer legt Argumente auf den Stapel
- Aufrufer legt Markierung ab
- Aufrufer legt CV-Referenz ab
EnterSubentnimmt CV, entnimmt Markierung, sammelt Argumente in aliasiertem@_- Neuer Pad wird allokiert (Slot 0 =
@_) - Closure-Zellen werden in den Pad geteilt (falls vorhanden)
- Sprung zur start_op des CV
- Koerper wird ausgefuehrt
LeaveSubsammelt Rueckgabewerte, stellt Pad wieder her- Rueckkehr zum Aufrufer
Pad-Layout
Slot 0: @_ (immer, befuellt durch EnterSub)
Slot 1+: Lexikalische Variablen in Deklarationsreihenfolge
Signaturparameter werden zu Pad-Slots:
sub foo ($x, $y, @rest) { }
# Pad-Layout:
# 0: @_
# 1: $x
# 2: $y
# 3: @rest
Closure-Erfassung
Zweistufiger Prozess:
- Codegen: Aeussere lexikalische Variablen analysieren,
outer_refsim CV aufzeichnen - Laufzeit:
pp_anoncodeerfasst Zellen,pp_entersubteilt sie
Warum zwei Stufen? Closure-Fabriken benoetigen Erfassung pro Instanz, nicht pro Definition.
Bekannte Einschraenkungen
| Funktionalitaet | Status |
|---|---|
| Benannte Parameter | Funktioniert |
| Prototypen | Geparst, teilweise Durchsetzung |
| Signaturen | Funktioniert (grundlegend) |
| Closures | Funktioniert |
| Rekursion | Funktioniert (1000 Ebenenlimit) |
| AUTOLOAD | Funktioniert |
| Methodendispatch | Funktioniert |
| wantarray | Teilweise |
| caller | Funktioniert |
| BEGIN/END-Bloecke | BEGIN funktioniert; END/INIT/CHECK teilweise |
Attribute (:lvalue usw.) | Geparst, nicht durchgesetzt |
| goto &sub | Funktioniert |
| State-Variablen | Grundlegende Unterstuetzung |
Leistung
PetaPerl-Unterprogramme werden ueber den Interpreter mit Schnellpfad-Dispatch ausgefuehrt. Der JIT-Compiler (Cranelift) behandelt heisse Schleifen innerhalb von Unterprogrammen, kompiliert aber nicht den Unterprogrammaufruf/-ruecksprung selbst per JIT.
Die Funktionszeiger-Dispatchtabelle des Interpreters und eingebettete Schnellpfade fuer gaengige Operationen (Arithmetik, Vergleich, Zuweisung) minimieren den Aufwand pro Operation.
Closure-Aufwand: Zellenallokation pro erfasster Variable. Minimal zur Laufzeit (Arc-Klon).
Testen
Siehe t/05-op/sub.t, t/05-op/closure.t fuer umfassende Testabdeckung.
Unterschiede zu Perl 5
PetaPerl strebt nahezu vollständige Kompatibilität mit Perl 5.42 an, weicht jedoch in mehreren Bereichen absichtlich davon ab. Dieses Dokument katalogisiert sowohl beabsichtigte Unterschiede als auch aktuelle Einschränkungen.
Beabsichtigte Unterschiede
Standardmäßig aktive moderne Funktionen
PetaPerl aktiviert alle modernen Perl-Funktionen standardmäßig. Kein use feature oder Versionsdeklaration notwendig:
say— Ausgabe mit Zeilenumbruchstate— persistente lexikalische Variablen//— Defined-Or-Operatorfc— Unicode-Faltung (Case Folding)- Unterprogramm-Signaturen
use v5.XX-Deklarationen werden geparst, haben aber keine Wirkung. PetaPerl verhält sich immer wie 5.42.
Kein XS-Support
PetaPerl lädt keine XS-Module (C-Erweiterungen). Stattdessen:
- Native Implementierungen: Leistungskritische Module (List::Util, Scalar::Util, Digest::MD5 usw.) sind in Rust als native Funktionen reimplementiert
- Pure-Perl-Fallback: Module mit reinen Perl-Implementierungen funktionieren automatisch
- JIT-Kompensation: Der JIT-Compiler schließt die Leistungslücke, die XS historisch rechtfertigte
Dies ist eine strategische Entscheidung: XS bindet Perl an eine C-Aufrufkonvention und verhindert JIT-Optimierung. PetaPerls JIT + Auto-Parallelisierung macht reines Perl wettbewerbsfähig gegenüber XS.
Nur Linux
PetaPerl läuft ausschließlich auf Linux (jede von Cranelift unterstützte Architektur: x86-64, AArch64, RISC-V, s390x). Kein Windows, macOS, BSD oder andere Plattformen.
$^O gibt immer "linux" zurück.
p5-Laufzeitumgebung
PetaPerl enthält ein experimentelles --p5-Flag, das eine eigenständige Perl-5-kompatible Laufzeitumgebung verwendet. Dies ist ein alternativer Ausführungsmodus, nicht der Standard.
Versionsangabe
$] meldet 5.042000 und $^V meldet v5.42.0. Code, der diese Werte prüft, sieht PetaPerl als Perl-5.42-Interpreter.
Nicht implementierte Funktionen
tie/tied/untie
Der tie-Mechanismus wird geparst und die Funktionen existieren, aber sie sind Stubs, die undef zurückgeben. Gebundene Variablen führen keine Dispatch-Aufrufe an die Tie-Klassenmethoden aus.
Module, die auf tie angewiesen sind (z. B. Tie::File, Tie::Hash::NamedCapture-Interna), funktionieren nicht korrekt.
Formate
format/write/formline werden geparst, aber das Format-Ausgabesystem ist nicht implementiert. write und formline sind Stubs. Verwenden Sie stattdessen printf/sprintf.
Lvalue-Unterprogramme
Das :lvalue-Attribut wird geparst, aber nicht erzwungen. Unterprogramme, die mit :lvalue deklariert sind, verhalten sich wie normale Unterprogramme.
Internals::SvREADONLY
Nicht implementiert. Dies betrifft Module, die auf das Markieren von Skalaren als schreibgeschützt angewiesen sind (charnames, unicore).
Einige Pragmas
| Pragma | Status |
|---|---|
strict | Geparst, teilweise erzwungen |
warnings | Geparst, teilweise Warnungskategorien |
utf8 | Geparst, Strings sind intern immer UTF-8 |
constant | Voll funktionsfähig |
base/parent | Voll funktionsfähig |
Carp | Voll funktionsfähig |
overload | Teilweise funktionsfähig |
re | Teilweise funktionsfähig |
Kommandozeilenoptionen
Diese perl5-Optionen werden nicht unterstützt:
| Option | Beschreibung |
|---|---|
-I | Include-Pfad hinzufügen (verwenden Sie stattdessen PERL5LIB) |
-M / -m | Modul von der Kommandozeile laden |
-n / -p | Implizite Eingabeschleife |
-l | Automatische Zeilenendebehandlung |
-a / -F | Autosplit-Modus |
-i | In-Place-Bearbeitung |
-x | Skript aus Nachricht extrahieren |
-T | Taint-Modus |
Bekannte Einschränkungen
Regex-Engine
PetaPerls Regex-Engine besteht 99,3 % von perl5s re_tests (1959/1972). Bekannte Lücken:
- Selbstreferentielle Captures (z. B.
(a\1)) — 3 Tests localin(?{code})-Blöcken — 2 Tests- Mehrzeichen-Unicode-Faltung (Case Folding) — 2 Tests
- Rückreferenzen in Branch-Reset-Gruppen — 5 Tests
- Sonderfälle bei String-Interpolation — 1 Test
Modulkompatibilität
| Modul | Problem |
|---|---|
| Test2::API | use Module(\$lexical) verliert lexikalische Seiteneffekte zur Kompilierzeit |
| Module::CoreList | Zeitüberschreitung (>20s) wegen enormer Hash-Literale |
| charnames/unicore | Hängt von Internals::SvREADONLY ab |
| Pod::Simple::RTF | Sonderfall beim Flip-Flop-Operator |
Flip-Flop-Operator
Die Operatoren .. und ... im skalaren Kontext (Flip-Flop) haben Sonderfälle, in denen sich pperls Verhalten von perl5 unterscheidet. Die gängigsten Verwendungen funktionieren, aber komplexe Verkettungen können Fehler auslösen.
Filehandle-Heuristiken
print $x, "\n" interpretiert $x als mögliches Filehandle (entsprechend dem perl5-Verhalten). Für Variablen, die Filehandle-Referenzen enthalten, verwenden Sie die Blockform: print {$fh} "data\n".
Kompilierzeit-Moduleffekte
use Module(args) läuft in perl5 zur Kompilierzeit. Module, die lexikalische Seiteneffekte zur Kompilierzeit erzeugen (z. B. use Instance(\$INST)), funktionieren möglicherweise nicht, da pperls Laufzeitumgebung diese Effekte nicht erneut abspielt. Seiteneffekte auf Paketvariablen funktionieren korrekt.
Native Module
PetaPerl unterstützt kein XS (C-Erweiterungen). Stattdessen werden leistungskritische CPAN-Module nativ in Rust reimplementiert. Diese „nativen Module“ bieten dieselbe Perl-API wie ihre XS-Gegenstücke, integrieren sich aber direkt in PetaPerls Laufzeitumgebung — was JIT-Kompilierung, Auto-Parallelisierung und Null-FFI-Overhead ermöglicht.
Funktionsweise
Wenn Sie use List::Util qw(sum min max) schreiben, erkennt PetaPerls Modul-Lader, dass List::Util eine native Implementierung besitzt, und registriert Rust-Funktionszeiger direkt. Es gibt keinen Kompilierungsschritt, kein .so-Laden und keinen XS-Glue-Code.
Native Funktionen werden über NativeFn dispatcht — einen direkten Funktionszeiger mit O(1)-Aufruf-Overhead, identisch zu eingebauten Operatoren.
Verfügbare native Module
Kern-Hilfsmittel
| Modul | Funktionen | Hinweise |
|---|---|---|
| Scalar::Util | blessed, reftype, refaddr, weaken, isweak, looks_like_number, … | Vollständige API |
| List::Util | sum, min, max, first, any, all, none, reduce, … | MULTICALL-optimiert |
| Sub::Util | subname, set_subname | |
| Hash::Util | lock_keys, lock_hash, … |
Digest / Kryptographie
| Modul | Funktionen | Hinweise |
|---|---|---|
| Digest::MD5 | md5, md5_hex, md5_base64, OO-Schnittstelle | Vollständige API |
| Digest::SHA | sha1, sha256, sha512, OO-Schnittstelle | Vollständige API |
| MIME::Base64 | encode_base64, decode_base64 | |
| MIME::QuotedPrint | encode_qp, decode_qp |
Datei / System
| Modul | Funktionen | Hinweise |
|---|---|---|
| File::Basename | basename, dirname, fileparse | |
| File::Copy | copy, move | |
| File::Find | find, finddepth | |
| File::Glob | bsd_glob | |
| File::Path | make_path, remove_tree | |
| File::Spec | catdir, catfile, rel2abs, … | |
| File::Temp | tempfile, tempdir | OO + funktional |
| File::stat | stat (OO) | |
| Cwd | cwd, getcwd, abs_path | |
| Sys::Hostname | hostname |
I/O
| Modul | Funktionen | Hinweise |
|---|---|---|
| IO::File | OO-Dateihandle | |
| IO::Handle | OO-Handle-Basis | |
| IO::Dir | OO-Verzeichnishandle | |
| IO::Pipe | OO-Pipe-Handle | |
| IO::Select | select-Wrapper | |
| IO::Socket | OO-Socket-Handle | |
| IO::Seekable | seek/tell-Mixin | |
| FileHandle | Legacy-OO-Handle | |
| Socket | Socket-Primitive |
Daten / Kodierung
| Modul | Funktionen | Hinweise |
|---|---|---|
| Data::Dumper | Dumper | |
| Storable | freeze, thaw, nstore, retrieve | |
| Encode | encode, decode, find_encoding | |
| JSON::PP | encode_json, decode_json | Über reines Perl |
Numerik / Mathematik
| Modul | Funktionen | Hinweise |
|---|---|---|
| POSIX | floor, ceil, fmod, strtod, strftime, … | Teilmenge |
| Math::GMP | Ganzzahlarithmetik beliebiger Genauigkeit | Über Peta::FFI::GMP |
Build / Konfiguration
| Modul | Funktionen | Hinweise |
|---|---|---|
| Config | %Config-Hash | Build-Konfiguration |
| Fcntl | O_RDONLY, O_WRONLY, … | Konstanten automatisch geladen |
| Errno | ENOENT, EACCES, … | Konstanten automatisch geladen |
Introspektion
| Modul | Funktionen | Hinweise |
|---|---|---|
| B | Compiler-Backend-Introspektion | Minimal |
| PadWalker | peek_my, peek_our | |
| mro | get_linear_isa, set_mro | |
| version | Versionsobjekt-Verarbeitung | Vollständige OO-API |
Leistung
Native Module laufen mit derselben Geschwindigkeit wie eingebaute Operatoren, da sie denselben Dispatch-Mechanismus verwenden. Für Funktionen, die Blöcke entgegennehmen (first, any, all, reduce), verwendet PetaPerl MULTICALL-Optimierung, um den Overhead von Unterprogrammaufrufen pro Element zu vermeiden.
Benchmarks (vs. perl5 mit XS):
| Funktion | PetaPerl nativ | perl5 XS | Verhältnis |
|---|---|---|---|
| List::Util::sum | 1,7x schneller | Basislinie | |
| List::Util::min/max | 2,9x schneller | Basislinie | |
| List::Util::first | ~1x | Basislinie | MULTICALL-Parität |
Native Module hinzufügen
Siehe Dokumentationspipeline für Informationen darüber, wie die Dokumentation nativer Module aus dem Rust-Quellcode extrahiert wird.
Auto-FFI (Peta::FFI)
PetaPerl enthält eine eingebaute Foreign Function Interface, die es ermöglicht, C-Bibliotheksfunktionen direkt aus Perl aufzurufen — ohne XS zu schreiben, ohne C-Compiler, ohne jeglichen Build-Schritt. Dies ist eine PetaPerl-exklusive Funktion ohne perl5-Entsprechung.
Architektur
Das FFI-System besteht aus drei Schichten:
| Schicht | Modul | Zweck |
|---|---|---|
| 0 | Peta::FFI | Rohes FFI: dlopen + Aufruf mit Typsignaturen |
| 1 | Peta::FFI::Libc, ::UUID, ::GMP | Fertige Bindings für bestimmte Bibliotheken |
| 2 | Peta::FFI::scan() | Erkennung: verfügbare Systembibliotheken auflisten |
Schicht 0 verwendet libffi für den Dispatch — jede C-Funktionssignatur funktioniert, keine Code-Generierung erforderlich.
Schicht 0: Rohes FFI
dlopen / call / dlclose
use Peta::FFI qw(dlopen call dlclose);
my $lib = dlopen("libm.so.6");
my $result = call($lib, "sqrt", "(d)d", 2.0); # √2 = 1.4142...
print "sqrt(2) = $result\n";
dlclose($lib);
Typsignatur-Format
Die Signaturzeichenkette "(arg_types)return_type" verwendet einstellige Typcodes:
| Code | C-Typ | Perl-Zuordnung |
|---|---|---|
v | void | (kein Wert) |
i | int | IV |
l | long | IV |
L | unsigned long / size_t | UV |
d | double | NV |
f | float | NV |
p | const char* | String (Eingabe) |
P | void* (veränderbarer Puffer) | String (Ausgabe) |
Beispiele:
"(d)d"— ein double-Argument, gibt double zurück (z. B.sqrt)"(p)L"— ein String-Argument, gibt unsigned long zurück (z. B.strlen)"(ppi)i"— zwei Strings + int, gibt int zurück"()i"— keine Argumente, gibt int zurück (z. B.getpid)
Bibliothekserkennung
use Peta::FFI qw(scan);
my $libs = scan();
# Gibt Hashref zurück: { "libz.so.1" => "/usr/lib/libz.so.1", ... }
for my $soname (sort keys %$libs) {
print "$soname => $libs->{$soname}\n";
}
scan() ruft ldconfig -p auf, um alle auf dem System verfügbaren Shared Libraries aufzulisten.
Schicht 1: Fertige Bindings
Schicht-1-Module bieten Perl-native APIs für bestimmte C-Bibliotheken mit korrekter Argumentvalidierung, Rückgabewertkonvertierung und Fehlerbehandlung. Keine Typsignaturen nötig — einfach Funktionen aufrufen.
Peta::FFI::Libc
Direkte Bindings zu libc-Funktionen, nach Kategorie gruppiert:
Prozess:
getpid, getppid, getuid, getgid, geteuid, getegid
Strings:
strlen, strerror
Umgebung:
getenv, setenv, unsetenv
Mathematik:
abs, labs
System:
sleep, usleep, gethostname, uname
Dateioperationen:
access, unlink, rmdir, mkdir, chmod, chown
use Peta::FFI::Libc qw(getpid gethostname uname);
print "PID: ", getpid(), "\n";
print "Host: ", gethostname(), "\n";
my @info = uname();
print "OS: $info[0] $info[2] ($info[4])\n";
Peta::FFI::UUID
Bindings zu libuuid (muss auf dem System installiert sein):
use Peta::FFI::UUID qw(uuid_generate uuid_generate_random);
my $uuid = uuid_generate(); # z. B. "550e8400-e29b-41d4-a716-446655440000"
my $rand = uuid_generate_random(); # zufallsbasierte UUID
Funktionen: uuid_generate, uuid_generate_random, uuid_generate_time,
uuid_parse, uuid_is_null, uuid_unparse.
Falls libuuid nicht installiert ist, bricht use Peta::FFI::UUID mit einer Diagnosemeldung ab.
Peta::FFI::GMP (Math::GMP)
Ganzzahlarithmetik mit beliebiger Genauigkeit über libgmp. Integriert sich unter dem Klassennamen Math::GMP für Kompatibilität mit CPANs Math::GMP:
use Math::GMP;
my $big = Math::GMP->new("123456789012345678901234567890");
my $sum = $big + 42;
print "$sum\n";
GMP-Ganzzahlen sind heap-allozierte mpz_t-Werte, die als gesegnete Referenzen gespeichert werden. Operatorüberladung (+, -, *, /, %, **, <=>, "") funktioniert identisch zum XS-Modul.
Falls libgmp nicht installiert ist, bricht use Math::GMP mit einer Diagnosemeldung ab.
Wann FFI statt nativer Module verwenden
| Szenario | Empfehlung |
|---|---|
| Gängiges CPAN-Modul (List::Util usw.) | Natives Modul (bereits eingebaut) |
| Systembibliothek noch ohne Wrapper | Schicht-0-Rohes-FFI |
| Häufige Aufrufe derselben Bibliothek | Schicht-1-Bindings anfordern |
| Einmaliges Experiment | Schicht-0-Rohes-FFI |
Schicht-0-FFI hat pro Aufruf Overhead durch Argument-Marshalling und libffi-Dispatch. Schicht-1-Bindings rufen Rust/C direkt auf und sind schneller. Native Module sind am schnellsten — gleiche Geschwindigkeit wie eingebaute Operatoren.
JIT-Kompilierung
PetaPerl enthält einen JIT-Compiler (Just-In-Time), der auf Cranelift basiert — demselben Backend, das auch von Wasmtime verwendet wird. Der JIT kompiliert häufig ausgeführte Schleifen in nativen Maschinencode und liefert dabei Leistung, die perl5 um Größenordnungen übertreffen kann.
Was JIT-kompiliert wird
Der JIT zielt auf Schleifen ab — for und while — die Arithmetik- und Vergleichsoperationen enthalten. Im Einzelnen:
Ganzzahlige For-Schleifen
my $sum = 0;
for my $i (1..1_000_000) {
$sum += $i;
}
Der JIT erkennt das Ganzzahl-Akkumulator-Muster und kompiliert den Schleifenrumpf in nativen Code. Die Schleifenvariable ($i) und Akkumulatoren ($sum) werden in CPU-Registern gehalten.
While-Schleifen mit Gleitkomma-Arithmetik
while ($y < $max_y) {
my $cx = $x * $scale;
my $cy = $y * $scale;
# ... Berechnung ...
$y += $step;
}
Gleitkomma-Arithmetik (+, -, *, /), Vergleiche (<, >, <=, >=) und Kontrollfluss (if/last) innerhalb von While-Schleifen werden in native SSE/AVX-Instruktionen kompiliert.
Verschachtelte Schleifen
Der JIT verarbeitet verschachtelte Schleifen bis zu 5 Ebenen tief. Jede Verschachtelungsebene erzeugt ihren eigenen Satz von Cranelift-Basisblöcken mit korrekten Phi-Knoten-Verbindungen für Variablen, die zwischen den Ebenen fließen.
String-Operationen (über Extern Calls)
Der JIT unterstützt .= (Concat-Assign) und $x = "" (Leeren) auf String-Variablen durch externe Funktionsaufrufe aus JIT-kompiliertem Code zurück in die Rust-Laufzeitumgebung.
Was nicht JIT-kompiliert wird
- Unterprogrammaufrufe — der Overhead von Funktionsaufrufen dominiert, der JIT-Vorteil ist marginal
- Regex-Operationen — die Regex-Engine hat ihren eigenen Optimierungspfad
- I/O-Operationen — I/O-gebundener Code profitiert nicht vom JIT
- Komplexer Datenstrukturzugriff — Hash-/Array-Operationen mit dynamischen Schlüsseln
- String-intensive Berechnungen — String-Aufbau wird stattdessen durch die PvBuf-Optimierung behandelt
Code, der nicht JIT-kompiliert wird, läuft weiterhin auf dem Interpreter, der seine eigenen Schnellpfade für häufige Operationen besitzt.
Leistung
Ackermann-Funktion
Der Interpreter-Schnellpfad (nicht JIT) verarbeitet rekursive Ganzzahlarithmetik:
| Laufzeitumgebung | Zeit | Beschleunigung |
|---|---|---|
| perl5 | 630ms | 1,0x |
| pperl (Interpreter) | 14ms | 45x schneller |
Mandelbrot-Menge (1000x1000)
JIT-Kompilierung verschachtelter While-Schleifen mit Gleitkomma-Arithmetik:
| Laufzeitumgebung | Zeit | Beschleunigung |
|---|---|---|
| perl5 | 12.514ms | 1,0x |
| pperl (nur JIT) | 163ms | 76x schneller |
| pperl (JIT + parallel) | 29ms | 431x schneller |
Wie das erreicht wird
- Registerallokation: Schleifenvariablen in CPU-Registern statt auf dem Interpreter-Stack
- Typspezialisierung: Variablen, die nachweislich Ganzzahl oder Gleitkomma sind, verwenden direkt native Instruktionen
- Branch-Eliminierung: Konstante Bedingungen werden zur Kompilierzeit entfernt
- Kein Dispatch-Overhead: Nativer Code ersetzt die Interpreter-Dispatch-Schleife innerhalb JIT-kompilierter Bereiche vollständig
CLI-Steuerung
# Standard: JIT aktiviert
pperl script.pl
# JIT deaktivieren (nur Interpreter)
pperl --no-jit script.pl
Der Testrahmen läuft standardmäßig mit --no-jit, um die Korrektheit des Interpreters zu testen. JIT-spezifische Tests in t/62-jit/ überschreiben dies.
Architektur
Kompilierungspipeline
Schleife erkannt → Variablen und Typen analysieren → JIT-IR aufbauen
→ Über Cranelift kompilieren → Kompilierte Funktion cachen → Nativen Code ausführen
Caching
Kompilierte Funktionen werden anhand der enterloop-Op-ID in der Op-Arena gecacht. Ein CachedWhileLoop speichert:
- Den kompilierten nativen Funktionszeiger
- Variablen-Typinformationen (Gleitkomma vs. Ganzzahl vs. String)
- Einen konstanten String-Pool (für String-Operationen)
- Metadaten zur Eignung für parallele Ausführung
Nachfolgende Iterationen derselben Schleife verwenden die gecachte Kompilierung wieder.
JIT-IR
Der JIT verwendet eine eigene Zwischendarstellung (JitIr), die Perl-Operationen auf Cranelift-Operationen abbildet:
JitIr::WhileLoop { condition_ir, body_ir }— rekursiv für VerschachtelungJitIr::FloatVar/JitIr::IntVar— typisierter VariablenzugriffJitIr::BinOp— Arithmetik und VergleicheJitIr::ExitIfFalse/JitIr::ExitIfTrue— Schleifenaustritt undlast
Variablentypen
Der JIT verfolgt zwei Variablentypen:
JitType::F64— Gleitkommawerte in einem f64-PufferJitType::Ptr— String-Werte, die über Extern Calls an die Rust-Laufzeitumgebung zugegriffen werden
Variablen werden anhand ihres Verwendungsmusters im Schleifenrumpf typisiert. Variablen mit gemischten Typen fallen auf den Interpreter zurück.
Parallele Ausführung
PetaPerl parallelisiert geeignete Schleifen automatisch mit Rayon, einem Work-Stealing-Thread-Pool. In Kombination mit JIT-Kompilierung ermöglicht dies dramatische Beschleunigungen für rechenintensive Aufgaben.
Funktionsweise
Wenn PetaPerl eine parallelisierbare Schleife erkennt, geschieht Folgendes:
- Analyse des Schleifenrumpfes auf Seiteneffekte und gemeinsam veränderlichen Zustand
- Identifikation von Reduktionsvariablen (Akkumulatoren wie
$sum += ...) - Verteilung der Iterationen über Threads mittels Rayons Work-Stealing-Scheduler
- Zusammenführung der Ergebnisse unter Verwendung der erkannten Reduktionsoperationen
Jeder Thread erhält seine eigene Kopie der schleifen-lokalen Variablen. Reduktionsvariablen werden nach Abschluss aller Threads zusammengeführt.
Was parallelisiert wird
JIT-kompilierte While-Schleifen
Wenn der JIT eine While-Schleife kompiliert und die Analyse Folgendes erkennt:
- Eine Zählvariable mit bekannten Grenzen
- Reduktionsvariablen (reines Akkumulationsmuster)
- Keine I/O-Operationen oder Seiteneffekte im Schleifenrumpf
Dann wird der Schleifenrumpf einmal kompiliert und parallel über mehrere Threads ausgeführt.
Eingebaute Funktionen
map und grep mit reinen Callbacks können parallel ausgeführt werden:
my @results = map { expensive_computation($_) } @large_array;
my @filtered = grep { complex_test($_) } @large_array;
Voraussetzungen für Parallelisierung:
- Keine Seiteneffekte im Callback
- Kein gemeinsam veränderlicher Zustand
- Sammlungsgröße über dem Parallelisierungs-Schwellenwert
CLI-Steuerung
# Standard: Parallelisierung aktiviert
pperl script.pl
# Parallelisierung deaktivieren
pperl --no-parallel script.pl
# Explizit aktivieren (Standard)
pperl --parallel script.pl
# Thread-Anzahl festlegen (Standard: Anzahl der CPU-Kerne)
pperl --threads=4 script.pl
# Minimale Sammlungsgröße für Parallelisierung festlegen
pperl --parallel-threshold=1000 script.pl
Der Testrahmen läuft standardmäßig mit --no-parallel, um deterministische Testausgaben sicherzustellen.
Leistung
Mandelbrot-Menge (1000x1000)
| Modus | Zeit | vs. perl5 |
|---|---|---|
| perl5 | 12.514ms | 1,0x |
| pperl Interpreter | ~3.500ms | 3,6x schneller |
| pperl JIT | 163ms | 76x schneller |
| pperl JIT + parallel (8 Threads) | 29ms | 431x schneller |
Skalierung
Der Work-Stealing-Scheduler liefert nahezu lineare Skalierung für trivial parallelisierbare Aufgaben:
| Threads | Mandelbrot 4000x4000 | Skalierung |
|---|---|---|
| 1 | Basislinie | 1,0x |
| 2 | ~50% Zeit | ~1,9x |
| 4 | ~25% Zeit | ~3,8x |
| 8 | ~13% Zeit | ~5,2x |
Die Skalierung ist sublinear aufgrund von Speicherbandbreite, Cache-Effekten und Reduktions-Overhead.
Einschränkungen
String-Operationen
String-Operationen (.= Concat, String-Aufbau) werden nicht parallelisiert. Die JIT-String-Unterstützung verwendet Extern Calls zurück in die Rust-Laufzeitumgebung, die veränderlichen Zugriff auf gemeinsamen Zustand erfordert. Wenn der JIT String-Variablen in einer Schleife erkennt, wird die parallele Ausführung deaktiviert.
Seiteneffekt-Erkennung
Der Parallelisierungs-Analysator ist konservativ. Jeder der folgenden Punkte schließt eine Schleife aus:
- I/O-Operationen (
print,open, Datei-Lesezugriffe) - Schreiben globaler Variablen
- Unterprogrammaufrufe (sofern nicht als rein nachgewiesen)
- Regex-Operationen mit Seiteneffekten (
s///)
Falsch-Negative (verpasste Parallelisierungsmöglichkeiten) sind sicher — die Schleife wird einfach sequenziell ausgeführt. Falsch-Positive (fehlerhafte Parallelisierung) wären Fehler.
Determinismus
Parallele Ausführung kann die Reihenfolge von Seiteneffekten verändern. Aus diesem Grund wird Parallelisierung nur angewendet, wenn die Analyse beweist, dass der Schleifenrumpf frei von beobachtbaren Seiteneffekten ist.
Die Ausgabereihenfolge wird bei map und grep beibehalten — das Ergebnis-Array behält dieselbe Elementreihenfolge wie bei sequenzieller Ausführung.
Wie die Reduktionserkennung funktioniert
Der Analysator identifiziert Reduktionsvariablen, indem er nach Akkumulationsmustern sucht und Rücksetzmuster davon abzieht:
# Als Reduktion erkannt: $sum akkumuliert, wird nie in der Schleife zurückgesetzt
my $sum = 0;
for my $x (@data) {
$sum += $x;
}
# KEINE Reduktion: $temp wird bei jeder Iteration zurückgesetzt
for my $x (@data) {
my $temp = $x * 2; # Rücksetzung (my-Deklaration)
$sum += $temp; # $sum ist weiterhin eine Reduktion
}
Die Formel: Reduktionen = Akkumulationen - Rücksetzungen. Dies verhindert Falsch-Positive, bei denen eine Variable innerhalb des Schleifenrumpfes sowohl akkumuliert als auch zurückgesetzt wird.
PetaPerl Architekturübersicht
Ein umfassender Leitfaden für Entwickler zum Verständnis und zur Mitarbeit an PetaPerl.
Inhaltsverzeichnis
- Vision und Ziele
- Architektur auf hoher Ebene
- Ausführungspipeline
- Quellcodestruktur
- Zentrale Datenstrukturen
- Der Op-Baum
- Wertesystem
- Laufzeitumgebung und Interpreter
- Parser und Codegenerierung
- Testinfrastruktur
- Entwicklungsablauf
- Wesentliche Architekturentscheidungen
- Einstieg für Mitarbeiter
Vision und Ziele
PetaPerl ist keine Portierung des Perl-5-C-Interpreters nach Rust. Es ist eine Perl-Laufzeitumgebung der nächsten Generation mit drei strategischen Alleinstellungsmerkmalen:
| Ziel | Beschreibung |
|---|---|
| Auto-Parallelisierung | Automatische parallele map-, grep-, for-Schleifen mittels Rayon Work-Stealing |
| JIT-Kompilierung | V8-Klasse-Performance über Cranelift (gestufte Kompilierung) |
| Pure-Perl-Tauglichkeit | Schnell genug, damit XS optional und nicht mehr zwingend erforderlich ist |
Ziel: Pure Perl wettbewerbsfähig mit XS-beschleunigtem Code machen.
Stufenweise Umsetzung
Phase 1: Walking [ERLEDIGT] - Minimaler Interpreter (print "Hello")
Phase 2: Running [ERLEDIGT] - Variablen, Operatoren, Interpolation
Phase 3: Climbing [ERLEDIGT] - Arrays, Hashes, Subs, Kontrollfluss
Phase 4: Leaping [ERLEDIGT] - Parallele Ausführung (Rayon)
Phase 5: Flying [ERLEDIGT] - JIT-Kompilierung (Cranelift)
Rahmenbedingungen
| Einschränkung | Wert | Begründung |
|---|---|---|
| Plattform | Nur Linux | Einfachheit; beliebige Architektur |
| Perl-Version | 5.42+ stable | Nicht blead (5.43.x) |
| Lizenz | Proprietär | Geschäftsentscheidung |
Architektur auf hoher Ebene
┌─────────────────────────────────────────────────────────────────┐
│ PetaPerl Runtime │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Parser │ │ Codegen │ │ OpArena │ │
│ │ (lexer.rs) │──▶│ (codegen.rs) │──▶│ (tree.rs) │ │
│ │ (parser.rs) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Interpreter │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │ │
│ │ │ Stack │ │ Pad │ │ PackageRegistry │ │ │
│ │ │ (Werte) │ │ (Lexikale) │ │ (Globale) │ │ │
│ │ └────────────┘ └────────────┘ └────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────────┐ │ │
│ │ │ PP-Dispatch (27 Module) │ │ │
│ │ │ control | strings | io | arrays | hashes | ... │ │ │
│ │ └────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Values │ │ Regex │ │ I/O │ │
│ │ Sv/Av/Hv/Cv │ │ Engine │ │ Layer │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Ausführungspipeline
Vom Quellcode zur Ausgabe
┌─────────────────┐
│ Perl-Quellcode │ my $x = 10;
│ │ print $x + 5;
└────────┬────────┘
│
▼
┌─────────────────┐
│ Lexer │ Tokens: [MY, SCALAR($x), ASSIGN, INT(10), ...]
│ (lexer.rs) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Parser │ AST: Stmt[VarDecl($x, 10), Print(BinOp(+, $x, 5))]
│ (parser.rs) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Codegen │ OpArena mit verknüpften Op-Knoten
│ (codegen.rs) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Interpreter │ Ops sequenziell ausführen
│(interpreter.rs) │ Stapelbasierte Wertübergabe
└────────┬────────┘
│
▼
┌─────────────────┐
│ Ausgabe │ "15"
└─────────────────┘
Interpreterschleife
#![allow(unused)]
fn main() {
fn run(&mut self) -> RuntimeResult<Vec<Sv>> {
let mut current = self.arena.first();
while current.is_some() {
let op = self.arena.get(current)?;
let next = self.dispatch(op)?; // Op ausführen, nächsten holen
current = next;
}
Ok(self.stack.drain())
}
}
Quellcodestruktur
peta-perl/src/
│
├── main.rs # CLI-Einstiegspunkt (729 Zeilen)
├── lib.rs # Bibliothekswurzel, öffentliche Exporte
│
├── op/ # Op-Baum-Schicht
│ ├── mod.rs # Öffentliche API
│ ├── tree.rs # OpArena, Op-Varianten (51KB)
│ ├── flags.rs # OpFlags, OpPrivate
│ └── opcode/ # OpCode-Definitionen
│ ├── mod.rs # ~200 Opcodes
│ ├── category.rs # 22 Kategorien
│ └── encoding.rs # 6-Bit-Kategorie + 10-Bit-Op
│
├── value/ # Werttypen (172KB gesamt)
│ ├── mod.rs # Öffentliche API
│ ├── sv.rs # Skalarer Wert (53KB) - KERNTYP
│ ├── av.rs # Array-Wert (23KB)
│ ├── hv.rs # Hash-Wert (24KB)
│ ├── cv.rs # Code-Wert (24KB)
│ ├── glob.rs # Typeglobs, Stash (19KB)
│ └── aliased_av.rs # Unterstützung für @_-Aliasing
│
├── runtime/ # Ausführungsschicht (177KB)
│ ├── mod.rs # Öffentliche API
│ ├── interpreter.rs # Hauptschleife (83KB) - KERN
│ ├── stack.rs # Wertstapel (11KB)
│ ├── pad.rs # Lexikalische Variablen (15KB)
│ ├── error.rs # RuntimeError-Typen
│ └── pp/ # PP-Operationen (~1MB über 27 Dateien)
│ ├── dispatch.rs # Kategoriebasierter Dispatcher
│ ├── helpers.rs # Häufige Muster
│ ├── control.rs # if/while/for/foreach
│ ├── strings.rs # Zeichenkettenoperationen (88KB)
│ ├── sub.rs # Subroutinenaufrufe (90KB)
│ ├── io.rs # Print/open/close (56KB)
│ ├── arrays.rs # Array-Operationen (64KB)
│ ├── regex.rs # Musterabgleich (63KB)
│ └── ... # 18+ weitere Module
│
├── parser/ # Nativer Parser (810KB)
│ ├── mod.rs # Öffentliche API
│ ├── lexer.rs # Tokenizer (85KB)
│ ├── parser.rs # Rekursiver Abstieg (257KB)
│ ├── ast.rs # AST-Definitionen (16KB)
│ ├── codegen.rs # AST → OpArena (401KB) - GRÖẞTE DATEI
│ ├── symtab.rs # Symboltabelle
│ └── token.rs # Tokentypen
│
├── regex/ # Regex-Engine (268KB)
│ ├── mod.rs # Öffentliche API
│ ├── compile.rs # Musterkompilierer (78KB)
│ ├── exec.rs # Backtracking-Ausführung (115KB)
│ └── types.rs # Kerntypen (43KB)
│
├── loader/ # Älteres Op-Laden
│ ├── parser.rs # B::Concise-Parser
│ ├── json.rs # B::PetaPerl JSON
│ └── builder.rs # Programmatische Konstruktion
│
└── io/ # I/O-Schicht (zukünftig)
└── mod.rs
Codezeilen nach Modul
| Modul | Zeilen | Zweck |
|---|---|---|
| parser/ | ~19.700 | Lexing, Parsing, Codegenerierung |
| runtime/pp/ | ~45.500 | Implementierungen der PP-Operationen |
| value/ | ~8.500 | Werttypen (Sv, Av, Hv, Cv) |
| op/ | ~4.000 | Op-Baum-Strukturen |
| regex/ | ~6.500 | Regex-Kompilierung und -Ausführung |
| Gesamt | ~72.000 |
Zentrale Datenstrukturen
Modulabhängigkeiten
┌─────────────┐
│ main.rs │
└──────┬──────┘
│
┌──────────────┼──────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ parser │ │ loader │ │ runtime │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────────────┼──────────────┘
│
▼
┌──────────────┐
│ op::tree │◀───────────┐
│ OpArena │ │
└──────┬───────┘ │
│ │
┌────────────┼────────────┐ │
│ │ │ │
▼ ▼ ▼ │
┌──────────┐ ┌───────────┐ ┌──────────┐ │
│ value │ │ runtime │ │ regex │───┘
│ Sv/Av/Hv │ │Interpreter│ │ Engine │
└──────────┘ └───────────┘ └──────────┘
Der Op-Baum
Arena-Allokation
#![allow(unused)]
fn main() {
pub struct OpArena {
ops: Vec<Op>, // Alle Ops zusammenhängend gespeichert
svs: Vec<Sv>, // Konstantenpool
root: OpId, // Einstiegspunkt
subs: HashMap<String, Cv>, // Definierte Subroutinen
}
pub struct OpId(u32); // Typsicherer Index (NONE = u32::MAX)
}
Warum Arena?
- Cache-Lokalität: Ops zusammenhängend gespeichert
- Kein Referenzzählungs-Overhead
- JIT-freundlich: linearer Durchlauf
- Serialisierbar: Op-Bäume können gespeichert/geladen werden
Op-Varianten
#![allow(unused)]
fn main() {
pub enum Op {
Base(OpData), // Basis-Op
UnOp(UnOp), // Unär: negate, defined, not
BinOp(BinOp), // Binär: add, concat, eq
ListOp(ListOp), // Liste: push, print, map
PmOp(PmOp), // Musterabgleich
SvOp(SvOp), // Konstanter Wert
PadOp(PadOp), // Zugriff auf lexikalische Variablen
Loop(Loop), // Schleifenkonstrukte
Cop(Cop), // Kontextoperationen (Zeilen-/Dateiinfo)
MulticoncatOp(...), // Optimierte Zeichenkettenverkettung
MultiderefOp(...), // Optimierte Dereferenzierung
}
}
Op-Ausführungsverkettung
Jeder Op hat ZWEI Ordnungen:
AUSFÜHRUNGSREIHENFOLGE (op_next): Lineare Sequenz für den Interpreter
enter → nextstate → add → print → leave
BAUMORDNUNG (op_first/sibling): Verschachtelte Geltungsbereichsstruktur
enter
└─ nextstate
└─ print
└─ add
├─ const(5)
└─ padsv($x)
OpCode-Kategorien (22 insgesamt)
Bits 15-10: Kategorie (6 Bits)
Bits 9-0: Op (10 Bits)
Kategorien:
0x01 META null, wantarray, pushmark
0x03 CONTEXT enter, leave, nextstate
0x04 CONTROL if, while, for, return
0x05 LOGIC and, or, not, xor
0x07 COMPARE ==, eq, <, lt, cmp, <=>
0x09 ASSIGN =, +=, list assign
0x11 ARITHMETIC +, -, *, /, %
0x13 STRINGS concat, substr, length
0x14 ARRAYS push, pop, shift, splice
0x15 HASHES keys, values, each, delete
0x17 SUBROUTINES entersub, leavesub, return
0x1F IO print, say, open, close
0x20 REGEX m//, s///, tr///
...
Wertesystem
Skalarer Wert (Sv)
#![allow(unused)]
fn main() {
pub enum Sv {
Undef, // undef
Iv(i64), // Ganzzahl
Uv(u64), // Vorzeichenlose Ganzzahl
Nv(f64), // Gleitkommazahl
Pv(Arc<str>), // Zeichenkette
Rv(RvInner), // Referenz
Av(Av), // Array
Hv(Hv), // Hash
Cv(Cv), // Subroutine
Alias(Arc<SvCell>), // Für @_-Aliasing
ArrayElemAlias(Av, i64), // Array-Element-Alias
Regex(Arc<CompiledRegex>), // Kompiliertes Muster
}
}
Speicherstrategie: Arc + RefCell
┌─────────────────────────────────────────────────────────┐
│ Sv::Pv(Arc<str>) │
│ └─ Arc ermöglicht günstiges Klonen (nur Refcount++) │
│ └─ Thread-sicher für zukünftige parallele Ausführung │
│ └─ Unveränderlicher Inhalt (Zeichenkettendaten) │
├─────────────────────────────────────────────────────────┤
│ Av / Hv │
│ └─ Arc<RefCell<Inner>> │
│ └─ Arc: geteilte Eigentümerschaft │
│ └─ RefCell: innere Veränderbarkeit (Single-Threaded) │
├─────────────────────────────────────────────────────────┤
│ SvCell (für @_-Aliasing) │
│ └─ Arc<RefCell<Sv>> │
│ └─ Mehrere Referenzen auf denselben Speicher │
│ └─ Ermöglicht: sub foo { $_[0] = 10 } # ändert Aufrufer │
└─────────────────────────────────────────────────────────┘
Typhierarchie
Sv (Skalar)
├─ Undef
├─ Zahlen: Iv, Uv, Nv
├─ Zeichenkette: Pv(Arc<str>)
├─ Referenz: Rv(RvInner)
│ └─ value: Arc<Sv>
│ └─ blessed: Option<Arc<str>>
├─ Sammlungen:
│ ├─ Av (Array)
│ │ └─ elements: Vec<Sv>
│ │ └─ blessed: Option<Arc<str>>
│ └─ Hv (Hash)
│ └─ entries: HashMap<Arc<str>, Sv>
│ └─ blessed: Option<Arc<str>>
├─ Cv (Code-Wert)
│ └─ start_op: OpId
│ └─ prototype: Option<String>
│ └─ captures: Vec<(u32, Arc<SvCell>)>
└─ Aliase (für @_):
└─ Alias, ArrayElemAlias, AliasedAv
Laufzeitumgebung und Interpreter
Duale Laufzeitarchitektur
PetaPerl liefert zwei unabhängige Laufzeitmaschinen, die sich denselben Parser, Op-Baum und die native Modulinfrastruktur teilen. Die Laufzeitumgebung wird beim Aufruf über Kommandozeilenparameter ausgewählt.
| PP-Laufzeit (Standard) | P5-Laufzeit (--p5) | |
|---|---|---|
| Entwurfsphilosophie | Eigenständige Rust-Implementierung | 1:1-Transliteration des Perl5-C-Interpreters |
| Wertdarstellung | Sv-Enum (32 Bytes, getaggte Union) | Sv(u64) NaN-geboxed, nachempfunden von perl5s SV* |
| Speichersicherheit | Durchgehend sicheres Rust | Umfangreicher unsafe-Code — Rohzeiger, manuelle Referenzzählung |
| Performance | ~25% von perl5 (ohne JIT) | Auf Augenhöhe mit perl5 |
| JIT-Kompilierung | Cranelift JIT für heiße Schleifen | Nicht verfügbar |
| Auto-Parallelisierung | Rayon Work-Stealing für parallele Schleifen | Nicht verfügbar |
| Kompatibilität | Hoch — deckt die üblichen Fälle ab | Sehr hoch — bildet perl5s subtile Randfälle nach |
| Native Module | Alle 47 Module | 17 Module (wachsend) |
PP-Laufzeit (src/runtime/) ist die primäre Engine. Sie verwendet idiomatisches, sicheres Rust mit einem sauberen Wertesystem (Sv-Enum mit Arc-basiertem COW für Zeichenketten). Die Performance liegt im Basis-Interpreter hinter perl5, aber der JIT-Compiler und die Auto-Parallelisierung kompensieren dies für heiße Codepfade mehr als ausreichend — Mandelbrot-Benchmarks laufen 76x-259x schneller als perl5.
P5-Laufzeit (src/p5/) ist eine experimentelle zweite Engine, die hinzugefügt wurde, um grundlegende Performance-Parität mit perl5 für Code zu erreichen, der nicht vom JIT profitiert. Sie spiegelt perl5s C-Implementierung nahezu Zeile für Zeile in Rust wider: NaN-geboxte SVs, manuelle Referenzzählung über rc_inc()/rc_dec(), direkte Zeigermanipulation. Der Kompromiss ist allgegenwärtiger unsafe-Code, aber die Engine reproduziert perl5s Performance-Eigenschaften und subtile Verhaltensdetails originalgetreu.
Beide Laufzeiten teilen sich:
- Den nativen Rust-Parser und Codegen (
src/parser/) - Die Op-Baum-Darstellung (
src/op/) - Native Modulimplementierungen (
src/native/) — jedes Modul stellt einepp.rs(PP-Laufzeit) und optional einep5.rs(P5-Laufzeit) bereit - Die Testinfrastruktur (
t/TEST)
Interpreterzustand
#![allow(unused)]
fn main() {
pub struct Interpreter {
// Ausführungskontext
arena: Arc<OpArena>, // Unveränderlicher Op-Baum (geteilt)
stack: Stack, // Wertstapel
pad: Pad, // Lexikalische Variablen
// Kontrollfluss
loop_stack: Vec<LoopContext>, // Für next/last/redo
call_stack: Vec<CallFrame>, // Subroutinenaufrufe
iter_stack: Vec<IterContext>, // Foreach-Iteration
// Map/Grep-Zustand
map_stack: Vec<MapGrepContext>,
// Zuweisungsverfolgung
lvalue_targets: Vec<u32>,
last_helem_target: Option<(Hv, Sv)>,
last_aelem_target: Option<(Av, i64)>,
// Sicherungsstapel (für `local`)
save_stack: Vec<SaveEntry>,
// Ausnahmebehandlung
try_stack: Vec<TryContext>,
// Globaler Zustand
packages: PackageRegistry, // Symboltabellen
current_package: String,
current_file: String,
current_line: u32,
}
}
Stapelmaschine
┌─────────────────────────────────────────┐
│ Wertstapel │
├─────────────────────────────────────────┤
│ [0] Sv::Integer(5) │
│ [1] Sv::Integer(3) │
│ [2] Sv::String("result") │
│ [3] MARK ◀── Listenkontextgrenze │
│ [4] Sv::Integer(1) │
│ [5] Sv::Integer(2) │
└─────────────────────────────────────────┘
Operationen:
push(sv) - Wert oben ablegen
pop() - Oberstes Element entfernen und zurückgeben
pushmark() - Listenkontextanfang markieren
popmark() - Werte seit der Markierung zurückgeben
PP-Dispatch (zweistufig)
execute_op(opcode)
│
▼
dispatch_by_category(opcode.category())
│
├─ META ──────────▶ dispatch_meta()
├─ ARITHMETIC ────▶ dispatch_arithmetic()
├─ STRINGS ───────▶ dispatch_strings()
├─ CONTROL ───────▶ dispatch_control()
├─ IO ────────────▶ dispatch_io()
└─ ... (22 Kategorien)
│
▼
spezifische pp_*-Funktion
Beispiel: Binäroperator
#![allow(unused)]
fn main() {
// In pp/helpers.rs
fn pp_binop<F>(&mut self, op: F) -> RuntimeResult<()>
where
F: Fn(&Sv, &Sv) -> Sv,
{
let b = self.stack.pop()?; // Rechten Operanden holen
let a = self.stack.pop()?; // Linken Operanden holen
let result = op(&a, &b); // Operation anwenden
self.stack.push(result); // Ergebnis ablegen
Ok(())
}
// Verwendung für Addition:
self.pp_binop(|a, b| {
Sv::Nv(a.to_number() + b.to_number())
})
}
Parser und Codegenerierung
Dreistufige Parserstrategie
Stufe 1 (Aktuell - Entwicklung):
perl -MO=Concise → Op-Baum-Dump → PetaPerl interpretiert
Stufe 2 (Geplant - Produktion):
FFI zu libperl → Op-Baum extrahieren → PetaPerl interpretiert/JIT-kompiliert
Stufe 3 (Langfristig):
Nativer Rust-Parser → vollständige Unabhängigkeit
Begründung: „Only perl can parse Perl“ — Perls Grammatik ist kontextsensitiv. Der Tokenizer konsultiert die Symboltabelle während des Lexings. Unser Mehrwert liegt in der Laufzeit (Parallelismus, JIT), nicht im Parsing.
Pipeline des nativen Parsers
Quellcode
│
▼
┌─────────────────────────────────────┐
│ Lexer (lexer.rs - 85KB) │
│ - Kontextabhängige Tokenisierung │
│ - Heredocs, Interpolation, Regex │
└─────────────┬───────────────────────┘
│ Token-Strom
▼
┌─────────────────────────────────────┐
│ Parser (parser.rs - 257KB) │
│ - Rekursiver Abstieg │
│ - Pratt-Präzedenzverfahren │
│ - Vollständige Perl-Grammatik │
└─────────────┬───────────────────────┘
│ AST
▼
┌─────────────────────────────────────┐
│ Codegen (codegen.rs - 401KB) │
│ - AST → OpArena-Absenkung │
│ - Geltungsbereichsverfolgung │
│ - Closure-Capture-Analyse │
└─────────────┬───────────────────────┘
│ OpArena
▼
Interpreter
AST-Struktur
#![allow(unused)]
fn main() {
pub struct Ast {
statements: Vec<Stmt>,
subs: Vec<SubDef>,
packages: Vec<String>,
}
pub struct Stmt {
kind: StmtKind,
span: Span,
}
pub enum StmtKind {
Expression(Expr),
VarDecl { name: String, init: Option<Expr> },
If { cond: Expr, then: Block, else_: Option<Block> },
While { cond: Expr, body: Block },
For { init: Stmt, cond: Expr, incr: Expr, body: Block },
Foreach { var: String, list: Expr, body: Block },
SubDecl(SubDef),
Return(Option<Expr>),
// ...
}
}
Testinfrastruktur
Test-Harness (t/TEST)
┌─────────────────────────────────────────────────────────┐
│ perl t/TEST --skip=bytecode,tier │
│ - Läuft unter Perl 5 (NICHT pperl) zur Sicherheit │
│ - Prozessisolation mit Ressourcenlimits │
│ - --skip=bytecode,tier erforderlich für schnelle │
│ Durchläufe (~30s) │
├─────────────────────────────────────────────────────────┤
│ Schutzmaßnahmen: │
│ - 30 Sekunden Zeitlimit pro Test │
│ - 512MB Speicherlimit │
│ - 60 Sekunden CPU-Limit │
│ - SIGTERM/SIGKILL bei Zeitüberschreitung │
├─────────────────────────────────────────────────────────┤
│ Funktionen: │
│ - TAP-Ausgabeparsing │
│ - Ergebnisverfolgung in .results/ │
│ - Regressionserkennung (--compare) │
│ - Ausführlicher Modus (-v) │
│ - Überwachungsmodus (--monitor) │
└─────────────────────────────────────────────────────────┘
Testverzeichnisstruktur
t/
├── TEST # Perl5-Harness-Skript
├── .results/ # Zeitgestempelte Ergebnisdateien
│
├── 00-base/ (14) # GATE-TESTS - müssen bestehen!
├── 01-parsing/ (12) # Literalparsing
├── 05-op/ (42) # Operatoren
├── 10-cmd/ (12) # Kontrollfluss
├── 15-sub/ (19) # Subroutinen
├── 20-data/ (37) # Datenstrukturen
├── 22-unicode/ (80) # Unicode-Unterstützung
├── 25-regex/ (12) # Reguläre Ausdrücke
├── 30-special/ (3) # Spezialvariablen
├── 40-package/ (24) # Pakete & OOP
├── 45-builtin/ (111) # Eingebaute Funktionen
├── 50-context/ (6) # Kontextbehandlung
├── 55-io/ (67) # Ein-/Ausgabe
├── 60-parallel/ (4) # Parallelisierung
├── 65-jit/ (3) # JIT-Kandidaten
├── 70-complex/ (1) # Integrationstests
└── 99-debug/ (69) # Regressionstests
Testdateiformat (TAP)
# Testbeschreibung
# Adaptiert von: perl5-upstream/t/op/something.t
print "1..5\n"; # Plan: 5 Tests
# Test 1
print 1 + 1 == 2 ? "ok 1 - addition\n" : "not ok 1\n";
# Test 2
my $x = "hello";
print length($x) == 5 ? "ok 2 - length\n" : "not ok 2\n";
# ... weitere Tests
Nummerierungskonvention für Tests
Dateien verwenden 5er-Lücken für zukünftige Einfügungen:
005-print.t
010-say.t ← Lücke erlaubt 006, 007, 008, 009
015-binmode.t
020-open.t ← Lücke erlaubt 016, 017, 018, 019
...
Entwicklungsablauf
Sichere Befehle
| Befehl | Sicherheit | Zweck |
|---|---|---|
cargo build | SICHER | Kompilieren |
cargo test --lib | SICHER | Rust-Unit-Tests |
perl t/TEST --skip=bytecode,tier | SICHER | Integrationstests (~30s) |
perl -c file.pl | SICHER | Syntaxprüfung |
Verbotene Befehle
| Befehl | Gefahr | Grund |
|---|---|---|
cargo run -- file.pl | VERBOTEN | Kann unbegrenzt hängen |
echo 'x' | cargo run | VERBOTEN | Stdin + pperl = Hänger |
| Direkter pperl-Aufruf | VERBOTEN | Keine Ressourcenlimits |
Standardentwicklungszyklus
# 1. Bauen
cargo build --release
# 2. Tests ausführen (IMMER --skip für schnelle Durchläufe!)
cd peta-perl && perl t/TEST --skip=bytecode,tier
# 3. Änderungen am Rust-Code vornehmen
# 4. Neu bauen
cargo build --release
# 5. Mit Vergleich testen
perl t/TEST --skip=bytecode,tier --compare # Zeigt Regressionen/Verbesserungen
# 6. Bei Zufriedenheit committen
Einen neuen Test hinzufügen
# 1. Passendes Verzeichnis wählen
# 2. Nächste verfügbare Nummer mit 5er-Lücke wählen
# 3. Test im TAP-Format schreiben
# Beispiel: t/45-builtin/515-new-function.t
cat > t/45-builtin/515-new-function.t << 'EOF'
#!/usr/bin/env perl
# Test new_function() builtin
print "1..3\n";
print new_function(1) == 1 ? "ok 1\n" : "not ok 1\n";
print new_function(2) == 4 ? "ok 2\n" : "not ok 2\n";
print new_function(3) == 9 ? "ok 3\n" : "not ok 3\n";
EOF
# 4. Test ausführen
perl t/TEST --skip=bytecode,tier 45-builtin
Wesentliche Architekturentscheidungen
ADR-1: Arena-Allokation für Ops
Entscheidung: Ops in einem zusammenhängenden Vec<Op> mit indexbasierten Referenzen speichern.
Begründung:
- Cache-Lokalität beim Durchlauf
- Kein Referenzzählungs-Overhead
- JIT-freundlich (linearer Speicher)
- Einfache Serialisierung
ADR-2: Arc<str> für Zeichenketten
Entscheidung: Arc<str> für sämtliche Zeichenkettenspeicherung verwenden.
Begründung:
- Günstiges Klonen (nur Referenzzähler erhöhen)
- Thread-sicher für zukünftigen Parallelismus
- Interning-fähig (häufige Zeichenketten können geteilt werden)
ADR-3: Zweistufiges Dispatch
Entscheidung: Ops nach Kategorie und dann nach spezifischem Opcode dispatchen.
Begründung:
- Reduziert die Anzahl der Match-Arme pro Dispatch
- Ermöglicht Optimierungen auf Kategorieebene
- Saubere Modulstruktur
ADR-4: Cranelift für JIT (Zukunft)
Entscheidung: Cranelift verwenden, nicht LLVM oder dynasm-rs.
Begründung:
- Rust-nativ mit hervorragender API
- Produktionserprobt (Wasmtime)
- Gute Balance: schnelle Kompilierung + ordentliche Codequalität
ADR-5: Rayon für Parallelismus (Zukunft)
Entscheidung: Rayon Work-Stealing auf Schleifenebene verwenden.
Begründung:
- Kampferprobt, Rust-nativ
- Work-Stealing verteilt die Last automatisch
- Parallelismus auf Schleifenebene: 80% Nutzen, 20% Komplexität
Einstieg für Mitarbeiter
Schnellstart
# Klonen und bauen
git clone https://gl.petatech.eu/petatech/peta-perl.git
cd peta-perl
cargo build --release
# Tests ausführen (IMMER --skip für schnelle Durchläufe!)
perl t/TEST --skip=bytecode,tier
# Bestimmtes Testverzeichnis ausführen
perl t/TEST --skip=bytecode,tier 00-base
perl t/TEST --skip=bytecode,tier -v # Ausführlich (Fehler anzeigen)
Die Codebasis verstehen
-
Mit Werten beginnen (
src/value/sv.rs)- Verstehen, wie Perl-Werte dargestellt werden
- Schlüssel:
Sv-Enum und seine Varianten
-
Den Interpreter studieren (
src/runtime/interpreter.rs)- Der
run()-Methode folgen - Sehen, wie Ops dispatcht werden
- Der
-
Ein PP-Modul erkunden (z.B.
src/runtime/pp/strings.rs)- Sehen, wie spezifische Operationen implementiert sind
- Muster: Operanden holen, berechnen, Ergebnis ablegen
-
Eine Testdatei lesen (z.B.
t/05-op/005-arith.t)- Erwartetes Perl-Verhalten verstehen
- Als Spezifikation für die Implementierung verwenden
Häufige Aufgaben
Eine neue eingebaute Funktion implementieren:
- Opcode zu
src/op/opcode/mod.rshinzufügen - Dispatch-Fall im passenden
pp/*.rs-Modul ergänzen - Die PP-Funktion implementieren
- Tests zu
t/45-builtin/hinzufügen
Einen fehlschlagenden Test beheben:
- Test ausführlich ausführen:
perl t/TEST --skip=bytecode,tier -v 45-builtin - Fehlschlagende Prüfung identifizieren
- Durch den Interpreter mit
--trace-Flag nachverfolgen - Implementierung korrigieren
- Verifizieren mit
perl t/TEST --skip=bytecode,tier --compare
Eine neue Testdatei hinzufügen:
- TAP-Format einhalten
- 5er-Lücken-Nummerierung verwenden
- Tests fokussiert halten (10-30 Prüfungen)
- Randfälle testen
Wichtige Dateien
| Datei | Zweck | Lesen bei |
|---|---|---|
src/value/sv.rs | Skalare Werte | Typenverständnis |
src/runtime/interpreter.rs | Hauptschleife | Ausführungsverständnis |
src/runtime/pp/dispatch.rs | Op-Routing | Neue Ops hinzufügen |
src/parser/codegen.rs | Codegenerierung | Parser-Probleme |
t/TEST | Test-Harness | Testinfrastruktur |
docs/TODO.md | Aufgabenliste | Arbeit finden |
Hilfe erhalten
CLAUDE.mdim übergeordneten Verzeichnis für Projektrichtlinien lesendocs/TODO.mdfür aktuelle Prioritäten prüfent/99-debug/für Regressionstest-Muster ansehen- Letzte Commits für Code-Stil-Beispiele durchsehen
Anhang: Glossar
| Begriff | Definition |
|---|---|
| Sv | Skalarer Wert — beliebiger einzelner Perl-Wert |
| Av | Array-Wert — Perl-Array |
| Hv | Hash-Wert — Perl-Hash |
| Cv | Code-Wert — Perl-Subroutine |
| Op | Operation — einzelne Instruktion im Op-Baum |
| OpId | Index in die OpArena (u32-Wrapper) |
| OpArena | Behälter für alle Ops und Konstanten |
| PP | Push-Pop — Funktion, die einen Opcode implementiert |
| Pad | Speicher für lexikalische Variablen eines Geltungsbereichs |
| Stash | Symboltabelle eines Pakets |
| TAP | Test Anything Protocol — Ausgabeformat für Tests |
| Glob | Typeglob — Eintrag in der Symboltabelle |
Fehlermeldungen
PetaPerl ist proprietäre Software. Es gibt keinen Open-Source-Beitragsprozess.
Fehlerberichte
Bei der Meldung eines Fehlers bitte folgende Angaben beifügen:
- Minimaler Reproduzierer — das kürzeste Perl-Skript, das das Problem demonstriert
- Erwartetes Verhalten — was perl5 5.42 für dasselbe Skript ausgibt
- Tatsächliches Verhalten — was pperl ausgibt (einschließlich stderr, falls relevant)
- pperl-Version — Ausgabe von
pperl -v - CLI-Flags — alle nicht-standardmäßigen Flags (
--no-jit,--no-parallel, usw.)
Nützliche Diagnosebefehle
# pperl- und perl5-Ausgabe direkt vergleichen
perl script.pl > /tmp/perl5.out 2>&1
pperl script.pl > /tmp/pperl.out 2>&1
diff /tmp/perl5.out /tmp/pperl.out
# Prüfen, ob JIT die Ursache ist
pperl --no-jit script.pl
# Prüfen, ob Parallelisierung die Ursache ist
pperl --no-parallel script.pl
# Ausführungstrace erhalten
pperl --trace script.pl 2>&1 | tail -50
Funktionswünsche
Funktionswünsche sollten Folgendes angeben:
- Das fehlende oder unvollständige Perl-5-Feature oder -Modul
- Einen Anwendungsfall, der zeigt, warum es wichtig ist
- Ob ein Pure-Perl-Workaround existiert