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