peruby 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rubocop.yml +35 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +92 -0
- data/bin/peruby +9 -0
- data/doc/COMPAT.md +54 -0
- data/doc/CONTRIBUTING.md +21 -0
- data/doc/DESIGN.md +25 -0
- data/doc/INCOMPATIBILITIES.md +30 -0
- data/doc/PERF.md +51 -0
- data/doc/ROADMAP.md +18 -0
- data/examples/hello.pl +1 -0
- data/examples/json.pl +2 -0
- data/examples/object.pl +5 -0
- data/examples/word_count.pl +6 -0
- data/lib/peruby/cli.rb +224 -0
- data/lib/peruby/compile_unit.rb +103 -0
- data/lib/peruby/compiler.rb +224 -0
- data/lib/peruby/errors.rb +40 -0
- data/lib/peruby/lexer/heredoc.rb +8 -0
- data/lib/peruby/lexer/keywords.rb +63 -0
- data/lib/peruby/lexer/number.rb +32 -0
- data/lib/peruby/lexer/quote_like.rb +72 -0
- data/lib/peruby/lexer/source_scanner.rb +104 -0
- data/lib/peruby/lexer/state.rb +46 -0
- data/lib/peruby/lexer/structure_scanner.rb +149 -0
- data/lib/peruby/lexer/term_scanner.rb +301 -0
- data/lib/peruby/lexer/token.rb +16 -0
- data/lib/peruby/lexer.rb +123 -0
- data/lib/peruby/node.rb +88 -0
- data/lib/peruby/op/assign.rb +128 -0
- data/lib/peruby/op/builtin.rb +903 -0
- data/lib/peruby/op/call.rb +378 -0
- data/lib/peruby/op/control.rb +256 -0
- data/lib/peruby/op/element.rb +136 -0
- data/lib/peruby/op/expression.rb +342 -0
- data/lib/peruby/op/io.rb +113 -0
- data/lib/peruby/op/list.rb +102 -0
- data/lib/peruby/op/literal.rb +84 -0
- data/lib/peruby/op/loop.rb +158 -0
- data/lib/peruby/op/regexp.rb +288 -0
- data/lib/peruby/op/variable.rb +534 -0
- data/lib/peruby/op.rb +47 -0
- data/lib/peruby/parser/grammar.rb +5797 -0
- data/lib/peruby/parser/grammar.y +576 -0
- data/lib/peruby/parser.rb +14 -0
- data/lib/peruby/runtime/code.rb +21 -0
- data/lib/peruby/runtime/conv.rb +140 -0
- data/lib/peruby/runtime/directory_handle.rb +18 -0
- data/lib/peruby/runtime/env.rb +129 -0
- data/lib/peruby/runtime/glob.rb +24 -0
- data/lib/peruby/runtime/interpolation.rb +223 -0
- data/lib/peruby/runtime/io_handle.rb +37 -0
- data/lib/peruby/runtime/local_stack.rb +90 -0
- data/lib/peruby/runtime/match_state.rb +62 -0
- data/lib/peruby/runtime/module_loader.rb +133 -0
- data/lib/peruby/runtime/mro.rb +94 -0
- data/lib/peruby/runtime/perl_array.rb +81 -0
- data/lib/peruby/runtime/perl_hash.rb +57 -0
- data/lib/peruby/runtime/ref.rb +43 -0
- data/lib/peruby/runtime/regexp_compiler.rb +75 -0
- data/lib/peruby/runtime/scalar.rb +27 -0
- data/lib/peruby/runtime/sprintf.rb +54 -0
- data/lib/peruby/runtime/stash.rb +50 -0
- data/lib/peruby/runtime/test_builder.rb +47 -0
- data/lib/peruby/runtime.rb +325 -0
- data/lib/peruby/validator.rb +236 -0
- data/lib/peruby/version.rb +5 -0
- data/lib/peruby.rb +31 -0
- data/t/00-basic.t +5 -0
- data/t/lib/MiniTest.pm +22 -0
- metadata +130 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e7c3d9d50d299d8c07b711e9dc07d689676750015c59489fe118916956d39dae
|
|
4
|
+
data.tar.gz: 9593822800bebfab84aa02c90e1d7892c3f025e4c782ed2c1f860ddb55aed4fa
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b316119852f0a061ac46b578952b3c10c5cd51de7310ed1a68e8528582f73bc5b034ba5d9eb113e0f6eff7093b8c3416875f8fabba30704a3c0102bc7b18e7a1
|
|
7
|
+
data.tar.gz: 8daeab8accd567dde3db0fddbfdb594657c184f09e34f06fdf772768f86e6f258376cdd978f9ec84dbc44385e8b218d4e92bd7a67bed9d34cadb4c6289be86ec
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.2
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- "lib/peruby/parser/grammar.rb"
|
|
7
|
+
- "tmp/**/*"
|
|
8
|
+
- "vendor/**/*"
|
|
9
|
+
|
|
10
|
+
Layout/LineLength:
|
|
11
|
+
Max: 120
|
|
12
|
+
|
|
13
|
+
Metrics/MethodLength:
|
|
14
|
+
Max: 30
|
|
15
|
+
|
|
16
|
+
Metrics/AbcSize:
|
|
17
|
+
Max: 30
|
|
18
|
+
Exclude:
|
|
19
|
+
- "test/**/*"
|
|
20
|
+
|
|
21
|
+
Metrics/ClassLength:
|
|
22
|
+
Max: 200
|
|
23
|
+
|
|
24
|
+
Metrics/ModuleLength:
|
|
25
|
+
Max: 200
|
|
26
|
+
|
|
27
|
+
Metrics/CyclomaticComplexity:
|
|
28
|
+
Max: 15
|
|
29
|
+
|
|
30
|
+
Metrics/PerceivedComplexity:
|
|
31
|
+
Max: 15
|
|
32
|
+
|
|
33
|
+
Lint/MissingSuper:
|
|
34
|
+
Exclude:
|
|
35
|
+
- "lib/peruby/op/**/*.rb"
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-08-30
|
|
4
|
+
|
|
5
|
+
- Implement the L4 interpreter pipeline, value model, contexts, and aliases.
|
|
6
|
+
- Add regexes, references, file I/O, OO dispatch, modules, phases, and selected
|
|
7
|
+
core modules.
|
|
8
|
+
- Add Perl-compatible CLI processing, diagnostics, oracle tests, compatibility
|
|
9
|
+
reports, and benchmarks.
|
|
10
|
+
- Add lambda-composed execution with a tree-interpreter fallback.
|
|
11
|
+
- Complete process, signal, diamond I/O, packing, module-loading, and overload behavior.
|
|
12
|
+
- Match the system Perl on all 615 scripts in the oracle corpus.
|
|
13
|
+
|
|
14
|
+
Peruby follows semantic versioning. Until 1.0, minor releases may expand or
|
|
15
|
+
correct Perl semantics; patch releases remain backward-compatible bug fixes.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yudai Takada
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Peruby
|
|
2
|
+
|
|
3
|
+
Peruby is a Perl 5 interpreter written in pure Ruby. It runs a practical L4
|
|
4
|
+
subset: procedural Perl, regexes, references, file I/O, objects, `use` /
|
|
5
|
+
`require`, compile phases, and selected core modules. It does not invoke a Perl
|
|
6
|
+
binary at runtime.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Peruby requires Ruby 3.2 or newer.
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
gem install peruby
|
|
14
|
+
peruby --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
From a checkout, use `bundle exec bin/peruby`.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
peruby script.pl arg1 arg2
|
|
23
|
+
peruby -e 'print "hello\n";'
|
|
24
|
+
peruby -n -l -a -F: -e 'print $F[0];' data.txt
|
|
25
|
+
peruby -MList::Util=sum -e 'print sum(1, 2, 3);'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Supported command switches include `-e`, `-E`, `-c`, `-I`, `-n`, `-p`, `-l`,
|
|
29
|
+
`-a`, `-F`, `-0`, `-M`, `-m`, and `-s`. Debugging switches include
|
|
30
|
+
`--dump-tokens`, `--dump-ast`, `--dump-ops`, `--trace-ops`, and
|
|
31
|
+
`--interpreter=tree`. Set `PERUBY_DEBUG=lexer,parser` for both front-end traces.
|
|
32
|
+
|
|
33
|
+
The exact measured scope is in [the compatibility report](doc/COMPAT.md), and
|
|
34
|
+
known differences are listed in [INCOMPATIBILITIES.md](doc/INCOMPATIBILITIES.md).
|
|
35
|
+
Pure-Perl CPAN compatibility is ongoing L5 work rather than a current promise.
|
|
36
|
+
|
|
37
|
+
## Development
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
bundle install
|
|
41
|
+
bundle exec rake
|
|
42
|
+
bundle exec rake oracle
|
|
43
|
+
bundle exec rake bench
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
See [CONTRIBUTING.md](doc/CONTRIBUTING.md), [DESIGN.md](doc/DESIGN.md), and the
|
|
47
|
+
[roadmap](doc/ROADMAP.md).
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT. See [LICENSE.txt](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
|
|
7
|
+
GRAMMAR_Y = 'lib/peruby/parser/grammar.y'
|
|
8
|
+
GRAMMAR_RB = 'lib/peruby/parser/grammar.rb'
|
|
9
|
+
GRAMMAR_OUTPUT = 'tmp/grammar.output'
|
|
10
|
+
|
|
11
|
+
desc 'Generate the parser'
|
|
12
|
+
task :parser do
|
|
13
|
+
mkdir_p 'tmp'
|
|
14
|
+
sh 'racc', '-F', '-o', GRAMMAR_RB, '-v', '-O', GRAMMAR_OUTPUT, GRAMMAR_Y
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
namespace :grammar do
|
|
18
|
+
desc 'Check that the generated grammar has no undocumented conflicts'
|
|
19
|
+
task check: :parser do
|
|
20
|
+
ruby 'tools/conflict_check.rb', GRAMMAR_OUTPUT
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Rake::TestTask.new(:test) do |task|
|
|
25
|
+
task.libs << 'test'
|
|
26
|
+
task.pattern = 'test/**/*_test.rb'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
desc 'Run Perl TAP tests with peruby'
|
|
30
|
+
task :t do
|
|
31
|
+
files = Dir['t/*.t']
|
|
32
|
+
tests = files.sum do |file|
|
|
33
|
+
stdout, stderr, status = Open3.capture3('bin/peruby', '-I', 't/lib', file)
|
|
34
|
+
print stdout
|
|
35
|
+
warn stderr unless stderr.empty?
|
|
36
|
+
planned = stdout.lines.filter_map { |line| line[/^1\.\.(\d+)/, 1]&.to_i }.last
|
|
37
|
+
passed = stdout.lines.count { |line| line.match?(/^ok \d+/) }
|
|
38
|
+
failed = stdout.lines.any? { |line| line.match?(/^not ok \d+/) }
|
|
39
|
+
raise "#{file}: invalid or failing TAP" unless status.success? && planned == passed && !failed
|
|
40
|
+
|
|
41
|
+
passed
|
|
42
|
+
end
|
|
43
|
+
puts "#{files.length} file(s), #{tests} test(s), all successful"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
desc 'Compare oracle scripts with perl'
|
|
47
|
+
task :oracle do
|
|
48
|
+
ruby 'tools/oracle_runner.rb'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
namespace :oracle do
|
|
52
|
+
desc 'Record oracle results from perl'
|
|
53
|
+
task :record do
|
|
54
|
+
ruby 'tools/oracle_runner.rb', '--record'
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
namespace :compat do
|
|
59
|
+
desc 'Report compatibility for the migrated perl op tests'
|
|
60
|
+
task :perl do
|
|
61
|
+
ruby 'tools/compat_report.rb'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc 'Report compatibility for unpacked CPAN distributions'
|
|
65
|
+
task :cpan do
|
|
66
|
+
ruby 'tools/cpan_report.rb', *ENV.fetch('DISTS', '').split
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
desc 'Run RuboCop'
|
|
71
|
+
task :rubocop do
|
|
72
|
+
sh 'rubocop', '--cache', 'false'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
desc 'Compare peruby and perl benchmark timings'
|
|
76
|
+
task :bench do
|
|
77
|
+
ruby 'tools/benchmark.rb'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
namespace :bench do
|
|
81
|
+
desc 'Measure compiled execution without process startup'
|
|
82
|
+
task :execution do
|
|
83
|
+
ruby 'tools/execution_benchmark.rb'
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
desc 'Count interpreter method calls for one benchmark'
|
|
88
|
+
task :profile do
|
|
89
|
+
ruby 'tools/profile.rb', ENV.fetch('SCRIPT', 'bench/fib.pl')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
task default: %i[parser test grammar:check]
|
data/bin/peruby
ADDED
data/doc/COMPAT.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Compatibility report
|
|
2
|
+
|
|
3
|
+
Measured on 2026-08-31 by comparing each output line with the system Perl.
|
|
4
|
+
|
|
5
|
+
## Oracle corpus
|
|
6
|
+
|
|
7
|
+
All 617 scripts pass against the system Perl (100%). The corpus covers context,
|
|
8
|
+
regexes, formatting, sorting, references, objects, I/O, numeric/string behavior,
|
|
9
|
+
and dynamic scoping.
|
|
10
|
+
|
|
11
|
+
## Migrated perl `t/op` subsets
|
|
12
|
+
|
|
13
|
+
Run `bundle exec rake compat:perl` to reproduce this table.
|
|
14
|
+
|
|
15
|
+
| file | passed | cases | rate |
|
|
16
|
+
|---|---:|---:|---:|
|
|
17
|
+
| arith.pl | 4 | 4 | 100.0% |
|
|
18
|
+
| cond.pl | 4 | 4 | 100.0% |
|
|
19
|
+
| context.pl | 4 | 4 | 100.0% |
|
|
20
|
+
| local.pl | 4 | 4 | 100.0% |
|
|
21
|
+
| ref.pl | 4 | 4 | 100.0% |
|
|
22
|
+
| sort.pl | 4 | 4 | 100.0% |
|
|
23
|
+
| substr.pl | 4 | 4 | 100.0% |
|
|
24
|
+
|
|
25
|
+
These are small, independently written adaptations of the corresponding Perl
|
|
26
|
+
core test themes; the Perl source distribution itself is not vendored.
|
|
27
|
+
All migrated operator cases currently match the system Perl.
|
|
28
|
+
|
|
29
|
+
## CPAN suites
|
|
30
|
+
|
|
31
|
+
The official release archives were unpacked outside the repository and every
|
|
32
|
+
top-level `t/*.t` file was run with its distribution `lib/` directory on
|
|
33
|
+
peruby's include path. A file passes only when it exits successfully and emits
|
|
34
|
+
a complete, nonempty TAP plan with no failing result (TODO failures are allowed).
|
|
35
|
+
Skip-all files are reported separately and do not count as passes.
|
|
36
|
+
|
|
37
|
+
| distribution | passed | skipped | files | rate |
|
|
38
|
+
|---|---:|---:|---:|---:|
|
|
39
|
+
| Text-CSV-2.06 (`Text::CSV_PP`) | 0 | 0 | 40 | 0.0% |
|
|
40
|
+
| URI-5.36 (`URI::Escape`) | 0 | 0 | 64 | 0.0% |
|
|
41
|
+
| MIME-Base64-3.16 (`MIME::Base64`) | 0 | 0 | 6 | 0.0% |
|
|
42
|
+
| Time-Local-1.35 (`Time::Local`) | 0 | 0 | 2 | 0.0% |
|
|
43
|
+
| Try-Tiny-0.32 (`Try::Tiny`) | 0 | 0 | 11 | 0.0% |
|
|
44
|
+
|
|
45
|
+
Run the same measurement after unpacking the releases:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
DISTS="/path/Text-CSV-2.06 /path/URI-5.36 /path/MIME-Base64-3.16 \
|
|
49
|
+
/path/Time-Local-1.35 /path/Try-Tiny-0.32" bundle exec rake compat:cpan
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This is an L5 baseline, not a claim of CPAN compatibility. Most files stop on
|
|
53
|
+
unsupported prototype/import syntax and module APIs before producing passing
|
|
54
|
+
TAP. Release archives are not vendored.
|
data/doc/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Use Ruby 3.2 or newer and install dependencies with `bundle install`.
|
|
4
|
+
|
|
5
|
+
Before submitting a change, run:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bundle exec rake test grammar:check
|
|
9
|
+
bundle exec rubocop --cache false
|
|
10
|
+
bundle exec rake oracle
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When changing `lib/peruby/parser/grammar.y`, regenerate and commit
|
|
14
|
+
`grammar.rb` in the same change. New Perl behavior needs a focused unit or
|
|
15
|
+
integration test and, when output compatibility matters, an oracle script with
|
|
16
|
+
its real-Perl expectation. Document deliberate incompatibilities in
|
|
17
|
+
`doc/INCOMPATIBILITIES.md`.
|
|
18
|
+
|
|
19
|
+
Keep Ops small and single-purpose. Preserve scalar/list/void context, aliasing,
|
|
20
|
+
`undef`, and exception restoration paths in tests. Do not add a dependency
|
|
21
|
+
when Ruby's standard library covers the behavior.
|
data/doc/DESIGN.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Design overview
|
|
2
|
+
|
|
3
|
+
Peruby is a pure-Ruby tree interpreter with an optional lambda-composed fast
|
|
4
|
+
path. Source passes through these stages:
|
|
5
|
+
|
|
6
|
+
1. the stateful lexer resolves Perl's quote, slash, and brace ambiguities;
|
|
7
|
+
2. the generated Racc parser creates immutable nodes with file/line metadata;
|
|
8
|
+
3. the compiler produces small `Peruby::Op` objects;
|
|
9
|
+
4. hot Ops compose into Ruby lambdas, while unsupported shapes call the same
|
|
10
|
+
tree Ops; `--interpreter=tree` disables composition;
|
|
11
|
+
5. `Peruby::Runtime` owns packages, lexicals, I/O, regex state, phases, modules,
|
|
12
|
+
and object destruction.
|
|
13
|
+
|
|
14
|
+
Scalars are mutable cells so aliases in `@_`, `foreach`, and `map` share the
|
|
15
|
+
same storage. Arrays and hashes contain cells. Every operation receives an
|
|
16
|
+
explicit scalar/list/void context.
|
|
17
|
+
|
|
18
|
+
`CompileUnit` makes nested `require`, string `eval`, and compile phases
|
|
19
|
+
reentrant. User `@INC` paths are searched before internal Ruby-backed modules.
|
|
20
|
+
The symbol table and lexical environment carry generation counters so cached
|
|
21
|
+
lookups are invalidated by declarations and subroutine redefinition.
|
|
22
|
+
|
|
23
|
+
The generated parser is committed beside `grammar.y`. `rake grammar:check`
|
|
24
|
+
enforces zero undocumented conflicts. Compatibility is measured against the
|
|
25
|
+
system Perl rather than inferred from unit tests alone.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Known incompatibilities
|
|
2
|
+
|
|
3
|
+
This list reflects measurements made on 2026-08-31. See [COMPAT.md](COMPAT.md)
|
|
4
|
+
for the reproducible reports.
|
|
5
|
+
|
|
6
|
+
| Area | Current behavior | Workaround or status |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `DESTROY` timing | Runs at shutdown; `--refcount` provides an eager approximation | Do not depend on exact Perl reference-count timing |
|
|
9
|
+
| Hash key order | Preserves insertion order instead of Perl's randomized order | Do not depend on iteration order |
|
|
10
|
+
| XS modules | Native extensions cannot load | Use a pure-Ruby internal replacement where provided |
|
|
11
|
+
| `format` / `write` | Not implemented | None |
|
|
12
|
+
| Taint mode | `-T` is rejected | None |
|
|
13
|
+
| Source filters | Not implemented | None |
|
|
14
|
+
| Regex `(?{ })` and `(??{ })` | Rejected at compilation | None |
|
|
15
|
+
| ithreads | Not implemented | None |
|
|
16
|
+
| Indirect object syntax | `new Foo(...)` is not parsed | Use `Foo->new(...)` |
|
|
17
|
+
| Byte/character duality | Ruby encoding is used; Perl's UTF8 flag is not reproduced | Keep inputs in one explicit encoding |
|
|
18
|
+
| Integer overflow | Uses Ruby integers rather than Perl IV overflow | Check numeric bounds explicitly |
|
|
19
|
+
| Float stringification | Close to `%.15g`; extreme values can differ | Format explicitly with `sprintf` |
|
|
20
|
+
| `local` references | Restoration works, but references taken before localization can differ | Avoid retaining localized slots |
|
|
21
|
+
| Prototypes | `*`, `+`, and `_` prototype behavior is incomplete | Use explicit parentheses |
|
|
22
|
+
| `goto LABEL` | Not implemented; `goto &sub` is supported | Restructure label jumps |
|
|
23
|
+
| `dump`, `study`, `reset` | Not implemented | Remove obsolete calls |
|
|
24
|
+
| `%SIG` | Uses Ruby `Signal.trap`; signals unavailable on the host remain unsupported | Restrict handlers to portable signal names |
|
|
25
|
+
| Runtime error locations | Perl-shaped `at FILE line N.` output is emitted, but runtime line tracking currently reports line 1 | Use stack-free messages only for compatibility checks |
|
|
26
|
+
| CPAN ecosystem | The five P14-03 upstream suites currently pass 0/123 files | L5 work; core-module substitutes remain available |
|
|
27
|
+
|
|
28
|
+
All 617 scripts in the current oracle corpus match Perl. The remaining rows are
|
|
29
|
+
outside that measured corpus or are architectural differences that cannot be
|
|
30
|
+
removed by output-level compatibility tests.
|
data/doc/PERF.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Performance
|
|
2
|
+
|
|
3
|
+
Measurements were taken on 2026-08-30 with Ruby 4.0.0 and the system Perl.
|
|
4
|
+
`RUNS=5 bundle exec rake bench` reports the median wall time including process
|
|
5
|
+
startup. Absolute values vary by host; the before/after comparison used the
|
|
6
|
+
same machine and scripts.
|
|
7
|
+
|
|
8
|
+
| benchmark | P14 baseline (s) | compiled (s) | improvement |
|
|
9
|
+
|---|---:|---:|---:|
|
|
10
|
+
| fib.pl | 0.6183 | 0.4915 | 20.5% |
|
|
11
|
+
| loop.pl | 1.3195 | 0.4644 | 64.8% |
|
|
12
|
+
| method.pl | 1.0338 | 0.6404 | 38.1% |
|
|
13
|
+
| regexp.pl | 1.2591 | 0.4812 | 61.8% |
|
|
14
|
+
| total | 4.2307 | 2.0775 | 50.9% (2.04x) |
|
|
15
|
+
|
|
16
|
+
Process startup puts a fixed floor under the command comparison. The P15-05
|
|
17
|
+
acceptance measurement compiles once and times execution with
|
|
18
|
+
`bundle exec rake bench:execution`. Set `INTERPRETER=tree` to measure the
|
|
19
|
+
fallback with the same harness:
|
|
20
|
+
|
|
21
|
+
| benchmark | P14 tree (s) | compiled (s) | speedup |
|
|
22
|
+
|---|---:|---:|---:|
|
|
23
|
+
| fib.pl | 0.3672 | 0.2766 | 1.33x |
|
|
24
|
+
| loop.pl | 0.5486 | 0.2840 | 1.93x |
|
|
25
|
+
| method.pl | 0.6302 | 0.4531 | 1.39x |
|
|
26
|
+
| regexp.pl | 1.5173 | 0.2987 | 5.08x |
|
|
27
|
+
| total | 3.0633 | 1.3124 | 2.33x |
|
|
28
|
+
|
|
29
|
+
The loop target of at least 30%, the fib target of at least 20%, and the 2x
|
|
30
|
+
combined execution target are met. Use `INTERPRETER=tree` for
|
|
31
|
+
`bench:execution`, or `PERUBY_ARGS=--interpreter=tree` for the process-level
|
|
32
|
+
benchmark, to measure the fallback tree walker.
|
|
33
|
+
|
|
34
|
+
## Hotspots and changes
|
|
35
|
+
|
|
36
|
+
Run `SCRIPT=bench/fib.pl bundle exec rake profile` for a stdlib-only method-call
|
|
37
|
+
profile. `stackprof` and `vernier` are deliberately not development dependencies;
|
|
38
|
+
TracePoint keeps the profile reproducible with the supported Ruby alone.
|
|
39
|
+
|
|
40
|
+
The measured hot paths led to these changes:
|
|
41
|
+
|
|
42
|
+
- compound assignment applies coercion directly instead of allocating three temporary Ops;
|
|
43
|
+
- lexical and code-glob lookup use generation-based caches with invalidation on declarations and redefinition;
|
|
44
|
+
- simple final `return` avoids a throw/catch round trip;
|
|
45
|
+
- static regexes and simple interpolation templates compile once;
|
|
46
|
+
- hot variable, argument, expression, branch, loop, method, and regexp paths compose into lambdas, with
|
|
47
|
+
`--interpreter=tree` as fallback.
|
|
48
|
+
|
|
49
|
+
Regex compilation was already cached by pattern and modifiers. Method resolution
|
|
50
|
+
now has the same symbol-generation fast path. Further work should target call
|
|
51
|
+
frame allocation and method arguments; those dominate the remaining gap.
|
data/doc/ROADMAP.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
The 0.1 line targets L4: modules and the selected core library on top of the
|
|
4
|
+
implemented procedural, regex, reference, I/O, and OO layers.
|
|
5
|
+
|
|
6
|
+
L5 work is driven by measured CPAN failures:
|
|
7
|
+
|
|
8
|
+
- complete prototype and import syntax used by upstream test suites;
|
|
9
|
+
- implement the remaining `Test::Builder` surface and TAP directives;
|
|
10
|
+
- extend operator overloading beyond the L4 set and add tied variables;
|
|
11
|
+
- support `Text::CSV_PP`, `URI::Escape`, `MIME::Base64` pure-Perl paths,
|
|
12
|
+
`Time::Local`, and `Try::Tiny` one suite at a time;
|
|
13
|
+
- improve runtime line tracking and Perl's byte/UTF8 duality;
|
|
14
|
+
- raise the official CPAN result from the current 0/123 files while keeping the
|
|
15
|
+
617-script oracle corpus at 100%.
|
|
16
|
+
|
|
17
|
+
XS, ithreads, taint mode, source filters, and regex code execution remain out
|
|
18
|
+
of scope.
|
data/examples/hello.pl
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
print "Hello from peruby\n";
|
data/examples/json.pl
ADDED
data/examples/object.pl
ADDED