Skip to content

Commit 727b991

Browse files
authored
Merge pull request #170 from ruby-syntax-tree/performance
Performance
2 parents 7aa9312 + 7b2bc9b commit 727b991

12 files changed

+1222
-794
lines changed

.rubocop.yml

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ Style/IdenticalConditionalBranches:
5555
Style/IfInsideElse:
5656
Enabled: false
5757

58+
Style/IfWithBooleanLiteralBranches:
59+
Enabled: false
60+
5861
Style/KeywordParametersOrder:
5962
Enabled: false
6063

Gemfile.lock

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ PATH
22
remote: .
33
specs:
44
syntax_tree (3.6.3)
5-
prettier_print
5+
prettier_print (>= 1.0.0)
66

77
GEM
88
remote: https://rubygems.org/
@@ -17,7 +17,7 @@ GEM
1717
prettier_print (1.0.0)
1818
rainbow (3.1.1)
1919
rake (13.0.6)
20-
regexp_parser (2.5.0)
20+
regexp_parser (2.6.0)
2121
rexml (3.2.5)
2222
rubocop (1.36.0)
2323
json (~> 2.3)
@@ -38,7 +38,7 @@ GEM
3838
simplecov_json_formatter (~> 0.1)
3939
simplecov-html (0.12.3)
4040
simplecov_json_formatter (0.1.4)
41-
unicode-display_width (2.2.0)
41+
unicode-display_width (2.3.0)
4242

4343
PLATFORMS
4444
arm64-darwin-21

bin/profile

+5-6
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,21 @@ require "bundler/inline"
66
gemfile do
77
source "https://rubygems.org"
88
gem "stackprof"
9+
gem "prettier_print"
910
end
1011

1112
$:.unshift(File.expand_path("../lib", __dir__))
1213
require "syntax_tree"
1314

14-
GC.disable
15-
1615
StackProf.run(mode: :cpu, out: "tmp/profile.dump", raw: true) do
17-
filepath = File.expand_path("../lib/syntax_tree/node.rb", __dir__)
18-
SyntaxTree.format(File.read(filepath))
16+
Dir[File.join(RbConfig::CONFIG["libdir"], "**/*.rb")].each do |filepath|
17+
SyntaxTree.format(SyntaxTree.read(filepath))
18+
end
1919
end
2020

21-
GC.enable
22-
2321
File.open("tmp/flamegraph.html", "w") do |file|
2422
report = Marshal.load(IO.binread("tmp/profile.dump"))
23+
StackProf::Report.new(report).print_text
2524
StackProf::Report.new(report).print_d3_flamegraph(file)
2625
end
2726

lib/syntax_tree.rb

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# frozen_string_literal: true
22

3-
require "delegate"
43
require "etc"
54
require "json"
65
require "pp"
@@ -10,7 +9,6 @@
109

1110
require_relative "syntax_tree/formatter"
1211
require_relative "syntax_tree/node"
13-
require_relative "syntax_tree/parser"
1412
require_relative "syntax_tree/version"
1513

1614
require_relative "syntax_tree/basic_visitor"
@@ -20,6 +18,18 @@
2018
require_relative "syntax_tree/visitor/match_visitor"
2119
require_relative "syntax_tree/visitor/pretty_print_visitor"
2220

21+
require_relative "syntax_tree/parser"
22+
23+
# We rely on Symbol#name being available, which is only available in Ruby 3.0+.
24+
# In case we're running on an older Ruby version, we polyfill it here.
25+
unless :+.respond_to?(:name)
26+
class Symbol # rubocop:disable Style/Documentation
27+
def name
28+
to_s.freeze
29+
end
30+
end
31+
end
32+
2333
# Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
2434
# provides the ability to generate a syntax tree from source, as well as the
2535
# tools necessary to inspect and manipulate that syntax tree. It can be used to

lib/syntax_tree/formatter.rb

+67-8
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,39 @@ def format(node, stackable: true)
6262
# If there are comments, then we're going to format them around the node
6363
# so that they get printed properly.
6464
if node.comments.any?
65-
leading, trailing = node.comments.partition(&:leading?)
65+
trailing = []
66+
last_leading = nil
6667

67-
# Print all comments that were found before the node.
68-
leading.each do |comment|
69-
comment.format(self)
70-
breakable(force: true)
68+
# First, we're going to print all of the comments that were found before
69+
# the node. We'll also gather up any trailing comments that we find.
70+
node.comments.each do |comment|
71+
if comment.leading?
72+
comment.format(self)
73+
breakable(force: true)
74+
last_leading = comment
75+
else
76+
trailing << comment
77+
end
7178
end
7279

7380
# If the node has a stree-ignore comment right before it, then we're
7481
# going to just print out the node as it was seen in the source.
7582
doc =
76-
if leading.last&.ignore?
83+
if last_leading&.ignore?
7784
range = source[node.location.start_char...node.location.end_char]
78-
separator = -> { breakable(indent: false, force: true) }
79-
seplist(range.split(/\r?\n/, -1), separator) { |line| text(line) }
85+
first = true
86+
87+
range.each_line(chomp: true) do |line|
88+
if first
89+
first = false
90+
else
91+
breakable_return
92+
end
93+
94+
text(line)
95+
end
96+
97+
breakable_return if range.end_with?("\n")
8098
else
8199
node.format(self)
82100
end
@@ -101,12 +119,53 @@ def format_each(nodes)
101119
nodes.each { |node| format(node) }
102120
end
103121

122+
def grandparent
123+
stack[-3]
124+
end
125+
104126
def parent
105127
stack[-2]
106128
end
107129

108130
def parents
109131
stack[0...-1].reverse_each
110132
end
133+
134+
# This is a simplified version of prettyprint's group. It doesn't provide
135+
# any of the more advanced options because we don't need them and they take
136+
# up expensive computation time.
137+
def group
138+
contents = []
139+
doc = Group.new(0, contents: contents)
140+
141+
groups << doc
142+
target << doc
143+
144+
with_target(contents) { yield }
145+
groups.pop
146+
doc
147+
end
148+
149+
# A similar version to the super, except that it calls back into the
150+
# separator proc with the instance of `self`.
151+
def seplist(list, sep = nil, iter_method = :each)
152+
first = true
153+
list.__send__(iter_method) do |*v|
154+
if first
155+
first = false
156+
elsif sep
157+
sep.call(self)
158+
else
159+
comma_breakable
160+
end
161+
yield(*v)
162+
end
163+
end
164+
165+
# This is a much simplified version of prettyprint's text. It avoids
166+
# calculating width by pushing the string directly onto the target.
167+
def text(string)
168+
target << string
169+
end
111170
end
112171
end

0 commit comments

Comments
 (0)