jejune 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,710 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+ #--
4
+ # Copyright (c) 2010-2011 Kyle C. Yetter
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ module Jejune
27
+ class Translator < TreeWalker
28
+ #require 'jejune/rewrite-debug'
29
+ #include DebugRewrite
30
+ include Data
31
+ include NodeTest
32
+
33
+ skip( BREAK, FALSE, TRUE, NULL, THIS, NUMBER, UNDEFINED )
34
+
35
+ def initialize( manager )
36
+ @manager = manager
37
+ super()
38
+ end
39
+
40
+
41
+ def on_macro( node )
42
+ @manager.define_macro( node )
43
+ node.replace!( '' )
44
+ end
45
+
46
+ def on_pound( node )
47
+ name, arguments = node
48
+ replacement = @manager.expand_macro( name.text, *arguments )
49
+ node.replace!( replacement )
50
+ end
51
+
52
+ def on_class_def( node )
53
+ descend( node )
54
+
55
+ name_token, *member_nodes = node
56
+ name = name_token.text
57
+ members = []
58
+ constructor = nil
59
+
60
+ for member_node in member_nodes
61
+ case member_node.type
62
+ when DEF
63
+ def_name = member_node.first.text
64
+ if member_node.length == 3
65
+ args = member_node[ 1 ]
66
+ body = member_node[ 2 ]
67
+ else
68
+ args = nil
69
+ body = member_node[ 1 ]
70
+ end
71
+
72
+ if def_name == 'initialize'
73
+ constructor = build_function( args, body, name );
74
+ else
75
+ members.push( [ @manager.jstring( def_name ), build_function( args, body ) ].join( ': ' ) )
76
+ end
77
+ when ASGN
78
+ var_name = @manager.jstring( member_node[ 0 ].text )
79
+ var_val = member_node[ 1 ].source
80
+
81
+ members.push( [ var_name, var_val ].join( ': ' ) )
82
+ when VAR
83
+ for assign in member_node
84
+ members.push( [ @manager.jstring( assign[ 0 ].text ), assign[ 1 ].source ].join( ': ' ) )
85
+ end
86
+ when GET, SET
87
+ members.push( member_node.source )
88
+ end
89
+ end
90
+
91
+ constructor ||= "function #{ name }() { return this; }"
92
+
93
+ node.replace!( "#{ constructor };\n#{ name }.prototype = {\n#{ members.join( ",\n" ) }\n};" )
94
+ end
95
+
96
+ def on_object_def( node )
97
+ descend( node )
98
+
99
+ var_token, *member_nodes = node
100
+ if is_var = ( var_token.type == VAR )
101
+ name_token, *member_nodes = member_nodes
102
+ else
103
+ name_token = var_token
104
+ end
105
+
106
+ name = name_token.text
107
+ members = []
108
+
109
+ for member_node in member_nodes
110
+ case member_node.type
111
+ when DEF
112
+ def_name = member_node.first.text
113
+ if member_node.length == 3
114
+ args = member_node[ 1 ]
115
+ body = member_node[ 2 ]
116
+ else
117
+ args = nil
118
+ body = member_node[ 1 ]
119
+ end
120
+
121
+ members.push( [ @manager.jstring( def_name ), build_function( args, body ) ].join( ': ' ) )
122
+ when ASGN
123
+ var_name = @manager.jstring( member_node[ 0 ].text )
124
+ var_val = member_node[ 1 ].source
125
+
126
+ members.push( [ var_name, var_val ].join( ': ' ) )
127
+ when VAR
128
+ for assign in member_node
129
+ members.push( [ @manager.jstring( assign[ 0 ].text ), assign[ 1 ].source ].join( ': ' ) )
130
+ end
131
+ when GET, SET
132
+ members.push( member_node.source )
133
+ end
134
+ end
135
+ prefix = is_var ? "var #{ name }" : name
136
+ node.replace!( "#{ prefix } = {\n#{ members.join( ",\n" ) }\n};" )
137
+ end
138
+
139
+ def on_try( node )
140
+ descend( node )
141
+ if default_catch = @manager.macros[ 'defaultCatch' ]
142
+ error_token = TokenData::Token.new( ID, :default, 'jjse' )
143
+ catch_source = ' catch ( jjse ) {' << default_catch.apply( error_token ) << '}'
144
+ else
145
+ catch_source = ' catch ( jjse ) { /* do nothing */ }'
146
+ end
147
+ stream = node.token_stream
148
+ i = node.stop_index
149
+ i -= 1 while tk = stream[ i ] and tk.type != RBRACE
150
+ if i >= 0 then node.insert_after!( i, catch_source )
151
+ else node.insert_after!( catch_source )
152
+ end
153
+ end
154
+
155
+
156
+ #
157
+ # ^( ITER ^(CALL $name ^(ARGUMENTS $args ...)) ^(PARAMS $block_parmas ...) ^(BLOCK ...) )
158
+ #
159
+ def on_iter( node )
160
+ descend( node )
161
+ target, args, block = node
162
+
163
+ func = build_function( args, block )
164
+ block.delete!
165
+
166
+ case target.type
167
+ when CALL, NEW
168
+ arg_node = target.last
169
+ if placeholder = arg_node.find { | child | child.type == FUNC_ARG }
170
+ placeholder.replace!( func )
171
+ else
172
+ append_argument( arg_node, func )
173
+ end
174
+ else
175
+ if target.type == ID and target.text == 'eval'
176
+ node.replace!( "(#{ func })()" )
177
+ else
178
+ target.append!( '( ' << func << ' )' )
179
+ end
180
+ end
181
+ end
182
+
183
+ # confirm that the `&' placeholder is occurring under a block call
184
+ def on_func_arg( node )
185
+ arguments_node = node.parent
186
+ arguments_type = arguments_node && arguments_node.type
187
+ unless arguments_type == ARGUMENTS
188
+ raise TranslationError, "BUG: function placeholder argument in an arguments list"
189
+ end
190
+
191
+ call_node = arguments_node.parent
192
+ call_type = call_node && call_node.type
193
+ unless call_type == CALL or call_type == NEW
194
+ raise TranslationError, "BUG: function placeholder argument isn't under a function/new call"
195
+ end
196
+
197
+ iter_node = call_node.parent
198
+ iter_type = iter_node && iter_node.type
199
+ unless iter_type == ITER
200
+ raise TranslationError, "no iteration block corresponding to function placeholder argument"
201
+ end
202
+ end
203
+
204
+ def on_ivar( node )
205
+ node.replace!( node.token, "this." + node.text[ 1..-1 ] )
206
+ end
207
+
208
+ def on_function( node )
209
+ descend( node )
210
+ add_param_parsing( *node.last( 2 ) )
211
+ end
212
+
213
+ def on_set( node )
214
+ descend( node )
215
+
216
+ param_node, body_node = node.last( 2 )
217
+ body_node ||= param_node
218
+ #add_return( body_node )
219
+
220
+ if param_node and param_node.type == PARAMS
221
+ add_param_parsing( param_node, body_node )
222
+ else
223
+ param_node.append!( '()' )
224
+ end
225
+ end
226
+
227
+ def on_get( node )
228
+ descend( node )
229
+
230
+ param_node, body_node = node.last( 2 )
231
+ body_node ||= param_node
232
+ add_return( body_node )
233
+
234
+ # this is a hack to fix double returns that show up in some situations until
235
+ # I figure out exactly why
236
+ node.source
237
+
238
+ if param_node and param_node.type == PARAMS
239
+ add_param_parsing( param_node, body_node )
240
+ else
241
+ param_node.append!( '()' )
242
+ end
243
+ end
244
+
245
+ def on_post_unless( node )
246
+ descend( node )
247
+ error_name, condition = node[ 0 ], node[ 1 ]
248
+ condition.surround!( '!( ', ')' )
249
+ node.replace!( node.token, 'if' )
250
+ end
251
+
252
+ def on_finally( node )
253
+ bottom = top = node
254
+ descent = []
255
+ while n = bottom[ 0 ] and CATCH_TYPES.include?( n.type )
256
+ descent.unshift( *bottom.drop( 1 ) )
257
+ bottom = n
258
+ end
259
+ descent.unshift( *bottom )
260
+
261
+ for child in descent
262
+ enter( child )
263
+ end
264
+
265
+ if FUNCTION_TYPES.include?( descent.first.type )
266
+ function_block = descent.first.last
267
+
268
+ catch_span = bottom.token.index .. top.token_stop_index
269
+ code = node.token_stream.render( catch_span )
270
+ node.delete!( catch_span )
271
+
272
+ surround_block( function_block, "try {", "} " << code )
273
+ end
274
+ end
275
+
276
+ def on_catch( node )
277
+ descend( node )
278
+ ##if node.first.type == FUNCTION
279
+ ## func_node = node.first
280
+ ## catch_range = ( node.first.token_stop_index + 1 .. node.token_stop_index )
281
+ ## catch_source = node.source( catch_range )
282
+ ## node.delete!( catch_range )
283
+ ## require 'pp'
284
+ ## puts func_node.last.source( func_node.last.inner_range )
285
+ ##end
286
+ end
287
+
288
+ def on_arrow( node )
289
+ descend( node )
290
+ name_node = param_node = body_node = nil
291
+ i = 0
292
+
293
+ if node[ i ].type == ID
294
+ name_node, i = node[ i ], i + 1
295
+ end
296
+
297
+ if node[ i ].type == PARAMS
298
+ param_node, i = node[ i ], i + 1
299
+ end
300
+
301
+ body_node = node[ i ]
302
+ add_return( body_node )
303
+ add_param_parsing( param_node, body_node ) unless param_node.nil?
304
+
305
+ if name_node
306
+ if node.token.index > name_node.token.index
307
+ # the name is a property definition ala { a: 3, b ->() { ... } }
308
+ name_node.append!( ':' )
309
+ node.replace!( node.token, param_node ? 'function' : 'function()' )
310
+ else
311
+ # the name is a function name ala ->b() { ... }
312
+ node.replace!( node.token, 'function ' )
313
+ name_node.append!( '()' ) if param_node.nil?
314
+ end
315
+ else
316
+ node.replace!( node.token, param_node ? 'function' : 'function()' )
317
+ end
318
+ end
319
+
320
+ def on_method( node )
321
+ descend( node )
322
+ on_arrow( node )
323
+ node.first.append!( ':' )
324
+ end
325
+
326
+ def on_arguments( node )
327
+ node.empty? and return
328
+ descend( node )
329
+ convert_property_lists( node )
330
+ stream = node.token_stream
331
+ # delete trailing comma
332
+ node.last.token_stop_index.upto( node.token_stop_index ) { | i |
333
+ if stream[ i ].type == COMMA
334
+ stream.delete( i )
335
+ break
336
+ end
337
+ }
338
+ end
339
+
340
+ def on_array( node )
341
+ node.empty? and return
342
+ descend( node )
343
+ convert_property_lists( node )
344
+ end
345
+
346
+
347
+ def on_if( node )
348
+ special_condition( node ) or descend( node )
349
+ end
350
+
351
+ def on_unless( node )
352
+ special_condition( node, true ) and return
353
+ descend( node )
354
+ invert_clause( node.first )
355
+ node.replace!( node.token, 'if' )
356
+ end
357
+
358
+ def on_until( node )
359
+ descend( node )
360
+ invert_clause( node.first )
361
+ node.replace!( node.token, 'while' )
362
+ end
363
+
364
+ def on_colon( node )
365
+ descend( node )
366
+ if key = node[ 0 ] and key.type == ID
367
+ key.replace!( key.text.to_json )
368
+ end
369
+ end
370
+
371
+ def on_string( node )
372
+ literal = node.text or return
373
+ quote = literal[ 0, 1 ]
374
+ body = literal[ 1, literal.length - 2 ]
375
+ node.replace!( @manager.jstring( body, quote == "'" ) )
376
+ end
377
+
378
+ def on_doc( node )
379
+ literal = node.text or return
380
+ body = @manager.outdent( literal[ 1, literal.length - 2 ] )
381
+ value = @manager.string_value( body )
382
+ node.replace!( value.to_json )
383
+ end
384
+
385
+ def on_dstring( node )
386
+ data = Blob.extract( node.token, DSTRING )
387
+ node.replace!( @manager.interpolate( data, false ) )
388
+ end
389
+
390
+ def on_ddoc( node )
391
+ data = Blob.extract( node.token, DDOC )
392
+ node.replace!( @manager.interpolate( data, true ) )
393
+ end
394
+
395
+ def on_general( node )
396
+ token = node.token
397
+ data = Blob.extract( token )
398
+ node.replace!( convert_blob( data ) )
399
+ end
400
+
401
+ def on_or_asgn( node )
402
+ descend( node )
403
+ left, right = node
404
+ target, value = left.source, right.source
405
+
406
+ right.type == QMARK and value = "( #{ value } )"
407
+ node.replace!( "#{ target } = #{ target } || #{ value }" )
408
+ end
409
+
410
+ def on_ejs( node )
411
+ ejs = extract_ejs( node.token )
412
+ node.replace!( ejs.as_function )
413
+ end
414
+
415
+ def on_is_undefined( node )
416
+ descend( node )
417
+ expr = %[('undefined' == typeof( #{ node.last.source } ))]
418
+ node.replace!( expr )
419
+ end
420
+
421
+ def on_is_defined( node )
422
+ descend( node )
423
+ expr = %[('undefined' != typeof( #{ node.last.source } ))]
424
+ node.replace!( expr )
425
+ end
426
+
427
+ def on_directive( node )
428
+ token = node.token
429
+ file = token.source_name
430
+
431
+ current_line = token.line
432
+ after_line = current_line + token.text.count( "\n" )
433
+
434
+ src = scanner( node.token, 2 )
435
+ src.space!
436
+
437
+ command = src.id! or raise( "malformed directive `#{ node.token.text }'" )
438
+
439
+ case command
440
+ when 'hide'
441
+ node.replace!( '' )
442
+ when 'require', 'include'
443
+ targets = []
444
+ src.space!
445
+
446
+ case c = src.peek( 1 )
447
+ when '<', '[', '(', '{'
448
+ content = src.nested!( c, DELIMS[ c ] )
449
+ targets.concat( @manager.split_words( content[ 1 ... -1 ] ) )
450
+ when '"', '`', "'"
451
+ content = src.string!
452
+ targets << @manager.string_value( content[ 1 ... -1 ], content[ 0, 1 ] == "'" )
453
+ else
454
+ content = src.chunk!
455
+ targets << content.gsub( /\\(.)/m, '\1' )
456
+ end
457
+
458
+ repl = ''
459
+ method = @manager.method( "#{ command }!" )
460
+ targets.inject( repl ) do | repl, lib |
461
+ @manager.location_markers? and repl << "// #{ lib }:1\n"
462
+ repl << method.call( lib ).to_s << "\n"
463
+ @manager.location_markers? ? repl << "// #{ file }:#{ after_line }\n" : repl
464
+ end
465
+
466
+ node.replace!( repl )
467
+ end
468
+ end
469
+
470
+
471
+ def translate( node )
472
+ node.infer_boundaries
473
+ enter( node )
474
+ #debug_rewrite( node.token_stream )
475
+ #puts '============'
476
+ #node.rewrite.reduce
477
+ #debug_rewrite( node.token_stream )
478
+ return node.rewrite.execute
479
+ #rescue ANTLR3::Bug
480
+ # debug_rewrite( node.token_stream )
481
+ # raise
482
+ ensure
483
+ node.token_stream.delete_program
484
+ end
485
+
486
+ private
487
+
488
+ def special_condition( node, invert = false )
489
+ clause = node.first
490
+ if clause.type == ID and BROWSERS.include?( clause.text )
491
+ if @manager.browser == clause.text
492
+ include_node = node[ invert ? 2 : 1 ]
493
+ else
494
+ include_node = node[ invert ? 1 : 2 ]
495
+ end
496
+
497
+ if include_node
498
+ descend( include_node )
499
+ replacement = include_node.source
500
+ replacement.gsub!( /\A\{\s*|\s*\}\z/, '' )
501
+ else
502
+ replacement = ''
503
+ end
504
+ node.replace!( replacement )
505
+ #program = node.token_stream.program
506
+ #range = node.token_range
507
+
508
+ #program.instance_variable_get( :@operations ).delete_if do | i |
509
+ # i.covered_by?( range )
510
+ #end
511
+ return true
512
+ end
513
+
514
+ return nil
515
+ end
516
+
517
+ def add_function_arg( arg_node, func )
518
+ if placeholder = arg_node.find { | child | child.type == FUNC_ARG }
519
+ placeholder.replace!( func )
520
+ else
521
+ append_argument( arg_node, func )
522
+ end
523
+ end
524
+
525
+ def scanner( token, pos = nil )
526
+ SourceScanner.new( token.text,
527
+ :line => token.line,
528
+ :column => token.column,
529
+ :file => token.source_name,
530
+ :position => pos
531
+ )
532
+ end
533
+
534
+ def convert_blob( blob )
535
+ handler_for( blob ).process( @manager, blob )
536
+ end
537
+
538
+ def extract_ejs( ejs_token )
539
+ name = nil
540
+ parameters = nil
541
+ src = scanner( ejs_token, 3 ) # skip leading `ejs'
542
+ src.space!( true )
543
+ name = src.id!
544
+ src.space!( true )
545
+
546
+ if src.see?( '(' )
547
+ arg_tokens = []
548
+ src.lexer do | lex |
549
+ arg_tokens << lex.next_token
550
+ depth = 1
551
+ lex.each do | token |
552
+ arg_tokens << token
553
+ case token.type
554
+ when LPAREN
555
+ depth += 1
556
+ when RPAREN
557
+ depth -= 1
558
+ depth.zero? and break
559
+ end
560
+ end
561
+ end
562
+
563
+ arg_tokens = RewriteStream.new( arg_tokens )
564
+ adaptor = RewriteAdaptor.new( arg_tokens )
565
+ tree = Jejune::Parser.new( arg_tokens, :adaptor => adaptor ).function_parameters.tree
566
+ parameters = ParameterSet.study( tree )
567
+ end
568
+
569
+ src.space!( true )
570
+ src.scan( /%/ )
571
+ body = @manager.outdent( src.rest[ 1 ... -1 ] )
572
+
573
+ EJJS.new( body,
574
+ :name => name, :parameters => parameters,
575
+ :file => ejs_token.source_name,
576
+ :line => src.line, :column => src.column
577
+ )
578
+ end
579
+
580
+
581
+ def convert_property_lists( node )
582
+ property_definition?( node.first ) and node.first.prepend!( '{ ' )
583
+
584
+ node.each_cons( 2 ) do | left, right |
585
+ t1, t2 = property_definition?( left ), property_definition?( right )
586
+
587
+ if !t1 and t2
588
+ right.prepend!( '{ ' )
589
+ elsif t1 and !t2
590
+ left.append!( ' }' )
591
+ end
592
+ end
593
+
594
+ property_definition?( node.last ) and node.last.replace!( node.last.source << ' }' )
595
+ end
596
+
597
+ def build_function( args, block, name = nil )
598
+ block.text == '{' and add_return( block )
599
+
600
+ block.strip! # get rid of surrounding { } / do end
601
+
602
+ if args
603
+ params = ParameterSet.study( args )
604
+ unless args.stop_index < args.start_index
605
+ # trash the | x, ... | declaration unless there isn't one
606
+ # note: if there's only an imaginary PARAMs, for some unknown reason,
607
+ # calling args.delete! will insert everything between the open brace
608
+ # and the first token of the first statement
609
+ args.delete!
610
+ end
611
+ else
612
+ params = ParameterSet.new();
613
+ end
614
+
615
+ body = params.parsing_source << block.source
616
+ body.strip!
617
+ if body =~ /\n/
618
+ body = "\n" << body << "\n"
619
+ else
620
+ body = " " << body << " "
621
+ end
622
+
623
+ prefix = name ? "function #{ name }" : "function"
624
+
625
+ "#{ prefix }#{ params.declaration } {#{ body }}"
626
+ end
627
+
628
+ def append_argument( node, source )
629
+ if node.empty?
630
+ tk = node.start
631
+ source = " #{ source } "
632
+ else
633
+ tk = node.last.stop
634
+ source = ", #{ source } "
635
+ end
636
+ node.insert_after!( tk, source )
637
+ end
638
+
639
+ def add_return( block )
640
+ if last_statement = block.last and expression?( last_statement )
641
+ term = last_statement.stop
642
+ term.type == SEMI and last_statement.delete!( term )
643
+ last_statement.surround!( 'return( ', ' );' )
644
+ end
645
+ end
646
+
647
+ def invert_clause( clause )
648
+ clause.surround!( '( !', ' )' )
649
+ end
650
+
651
+ def add_param_parsing( param_node, body_node )
652
+ if params = ParameterSet.study( param_node )
653
+ param_node.replace!( params.declaration )
654
+ surround_block( body_node, params.parsing_source )
655
+ end
656
+ end
657
+
658
+ def surround_block( block_node, before = nil, after = nil )
659
+ tokens = block_node.tokens
660
+ if before
661
+ if n = block_node.first
662
+ n.prepend!( before )
663
+ elsif t = tokens.find { | t | t.type == LBRACE }
664
+ block_node.append!( t, before )
665
+ end
666
+ end
667
+ if after
668
+ if t = tokens.reverse_each.find { | t | t.type == RBRACE }
669
+ block_node.prepend!( t, after )
670
+ elsif n = block_node.last
671
+ n.append!( after )
672
+ end
673
+ end
674
+ return block_node
675
+ end
676
+ end
677
+
678
+
679
+
680
+ ClassBuilder = Struct.new( :name, :constructor, :members );
681
+ class ClassBuilder
682
+ include Constants
683
+
684
+ def initialize( node )
685
+ super( nil, nil, [] );
686
+ study( *node );
687
+ end
688
+
689
+ def study( name, *members );
690
+ self.name = name.text;
691
+ members.each do | member |
692
+ case member.type
693
+ when DEF
694
+ if member.first.text == 'initialize'
695
+ self.constructor = member
696
+ else
697
+ self.members.push( member );
698
+ end
699
+ else
700
+ self.members.push( member );
701
+ end
702
+ end
703
+ return self
704
+ end
705
+
706
+
707
+ end
708
+ end
709
+
710
+