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 +4 -4
- data/lib/proc_to_ast.rb +81 -45
- data/lib/proc_to_ast/version.rb +1 -1
- data/spec/proc_to_ast_spec.rb +44 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af92479d75ab4508025af6183f02826935da753f
|
4
|
+
data.tar.gz: e7d4661d31825657ab4d3f87b5f5f773a083d89f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
29
|
+
(linenum - 1).times { file.gets }
|
30
|
+
buf = []
|
31
|
+
try_count = 0
|
28
32
|
|
29
|
-
|
33
|
+
begin
|
34
|
+
try_count += 1
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
buf << file.gets
|
37
|
+
do_parse(buf)
|
38
|
+
rescue ::Parser::SyntaxError => e
|
39
|
+
node = trim_and_retry(buf)
|
35
40
|
|
36
|
-
return
|
37
|
-
|
41
|
+
return node unless node.nil?
|
42
|
+
retry if try_count < retry_limit
|
38
43
|
|
39
|
-
|
44
|
+
raise e
|
45
|
+
ensure
|
46
|
+
file.close
|
47
|
+
end
|
40
48
|
end
|
41
|
-
end
|
42
|
-
end
|
43
49
|
|
44
|
-
|
45
|
-
def to_ast(retry_limit = 20)
|
46
|
-
filename, linenum = source_location
|
47
|
-
file = File.open(filename, "rb")
|
50
|
+
private
|
48
51
|
|
49
|
-
(
|
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 =
|
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
|
-
|
77
|
-
|
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
|
data/lib/proc_to_ast/version.rb
CHANGED
data/spec/proc_to_ast_spec.rb
CHANGED
@@ -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
|
|