Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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, for Schleifen
  • 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

OptionBeschreibung
-e 'code'Code ausführen (moderne Features immer aktiv)
-cNur Syntax prüfen
-wWarnungen aktivieren
-vVersion anzeigen
--no-jitJIT-Kompilierung deaktivieren
--no-parallelAuto-Parallelisierung deaktivieren
--p5Eigenständige P5-Laufzeit verwenden (experimentell)
--cacheBytecode-Caching aktivieren

Siehe Skripte ausführen für die vollständige Optionsliste.

Hinweise

  • Moderne Features (say, state, //, Signaturen) sind immer aktiviert — kein use feature nötig
  • XS-Module werden nicht unterstützt; für gängige Module gibt es native Rust-Reimplementierungen
  • -I wird nicht unterstützt; die Umgebungsvariable PERL5LIB kann 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

OptionBeschreibungBeispiel
-e 'code'Einzeiler ausführen (moderne Features immer aktiv)pperl -e 'say "hi"'
-cNur Syntax prüfenpperl -c script.pl
-wWarnungen aktivierenpperl -w script.pl
-vVersion anzeigenpperl -v
-VAusführliche Konfigurationpperl -V
-h, -?, --helpHilfe anzeigenpperl --help

Kombinierte Kurzoptionen:

pperl -wc script.pl  # Warnungen aktivieren + Syntaxprüfung

PetaPerl-spezifische Optionen

OptionBeschreibungStandard
--stats, -sLeistungsstatistiken anzeigen (Zeit, Speicher, Ops)Deaktiviert
--trace, -tAusführungsverfolgung aktivierenDeaktiviert
--timeout=SECSZeitlimit für Ausführung in SekundenKeines
--p5P5-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

OptionBeschreibungStandard
--no-jitJIT-Kompilierung deaktivierenJIT aktiviert
--no-parallelAuto-Parallelisierung deaktivierenParallelisierung aktiv
--threads=NAnzahl der Threads für ParallelisierungAlle CPUs
--parallel-threshold=NMinimale Iterationen für Parallelisierung100

Caching- und Analyseoptionen

OptionBeschreibung
--cacheBytecode-Caching aktivieren (~/.pperl/cache/)
--flushAlle Bytecode-Caches leeren und beenden
--dump-optreeKanonischen Op-Tree ausgeben (nicht ausführen)
--from-json, -jOp-Tree von stdin lesen (B::PetaPerl-JSON-Format)
--optree, -oOp-Tree von stdin lesen (B::Concise-Format)
--compare-bytecodeOp-Trees vergleichen: perl5-Backend vs. nativer Parser

Nicht unterstützte perl5-Optionen

Folgende perl5-Flags sind nicht implementiert:

OptionBeschreibungUmgehung
-IInclude-Pfad hinzufügenPERL5LIB verwenden
-EMit Features ausführen-e verwenden (Features immer aktiv)
-M / -mModul ladenuse im Code verwenden
-n / -pImplizite EingabeschleifeExplizite Schleife schreiben
-lAutomatisches Zeilenendechomp/say verwenden
-a / -FAutosplitsplit explizit verwenden
-iIn-Place-Bearbeitungopen/close verwenden
-dDebugger-
-xSkript extrahieren-
-TTaint-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

CodeBedeutung
0Erfolg
1Laufzeitfehler oder Kompilierfehler
2Fehler bei Kommandozeilenverwendung

Umgebungsvariablen

VariableWirkungStandard
PPERL_MAX_RECURSIONMaximale Rekursionstiefe für Subroutinen1000
PPERL_CACHEVerzeichnis für Bytecode-Cache-
PERL5LIBModul-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 Zeilenumbruch
  • state - 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

FunktionStatusBeschreibung
printAusgabe auf Dateihandle oder STDOUT
sayAusgabe mit Zeilenumbruch
printfFormatierte Ausgabe
sprintfFormatierte Zeichenkette
print "Hello\n";
print $fh "data\n";
say "Hello";                    # Fuegt Zeilenumbruch hinzu
printf "%d Elemente\n", $count;
my $str = sprintf "%05d", $num;

Dateioperationen

FunktionStatusBeschreibung
openDatei oder Pipe oeffnen
closeDateihandle schliessen
readDaten fester Laenge lesen
sysreadLesen auf Systemebene
syswriteSchreiben auf Systemebene
readlineZeile(n) vom Dateihandle lesen
getcEinzelnes Zeichen lesen
eofAuf Dateiende pruefen
seekDateihandle positionieren
tellPosition des Dateihandles abfragen
filenoDateideskriptornummer abfragen
binmodeBinaermodus setzen
selectStandard-Dateihandle setzen
writeFormatierten 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

FunktionStatusBeschreibung
opendirVerzeichnishandle oeffnen
readdirVerzeichniseintrag lesen
closedirVerzeichnishandle schliessen
rewinddirVerzeichnishandle zuruecksetzen
seekdirVerzeichnishandle positionieren
telldirVerzeichnisposition abfragen
mkdirVerzeichnis erstellen
rmdirVerzeichnis entfernen
chdirArbeitsverzeichnis wechseln
opendir my $dh, $dir or die $!;
my @entries = readdir $dh;
closedir $dh;

mkdir "newdir", 0755 or die $!;
rmdir "olddir";
chdir "/tmp";

Dateisystemoperationen

FunktionStatusBeschreibung
unlinkDateien loeschen
renameDatei umbenennen
linkHarten Link erstellen
symlinkSymbolischen Link erstellen
readlinkSymbolischen Link lesen
chmodDateiberechtigungen aendern
chownDateibesitzer/-gruppe aendern
utimeZugriffs-/Aenderungszeiten aendern
truncateDatei auf Laenge kuerzen
statDateistatus abfragen
lstatDateistatus abfragen (ohne Link-Verfolgung)
globDateinamenerweiterung
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

FunktionStatusBeschreibung
lengthZeichenketten-/Array-Laenge
substrTeilzeichenkette extrahieren/ersetzen
indexPosition einer Teilzeichenkette finden
rindexTeilzeichenkette finden (rueckwaerts)
chrZahl in Zeichen umwandeln
ordZeichen in Zahl umwandeln
lcZeichenkette in Kleinbuchstaben
ucZeichenkette in Grossbuchstaben
lcfirstErstes Zeichen in Kleinbuchstaben
ucfirstErstes Zeichen in Grossbuchstaben
quotemetaMetazeichen quotieren
chopLetztes Zeichen entfernen
chompZeilenende entfernen
hexHexadezimale Zeichenkette in Zahl
octOktale Zeichenkette in Zahl
packWerte in Binaerzeichenkette packen
unpackBinaerzeichenkette entpacken
vecBitvektorzugriff
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

FunktionStatusBeschreibung
pushAn Array anhaengen
popLetztes Element entfernen
shiftErstes Element entfernen
unshiftVor Array stellen
spliceArray-Elemente entfernen/ersetzen
joinArray mit Trennzeichen verbinden
splitZeichenkette in Array aufteilen
reverseListe/Zeichenkette umkehren
sortListe sortieren
grepListe filtern
mapListe 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

FunktionStatusBeschreibung
keysHash-Schluessel abfragen
valuesHash-Werte abfragen
eachSchluessel-Wert-Paare iterieren
existsExistenz eines Schluessels pruefen
deleteHash-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

FunktionStatusBeschreibung
absAbsolutwert
intGanzzahlige Abschneidung
sqrtQuadratwurzel
sinSinus
cosKosinus
expe hoch Potenz
logNatuerlicher Logarithmus
atan2Arkustangens von y/x
randZufallszahl
srandZufallsgenerator 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

FunktionStatusBeschreibung
refReferenztyp abfragen
blessReferenz in Paket segnen
definedAuf Definiertheit pruefen
undefVariable undefinieren
scalarSkalaren 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

FunktionStatusBeschreibung
callerAufruferinformation abfragen
wantarrayAufrufkontext abfragen
prototypeUnterprogramm-Prototyp abfragen
posRegex-Position abfragen/setzen
studyRegex-Abgleich optimieren
resetVariablen 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

FunktionStatusBeschreibung
dieAusnahme ausloesen
warnWarnung ausgeben
exitProgramm beenden
systemExternen Befehl ausfuehren
execAusfuehren und Prozess ersetzen
forkKindprozess erzeugen
waitAuf Kindprozess warten
waitpidAuf bestimmten Kindprozess warten
sleepFuer Sekunden pausieren
timeAktuelle Unix-Zeit abfragen
timesProzesszeiten abfragen
localtimeZeit in Ortszeit umwandeln
gmtimeZeit in GMT umwandeln
getloginAnmeldenamen 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:

FunktionStatusBeschreibung
getpwnamPassworteintrag nach Name abfragen
getpwuidPassworteintrag nach UID abfragen
getpwentNaechsten Passworteintrag abfragen
setpwentPasswort-Iteration zuruecksetzen
endpwentPasswort-Iteration beenden
getgrnamGruppeneintrag nach Name abfragen
getgrgidGruppeneintrag nach GID abfragen
getgrentNaechsten Gruppeneintrag abfragen
setgrentGruppen-Iteration zuruecksetzen
endgrentGruppen-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:

FunktionStatusBeschreibung
gethostbynameHost nach Name abfragen
gethostbyaddrHost nach Adresse abfragen
gethostentNaechsten Hosteintrag abfragen
sethostentHost-Iteration zuruecksetzen
endhostentHost-Iteration beenden
getnetbynameNetzwerk nach Name abfragen
getnetbyaddrNetzwerk nach Adresse abfragen
getnetentNaechsten Netzwerkeintrag abfragen
setnetentNetzwerk-Iteration zuruecksetzen
endnetentNetzwerk-Iteration beenden
getprotobynameProtokoll nach Name abfragen
getprotobynumberProtokoll nach Nummer abfragen
getprotoentNaechsten Protokolleintrag abfragen
setprotoentProtokoll-Iteration zuruecksetzen
endprotoentProtokoll-Iteration beenden
getservbynameDienst nach Name abfragen
getservbyportDienst nach Port abfragen
getserventNaechsten Diensteintrag abfragen
setserventDienst-Iteration zuruecksetzen
endserventDienst-Iteration beenden
my @host = gethostbyname "example.com";
my @net = getnetbyname "loopback";
my @proto = getprotobyname "tcp";
my @serv = getservbyname "http", "tcp";

Tie-Mechanismus

FunktionStatusBeschreibung
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)

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

