proc_to_ast 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b7485fc28f9e8f9106d852bd481ce684303a34b
4
- data.tar.gz: 961f9b5b02d498888dcb77421498e2d1b7208d97
3
+ metadata.gz: af92479d75ab4508025af6183f02826935da753f
4
+ data.tar.gz: e7d4661d31825657ab4d3f87b5f5f773a083d89f
5
5
  SHA512:
6
- metadata.gz: 40c92362331a397794ddd157fb31b914f49a9a27e796a75850eafc3be72fc89e9fe10bde4834e7ec2766b2de2567c1df30abb5f0bcdfb03543d917d1f3ab2f92
7
- data.tar.gz: bf5474a65640f2e2505ae8a4407f248815c11f3951662d69725c74cd1e289204e59adfbd7e58c5865e4fda883ea786a2a1e9b5ed9d3227cd205daa6d0aa4d5f0
6
+ metadata.gz: bb3e438c238c4e153e9cb24adfcc11fc090a6dc713bad95dcd72748062983a62fc0eae58cc9a79721a365e4ffa7022babeb74188b3208acc010d0d13717a2048
7
+ data.tar.gz: ea3ca120b291b266d5bda7b2fc0541400e38d696c80ac836d36d9b48bc90f3171e976c3f2e18efbe107ee2480ae5870cca16e45330db32832c12712f34f3438c
data/lib/proc_to_ast.rb CHANGED
@@ -7,74 +7,110 @@ require 'coderay'
7
7
  module ProcToAst
8
8
  class MultiMatchError < StandardError; end
9
9
 
10
- class Traverser
11
- def traverse_node(node)
12
- if node.type != :block
13
- node.children.flat_map { |child|
14
- if child.is_a?(AST::Node)
15
- traverse_node(child)
16
- end
17
- }.compact
18
- else
19
- [node]
20
- end
10
+ class Parser
11
+ attr_reader :parser
12
+
13
+ def initialize
14
+ @parser = ::Parser::CurrentRuby.default_parser
15
+ @parser.diagnostics.consumer = ->(diagnostic) {} # suppress error message
21
16
  end
22
17
 
23
- def proc_block?(node)
24
- head = node.children[0]
25
- return false unless head.type == :send
18
+ # Read file and try parsing
19
+ # if success parse, find proc AST
20
+ #
21
+ # @param filename [String] reading file path
22
+ # @param linenum [Integer] start line number
23
+ # @param retry_limit [Integer]
24
+ # @return [Parser::AST::Node] Proc AST
25
+ def parse(filename, linenum, retry_limit = 20)
26
+ @filename, @linenum = filename, linenum
27
+ file = File.open(filename, "rb")
26
28
 
27
- receiver, symbol = head.children
29
+ (linenum - 1).times { file.gets }
30
+ buf = []
31
+ try_count = 0
28
32
 
29
- return true if receiver.nil? && (symbol == :proc || symbol == :lambda)
33
+ begin
34
+ try_count += 1
30
35
 
31
- if receiver.is_a?(AST::Node) &&
32
- receiver.type == :const &&
33
- receiver.children[1] == :Proc &&
34
- symbol == :new
36
+ buf << file.gets
37
+ do_parse(buf)
38
+ rescue ::Parser::SyntaxError => e
39
+ node = trim_and_retry(buf)
35
40
 
36
- return true
37
- end
41
+ return node unless node.nil?
42
+ retry if try_count < retry_limit
38
43
 
39
- false
44
+ raise e
45
+ ensure
46
+ file.close
47
+ end
40
48
  end
41
- end
42
- end
43
49
 
44
- class Proc
45
- def to_ast(retry_limit = 20)
46
- filename, linenum = source_location
47
- file = File.open(filename, "rb")
50
+ private
48
51
 
49
- (linenum - 1).times { file.gets }
50
- buf = []
51
- try_count = 0
52
-
53
- parser = Parser::CurrentRuby.default_parser
54
- parser.diagnostics.consumer = ->(diagnostic) {} # suppress error message
55
- begin
52
+ def do_parse(buf)
56
53
  parser.reset
57
- try_count += 1
58
-
59
- buf << file.gets
60
54
  source = buf.join.force_encoding(parser.default_encoding)
61
55
 
62
- source_buffer = Parser::Source::Buffer.new(filename, linenum)
56
+ source_buffer = ::Parser::Source::Buffer.new(@filename, @linenum)
63
57
  source_buffer.source = source
