proc_to_ast 0.0.3 → 0.0.4

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
  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