Perl 5 compiled to WebAssembly. Run Perl scripts in the browser or other JavaScript environments without installing Perl.
Built on zeroperl.
- Runs Perl 5 in browser, Node.js, Deno, and Bun
- Virtual filesystem for script and data files
- Bidirectional data exchange between JavaScript and Perl
- Register JavaScript functions callable from Perl
- Call Perl functions from JavaScript
- Environment variable support
- Output capture (stdout/stderr)
- TypeScript type definitions included
npm install @6over3/zeroperl-ts
# or
bun add @6over3/zeroperl-tsOr, for Browser Usage, copy the zeroperl WASM binary to your local website (to avoid CORS errors).
# The -L option allows the CDN to redirect to the latest version
curl -L -O https://esm.sh/@6over3/zeroperl-ts/zeroperl.wasmimport { ZeroPerl } from '@6over3/zeroperl-ts';
const perl = await ZeroPerl.create();
await perl.eval('print "Hello, World!\\n"');
perl.flush(); // Required to see output
perl.dispose();Perl buffers output by default. Choose one approach:
Option 1: Call flush() after printing
await perl.eval('print "Hello!\\n"');
perl.flush();Option 2: Enable autoflush in Perl
await perl.eval(`
$| = 1; # Enable autoflush
print "Hello!\\n";
`);import { ZeroPerl } from '@6over3/zeroperl-ts';
const perl = await ZeroPerl.create();
const result = await perl.eval(`
$| = 1;
my $x = 42;
print "The answer is $x\\n";
`);
if (!result.success) {
console.error('Error:', result.error);
}
perl.dispose();let output = '';
const perl = await ZeroPerl.create({
stdout: (data) => {
output += typeof data === 'string' ? data : new TextDecoder().decode(data);
}
});
await perl.eval(`
$| = 1;
print "Line 1\\n";
print "Line 2\\n";
`);
console.log(output);
// Output:
// Line 1
// Line 2
perl.dispose();const perl = await ZeroPerl.create();
// JavaScript to Perl
perl.setVariable('name', 'Alice');
perl.setVariable('age', 30);
await perl.eval(`
$| = 1;
print "Name: $name\\n";
print "Age: $age\\n";
`);
// Perl to JavaScript
await perl.eval('$result = 2 + 2');
const result = perl.getVariable('result');
console.log(result.toInt()); // 4
result.dispose();
perl.dispose();const perl = await ZeroPerl.create();
// Create array
const arr = perl.createArray([1, 2, 3, 'hello']);
perl.setVariable('myarray', arr.toValue());
// Create hash
const hash = perl.createHash({
name: 'Alice',
age: 30,
active: true
});
perl.setVariable('user', hash.toValue());
await perl.eval(`
$| = 1;
print "Array length: ", scalar(@$myarray), "\\n";
print "User: $user->{name}, Age: $user->{age}\\n";
`);
// Convert back to JavaScript
const jsArray = arr.project(); // [1, 2, 3, 'hello']
const jsObject = hash.project(); // { name: 'Alice', age: 30, active: true }
arr.dispose();
hash.dispose();
perl.dispose();const perl = await ZeroPerl.create();
await perl.eval(`
$| = 1;
print "Arguments: @ARGV\\n";
foreach my $arg (@ARGV) {
print " $arg\\n";
}
`, ['foo', 'bar', 'baz']);
perl.dispose();import { ZeroPerl, MemoryFileSystem } from '@6over3/zeroperl-ts';
const fs = new MemoryFileSystem({ "/": "" });
fs.addFile("/data.txt", "Hello from a file!");
fs.addFile("/script.pl", `
$| = 1;
open my $fh, '<', '/data.txt' or die $!;
while (my $line = <$fh>) {
print "Read: $line";
}
close $fh;
`);
const perl = await ZeroPerl.create({ fileSystem: fs });
await perl.runFile('/script.pl');
perl.dispose();const fs = new MemoryFileSystem({ "/": "" });
fs.addFile("/greet.pl", `
$| = 1;
my ($name, $greeting) = @ARGV;
print "$greeting, $name!\\n";
`);
const perl = await ZeroPerl.create({ fileSystem: fs });
await perl.runFile('/greet.pl', ['Alice', 'Hello']);
// Output: Hello, Alice!
perl.dispose();const fs = new MemoryFileSystem({ "/": "" });
const perl = await ZeroPerl.create({ fileSystem: fs });
// Write from Perl
await perl.eval(`
$| = 1;
open my $fh, '>', '/output.txt' or die $!;
print $fh "Generated content\\n";
close $fh;
print "File written!\\n";
`);
// Read from JavaScript
const content = fs.readFile('/output.txt');
console.log(content); // "Generated content\n"
perl.dispose();Register JavaScript functions that can be called from Perl:
const perl = await ZeroPerl.create();
perl.registerFunction('add', (a, b) => {
const x = a.toInt();
const y = b.toInt();
return perl.createInt(x + y);
});
await perl.eval(`
$| = 1;
my $sum = add(10, 32);
print "Sum: $sum\\n";
`);
perl.dispose();const perl = await ZeroPerl.create();
perl.registerMethod('Math', 'square', (x) => {
const num = x.toInt();
return perl.createInt(num * num);
});
await perl.eval(`
$| = 1;
my $result = Math::square(7);
print "Square: $result\\n";
`);
perl.dispose();const perl = await ZeroPerl.create();
await perl.eval(`
sub greet {
my ($name) = @_;
return "Hello, $name!";
}
sub get_values {
return (1, 2, 3);
}
`);
// Scalar context (single return value)
const arg = perl.createString("Alice");
const greeting = await perl.call("greet", [arg], "scalar");
console.log(greeting?.toString()); // "Hello, Alice!"
// List context (multiple return values)
const values = await perl.call("get_values", [], "list");
console.log(values.map(v => v.toInt())); // [1, 2, 3]
// Void context (no return value)
await perl.call("some_sub", [], "void");
arg.dispose();
greeting?.dispose();
for (const v of values) v.dispose();
perl.dispose();const perl = await ZeroPerl.create();
const result = await perl.eval(`
die "Something went wrong!";
`);
if (!result.success) {
console.log('Exit code:', result.exitCode);
console.log('Error:', result.error);
}
// Get error directly
const error = perl.getLastError();
console.log(error); // "Something went wrong! at ..."
// Clear error
perl.clearError();
perl.dispose();const perl = await ZeroPerl.create({
env: {
API_KEY: 'secret123',
DEBUG: 'true'
}
});
await perl.eval(`
$| = 1;
print "API Key: $ENV{API_KEY}\\n";
print "Debug: $ENV{DEBUG}\\n";
`);
perl.dispose();const perl = await ZeroPerl.create();
await perl.eval('$counter = 1');
const val1 = perl.getVariable('counter');
console.log(val1?.toInt()); // 1
await perl.reset();
const val2 = perl.getVariable('counter');
console.log(val2); // null
val1?.dispose();
perl.dispose();const perl = await ZeroPerl.create({
stdout: (data) => process.stdout.write(data)
});
for (let i = 0; i < 5; i++) {
await perl.eval('print "."');
perl.flush();
await new Promise(r => setTimeout(r, 500));
}
perl.dispose();With bundler (recommended):
import { ZeroPerl } from '@6over3/zeroperl-ts';
import zeroperl from '@6over3/zeroperl-ts/zeroperl.wasm';
const perl = await ZeroPerl.create({
fetch: () => fetch(zeroperl),
stdout: (data) => console.log(data)
});
await perl.eval(`
$| = 1;
print "Hello from Perl!\\n";
print "Running in: $^O\\n";
`);
perl.dispose();Note: Most bundlers should copy the WASM file when imported explicitly. If your bundler doesn't handle this, configure it to copy static assets or use the CDN approach below.
From CDN:
<!DOCTYPE html>
<html>
<body>
<div id="output"></div>
<script type="module">
import { ZeroPerl } from 'https://esm.sh/@6over3/zeroperl-ts';
const output = document.getElementById('output');
const perl = await ZeroPerl.create({
stdout: (data) => {
const text = typeof data === 'string' ? data : new TextDecoder().decode(data);
output.innerHTML += text.replace(/\n/g, '<br>');
}
});
await perl.eval(`
$| = 1;
print "Hello from Perl!\\n";
print "Running in: $^O\\n";
`);
perl.dispose();
</script>
</body>
</html>Create a new Perl interpreter instance.
Options:
env- Environment variables (Record<string, string>)fileSystem- Virtual filesystem (MemoryFileSystem)stdout- stdout callback ((data: string | Uint8Array) => void)stderr- stderr callback ((data: string | Uint8Array) => void)fetch- Custom fetch for WASM loading
const perl = await ZeroPerl.create({
env: { KEY: 'value' },
fileSystem: fs,
stdout: (data) => console.log(data),
stderr: (data) => console.error(data)
});Evaluate Perl code. Returns { success: boolean, error?: string, exitCode: number }.
const result = await perl.eval('print "Hello\\n"', ['arg1', 'arg2']);Run a Perl script from the virtual filesystem.
await perl.runFile('/script.pl', ['arg1', 'arg2']);perl.createInt(value), perl.createDouble(value), perl.createString(value), perl.createBool(value), perl.createUndef()
Create Perl values. Returns PerlValue.
const num = perl.createInt(42);
const str = perl.createString("hello");
const bool = perl.createBool(true);Create Perl arrays and hashes. Returns PerlArray or PerlHash.
const arr = perl.createArray([1, 2, 3]);
const hash = perl.createHash({ key: 'value' });Convert JavaScript value to Perl. Handles primitives, arrays, and objects.
const perlVal = perl.toPerlValue({ name: 'Alice', age: 30 });Set and get scalar variables. Variable names should not include the $ prefix.
perl.setVariable('x', 42);
const x = perl.getVariable('x');
console.log(x?.toInt()); // 42Get array and hash variables. Returns PerlArray or PerlHash.
const arr = perl.getArrayVariable('myarray');
const hash = perl.getHashVariable('myhash');Register a JavaScript function callable from Perl.
perl.registerFunction('add', (a, b) => {
const x = a.toInt();
const y = b.toInt();
return perl.createInt(x + y);
});Register a JavaScript method callable from Perl.
perl.registerMethod('Math', 'square', (x) => {
const num = x.toInt();
return perl.createInt(num * num);
});Call a Perl function. Context can be "void", "scalar", or "list".
const result = await perl.call('my_sub', [arg1, arg2], 'scalar');
const results = await perl.call('my_sub', [], 'list');
await perl.call('my_sub', [], 'void');Flush output buffers. Required if autoflush ($| = 1) is not set.
await perl.eval('print "text"');
perl.flush();Reset interpreter to clean state. Clears all variables.
await perl.reset();Get and clear the Perl error state ($@).
const error = perl.getLastError();
perl.clearError();Check interpreter state.
const ready = perl.isInitialized() && perl.canEvaluate();Free resources. Use dispose() for normal cleanup, shutdown() for complete termination.
perl.dispose();
// or
perl.shutdown();toInt()- Convert to 32-bit integertoDouble()- Convert to double-precision floattoString()- Convert to UTF-8 stringtoBoolean()- Convert to boolean (Perl truth test)isUndef()- Check if undefinedisRef()- Check if referencegetType()- Get Perl typeproject()- Convert to JavaScript primitivecreateRef()- Create referencederef()- Dereference valuedispose()- Free memory
push(value)- Add to endpop()- Remove from endget(index)- Get value at indexset(index, value)- Set value at indexgetLength()- Get array lengthclear()- Remove all elementstoValue()- Convert to PerlValue (array reference)project()- Convert to JavaScript array[Symbol.iterator]()- Iterate over valuesdispose()- Free memory
set(key, value)- Set key-value pairget(key)- Get value by keyhas(key)- Check if key existsdelete(key)- Delete keyclear()- Remove all entriestoValue()- Convert to PerlValue (hash reference)project()- Convert to JavaScript objectentries()- Iterate over key-value pairskeys()- Iterate over keysvalues()- Iterate over valuesdispose()- Free memory
import { ZeroPerl, MemoryFileSystem } from '@6over3/zeroperl-ts';
const fs = new MemoryFileSystem({ "/": "" });
fs.addFile("/data.json", JSON.stringify({ users: ['Alice', 'Bob'] }));
fs.addFile("/process.pl", `
$| = 1;
use strict;
use warnings;
open my $fh, '<', '/data.json' or die $!;
my $json = do { local $/; <$fh> };
close $fh;
print "Processing: $json\\n";
`);
const perl = await ZeroPerl.create({ fileSystem: fs });
await perl.runFile('/process.pl');
perl.dispose();const perl = await ZeroPerl.create({
stdout: (data) => console.log(data)
});
await perl.eval('$| = 1');
await perl.eval('$x = 10');
await perl.eval('print "$x\\n"');
await perl.eval('$x *= 2');
await perl.eval('print "$x\\n"');
perl.dispose();const perl = await ZeroPerl.create();
perl.setVariable('config', {
server: {
host: 'localhost',
port: 8080
},
features: ['auth', 'logging']
});
await perl.eval(`
$| = 1;
print "Host: $config->{server}{host}\\n";
print "Port: $config->{server}{port}\\n";
print "Features: @{$config->{features}}\\n";
`);
perl.dispose();npm i # Install dependencies
bun run build # Build distributions
bun test # Run testsApache-2.0
ZeroPerl compiles Perl 5 to WebAssembly using a WASI-compliant implementation. This package provides a TypeScript/JavaScript API for the ZeroPerl WASM module.