FunktionStatusBeschreibung
socketSocket erstellen
socketpairSocket-Paar erstellen
bindSocket an Adresse binden
listenAuf Socket lauschen
acceptVerbindung annehmen
connectMit Gegenstelle verbinden
shutdownSocket herunterfahren
sendDaten senden
recvDaten empfangen
getsockoptSocket-Option abfragen
setsockoptSocket-Option setzen
getsocknameLokale Adresse abfragen
getpeernameEntfernte Adresse abfragen

IPC-Funktionen

FunktionStatusBeschreibung
pipePipe erstellen
msgctlNachrichtenwarteschlangensteuerung
msggetNachrichtenwarteschlange abfragen
msgsndNachricht senden
msgrcvNachricht empfangen
semctlSemaphorsteuerung
semgetSemaphorsatz abfragen
semopSemaphoroperationen
shmctlGemeinsamer-Speicher-Steuerung
shmgetGemeinsamen Speicher abfragen
shmreadGemeinsamen Speicher lesen
shmwriteGemeinsamen 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

  • use und require funktionieren 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, $], $^V usw.)
  • Internals::SvREADONLY ist 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 Elementtransformation
  • grep - Parallele Filterung
  • sort mit 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

OperatorOperationBeispiel
+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

OperatorOperationBeispiel
.Verkettung$a . $b
xWiederholung$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

