masamune-ast 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/Gemfile.lock +4 -1
  4. data/README.md +30 -82
  5. data/lib/masamune/abstract_syntax_tree/prism/node_extensions.rb +82 -0
  6. data/lib/masamune/abstract_syntax_tree/visitors/block_parameters_visitor.rb +18 -0
  7. data/lib/masamune/abstract_syntax_tree/visitors/method_calls_visitor.rb +19 -0
  8. data/lib/masamune/abstract_syntax_tree/visitors/method_definitions_visitor.rb +17 -0
  9. data/lib/masamune/abstract_syntax_tree/visitors/parameters_visitor.rb +18 -0
  10. data/lib/masamune/abstract_syntax_tree/visitors/strings_visitor.rb +17 -0
  11. data/lib/masamune/abstract_syntax_tree/visitors/symbols_visitor.rb +17 -0
  12. data/lib/masamune/abstract_syntax_tree/visitors/variables_visitor.rb +34 -0
  13. data/lib/masamune/abstract_syntax_tree.rb +57 -134
  14. data/lib/masamune/lex_node.rb +6 -6
  15. data/lib/masamune/slasher.rb +9 -15
  16. data/lib/masamune/version.rb +1 -1
  17. data/lib/masamune.rb +10 -22
  18. metadata +29 -27
  19. data/lib/masamune/abstract_syntax_tree/data_node.rb +0 -49
  20. data/lib/masamune/abstract_syntax_tree/node.rb +0 -41
  21. data/lib/masamune/abstract_syntax_tree/nodes/assign.rb +0 -11
  22. data/lib/masamune/abstract_syntax_tree/nodes/blocks/brace_block.rb +0 -13
  23. data/lib/masamune/abstract_syntax_tree/nodes/blocks/do_block.rb +0 -13
  24. data/lib/masamune/abstract_syntax_tree/nodes/call.rb +0 -17
  25. data/lib/masamune/abstract_syntax_tree/nodes/command.rb +0 -64
  26. data/lib/masamune/abstract_syntax_tree/nodes/def.rb +0 -17
  27. data/lib/masamune/abstract_syntax_tree/nodes/params.rb +0 -23
  28. data/lib/masamune/abstract_syntax_tree/nodes/program.rb +0 -16
  29. data/lib/masamune/abstract_syntax_tree/nodes/string_content.rb +0 -17
  30. data/lib/masamune/abstract_syntax_tree/nodes/support_nodes/block.rb +0 -18
  31. data/lib/masamune/abstract_syntax_tree/nodes/support_nodes/comment.rb +0 -25
  32. data/lib/masamune/abstract_syntax_tree/nodes/symbol.rb +0 -17
  33. data/lib/masamune/abstract_syntax_tree/nodes/symbols/dyna_symbol.rb +0 -20
  34. data/lib/masamune/abstract_syntax_tree/nodes/symbols/symbol_literal.rb +0 -21
  35. data/lib/masamune/abstract_syntax_tree/nodes/variables/block_var.rb +0 -22
  36. data/lib/masamune/abstract_syntax_tree/nodes/variables/var_field.rb +0 -17
  37. data/lib/masamune/abstract_syntax_tree/nodes/variables/var_ref.rb +0 -19
  38. data/lib/masamune/abstract_syntax_tree/nodes/vcall.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f599b770d8e12444bbb2c2bb6aad28d8d0b9673e99708d9d713af4186f96fb4f
4
- data.tar.gz: 723f6ab17c2152f9ab92e6da6f0130bc1ab1aa1fd0174ee00a1d9bb7206df182
3
+ metadata.gz: caf2d78e9516f9431e751db61028a2dd90354b2dce71eecc116a848b7b171344
4
+ data.tar.gz: c556d620be50a699a6757981ce692929298baf92cd9ebbaa86256f7277e28d9e
5
5
  SHA512:
