masamune-ast 1.1.5 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -1
- data/README.md +49 -45
- data/lib/masamune/abstract_syntax_tree/data_node.rb +21 -35
- data/lib/masamune/abstract_syntax_tree.rb +16 -32
- data/lib/masamune/slasher.rb +9 -5
- data/lib/masamune/version.rb +1 -1
- data/lib/masamune.rb +0 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f599b770d8e12444bbb2c2bb6aad28d8d0b9673e99708d9d713af4186f96fb4f
|
4
|
+
data.tar.gz: 723f6ab17c2152f9ab92e6da6f0130bc1ab1aa1fd0174ee00a1d9bb7206df182
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 622cfe88710212e12d4045fe39837dd9767b56b64abb62872f544b37d6ac286f85dfb9324ef709bb32ab4219e280eff7f4fc6a966ddb379b79b5a5b3b9c06dc3
|
7
|
+
data.tar.gz: 7db8c29128764c9c36f79a98156c68d4bc008a183ceeed351405a6377606721e33523b267362b0cf1f3a19119db3e1606d494869ec28f340680a8812a4e3868b
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -57,21 +57,21 @@ CODE
|
|
57
57
|
msmn = Masamune::AbstractSyntaxTree.new(code)
|
58
58
|
|
59
59
|
msmn.variables
|
60
|
-
#=> [{:
|
61
|
-
#=> {:
|
62
|
-
#=> {:
|
63
|
-
#=> {:
|
64
|
-
#=> {:
|
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
65
|
|
66
66
|
msmn.strings
|
67
|
-
#=> [{:
|
68
|
-
#=> {:
|
69
|
-
#=> {:
|
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
70
|
|
71
71
|
msmn.variables(name: "java")
|
72
|
-
#=> [{
|
73
|
-
#=> {
|
74
|
-
#=> {
|
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"}]
|
75
75
|
|
76
76
|
code = <<CODE
|
77
77
|
ary = [1, 2, 3]
|
@@ -88,22 +88,22 @@ CODE
|
|
88
88
|
msmn = Masamune::AbstractSyntaxTree.new(code)
|
89
89
|
|
90
90
|
msmn.all_methods
|
91
|
-
#=> [{:
|
92
|
-
#=> {:
|
93
|
-
#=> {:
|
94
|
-
#=> {:
|
95
|
-
#=> {:
|
96
|
-
#=> {:
|
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
97
|
|
98
98
|
msmn.method_calls
|
99
|
-
#=> [{:
|
100
|
-
#=> {:
|
101
|
-
#=> {:
|
102
|
-
#=> {:
|
103
|
-
#=> {:
|
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
104
|
|
105
105
|
msmn.method_definitions
|
106
|
-
#=> [{:
|
106
|
+
#=> [{:line_number=>6, :index_on_line=>4, :token=>"foo"}]
|
107
107
|
```
|
108
108
|
|
109
109
|
You can also return the node classes themselves and get the data from there:
|
@@ -115,28 +115,32 @@ CODE
|
|
115
115
|
|
116
116
|
msmn = Masamune::AbstractSyntaxTree.new(code)
|
117
117
|
msmn.strings(result_type: :nodes)
|
118
|
-
#=> [#<Masamune::AbstractSyntaxTree::StringContent:
|
119
|
-
#=>
|
120
|
-
#=>
|
121
|
-
#=>
|
122
|
-
#=>
|
123
|
-
#=>
|
124
|
-
#=>
|
125
|
-
#=>
|
126
|
-
#=>
|
127
|
-
#=>
|
128
|
-
#=>
|
129
|
-
#=>
|
130
|
-
#=>
|
131
|
-
#=>
|
132
|
-
#=>
|
133
|
-
#=> [
|
134
|
-
#=>
|
135
|
-
#=>
|
136
|
-
#=>
|
137
|
-
#=>
|
138
|
-
#=>
|
139
|
-
#=>
|
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>]>]
|
140
144
|
```
|
141
145
|
|
142
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:
|
@@ -3,16 +3,17 @@
|
|
3
3
|
# These values are the `type`, `token`, and `position`, respectively.
|
4
4
|
# It is simliar to what you see in `Ripper.lex(code)` and `Masamune::AbstractSyntaxTree's @lex_nodes`.
|
5
5
|
|
6
|
-
# We break this down into a simpler structure, `
|
7
|
-
# which looks like this: {
|
6
|
+
# We break this down into a simpler structure, `line_data_and_token`,
|
7
|
+
# which looks like this: {line_number: 4, index_on_line: 7, token: "ruby"}
|
8
8
|
|
9
9
|
module Masamune
|
10
10
|
class AbstractSyntaxTree
|
11
11
|
class DataNode < Node
|
12
|
-
attr_reader :type, :token, :
|
12
|
+
attr_reader :type, :token, :line_number, :index_on_line
|
13
13
|
|
14
14
|
def initialize(contents, ast_id, parent)
|
15
|
-
@type, @token,
|
15
|
+
@type, @token, line_position = contents
|
16
|
+
@line_number, @index_on_line = line_position
|
16
17
|
@parent = parent
|
17
18
|
super(contents, ast_id)
|
18
19
|
end
|
@@ -20,43 +21,28 @@ module Masamune
|
|
20
21
|
# Results here represent the position and token of the
|
21
22
|
# data we're searching in the form of a Hash like the following:
|
22
23
|
# [
|
23
|
-
# {
|
24
|
-
# {
|
24
|
+
# {line_number: 4, index_on_line: 7, token: "ruby"},
|
25
|
+
# {line_number: 7, index_on_line: 7, token: "rails"}
|
25
26
|
# ]
|
26
27
|
# TODO: Worry about using a faster sorting algorithm later.
|
27
|
-
def self.order_results_by_position(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
final_result = []
|
34
|
-
line_numbers.each do |line_number|
|
35
|
-
# Group data together in an array if they're on the same line.
|
36
|
-
shared_line_data = position_and_token_ary.select do |position_and_token|
|
37
|
-
position_and_token[:position].first == line_number
|
38
|
-
end
|
39
|
-
|
40
|
-
# Sort the positions on each line number respectively.
|
41
|
-
positions_on_line = shared_line_data.map do |position_and_token|
|
42
|
-
position_and_token[:position].last
|
43
|
-
end.sort
|
44
|
-
|
45
|
-
# Apply to the final result.
|
46
|
-
positions_on_line.each do |position_on_line|
|
47
|
-
shared_line_data.each do |position_and_token|
|
48
|
-
if position_and_token[:position].last == position_on_line
|
49
|
-
final_result << position_and_token
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
28
|
+
def self.order_results_by_position(results)
|
29
|
+
results.sort do |a, b|
|
30
|
+
by_line = a[:line_number] <=> b[:line_number]
|
31
|
+
# If we're on the same line, refer to the inner index for order.
|
32
|
+
by_line.zero? ? a[:index_on_line] <=> b[:index_on_line] : by_line
|
53
33
|
end
|
34
|
+
end
|
54
35
|
|
55
|
-
|
36
|
+
def line_data_and_token
|
37
|
+
{
|
38
|
+
line_number: @line_number,
|
39
|
+
index_on_line: @index_on_line,
|
40
|
+
token: @token
|
41
|
+
}
|
56
42
|
end
|
57
43
|
|
58
|
-
def
|
59
|
-
|
44
|
+
def position
|
45
|
+
[@line_number, @index_on_line]
|
60
46
|
end
|
61
47
|
end
|
62
48
|
end
|
@@ -52,32 +52,22 @@ module Masamune
|
|
52
52
|
|
53
53
|
# TODO: Add block_params: true to the arguments.
|
54
54
|
def variables(name: nil, result_type: Hash)
|
55
|
-
|
56
|
-
:var_field,
|
57
|
-
:var_ref,
|
58
|
-
:params
|
59
|
-
].map {|type| get_node_class(type)}
|
60
|
-
results = find_nodes(var_classes, token: name, result_type: result_type)
|
55
|
+
results = find_nodes([VarField, VarRef, Params], token: name, result_type: result_type)
|
61
56
|
order_results(results)
|
62
57
|
end
|
63
58
|
|
64
59
|
def strings(content: nil, result_type: Hash)
|
65
|
-
results = find_nodes(
|
60
|
+
results = find_nodes(StringContent, token: content, result_type: result_type)
|
66
61
|
order_results(results)
|
67
62
|
end
|
68
63
|
|
69
64
|
def method_definitions(name: nil, result_type: Hash)
|
70
|
-
results = find_nodes(
|
65
|
+
results = find_nodes(Def, token: name, result_type: result_type)
|
71
66
|
order_results(results)
|
72
67
|
end
|
73
68
|
|
74
69
|
def method_calls(name: nil, result_type: Hash)
|
75
|
-
|
76
|
-
:vcall,
|
77
|
-
:call,
|
78
|
-
:command
|
79
|
-
].map {|type| get_node_class(type)}
|
80
|
-
results = find_nodes(method_classes, token: name, result_type: result_type)
|
70
|
+
results = find_nodes([Vcall, Call, Command], token: name, result_type: result_type)
|
81
71
|
order_results(results)
|
82
72
|
end
|
83
73
|
|
@@ -95,17 +85,17 @@ module Masamune
|
|
95
85
|
end
|
96
86
|
|
97
87
|
def symbol_literals(content: nil, result_type: Hash)
|
98
|
-
results = find_nodes(
|
88
|
+
results = find_nodes(SymbolLiteral, token: content, result_type: result_type)
|
99
89
|
order_results(results)
|
100
90
|
end
|
101
91
|
|
102
92
|
def string_symbols(content: nil, result_type: Hash)
|
103
|
-
results = find_nodes(
|
93
|
+
results = find_nodes(DynaSymbol, token: content, result_type: result_type)
|
104
94
|
order_results(results)
|
105
95
|
end
|
106
96
|
|
107
97
|
def comments(content: nil, result_type: Hash)
|
108
|
-
results = find_nodes(
|
98
|
+
results = find_nodes(Comment, token: content, result_type: result_type)
|
109
99
|
order_results(results)
|
110
100
|
end
|
111
101
|
|
@@ -116,17 +106,16 @@ module Masamune
|
|
116
106
|
|
117
107
|
def block_params(content: nil, result_type: Hash)
|
118
108
|
# TODO: do_block_params + brace_block_params
|
119
|
-
results = find_nodes(
|
109
|
+
results = find_nodes(Params, token: content, result_type: result_type)
|
120
110
|
order_results(results)
|
121
111
|
end
|
122
112
|
|
123
113
|
def find_nodes(token_classes, token: nil, result_type: Hash)
|
124
|
-
|
125
|
-
token_classes = [token_classes].flatten
|
114
|
+
token_classes = Array(token_classes)
|
126
115
|
|
127
116
|
nodes = []
|
128
117
|
token_classes.each do |klass|
|
129
|
-
if klass ==
|
118
|
+
if klass == Comment
|
130
119
|
nodes = @comment_list.dup
|
131
120
|
else
|
132
121
|
nodes << @node_list.select {|node| node.class == klass}
|
@@ -149,17 +138,18 @@ module Masamune
|
|
149
138
|
nodes.each do |node|
|
150
139
|
# Data for symbols are housed within a nested node, so we handle those differently here.
|
151
140
|
# Read the comments for `get_symbol_data` in the symbol node classes for details.
|
152
|
-
if node.class ==
|
153
|
-
final_result << node.get_symbol_data.
|
141
|
+
if node.class == SymbolLiteral || node.class == DynaSymbol
|
142
|
+
final_result << node.get_symbol_data.line_data_and_token
|
154
143
|
else
|
155
|
-
node.data_nodes.each {|dn| final_result << dn.
|
144
|
+
node.data_nodes.each {|dn| final_result << dn.line_data_and_token} if node.data_nodes
|
156
145
|
end
|
157
146
|
end
|
158
147
|
|
159
148
|
# Only order the information if we're returning hashes.
|
160
149
|
# TODO: We might want to change the placement of order_results_by_position
|
161
150
|
# if the operation is being done against hashes and not data nodes.
|
162
|
-
nodes.first.class.is_a?(Hash) ? DataNode.order_results_by_position(final_result) : final_result
|
151
|
+
# nodes.first.class.is_a?(Hash) ? DataNode.order_results_by_position(final_result) : final_result
|
152
|
+
final_result
|
163
153
|
end
|
164
154
|
|
165
155
|
def replace(type:, old_token:, new_token:)
|
@@ -175,13 +165,7 @@ module Masamune
|
|
175
165
|
private
|
176
166
|
|
177
167
|
def get_node_class(type)
|
178
|
-
|
179
|
-
"Masamune::AbstractSyntaxTree::#{type.to_s.camelize}".constantize
|
180
|
-
rescue NameError
|
181
|
-
# For all other nodes that we haven't covered yet, we just make a general class.
|
182
|
-
# We can worry about adding the classes for other nodes as we go.
|
183
|
-
Node
|
184
|
-
end
|
168
|
+
"#{self.class}::#{type.to_s.camelize}".safe_constantize || Node # Return base Node class for any not-yet-covered nodes.
|
185
169
|
end
|
186
170
|
|
187
171
|
# We only order results when they are a Hash.
|
data/lib/masamune/slasher.rb
CHANGED
@@ -3,22 +3,26 @@ module Masamune
|
|
3
3
|
def self.replace(type:, old_token:, new_token:, code:, ast:)
|
4
4
|
# `type` can either be a method from the ast like `method_definitions`,
|
5
5
|
# or it can be a list of Masamune::AbstractSyntaxTree node classes.
|
6
|
-
|
6
|
+
line_data_and_token_ary = if type.is_a?(Symbol)
|
7
7
|
type_to_method = type.to_s.pluralize.to_sym
|
8
8
|
ast.send(type_to_method)
|
9
9
|
elsif type.is_a?(Array)
|
10
10
|
type.map {|klass| ast.find_nodes(klass)}.flatten
|
11
11
|
end
|
12
12
|
|
13
|
-
tokens_to_replace =
|
14
|
-
|
13
|
+
tokens_to_replace = line_data_and_token_ary.select do |line_data_and_token|
|
14
|
+
line_data_and_token[:token] == old_token
|
15
15
|
end
|
16
16
|
|
17
17
|
# Build from lex nodes
|
18
18
|
result = ast.lex_nodes.map do |lex_node|
|
19
19
|
match_found = false
|
20
|
-
tokens_to_replace.each do |
|
21
|
-
|
20
|
+
tokens_to_replace.each do |line_data_and_token|
|
21
|
+
position = [
|
22
|
+
line_data_and_token[:line_number],
|
23
|
+
line_data_and_token[:index_on_line]
|
24
|
+
]
|
25
|
+
if position == lex_node.position
|
22
26
|
match_found = true
|
23
27
|
break
|
24
28
|
end
|
data/lib/masamune/version.rb
CHANGED
data/lib/masamune.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: masamune-ast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Zayas
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -71,7 +71,7 @@ metadata:
|
|
71
71
|
homepage_uri: https://www.github.com/gazayas/masamune-ast
|
72
72
|
source_code_uri: https://www.github.com/gazayas/masamune-ast
|
73
73
|
changelog_uri: https://www.github.com/gazayas/masamune-ast
|
74
|
-
post_install_message:
|
74
|
+
post_install_message:
|
75
75
|
rdoc_options: []
|
76
76
|
require_paths:
|
77
77
|
- lib
|
@@ -86,8 +86,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
86
|
- !ruby/object:Gem::Version
|
87
87
|
version: '0'
|
88
88
|
requirements: []
|
89
|
-
rubygems_version: 3.
|
90
|
-
signing_key:
|
89
|
+
rubygems_version: 3.4.1
|
90
|
+
signing_key:
|
91
91
|
specification_version: 4
|
92
92
|
summary: Masamune
|
93
93
|
test_files: []
|