OperatorOperationGibt zurueck
==GleichWahr bei Gleichheit
!=UngleichWahr bei Ungleichheit
<Kleiner alsWahr wenn kleiner
>Groesser alsWahr wenn groesser
<=Kleiner oder gleichWahr wenn kleiner oder gleich
>=Groesser oder gleichWahr 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

OperatorOperationGibt zurueck
eqGleichWahr bei Gleichheit
neUngleichWahr bei Ungleichheit
ltKleiner alsWahr wenn kleiner
gtGroesser alsWahr wenn groesser
leKleiner oder gleichWahr wenn kleiner oder gleich
geGroesser oder gleichWahr wenn groesser oder gleich
cmpDreiwegevergleich-1, 0 oder 1

Zeichenkettenvergleiche verwenden lexikographische (Woerterbuch-)Ordnung.

if ($name eq "John") { ... }
my $cmp = $a cmp $b;  # -1, 0 oder 1

Logisch

OperatorOperationKurzschlussVorrang
&&UndJaHoch
``Oder
//Definiert-oderJaHoch
andUndJaNiedrig
orOderJaNiedrig
xorExklusiv-oderNeinNiedrig

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

OperatorOperationBeispiel
&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

OperatorOperationKontext
..Inklusiver BereichErzeugt im Listenkontext eine Liste
...Flip-FlopIst 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

