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.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +35 -0
  3. data/CHANGELOG.md +15 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +51 -0
  6. data/Rakefile +92 -0
  7. data/bin/peruby +9 -0
  8. data/doc/COMPAT.md +54 -0
  9. data/doc/CONTRIBUTING.md +21 -0
  10. data/doc/DESIGN.md +25 -0
  11. data/doc/INCOMPATIBILITIES.md +30 -0
  12. data/doc/PERF.md +51 -0
  13. data/doc/ROADMAP.md +18 -0
  14. data/examples/hello.pl +1 -0
  15. data/examples/json.pl +2 -0
  16. data/examples/object.pl +5 -0
  17. data/examples/word_count.pl +6 -0
  18. data/lib/peruby/cli.rb +224 -0
  19. data/lib/peruby/compile_unit.rb +103 -0
  20. data/lib/peruby/compiler.rb +224 -0
  21. data/lib/peruby/errors.rb +40 -0
  22. data/lib/peruby/lexer/heredoc.rb +8 -0
  23. data/lib/peruby/lexer/keywords.rb +63 -0
  24. data/lib/peruby/lexer/number.rb +32 -0
  25. data/lib/peruby/lexer/quote_like.rb +72 -0
  26. data/lib/peruby/lexer/source_scanner.rb +104 -0
  27. data/lib/peruby/lexer/state.rb +46 -0
  28. data/lib/peruby/lexer/structure_scanner.rb +149 -0
  29. data/lib/peruby/lexer/term_scanner.rb +301 -0
  30. data/lib/peruby/lexer/token.rb +16 -0
  31. data/lib/peruby/lexer.rb +123 -0
  32. data/lib/peruby/node.rb +88 -0
  33. data/lib/peruby/op/assign.rb +128 -0
  34. data/lib/peruby/op/builtin.rb +903 -0
  35. data/lib/peruby/op/call.rb +378 -0
  36. data/lib/peruby/op/control.rb +256 -0
  37. data/lib/peruby/op/element.rb +136 -0
  38. data/lib/peruby/op/expression.rb +342 -0
  39. data/lib/peruby/op/io.rb +113 -0
  40. data/lib/peruby/op/list.rb +102 -0
  41. data/lib/peruby/op/literal.rb +84 -0
  42. data/lib/peruby/op/loop.rb +158 -0
  43. data/lib/peruby/op/regexp.rb +288 -0
  44. data/lib/peruby/op/variable.rb +534 -0
  45. data/lib/peruby/op.rb +47 -0
  46. data/lib/peruby/parser/grammar.rb +5797 -0
  47. data/lib/peruby/parser/grammar.y +576 -0
  48. data/lib/peruby/parser.rb +14 -0
  49. data/lib/peruby/runtime/code.rb +21 -0
  50. data/lib/peruby/runtime/conv.rb +140 -0
  51. data/lib/peruby/runtime/directory_handle.rb +18 -0
  52. data/lib/peruby/runtime/env.rb +129 -0
  53. data/lib/peruby/runtime/glob.rb +24 -0
  54. data/lib/peruby/runtime/interpolation.rb +223 -0
  55. data/lib/peruby/runtime/io_handle.rb +37 -0
  56. data/lib/peruby/runtime/local_stack.rb +90 -0
  57. data/lib/peruby/runtime/match_state.rb +62 -0
  58. data/lib/peruby/runtime/module_loader.rb +133 -0
  59. data/lib/peruby/runtime/mro.rb +94 -0
  60. data/lib/peruby/runtime/perl_array.rb +81 -0
  61. data/lib/peruby/runtime/perl_hash.rb +57 -0
  62. data/lib/peruby/runtime/ref.rb +43 -0
  63. data/lib/peruby/runtime/regexp_compiler.rb +75 -0
  64. data/lib/peruby/runtime/scalar.rb +27 -0
  65. data/lib/peruby/runtime/sprintf.rb +54 -0
  66. data/lib/peruby/runtime/stash.rb +50 -0
  67. data/lib/peruby/runtime/test_builder.rb +47 -0
  68. data/lib/peruby/runtime.rb +325 -0
  69. data/lib/peruby/validator.rb +236 -0
  70. data/lib/peruby/version.rb +5 -0
  71. data/lib/peruby.rb +31 -0
  72. data/t/00-basic.t +5 -0
  73. data/t/lib/MiniTest.pm +22 -0
  74. 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
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+
6
+ require 'peruby'
7
+ require 'peruby/cli'
8
+
9
+ exit Peruby::CLI.run(ARGV)
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.
@@ -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
@@ -0,0 +1,2 @@
1
+ use JSON::PP qw(encode_json);
2
+ print encode_json({language => "Perl", numbers => [1, 2, 3]}), "\n";
@@ -0,0 +1,5 @@
1
+ package Greeter;
2
+ sub new { my ($class, $name) = @_; return bless {name => $name}, $class; }
3
+ sub hello { my ($self) = @_; print "Hello, ", $self->{name}, "\n"; }
4
+ package main;
5
+ Greeter->new("Ruby")->hello();
@@ -0,0 +1,6 @@
1
+ my %count;
2
+ for my $word (split /\s+/, "the quick brown fox the fox") {
3
+ my $current = $count{$word};
4
+ $count{$word} = $current + 1;
5
+ }
6
+ print "fox=", $count{fox}, "\n";