64
58
  node = parser.parse(source_buffer)
65
- block_nodes = ProcToAst::Traverser.new.traverse_node(node)
59
+ block_nodes = traverse_node(node)
60
+
66
61
  if block_nodes.length == 1
67
62
  block_nodes.first
68
63
  else
69
64
  raise ProcToAst::MultiMatchError
70
65
  end
71
- rescue Parser::SyntaxError
72
- retry if try_count < retry_limit
73
66
  end
67
+
68
+ # Remove tail comma or Hash syntax, and retry parsing
69
+ def trim_and_retry(buf)
70
+ *lines, last = buf
71
+
72
+ # For inner Array or Hash or Arguments list.
73
+ lines << last.gsub(/,\s*/, "")
74
+
75
+ lines[0] = lines[0]
76
+ .gsub(/[0-9a-zA-Z_]+:\s*/, "")
77
+ .gsub(/[^\s]+\s*=>\s*/, "")
78
+
79
+ begin
80
+ do_parse(lines)
81
+ rescue ::Parser::SyntaxError
82
+ nil
83
+ end
84
+ end
85
+
86
+ def traverse_node(node)
87
+ if node.type != :block
88
+ node.children.flat_map { |child|
89
+ if child.is_a?(AST::Node)
90
+ traverse_node(child)
91
+ end
92
+ }.compact
93
+ else
94
+ [node]
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ class Proc
101
+ # @param retry_limit [Integer]
102
+ # @return [Parser::AST::Node] Proc AST
103
+ def to_ast(retry_limit = 20)
104
+ filename, linenum = source_location
105
+ parser = ProcToAst::Parser.new
106
+ parser.parse(filename, linenum, retry_limit)
74
107
  end
75
108
 
76
- def to_source(highlight: false)
77
- source = Unparser.unparse(to_ast)
109
+ # @param highlight [Boolean] enable output highlight
110
+ # @param retry_limit [Integer]
111
+ # @return [String] proc source code
112
+ def to_source(highlight: false, retry_limit: 20)
113
+ source = Unparser.unparse(to_ast(retry_limit))
78
114
  if highlight
79
115
  CodeRay.scan(source, :ruby).terminal
80
116
  else
@@ -1,3 +1,3 @@
1
1
  module ProcToAst
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -48,6 +48,50 @@ describe Proc do
48
48
 
49
49
  expect{ fuga.to_ast }.to raise_error(ProcToAst::MultiMatchError)
50
50
  end
51
+
52
+ it "inner array" do
53
+ array = [
54
+ 1,
55
+ -> { 2 },
56
+ [
57
+ -> {
58
+ 3
59
+ },
60
+ ],
61
+ proc do |a|
62
+ [
63
+ a,
64
+ ]
65
+ end
66
+ ]
67
+ expect(array[1].to_ast).to be_a(AST::Node)
68
+ expect(array[1].to_source).to eq("lambda do\n 2\nend")
69
+ expect(array[2][0].to_source).to eq("lambda do\n 3\nend")
70
+ expect(array[3].to_source).to eq("proc do |a|\n [a]\nend")
71
+ end
72
+
73
+ it "inner hash" do
74
+ hash_oneline = {a: -> { 1 }}
75
+ expect(hash_oneline[:a].to_ast).to be_a(AST::Node)
76
+ expect(hash_oneline[:a].to_source).to eq("lambda do\n 1\nend")
77
+
78
+ hash = {
79
+ a: -> { 1 },
80
+ :b => -> { 2 },
81
+ c: -> {
82
+ 3
83
+ },
84
+ d: -> { 4 }
85
+ }
86
+ expect(hash[:a].to_ast).to be_a(AST::Node)
87
+ expect(hash[:a].to_source).to eq("lambda do\n 1\nend")
88
+ expect(hash[:b].to_ast).to be_a(AST::Node)
89
+ expect(hash[:b].to_source).to eq("lambda do\n 2\nend")
90
+ expect(hash[:c].to_ast).to be_a(AST::Node)
91
+ expect(hash[:c].to_source).to eq("lambda do\n 3\nend")
92
+ expect(hash[:d].to_ast).to be_a(AST::Node)
93
+ expect(hash[:d].to_source).to eq("lambda do\n 4\nend")
94
+ end
51
95
  end
52
96
  end
53
97
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proc_to_ast
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - joker1007