OperatorOperationBeispiel
=~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

OperatorOperationBeispiel
-Negation-$x
+Unaeres Plus+$x
my $neg = -5;
my $pos = +$x;  # Numerischer Kontext

Logisch

OperatorOperationBeispiel
!Nicht!$x
notNicht (niedriger Vorrang)not $x
if (!$error) { ... }
die "Fehlgeschlagen" if not $ok;

Bitweise

OperatorOperationBeispiel
~Bitweises Komplement~$x
my $inverted = ~$bits;

Referenz

OperatorOperationBeispiel
\Referenz erzeugen\$x, \@arr, \%hash
my $scalar_ref = \$value;
my $array_ref = \@data;
my $hash_ref = \%config;

Inkrement/Dekrement

OperatorOperationWann ausgewertet
++$xPraeinkrementNach dem Erhoehen
--$xPraedekrementNach dem Verringern
$x++PostinkrementVor dem Erhoehen
$x--PostdekrementVor 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.

OperatorTestGibt zurueck
-eExistiertBoolesch
-rLesbarBoolesch
-wSchreibbarBoolesch
-xAusfuehrbarBoolesch
-oGehoert effektiver UIDBoolesch
-RLesbar fuer reale UIDBoolesch
-WSchreibbar fuer reale UIDBoolesch
-XAusfuehrbar fuer reale UIDBoolesch
-OGehoert realer UIDBoolesch
-zGroesse NullBoolesch
-sGroesse ungleich NullGroesse in Bytes oder falsch
-fRegulaere DateiBoolesch
-dVerzeichnisBoolesch
-lSymbolischer LinkBoolesch
-pBenannte Pipe (FIFO)Boolesch
-SSocketBoolesch
-bBlockspezialdateiBoolesch
-cZeichenspezialdateiBoolesch
-tTTY (Terminal)Boolesch
-uSetuid-Bit gesetztBoolesch
-gSetgid-Bit gesetztBoolesch
-kSticky-Bit gesetztBoolesch
-TTextdateiBoolesch
-BBinaerdateiBoolesch
-MAenderungszeit (Tage)Zahl
-AZugriffszeit (Tage)Zahl
-CInode-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

OperatorOperationBeispiel
definedAuf Definiertheit pruefendefined $x
if (defined $value) { ... }

Zuweisungsoperatoren

Einfache Zuweisung

OperatorOperationBeispiel
=Zuweisung$x = 5
my $x = 10;
my ($a, $b, $c) = (1, 2, 3);  # Listenzuweisung

Zusammengesetzte Zuweisung

Alle binaeren Operatoren haben zusammengesetzte Zuweisungsformen:

OperatorAequivalentBeispiel
+=$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

OperatorSyntaxBeispiel
? :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

SyntaxOperationBeispiel
$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

SyntaxOperationBeispiel
->[]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

