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
data/lib/jejune/macro.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# author: Kyle Yetter
|
5
|
+
#
|
6
|
+
|
7
|
+
module Jejune
|
8
|
+
class Macro
|
9
|
+
include Constants
|
10
|
+
|
11
|
+
attr_accessor :manager, :name, :parameters, :blueprint
|
12
|
+
|
13
|
+
def initialize( manager, tree )
|
14
|
+
@manager = manager
|
15
|
+
#@tree = tree
|
16
|
+
@name = tree[ 0 ].text
|
17
|
+
|
18
|
+
if node = tree[ 1 ] and node.type == PARAMS
|
19
|
+
@parameters = ParameterSet.study( node )
|
20
|
+
else
|
21
|
+
@parameters = ParameterSet.new
|
22
|
+
end
|
23
|
+
|
24
|
+
body_tokens = tree.last.tokens
|
25
|
+
|
26
|
+
body_tokens.shift while t = body_tokens.first and t.type != LBRACE
|
27
|
+
body_tokens.shift
|
28
|
+
|
29
|
+
body_tokens.pop while t = body_tokens.last and t.type != RBRACE
|
30
|
+
body_tokens.pop
|
31
|
+
|
32
|
+
body_tokens.map! do | token |
|
33
|
+
if token.type == ID and index = @parameters.include?( token.text )
|
34
|
+
index
|
35
|
+
else
|
36
|
+
token.text
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@blueprint = body_tokens
|
41
|
+
end
|
42
|
+
|
43
|
+
def self._load( serialized )
|
44
|
+
macro = allocate
|
45
|
+
macro.name, macro.parameters, macro.blueprint = Marshal.load( serialized )
|
46
|
+
return macro
|
47
|
+
end
|
48
|
+
|
49
|
+
def _dump( depth )
|
50
|
+
Marshal.dump( [ @name, @parameters, @blueprint ] )
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply( *params )
|
54
|
+
params =
|
55
|
+
params.map do | i |
|
56
|
+
if i.respond_to?( :tokens ) then i.tokens
|
57
|
+
elsif TokenData::Token === i then [ i ]
|
58
|
+
else i
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
tokens = []
|
63
|
+
for item in @blueprint
|
64
|
+
case item
|
65
|
+
when Fixnum
|
66
|
+
tokens.concat( params[ item ] )
|
67
|
+
else
|
68
|
+
tokens << item
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
@manager.translate( tokens.join( '' ) )
|
73
|
+
end
|
74
|
+
|
75
|
+
alias expand apply
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/jejune/main.rb
ADDED
@@ -0,0 +1,289 @@
|
|
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
|
+
require 'optparse'
|
27
|
+
require 'jejune'
|
28
|
+
|
29
|
+
module Jejune
|
30
|
+
|
31
|
+
module Options
|
32
|
+
# the input encoding type; defaults to +nil+ (currently, not used)
|
33
|
+
attr_accessor :encoding
|
34
|
+
# the input stream used by the Main script; defaults to <tt>$stdin</tt>
|
35
|
+
attr_accessor :input
|
36
|
+
# a boolean flag indicating whether or not to run the Main
|
37
|
+
# script in interactive mode; defaults to +false+
|
38
|
+
attr_accessor :interactive
|
39
|
+
attr_accessor :no_output
|
40
|
+
attr_accessor :profile
|
41
|
+
attr_accessor :debug_socket
|
42
|
+
attr_accessor :ruby_prof
|
43
|
+
|
44
|
+
def initialize( options = {} )
|
45
|
+
@no_output = options.fetch( :no_output, false )
|
46
|
+
@profile = options.fetch( :profile, false )
|
47
|
+
@debug_socket = options.fetch( :debug_socket, false )
|
48
|
+
@ruby_prof = options.fetch( :ruby_prof, false )
|
49
|
+
@encoding = options.fetch( :encoding, nil )
|
50
|
+
@interactive = options.fetch( :interactive, false )
|
51
|
+
@input = options.fetch( :input, $stdin )
|
52
|
+
end
|
53
|
+
|
54
|
+
# constructs an OptionParser and parses the argument list provided by +argv+
|
55
|
+
def parse_options( argv = ARGV )
|
56
|
+
oparser = OptionParser.new do | o |
|
57
|
+
o.separator 'Input Options:'
|
58
|
+
|
59
|
+
o.on( '-i', '--input "text to process"', doc( <<-END ) ) { |val| @input = val }
|
60
|
+
| a string to use as direct input to the recognizer
|
61
|
+
END
|
62
|
+
|
63
|
+
o.on( '-I', '--interactive', doc( <<-END ) ) { @interactive = true }
|
64
|
+
| run an interactive session with the recognizer
|
65
|
+
END
|
66
|
+
end
|
67
|
+
|
68
|
+
setup_options( oparser )
|
69
|
+
return oparser.parse( argv )
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def setup_options( oparser )
|
75
|
+
# overridable hook to modify / append options
|
76
|
+
end
|
77
|
+
|
78
|
+
def doc( description_string )
|
79
|
+
description_string.chomp!
|
80
|
+
description_string.gsub!( /^ *\| ?/, '' )
|
81
|
+
description_string.gsub!( /\s+/, ' ' )
|
82
|
+
return description_string
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
=begin rdoc ANTLR3::Main::Main
|
88
|
+
|
89
|
+
The base-class for the three primary Main script-runner classes.
|
90
|
+
It defines the skeletal structure shared by all main
|
91
|
+
scripts, but isn't particularly useful on its own.
|
92
|
+
|
93
|
+
=end
|
94
|
+
|
95
|
+
class Main
|
96
|
+
include Options
|
97
|
+
include Util
|
98
|
+
attr_accessor :output, :error
|
99
|
+
|
100
|
+
def initialize( options = {} )
|
101
|
+
@input = options.fetch( :input, $stdin )
|
102
|
+
@output = options.fetch( :output, $stdout )
|
103
|
+
@error = options.fetch( :error, $stderr )
|
104
|
+
@name = options.fetch( :name, File.basename( $0, '.rb' ) )
|
105
|
+
super
|
106
|
+
block_given? and yield( self )
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# runs the script
|
111
|
+
def execute( argv = ARGV )
|
112
|
+
args = parse_options( argv )
|
113
|
+
setup
|
114
|
+
|
115
|
+
@interactive and return execute_interactive
|
116
|
+
|
117
|
+
in_stream =
|
118
|
+
case
|
119
|
+
when @input.is_a?( ::String ) then StringStream.new( @input )
|
120
|
+
when args.length == 1 && args.first != '-'
|
121
|
+
ANTLR3::FileStream.new( args[ 0 ] )
|
122
|
+
else ANTLR3::FileStream.new( @input )
|
123
|
+
end
|
124
|
+
case
|
125
|
+
when @ruby_prof
|
126
|
+
load_ruby_prof
|
127
|
+
profile = RubyProf.profile do
|
128
|
+
recognize( in_stream )
|
129
|
+
end
|
130
|
+
printer = RubyProf::FlatPrinter.new( profile )
|
131
|
+
printer.print( @output )
|
132
|
+
when @profile
|
133
|
+
require 'profiler'
|
134
|
+
Profiler__.start_profile
|
135
|
+
recognize( in_stream )
|
136
|
+
Profiler__.print_profile
|
137
|
+
else
|
138
|
+
recognize( in_stream )
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def recognize( *args )
|
145
|
+
# overriden by subclasses
|
146
|
+
end
|
147
|
+
|
148
|
+
def execute_interactive
|
149
|
+
@output.puts( tidy( <<-END ) )
|
150
|
+
| ===================================================================
|
151
|
+
| Ruby ANTLR Console for #{ $0 }
|
152
|
+
| ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
153
|
+
| * Enter source code lines
|
154
|
+
| * Enter EOF to finish up and exit
|
155
|
+
| (control+D on Mac/Linux/Unix or control+Z on Windows)
|
156
|
+
| ===================================================================
|
157
|
+
|
|
158
|
+
END
|
159
|
+
|
160
|
+
read_method =
|
161
|
+
begin
|
162
|
+
require 'readline'
|
163
|
+
line_number = 0
|
164
|
+
lambda do
|
165
|
+
begin
|
166
|
+
if line = Readline.readline( "#@name:#{ line_number += 1 }> ", true )
|
167
|
+
line << $/
|
168
|
+
else
|
169
|
+
@output.print( "\n" ) # ensures result output is on a new line after EOF is entered
|
170
|
+
nil
|
171
|
+
end
|
172
|
+
rescue Interrupt, EOFError
|
173
|
+
retry
|
174
|
+
end
|
175
|
+
line << "\n" if line
|
176
|
+
end
|
177
|
+
|
178
|
+
rescue LoadError
|
179
|
+
lambda do
|
180
|
+
begin
|
181
|
+
printf( "%s:%i> ", @name, @input.lineno )
|
182
|
+
flush
|
183
|
+
line = @input.gets or
|
184
|
+
@output.print( "\n" ) # ensures result output is on a new line after EOF is entered
|
185
|
+
line
|
186
|
+
rescue Interrupt, EOFError
|
187
|
+
retry
|
188
|
+
end
|
189
|
+
line
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
stream = InteractiveStringStream.new( :name => @name, &read_method )
|
194
|
+
recognize( stream )
|
195
|
+
end
|
196
|
+
|
197
|
+
def screen_width
|
198
|
+
( ENV[ 'COLUMNS' ] || 80 ).to_i
|
199
|
+
end
|
200
|
+
|
201
|
+
def attempt( lib, message = nil, exit_status = nil )
|
202
|
+
yield
|
203
|
+
rescue LoadError => error
|
204
|
+
message or raise
|
205
|
+
@error.puts( message )
|
206
|
+
report_error( error )
|
207
|
+
report_load_path
|
208
|
+
exit( exit_status ) if exit_status
|
209
|
+
rescue => error
|
210
|
+
@error.puts( "received an error while attempting to load %p" % lib )
|
211
|
+
report_error( error )
|
212
|
+
exit( exit_status ) if exit_status
|
213
|
+
end
|
214
|
+
|
215
|
+
def report_error( error )
|
216
|
+
puts!( "~ error details:" )
|
217
|
+
puts!( ' [ %s ]' % error.class.name )
|
218
|
+
message = error.to_s.gsub( /\n/, "\n " )
|
219
|
+
puts!( ' -> ' << message )
|
220
|
+
for call in error.backtrace
|
221
|
+
puts!( ' ' << call )
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def report_load_path
|
226
|
+
puts!( "~ content of $LOAD_PATH: " )
|
227
|
+
for dir in $LOAD_PATH
|
228
|
+
puts!( " - #{ dir }" )
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def setup
|
233
|
+
# hook
|
234
|
+
end
|
235
|
+
|
236
|
+
def fetch_class( name )
|
237
|
+
name.nil? || name.empty? and return( nil )
|
238
|
+
unless constant_exists?( name )
|
239
|
+
try_to_load( name )
|
240
|
+
constant_exists?( name ) or return( nil )
|
241
|
+
end
|
242
|
+
|
243
|
+
name.split( /::/ ).inject( Object ) do |mod, name|
|
244
|
+
# ::SomeModule splits to ['', 'SomeModule'] - so ignore empty strings
|
245
|
+
name.empty? and next( mod )
|
246
|
+
mod.const_get( name )
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def constant_exists?( name )
|
251
|
+
eval( "defined?(#{ name })" ) == 'constant'
|
252
|
+
end
|
253
|
+
|
254
|
+
def try_to_load( name )
|
255
|
+
if name =~ /(\w+)::(Lexer|Parser|TreeParser)$/
|
256
|
+
retry_ok = true
|
257
|
+
module_name, recognizer_type = $1, $2
|
258
|
+
script = name.gsub( /::/, '' )
|
259
|
+
begin
|
260
|
+
return( require( script ) )
|
261
|
+
rescue LoadError
|
262
|
+
if retry_ok
|
263
|
+
script, retry_ok = module_name, false
|
264
|
+
retry
|
265
|
+
else
|
266
|
+
return( nil )
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
%w(puts print printf flush).each do |method|
|
273
|
+
class_eval( <<-END, __FILE__, __LINE__ )
|
274
|
+
private
|
275
|
+
|
276
|
+
def #{ method }(*args)
|
277
|
+
@output.#{ method }(*args) unless @no_output
|
278
|
+
end
|
279
|
+
|
280
|
+
def #{ method }!( *args )
|
281
|
+
@error.#{ method }(*args) unless @no_output
|
282
|
+
end
|
283
|
+
END
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
end
|
@@ -0,0 +1,333 @@
|
|
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 Manager
|
28
|
+
include Constants
|
29
|
+
include Utils
|
30
|
+
include JString
|
31
|
+
|
32
|
+
attr_reader :load_path, :input, :translator, :data_directory, :macros, :dependency_map
|
33
|
+
attr_accessor :browser, :location_markers, :verbose, :root_input
|
34
|
+
|
35
|
+
alias location_markers? location_markers
|
36
|
+
private :location_markers
|
37
|
+
|
38
|
+
def initialize( *args )
|
39
|
+
options = {}
|
40
|
+
args.last.is_a?( Hash ) and options.update( args.pop )
|
41
|
+
@root_input = args.shift
|
42
|
+
|
43
|
+
@data_directory = File.expand_path( '~/.jejune' )
|
44
|
+
test( ?d, @data_directory ) or Dir.mkdir( @data_directory )
|
45
|
+
@cache_directory = File.join( @data_directory, 'cache' )
|
46
|
+
test( ?d, @cache_directory ) or Dir.mkdir( @cache_directory )
|
47
|
+
|
48
|
+
@input = nil
|
49
|
+
@input_stack = []
|
50
|
+
|
51
|
+
@load_path = options.fetch( :load_path, [] )
|
52
|
+
@load_path = [ @load_path ].flatten
|
53
|
+
@load_path.compact!
|
54
|
+
|
55
|
+
@loaded_files = {}
|
56
|
+
@translator = Translator.new( self )
|
57
|
+
@browser = options[ :browser ]
|
58
|
+
@location_markers = options.fetch( :location_markers, true )
|
59
|
+
@macros = options.fetch( :macros, {} )
|
60
|
+
@verbose = options.fetch( :verbose, false )
|
61
|
+
@dependency_map = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def define_macro( node )
|
66
|
+
macro = Macro.new( self, node )
|
67
|
+
@macros[ macro.name ] = macro
|
68
|
+
@input and @input.macros[ macro.name ] = macro
|
69
|
+
return macro
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_to_path( *directories )
|
73
|
+
directories = [ directories ].flatten!.map! { | i | i.to_s }
|
74
|
+
@load_path.unshift( *directories )
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_root_file( file, options = {} )
|
78
|
+
options[ :manager ] = self
|
79
|
+
@root_input = JJSFile.new( file, options )
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_root_source( source, options = {} )
|
83
|
+
options[ :manager ] = self
|
84
|
+
@root_input = JJSSource.new( source, options )
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_path( name, exts = DEFAULT_EXT )
|
88
|
+
find_in_path_list( @load_path, name, exts )
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_resource( name, ext, system = false )
|
92
|
+
unless path = system ? find_path( name, ext ) : find_relative( name, ext )
|
93
|
+
raise( "cannot locate file `#{ name }' (exts = #{ ext })" )
|
94
|
+
end
|
95
|
+
return path
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_relative( name, exts = DEFAULT_EXT )
|
99
|
+
find_in_directory( @input ? @input.directory : File.expand_path( '.' ), name, exts )
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_library( name, system = false )
|
103
|
+
unless path = system ? find_path( name ) : find_relative( name )
|
104
|
+
raise( "cannot locate file `#{ name }'" )
|
105
|
+
end
|
106
|
+
return path
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def cache_file( path )
|
111
|
+
id = MD5.hexdigest( File.expand_path( path ) << "\0" << @browser.to_s )
|
112
|
+
File.join( @cache_directory, id )
|
113
|
+
end
|
114
|
+
|
115
|
+
def loaded?( path )
|
116
|
+
@loaded_files.has_key?( File.expand_path( path ) )
|
117
|
+
end
|
118
|
+
|
119
|
+
def load_file( path )
|
120
|
+
#$stderr.puts( "- loading #{ path } #{ File.basename( cache_file( File.expand_path( path ) ) ) }" )
|
121
|
+
log( "cache check - #{ path }", 34 )
|
122
|
+
full_path = File.expand_path( path )
|
123
|
+
add_dependency( full_path )
|
124
|
+
|
125
|
+
@loaded_files.fetch( full_path ) do
|
126
|
+
cache = cache_file( full_path )
|
127
|
+
output = nil
|
128
|
+
|
129
|
+
begin
|
130
|
+
if test( ?f, cache ) and test( ?>, cache, path )
|
131
|
+
data = Marshal.load( File.read( cache ) )
|
132
|
+
for depend in data[ :dependencies ]
|
133
|
+
unless test( ?f, depend ) and test( ?>, cache, depend )
|
134
|
+
#$stderr.puts( " old < #{ depend }" )
|
135
|
+
raise "no cache"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
data[ :macros ].each_value { | macro | macro.manager = self }
|
140
|
+
@macros.update( data[ :macros ] )
|
141
|
+
|
142
|
+
return( @loaded_files[ full_path ] = data[ :output ] )
|
143
|
+
end
|
144
|
+
rescue => e
|
145
|
+
#$stderr.puts( e.inspect )
|
146
|
+
# ignore
|
147
|
+
end
|
148
|
+
|
149
|
+
@loaded_files[ full_path ] = JJSFile.new( path, :manager => self, :cache_file => cache )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def require!( lib, system = lib !~ /^[\.\/\~]/ )
|
154
|
+
path = find_library( lib, system )
|
155
|
+
@input and @input.dependencies.add( File.expand_path( path ) )
|
156
|
+
log( "require! #{ path.inspect }" )
|
157
|
+
jejune_file?( path ) ? loaded?( path ) ? nil : load_file( path ) : File.read( path )
|
158
|
+
end
|
159
|
+
|
160
|
+
def include!( lib )
|
161
|
+
require!( lib, false )
|
162
|
+
end
|
163
|
+
|
164
|
+
def load!( lib, system = false )
|
165
|
+
path = find_library( lib, system )
|
166
|
+
@input and @input.dependencies.add( File.expand_path( path ) )
|
167
|
+
log( "load! #{ path.inspect }" )
|
168
|
+
jejune_file?( path ) ? load_file( path ) : File.read( path )
|
169
|
+
end
|
170
|
+
|
171
|
+
def expand_macro( name, *parameters )
|
172
|
+
macro = @macros[ name.to_s ] or raise MacroNotFound.new( name.to_s )
|
173
|
+
macro.expand( *parameters )
|
174
|
+
end
|
175
|
+
|
176
|
+
def translate( input = @root_input, options = nil )
|
177
|
+
case input
|
178
|
+
when RewriteTree
|
179
|
+
@translator.translate( input )
|
180
|
+
when JJSInput
|
181
|
+
log( "translating #{ input.path }" )
|
182
|
+
with_input( input ) do
|
183
|
+
@translator.translate( input.tree )
|
184
|
+
end
|
185
|
+
when IO, ARGF
|
186
|
+
options ||= {}
|
187
|
+
options[ :manager ] = self
|
188
|
+
input = JJSFile.new( input, options )
|
189
|
+
translate( input )
|
190
|
+
when String
|
191
|
+
options ||= {}
|
192
|
+
options[ :manager ] = self
|
193
|
+
input = JJSSource.new( input, options )
|
194
|
+
translate( input )
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def compress( source, type = 'js' )
|
199
|
+
command = %W( yui-compressor --type #{ type } ).join( ' ' )
|
200
|
+
Open3.popen3( command ) do | inp, out, err |
|
201
|
+
inp.write source
|
202
|
+
inp.close
|
203
|
+
return out.read
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def parse( tokens, *args )
|
208
|
+
options = args.last.is_a?( Hash ) ? args.pop : {}
|
209
|
+
rule = args.pop || options[ :rule ] || :program
|
210
|
+
|
211
|
+
stream = RewriteStream.new( tokens, options )
|
212
|
+
adaptor = RewriteAdaptor.new( stream )
|
213
|
+
parser = Parser.new( stream, :adaptor => adaptor )
|
214
|
+
parser.send( rule ).tree
|
215
|
+
end
|
216
|
+
|
217
|
+
def commit_dependencies( file, deps )
|
218
|
+
@dependency_map[ file ] = deps.to_a
|
219
|
+
end
|
220
|
+
|
221
|
+
def with_input( input )
|
222
|
+
@input_stack.push( @input ) if @input
|
223
|
+
@input = input
|
224
|
+
yield self
|
225
|
+
ensure
|
226
|
+
@input = @input_stack.pop
|
227
|
+
end
|
228
|
+
|
229
|
+
def add_dependency( path )
|
230
|
+
full_path = File.expand_path( path )
|
231
|
+
@input and @input.dependencies.add( full_path )
|
232
|
+
@input_stack.each { | f | f.dependencies.add( full_path ) }
|
233
|
+
end
|
234
|
+
|
235
|
+
def interpolate( blob, outdent = false )
|
236
|
+
segments = []
|
237
|
+
|
238
|
+
s = SourceScanner.new(
|
239
|
+
blob.data, :file => blob.file, :line => blob.line, :column => blob.data_column
|
240
|
+
)
|
241
|
+
indent = nil
|
242
|
+
margin = true
|
243
|
+
nlines = 0
|
244
|
+
|
245
|
+
until s.eos?
|
246
|
+
case
|
247
|
+
when s.scan( /#(?:(\{)|([@%\$][\$\w]*))/ )
|
248
|
+
if s[ 1 ]
|
249
|
+
tks = s.lexer { | lx | lx.tokenize_until_balanced( LBRACE, RBRACE ) }
|
250
|
+
tree = parse( tks, :source_name => blob.file )
|
251
|
+
segments << [ ?c, translate( tree ) ]
|
252
|
+
elsif variable = s[ 2 ]
|
253
|
+
case variable[ 0 ]
|
254
|
+
when ?$
|
255
|
+
segments << [ ?c, variable ]
|
256
|
+
when ?@
|
257
|
+
variable.length > 1 or raise( "syntax error -- expected variable name: `#{ s.matched }'" )
|
258
|
+
segments << [ ?c, 'this.' << variable[ 1, variable.length ] ]
|
259
|
+
when ?%
|
260
|
+
variable.length > 1 or raise( "syntax error -- expected variable name: `#{ s.matched }'" )
|
261
|
+
segments << [ ?c, variable[ 1, variable.length ] ]
|
262
|
+
else
|
263
|
+
raise( "BUG: this shouldn't happen" )
|
264
|
+
end
|
265
|
+
else
|
266
|
+
raise( "BUG: this shouldn't happen" )
|
267
|
+
end
|
268
|
+
when s.scan( /\r?\n(?:\s*\r?\n)* / )
|
269
|
+
nl = s.matched
|
270
|
+
nlines += nl.count( "\n" )
|
271
|
+
|
272
|
+
if last = segments.last and last[ 0 ] == ?s
|
273
|
+
last[ 1 ] << nl
|
274
|
+
else
|
275
|
+
segments << [ ?s, nl ]
|
276
|
+
end
|
277
|
+
|
278
|
+
if leading_space = s.check( /^\s*(?=(\S))/ )
|
279
|
+
margin &&= ( s[ 1 ] == '|' )
|
280
|
+
n = expand_tabs( leading_space ).length
|
281
|
+
indent = indent ? n < indent ? n : indent : n
|
282
|
+
end
|
283
|
+
when s.scan( %r<(?: [^\\\r\n\#]+ | (?: \\ \\ )+ | \\ \r? \n | \\ . | \# (?! [\{\$@%] ))+>mx )
|
284
|
+
if last = segments.last and last[ 0 ] == ?s
|
285
|
+
last[ 1 ] << s.matched
|
286
|
+
else
|
287
|
+
segments << [ ?s, s.matched ]
|
288
|
+
end
|
289
|
+
else
|
290
|
+
msg = "BUG: this shouldn't happen!\n"
|
291
|
+
msg << ( '= ' * 30 ) << "\n"
|
292
|
+
msg << blob.inspect << "\n"
|
293
|
+
msg << ( '= ' * 30 ) << "\n"
|
294
|
+
msg << s.inspect
|
295
|
+
raise( msg )
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
if outdent and nlines > 0
|
300
|
+
if first = segments.first and first[ 0 ] == ?s
|
301
|
+
first[ 1 ].sub!( /\A[ \t]*\r?\n/, '' )
|
302
|
+
first.empty? and segments.shift
|
303
|
+
end
|
304
|
+
if last = segments.last and last[ 0 ] == ?s
|
305
|
+
last[ 1 ].sub!( /\r?\n[ \t]*\z/, '' )
|
306
|
+
last.empty? and segments.pop
|
307
|
+
end
|
308
|
+
|
309
|
+
for type, text in segments
|
310
|
+
if type == ?s
|
311
|
+
margin ? text.gsub!( /(\r?\n)[ \t]*\|[ \t]?/, '\1' ) :
|
312
|
+
text.gsub!( /(\r?\n)([ \t]*)/ ) { $1 << $2[ indent, $2.length ].to_s }
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
case segments.length
|
318
|
+
when 0 then %q("")
|
319
|
+
when 1
|
320
|
+
type, text = segments.first
|
321
|
+
type == ?s ? jstring( text ) : "(#{ text }).toString()"
|
322
|
+
else
|
323
|
+
segments.map! { | type, text | type == ?s ? jstring( text ) : text.strip }
|
324
|
+
%([#{ segments.join( ',' ) }].join(''))
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def log( message, color = 33 )
|
329
|
+
@verbose and $stderr.puts( "\e[#{ color }m#{ message }\e[0m" )
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
end
|