to_source 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rbx
6
+ *.rbc
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use rbx-head@to_source --create
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ rvm:
2
+ - rbx-18mode
3
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in to_source.gemspec
4
+ gemspec
5
+ gem 'rake'
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ task :default => :test
data/Readme.md ADDED
@@ -0,0 +1,31 @@
1
+ # to_source [![Build Status](https://secure.travis-ci.org/txus/to_source.png)](http://travis-ci.org/txus/to_source)
2
+
3
+ to_source is a little Rubinius gem that enables Abstract Syntax Tree nodes to
4
+ transform themselves into source code. It's the reverse of Rubinius' builtin
5
+ `#to_ast` method. See for yourself:
6
+
7
+ #!/bin/rbx
8
+ some_code = "a = 123"
9
+ ast = some_code.to_ast
10
+ # => #<Rubinius::AST::LocalVariableAssignment:0x21b8
11
+ @value=#<Rubinius::AST::FixnumLiteral:0x21bc @value=123 @line=1>
12
+ @variable=nil @line=1 @name=:a>
13
+
14
+ ast.to_source
15
+ # => "a = 123"
16
+
17
+ ## Installing
18
+
19
+ to_source needs Rubinius 2.0 to run, in either 1.8 or 1.9 mode.
20
+
21
+ Put this in your Gemfile:
22
+
23
+ gem 'to_source'
24
+
25
+ And just call `#to_source` in any AST node!
26
+
27
+ ## Who's this
28
+
29
+ This was made by [Josep M. Bach (Txus)](http://txustice.me) under the MIT
30
+ license. I'm [@txustice](http://twitter.com/txustice) on twitter (where you
31
+ should probably follow me!).
@@ -0,0 +1,19 @@
1
+ module Rubinius
2
+ module AST
3
+ class Node
4
+ # Public: Works like #visit, but it doesn't visit the children just yet;
5
+ # instead, lets the visitor decide when and how to do it.
6
+ #
7
+ # visitor - The visitor object. It must respond to methods named after the
8
+ # node names.
9
+ #
10
+ # Returns nothing.
11
+ def lazy_visit(visitor, parent=nil, indent=false)
12
+ args = [self.node_name, self, parent]
13
+ args.push true if indent
14
+
15
+ visitor.__send__ *args
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module ToSource
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,237 @@
1
+ module ToSource
2
+ class Visitor
3
+ def initialize
4
+ @output = []
5
+ @indentation = 0
6
+ end
7
+
8
+ def emit(code)
9
+ @output.push code
10
+ end
11
+
12
+ def output
13
+ @output.join
14
+ end
15
+
16
+ def current_indentation
17
+ ' ' * @indentation
18
+ end
19
+
20
+ def local_variable_assignment(node, parent)
21
+ emit "%s = " % node.name
22
+ node.value.lazy_visit self, node
23
+ end
24
+
25
+ def local_variable_access(node, parent)
26
+ emit node.name
27
+ end
28
+
29
+ def fixnum_literal(node, parent)
30
+ emit node.value.to_s
31
+ end
32
+
33
+ def float_literal(node, parent)
34
+ emit node.value.to_s
35
+ end
36
+
37
+ def string_literal(node, parent)
38
+ emit '"' << node.string.to_s << '"'
39
+ end
40
+
41
+ def symbol_literal(node, parent)
42
+ emit ':' << node.value.to_s
43
+ end
44
+
45
+ def true_literal(node, parent)
46
+ emit 'true'
47
+ end
48
+
49
+ def false_literal(node, parent)
50
+ emit 'false'
51
+ end
52
+
53
+ def nil_literal(node, parent)
54
+ emit 'nil'
55
+ end
56
+
57
+ def array_literal(node, parent)
58
+ body = node.body
59
+
60
+ emit '['
61
+ body.each_with_index do |node, index|
62
+ node.lazy_visit self, node
63
+ emit ', ' unless body.length == index + 1 # last element
64
+ end
65
+ emit ']'
66
+ end
67
+
68
+ def hash_literal(node, parent)
69
+ body = node.array.each_slice(2)
70
+
71
+ emit '{'
72
+ body.each_with_index do |slice, index|
73
+ key, value = slice
74
+
75
+ key.lazy_visit self, node
76
+ emit " => "
77
+ value.lazy_visit self, node
78
+
79
+ emit ', ' unless body.to_a.length == index + 1 # last element
80
+ end
81
+ emit '}'
82
+ end
83
+
84
+ def range(node, parent)
85
+ node.start.lazy_visit self, node
86
+ emit '..'
87
+ node.finish.lazy_visit self, node
88
+ end
89
+
90
+ def regex_literal(node, parent)
91
+ emit '/'
92
+ emit node.source
93
+ emit '/'
94
+ end
95
+
96
+ def send(node, parent)
97
+ unless node.receiver.is_a?(Rubinius::AST::Self)
98
+ node.receiver.lazy_visit self, node
99
+ emit '.'
100
+ end
101
+ emit node.name
102
+
103
+ if node.block
104
+ emit ' '
105
+ node.block.lazy_visit self, node if node.block
106
+ end
107
+ end
108
+
109
+ def send_with_arguments(node, parent)
110
+ return if process_binary_operator(node, parent) # 1 * 2, a / 3, true && false
111
+
112
+ unless node.receiver.is_a?(Rubinius::AST::Self)
113
+ node.receiver.lazy_visit self, node
114
+ emit '.'
115
+ end
116
+
117
+ emit node.name
118
+ emit '('
119
+ node.arguments.lazy_visit self, node
120
+ emit ')'
121
+ if node.block
122
+ emit ' '
123
+ node.block.lazy_visit self, node if node.block
124
+ end
125
+ end
126
+
127
+ def actual_arguments(node, parent)
128
+ body = node.array
129
+ body.each_with_index do |argument, index|
130
+ argument.lazy_visit self, parent
131
+ emit ', ' unless body.length == index + 1 # last element
132
+ end
133
+ end
134
+
135
+ def iter_arguments(node, parent)
136
+ body = if node.prelude == :single
137
+ Array(node.arguments.name)
138
+ else
139
+ node.arguments.left.body.map(&:name)
140
+ end
141
+
142
+ emit '|'
143
+ body.each_with_index do |argument, index|
144
+ emit argument.to_s
145
+ emit ', ' unless body.length == index + 1 # last element
146
+ end
147
+ emit '|'
148
+ end
149
+
150
+ def iter(node, parent)
151
+ emit 'do'
152
+
153
+ if node.arguments && node.arguments.arity != -1
154
+ emit ' '
155
+ node.arguments.lazy_visit self, parent
156
+ end
157
+
158
+ emit "\n"
159
+ @indentation += 1
160
+
161
+ if node.body.is_a?(Rubinius::AST::Block)
162
+ node.body.lazy_visit self, parent, true
163
+ else
164
+ emit current_indentation
165
+ node.body.lazy_visit self, parent
166
+ end
167
+
168
+ emit "\n"
169
+ emit 'end'
170
+ end
171
+
172
+ def block(node, parent, indent=false)
173
+ body = node.array
174
+ body.each_with_index do |expression, index|
175
+ emit current_indentation if indent
176
+ expression.lazy_visit self, parent
177
+ emit "\n" unless body.length == index + 1 # last element
178
+ end
179
+ end
180
+
181
+ def not(node, parent)
182
+ emit '!'
183
+ node.value.lazy_visit self, parent
184
+ end
185
+
186
+ def and(node, parent)
187
+ node.left.lazy_visit self, node
188
+ emit ' && '
189
+ node.right.lazy_visit self, node
190
+ end
191
+
192
+ def or(node, parent)
193
+ node.left.lazy_visit self, node
194
+ emit ' || '
195
+ node.right.lazy_visit self, node
196
+ end
197
+
198
+ def op_assign_and(node, parent)
199
+ node.left.lazy_visit self, node
200
+ emit ' && '
201
+ node.right.lazy_visit self, node
202
+ end
203
+
204
+ def op_assign_or(node, parent)
205
+ node.left.lazy_visit self, node
206
+ emit ' || '
207
+ node.right.lazy_visit self, node
208
+ end
209
+
210
+ def constant_access(node, parent)
211
+ emit node.name
212
+ end
213
+
214
+ def scoped_constant(node, parent)
215
+ node.parent.lazy_visit self, node
216
+ emit '::'
217
+ emit node.name
218
+ end
219
+
220
+ private
221
+
222
+ def process_binary_operator(node, parent)
223
+ operators = %w(+ - * / & | <<).map(&:to_sym)
224
+ return false unless operators.include?(node.name)
225
+ return false if node.arguments.array.length != 1
226
+
227
+ operand = node.arguments.array[0]
228
+
229
+ unless node.receiver.is_a?(Rubinius::AST::Self)
230
+ node.receiver.lazy_visit self, node
231
+ end
232
+
233
+ emit ' ' << node.name.to_s << ' '
234
+ operand.lazy_visit self, node
235
+ end
236
+ end
237
+ end
data/lib/to_source.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "to_source/version"
2
+ require "to_source/core_ext/node"
3
+ require "to_source/visitor"
4
+
5
+ module ToSource
6
+ # Public: Converts the node back to its original source code.
7
+ #
8
+ # Returns the String output.
9
+ def to_source
10
+ visitor = Visitor.new
11
+ lazy_visit(visitor)
12
+ visitor.output
13
+ end
14
+ end
15
+
16
+ Rubinius::AST::Node.send :include, ToSource
@@ -0,0 +1,126 @@
1
+ require 'test/unit'
2
+ require 'to_source'
3
+
4
+ module ToSource
5
+ class VisitorTest < Test::Unit::TestCase
6
+ def visit(code)
7
+ code.to_ast.to_source
8
+ end
9
+
10
+ def assert_source(code)
11
+ assert_equal code, visit(code)
12
+ end
13
+
14
+ def assert_converts(expected, code)
15
+ assert_equal expected, visit(code)
16
+ end
17
+
18
+ def test_local_assignment
19
+ assert_source "foo = 1"
20
+ end
21
+
22
+ def test_local_access
23
+ assert_source "foo = 1\nfoo"
24
+ end
25
+
26
+ def test_constant_access
27
+ assert_source "Rubinius"
28
+ end
29
+
30
+ def test_scoped_constant_access
31
+ assert_source "Rubinius::Debugger"
32
+ end
33
+
34
+ def test_fixnum_literal
35
+ assert_source "1"
36
+ end
37
+
38
+ def test_float_literal
39
+ assert_source "1.0"
40
+ end
41
+
42
+ def test_string_literal
43
+ assert_source '"foo"'
44
+ end
45
+
46
+ def test_symbol_literal
47
+ assert_source ':foo'
48
+ end
49
+
50
+ def test_true_literal
51
+ assert_source 'true'
52
+ end
53
+
54
+ def test_false_literal
55
+ assert_source 'false'
56
+ end
57
+
58
+ def test_nil_literal
59
+ assert_source 'nil'
60
+ end
61
+
62
+ def test_array_literal
63
+ assert_source '[1, 2, 3]'
64
+ end
65
+
66
+ def test_hash_literal
67
+ assert_source '{:answer => 42, :bar => :baz}'
68
+ end
69
+
70
+ def test_range
71
+ assert_source '20..34'
72
+ end
73
+
74
+ def test_regex
75
+ assert_source '/.*/'
76
+ end
77
+
78
+ def test_send
79
+ assert_source 'foo.bar'
80
+ end
81
+
82
+ def test_send_with_arguments
83
+ assert_converts 'foo.bar(:baz, :yeah)', 'foo.bar :baz, :yeah'
84
+ end
85
+
86
+ def test_send_with_arguments_and_empty_block
87
+ assert_converts "foo.bar(:baz, :yeah) do\n nil\nend", "foo.bar(:baz, :yeah) do\nend"
88
+ end
89
+
90
+ def test_send_with_arguments_and_block_with_one_argument
91
+ assert_source "foo.bar(:baz, :yeah) do |a|\n 3\n 4\nend"
92
+ end
93
+
94
+ def test_send_with_arguments_and_block_with_arguments
95
+ assert_source "foo.bar(:baz, :yeah) do |a, b|\n 3\n 4\nend"
96
+ end
97
+
98
+ def test_lambda
99
+ assert_source "lambda do |a, b|\n a\nend"
100
+ end
101
+
102
+ def test_proc
103
+ assert_source "Proc.new do\n a\nend"
104
+ end
105
+
106
+ def test_binary_operators
107
+ %w(+ - * / & | && || <<).each do |operator|
108
+ assert_source "1 #{operator} 2"
109
+ end
110
+ end
111
+
112
+ def test_expands_binary_equal
113
+ assert_converts "a = a + 2", "a += 2"
114
+ assert_converts "a = a - 2", "a -= 2"
115
+ assert_converts "a = a * 2", "a *= 2"
116
+ assert_converts "a = a / 2", "a /= 2"
117
+ assert_converts "a && a = 2", "a &&= 2"
118
+ assert_converts "a || a = 2", "a ||= 2"
119
+ end
120
+
121
+ def test_unary_operators
122
+ assert_source "!1"
123
+ assert_source "!!1"
124
+ end
125
+ end
126
+ end
data/to_source.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "to_source/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "to_source"
7
+ s.version = ToSource::VERSION
8
+ s.authors = ["Josep M. Bach"]
9
+ s.email = ["josep.m.bach@gmail.com"]
10
+ s.homepage = "http://github.com/txus/to_source"
11
+ s.summary = %q{Transform your Rubinius AST nodes back to source code. Reverse parsing!}
12
+ s.description = %q{Transform your Rubinius AST nodes back to source code. Reverse parsing!}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: to_source
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Josep M. Bach
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Transform your Rubinius AST nodes back to source code. Reverse parsing!
15
+ email:
16
+ - josep.m.bach@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - .rvmrc
23
+ - .travis.yml
24
+ - Gemfile
25
+ - Rakefile
26
+ - Readme.md
27
+ - lib/to_source.rb
28
+ - lib/to_source/core_ext/node.rb
29
+ - lib/to_source/version.rb
30
+ - lib/to_source/visitor.rb
31
+ - test/to_source/visitor_test.rb
32
+ - to_source.gemspec
33
+ homepage: http://github.com/txus/to_source
34
+ licenses: []
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubyforge_project:
53
+ rubygems_version: 1.8.12
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: Transform your Rubinius AST nodes back to source code. Reverse parsing!
57
+ test_files:
58
+ - test/to_source/visitor_test.rb