# Subroutines Subroutines are reusable code blocks in Perl. PetaPerl implements Perl5-compatible subroutines with full support for lexical variables, closures, and signatures. ## Declaration ```perl sub greet { print "Hello, World!\n"; } # Named sub sub add { my ($a, $b) = @_; return $a + $b; } # Anonymous sub my $sub = sub { my $x = shift; return $x * 2; }; ``` ## Arguments (@_) Arguments are passed via the special `@_` array. Each element is aliased to the caller's variable. ```perl sub modify { $_[0] = "modified"; # Modifies caller's variable } my $x = "original"; modify($x); say $x; # "modified" ``` **Copying arguments:** ```perl sub safe_modify { my ($val) = @_; # Copy, not alias $val = "modified"; # Does not affect caller } ``` **Direct access:** ```perl sub first { $_[0] } sub second { $_[1] } ``` ## Return Values ### Explicit Return ```perl sub max { my ($a, $b) = @_; return $a if $a > $b; return $b; } ``` ### Implicit Return The last expression's value is returned automatically. ```perl sub square { my $x = shift; $x * $x # Implicit return } ``` ### Context-Sensitive Returns ```perl sub get_data { if (wantarray) { return (1, 2, 3); # List context } else { return "scalar data"; # Scalar context } } my @list = get_data(); # (1, 2, 3) my $scalar = get_data(); # "scalar data" ``` **Note:** PetaPerl's `wantarray` works for direct calls and most common patterns. Complex nesting may have edge cases. ## Prototypes Prototypes provide compile-time argument checking and context forcing. ```perl sub scalar_arg ($) { my $x = shift; } sub array_arg (@) { my @vals = @_; } sub no_args () { return 42; } ``` **Common prototypes:** | Prototype | Meaning | |-----------|---------| | `$` | Single scalar argument | | `@` | Array of arguments (slurpy) | | `%` | Hash (even-length list) | | `&` | Subroutine reference | | `*` | Bareword or typeglob | | `\$` | Scalar reference | | `\@` | Array reference | | `\%` | Hash reference | | `()` | No arguments (constant sub) | **PetaPerl Status:** Basic prototype parsing implemented. Runtime enforcement partial. ## Signatures (Experimental) Perl 5.20+ signatures provide named parameters with optional type constraints and defaults. ```perl sub greet ($name, $greeting = "Hello") { say "$greeting, $name!"; } greet("Alice"); # Hello, Alice! greet("Bob", "Hi"); # Hi, Bob! ``` **Slurpy parameters:** ```perl sub sum ($first, @rest) { my $total = $first; $total += $_ for @rest; return $total; } sum(1, 2, 3, 4); # 10 ``` **PetaPerl Status:** Signature parsing implemented in AST. Codegen lowers to pad allocation. Runtime parameter assignment works. Default values and slurpy params functional. ## Closures Anonymous subs capture lexical variables from enclosing scope. ```perl 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 (independent counter) ``` **Closure capture mechanism:** - Parser identifies outer lexicals referenced in sub body - Codegen records `outer_refs` mapping (sub_pad_slot, outer_pad_slot) - `pp_anoncode` captures cells at instantiation time - `pp_entersub` shares captured cells into sub's pad **Example: Closure factory** ```perl sub make_adder { my $offset = shift; return sub { my $x = shift; return $x + $offset; # Captures $offset }; } my $add5 = make_adder(5); my $add10 = make_adder(10); say $add5->(3); # 8 say $add10->(3); # 13 ``` Each closure captures its own `$offset` cell. Modifications affect only that closure's instance. ## Method Calls ```perl my $obj = Some::Class->new(); $obj->method($arg); # Indirect object notation (discouraged) method $obj $arg; ``` **PetaPerl Status:** Method dispatch via `pp_method`, `pp_method_named`. ISA lookup implemented. AUTOLOAD supported. ## Special Subroutines ### AUTOLOAD Called when undefined sub is invoked. ```perl package Foo; our $AUTOLOAD; sub AUTOLOAD { my $method = $AUTOLOAD; $method =~ s/.*:://; # Strip package print "Called undefined method: $method\n"; } Foo->undefined_method(); # Triggers AUTOLOAD ``` **PetaPerl Status:** AUTOLOAD dispatch implemented in `pp_entersub`. Sets `$Package::AUTOLOAD` correctly. ### BEGIN, END, INIT, CHECK, UNITCHECK **PetaPerl Status:** BEGIN blocks execute during compilation. END blocks execute at program exit. INIT, CHECK, and UNITCHECK have partial support. ## Recursion Recursive calls are supported with depth limit. ```perl sub factorial { my $n = shift; return 1 if $n <= 1; return $n * factorial($n - 1); } ``` **Recursion limit:** Default 1000 levels (configurable via `PPERL_MAX_RECURSION`). Prevents stack overflow. ## Calling Conventions ### With parentheses ```perl foo(); # No arguments foo($a); # One argument foo($a, $b); # Multiple arguments ``` ### Without parentheses (ampersand form) ```perl &foo; # Pass current @_ to foo ``` When called as `&foo` (no parens), the current `@_` is passed to the callee. This enables wrapper functions: ```perl sub wrapper { log_call(); goto &real_function; # Tail call with current @_ } ``` ## Goto and Tail Calls ```perl sub outer { # ... setup ... goto &inner; # Replace current call frame } ``` `goto &sub` performs tail call optimization: replaces current frame instead of adding a new one. **PetaPerl Status:** `goto &sub` implemented in `pp_goto`. Tail recursion optimization functional. ## Advanced Features ### Constant Subs ```perl use constant PI => 3.14159; use constant DEBUG => 0; # Equivalent to: sub PI () { 3.14159 } ``` Constant subs have empty prototype `()` and return fixed value. Perl can inline them at compile time. **PetaPerl Status:** `use constant` creates constant subs. `pp_entersub` returns constant value directly without entering sub. ### Lvalue Subs ```perl sub get_value : lvalue { $global_var; } get_value() = 42; # Assigns to $global_var ``` **PetaPerl Status:** `:lvalue` attribute not implemented. `pp_leavesublv` stub exists. ### State Variables ```perl sub counter { state $count = 0; return ++$count; } ``` `state` variables persist across calls (initialized once). **PetaPerl Status:** State variables are implemented. Variables are initialized once and persist across calls. ## Built-in Functions ### wantarray Returns calling context: `undef` (void), `0` (scalar), `1` (list). ```perl sub context_aware { if (!defined wantarray) { say "void context"; } elsif (wantarray) { say "list context"; return (1, 2, 3); } else { say "scalar context"; return 42; } } ``` **PetaPerl Status:** Partial implementation. Works for direct calls. May fail in complex nesting. ### caller Returns information about calling stack frame. ```perl # Scalar context: boolean (is there a caller?) if (caller) { # Called from another sub } # List context: detailed info my ($package, $filename, $line) = caller(0); my ($pkg, $file, $line, $sub) = caller(1); # One frame up ``` **PetaPerl Status:** Implemented in `pp_caller`. Returns `(package, filename, line, subroutine, ...)` in list context. ## PetaPerl Implementation Notes ### Op Tree Structure Named subs register CV (Code Value) in arena: ``` SubDef → lower_sub() → NextState → body ops → LeaveSub ↓ Register CV(name, start_op, pad_size, outer_refs) ``` Anonymous subs emit `AnonCode` op: ``` AnonSub → lower_anon_sub() → NextState → body ops → LeaveSub ↓ AnonCode (pushes CV onto stack) ``` ### Call Sequence 1. Caller pushes args onto stack 2. Caller pushes mark 3. Caller pushes CV reference 4. `EnterSub` pops CV, pops mark, collects args into aliased `@_` 5. New pad allocated (slot 0 = `@_`) 6. Closure cells shared into pad (if any) 7. Jump to CV's start_op 8. Body executes 9. `LeaveSub` collects return values, restores pad 10. Return to caller ### Pad Layout ``` Slot 0: @_ (always, populated by EnterSub) Slot 1+: Lexical variables in declaration order ``` Signature parameters become pad slots: ```perl sub foo ($x, $y, @rest) { } # Pad layout: # 0: @_ # 1: $x # 2: $y # 3: @rest ``` ### Closure Capture **Two-stage process:** 1. **Codegen:** Analyze outer lexicals, record `outer_refs` in CV 2. **Runtime:** `pp_anoncode` captures cells, `pp_entersub` shares them **Why two stages?** Closure factories need per-instance capture, not per-definition. ## Known Limitations | Feature | Status | |---------|--------| | Named parameters | Working | | Prototypes | Parsed, partial enforcement | | Signatures | Working (basic) | | Closures | Working | | Recursion | Working (1000 depth limit) | | AUTOLOAD | Working | | Method dispatch | Working | | wantarray | Partial | | caller | Working | | BEGIN/END blocks | BEGIN works; END/INIT/CHECK partial | | Attributes (`:lvalue`, etc.) | Parsed, not enforced | | goto &sub | Working | | State variables | Basic support | ## Performance PetaPerl subs execute via the interpreter with fast-path dispatch. The JIT compiler (Cranelift) handles hot loops within subroutines but does not JIT subroutine call/return itself. The interpreter's function-pointer dispatch table and inline fast paths for common operations (arithmetic, comparison, assignment) minimize per-op overhead. Closure overhead: Cell allocation per captured variable. Minimal at runtime (Arc clone). ## Testing See `t/05-op/sub.t`, `t/05-op/closure.t` for comprehensive test coverage.