Skip to content

Implement variable sharing between interpreter and compiled code#191

Merged
fglock merged 4 commits intomasterfrom
feature/interpreter-phase2-control-flow
Feb 12, 2026
Merged

Implement variable sharing between interpreter and compiled code#191
fglock merged 4 commits intomasterfrom
feature/interpreter-phase2-control-flow

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Feb 12, 2026

Summary

Enables interpreted main scripts to share lexical variables with compiled named subroutines, allowing programs like examples/life.pl to run correctly in interpreter mode.

Problem

Previously, variables declared in the interpreted main script (like $width, $height) were stored only in interpreter registers and were inaccessible to compiled named subroutines. This caused programs with mixed interpreted/compiled code to fail.

Solution

Store captured variables in persistent globals using the existing BEGIN mechanism:

  • Variables captured by named subs are stored using SLOWOP_RETRIEVE_BEGIN_* opcodes
  • New SET_SCALAR opcode sets values without overwriting references (preserves aliasing)
  • Both interpreter and compiled code access the same RuntimeScalar/Array/Hash objects

Key Changes

  • New opcode SET_SCALAR: Calls .set() on RuntimeScalar without overwriting the reference
  • Context detection (wantarray): Subroutine calls now properly detect VOID/SCALAR/LIST context based on assignment target
  • Range null handling: RANGE opcode now handles null registers gracefully
  • Variable capture analysis: Added VariableCaptureAnalyzer (currently uses parser-set AST node ids)

Testing

./jperl --interpreter examples/life.pl 10 10 5

Now runs successfully with:

  • Proper variable sharing between interpreted main and compiled subs
  • Correct wantarray behavior (functions return appropriate values based on context)
  • Working animations through multiple generations

🤖 Generated with Claude Code

fglock and others added 4 commits February 12, 2026 16:14
…tics

Add support for $array[index] and $hash{key} operations using correct
Perl semantics where assignment modifies content, doesn't create new objects.

Changes:
- Add NEW_ARRAY (93) and NEW_HASH (94) opcodes for creating empty containers
- Add ARRAY_SET_FROM_LIST (95) and HASH_SET_FROM_LIST (96) for populating
  * Uses setFromList() semantic: modifies existing array/hash content
  * Matches compiler behavior: create once, populate/modify separately
- Implement array element access: $a[index]
  * Supports both lexical and global arrays
  * ARRAY_GET opcode for element retrieval
- Implement hash element access: $h{key}
  * Supports both lexical and global hashes
  * Bareword keys treated as string literals (not variable lookup)
  * HASH_GET opcode for element retrieval
- Support my @array = list and my %hash = list declarations
  * Proper two-step: create empty + setFromList
  * Allows subsequent reassignment to modify content

Technical fixes:
- Fixed bareword hash key handling: IdentifierNode → string literal
- Replaced incorrect LIST_TO_ARRAY/LIST_TO_HASH opcodes with proper semantics
- Added LOAD_GLOBAL_ARRAY and LOAD_GLOBAL_HASH for global container access

Testing:
```bash
./jperl --interpreter -E 'my @A = (10, 20, 30); print $a[1], "\n"'
# Output: 20

./jperl --interpreter -E 'my %h = (a => 1, b => 2); print $h{a}, " ", $h{b}, "\n"'
# Output: 1 2
```

Dense opcodes: 0-96 (97 total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use PerlCompilerException with ErrorMessageUtil for interpreter compilation
errors, providing user-friendly error messages with file names, line numbers,
and code context instead of bare RuntimeExceptions.

Changes:
- Add ErrorMessageUtil parameter to BytecodeCompiler constructor
- Track currentTokenIndex during AST traversal (BinaryOperatorNode, OperatorNode)
- Add throwCompilerException() helper that uses PerlCompilerException
- Replace "Unsupported operator" RuntimeException/UnsupportedOperationException
  with properly formatted PerlCompilerException
- Update PerlLanguageProvider to pass ctx.errorUtil to BytecodeCompiler
- Update test harnesses (InterpreterTest) to pass errorUtil

Error message improvement:
Before: "Unsupported operator: &"
After:  "Unsupported operator: & at examples/life.pl line 57, near \"@life = generate;\""

The ErrorMessageUtil automatically:
- Adds source file name
- Computes line number from token index
- Shows code context around the error
- Formats with proper Perl-style error messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add support for calling named subroutines using the & operator to get code
references, enabling patterns like foo() and printlife @Args.

Changes:
- Add & operator support in OperatorNode visitor
  * Gets code reference using LOAD_GLOBAL_CODE opcode
  * Adds "main::" package prefix if needed
  * Returns RuntimeScalar containing code reference
- Fix ( operator case to include "(" (was only "()" and "->")
  * Now handles: foo(args), $coderef->(args), &subname(args)
- Use throwCompilerException for & operator errors (proper formatting)

This completes the subroutine call chain:
1. & operator → gets code reference
2. ( operator → calls the code reference with arguments
3. CALL_SUB opcode → executes via RuntimeCode.apply()

Testing:
```bash
./jperl --interpreter -E 'sub foo { return 42; } my $x = foo(); print "$x\n"'
# Output: 42

./jperl --interpreter -E 'sub add { my ($a, $b) = @_; return $a + $b; } print add(10, 20), "\n"'
# Output: 30
```

Progress on life.pl: Now fails on passing arrays to subroutines (@life)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable interpreted main scripts to share lexical variables with compiled
named subroutines by storing captured variables in persistent globals.

Key changes:
- Add SET_SCALAR opcode to set values in persistent scalars without
  overwriting references, preserving aliasing between interpreter and
  compiled code
- Use SLOWOP_RETRIEVE_BEGIN_* opcodes to load persistent variables into
  interpreter registers, enabling both modes to access the same objects
- Implement context detection (wantarray) for subroutine calls based on
  assignment target type (scalar/array/void)
- Fix RANGE opcode to handle null registers gracefully
- Add VariableCaptureAnalyzer for identifying shared variables (note:
  currently uses AST node ids set by parser)

This allows programs like examples/life.pl to run correctly in interpreter
mode with proper variable sharing and context handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@fglock fglock merged commit 2a4a9a2 into master Feb 12, 2026
2 checks passed
@fglock fglock deleted the feature/interpreter-phase2-control-flow branch February 12, 2026 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant