cappruby 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,494 @@
1
+ #
2
+ # ruby_builder.rb
3
+ # cappruby
4
+ #
5
+ # Created by Adam Beynon.
6
+ # Copyright 2010 Adam Beynon.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #
26
+
27
+ module CappRuby
28
+
29
+ class RubyBuilder < ::Vienna::RubyParser
30
+
31
+ def initialize(source, project, build_name)
32
+ super
33
+ end
34
+
35
+ class CappIseq
36
+
37
+ def initialize(type)
38
+ # iseq type
39
+ @type = type
40
+ # names => minimixed names (i.e. letters)
41
+ @local_names = { }
42
+ # same, for args
43
+ @arg_names = { }
44
+ # has a value, other than nil, of the str used to set the block param
45
+ # e.g. def method(&adam)' end ... this will be "adam", incase we
46
+ # reference it. The block always points to _$, whether we name it or not
47
+ @block_name = nil
48
+ # current to use for minimized. 'a' is always self, so we start there
49
+ # and increment one time for every new local in context.
50
+ @local_current = 'a'
51
+ # Also, if type is a method, then we need room for a selector aswell:
52
+ if type == RubyBuilder::ISEQ_TYPE_METHOD
53
+ @local_current = @local_current.next
54
+ end
55
+
56
+
57
+ # to store code strings
58
+ @code = []
59
+ end
60
+
61
+ def type
62
+ @type
63
+ end
64
+
65
+ def local_current
66
+ @local_current
67
+ end
68
+
69
+ def local_current=(s)
70
+ @local_current = s
71
+ end
72
+
73
+ def write(str)
74
+ @code << str
75
+ end
76
+
77
+ # When we set this, if we are a blcok, then we need to set our
78
+ # @local current to that of the parent, and then, when we finish, set the
79
+ # parents to be what ours finishes at. This avoids clashing local var
80
+ # names.
81
+ #
82
+ # Doing it this way avoids us including these vars, declared in the block
83
+ # as vars in the method. they are vas just for the block.
84
+ def parent_iseq=(other)
85
+ @parent_iseq = other
86
+
87
+ if @type == RubyBuilder::ISEQ_TYPE_BLOCK
88
+ @local_current = other.local_current
89
+ end
90
+ end
91
+
92
+ # finalizes, as described above
93
+ def finalize
94
+ if @type == RubyBuilder::ISEQ_TYPE_BLOCK
95
+ @parent_iseq.local_current = @local_current
96
+ end
97
+ end
98
+
99
+ # looks u; the local name "str". This checks method arguments as well as
100
+ # local variables defined. If we have the local, then the string version
101
+ # of the minimized name is returned (e.g. _a, _b..... etc), or if we do
102
+ # not have it, then nil is returned.
103
+ #
104
+ # Also of note, this also checks if the str is the name of the block that
105
+ # was declared (if a method). A block &block is not an arg or a local, but
106
+ # can be referecenced if declared as a method "argument". These are stored
107
+ # as the local var _$, if present.
108
+ def lookup_local(str)
109
+ if @local_names.has_key? str
110
+ @local_names[str]
111
+ elsif @arg_names.has_key? str
112
+ @arg_names[str]
113
+ elsif @block_name
114
+ "_$"
115
+ else
116
+ if @type == RubyBuilder::ISEQ_TYPE_BLOCK
117
+ return @parent_iseq.lookup_local(str)
118
+ end
119
+ # if we are a block, check parent...
120
+ nil
121
+ end
122
+ end
123
+
124
+ # push arg name, and returns the "minimized version"
125
+ def push_arg_name(str)
126
+ @local_current = @local_current.next
127
+ @arg_names[str] = "_#{@local_current}"
128
+ "_#{@local_current}"
129
+ end
130
+
131
+ # push local name, and returns the "minimized version"
132
+ def push_local_name(str)
133
+ @local_current = @local_current.next
134
+ @local_names[str] = "_#{@local_current}"
135
+ "_#{@local_current}"
136
+ end
137
+
138
+ # set the block name, if present
139
+ def push_block_name(str)
140
+ @block_name = str
141
+ "_$"
142
+ end
143
+
144
+ def to_s
145
+ r = ""
146
+ # function should accept self. if a method type, then accept "sel" for
147
+ # selector, if block, dont add self, just args. top and class also
148
+ # accept just "self" - self is always _a
149
+ # puts @code
150
+ case @type
151
+ when RubyBuilder::ISEQ_TYPE_TOP
152
+ # _a is self (top self)
153
+ r << "function(_a"
154
+ when RubyBuilder::ISEQ_TYPE_METHOD
155
+ # _a is self, _b is the selector
156
+ r << "function(_a,_b"
157
+ @arg_names.each_value { |v| r << ",#{v}" }
158
+ when RubyBuilder::ISEQ_TYPE_CLASS
159
+ # _a is self (the class just created/modified)
160
+ r << "function(_a"
161
+ when RubyBuilder::ISEQ_TYPE_BLOCK
162
+ # no self, just args etc..
163
+ r << "function("
164
+ r << @arg_names.each_value.to_a.join(",")
165
+ else
166
+ abort "erm, unknwon iseq type"
167
+ end
168
+
169
+ r << "){"
170
+ # locals
171
+ if @local_names.length > 0
172
+ r << "var #{@local_names.each_value.to_a.join(", ")};"
173
+ end
174
+
175
+ r << "#{@code.join("")}}"
176
+
177
+ r
178
+ end
179
+
180
+ end
181
+
182
+ def iseq_stack_push type
183
+ @iseq_current = CappIseq.new type
184
+ @iseq_stack << @iseq_current
185
+ @iseq_current
186
+ end
187
+
188
+ def iseq_stack_pop
189
+ iseq = @iseq_stack.last
190
+ @iseq_stack.pop
191
+ @iseq_current = @iseq_stack.last
192
+ # finalize (var locals etc)
193
+ iseq.finalize
194
+ iseq.to_s
195
+ end
196
+
197
+ def generate_tree(tree)
198
+ top_iseq = iseq_stack_push ISEQ_TYPE_TOP
199
+ tree.each do |stmt|
200
+ generate_stmt stmt, :full_stmt => true, :last_stmt => tree.last == stmt
201
+ end
202
+ iseq_stack_pop
203
+ end
204
+
205
+ def generate_class(cls, context)
206
+ write "return " if context[:last_stmt] and context[:full_stmt]
207
+
208
+ current_iseq = @iseq_current
209
+ class_iseq = iseq_stack_push ISEQ_TYPE_CLASS
210
+ class_iseq.parent_iseq = current_iseq
211
+
212
+ # do each body stmt, but for now, assume nonoe
213
+ cls.bodystmt.each do |b|
214
+ generate_stmt b, :full_stmt => true, :last_stmt => b == cls.bodystmt.last
215
+ end
216
+
217
+ if cls.bodystmt.length == 0
218
+ write "return nil;"
219
+ end
220
+
221
+ iseq_stack_pop
222
+
223
+ write %{cr_defineclass(nil,nil,"#{cls.klass_name}",#{class_iseq},0)}
224
+
225
+ write ";" if context[:full_stmt]
226
+ end
227
+
228
+ def generate_def stmt, context
229
+ # work out if its an objj style method call..
230
+
231
+ write "return " if context[:last_stmt] and context[:full_stmt]
232
+
233
+ is_singleton = stmt[:singleton] ? 1 : 0
234
+
235
+ current_iseq = @iseq_current
236
+ def_iseq = iseq_stack_push(ISEQ_TYPE_METHOD)
237
+ def_iseq.parent_iseq = current_iseq
238
+
239
+ # arg names
240
+ if stmt[:arglist].arg
241
+ stmt[:arglist].arg.each { |a| @iseq_current.push_arg_name a[:value] }
242
+ end
243
+
244
+ # body stmts
245
+ stmt[:bodystmt].each do |b|
246
+ generate_stmt b, :full_stmt => true, :last_stmt => b == stmt[:bodystmt].last
247
+ end
248
+
249
+ iseq_stack_pop
250
+
251
+ # definemethod
252
+ write "cr_definemethod("
253
+
254
+ # base (singleton?)
255
+ if stmt[:singleton]
256
+ generate_stmt stmt[:singleton], :full_stmt => false, :last_stmt => false
257
+ else
258
+ # self
259
+ write "_a"
260
+ end
261
+
262
+ # method returns true if our normal method should add a colon onto the
263
+ # selector name (i.e. it takes either exactly 1 param, or it takes a
264
+ # single default param.. this might be the case, we just add a defailt
265
+ # value to the selector:)
266
+ sel_colon = gen_def_should_use_colon?(stmt[:arglist]) ? ":" : ""
267
+
268
+ write %{,"#{stmt[:fname]}#{sel_colon}",#{def_iseq},#{is_singleton})}
269
+
270
+ write ";" if context[:full_stmt]
271
+ end
272
+
273
+ # method returns true if our normal method should add a colon onto the
274
+ # selector name (i.e. it takes either exactly 1 param, or it takes a
275
+ # single default param.. this might be the case, we just add a defailt
276
+ # value to the selector:)
277
+ def gen_def_should_use_colon?(a)
278
+ # one normal arg, nothing else (block is irrelevant)
279
+ if a.arg_size==1 && a.opt_size== 0 && a.rest_size == 0 && a.post_size == 0
280
+ true
281
+ elsif a.arg_size==0 && a.opt_size==1 && a.rest_size==0 && a.post_size == 0
282
+ true
283
+ else
284
+ false
285
+ end
286
+ end
287
+
288
+ def generate_call call, context
289
+ # capture "function calls"
290
+ if call[:meth].match /^[A-Z](.*)$/
291
+ return generate_function_call call, context
292
+ end
293
+ # capture objj
294
+
295
+ write "return " if context[:last_stmt] and context[:full_stmt]
296
+
297
+ write %{cr_send(}
298
+ # receiver
299
+ if call[:recv]
300
+ call_bit = 0
301
+ generate_stmt call[:recv], :full_stmt => false, :last_stmt => false
302
+ else
303
+ call_bit = 8
304
+ # self as recv
305
+ write "_a"
306
+ end
307
+
308
+ write ","
309
+ # method id - we should look for adding a selector here..
310
+ sel_colon = gen_call_should_use_colon?(call) ? ":" : ""
311
+ write %{"#{call[:meth]}#{sel_colon}"}
312
+
313
+ write ","
314
+
315
+ # arguments
316
+ write "["
317
+ unless call[:call_args].nil? or call[:call_args][:args].nil?
318
+ call[:call_args][:args].each do |arg|
319
+ write "," unless call[:call_args][:args].last == arg
320
+ generate_stmt arg, :full_stmt => false
321
+ end
322
+ end
323
+
324
+ # assocs
325
+ if call[:call_args] and call[:call_args][:assocs]
326
+ write "," unless call[:call_args].nil? or call[:call_args][:args].nil?
327
+ write "cr_newhash("
328
+ call[:call_args][:assocs].each do |assoc|
329
+ write "," unless call[:call_args][:assocs].first == assoc
330
+ generate_stmt assoc[:key], :full_stmt => false, :last_stmt => false
331
+ write ","
332
+ generate_stmt assoc[:value], :full_stmt => false, :last_stmt => false
333
+ end
334
+ write ")"
335
+ end
336
+
337
+ write "]"
338
+ write ","
339
+
340
+ # block
341
+ if call[:brace_block]
342
+ current_iseq = @iseq_current
343
+ block_iseq = iseq_stack_push(ISEQ_TYPE_BLOCK)
344
+ block_iseq.parent_iseq = current_iseq
345
+
346
+ # block arg names
347
+ if call[:brace_block][:params]
348
+ call[:brace_block][:params].each do |p|
349
+ block_iseq.push_arg_name p[:value]
350
+ end
351
+ end
352
+
353
+ # block stmts
354
+ if call[:brace_block][:stmt]
355
+ call[:brace_block][:stmt].each do |a|
356
+ generate_stmt a, :full_stmt => true, :last_stmt => call[:brace_block][:stmt].last == a
357
+ end
358
+ end
359
+
360
+ iseq_stack_pop
361
+
362
+ write block_iseq.to_s
363
+ else
364
+ write "nil"
365
+ end
366
+ write ","
367
+
368
+ # op flag
369
+ write "#{call_bit})"
370
+
371
+ write ";" if context[:full_stmt]
372
+ end
373
+
374
+ def generate_function_call call, context
375
+ write "return " if context[:last_stmt] and context[:full_stmt]
376
+
377
+ write "cr_functioncall(\""
378
+ write call[:meth]
379
+ write "\",["
380
+
381
+ unless call[:call_args].nil? or call[:call_args][:args].nil?
382
+ call[:call_args][:args].each do |arg|
383
+ write "," unless call[:call_args][:args].first == arg
384
+ generate_stmt arg, :full_stmt => false
385
+ end
386
+ end
387
+
388
+ write "])"
389
+
390
+ write ";" if context[:full_stmt]
391
+ end
392
+
393
+ def generate_identifier id, context
394
+ write "return " if context[:last_stmt] and context[:full_stmt]
395
+
396
+ local = @iseq_current.lookup_local id[:name]
397
+ if local
398
+ write local
399
+ else
400
+ # cannot find local, so we assume it is a function call...
401
+ write %{cr_send(_a,"#{id[:name]}",[],nil,8)}
402
+ end
403
+
404
+ write ";" if context[:full_stmt]
405
+ end
406
+
407
+ # similar to def, but for calls (not objj style calls..)
408
+ def gen_call_should_use_colon?(c)
409
+ # basically, if a "special" method using ruby only things, then dont use
410
+ # a colon
411
+ return false if c[:meth].match /(\<|\!|\?\>\=\!)/
412
+
413
+ args_len = 0
414
+ if c[:call_args] and c[:call_args][:args]
415
+ args_len = args_len + c[:call_args][:args].length
416
+ end
417
+
418
+ if c[:call_args] and c[:call_args][:assocs]
419
+ args_len = args_len + 1
420
+ end
421
+
422
+ # essentially, true if only one arg..
423
+ (args_len == 1) ? true : false
424
+ end
425
+
426
+ def generate_self stmt, context
427
+ write "_a"
428
+ end
429
+
430
+ # Generate a yield stmt. This makes sure the current iseq is a method, as
431
+ # only methods can "yield".. also, sets the current iseq to "get block". We
432
+ # know the method uses a block, so we need to make sure we can get it, and
433
+ # put it into a local var.
434
+ def generate_yield stmt, context
435
+ write "\"block\""
436
+ end
437
+
438
+ def generate_string str, context
439
+ write %{"#{str[:value][0][:value]}"}
440
+ end
441
+
442
+ def generate_symbol sym, context
443
+ write %{ID2SYM("#{sym[:name]}")}
444
+ end
445
+
446
+ def generate_constant cnst, context
447
+ write %{cr_getconstant(_a,"#{cnst[:name]}")}
448
+ end
449
+
450
+ def generate_numeric num, context
451
+ write num[:value]
452
+ end
453
+
454
+ def generate_array ary, context
455
+ write "["
456
+ if ary[:args]
457
+ ary[:args].each do |a|
458
+ write "," unless ary[:args].first == a
459
+ generate_stmt a, :full_stmt => false, :last_stmt => false
460
+ end
461
+ end
462
+
463
+ write "]"
464
+ end
465
+
466
+ def generate_xstring stmt, context
467
+ write "return " if context[:last_stmt] and context[:full_stmt]
468
+ write stmt[:value][0][:value]
469
+ write ";" if context[:full_stmt]
470
+ end
471
+
472
+ def generate_assign stmt, context
473
+ write "return " if context[:last_stmt] and context[:full_stmt]
474
+
475
+ # LHS is an identifier (local)
476
+ if stmt[:lhs].node == :identifier
477
+ # check to see if it already exists, i.e. reusing old name
478
+ local = @iseq_current.lookup_local stmt[:lhs][:name]
479
+ unless local
480
+ # cannot find local, so we must make it
481
+ local = @iseq_current.push_local_name stmt[:lhs][:name]
482
+ end
483
+
484
+ write "#{local} = "
485
+ # RHS
486
+ generate_stmt stmt[:rhs], :full_stmt => false, :last_stmt => false
487
+ else
488
+ abort "unknown assign type: #{stmt[:lhs].node}"
489
+ end
490
+
491
+ write ";" if context[:full_stmt]
492
+ end
493
+ end
494
+ end
data/lib/cappruby.rb ADDED
@@ -0,0 +1,73 @@
1
+ #
2
+ # cappruby.rb
3
+ # cappruby
4
+ #
5
+ # Created by Adam Beynon.
6
+ # Copyright 2010 Adam Beynon.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #
26
+
27
+ require 'yaml'
28
+ require 'fileutils'
29
+ require 'rubygems'
30
+ # require 'vienna'
31
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'vienna.rb')
32
+
33
+ module CappRuby
34
+
35
+ ROOTPATH = File.join(File.dirname(__FILE__), '..')
36
+
37
+ def self.tools(args)
38
+
39
+ cmd = args.shift
40
+ if cmd.nil? || cmd == "-h" || cmd == '--help'
41
+ print_usage
42
+ exit
43
+ end
44
+
45
+ case cmd
46
+ when "gen"
47
+ gen args
48
+ when "build"
49
+ require_libs
50
+ AppBuilder.new(args).build!
51
+ when "-f"
52
+ puts "update frameworks"
53
+ else
54
+ print_usage
55
+ exit
56
+ end
57
+
58
+ end
59
+
60
+ def self.print_usage
61
+ puts "Usage: cappruby gen <app name>"
62
+ end
63
+
64
+ def self.gen(args)
65
+ puts "gen.."
66
+ end
67
+
68
+ def self.require_libs
69
+ libs = File.join(File.dirname(__FILE__), 'cappruby', '**', '*.rb')
70
+ Dir.glob(libs).each { |f| require f }
71
+ end
72
+
73
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cappruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Beynon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-30 00:00:00 +00:00
13
+ default_executable: cappruby
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: vienna
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: cappruby
26
+ email: adam@adambeynon.com
27
+ executables:
28
+ - cappruby
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - Rakefile
35
+ - VERSION
36
+ - bin/cappruby
37
+ - build/CappRuby/CappRuby.j
38
+ - cappruby.gemspec
39
+ - demos/simple_app/config/build.yml
40
+ - demos/simple_app/lib/application.rb
41
+ - demos/simple_app/lib/menu.rb
42
+ - framework/array.js
43
+ - framework/cappruby.js
44
+ - framework/class.js
45
+ - framework/enumerator.js
46
+ - framework/file.js
47
+ - framework/geometry.js
48
+ - framework/hash.js
49
+ - framework/mapings.js
50
+ - framework/method.js
51
+ - framework/module.js
52
+ - framework/object.js
53
+ - framework/objj_additions.js
54
+ - framework/proc.js
55
+ - framework/string.js
56
+ - framework/variable.js
57
+ - framework/vm.js
58
+ - framework/window.js
59
+ - lib/cappruby.rb
60
+ - lib/cappruby/app_builder.rb
61
+ - lib/cappruby/framework_builder.rb
62
+ - lib/cappruby/ruby_builder.rb
63
+ has_rdoc: true
64
+ homepage: http://github.com/adambeynon/cappruby
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.5
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: cappruby
91
+ test_files: []
92
+