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