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.
@@ -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
@@ -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