jejune 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gemtest +0 -0
- data/History.txt +12 -0
- data/Manifest.txt +39 -0
- data/README.txt +55 -0
- data/Rakefile +75 -0
- data/bin/jjs +173 -0
- data/lib/jejune.rb +78 -0
- data/lib/jejune/boot.rb +107 -0
- data/lib/jejune/constants.rb +105 -0
- data/lib/jejune/data-extension.rb +144 -0
- data/lib/jejune/dependency-scanner.rb +69 -0
- data/lib/jejune/ejjs.rb +178 -0
- data/lib/jejune/errors.rb +53 -0
- data/lib/jejune/grammar.rb +32 -0
- data/lib/jejune/grammar/JavaScript.g +668 -0
- data/lib/jejune/grammar/Jejune.g +1029 -0
- data/lib/jejune/grammar/Jejune.tokens +241 -0
- data/lib/jejune/grammar/lexer.rb +6504 -0
- data/lib/jejune/grammar/parser.rb +17378 -0
- data/lib/jejune/grammar/rakefile +29 -0
- data/lib/jejune/grammar/tree.rb +6737 -0
- data/lib/jejune/input.rb +124 -0
- data/lib/jejune/jstring.rb +163 -0
- data/lib/jejune/lo-fi-lexer.rb +633 -0
- data/lib/jejune/macro.rb +78 -0
- data/lib/jejune/main.rb +289 -0
- data/lib/jejune/manager.rb +333 -0
- data/lib/jejune/node-test.rb +71 -0
- data/lib/jejune/parameters.rb +83 -0
- data/lib/jejune/rewrite-debug.rb +61 -0
- data/lib/jejune/rewrite.rb +125 -0
- data/lib/jejune/scanner.rb +201 -0
- data/lib/jejune/translator.rb +710 -0
- data/lib/jejune/tree-walker.rb +81 -0
- data/lib/jejune/utils.rb +81 -0
- data/lib/jejune/version.rb +38 -0
- data/spec/samples.txt +51 -0
- data/spec/translation.rb +69 -0
- data/spec/utils.rb +63 -0
- data/tools/env.fish +2 -0
- metadata +147 -0
@@ -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
|
+
|