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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34d1bcf1f5fc587393f5ded22b65afb26d64217253dcea0d83ea483574988326
4
- data.tar.gz: 21f726e99d53494fb50c08012962156245a37920425fae2ab338946d463f2721
3
+ metadata.gz: 3029256236d8953d9f258d370abb57d1dac4a425b9112f7f4a5a42e951634943
4
+ data.tar.gz: 123f4977244d1db7d1cbceebee495e6a7d8494401f1db86f4a8360de27b06f2e
5
5
  SHA512:
6
- metadata.gz: 2cca246c5770392976a5b9b9a577ac90a7363c8972bbf954f76493b13dfebbc16e3c3eeb3230238e348fa77f11accadf326fb774d11a40d03aaf172e15f9a946
7
- data.tar.gz: 9da6db975cdd75e0a3fc57056bcb2908054a54f90da996660d4fca46e967e64aa5431a78e118b8a264ca4749f421fc7dc667626da5b6e28b7d724656d7bca241
6
+ metadata.gz: 3282cd14e7c591bff140c5d92d782a9724be2af32e9cc2542a0aab949fd469ad4c526c2a11255be267546cb2d1634e97ea7d950c8762c21635fc4274fe5f487c
7
+ data.tar.gz: d652c21b25b38d460045336d9dd330afb302470358a98f43a0eacb7f79128cdc26e8efe6d233f41067f2be28af3314585ba03b0df1f83e47f8f7d8419a018d98
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- masamune-ast (1.1.4)
4
+ masamune-ast (1.2.0)
5
5
  activesupport
6
6
 
7
7
  GEM
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 (`Ripper#sexp`).
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
- #=> [{:position=>[1, 0], :token=>"java"},
61
- #=> {:position=>[2, 0], :token=>"javascript"},
62
- #=> {:position=>[2, 13], :token=>"java"},
63
- #=> {:position=>[3, 5], :token=>"java"},
64
- #=> {:position=>[3, 25], :token=>"javascript"}]
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
- #=> [{:position=>[1, 8], :token=>"java"},
68
- #=> {:position=>[2, 21], :token=>"script"},
69
- #=> {:position=>[3, 13], :token=>" is not "}]
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
- #=> [{position: [1, 0], token: "java"},
73
- #=> {position: [2, 13], token: "java"},
74
- #=> {position: [3, 5], token: "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"}]
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
- #=> [{:position=>[2, 4], :token=>"sum"},
92
- #=> {:position=>[2, 8], :token=>"times"},
93
- #=> {:position=>[6, 4], :token=>"foo"},
94
- #=> {:position=>[8, 0], :token=>"foo"},
95
- #=> {:position=>[9, 0], :token=>"foo"}]
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
- #=> [{:position=>[2, 4], :token=>"sum"},
99
- #=> {:position=>[2, 8], :token=>"times"},
100
- #=> {:position=>[8, 0], :token=>"foo"},
101
- #=> {:position=>[9, 0], :token=>"foo"}]
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
- #=> [{:position=>[6, 4], :token=>"foo"}]
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:0x00007f6f7c9a6850
117
- #=> @ast_id=1440,
118
- #=> @contents=[:string_content, [:@tstring_content, "ruby", [1, 1]]],
119
- #=> @data_nodes=
120
- #=> [#<Masamune::AbstractSyntaxTree::DataNode:0x00007f6f7c9a6828
121
- #=> @ast_id=1440,
122
- #=> @contents=[:@tstring_content, "ruby", [1, 1]],
123
- #=> @data_nodes=nil,
124
- #=> @line_position=[1, 1],
125
- #=> @token="ruby",
126
- #=> @type=:@tstring_content>]>,
127
- #=> #<Masamune::AbstractSyntaxTree::StringContent:0x00007f6f7c9a5630
128
- #=> @ast_id=1440,
129
- #=> @contents=[:string_content, [:@tstring_content, "rails", [2, 1]]],
130
- #=> @data_nodes=
131
- #=> [#<Masamune::AbstractSyntaxTree::DataNode:0x00007f6f7c9a5608
132
- #=> @ast_id=1440,
133
- #=> @contents=[:@tstring_content, "rails", [2, 1]],
134
- #=> @data_nodes=nil,
135
- #=> @line_position=[2, 1],
136
- #=> @token="rails",
137
- #=> @type=:@tstring_content>]>]
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, `position_and_token`,
7
- # which looks like this: {position: [4, 7], token: "ruby"}
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, :line_position
12
+ attr_reader :type, :token, :line_number, :index_on_line
13
13
 
