jejune 1.1.0
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.
- 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
|
+
|