Skip to content

Commit c6b9df3

Browse files
committed
Migrate to Prism parser.
1 parent e370ca4 commit c6b9df3

19 files changed

Lines changed: 585 additions & 301 deletions

File tree

decode.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
2525

2626
spec.required_ruby_version = ">= 3.2"
2727

28-
spec.add_dependency "parser"
28+
spec.add_dependency "prism"
2929
end

lib/decode/definition.rb

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@ class Definition
1313
# @parameter parent [Symbol] The parent lexical scope.
1414
# @parameter language [Language] The language in which the symbol is defined in.
1515
# @parameter comments [Array(String)] The comments associated with the definition.
16-
def initialize(name, parent: nil, language: parent.language, comments: nil)
17-
@name = name
16+
def initialize(path, parent: nil, language: parent.language, comments: nil, visibility: :public)
17+
@path = Array(path).map(&:to_sym)
1818

1919
@parent = parent
2020
@language = language
2121

2222
@comments = comments
23+
@visibility = visibility
2324

24-
@path = nil
25+
@full_path = nil
2526
@qualified_name = nil
27+
@nested_name = nil
2628
end
2729

2830
def inspect
@@ -34,7 +36,24 @@ def inspect
3436
# The symbol name.
3537
# e.g. `:Decode`.
3638
# @attribute [Symbol]
37-
attr :name
39+
def name
40+
@path.last
41+
end
42+
43+
# The path to the definition, relative to the parent.
44+
# @attribute [Array(Symbol)]
45+
attr :path
46+
47+
# The full path to the definition.
48+
def full_path
49+
@full_path ||= begin
50+
if @parent
51+
@parent.full_path + @path
52+
else
53+
@path
54+
end
55+
end
56+
end
3857

3958
# The parent definition, defining lexical scope.
4059
# @attribute [Definition | Nil]
@@ -62,17 +81,17 @@ def public?
6281
def qualified_name
6382
@qualified_name ||= begin
6483
if @parent
65-
@parent.qualified_name + self.nested_name
84+
[@parent.qualified_name, self.nested_name].join("::")
6685
else
67-
@name.to_s
86+
self.nested_name
6887
end
6988
end
7089
end
7190

72-
# The name of this definition plus the nesting prefix.
91+
# The name relative to the parent.
7392
# @returns [String]
7493
def nested_name
75-
"::#{@name}"
94+
@nested_name ||= "#{@path.join("::")}"
7695
end
7796

7897
# Does the definition name match the specified prefix?
@@ -86,28 +105,6 @@ def convert(kind)
86105
raise ArgumentError, "Unable to convert #{self} into #{kind}!"
87106
end
88107

89-
# The lexical scope as an array of names.
90-
# e.g. `[:Decode, :Definition]`
91-
# @returns [Array]
92-
def path
93-
if @path
94-
# Cached version:
95-
@path
96-
elsif @parent
97-
# Merge with parent:
98-
@path = [*@parent.path, *path_name].freeze
99-
else
100-
# At top:
101-
@path = path_name.freeze
102-
end
103-
end
104-
105-
def path_name
106-
[@name]
107-
end
108-
109-
alias lexical_path path
110-
111108
# A short form of the definition.
112109
# e.g. `def short_form`.
113110
#
@@ -173,5 +170,9 @@ def documentation
173170
def location
174171
nil
175172
end
173+
174+
# The visibility of the definition.
175+
# @attribute [Symbol] :public, :private, :protected
176+
attr_accessor :visibility
176177
end
177178
end

lib/decode/index.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def update(paths)
5757
# $stderr.puts "Adding #{symbol.qualified_name} to #{symbol.lexical_path.join(' -> ')}"
5858

5959
@definitions[symbol.qualified_name] = symbol
60-
@trie.insert(symbol.path, symbol)
60+
@trie.insert(symbol.full_path, symbol)
6161
end
6262
end
6363
end
@@ -71,7 +71,7 @@ def lookup(reference, relative_to: nil)
7171
if reference.absolute? || relative_to.nil?
7272
lexical_path = []
7373
else
74-
lexical_path = relative_to.path.dup
74+
lexical_path = relative_to.full_path.dup
7575
end
7676

7777
path = reference.path

lib/decode/language/ruby/alias.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
# Released under the MIT License.
4+
# Copyright, 2020-2024, by Samuel Williams.
5+
6+
require_relative "definition"
7+
8+
module Decode
9+
module Language
10+
module Ruby
11+
# Represents an alias statement, e.g., `alias new_name old_name` or `alias_method :new_name, :old_name`
12+
class Alias < Definition
13+
def initialize(new_name, old_name, **options)
14+
super(new_name, **options)
15+
@old_name = old_name
16+
end
17+
18+
attr :old_name
19+
20+
def short_form
21+
"alias #{self.name} #{@old_name}"
22+
end
23+
24+
def long_form
25+
"alias #{self.name} #{@old_name}"
26+
end
27+
28+
def to_s
29+
"#{self.class.name} #{self.name} -> #{@old_name}"
30+
end
31+
end
32+
end
33+
end
34+
end

lib/decode/language/ruby/attribute.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ class Attribute < Definition
1313
# The short form of the attribute.
1414
# e.g. `attr :value`.
1515
def short_form
16-
case @node.type
17-
when :block
16+
case @node&.type
17+
when :block_node
1818
"#{@name} { ... }"
1919
else
20-
@node.location.expression.source
20+
@node&.location&.slice || @name
2121
end
2222
end
2323

2424
def long_form
25-
if @node.location.line == @node.location.last_line
26-
@node.location.expression.source
25+
if @node&.location&.start_line == @node&.location&.end_line
26+
@node.location.slice
2727
else
2828
short_form
2929
end

