ripper_ruby_parser 0.0.1

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