endlessruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gemtest ADDED
File without changes
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2011-11-17
2
+
3
+ * Alpha:
4
+ * initial release
data/Manifest.txt ADDED
@@ -0,0 +1,30 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ bin/endlessruby
6
+ lib/endlessruby.rb
7
+ lib/endlessruby/extensions.rb
8
+ lib/endlessruby/main.rb
9
+ src/endlessruby.er
10
+ src/endlessruby/extensions.er
11
+ src/endlessruby/main.er
12
+ src/er.er
13
+ spec/blank_line_spec.rb
14
+ spec/build_self_spec.rb
15
+ spec/comment_spec.rb
16
+ spec/concrete_examples/case_1_simply_test_case.er
17
+ spec/concrete_examples/case_1_simply_test_case.rb
18
+ spec/concrete_examples_spec.rb
19
+ spec/endlessruby_spec.rb
20
+ spec/here_document_spec.rb
21
+ spec/require_spec.rb
22
+ spec/semicolon_spec.rb
23
+ spec/simply_spec.rb
24
+ spec/spec.opts
25
+ spec/spec_helper.rb
26
+ spec/test_data/er_omission.er
27
+ spec/test_data/file.er
28
+ spec/test_data/rb_omission.rb
29
+ spec/test_data/ruby_script.rb
30
+ tasks/rspec.rake
data/README.rdoc ADDED
@@ -0,0 +1,53 @@
1
+ = EndlessRuby
2
+
3
+ * http://github.com/pasberth/EndlessRuby
4
+
5
+ == DESCRIPTION:
6
+
7
+ EndlessRuby は Ruby を end なしで書けるプリプロセッサまたはコンパイラです。
8
+ * EndlessRuby で書かれたソースコードを Ruby プログラムから require
9
+ * EndlessRuby で書かれたソースコードを ピュア Ruby にコンパイル
10
+ ができます。
11
+
12
+ 基本的にRubyの構文からendを取り除いただけで書けます。endを取り除かなくても実行可能です。
13
+ EndlessRubyの独自な拡張的な構文はありません。
14
+ ただ行単位で処理しているので def method; a; end みたいな書式できません。
15
+
16
+ コンパイルする場合は
17
+ $ endlessruby -c src/example.er src/more.er -o lib
18
+ とします。
19
+ この場合、
20
+ src/example.er => lib/example.rb
21
+ src/more.er => lib/more.rb
22
+ として書き出されます。
23
+
24
+ 実行する場合は
25
+ $ endlessruby src/example.er
26
+
27
+ コンパイルされていないEndlessRubyのファイルをrequire する場合は
28
+
29
+ require 'endlessruby'
30
+ require 'example.er'
31
+
32
+ たぶんバグだらけです
33
+
34
+ == FEATURES/PROBLEMS:
35
+
36
+ * def method; a; end みたいな書式に対応
37
+
38
+ == SYNOPSIS:
39
+
40
+ = REQUIREMENTs
41
+
42
+ * ruby 1.8.7, 1.9.1, or 1.9.2 ...
43
+ * rubygems
44
+
45
+ == INSTALL:
46
+
47
+ * sudo gem install endlessruby
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2011 pasberth
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/EndlessRuby'
6
+
7
+ $hoe = Hoe.spec 'endlessruby' do
8
+ self.developer 'pasberth', 'pasberth@gmail.com'
9
+ self.rubyforge_name = self.name
10
+ self.readme_file = 'README.rdoc'
11
+ end
data/bin/endlessruby ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "endlessruby"
4
+ require "endlessruby/main"
5
+ EndlessRuby::Main.main ARGV
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'rubygems'
5
+
6
+ module Kernel
7
+
8
+ alias endlessruby_original_require require
9
+
10
+ # EndlessRuby によって再定義された require です。
11
+ # たいていのこのrequireはオリジナルなrequireまたはrubygemsのrequireがSyntaxErrorによって失敗した場合のみ機能します。
12
+ # SytanxError によってrequireが失敗した場合、pathを探してpathまたはpath.erの名前のファイルをEndlessRubyの構文として評価します。
13
+ # pathが./または/で以外で始まる場合は$LOAD_PATHと$:をそれぞれ参照してpathを探します。
14
+ # もしpathがそれらで始まる場合はそれぞれ参照しません。(つまり通常のrequireの動作と同じです)
15
+ def require path
16
+ at = caller
17
+ endlessruby_original_require path
18
+ rescue SyntaxError, LoadError
19
+ case path
20
+ when /^\.\/.*?$/, /^\/.*?$/
21
+ unless File.exist? path
22
+ if File.exist? "#{path}.er"
23
+ path = "#{path}.er"
24
+ else
25
+ $@ = at
26
+ raise LoadError, "no such file to load -- #{path}"
27
+ end
28
+ end
29
+
30
+ if File.directory? path
31
+ $@ = at
32
+ raise LoadError, "Is a directory - #{path}"
33
+ end
34
+
35
+ open(path) do |file|
36
+ begin
37
+ EndlessRuby.ereval file.read, TOPLEVEL_BINDING, path
38
+ rescue Exception => e
39
+ $@ = at
40
+ raise e
41
+ end
42
+ return true
43
+ end
44
+ else
45
+ is_that_dir = false
46
+ ($LOAD_PATH | $:).each do |load_path|
47
+ real_path = File.join load_path, path
48
+ unless File.exist? real_path
49
+ if File.exist? "#{real_path}.er"
50
+ real_path = "#{real_path}.er"
51
+ else
52
+ next
53
+ end
54
+ end
55
+
56
+ next is_that_dir = true if File.directory? real_path
57
+ open(real_path) do |file|
58
+ begin
59
+ EndlessRuby.ereval file.read, TOPLEVEL_BINDING, real_path
60
+ rescue Exception => e
61
+ $@ = at
62
+ raise e
63
+ end
64
+ end
65
+ return true
66
+ end
67
+ $@ = at
68
+ if is_that_dir
69
+ raise LoadError, "Is a directory - #{path}"
70
+ else
71
+ raise LoadError, "no such file to load -- #{path}"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,124 @@
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
+ end
16
+ end
17
+ end
18
+
19
+ # rbファイルを読み込みそれからすべてのendを取り除きます。
20
+ def decompile rb, er
21
+ open(rb) do |rbfile|
22
+ open(er, "w") do |erfile|
23
+ erfile.write PR2ER(rbfile.read)
24
+ end
25
+ end
26
+ end
27
+
28
+
29
+ # EndlessRuby::Main.main と同じ動作をします。このモジュールをincludeした場合に使用します。
30
+ def endlessruby argv
31
+ EndlessRuby::Main.main argv
32
+ end
33
+
34
+ # $ endlessruby.rb args とまったく同じ動作をします。argvはARGVと同じ形式でなければなりません。
35
+ def self.main argv
36
+
37
+ if argv.first && File.exist?(argv.first)
38
+ $PROGRAM_NAME = argv.shift
39
+ open $PROGRAM_NAME do |file|
40
+ EndlessRuby.ereval file.read, TOPLEVEL_BINDING, $PROGRAM_NAME
41
+ end
42
+ return true
43
+ end
44
+
45
+ require 'optparse'
46
+
47
+ options = {
48
+ }
49
+
50
+ parser = OptionParser.new do |opts|
51
+ opts.on '-o OUT' do |out|
52
+ options[:out] = out
53
+ end
54
+
55
+ opts.on '-c', '--compile' do |c|
56
+ options[:compile] = true
57
+ end
58
+
59
+ opts.on '-d', '--decompile' do |d|
60
+ options[:decompile] = true
61
+ end
62
+
63
+ opts.on '-r' do |r|
64
+ options[:recursive] = true
65
+ end
66
+ end
67
+
68
+ parser.parse! argv
69
+
70
+ if options[:compile]
71
+ out = options[:out] || '.'
72
+
73
+ argv.each do |er|
74
+ unless File.exist? er
75
+ puts "no such file to load -- #{er}"
76
+ next
77
+ end
78
+
79
+ if File.directory? er
80
+ unless options[:recursive]
81
+ puts "Is a directory - #{er}"
82
+ next
83
+ end
84
+ # Unimolementation
85
+ next
86
+ end
87
+
88
+ rb = er
89
+ if er =~ /^(.*)\.er$/
90
+ rb = $1
91
+ end
92
+ rb = File.split(rb)[1]
93
+ rb = File.join(out, "#{rb}.rb")
94
+ compile er, rb
95
+ end
96
+ elsif options[:decompile]
97
+ out = options[:out] || '.'
98
+
99
+ argv.each do |rb|
100
+ unless File.exist? rb
101
+ puts "no such file to load -- #{rb}"
102
+ next
103
+ end
104
+
105
+ if File.directory? rb
106
+ unless options[:recursive]
107
+ puts "Is a directory - #{rb}"
108
+ next
109
+ end
110
+ # Unimolementation
111
+ next
112
+ end
113
+
114
+ er = rb
115
+ if rb =~ /^(.*)\.rb$/
116
+ er = $1
117
+ end
118
+ er = File.split(er)[1]
119
+ er = File.join(out, "#{er}.er")
120
+ decompile rb, er
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,243 @@
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
+ end
48
+
49
+ # 内部で使用します。インデントを取り除きます
50
+ def unindent line
51
+ line =~ /^\s*?(\S.*?)$/
52
+ $1
53
+ end
54
+ # 内部で使用します。インデントします
55
+ def indent line, level, indent=" "
56
+ "#{indent * level}#{line}"
57
+ end
58
+
59
+ # 内部で使用します。インデントの数を数えます。
60
+ def indent_count line, indent=" "
61
+ return 0 unless line
62
+ if line =~ /^#{indent}(.*?)$/
63
+ 1 + (indent_count $1, indent)
64
+ else
65
+ 0
66
+ end
67
+ end
68
+
69
+ # 内部で使用します。ブロックを作るキーワード群です。
70
+ BLOCK_KEYWORDS = [
71
+ [/^if(:?\s|\().*?$/, /^elsif(:?\s|\().*?$/, /^else(?:$|\s+)/],
72
+ [/^unless(:?\s|\().*?$/, /^elsif(:?\s|\().*?$/, /^else(?:$|\s+)/],
73
+ [/^while(:?\s|\().*?$/],
74
+ [/^until(:?\s|\().*?$/],
75
+ [/^case(:?\s|\().*?$/, /^when(:?\s|\().*?$/, /^else(?:$|\s+)/],
76
+ [/^def\s.*?$/, /^rescue(:?$|\s|\().*?$/, /^else(?:$|\s+)/, /^ensure(?:$|\s+)/],
77
+ [/^class\s.*?$/],
78
+ [/^module\s.*?$/],
79
+ [/^begin(?:$|\s+)/, /^rescue(:?$|\s|\().*?$/, /^else(?:$|\s+)/, /^ensure(?:$|\s+)/],
80
+ [/^.*?\s+do(:?$|\s|\|)/]
81
+ ]
82
+
83
+ public
84
+
85
+ # 文字列をEndlessRubyの構文として実行します。引数の意味はKernel#evalと同じです
86
+ def ereval(src, binding=TOPLEVEL_BINDING, filename=__FILE__, lineno=1)
87
+ at = caller
88
+ eval(ER2PR(src), binding, filename, lineno)
89
+ rescue Exception => e
90
+ $@ = at
91
+ raise e
92
+ end
93
+
94
+ # Rubyの構文をEndlessRubyの構文に変換します。
95
+ def pure_ruby_to_endless_ruby src
96
+ @decompile = true
97
+ s = ER2RB(src)
98
+ @decompile = nil
99
+ s
100
+ end
101
+
102
+
103
+ alias RB2ER pure_ruby_to_endless_ruby
104
+ alias PR2ER pure_ruby_to_endless_ruby
105
+
106
+ # EndlessRubyの構文をピュアなRubyの構文に変換します。
107
+ def endless_ruby_to_pure_ruby src
108
+ endless = src.split "\n"
109
+
110
+ pure = []
111
+ i = 0
112
+ while i < endless.length
113
+ pure << (currently_line = endless[i])
114
+
115
+ if currently_line =~ /(.*)(?:^|(?:(?!\\).))\#(?!\{).*$/
116
+ currently_line = $1
117
+ end
118
+
119
+ if blank_line? currently_line
120
+ i += 1
121
+ next
122
+ end
123
+
124
+ # ブロックを作らない構文なら単に無視する
125
+ next i += 1 unless BLOCK_KEYWORDS.any? { |k| k[0] =~ unindent(currently_line) }
126
+
127
+ # ブロックに入る
128
+ keyword = BLOCK_KEYWORDS.each { |k| break k if k[0] =~ unindent(currently_line) }
129
+
130
+ currently_indent_depth = indent_count currently_line
131
+ base_indent_depth = currently_indent_depth
132
+
133
+ inner_statements = []
134
+ # def method1
135
+ # statemetns
136
+ # # document of method2
137
+ # def method2
138
+ # statements
139
+ # のような場合にコメントの部分はmethod1内に含まないようにする。
140
+ # def method1
141
+ # statemetns
142
+ # # comment
143
+ # return
144
+ # のような場合と区別するため。
145
+ comment_count = 0
146
+ in_here_document = nil
147
+ while i < endless.length
148
+
149
+ inner_currently_line = endless[i + 1]
150
+
151
+ if inner_currently_line =~ /(.*)(?:^|(?:(?!\\).))\#(?!\{).*$/
152
+ if blank_line?($1) && currently_indent_depth >= indent_count(inner_currently_line)
153
+ comment_count += 1
154
+ end
155
+ inner_currently_line = $1
156
+ elsif blank_line? inner_currently_line
157
+ comment_count += 1
158
+ end
159
+
160
+ if blank_line? inner_currently_line
161
+ inner_statements << endless[i + 1]
162
+ i += 1
163
+ next
164
+ end
165
+
166
+ just_after_indent_depth = indent_count inner_currently_line
167
+
168
+ # 次の行がendならば意図のあるものなのでendを持ちあ揚げない
169
+ if inner_currently_line =~ /^\s*end(?!\w).*$/
170
+ comment_count = 0
171
+ end
172
+
173
+ if base_indent_depth < just_after_indent_depth
174
+ comment_count = 0
175
+ end
176
+
177
+ if in_here_document
178
+ if (in_here_document[0] == '' && inner_currently_line =~ /^#{in_here_document[1]}\s*$/) || # <<DEFINE case
179
+ (in_here_document[0] == '-' && inner_currently_line =~ /^\s*#{in_here_document[1]}\s*$/) # <<-DEFINE case
180
+ in_here_document = nil
181
+ inner_statements << endless[i + 1]
182
+ i += 1
183
+ next
184
+ else
185
+ inner_statements << endless[i + 1]
186
+ i += 1
187
+ next
188
+ end
189
+ end
190
+
191
+ if inner_currently_line =~ /^.*?\<\<(\-?)(\w+)(?!\w).*$/
192
+ in_here_document = [$1, $2]
193
+ end
194
+
195
+ if base_indent_depth > indent_count(inner_currently_line)
196
+ break
197
+ end
198
+
199
+ if base_indent_depth == indent_count(inner_currently_line)
200
+ unless keyword[1..-1].any? { |k| k =~ unindent(inner_currently_line) }
201
+ break
202
+ end
203
+ end
204
+
205
+ inner_statements << endless[i + 1]
206
+ i += 1
207
+ end
208
+
209
+ # endをコメントより上の行へ持ち上げる
210
+ if 0 < comment_count
211
+ comment_count.times do
212
+ inner_statements.pop
213
+ end
214
+ i -= comment_count
215
+ end
216
+
217
+ pure += ER2PR(inner_statements.join("\n")).split "\n"
218
+ # 次の行がendならばendを補完しない(ワンライナーのため)
219
+ unless @decompile
220
+ unless endless[i + 1] && endless[i + 1] =~ /^\s*end(?!\w).*$/
221
+ pure << "#{' '*currently_indent_depth}end"
222
+ end
223
+ else
224
+ # メソッドチェインは削除しない
225
+ if endless[i + 1] && endless[i + 1] =~ /^\s*end(?:\s|$)\s*$/
226
+ i += 1
227
+ end
228
+ end
229
+
230
+ i += 1
231
+ end
232
+ pure.join "\n"
233
+ end
234
+
235
+ alias to_pure_ruby endless_ruby_to_pure_ruby
236
+ alias ER2PR endless_ruby_to_pure_ruby
237
+ alias ER2RB endless_ruby_to_pure_ruby
238
+ end
239
+
240
+ if __FILE__ == $PROGRAM_NAME
241
+ require 'endlessruby/main'
242
+ EndlessRuby::Main.main ARGV
243
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "blank lines" do
4
+
5
+ it "blank line ending" do
6
+ ER2RB(<<DEFINE).should ==
7
+ module Kenel
8
+ def method1
9
+ statements
10
+
11
+ return
12
+ DEFINE
13
+ <<DEFINE.chomp!
14
+ module Kenel
15
+ def method1
16
+ statements
17
+
18
+ return
19
+ end
20
+ end
21
+ DEFINE
22
+ end
23
+
24
+ end
@@ -0,0 +1,52 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.dirname(__FILE__) + '/spec_helper.rb'
3
+
4
+ bin = File.dirname(__FILE__) + "/../bin"
5
+ src = File.dirname(__FILE__) + "/../src"
6
+ lib = File.dirname(__FILE__) + "/../lib"
7
+
8
+ describe EndlessRuby, "must can compile self" do
9
+
10
+ myself_sources = [
11
+ "#{src}/endlessruby.er",
12
+ "#{lib}/endlessruby.rb",
13
+ "#{src}/endlessruby/main.er",
14
+ "#{lib}/endlessruby/main.rb",
15
+ "#{src}/endlessruby/extensions.er",
16
+ "#{lib}/endlessruby/extensions.rb",
17
+ "#{src}/er.er",
18
+ "#{bin}/endlessruby",
19
+ ]
20
+ myself_sources.each_slice(2) do |er, rb|
21
+ it "must can compile #{er} to #{rb}" do
22
+ begin
23
+ rb = open rb
24
+ er = open er
25
+
26
+ ER2RB(er.read).should == rb.read
27
+ ensure
28
+ rb.close
29
+ er.close
30
+ end
31
+ end
32
+ end
33
+
34
+ myself_sources.each_slice(2) do |er, rb|
35
+ it "must can decompile #{rb} to #{er}" do
36
+ begin
37
+ rb = open rb
38
+ er = open er
39
+
40
+ s = er.read
41
+ # 改行は取り除く
42
+ s = ERSpecHelper.chomp s
43
+
44
+ RB2ER(rb.read).should == s
45
+ ensure
46
+ rb.close
47
+ er.close
48
+ end
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe "comment out case" do
4
+
5
+ it "comment" do
6
+ ER2RB(<<DEFINE).should ==
7
+ #TODO:
8
+ # proc do |item|
9
+ # statements
10
+ DEFINE
11
+ <<DEFINE.chomp!
12
+ #TODO:
13
+ # proc do |item|
14
+ # statements
15
+ DEFINE
16
+ end
17
+
18
+ it "simple comment" do
19
+ ER2RB(<<DEFINE).should ==
20
+ #TODO:
21
+ # proc do |item|
22
+ # statements
23
+ DEFINE
24
+ <<DEFINE.chomp!
25
+ #TODO:
26
+ # proc do |item|
27
+ # statements
28
+ DEFINE
29
+ end
30
+
31
+ end
32
+
33
+ describe "rdoc case" do
34
+
35
+ it "rerurned" do
36
+ ER2RB(<<DEFINE).should ==
37
+ # DOCUMENT LINE 1
38
+ # DOCUMENT LINE 2
39
+ def method1
40
+ statements
41
+ return
42
+ # DOCUMENT LINE 1
43
+ # DOCUMENT LINE 2
44
+ def method1
45
+ statements
46
+ return
47
+ DEFINE
48
+ <<DEFINE.chomp!
49
+ # DOCUMENT LINE 1
50
+ # DOCUMENT LINE 2
51
+ def method1
52
+ statements
53
+ return
54
+ end
55
+ # DOCUMENT LINE 1
56
+ # DOCUMENT LINE 2
57
+ def method1
58
+ statements
59
+ return
60
+ end
61
+ DEFINE
62
+ end
63
+
64
+ end