masamune-ast 1.2.0 → 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 +5 -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 -151
  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 -23
  18. metadata +26 -24
  19. data/lib/masamune/abstract_syntax_tree/data_node.rb +0 -65
  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: 3029256236d8953d9f258d370abb57d1dac4a425b9112f7f4a5a42e951634943
4
- data.tar.gz: 123f4977244d1db7d1cbceebee495e6a7d8494401f1db86f4a8360de27b06f2e
3
+ metadata.gz: caf2d78e9516f9431e751db61028a2dd90354b2dce71eecc116a848b7b171344
4
+ data.tar.gz: c556d620be50a699a6757981ce692929298baf92cd9ebbaa86256f7277e28d9e
5
5
  SHA512:
6
- metadata.gz: 3282cd14e7c591bff140c5d92d782a9724be2af32e9cc2542a0aab949fd469ad4c526c2a11255be267546cb2d1634e97ea7d950c8762c21635fc4274fe5f487c
7
- data.tar.gz: d652c21b25b38d460045336d9dd330afb302470358a98f43a0eacb7f79128cdc26e8efe6d233f41067f2be28af3314585ba03b0df1f83e47f8f7d8419a018d98
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.0)
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,8 @@ GEM
26
28
  concurrent-ruby (~> 1.0)
27
29
 
28
30
  PLATFORMS
31
+ arm64-darwin-22
32
+ x86_64-darwin-21
29
33
  x86_64-linux
30
34
 
31
35
  DEPENDENCIES
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