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