14
- def initialize(contents, ast_id)
15
- @type, @token, @line_position = contents
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
- # {position: [4, 7], token: "ruby"},
23
- # {position: [7, 7], token: "rails"}
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(position_and_token_ary)
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 = position_and_token_ary.select do |position_and_token|
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
- positions_on_line = shared_line_data.map do |position_and_token|
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
- positions_on_line.each do |position_on_line|
46
- shared_line_data.each do |position_and_token|
47
- if position_and_token[:position].last == position_on_line
48
- final_result << position_and_token
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 position_and_token
58
- {position: @line_position, token: @token}
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
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents.last, @ast_id)
12
+ DataNode.new(@contents.last, @ast_id, self)
13
13
  ]
14
14
  end
15
15
  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
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents[1], @ast_id)
12
+ DataNode.new(@contents[1], @ast_id, self)
13
13
  ]
14
14
  end
15
15
  end
@@ -14,7 +14,7 @@ module Masamune
14
14
  # to ensure that it's being handled properly.
15
15
  unless @contents[1].nil?
16
16
  @contents[1].map do |content|
17
- DataNode.new(content, @ast_id)
17
+ DataNode.new(content, @ast_id, self)
18
18
  end
19
19
  end
20
20
  end
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents[1], @ast_id)
12
+ DataNode.new(@contents[1], @ast_id, self)
13
13
  ]
14
14
  end
15
15
  end
@@ -17,7 +17,7 @@ module Masamune
17
17
 
18
18
  def extract_data_nodes
19
19
  [
20
- DataNode.new([contents.type, contents.token, contents.position], @ast_id)
20
+ DataNode.new([contents.type, contents.token, contents.position], @ast_id, self)
21
21
  ]
22
22
  end
23
23
  end
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents[1], @ast_id)
12
+ DataNode.new(@contents[1], @ast_id, self)
13
13
  ]
14
14
  end
15
15
  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
@@ -14,7 +14,7 @@ module Masamune
14
14
  # nice to find out and document/implement it somewhere.
15
15
  def extract_data_nodes
16
16
  @contents[1][1].map do |content|
17
- DataNode.new(content, @ast_id)
17
+ DataNode.new(content, @ast_id, self)
18
18
  end
19
19
  end
20
20
  end
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents[1], @ast_id)
12
+ DataNode.new(@contents[1], @ast_id, self)
13
13
  ]
14
14
  end
15
15
  end
@@ -11,7 +11,7 @@ module Masamune
11
11
 
12
12
  def extract_data_nodes
13
13
  [
14
- DataNode.new(@contents[1], @ast_id)
14
+ DataNode.new(@contents[1], @ast_id, self)
15
15
  ]
16
16
  end
17
17
  end
@@ -9,7 +9,7 @@ module Masamune
9
9
 
10
10
  def extract_data_nodes
11
11
  [
12
- DataNode.new(@contents[1], @ast_id)
12
+ DataNode.new(@contents[1], @ast_id, self)
13
13
  ]
14
14
  end
15
15
  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.position_and_token
153
+ final_result << node.get_symbol_data.line_data_and_token
153
154
  else
154
- node.data_nodes.each {|dn| final_result << dn.position_and_token} if node.data_nodes
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:)
@@ -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
- position_and_token_ary = if type.is_a?(Symbol)
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 = position_and_token_ary.select do |pos_and_tok|
14
- pos_and_tok[:token] == old_token
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 |position_and_token|
21
- if position_and_token[:position] == lex_node.position
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Masamune
4
- VERSION = "1.1.4"
4
+ VERSION = "1.2.0"
5
5
  end
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.1.4
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-10 00:00:00.000000000 Z
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