OperatorOperationVerwendung
,Listentrennzeichen(1, 2, 3)
=>Fettes Kommakey => 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):

  1. Terme und Listenoperatoren (links)
  2. -> (links)
  3. ++ -- (kein)
  4. ** (rechts)
  5. ! ~ \ unaeres + unaeres - (rechts)
  6. =~ !~ (links)
  7. * / % x (links)
  8. + - . (links)
  9. << >> (links)
  10. Benannte unaere Operatoren
  11. < > <= >= lt gt le ge (kein)
  12. == != <=> eq ne cmp ~~ (kein) – ~~ Smart Match ist teilweise implementiert
  13. & (links)
  14. | ^ (links)
  15. && (links)
  16. || // (links)
  17. .. ... (kein)
  18. ?: (rechts)
  19. = += -= usw. (rechts)
  20. , => (links)
  21. Listenoperatoren (rechts)
  22. not (rechts)
  23. and (links)
  24. or xor (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

ZeichenBedeutung
.Beliebiges Zeichen ausser Zeilenumbruch
^Anfang der Zeichenkette
$Ende der Zeichenkette
\AAnfang der Zeichenkette (absolut)
\zEnde der Zeichenkette (absolut)
\ZEnde der Zeichenkette oder vor abschliessendem Zeilenumbruch
\bWortgrenze
\BKeine Wortgrenze
\GPosition 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

KlasseTrifft aufNegiert
\dZiffer [0-9]\D (Nicht-Ziffer)
\wWortzeichen [a-zA-Z0-9_]\W (Nicht-Wortzeichen)
\sLeerraum [ \t\n\r\f]\S (Nicht-Leerraum)
\hHorizontaler Leerraum\H
\vVertikaler 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

QuantifiziererBedeutungGierigNicht-gierigPossessiv
*0 oder mehrJa*?*+
+1 oder mehrJa+?++
?0 oder 1Ja???+
{n}Genau nJaN/AN/A
{n,}n oder mehrJa{n,}?N/A
{n,m}n bis mJa{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

ZusicherungBedeutung
(?=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:

ModifiziererBedeutung
iGross-/Kleinschreibung ignorieren
mMehrzeilig (^/$ treffen auf Zeilengrenzen)
sEinzeilig (. trifft auf Zeilenumbruch)
xErweitert (Leerraum ignorieren, Kommentare erlaubt)
gGlobal (alle Treffer finden)
cSuche nach fehlgeschlagenem Abgleich fortsetzen
oEinmal kompilieren (veraltet, in PetaPerl nicht noetig)
eErsetzung 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:

VariableEnthaelt
$&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)
  • local in 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

  • perlop - Bindungsoperatoren =~ und !~
  • perlvar - Spezielle Variablen wie $&, $1 usw.

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:

TypBeschreibungBeispiel
UndefUndefinierter Wertundef
IvGanzzahl (i64)42, -17
UvVorzeichenlose Ganzzahl (u64)Grosse positive Zahlen
NvGleitkommazahl (f64)3.14, 1.5e10
PvUnveraenderliche Zeichenkette (Arc<str>)Konstanten, Literale
PvBufVeraenderliche Zeichenkette (Arc<String>).=-Ziele, $_
RvReferenz\$x, \@arr, \%hash
AvArrayErzeugt durch []
HvHashErzeugt 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

WertBeschreibung
undefUndefinierter Wert
0Numerische 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 = \&sub;
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 = \&sub;               # 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 SvCell fuer veraenderliche Indirektion
sub modify {
    $_[0] = "changed";       # Aendert die Variable des Aufrufers
}

my $x = "original";
modify($x);
print $x;                    # "changed"

Leistungsmerkmale

OperationKomplexitaetAnmerkungen
SkalarzuweisungO(1)Zeichenkette nutzt Arc (keine Kopie)
Array push/popO(1) amortisiertDynamisches Wachstum
Array shift/unshiftO(n)Elemente muessen verschoben werden
Hash-ZugriffO(1) durchschnittlichOptimierte Hash-Funktion
Hash-EinfuegenO(1) durchschnittlichMit 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:

PrototypBedeutung
$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_anoncode erfasst Zellen zum Instanziierungszeitpunkt
  • pp_entersub teilt 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

  1. Aufrufer legt Argumente auf den Stapel
  2. Aufrufer legt Markierung ab
  3. Aufrufer legt CV-Referenz ab
  4. EnterSub entnimmt CV, entnimmt Markierung, sammelt Argumente in aliasiertem @_
  5. Neuer Pad wird allokiert (Slot 0 = @_)
  6. Closure-Zellen werden in den Pad geteilt (falls vorhanden)
  7. Sprung zur start_op des CV
  8. Koerper wird ausgefuehrt
  9. LeaveSub sammelt Rueckgabewerte, stellt Pad wieder her
  10. 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:

  1. Codegen: Aeussere lexikalische Variablen analysieren, outer_refs im CV aufzeichnen
  2. Laufzeit: pp_anoncode erfasst Zellen, pp_entersub teilt sie

Warum zwei Stufen? Closure-Fabriken benoetigen Erfassung pro Instanz, nicht pro Definition.

Bekannte Einschraenkungen

FunktionalitaetStatus
Benannte ParameterFunktioniert
PrototypenGeparst, teilweise Durchsetzung
SignaturenFunktioniert (grundlegend)
ClosuresFunktioniert
RekursionFunktioniert (1000 Ebenenlimit)
AUTOLOADFunktioniert
MethodendispatchFunktioniert
wantarrayTeilweise
callerFunktioniert
BEGIN/END-BloeckeBEGIN funktioniert; END/INIT/CHECK teilweise
Attribute (:lvalue usw.)Geparst, nicht durchgesetzt
goto &subFunktioniert
State-VariablenGrundlegende 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 Zeilenumbruch
  • state — persistente lexikalische Variablen
  • // — Defined-Or-Operator
  • fc — 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

PragmaStatus
strictGeparst, teilweise erzwungen
warningsGeparst, teilweise Warnungskategorien
utf8Geparst, Strings sind intern immer UTF-8
constantVoll funktionsfähig
base/parentVoll funktionsfähig
CarpVoll funktionsfähig
overloadTeilweise funktionsfähig
reTeilweise funktionsfähig

Kommandozeilenoptionen

Diese perl5-Optionen werden nicht unterstützt:

OptionBeschreibung
-IInclude-Pfad hinzufügen (verwenden Sie stattdessen PERL5LIB)
-M / -mModul von der Kommandozeile laden
-n / -pImplizite Eingabeschleife
-lAutomatische Zeilenendebehandlung
-a / -FAutosplit-Modus
-iIn-Place-Bearbeitung
-xSkript aus Nachricht extrahieren
-TTaint-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
  • local in (?{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

ModulProblem
Test2::APIuse Module(\$lexical) verliert lexikalische Seiteneffekte zur Kompilierzeit
Module::CoreListZeitüberschreitung (>20s) wegen enormer Hash-Literale
charnames/unicoreHängt von Internals::SvREADONLY ab
Pod::Simple::RTFSonderfall 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

ModulFunktionenHinweise
Scalar::Utilblessed, reftype, refaddr, weaken, isweak, looks_like_number, …Vollständige API
List::Utilsum, min, max, first, any, all, none, reduce, …MULTICALL-optimiert
Sub::Utilsubname, set_subname
Hash::Utillock_keys, lock_hash, …

Digest / Kryptographie

ModulFunktionenHinweise
Digest::MD5md5, md5_hex, md5_base64, OO-SchnittstelleVollständige API
Digest::SHAsha1, sha256, sha512, OO-SchnittstelleVollständige API
MIME::Base64encode_base64, decode_base64
MIME::QuotedPrintencode_qp, decode_qp

Datei / System

ModulFunktionenHinweise
File::Basenamebasename, dirname, fileparse
File::Copycopy, move
File::Findfind, finddepth
File::Globbsd_glob
File::Pathmake_path, remove_tree
File::Speccatdir, catfile, rel2abs, …
File::Temptempfile, tempdirOO + funktional
File::statstat (OO)
Cwdcwd, getcwd, abs_path
Sys::Hostnamehostname

I/O

ModulFunktionenHinweise
IO::FileOO-Dateihandle
IO::HandleOO-Handle-Basis
IO::DirOO-Verzeichnishandle
IO::PipeOO-Pipe-Handle
IO::Selectselect-Wrapper
IO::SocketOO-Socket-Handle
IO::Seekableseek/tell-Mixin
FileHandleLegacy-OO-Handle
SocketSocket-Primitive

Daten / Kodierung

ModulFunktionenHinweise
Data::DumperDumper
Storablefreeze, thaw, nstore, retrieve
Encodeencode, decode, find_encoding
JSON::PPencode_json, decode_jsonÜber reines Perl

Numerik / Mathematik

ModulFunktionenHinweise
POSIXfloor, ceil, fmod, strtod, strftime, …Teilmenge
Math::GMPGanzzahlarithmetik beliebiger GenauigkeitÜber Peta::FFI::GMP

Build / Konfiguration

ModulFunktionenHinweise
Config%Config-HashBuild-Konfiguration
FcntlO_RDONLY, O_WRONLY, …Konstanten automatisch geladen
ErrnoENOENT, EACCES, …Konstanten automatisch geladen

Introspektion

ModulFunktionenHinweise
BCompiler-Backend-IntrospektionMinimal
PadWalkerpeek_my, peek_our
mroget_linear_isa, set_mro
versionVersionsobjekt-VerarbeitungVollstä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):

FunktionPetaPerl nativperl5 XSVerhältnis
List::Util::sum1,7x schnellerBasislinie
List::Util::min/max2,9x schnellerBasislinie
List::Util::first~1xBasislinieMULTICALL-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:

SchichtModulZweck
0Peta::FFIRohes FFI: dlopen + Aufruf mit Typsignaturen
1Peta::FFI::Libc, ::UUID, ::GMPFertige Bindings für bestimmte Bibliotheken
2Peta::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:

CodeC-TypPerl-Zuordnung
vvoid(kein Wert)
iintIV
llongIV
Lunsigned long / size_tUV
ddoubleNV
ffloatNV
pconst char*String (Eingabe)
Pvoid* (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

SzenarioEmpfehlung
Gängiges CPAN-Modul (List::Util usw.)Natives Modul (bereits eingebaut)
Systembibliothek noch ohne WrapperSchicht-0-Rohes-FFI
Häufige Aufrufe derselben BibliothekSchicht-1-Bindings anfordern
Einmaliges ExperimentSchicht-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:

LaufzeitumgebungZeitBeschleunigung
perl5630ms1,0x
pperl (Interpreter)14ms45x schneller

Mandelbrot-Menge (1000x1000)

JIT-Kompilierung verschachtelter While-Schleifen mit Gleitkomma-Arithmetik:

LaufzeitumgebungZeitBeschleunigung
perl512.514ms1,0x
pperl (nur JIT)163ms76x schneller
pperl (JIT + parallel)29ms431x schneller

Wie das erreicht wird

  1. Registerallokation: Schleifenvariablen in CPU-Registern statt auf dem Interpreter-Stack
  2. Typspezialisierung: Variablen, die nachweislich Ganzzahl oder Gleitkomma sind, verwenden direkt native Instruktionen
  3. Branch-Eliminierung: Konstante Bedingungen werden zur Kompilierzeit entfernt
  4. 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 Verschachtelung
  • JitIr::FloatVar / JitIr::IntVar — typisierter Variablenzugriff
  • JitIr::BinOp — Arithmetik und Vergleiche
  • JitIr::ExitIfFalse / JitIr::ExitIfTrue — Schleifenaustritt und last

Variablentypen

Der JIT verfolgt zwei Variablentypen:

  • JitType::F64 — Gleitkommawerte in einem f64-Puffer
  • JitType::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:

  1. Analyse des Schleifenrumpfes auf Seiteneffekte und gemeinsam veränderlichen Zustand
  2. Identifikation von Reduktionsvariablen (Akkumulatoren wie $sum += ...)
  3. Verteilung der Iterationen über Threads mittels Rayons Work-Stealing-Scheduler
  4. 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)

ModusZeitvs. perl5
perl512.514ms1,0x
pperl Interpreter~3.500ms3,6x schneller
pperl JIT163ms76x schneller
pperl JIT + parallel (8 Threads)29ms431x schneller

Skalierung

Der Work-Stealing-Scheduler liefert nahezu lineare Skalierung für trivial parallelisierbare Aufgaben:

ThreadsMandelbrot 4000x4000Skalierung
1Basislinie1,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

  1. Vision und Ziele
  2. Architektur auf hoher Ebene
  3. Ausführungspipeline
  4. Quellcodestruktur
  5. Zentrale Datenstrukturen
  6. Der Op-Baum
  7. Wertesystem
  8. Laufzeitumgebung und Interpreter
  9. Parser und Codegenerierung
  10. Testinfrastruktur
  11. Entwicklungsablauf
  12. Wesentliche Architekturentscheidungen
  13. 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:

ZielBeschreibung
Auto-ParallelisierungAutomatische parallele map-, grep-, for-Schleifen mittels Rayon Work-Stealing
JIT-KompilierungV8-Klasse-Performance über Cranelift (gestufte Kompilierung)
Pure-Perl-TauglichkeitSchnell 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änkungWertBegründung
PlattformNur LinuxEinfachheit; beliebige Architektur
Perl-Version5.42+ stableNicht blead (5.43.x)
LizenzProprietärGeschä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

ModulZeilenZweck
parser/~19.700Lexing, Parsing, Codegenerierung
runtime/pp/~45.500Implementierungen der PP-Operationen
value/~8.500Werttypen (Sv, Av, Hv, Cv)
op/~4.000Op-Baum-Strukturen
regex/~6.500Regex-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)
EntwurfsphilosophieEigenständige Rust-Implementierung1:1-Transliteration des Perl5-C-Interpreters
WertdarstellungSv-Enum (32 Bytes, getaggte Union)Sv(u64) NaN-geboxed, nachempfunden von perl5s SV*
SpeichersicherheitDurchgehend sicheres RustUmfangreicher unsafe-Code — Rohzeiger, manuelle Referenzzählung
Performance~25% von perl5 (ohne JIT)Auf Augenhöhe mit perl5
JIT-KompilierungCranelift JIT für heiße SchleifenNicht verfügbar
Auto-ParallelisierungRayon Work-Stealing für parallele SchleifenNicht verfügbar
KompatibilitätHoch — deckt die üblichen Fälle abSehr hoch — bildet perl5s subtile Randfälle nach
Native ModuleAlle 47 Module17 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 eine pp.rs (PP-Laufzeit) und optional eine p5.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

BefehlSicherheitZweck
cargo buildSICHERKompilieren
cargo test --libSICHERRust-Unit-Tests
perl t/TEST --skip=bytecode,tierSICHERIntegrationstests (~30s)
perl -c file.plSICHERSyntaxprüfung

Verbotene Befehle

BefehlGefahrGrund
cargo run -- file.plVERBOTENKann unbegrenzt hängen
echo 'x' | cargo runVERBOTENStdin + pperl = Hänger
Direkter pperl-AufrufVERBOTENKeine 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

  1. Mit Werten beginnen (src/value/sv.rs)

    • Verstehen, wie Perl-Werte dargestellt werden
    • Schlüssel: Sv-Enum und seine Varianten
  2. Den Interpreter studieren (src/runtime/interpreter.rs)

    • Der run()-Methode folgen
    • Sehen, wie Ops dispatcht werden
  3. Ein PP-Modul erkunden (z.B. src/runtime/pp/strings.rs)

    • Sehen, wie spezifische Operationen implementiert sind
    • Muster: Operanden holen, berechnen, Ergebnis ablegen
  4. 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:

  1. Opcode zu src/op/opcode/mod.rs hinzufügen
  2. Dispatch-Fall im passenden pp/*.rs-Modul ergänzen
  3. Die PP-Funktion implementieren
  4. Tests zu t/45-builtin/ hinzufügen

Einen fehlschlagenden Test beheben:

  1. Test ausführlich ausführen: perl t/TEST --skip=bytecode,tier -v 45-builtin
  2. Fehlschlagende Prüfung identifizieren
  3. Durch den Interpreter mit --trace-Flag nachverfolgen
  4. Implementierung korrigieren
  5. Verifizieren mit perl t/TEST --skip=bytecode,tier --compare

Eine neue Testdatei hinzufügen:

  1. TAP-Format einhalten
  2. 5er-Lücken-Nummerierung verwenden
  3. Tests fokussiert halten (10-30 Prüfungen)
  4. Randfälle testen

Wichtige Dateien

DateiZweckLesen bei
src/value/sv.rsSkalare WerteTypenverständnis
src/runtime/interpreter.rsHauptschleifeAusführungsverständnis
src/runtime/pp/dispatch.rsOp-RoutingNeue Ops hinzufügen
src/parser/codegen.rsCodegenerierungParser-Probleme
t/TESTTest-HarnessTestinfrastruktur
docs/TODO.mdAufgabenlisteArbeit finden

Hilfe erhalten

  • CLAUDE.md im übergeordneten Verzeichnis für Projektrichtlinien lesen
  • docs/TODO.md für aktuelle Prioritäten prüfen
  • t/99-debug/ für Regressionstest-Muster ansehen
  • Letzte Commits für Code-Stil-Beispiele durchsehen

Anhang: Glossar

BegriffDefinition
SvSkalarer Wert — beliebiger einzelner Perl-Wert
AvArray-Wert — Perl-Array
HvHash-Wert — Perl-Hash
CvCode-Wert — Perl-Subroutine
OpOperation — einzelne Instruktion im Op-Baum
OpIdIndex in die OpArena (u32-Wrapper)
OpArenaBehälter für alle Ops und Konstanten
PPPush-Pop — Funktion, die einen Opcode implementiert
PadSpeicher für lexikalische Variablen eines Geltungsbereichs
StashSymboltabelle eines Pakets
TAPTest Anything Protocol — Ausgabeformat für Tests
GlobTypeglob — 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:

  1. Minimaler Reproduzierer — das kürzeste Perl-Skript, das das Problem demonstriert
  2. Erwartetes Verhalten — was perl5 5.42 für dasselbe Skript ausgibt
  3. Tatsächliches Verhalten — was pperl ausgibt (einschließlich stderr, falls relevant)
  4. pperl-Version — Ausgabe von pperl -v
  5. 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