ripper_ruby_parser 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,76 @@
1
+ = RipperRubyParser
2
+
3
+ by Matijs van Zuijlen
4
+
5
+ http://www.github.com/mvz/ripper_ruby_parser
6
+
7
+ == Description
8
+
9
+ Parse with Ripper, produce sexps that are compatible with RubyParser.
10
+
11
+ == Features/Notes
12
+
13
+ * Drop-in replacement for RubyParser.
14
+ * Should handle 1.9 syntax gracefully.
15
+ * Only tested with MRI 1.9.3. Definitely does not work with the ripper gem for
16
+ MRI 1.8.
17
+ * Many edge cases are surely not yet taken care of (but hey, it's version
18
+ 0.0.1).
19
+
20
+ == Install
21
+
22
+ * gem install ripper_ruby_parser
23
+
24
+ == Synopsis
25
+
26
+ Basic usage:
27
+
28
+ require 'ripper_ruby_parser'
29
+
30
+ parser = RipperRubyParser::Parser.new
31
+ result = parser.parse "puts 'Hello World'"
32
+ p result
33
+ # => s(:call, nil, :puts, s(:arglist, s(:str, "Hello World!")))
34
+
35
+ == Requirements
36
+
37
+ * Ruby 1.9.3. It may work with lower 1.9 versions. Let me know if it does.
38
+ * sexp_processor
39
+
40
+ == Hacking and contributing
41
+
42
+ If you want to send pull requests or patches, please:
43
+
44
+ * Make sure `rake test` runs without reporting any failures. If your code
45
+ breaks existing stuff, it won't get merged in.
46
+ * Add tests for your feature. Otherwise, I can't see if it works or if I
47
+ break it later.
48
+ * Make sure latest master merges cleanly with your branch. Things might
49
+ have moved around since you forked.
50
+ * Try not to include changes that are irrelevant to your feature in the
51
+ same commit.
52
+
53
+ == License
54
+
55
+ (The MIT License)
56
+
57
+ Copyright (c) 2012 Matijs van Zuijlen
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining
60
+ a copy of this software and associated documentation files (the
61
+ 'Software'), to deal in the Software without restriction, including
62
+ without limitation the rights to use, copy, modify, merge, publish,
63
+ distribute, sublicense, and/or sell copies of the Software, and to
64
+ permit persons to whom the Software is furnished to do so, subject to
65
+ the following conditions:
66
+
67
+ The above copyright notice and this permission notice shall be
68
+ included in all copies or substantial portions of the Software.
69
+
70
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
71
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
72
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
73
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
74
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
75
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
76
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rake/testtask'
2
+
3
+ namespace :test do
4
+
5
+ Rake::TestTask.new(:unit) do |t|
6
+ t.libs = ['lib']
7
+ t.test_files = FileList['test/unit/*_test.rb']
8
+ t.ruby_opts += ["-w"]
9
+ end
10
+
11
+ Rake::TestTask.new(:end_to_end) do |t|
12
+ t.libs = ['lib']
13
+ t.test_files = FileList['test/end_to_end/*_test.rb']
14
+ t.ruby_opts += ["-w"]
15
+ end
16
+
17
+ task :run => [:unit, :end_to_end]
18
+
19
+ end
20
+
21
+ desc 'Alias to test:run'
22
+ task :test => 'test:run'
23
+
24
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ require 'ripper'
2
+ require 'ripper_ruby_parser/sexp_processor'
3
+
4
+ module RipperRubyParser
5
+ # Main parser class. Brings together Ripper and our
6
+ # RipperRubyParser::SexpProcessor.
7
+ class Parser
8
+ def initialize processor=SexpProcessor.new
9
+ @processor = processor
10
+ end
11
+
12
+ def parse source
13
+ exp = Sexp.from_array(Ripper.sexp source)
14
+ @processor.process exp
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,14 @@
1
+ require 'sexp_processor'
2
+
3
+ module RipperRubyParser
4
+ # Extensions to Sexp
5
+ module SexpExt
6
+ def fix_empty_type
7
+ unless sexp_type.is_a? Symbol
8
+ unshift :__empty
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ Sexp.send :include, RipperRubyParser::SexpExt
@@ -0,0 +1,23 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Arguments
4
+ def process_args_add_block exp
5
+ _, content, _ = exp.shift 3
6
+ s(:arglist, *handle_list_with_optional_splat(content))
7
+ end
8
+
9
+ def process_args_add_star exp
10
+ generic_add_star exp
11
+ end
12
+
13
+ def process_arg_paren exp
14
+ _, args = exp.shift 2
15
+ args = s() if args.nil?
16
+ unless args.first.is_a? Symbol
17
+ args.unshift :arglist
18
+ end
19
+ process(args)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Arrays
4
+ def process_array exp
5
+ _, elems = exp.shift 2
6
+ s(:array, *handle_list_with_optional_splat(elems))
7
+ end
8
+
9
+ def process_aref exp
10
+ _, item, idx = exp.shift 3
11
+ s(:call, process(item), :[], process(idx))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,80 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Assignment
4
+ def process_assign exp
5
+ _, lvalue, value = exp.shift 3
6
+
7
+ lvalue = process(lvalue)
8
+ value = process(value)
9
+
10
+ create_assignment_sub_type lvalue, value
11
+ end
12
+
13
+ def process_massign exp
14
+ _, left, right = exp.shift 3
15
+
16
+ left = handle_list_with_optional_splat left
17
+
18
+ left.each do |item|
19
+ case item.sexp_type
20
+ when :splat
21
+ item[1][0] = :lasgn
22
+ else
23
+ item[0] = :lasgn
24
+ end
25
+ end
26
+
27
+ right = process(right)
28
+
29
+ unless right.sexp_type == :array
30
+ right = s(:to_ary, right)
31
+ end
32
+
33
+ s(:masgn, s(:array, *left), right)
34
+ end
35
+
36
+ def process_mrhs_new_from_args exp
37
+ _, inner, last = exp.shift 3
38
+ inner.map! {|item| process(item)}
39
+ inner.push process(last)
40
+ s(:array, *inner)
41
+ end
42
+
43
+ def process_mlhs_add_star exp
44
+ generic_add_star exp
45
+ end
46
+
47
+ def process_opassign exp
48
+ _, lvalue, operator, value = exp.shift 4
49
+
50
+ lvalue = process(lvalue)
51
+ value = process(value)
52
+ operator = operator[1].gsub(/=/, '').to_sym
53
+ operator_call = s(:call, lvalue, operator, s(:arglist, value))
54
+
55
+ case lvalue.sexp_type
56
+ when :ivar
57
+ s(:iasgn, lvalue[1], operator_call)
58
+ when :aref_field
59
+ s(:op_asgn1, lvalue[1], s(:arglist, lvalue[2][1]), operator, value)
60
+ else
61
+ s(:lasgn, lvalue[1], operator_call)
62
+ end
63
+ end
64
+
65
+ def create_assignment_sub_type lvalue, value
66
+ case lvalue.sexp_type
67
+ when :ivar
68
+ s(:iasgn, lvalue[1], value)
69
+ when :aref_field
70
+ s(:attrasgn, lvalue[1], :[]=, s(:arglist, lvalue[2][1], value))
71
+ when :const
72
+ s(:cdecl, lvalue[1], value)
73
+ else
74
+ s(:lasgn, lvalue[1], value)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
@@ -0,0 +1,49 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Blocks
4
+ def process_method_add_block exp
5
+ _, call, block = exp.shift 3
6
+ block = process(block)
7
+ args = convert_block_args(block[1])
8
+ stmt = block[2].first
9
+ s(:iter, process(call), args, stmt)
10
+ end
11
+
12
+ def process_brace_block exp
13
+ handle_generic_block exp
14
+ end
15
+
16
+ def process_do_block exp
17
+ handle_generic_block exp
18
+ end
19
+
20
+ def process_params exp
21
+ _, normal, defaults, *_ = exp.shift 6
22
+
23
+ args = [*normal].map do |id|
24
+ identifier_node_to_symbol id
25
+ end
26
+
27
+ assigns = [*defaults].map do |pair|
28
+ sym = identifier_node_to_symbol pair[0]
29
+ val = process pair[1]
30
+ s(:lasgn, sym, val)
31
+ end
32
+
33
+ if assigns.length > 0
34
+ args += assigns.map {|lasgn| lasgn[1]}
35
+ args << s(:block, *assigns)
36
+ end
37
+
38
+ s(:args, *args)
39
+ end
40
+
41
+ private
42
+
43
+ def handle_generic_block exp
44
+ _, args, stmts = exp.shift 3
45
+ s(:block, process(args), s(handle_statement_list(stmts)))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Conditionals
4
+ def process_if exp
5
+ _, cond, truepart, falsepart = exp.shift 4
6
+ s(:if,
7
+ process(cond),
8
+ handle_statement_list(truepart),
9
+ process(falsepart))
10
+ end
11
+
12
+ def process_elsif exp
13
+ process_if exp
14
+ end
15
+
16
+ def process_if_mod exp
17
+ _, cond, truepart = exp.shift 3
18
+ s(:if, process(cond), process(truepart), nil)
19
+ end
20
+
21
+ def process_unless_mod exp
22
+ _, cond, truepart = exp.shift 3
23
+ s(:if, process(cond), nil, process(truepart))
24
+ end
25
+
26
+ def process_unless exp
27
+ _, cond, truepart, falsepart = exp.shift 4
28
+ s(:if,
29
+ process(cond),
30
+ process(falsepart),
31
+ handle_statement_list(truepart))
32
+ end
33
+
34
+ def process_case exp
35
+ _, expr, clauses = exp.shift 3
36
+ s(:case, process(expr), *process(clauses))
37
+ end
38
+
39
+ def process_when exp
40
+ _, values, truepart, falsepart = exp.shift 4
41
+
42
+ if falsepart.nil?
43
+ falsepart = [nil]
44
+ else
45
+ falsepart = process(falsepart)
46
+ if falsepart.first.is_a? Symbol
47
+ falsepart = s(falsepart)
48
+ end
49
+ end
50
+
51
+ s(s(:when,
52
+ process(s(:array, values)),
53
+ handle_statement_list(truepart)),
54
+ *falsepart)
55
+ end
56
+
57
+ def process_else exp
58
+ _, body = exp.shift 2
59
+ handle_statement_list body
60
+ end
61
+ end
62
+ end
63
+ end
64
+
@@ -0,0 +1,27 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Hashes
4
+ def process_hash exp
5
+ _, elems = exp.shift 2
6
+ s(:hash, *process(elems))
7
+ end
8
+
9
+ def process_assoclist_from_args exp
10
+ _, elems = exp.shift 2
11
+ result = s()
12
+ elems.each {|sub_exp|
13
+ process(sub_exp).each {|elm|
14
+ result << elm
15
+ }
16
+ }
17
+ result
18
+ end
19
+
20
+ def process_assoc_new exp
21
+ _, left, right = exp.shift 3
22
+ s(process(left), process(right))
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,49 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module HelperMethods
4
+ def handle_list_with_optional_splat exp
5
+ if exp.nil?
6
+ []
7
+ elsif exp.first.is_a? Symbol
8
+ process(exp)
9
+ else
10
+ exp.map { |sub_exp| process(sub_exp) }
11
+ end
12
+ end
13
+
14
+ def convert_block_args(args)
15
+ args && s(:lasgn, args[1][1])
16
+ end
17
+
18
+ def handle_statement_list exp
19
+ statements = exp.
20
+ map { |sub_exp| process(sub_exp) }.
21
+ reject { |sub_exp| sub_exp.sexp_type == :void_stmt }
22
+
23
+ if statements.length == 1
24
+ statements.first
25
+ else
26
+ s(:block, *statements)
27
+ end
28
+ end
29
+
30
+ def identifier_node_to_symbol exp
31
+ assert_type exp, :@ident
32
+ _, ident, _ = exp.shift 3
33
+
34
+ ident.to_sym
35
+ end
36
+
37
+ def generic_add_star exp
38
+ _, args, splatarg = exp.shift 3
39
+ items = args.map { |sub| process(sub) }
40
+ items << s(:splat, process(splatarg))
41
+ s(*items)
42
+ end
43
+
44
+ def is_literal? exp
45
+ exp.sexp_type == :lit
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,83 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Literals
4
+ def process_string_literal exp
5
+ _, content = exp.shift 2
6
+ process(content)
7
+ end
8
+
9
+ def process_string_content exp
10
+ _, inner = exp.shift 2
11
+
12
+ string = extract_inner_string(inner)
13
+ rest = []
14
+
15
+ if string.sexp_type == :str
16
+ string = string[1]
17
+ else
18
+ rest << string
19
+ string = ""
20
+ end
21
+
22
+ string = unescape(string)
23
+
24
+ until exp.empty? do
25
+ result = process(exp.shift)
26
+ if result.sexp_type == :str
27
+ result[1] = unescape(result[1])
28
+ end
29
+ rest << result
30
+ end
31
+
32
+ if rest.empty?
33
+ s(:str, string)
34
+ else
35
+ s(:dstr, string, *rest)
36
+ end
37
+ end
38
+
39
+ def process_string_embexpr exp
40
+ _, list = exp.shift 2
41
+ s(:evstr, process(list.first))
42
+ end
43
+
44
+ def process_regexp_literal exp
45
+ _, content, _ = exp.shift 3
46
+
47
+ string = extract_inner_string content[0]
48
+
49
+ s(:lit, Regexp.new(string[1]))
50
+ end
51
+
52
+ def process_symbol_literal exp
53
+ _, symbol = exp.shift 2
54
+ sym = symbol[1]
55
+ s(:lit, extract_node_symbol(sym))
56
+ end
57
+
58
+ def process_dyna_symbol exp
59
+ _, list = exp.shift 2
60
+
61
+ string = process list[0]
62
+ s(:lit, string[1].to_sym)
63
+ end
64
+
65
+ def process_at_tstring_content exp
66
+ _, string, _ = exp.shift 3
67
+ s(:str, string)
68
+ end
69
+
70
+ private
71
+
72
+ def extract_inner_string exp
73
+ process(exp) || s(:str, "")
74
+ end
75
+
76
+ def unescape string
77
+ string.gsub /(\\[n\\"])/ do
78
+ eval "\"#{$1}\""
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,11 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Loops
4
+ def process_until exp
5
+ _, cond, block = exp.shift 3
6
+ s(:until, process(cond), handle_statement_list(block), true)
7
+ end
8
+ end
9
+ end
10
+ end
11
+
@@ -0,0 +1,47 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module MethodCalls
4
+ def process_method_add_arg exp
5
+ _, call, parens = exp.shift 3
6
+ call = process call
7
+ s(:call, call[1], call[2], process(parens))
8
+ end
9
+
10
+ def process_call exp
11
+ _, receiver, _, method = exp.shift 4
12
+ s(:call, process(receiver), identifier_node_to_symbol(method), s(:arglist))
13
+ end
14
+
15
+ def process_command exp
16
+ _, ident, arglist = exp.shift 3
17
+
18
+ ident = identifier_node_to_symbol ident
19
+ arglist = process arglist
20
+
21
+ s(:call, nil, ident, arglist)
22
+ end
23
+
24
+ def process_command_call exp
25
+ _, receiver, _, method, arguments = exp.shift 5
26
+ s(:call,
27
+ process(receiver),
28
+ identifier_node_to_symbol(method),
29
+ process(arguments))
30
+ end
31
+
32
+ def process_vcall exp
33
+ _, ident = exp.shift 3
34
+
35
+ ident = identifier_node_to_symbol ident
36
+
37
+ s(:call, nil, ident, s(:arglist))
38
+ end
39
+
40
+ def process_fcall exp
41
+ _, method = exp.shift 2
42
+ s(:call, nil, identifier_node_to_symbol(method), s(:arglist))
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,38 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Methods
4
+ def process_def exp
5
+ _, ident, params, body = exp.shift 4
6
+ ident = identifier_node_to_symbol ident
7
+ s(:defn, ident, process(params), method_body(body))
8
+ end
9
+
10
+ def process_defs exp
11
+ _, receiver, _, method, args, body = exp.shift 6
12
+ s(:defs, process(receiver),
13
+ identifier_node_to_symbol(method),
14
+ process(args), process(body))
15
+ end
16
+
17
+ def process_return exp
18
+ _, arglist = exp.shift 2
19
+ arglist = process(arglist)
20
+ s(:return, arglist[1])
21
+ end
22
+
23
+ def process_return0 exp
24
+ _ = exp.shift
25
+ s(:return)
26
+ end
27
+
28
+ def method_body exp
29
+ scope = process exp
30
+ block = scope[1]
31
+ if block.length == 1
32
+ block.push s(:nil)
33
+ end
34
+ scope
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ module RipperRubyParser
2
+ module SexpHandlers
3
+ module Operators
4
+ OPERATOR_MAP = {
5
+ "&&".to_sym => :and,
6
+ "||".to_sym => :or
7
+ }
8
+
9
+ def process_binary exp
10
+ _, left, op, right = exp.shift 4
11
+ mapped = OPERATOR_MAP[op]
12
+ if mapped
13
+ s(mapped, process(left), process(right))
14
+ else
15
+ s(:call, process(left), op, s(:arglist, process(right)))
16
+ end
17
+ end
18
+
19
+ def process_unary exp
20
+ _, _, arg = exp.shift 3
21
+ arg = process(arg)
22
+ if is_literal? arg
23
+ s(:lit, -arg[1])
24
+ else
25
+ s(:call, arg, :-@, s(:arglist))
26
+ end
27
+ end
28
+
29
+ def process_dot2 exp
30
+ _, left, right = exp.shift 3
31
+ left = process(left)
32
+ right = process(right)
33
+ if is_literal?(left) && is_literal?(right)
34
+ s(:lit, Range.new(left[1], right[1]))
35
+ else
36
+ s(:dot2, left, right)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end