endlessruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ module EndlessRuby::Main
5
+
6
+ extend self
7
+ extend EndlessRuby
8
+ include EndlessRuby
9
+
10
+ # er ファイルから読み込みそれをピュアなRubyにコンパイルしてrbに書き出します
11
+ def compile er, rb
12
+ open(er) do |erfile|
13
+ open(rb, "w") do |rbfile|
14
+ rbfile.write ER2PR(erfile.read)
15
+
16
+ # rbファイルを読み込みそれからすべてのendを取り除きます。
17
+ def decompile rb, er
18
+ open(rb) do |rbfile|
19
+ open(er, "w") do |erfile|
20
+ erfile.write PR2ER(rbfile.read)
21
+
22
+
23
+ # EndlessRuby::Main.main と同じ動作をします。このモジュールをincludeした場合に使用します。
24
+ def endlessruby argv
25
+ EndlessRuby::Main.main argv
26
+
27
+ # $ endlessruby.rb args とまったく同じ動作をします。argvはARGVと同じ形式でなければなりません。
28
+ def self.main argv
29
+
30
+ if argv.first && File.exist?(argv.first)
31
+ $PROGRAM_NAME = argv.shift
32
+ open $PROGRAM_NAME do |file|
33
+ EndlessRuby.ereval file.read, TOPLEVEL_BINDING, $PROGRAM_NAME
34
+ return true
35
+
36
+ require 'optparse'
37
+
38
+ options = {
39
+ }
40
+
41
+ parser = OptionParser.new do |opts|
42
+ opts.on '-o OUT' do |out|
43
+ options[:out] = out
44
+
45
+ opts.on '-c', '--compile' do |c|
46
+ options[:compile] = true
47
+
48
+ opts.on '-d', '--decompile' do |d|
49
+ options[:decompile] = true
50
+
51
+ opts.on '-r' do |r|
52
+ options[:recursive] = true
53
+
54
+ parser.parse! argv
55
+
56
+ if options[:compile]
57
+ out = options[:out] || '.'
58
+
59
+ argv.each do |er|
60
+ unless File.exist? er
61
+ puts "no such file to load -- #{er}"
62
+ next
63
+
64
+ if File.directory? er
65
+ unless options[:recursive]
66
+ puts "Is a directory - #{er}"
67
+ next
68
+ # Unimolementation
69
+ next
70
+
71
+ rb = er
72
+ if er =~ /^(.*)\.er$/
73
+ rb = $1
74
+ rb = File.split(rb)[1]
75
+ rb = File.join(out, "#{rb}.rb")
76
+ compile er, rb
77
+ elsif options[:decompile]
78
+ out = options[:out] || '.'
79
+
80
+ argv.each do |rb|
81
+ unless File.exist? rb
82
+ puts "no such file to load -- #{rb}"
83
+ next
84
+
85
+ if File.directory? rb
86
+ unless options[:recursive]
87
+ puts "Is a directory - #{rb}"
88
+ next
89
+ # Unimolementation
90
+ next
91
+
92
+ er = rb
93
+ if rb =~ /^(.*)\.rb$/
94
+ er = $1
95
+ er = File.split(er)[1]
96
+ er = File.join(out, "#{er}.er")
97
+ decompile rb, er
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ $:.unshift(File.dirname(__FILE__)) unless
5
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
6
+
7
+ require 'endlessruby/extensions'
8
+
9
+ # EndlessRubyはRubyをendを取り除いて書けます。
10
+ #
11
+ # erファイルをrequire
12
+ # require 'homuhomu.er' | require 'homuhomu'
13
+ #
14
+ # erファイルを実行
15
+ # $ path/to/endlessruby.rb homuhomu.er
16
+ #
17
+ # erファイルをコンパイル
18
+ # $ path/to/endlessruby.rb -c src/homuhomu.er -o lib
19
+ # # => src/homuhomu.er をコンパイルして lib/homuhomu.rb を書き出します。
20
+ # # -o が省略された場合はカレントディレクトリに書き出します。
21
+ #
22
+ # Example:
23
+ # class EndlessRubyWorld
24
+ #
25
+ # def self.hello!
26
+ # puts "hello!"
27
+ #
28
+ #
29
+ # [-2, -1, 0, 1, 2].reject do |x|
30
+ # x < 0
31
+ # end.each do |n|
32
+ # puts n
33
+ #
34
+ module EndlessRuby
35
+
36
+ # EndlessRuby のバージョンです
37
+ VERSION = "0.0.1"
38
+
39
+ extend self
40
+
41
+ private
42
+
43
+ # 内部で使用します。空業かどうか判定します。
44
+ def blank_line? line
45
+ return true unless line
46
+ (line.chomp.gsub /\s+?/, '') == ""
47
+
48
+ # 内部で使用します。インデントを取り除きます
49
+ def unindent line
50
+ line =~ /^\s*?(\S.*?)$/
51
+ $1
52
+ # 内部で使用します。インデントします
53
+ def indent line, level, indent=" "
54
+ "#{indent * level}#{line}"
55
+
56
+ # 内部で使用します。インデントの数を数えます。
57
+ def indent_count line, indent=" "
58
+ return 0 unless line
59
+ if line =~ /^#{indent}(.*?)$/
60
+ 1 + (indent_count $1, indent)
61
+ else
62
+ 0
63
+
64
+ # 内部で使用します。ブロックを作るキーワード群です。
65
+ BLOCK_KEYWORDS = [
66
+ [/^if(:?\s|\().*?$/, /^elsif(:?\s|\().*?$/, /^else(?:$|\s+)/],
67
+ [/^unless(:?\s|\().*?$/, /^elsif(:?\s|\().*?$/, /^else(?:$|\s+)/],
68
+ [/^while(:?\s|\().*?$/],
69
+ [/^until(:?\s|\().*?$/],
70
+ [/^case(:?\s|\().*?$/, /^when(:?\s|\().*?$/, /^else(?:$|\s+)/],
71
+ [/^def\s.*?$/, /^rescue(:?$|\s|\().*?$/, /^else(?:$|\s+)/, /^ensure(?:$|\s+)/],
72
+ [/^class\s.*?$/],
73
+ [/^module\s.*?$/],
74
+ [/^begin(?:$|\s+)/, /^rescue(:?$|\s|\().*?$/, /^else(?:$|\s+)/, /^ensure(?:$|\s+)/],
75
+ [/^.*?\s+do(:?$|\s|\|)/]
76
+ ]
77
+
78
+ public
79
+
80
+ # 文字列をEndlessRubyの構文として実行します。引数の意味はKernel#evalと同じです
81
+ def ereval(src, binding=TOPLEVEL_BINDING, filename=__FILE__, lineno=1)
82
+ at = caller
83
+ eval(ER2PR(src), binding, filename, lineno)
84
+ rescue Exception => e
85
+ $@ = at
86
+ raise e
87
+
88
+ # Rubyの構文をEndlessRubyの構文に変換します。
89
+ def pure_ruby_to_endless_ruby src
90
+ @decompile = true
91
+ s = ER2RB(src)
92
+ @decompile = nil
93
+ s
94
+
95
+
96
+ alias RB2ER pure_ruby_to_endless_ruby
97
+ alias PR2ER pure_ruby_to_endless_ruby
98
+
99
+ # EndlessRubyの構文をピュアなRubyの構文に変換します。
100
+ def endless_ruby_to_pure_ruby src
101
+ endless = src.split "\n"
102
+
103
+ pure = []
104
+ i = 0
105
+ while i < endless.length
106
+ pure << (currently_line = endless[i])
107
+
108
+ if currently_line =~ /(.*)(?:^|(?:(?!\\).))\#(?!\{).*$/
109
+ currently_line = $1
110
+
111
+ if blank_line? currently_line
112
+ i += 1
113
+ next
114
+
115
+ # ブロックを作らない構文なら単に無視する
116
+ next i += 1 unless BLOCK_KEYWORDS.any? { |k| k[0] =~ unindent(currently_line) }
117
+
118
+ # ブロックに入る
119
+ keyword = BLOCK_KEYWORDS.each { |k| break k if k[0] =~ unindent(currently_line) }
120
+
121
+ currently_indent_depth = indent_count currently_line
122
+ base_indent_depth = currently_indent_depth
123
+
124
+ inner_statements = []
125
+ # def method1
126
+ # statemetns
127
+ # # document of method2
128
+ # def method2
129
+ # statements
130
+ # のような場合にコメントの部分はmethod1内に含まないようにする。
131
+ # def method1
132
+ # statemetns
133
+ # # comment
134
+ # return
135
+ # のような場合と区別するため。
136
+ comment_count = 0
137
+ in_here_document = nil
138
+ while i < endless.length
139
+
140
+ inner_currently_line = endless[i + 1]
141
+
142
+ if inner_currently_line =~ /(.*)(?:^|(?:(?!\\).))\#(?!\{).*$/
143
+ if blank_line?($1) && currently_indent_depth >= indent_count(inner_currently_line)
144
+ comment_count += 1
145
+ inner_currently_line = $1
146
+ elsif blank_line? inner_currently_line
147
+ comment_count += 1
148
+
149
+ if blank_line? inner_currently_line
150
+ inner_statements << endless[i + 1]
151
+ i += 1
152
+ next
153
+
154
+ just_after_indent_depth = indent_count inner_currently_line
155
+
156
+ # 次の行がendならば意図のあるものなのでendを持ちあ揚げない
157
+ if inner_currently_line =~ /^\s*end(?!\w).*$/
158
+ comment_count = 0
159
+
160
+ if base_indent_depth < just_after_indent_depth
161
+ comment_count = 0
162
+
163
+ if in_here_document
164
+ if (in_here_document[0] == '' && inner_currently_line =~ /^#{in_here_document[1]}\s*$/) || # <<DEFINE case
165
+ (in_here_document[0] == '-' && inner_currently_line =~ /^\s*#{in_here_document[1]}\s*$/) # <<-DEFINE case
166
+ in_here_document = nil
167
+ inner_statements << endless[i + 1]
168
+ i += 1
169
+ next
170
+ else
171
+ inner_statements << endless[i + 1]
172
+ i += 1
173
+ next
174
+
175
+ if inner_currently_line =~ /^.*?\<\<(\-?)(\w+)(?!\w).*$/
176
+ in_here_document = [$1, $2]
177
+
178
+ if base_indent_depth > indent_count(inner_currently_line)
179
+ break
180
+
181
+ if base_indent_depth == indent_count(inner_currently_line)
182
+ unless keyword[1..-1].any? { |k| k =~ unindent(inner_currently_line) }
183
+ break
184
+
185
+ inner_statements << endless[i + 1]
186
+ i += 1
187
+
188
+ # endをコメントより上の行へ持ち上げる
189
+ if 0 < comment_count
190
+ comment_count.times do
191
+ inner_statements.pop
192
+ i -= comment_count
193
+
194
+ pure += ER2PR(inner_statements.join("\n")).split "\n"
195
+ # 次の行がendならばendを補完しない(ワンライナーのため)
196
+ unless @decompile
197
+ unless endless[i + 1] && endless[i + 1] =~ /^\s*end(?!\w).*$/
198
+ pure << "#{' '*currently_indent_depth}end"
199
+ else
200
+ # メソッドチェインは削除しない
201
+ if endless[i + 1] && endless[i + 1] =~ /^\s*end(?:\s|$)\s*$/
202
+ i += 1
203
+
204
+ i += 1
205
+ pure.join "\n"
206
+
207
+ alias to_pure_ruby endless_ruby_to_pure_ruby
208
+ alias ER2PR endless_ruby_to_pure_ruby
209
+ alias ER2RB endless_ruby_to_pure_ruby
210
+
211
+ if __FILE__ == $PROGRAM_NAME
212
+ require 'endlessruby/main'
213
+ EndlessRuby::Main.main ARGV
data/src/er.er ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "endlessruby"
4
+ require "endlessruby/main"
5
+ EndlessRuby::Main.main ARGV
data/tasks/rspec.rake ADDED
@@ -0,0 +1,11 @@
1
+ begin
2
+ require 'rspec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'rspec'
6
+ end
7
+
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.rspec_opts = ['--options', "spec/spec.opts"]
10
+ t.pattern = 'spec/*_spec.rb'
11
+ end
@@ -0,0 +1,12 @@
1
+ class TestEndlessRuby
2
+
3
+ def test_blank_line
4
+ assert_equal true, (blank_line? " ")
5
+ end
6
+
7
+ def test_not_blank_line
8
+ assert_equal false, (blank_line? " pass")
9
+ end
10
+
11
+
12
+ end
@@ -0,0 +1,31 @@
1
+ class TestEndlessRuby
2
+
3
+ def test_require_without_er
4
+ require "#{ROOT}/test/homu"
5
+ assert_equal "hello", Homu.hello
6
+ end
7
+
8
+ def test_require
9
+ require "#{ROOT}/test/foo.er"
10
+ assert_equal "hello", Foo.hello
11
+ end
12
+
13
+ def test_load_path
14
+ $LOAD_PATH << "#{ROOT}/test"
15
+ require "bar.er"
16
+ assert_equal "hello", Bar.hello
17
+ end
18
+
19
+ def test_is_a_dir
20
+ assert_raise(LoadError) {
21
+ require "#{ROOT}/test/is_a_dir"
22
+ }
23
+ end
24
+
25
+ def test_no_such_file
26
+ assert_raise(LoadError) {
27
+ require "#{ROOT}/test/no_such_file"
28
+ }
29
+ end
30
+
31
+ end
@@ -0,0 +1,173 @@
1
+ class TestEndlessRuby
2
+
3
+ def test_1
4
+ er2rb_right_output?(<<DEFINE.chomp!,
5
+ def test
6
+ test
7
+ end
8
+ DEFINE
9
+ <<DEFINE)
10
+ def test
11
+ test
12
+ DEFINE
13
+ end
14
+
15
+ def test_2
16
+ er2rb_right_output?(<<DEFINE.chomp!,
17
+ class Test
18
+ def test
19
+ proc do |args|
20
+ pass
21
+ end
22
+ proc do |args|
23
+ pass
24
+ end
25
+ end
26
+ end
27
+ DEFINE
28
+ <<DEFINE)
29
+ class Test
30
+ def test
31
+ proc do |args|
32
+ pass
33
+ proc do |args|
34
+ pass
35
+ DEFINE
36
+ end
37
+
38
+ def test_3
39
+ er2rb_right_output?(<<DEFINE.chomp!,
40
+ def test
41
+ if false
42
+ pass
43
+ elsif true
44
+ pass
45
+ else
46
+ pass
47
+ end
48
+ end
49
+ DEFINE
50
+ <<DEFINE)
51
+ def test
52
+ if false
53
+ pass
54
+ elsif true
55
+ pass
56
+ else
57
+ pass
58
+ DEFINE
59
+ end
60
+
61
+ def test_nest_keywords
62
+ er2rb_right_output?(<<DEFINE.chomp!,
63
+ def test
64
+ if false
65
+ pass
66
+ elsif true
67
+ if true
68
+ pass
69
+ elsif false
70
+ pass
71
+ else
72
+ pass
73
+ end
74
+ else
75
+ pass
76
+ end
77
+ end
78
+ DEFINE
79
+ <<DEFINE)
80
+ def test
81
+ if false
82
+ pass
83
+ elsif true
84
+ if true
85
+ pass
86
+ elsif false
87
+ pass
88
+ else
89
+ pass
90
+ else
91
+ pass
92
+ DEFINE
93
+ end
94
+
95
+ def test_4
96
+ er2rb_right_output?(<<DEFINE.chomp!,
97
+ def test
98
+ if_nantoka
99
+ end
100
+ DEFINE
101
+ <<DEFINE)
102
+ def test
103
+ if_nantoka
104
+ DEFINE
105
+ end
106
+
107
+
108
+ def test_here_document
109
+ er2rb_right_output?(<<DEFINE.chomp!,
110
+ def method
111
+ src = <<SRC
112
+ hello world
113
+ SRC
114
+ end
115
+ DEFINE
116
+ <<DEFINE)
117
+ def method
118
+ src = <<SRC
119
+ hello world
120
+ SRC
121
+ DEFINE
122
+ end
123
+
124
+ def test_here_document2
125
+ er2rb_right_output?(<<DEFINE.chomp!,
126
+ def method
127
+ src = <<-SRC
128
+ hello world
129
+ SRC
130
+ end
131
+ DEFINE
132
+ <<DEFINE)
133
+ def method
134
+ src = <<-SRC
135
+ hello world
136
+ SRC
137
+ DEFINE
138
+ end
139
+
140
+
141
+ def test_commentout
142
+ er2rb_right_output?(<<DEFINE.chomp!,
143
+ def method
144
+ #TODO:
145
+ # proc do
146
+ # statements
147
+ end
148
+ DEFINE
149
+ <<DEFINE)
150
+ def method
151
+ #TODO:
152
+ # proc do
153
+ # statements
154
+ DEFINE
155
+ end
156
+
157
+ def test_commentout
158
+ er2rb_right_output?(<<DEFINE.chomp!,
159
+ def method
160
+ statements #TODO:
161
+ statements # proc do
162
+ statements # statements
163
+ end
164
+ DEFINE
165
+ <<DEFINE)
166
+ def method
167
+ statements #TODO:
168
+ statements # proc do
169
+ statements # statements
170
+ DEFINE
171
+ end
172
+
173
+ end
@@ -0,0 +1,81 @@
1
+ class TestEndlessRuby
2
+
3
+
4
+ def test_method_chain_case
5
+
6
+ er2rb_right_output?(<<DEFINE.chomp!,
7
+ def test
8
+ self.map do |item|
9
+ pass
10
+ end.each do |item|
11
+ pass
12
+ end
13
+ end
14
+ DEFINE
15
+ <<DEFINE)
16
+ def test
17
+ self.map do |item|
18
+ pass
19
+ end.each do |item|
20
+ pass
21
+ DEFINE
22
+
23
+ end
24
+
25
+ def test_method_chain_and_nest_case
26
+
27
+ er2rb_right_output?(<<DEFINE.chomp!,
28
+ def test
29
+ self.map do |item|
30
+ self.map do |item2|
31
+ pass
32
+ end
33
+ end.each do |item|
34
+ pass
35
+ end
36
+ end
37
+ DEFINE
38
+ <<DEFINE)
39
+ def test
40
+ self.map do |item|
41
+ self.map do |item2|
42
+ pass
43
+ end.each do |item|
44
+ pass
45
+ DEFINE
46
+
47
+ end
48
+
49
+ def test_use_end_case
50
+ er2rb_right_output?(<<DEFINE.chomp!,
51
+ def test
52
+ self.map do |item|
53
+ end
54
+ end
55
+ DEFINE
56
+ <<DEFINE)
57
+ def test
58
+ self.map do |item|
59
+ end
60
+ end
61
+ DEFINE
62
+ end
63
+
64
+ def test_beggining_of_the_end_function
65
+ er2rb_right_output?(<<DEFINE.chomp!,
66
+ def test
67
+ self.each do |item|
68
+ pass
69
+ end
70
+ endlessruby src
71
+ end
72
+ DEFINE
73
+ <<DEFINE)
74
+ def test
75
+ self.each do |item|
76
+ pass
77
+ endlessruby src
78
+ DEFINE
79
+ end
80
+
81
+ end