summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorEarlopain <14981592+Earlopain@users.noreply.github.com>2026-01-24 20:34:23 +0100
committergit <svn-admin@ruby-lang.org>2026-01-24 23:10:34 +0000
commitf7bc28d8244cd69b78382d193421abda1204aa73 (patch)
tree293df0eab0433f65340e3a2f2da8b00413f1b3d3 /lib
parent1de6133825862ef987160a5ba39f5af685610f4e (diff)
[ruby/prism] Further optimize ripper translator by not using `delegate`
Using it seems pretty bad for performance: ```rb require "benchmark/ips" require "prism" require "ripper" codes = Dir["**/*.rb"].map { File.read(it) } Benchmark.ips do |x| x.report("prism") { codes.each { Prism::Translation::Ripper.lex(it) } } x.report("ripper") { codes.each { Ripper.lex(it) } } x.compare! end ``` ``` # Before ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.319 (± 0.0%) i/s (3.14 s/i) - 2.000 in 6.276154s ripper 0.647 (± 0.0%) i/s (1.54 s/i) - 4.000 in 6.182662s Comparison: ripper: 0.6 i/s prism: 0.3 i/s - 2.03x slower # After ruby 4.0.0 (2025-12-25 revision https://github.com/ruby/prism/commit/553f1675f3) +PRISM [x86_64-linux] Warming up -------------------------------------- prism 1.000 i/100ms ripper 1.000 i/100ms Calculating ------------------------------------- prism 0.482 (± 0.0%) i/s (2.08 s/i) - 3.000 in 6.225603s ripper 0.645 (± 0.0%) i/s (1.55 s/i) - 4.000 in 6.205636s Comparison: ripper: 0.6 i/s prism: 0.5 i/s - 1.34x slower ``` `vernier` tells me it does `method_missing` even for explicitly defined methods like `location`. https://github.com/ruby/prism/commit/2ea81398cc
Diffstat (limited to 'lib')
-rw-r--r--lib/prism/lex_compat.rb30
1 files changed, 22 insertions, 8 deletions
diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb
index 597e63c73e..c14a9f1603 100644
--- a/lib/prism/lex_compat.rb
+++ b/lib/prism/lex_compat.rb
@@ -1,8 +1,6 @@
# frozen_string_literal: true
# :markup: markdown
-require "delegate"
-
module Prism
# This class is responsible for lexing the source using prism and then
# converting those tokens to be compatible with Ripper. In the vast majority
@@ -201,27 +199,43 @@ module Prism
# When we produce tokens, we produce the same arrays that Ripper does.
# However, we add a couple of convenience methods onto them to make them a
# little easier to work with. We delegate all other methods to the array.
- class Token < SimpleDelegator
- # @dynamic initialize, each, []
+ class Token < BasicObject
+ # Create a new token object with the given ripper-compatible array.
+ def initialize(array)
+ @array = array
+ end
# The location of the token in the source.
def location
- self[0]
+ @array[0]
end
# The type of the token.
def event
- self[1]
+ @array[1]
end
# The slice of the source that this token represents.
def value
- self[2]
+ @array[2]
end
# The state of the lexer when this token was produced.
def state
- self[3]
+ @array[3]
+ end
+
+ # We want to pretend that this is just an Array.
+ def ==(other) # :nodoc:
+ @array == other
+ end
+
+ def respond_to_missing?(name, include_private = false) # :nodoc:
+ @array.respond_to?(name, include_private)
+ end
+
+ def method_missing(name, ...) # :nodoc:
+ @array.send(name, ...)
end
end