6
- metadata.gz: 622cfe88710212e12d4045fe39837dd9767b56b64abb62872f544b37d6ac286f85dfb9324ef709bb32ab4219e280eff7f4fc6a966ddb379b79b5a5b3b9c06dc3
7
- data.tar.gz: 7db8c29128764c9c36f79a98156c68d4bc008a183ceeed351405a6377606721e33523b267362b0cf1f3a19119db3e1606d494869ec28f340680a8812a4e3868b
6
+ metadata.gz: 62015efa9a2e971af08db707acccf0df1761fc4a6b89fa14ca3ee830732d02ad3c06434c14c9b153006a938217f01e80370ec1dda11fa108f954ec8b47575b5a
7
+ data.tar.gz: 125c5527076511907993aee66d47e66caee9c416c33d44f63585e31676c214cbe959f5bf35165a748199fca2c87d3fda181f0f55a51331edd45c382998796178
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [2.0.0] - 2023-10-13
4
+
5
+ - Migrate to Prism by @gazayas in #60
6
+ - Return array of Prism nodes for `Masamune::AbstractSyntaxTree` search methods.
7
+ - Refactor NodeHelper into inline extensions by @kaspth in #61
8
+ - Remove functionality to replace source code by Node name (the `type` keyword now only accepts Symbols such as `:variable` and `:method_call`).
9
+
3
10
  ## [1.0.0] - 2023-05-01
4
11
 
5
12
  - Change `MasamuneAst` module to `Masamune`.
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- masamune-ast (1.2.1)
4
+ masamune-ast (2.0.0)
5
5
  activesupport
