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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +5 -1
- data/README.md +30 -82
- data/lib/masamune/abstract_syntax_tree/prism/node_extensions.rb +82 -0
- data/lib/masamune/abstract_syntax_tree/visitors/block_parameters_visitor.rb +18 -0
- data/lib/masamune/abstract_syntax_tree/visitors/method_calls_visitor.rb +19 -0
- data/lib/masamune/abstract_syntax_tree/visitors/method_definitions_visitor.rb +17 -0
- data/lib/masamune/abstract_syntax_tree/visitors/parameters_visitor.rb +18 -0
- data/lib/masamune/abstract_syntax_tree/visitors/strings_visitor.rb +17 -0
- data/lib/masamune/abstract_syntax_tree/visitors/symbols_visitor.rb +17 -0
- data/lib/masamune/abstract_syntax_tree/visitors/variables_visitor.rb +34 -0
- data/lib/masamune/abstract_syntax_tree.rb +57 -151
- data/lib/masamune/lex_node.rb +6 -6
- data/lib/masamune/slasher.rb +9 -15
- data/lib/masamune/version.rb +1 -1
- data/lib/masamune.rb +10 -23
- metadata +26 -24
- data/lib/masamune/abstract_syntax_tree/data_node.rb +0 -65
- data/lib/masamune/abstract_syntax_tree/node.rb +0 -41
- data/lib/masamune/abstract_syntax_tree/nodes/assign.rb +0 -11
- data/lib/masamune/abstract_syntax_tree/nodes/blocks/brace_block.rb +0 -13
- data/lib/masamune/abstract_syntax_tree/nodes/blocks/do_block.rb +0 -13
- data/lib/masamune/abstract_syntax_tree/nodes/call.rb +0 -17
- data/lib/masamune/abstract_syntax_tree/nodes/command.rb +0 -64
- data/lib/masamune/abstract_syntax_tree/nodes/def.rb +0 -17
- data/lib/masamune/abstract_syntax_tree/nodes/params.rb +0 -23
- data/lib/masamune/abstract_syntax_tree/nodes/program.rb +0 -16
- data/lib/masamune/abstract_syntax_tree/nodes/string_content.rb +0 -17
- data/lib/masamune/abstract_syntax_tree/nodes/support_nodes/block.rb +0 -18
- data/lib/masamune/abstract_syntax_tree/nodes/support_nodes/comment.rb +0 -25
- data/lib/masamune/abstract_syntax_tree/nodes/symbol.rb +0 -17
- data/lib/masamune/abstract_syntax_tree/nodes/symbols/dyna_symbol.rb +0 -20
- data/lib/masamune/abstract_syntax_tree/nodes/symbols/symbol_literal.rb +0 -21
- data/lib/masamune/abstract_syntax_tree/nodes/variables/block_var.rb +0 -22
- data/lib/masamune/abstract_syntax_tree/nodes/variables/var_field.rb +0 -17
- data/lib/masamune/abstract_syntax_tree/nodes/variables/var_ref.rb +0 -19
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: caf2d78e9516f9431e751db61028a2dd90354b2dce71eecc116a848b7b171344
|
4
|
+
data.tar.gz: c556d620be50a699a6757981ce692929298baf92cd9ebbaa86256f7277e28d9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 (
|
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
|
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
|
-
|
27
|
-
|
28
|
-
|
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,
|
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
|
-
|
40
|
-
|
41
|
-
|
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(
|
72
|
-
|
73
|
-
#=>
|
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.
|
91
|
-
#=>
|
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
|
-
|
110
|
-
|
111
|
-
code = <<~CODE
|
112
|
-
"ruby"
|
113
|
-
"rails"
|
114
|
-
CODE
|
87
|
+
msmn.method_calls.size
|
88
|
+
#=> 5
|
115
89
|
|
116
|
-
msmn
|
117
|
-
|
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
|
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, @
|
150
|
-
#<Masamune::LexNode:0x00007fd61810c930 @ast_id=1200, @index=1, @
|
151
|
-
#<Masamune::LexNode:0x00007fd61810c7c8 @ast_id=1200, @index=2, @
|
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
|