lib/decode/language/ruby/call.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,32 @@ module Ruby
1212
class Call < Definition
1313
# A block can sometimes be a container for other definitions.
1414
def container?
15-
false
15+
@node&.block && @node.block.opening == "do"
1616
end
1717

1818
# The short form of the class.
1919
# e.g. `foo`.
2020
def short_form
21-
@name.to_s
21+
if @node&.block && @node.block.opening == "{"
22+
"#{name} { ... }"
23+
else
24+
name.to_s
25+
end
2226
end
2327

2428
# The long form of the class.
2529
# e.g. `foo(:bar)`.
2630
def long_form
27-
if @node.location.line == @node.location.last_line
28-
@node.location.expression.source
31+
if @node.location.start_line == @node.location.end_line
32+
@node.location.slice
2933
else
30-
self.short_form
34+
# For multiline calls, use the actual call name with arguments
35+
if @node.arguments && @node.arguments.arguments.any?
36+
arg_text = @node.arguments.arguments.map { |arg| arg.location.slice }.join(", ")
37+
"#{@node.name}(#{arg_text})"
38+
else
39+
@node.name.to_s
40+
end
3141
end
3242
end
3343

lib/decode/language/ruby/class.rb

Lines changed: 12 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@ module Language
1010
module Ruby
1111
# A Ruby-specific class.
1212
class Class < Definition
13+
def initialize(*arguments, super_class: nil, **options)
14+
super(*arguments, **options)
15+
16+
@super_class = super_class
17+
end
18+
19+
attr :super_class
20+
1321
# A class is a container for other definitions.
1422
def container?
1523
true
1624
end
1725

18-
def nested_name
19-
"::#{name}"
20-
end
21-
2226
# The short form of the class.
2327
# e.g. `class Animal`.
2428
def short_form
25-
"class #{path_name.last}"
29+
"class #{self.name}"
2630
end
2731

2832
# The long form of the class.
@@ -35,21 +39,11 @@ def long_form
3539
end
3640
end
3741

38-
def super_class
39-
if super_node = @node.children[1]
40-
super_node.location.expression.source
41-
end
42-
end
43-
4442
# The fully qualified name of the class.
4543
# e.g. `class ::Barnyard::Dog`.
4644
def qualified_form
4745
"class #{self.qualified_name}"
4846
end
49-
50-
def path_name
51-
@name.to_s.split("::").map(&:to_sym)
52-
end
5347
end
5448

5549
# A Ruby-specific singleton class.
@@ -73,30 +67,14 @@ def nested?
7367
# The short form of the class.
7468
# e.g. `class << self`.
7569
def short_form
76-
"class << #{@name}"
70+
"class << #{self.name}"
7771
end
7872

7973
# The long form is the same as the short form.
8074
alias long_form short_form
81-
82-
def path_name
83-
[:class]
84-
end
85-
86-
# The lexical scope as an array of names.
87-
# e.g. `[:Decode, :Definition]`
88-
# @returns [Array]
89-
def path
90-
if @path
91-
# Cached version:
92-
@path
93-
else
94-
@path = [*self.absolute_path, *self.path_name]
95-
end
96-
end
97-
75+
9876
private
99-
77+
10078
def absolute_path
10179
if @parent
10280
@parent.path

lib/decode/language/ruby/code.rb

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
require_relative "definition"
77
require_relative "../../syntax/link"
88

9-
require "parser/current"
9+
require "prism"
1010

1111
module Decode
1212
module Language
@@ -15,7 +15,7 @@ module Ruby
1515
class Code
1616
def initialize(text, index, relative_to: nil, language: relative_to&.language)
1717
@text = text
18-
@root = ::Parser::CurrentRuby.parse(text)
18+
@root = ::Prism.parse(text)
1919
@index = index
2020
@relative_to = relative_to
2121
@language = language
@@ -27,7 +27,7 @@ def initialize(text, index, relative_to: nil, language: relative_to&.language)
2727

2828
def extract(into = [])
2929
if @index
30-
traverse(@root, into)
30+
traverse(@root.value, into)
3131
end
3232

3333
return into
@@ -37,29 +37,34 @@ def extract(into = [])
3737

3838
def traverse(node, into)
3939
case node&.type
40-
when :send
40+
when :program_node
41+
traverse(node.statements, into)
42+
when :call_node
4143
if reference = Reference.from_const(node, @language)
4244
if definition = @index.lookup(reference, relative_to: @relative_to)
43-
expression = node.location.selector
44-
range = expression.begin_pos...expression.end_pos
45+
# Use message_loc for the method name, not the entire call
46+
expression = node.message_loc
47+
range = expression.start_offset...expression.end_offset
4548
into << Syntax::Link.new(range, definition)
4649
end
4750
end
4851

4952
# Extract constants from arguments:
50-
children = node.children[2..-1].each do |node|
51-
traverse(node, into)
53+
if node.arguments
54+
node.arguments.arguments.each do |arg_node|
55+
traverse(arg_node, into)
56+
end
5257
end
53-
when :const
58+
when :constant_read_node
5459
if reference = Reference.from_const(node, @language)
5560
if definition = @index.lookup(reference, relative_to: @relative_to)
56-
expression = node.location.name
57-
range = expression.begin_pos...expression.end_pos
61+
expression = node.location
62+
range = expression.start_offset...expression.end_offset
5863
into << Syntax::Link.new(range, definition)
5964
end
6065
end
61-
when :begin
62-
node.children.each do |child|
66+
when :statements_node
67+
node.body.each do |child|
6368
traverse(child, into)
6469
end
6570
end

0 commit comments

Comments
 (0)