6
+ prism (>= 0.13.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
@@ -18,6 +19,7 @@ GEM
18
19
  concurrent-ruby (~> 1.0)
19
20
  method_source (1.0.0)
20
21
  minitest (5.18.0)
22
+ prism (0.13.0)
21
23
  pry (0.14.2)
22
24
  coderay (~> 1.1)
23
25
  method_source (~> 1.0)
@@ -26,6 +28,7 @@ GEM
26
28
  concurrent-ruby (~> 1.0)
27
29
 
28
30
  PLATFORMS
31
+ arm64-darwin-22
29
32
  x86_64-darwin-21
30
33
  x86_64-linux
31
34
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Masamune
2
2
 
3
- ## A Ruby source code analyzer based on Ripper’s Abstract Syntax Tree generator.
3
+ ## A covenience wrapper around Prism, a Ruby source code parser
4
4
 
5
5
  ## Installation
6
6
 
@@ -23,25 +23,21 @@ require "masamune"
23
23
  Isolate and replace variables, methods, or strings in your Ruby source code according to the specific tokens allotted when the code is intially parsed:
24
24
  ```ruby
25
25
  code = <<~CODE
26
- 10.times do |n|
27
- puts n
28
- end
29
-
30
- def n
31
- "n"
26
+ :hello
27
+ hello = "hello"
28
+ def hello
29
+ puts hello
32
30
  end
33
31
  CODE
34
32
 
35
33
  msmn = Masamune::AbstractSyntaxTree.new(code)
36
- msmn.replace(type: :variable, old_token: "n", new_token: "foo")
34
+ msmn.replace(type: :variable, old_token_value: "hello", new_token_value: "greeting")
37
35
 
38
36
  # This will produce the following code in string form.
39
- 10.times do |foo|
40
- puts foo
41
- end
42
-
43
- def n
44
- "n"
37
+ :hello
38
+ greeting = "hello"
39
+ def hello
40
+ puts greeting
45
41
  end
46
42
  ```
47
43
 
@@ -56,22 +52,20 @@ CODE
56
52
 
57
53
  msmn = Masamune::AbstractSyntaxTree.new(code)
58
54
 
55
+ # Returns an array of Prism nodes
59
56
  msmn.variables
60
- #=> [{:line_number=>1, :index_on_line=>0, :token=>"java"},
61
- #=> {:line_number=>2, :index_on_line=>0, :token=>"javascript"},
62
- #=> {:line_number=>2, :index_on_line=>13, :token=>"java"},
63
- #=> {:line_number=>3, :index_on_line=>5, :token=>"java"},
64
- #=> {:line_number=>3, :index_on_line=>25, :token=>"javascript"}]
65
57
 
58
+ msmn.variables.first.token_value
59
+ #=> "java"
60
+ msmn.variables.first.token_location
61
+ #=> (1,0)-(1,4)
62
+
63
+ # Returns an array of Prism nodes
66
64
  msmn.strings
67
- #=> [{:line_number=>1, :index_on_line=>8, :token=>"java"},
68
- #=> {:line_number=>2, :index_on_line=>21, :token=>"script"},
69
- #=> {:line_number=>3, :index_on_line=>13, :token=>" is not "}]
70
65
 
71
- msmn.variables(name: "java")
72
- #=> [{:line_number=>1, :index_on_line=>0, :token=>"java"},
73
- #=> {:line_number=>2, :index_on_line=>13, :token=>"java"},
74
- #=> {:line_number=>3, :index_on_line=>5, :token=>"java"}]
66
+ last_java_node = msmn.variables(token_value: "java").last
67
+ last_java_node.token_location
68
+ #=> (3,5)-(3,9)
75
69
 
76
70
  code = <<CODE
77
71
  ary = [1, 2, 3]
@@ -87,68 +81,22 @@ CODE
87
81
 
88
82
  msmn = Masamune::AbstractSyntaxTree.new(code)
89
83
 
90
- msmn.all_methods
91
- #=> [{:line_number=>2, :index_on_line=>4, :token=>"sum"},
92
- #=> {:line_number=>2, :index_on_line=>8, :token=>"times"},
93
- #=> {:line_number=>3, :index_on_line=>2, :token=>"puts"},
94
- #=> {:line_number=>6, :index_on_line=>4, :token=>"foo"},
95
- #=> {:line_number=>8, :index_on_line=>0, :token=>"foo"},
96
- #=> {:line_number=>9, :index_on_line=>0, :token=>"foo"}]
97
-
98
- msmn.method_calls
99
- #=> [{:line_number=>2, :index_on_line=>4, :token=>"sum"},
100
- #=> {:line_number=>2, :index_on_line=>8, :token=>"times"},
101
- #=> {:line_number=>3, :index_on_line=>2, :token=>"puts"},
102
- #=> {:line_number=>8, :index_on_line=>0, :token=>"foo"},
103
- #=> {:line_number=>9, :index_on_line=>0, :token=>"foo"}]
104
-
105
- msmn.method_definitions
106
- #=> [{:line_number=>6, :index_on_line=>4, :token=>"foo"}]
107
- ```
84
+ msmn.method_definitions.size
85
+ #=> 1
108
86
 
109
- You can also return the node classes themselves and get the data from there:
110
- ```ruby
111
- code = <<~CODE
112
- "ruby"
113
- "rails"
114
- CODE
87
+ msmn.method_calls.size
88
+ #=> 5
115
89
 
116
- msmn = Masamune::AbstractSyntaxTree.new(code)
117
- msmn.strings(result_type: :nodes)
118
- #=> [#<Masamune::AbstractSyntaxTree::StringContent:0x00007ff2d987c020
119
- #=> @ast_id=406820,
120
- #=> @contents=[:string_content, [:@tstring_content, "ruby", [1, 1]]],
121
- #=> @data_nodes=
122
- #=> [#<Masamune::AbstractSyntaxTree::DataNode:0x00007ff2d9883fc8
123
- #=> @ast_id=406820,
124
- #=> @contents=[:@tstring_content, "ruby", [1, 1]],
125
- #=> @data_nodes=nil,
126
- #=> @index_on_line=1,
127
- #=> @line_number=1,
128
- #=> @parent=#<Masamune::AbstractSyntaxTree::StringContent:0x00007ff2d987c020 ...>,
129
- #=> @token="ruby",
130
- #=> @type=:@tstring_content>]>,
131
- #=> #<Masamune::AbstractSyntaxTree::StringContent:0x00007ff2d9883190
132
- #=> @ast_id=406820,
133
- #=> @contents=[:string_content, [:@tstring_content, "rails", [2, 1]]],
134
- #=> @data_nodes=
135
- #=> [#<Masamune::AbstractSyntaxTree::DataNode:0x00007ff2d9883168
136
- #=> @ast_id=406820,
137
- #=> @contents=[:@tstring_content, "rails", [2, 1]],
138
- #=> @data_nodes=nil,
139
- #=> @index_on_line=1,
140
- #=> @line_number=2,
141
- #=> @parent=#<Masamune::AbstractSyntaxTree::StringContent:0x00007ff2d9883190 ...>,
142
- #=> @token="rails",
143
- #=> @type=:@tstring_content>]>]
90
+ msmn.all_methods.size
91
+ #=> 6
144
92
  ```
145
93
 
146
- In some cases, it can be easier to look at the given lex nodes to analyze your source code since you can easily see the index and the line position it's on:
94
+ In some cases, it can be easier to look at the given lex nodes to analyze your source code:
147
95
  ```ruby
148
96
  msmn.lex_nodes
149
- => [#<Masamune::LexNode:0x00007fd61810cac0 @ast_id=1200, @index=0, @position=[1, 0], @state=CMDARG, @token="java", @type=:ident>,
150
- #<Masamune::LexNode:0x00007fd61810c930 @ast_id=1200, @index=1, @position=[1, 4], @state=CMDARG, @token=" ", @type=:sp>,
151
- #<Masamune::LexNode:0x00007fd61810c7c8 @ast_id=1200, @index=2, @position=[1, 5], @state=BEG, @token="=", @type=:op>,
97
+ => [#<Masamune::LexNode:0x00007fd61810cac0 @ast_id=1200, @index=0, @location=[1, 0], @state=CMDARG, @token="java", @type=:ident>,
98
+ #<Masamune::LexNode:0x00007fd61810c930 @ast_id=1200, @index=1, @location=[1, 4], @state=CMDARG, @token=" ", @type=:sp>,
99
+ #<Masamune::LexNode:0x00007fd61810c7c8 @ast_id=1200, @index=2, @location=[1, 5], @state=BEG, @token="=", @type=:op>,
152
100
 
153
101
  ]
154
102
 
@@ -0,0 +1,82 @@
1
+ module Prism
2
+ class Node
3
+ def comment? = false
4
+
5
+ def self.node_predicate(name, body = -> { true })
6
+ define_method(name, &body)
7
+ Node.define_method(name) { false } unless Node.respond_to?(name, false)
8
+ end
9
+
10
+ def line_number
11
+ token_location.start_line
12
+ end
13
+
14
+ # #location provides helpful information for the source code of a node as a whole, but in
15
+ # Masamune we generally want the token value itself, so we primarily get the token value's location.
16
+ def token_location
17
+ location
18
+ end
19
+
20
+ # The source code of Prism nodes can be retrieved by calling #slice.
21
+ # However, the output tends to vary from node to node, and other methods
22
+ # like `name`, `message`, etc. are available in Prism nodes which means you
23
+ # have to know exactly which method to call the get the results you want.
24
+ #
25
+ # The `token` method simplifies all of this, and just gives you the
26
+ # variable, method name, etc. in String form by just calling `token_value`.
27
+ def token_value
28
+ content
29
+ end
30
+ end
31
+
32
+ class DefNode
33
+ node_predicate :method?
34
+ node_predicate :method_definition?
35
+ def token_location = name_loc
36
+ def token_value = name.to_s
37
+ end
38
+
39
+ class CallNode
40
+ def method? = true
41
+ node_predicate :method_call?
42
+ def token_location = message_loc
43
+ def token_value = message
44
+ end
45
+
46
+ class LocalVariableWriteNode
47
+ node_predicate :variable?
48
+ def token_location = name_loc
49
+ def token_value = name.to_s
50
+ end
51
+
52
+ class LocalVariableReadNode
53
+ def variable? = true
54
+ def token_value = name.to_s
55
+ end
56
+
57
+ class RequiredParameterNode
58
+ def variable? = true
59
+ def token_value = slice
60
+ end
61
+
62
+ class StringNode
63
+ node_predicate :string?
64
+ def token_location = content_loc
65
+ def token_value = content
66
+ end
67
+
68
+ class SymbolNode
69
+ node_predicate :symbol?
70
+ node_predicate :symbol_literal?, -> { closing_loc.nil? }
71
+ node_predicate :symbol_string?, -> { closing_loc.present? }
72
+
73
+ def token_location = value_loc
74
+ def token_value = value
75
+ end
76
+
77
+ class Comment
78
+ def comment? = true
79
+ def token_location = location
80
+ def token_value = location.slice
81
+ end
82
+ end
@@ -0,0 +1,18 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class BlockParametersVisitors < Prism::Visitor
4
+ attr_reader :results
5
+
6
+ def initialize
7
+ @results = []
8
+ end
9
+
10
+ # Since block parameters nodes can house multiple variables in one node,
11
+ # we don't have to check for a token_value.
12
+ def visit_block_parameters_node(node)
13
+ results << node
14
+ super
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class MethodCallsVisitor < Prism::Visitor
4
+ attr_reader :token_value, :results
5
+
6
+ def initialize(token_value)
7
+ @token_value = token_value
8
+ @results = []
9
+ end
10
+
11
+ def visit_call_node(node)
12
+ if node.method_call?
13
+ results << node if token_value.nil? || token_value == node.name
14
+ end
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class MethodDefinitionsVisitor < Prism::Visitor
4
+ attr_reader :token_value, :results
5
+
6
+ def initialize(token_value)
7
+ @token_value = token_value
8
+ @results = []
9
+ end
10
+
11
+ def visit_def_node(node)
12
+ results << node if token_value.nil? || token_value == node.name.to_s
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class ParametersVisitor < Prism::Visitor
4
+ attr_reader :results
5
+
6
+ def initialize
7
+ @results = []
8
+ end
9
+
10
+ # Since parameters nodes can house multiple variables in one node,
11
+ # we don't have to check for a token_value.
12
+ def visit_parameters_node(node)
13
+ results << node
14
+ super
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class StringsVisitor < Prism::Visitor
4
+ attr_reader :token_value, :results
5
+
6
+ def initialize(token_value)
7
+ @token_value = token_value
8
+ @results = []
9
+ end
10
+
11
+ def visit_string_node(node)
12
+ results << node if token_value.nil? || token_value == node.content
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class SymbolsVisitor < Prism::Visitor
4
+ attr_reader :token_value, :results
5
+
6
+ def initialize(token_value)
7
+ @token_value = token_value
8
+ @results = []
9
+ end
10
+
11
+ def visit_symbol_node(node)
12
+ results << node if token_value.nil? || token_value == node.value
13
+ super
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ module Masamune
2
+ class AbstractSyntaxTree
3
+ class VariablesVisitor < Prism::Visitor
4
+ attr_reader :token_value, :results
5
+
6
+ def initialize(token_value)
7
+ @token_value = token_value
8
+ @results = []
9
+ end
10
+
11
+ def visit_local_variable_write_node(node)
12
+ results << node if token_value.nil? || token_value == node.name.to_s
13
+ super
14
+ end
15
+
16
+ def visit_local_variable_read_node(node)
17
+ results << node if token_value.nil? || token_value == node.name.to_s
18
+ super
19
+ end
20
+
21
+ def visit_required_parameter_node(node)
22
+ results << node if token_value.nil? || token_value == node.name.to_s
23
+ super
24
+ end
25
+
26
+ def visit_call_node(node)
27
+ if node.variable_call?
28
+ results << node if token_value.nil? || token_value == node.name
29
+ end
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,162 +1,91 @@
1
+ require_relative "abstract_syntax_tree/prism/node_extensions"
2
+
1
3
  module Masamune
2
4
  class AbstractSyntaxTree
3
- attr_reader :code, :tree, :comment_list
4
- attr_accessor :node_list, :data_node_list, :lex_nodes
5
+ attr_reader :code, :prism, :ripper
6
+ attr_accessor :lex_nodes
5
7
 
6
8
  def initialize(code)
7
9
  @code = code
8
- @tree = Ripper.sexp(code)
10
+ @prism = Prism.parse(code)
11
+ @ripper = Ripper.sexp(code)
9
12
  raw_lex_nodes = Ripper.lex(code)
10
13
  @lex_nodes = raw_lex_nodes.map do |lex_node|
11
14
  LexNode.new(raw_lex_nodes.index(lex_node), lex_node, self.__id__)
12
15
  end
13
-
14
- @node_list = []
15
- @data_node_list = []
16
- @comment_list = []
17
- register_nodes(@tree)
18
-
19
- # Refer to Masamune::AbstractSyntaxTree::Comment
20
- # to see why we register these separately.
21
- register_comments
22
16
  end
23
17
 
24
- def register_nodes(tree_node = self.tree)
25
- if tree_node.is_a?(Array)
26
- klass = get_node_class(tree_node.first)
27
- msmn_node = klass.new(tree_node, self.__id__)
28
- else
29
- # Create a general node if the node is a single value.
30
- msmn_node = Node.new(tree_node, self.__id__)
31
- end
32
-
33
- # Register nodes and any data nodes housed within it.
34
- # See Masamune::AbstractSyntaxTree::DataNode for more details on what a data node is.
35
- @node_list << msmn_node
36
- msmn_node.data_nodes.each { |dn| @data_node_list << dn } if msmn_node.data_nodes
37
-
38
- # Continue down the tree until base case is reached.
39
- if !msmn_node.nil? && msmn_node.contents.is_a?(Array)
40
- msmn_node.contents.each { |node| register_nodes(node) }
41
- end
18
+ def variables(token_value: nil)
19
+ visitor = VariablesVisitor.new(token_value)
20
+ @prism.value.accept(visitor)
21
+ visitor.results
42
22
  end
43
23
 
44
- def register_comments
45
- comment_lex_nodes = @lex_nodes.select {|node| node.type == :comment}.flatten
46
- @comment_list << comment_lex_nodes.map {|node| Comment.new(node, self.__id__)}
47
- @comment_list.flatten!
24
+ def strings(token_value: nil)
25
+ visitor = StringsVisitor.new(token_value)
26
+ @prism.value.accept(visitor)
27
+ visitor.results
48
28
  end
49
29
 
50
- # TODO: A lot of these methods have the same content,
51
- # so I want to come up with a way to refactor these.
52
-
53
- # TODO: Add block_params: true to the arguments.
54
- def variables(name: nil, result_type: Hash)
55
- results = find_nodes([VarField, VarRef, Params], token: name, result_type: result_type)
56
- order_results(results)
57
- end
58
-
59
- def strings(content: nil, result_type: Hash)
60
- results = find_nodes(StringContent, token: content, result_type: result_type)
61
- order_results(results)
30
+ def all_methods(token_value: nil)
31
+ order_nodes(method_definitions(token_value: token_value) + method_calls(token_value: token_value))
62
32
  end
63
33
 
64
- def method_definitions(name: nil, result_type: Hash)
65
- results = find_nodes(Def, token: name, result_type: result_type)
66
- order_results(results)
34
+ def method_definitions(token_value: nil)
35
+ visitor = MethodDefinitionsVisitor.new(token_value)
36
+ @prism.value.accept(visitor)
37
+ visitor.results
67
38
  end
68
39
 
69
- def method_calls(name: nil, result_type: Hash)
70
- results = find_nodes([Vcall, Call, Command], token: name, result_type: result_type)
71
- order_results(results)
40
+ def method_calls(token_value: nil)
41
+ visitor = MethodCallsVisitor.new(token_value)
42
+ @prism.value.accept(visitor)
43
+ visitor.results
72
44
  end
73
45
 
74
- # TODO
75
- def do_block_params
46
+ def symbols(token_value: nil)
47
+ visitor = SymbolsVisitor.new(token_value)
48
+ @prism.value.accept(visitor)
49
+ visitor.results
76
50
  end
77
51
 
78
- # TODO
79
- def brace_block_params
80
- end
52
+ def symbol_literals(token_value: nil)
53
+ result = symbols(token_value: token_value)
81
54
 
82
- def symbols(content: nil, result_type: Hash)
83
- results = symbol_literals(content: content, result_type: result_type) + string_symbols(content: content, result_type: result_type)
84
- order_results(results)
55
+ # TODO: Describe why closing_loc has to happen.
56
+ result.select{|node| node.closing_loc.nil?}
85
57
  end
86
58
 
87
- def symbol_literals(content: nil, result_type: Hash)
88
- results = find_nodes(SymbolLiteral, token: content, result_type: result_type)
89
- order_results(results)
90
- end
59
+ def string_symbols(token_value: nil)
60
+ result = symbols(token_value: token_value)
91
61
 
92
- def string_symbols(content: nil, result_type: Hash)
93
- results = find_nodes(DynaSymbol, token: content, result_type: result_type)
94
- order_results(results)
62
+ # TODO: Describe why closing_loc has to happen.
63
+ result.reject{|node| node.closing_loc.nil?}
95
64
  end
96
65
 
97
- def comments(content: nil, result_type: Hash)
98
- results = find_nodes(Comment, token: content, result_type: result_type)
99
- order_results(results)
66
+ # Retrieves all parameters within pipes (i.e. - |x, y, z|).
67
+ def block_parameters
68
+ visitor = BlockParametersVisitor.new
69
+ @prism.value.accept(visitor)
70
+ visitor.results
100
71
  end
101
72
 
102
- def all_methods(name: nil, result_type: Hash)
103
- results = method_definitions(name: name, result_type: result_type) + method_calls(name: name, result_type: result_type)
104
- order_results(results)
73
+ def parameters(token_value: nil)
74
+ visitor = ParametersVisitor.new(token_value)
75
+ @prism.value.accept(visitor)
76
+ visitor.results
105
77
  end
106
78
 
107
- def block_params(content: nil, result_type: Hash)
108
- # TODO: do_block_params + brace_block_params
109
- results = find_nodes(Params, token: content, result_type: result_type)
110
- order_results(results)
79
+ # TODO: Search by token_value if necessary.
80
+ def comments
81
+ @prism.comments
111
82
  end
112
83
 
113
- def find_nodes(token_classes, token: nil, result_type: Hash)
114
- token_classes = Array(token_classes)
115
-
116
- nodes = []
117
- token_classes.each do |klass|
118
- if klass == Comment
119
- nodes = @comment_list.dup
120
- else
121
- nodes << @node_list.select {|node| node.class == klass}
122
- end
123
- end
124
-
125
- # Searching for multiple classes will yield multi-dimensional arrays,
126
- # so we ensure everything is flattened out before moving forward.
127
- nodes.flatten!
128
-
129
- return nodes if result_type == :nodes
130
-
131
- if token
132
- # TODO: This most likely shouldn't be `node.data_nodes.first`.
133
- # There are probably more data_nodes we need to check depending on the node class.
134
- nodes = nodes.select {|node| node.data_nodes.first.token == token}.flatten
135
- end
136
-
137
- final_result = []
138
- nodes.each do |node|
139
- # Data for symbols are housed within a nested node, so we handle those differently here.
140
- # Read the comments for `get_symbol_data` in the symbol node classes for details.
141
- if node.class == SymbolLiteral || node.class == DynaSymbol
142
- final_result << node.get_symbol_data.line_data_and_token
143
- else
144
- node.data_nodes.each {|dn| final_result << dn.line_data_and_token} if node.data_nodes
145
- end
146
- end
147
-
148
- # Only order the information if we're returning hashes.
149
- # TODO: We might want to change the placement of order_results_by_position
150
- # if the operation is being done against hashes and not data nodes.
151
- # nodes.first.class.is_a?(Hash) ? DataNode.order_results_by_position(final_result) : final_result
152
- final_result
153
- end
154
-
155
- def replace(type:, old_token:, new_token:)
84
+ def replace(type:, old_token_value:, new_token_value:)
156
85
  Slasher.replace(
157
86
  type: type,
158
- old_token: old_token,
159
- new_token: new_token,
87
+ old_token_value: old_token_value,
88
+ new_token_value: new_token_value,
160
89
  code: @code,
161
90
  ast: self
162
91
  )
@@ -164,18 +93,12 @@ module Masamune
164
93
 
165
94
  private
166
95
 
167
- def get_node_class(type)
168
- "#{self.class}::#{type.to_s.camelize}".safe_constantize || Node # Return base Node class for any not-yet-covered nodes.
169
- end
170
-
171
- # We only order results when they are a Hash.
172
- # The contents from the Hash are pulled from data nodes.
173
- # i.e. - {position: [4, 7], token: "project"}
174
- def order_results(results)
175
- if results.first.is_a?(Hash)
176
- DataNode.order_results_by_position(results)
177
- else
178
- results
96
+ # Order results according to their token location.
97
+ def order_nodes(nodes)
98
+ nodes.sort do |a, b|
99
+ by_line = a.token_location.start_line <=> b.token_location.start_line
100
+ # If we're on the same line, refer to the inner index for order.
101
+ by_line.zero? ? a.token_location.start_column <=> b.token_location.start_column : by_line
179
102
  end
180
103
  end
181
104
  end