masamune-ast 1.1.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +50 -44
- data/lib/masamune/abstract_syntax_tree/data_node.rb +28 -25
- data/lib/masamune/abstract_syntax_tree/nodes/call.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/command.rb +64 -0
- data/lib/masamune/abstract_syntax_tree/nodes/def.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/params.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/string_content.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/support_nodes/comment.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/symbol.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/symbols/dyna_symbol.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/symbols/symbol_literal.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/variables/block_var.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/variables/var_field.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/variables/var_ref.rb +1 -1
- data/lib/masamune/abstract_syntax_tree/nodes/vcall.rb +1 -1
- data/lib/masamune/abstract_syntax_tree.rb +6 -4
- data/lib/masamune/slasher.rb +9 -5
- data/lib/masamune/version.rb +1 -1
- data/lib/masamune.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3029256236d8953d9f258d370abb57d1dac4a425b9112f7f4a5a42e951634943
|
4
|
+
data.tar.gz: 123f4977244d1db7d1cbceebee495e6a7d8494401f1db86f4a8360de27b06f2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3282cd14e7c591bff140c5d92d782a9724be2af32e9cc2542a0aab949fd469ad4c526c2a11255be267546cb2d1634e97ea7d950c8762c21635fc4274fe5f487c
|
7
|
+
data.tar.gz: d652c21b25b38d460045336d9dd330afb302470358a98f43a0eacb7f79128cdc26e8efe6d233f41067f2be28af3314585ba03b0df1f83e47f8f7d8419a018d98
|
data/Gemfile.lock
CHANGED
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 Ruby source code analyzer based on Ripper’s Abstract Syntax Tree generator.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -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,20 +88,22 @@ CODE
|
|
88
88
|
msmn = Masamune::AbstractSyntaxTree.new(code)
|
89
89
|
|
90
90
|
msmn.all_methods
|
91
|
-
#=> [{:
|
92
|
-
#=> {:
|
93
|
-
#=> {:
|
94
|
-
#=> {:
|
95
|
-
#=> {:
|
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"}]
|
96
97
|
|
97
98
|
msmn.method_calls
|
98
|
-
#=> [{:
|
99
|
-
#=> {:
|
100
|
-
#=> {:
|
101
|
-
#=> {:
|
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"}]
|
102
104
|
|
103
105
|
msmn.method_definitions
|
104
|
-
#=> [{:
|
106
|
+
#=> [{:line_number=>6, :index_on_line=>4, :token=>"foo"}]
|
105
107
|
```
|
106
108
|
|
107
109
|
You can also return the node classes themselves and get the data from there:
|
@@ -113,28 +115,32 @@ CODE
|
|
113
115
|
|
114
116
|
msmn = Masamune::AbstractSyntaxTree.new(code)
|
115
117
|
msmn.strings(result_type: :nodes)
|
116
|
-
#=> [#<Masamune::AbstractSyntaxTree::StringContent:
|
117
|
-
#=>
|
118
|
-
#=>
|
119
|
-
#=>
|
120
|
-
#=>
|
121
|
-
#=>
|
122
|
-
#=>
|
123
|
-
#=>
|
124
|
-
#=>
|
125
|
-
#=>
|
126
|
-
#=>
|
127
|
-
#=>
|
128
|
-
#=>
|
129
|
-
#=>
|
130
|
-
#=>
|
131
|
-
#=> [
|
132
|
-
#=>
|
133
|
-
#=>
|
134
|
-
#=>
|
135
|
-
#=>
|
136
|
-
#=>
|
137
|
-
#=>
|
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>]>]
|
138
144
|
```
|
139
145
|
|
140
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,49 +3,44 @@
|
|
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
|
-
def initialize(contents, ast_id)
|
15
|
-
@type, @token,
|
14
|
+
def initialize(contents, ast_id, parent)
|
15
|
+
@type, @token, line_position = contents
|
16
|
+
@line_number, @index_on_line = line_position
|
17
|
+
@parent = parent
|
16
18
|
super(contents, ast_id)
|
17
19
|
end
|
18
20
|
|
19
21
|
# Results here represent the position and token of the
|
20
22
|
# data we're searching in the form of a Hash like the following:
|
21
23
|
# [
|
22
|
-
# {
|
23
|
-
# {
|
24
|
+
# {line_number: 4, index_on_line: 7, token: "ruby"},
|
25
|
+
# {line_number: 7, index_on_line: 7, token: "rails"}
|
24
26
|
# ]
|
25
27
|
# TODO: Worry about using a faster sorting algorithm later.
|
26
|
-
def self.order_results_by_position(
|
27
|
-
# Extract the line numbers first, i.e - 4 from [4, 7]
|
28
|
-
line_numbers = position_and_token_ary.map do |position_and_token|
|
29
|
-
line_number = position_and_token[:position].first
|
30
|
-
end.uniq.sort
|
31
|
-
|
28
|
+
def self.order_results_by_position(results)
|
32
29
|
final_result = []
|
30
|
+
|
31
|
+
line_numbers = results.map {|result| result[:line_number]}.uniq.sort
|
33
32
|
line_numbers.each do |line_number|
|
34
33
|
# Group data together in an array if they're on the same line.
|
35
|
-
shared_line_data =
|
36
|
-
position_and_token[:position].first == line_number
|
37
|
-
end
|
34
|
+
shared_line_data = results.select {|result| result[:line_number] == line_number}
|
38
35
|
|
39
36
|
# Sort the positions on each line number respectively.
|
40
|
-
|
41
|
-
position_and_token[:position].last
|
42
|
-
end.sort
|
37
|
+
indexes_on_line = shared_line_data.map {|data| data[:index_on_line]}.sort
|
43
38
|
|
44
39
|
# Apply to the final result.
|
45
|
-
|
46
|
-
shared_line_data.each do |
|
47
|
-
if
|
48
|
-
final_result <<
|
40
|
+
indexes_on_line.each do |index_on_line|
|
41
|
+
shared_line_data.each do |data|
|
42
|
+
if data[:index_on_line] == index_on_line
|
43
|
+
final_result << data
|
49
44
|
end
|
50
45
|
end
|
51
46
|
end
|
@@ -54,8 +49,16 @@ module Masamune
|
|
54
49
|
final_result
|
55
50
|
end
|
56
51
|
|
57
|
-
def
|
58
|
-
{
|
52
|
+
def line_data_and_token
|
53
|
+
{
|
54
|
+
line_number: @line_number,
|
55
|
+
index_on_line: @index_on_line,
|
56
|
+
token: @token
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def position
|
61
|
+
[@line_number, @index_on_line]
|
59
62
|
end
|
60
63
|
end
|
61
64
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Code example:
|
2
|
+
# puts "hello"
|
3
|
+
#
|
4
|
+
# syntax_tree output:
|
5
|
+
# SyntaxTree::Command[
|
6
|
+
# message: SyntaxTree::Ident[value: "puts"],
|
7
|
+
# arguments: SyntaxTree::Args[
|
8
|
+
# parts: [
|
9
|
+
# SyntaxTree::StringLiteral[
|
10
|
+
# parts: [SyntaxTree::TStringContent[value: "greetings"]]
|
11
|
+
# ]
|
12
|
+
# ]
|
13
|
+
# ]
|
14
|
+
# ]
|
15
|
+
|
16
|
+
# Code example:
|
17
|
+
# namespace :project
|
18
|
+
# resources :pages
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# syntax_tree output:
|
22
|
+
# SyntaxTree::Command[
|
23
|
+
# message: SyntaxTree::Ident[value: "namespace"],
|
24
|
+
# arguments: SyntaxTree::Args[
|
25
|
+
# parts: [SyntaxTree::SymbolLiteral[value: SyntaxTree::Ident[value: "project"]]]
|
26
|
+
# ],
|
27
|
+
# block: SyntaxTree::BlockNode[
|
28
|
+
# bodystmt: SyntaxTree::BodyStmt[
|
29
|
+
# statements: SyntaxTree::Statements[
|
30
|
+
# body: [
|
31
|
+
# SyntaxTree::Command[
|
32
|
+
# message: SyntaxTree::Ident[value: "resources"],
|
33
|
+
# arguments: SyntaxTree::Args[
|
34
|
+
# parts: [
|
35
|
+
# SyntaxTree::SymbolLiteral[
|
36
|
+
# value: SyntaxTree::Ident[value: "pages"]
|
37
|
+
# ]
|
38
|
+
# ]
|
39
|
+
# ]
|
40
|
+
# ]
|
41
|
+
# ]
|
42
|
+
# ]
|
43
|
+
# ]
|
44
|
+
# ]
|
45
|
+
# ]
|
46
|
+
|
47
|
+
|
48
|
+
# TODO: As you can see in the second example above, `command` can receive a
|
49
|
+
# block, so we might want to have a way to access the block node in the future.
|
50
|
+
module Masamune
|
51
|
+
class AbstractSyntaxTree
|
52
|
+
class Command < Node
|
53
|
+
def initialize(contents, ast_id)
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
def extract_data_nodes
|
58
|
+
[
|
59
|
+
DataNode.new(@contents[1], @ast_id, self)
|
60
|
+
]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -13,7 +13,7 @@ module Masamune
|
|
13
13
|
# we just use a method specifically for getting the symbol.
|
14
14
|
# This should be the same as the :symbol_literal type.
|
15
15
|
def get_symbol_data
|
16
|
-
DataNode.new(@contents[1][1], @ast_id)
|
16
|
+
DataNode.new(@contents[1][1], @ast_id, self)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -14,7 +14,7 @@ module Masamune
|
|
14
14
|
# we just use a method specifically for getting the symbol.
|
15
15
|
# This should be the same as the :dyna_symbol type.
|
16
16
|
def get_symbol_data
|
17
|
-
DataNode.new(@contents[1][1], @ast_id)
|
17
|
+
DataNode.new(@contents[1][1], @ast_id, self)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -74,7 +74,8 @@ module Masamune
|
|
74
74
|
def method_calls(name: nil, result_type: Hash)
|
75
75
|
method_classes = [
|
76
76
|
:vcall,
|
77
|
-
:call
|
77
|
+
:call,
|
78
|
+
:command
|
78
79
|
].map {|type| get_node_class(type)}
|
79
80
|
results = find_nodes(method_classes, token: name, result_type: result_type)
|
80
81
|
order_results(results)
|
@@ -149,16 +150,17 @@ module Masamune
|
|
149
150
|
# Data for symbols are housed within a nested node, so we handle those differently here.
|
150
151
|
# Read the comments for `get_symbol_data` in the symbol node classes for details.
|
151
152
|
if node.class == Masamune::AbstractSyntaxTree::SymbolLiteral || node.class == Masamune::AbstractSyntaxTree::DynaSymbol
|
152
|
-
final_result << node.get_symbol_data.
|
153
|
+
final_result << node.get_symbol_data.line_data_and_token
|
153
154
|
else
|
154
|
-
node.data_nodes.each {|dn| final_result << dn.
|
155
|
+
node.data_nodes.each {|dn| final_result << dn.line_data_and_token} if node.data_nodes
|
155
156
|
end
|
156
157
|
end
|
157
158
|
|
158
159
|
# Only order the information if we're returning hashes.
|
159
160
|
# TODO: We might want to change the placement of order_results_by_position
|
160
161
|
# if the operation is being done against hashes and not data nodes.
|
161
|
-
nodes.first.class.is_a?(Hash) ? DataNode.order_results_by_position(final_result) : final_result
|
162
|
+
# nodes.first.class.is_a?(Hash) ? DataNode.order_results_by_position(final_result) : final_result
|
163
|
+
final_result
|
162
164
|
end
|
163
165
|
|
164
166
|
def replace(type:, old_token:, new_token:)
|
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
@@ -21,6 +21,7 @@ require "masamune/abstract_syntax_tree/nodes/variables/var_field"
|
|
21
21
|
require "masamune/abstract_syntax_tree/nodes/variables/var_ref"
|
22
22
|
require "masamune/abstract_syntax_tree/nodes/assign"
|
23
23
|
require "masamune/abstract_syntax_tree/nodes/call"
|
24
|
+
require "masamune/abstract_syntax_tree/nodes/command"
|
24
25
|
require "masamune/abstract_syntax_tree/nodes/def"
|
25
26
|
require "masamune/abstract_syntax_tree/nodes/params"
|
26
27
|
require "masamune/abstract_syntax_tree/nodes/program"
|
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.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabriel Zayas
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -46,6 +46,7 @@ files:
|
|
46
46
|
- lib/masamune/abstract_syntax_tree/nodes/blocks/brace_block.rb
|
47
47
|
- lib/masamune/abstract_syntax_tree/nodes/blocks/do_block.rb
|
48
48
|
- lib/masamune/abstract_syntax_tree/nodes/call.rb
|
49
|
+
- lib/masamune/abstract_syntax_tree/nodes/command.rb
|
49
50
|
- lib/masamune/abstract_syntax_tree/nodes/def.rb
|
50
51
|
- lib/masamune/abstract_syntax_tree/nodes/params.rb
|
51
52
|
- lib/masamune/abstract_syntax_tree/nodes/program.rb
|