antlr3 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -1,13 +1,456 @@
1
1
  #!/usr/bin/ruby
2
2
  # encoding: utf-8
3
3
 
4
+ require 'antlr3'
4
5
  require 'rake/tasklib'
6
+ require 'shellwords'
5
7
 
6
8
  module ANTLR3
9
+
10
+ =begin rdoc ANTLR3::CompileTask
11
+
12
+ A rake task-generating utility concerning ANTLR grammar file
13
+ compilation. This is a general utility -- the grammars do
14
+ not have to be targetted for Ruby output; it handles all
15
+ known ANTLR language targets.
16
+
17
+ require 'antlr3/task'
18
+
19
+ ANTLR3::CompileTask.define(
20
+ :name => 'grammars', :output_directory => 'lib/parsers'
21
+ ) do | t |
22
+ t.grammar_set( 'antlr/MainParser.g', 'antlr/MainTree.g' )
23
+
24
+ t.grammar_set( 'antlr/Template.g' ) do | gram |
25
+ gram.output_directory = 'lib/parsers/template'
26
+ gram.debug = true
27
+ end
28
+ end
29
+
30
+
31
+ TODO: finish documentation
32
+
33
+ =end
34
+
7
35
  class CompileTask < Rake::TaskLib
36
+ attr_reader :grammar_sets, :options
37
+ attr_accessor :name
38
+
39
+ def self.define( *grammar_files )
40
+ lib = new( *grammar_files )
41
+ block_given? and yield( lib )
42
+ lib.define
43
+ return( lib )
44
+ end
45
+
46
+ def initialize( *grammar_files )
47
+ grammar_files = [ grammar_files ].flatten!
48
+ options = Hash === grammar_files.last ? grammar_files.pop : {}
49
+ @grammar_sets = []
50
+ @name = options.fetch( :name, 'antlr-grammars' )
51
+ @options = options
52
+ grammar_files.empty? or grammar_set( grammar_files )
53
+ end
54
+
55
+ def target_files
56
+ @grammar_sets.inject( [] ) do | list, set |
57
+ list.concat( set.target_files )
58
+ end
59
+ end
60
+
61
+ def grammar_set( *grammar_files )
62
+ grammar_files = [ grammar_files ].flatten!
63
+ options = @options.merge(
64
+ Hash === grammar_files.last ? grammar_files.pop : {}
65
+ )
66
+ set = GrammarSet.new( grammar_files, options )
67
+ block_given? and yield( set )
68
+ @grammar_sets << set
69
+ return( set )
70
+ end
8
71
 
72
+ def define
73
+ namespace( @name ) do
74
+ desc( "trash all ANTLR-generated source code")
75
+ task( 'clobber' ) do
76
+ for set in @grammar_sets
77
+ set.clean
78
+ end
79
+ end
80
+
81
+ for set in @grammar_sets
82
+ set.define_tasks
83
+ end
84
+
85
+ desc( "compile ANTLR grammars" )
86
+ task( 'compile' => target_files )
87
+ end
88
+ end
9
89
 
90
+
91
+ class CompileTask::GrammarSet
92
+ attr_accessor :antlr_jar, :debug,
93
+ :trace, :profile, :compile_options,
94
+ :java_options
95
+ attr_reader :load_path, :grammars
96
+ attr_writer :output_directory
97
+
98
+ def initialize( grammar_files, options = {} )
99
+ @load_path = grammar_files.map { | f | File.dirname( f ) }
100
+ @load_path.push( '.', @output_directory )
101
+
102
+ if extra_load = options[ :load_path ]
103
+ extra_load = [ extra_load ].flatten
104
+ @load_path.unshift( extra_load )
105
+ end
106
+ @load_path.uniq!
107
+
108
+ @grammars = grammar_files.map do | file |
109
+ GrammarFile.new( self, file )
110
+ end
111
+ @output_directory = nil
112
+ dir = options[ :output_directory ] and @output_directory = dir.to_s
113
+
114
+ @antlr_jar = options.fetch( :antlr_jar, ANTLR3.antlr_jar )
115
+ @debug = options.fetch( :debug, false )
116
+ @trace = options.fetch( :trace, false )
117
+ @profile = options.fetch( :profile, false )
118
+ @compile_options =
119
+ case opts = options[ :compile_options ]
120
+ when Array then opts
121
+ else Shellwords.shellsplit( opts.to_s )
122
+ end
123
+ @java_options =
124
+ case opts = options[ :java_options ]
125
+ when Array then opts
126
+ else Shellwords.shellsplit( opts.to_s )
127
+ end
128
+ end
129
+
130
+ def target_files
131
+ @grammars.map { | gram | gram.target_files }.flatten
132
+ end
133
+
134
+ def output_directory
135
+ @output_directory || '.'
136
+ end
137
+
138
+ def define_tasks
139
+ directory( @output_directory )
140
+ file( @antlr_jar )
141
+
142
+ for grammar in @grammars
143
+ deps = [ @output_directory, @antlr_jar ]
144
+ if vocab = grammar.token_vocab and
145
+ tfile = find_tokens_file( vocab, grammar )
146
+ file( tfile )
147
+ deps << tfile
148
+ end
149
+ grammar.define_tasks( deps )
150
+ end
151
+ end
152
+
153
+ def clean
154
+ for grammar in @grammars
155
+ grammar.clean
156
+ end
157
+ if test( ?d, output_directory ) and ( Dir.entries( output_directory ) - %w( . .. ) ).empty?
158
+ rmdir( output_directory )
159
+ end
160
+ end
10
161
 
162
+ def find_tokens_file( vocab, grammar )
163
+ gram = @grammars.find { | gram | gram.name == vocab } and
164
+ return( gram.tokens_file )
165
+ file = locate( "#{ vocab }.tokens" ) and return( file )
166
+ warn( Util.tidy( <<-END, true ) )
167
+ | unable to locate .tokens file `#{ vocab }' referenced in #{ grammar.path }
168
+ | -- ignoring dependency
169
+ END
170
+ return( nil )
171
+ end
11
172
 
173
+ def locate( file_name )
174
+ dir = @load_path.find do | dir |
175
+ File.file?( File.join( dir, file_name ) )
176
+ end
177
+ dir and return( File.join( dir, file_name ) )
178
+ end
179
+
180
+ def compile( grammar )
181
+ sh( build_command( grammar ) )
182
+ end
183
+
184
+ def build_command( grammar )
185
+ parts = [ 'java', '-cp', @antlr_jar ]
186
+ parts.concat( @java_options )
187
+ parts << 'org.antlr.Tool' << '-fo' << output_directory
188
+ parts << '-debug' if @debug
189
+ parts << '-profile' if @profile
190
+ parts << '-trace' if @trace
191
+ parts.concat( @compile_options )
192
+ parts << grammar.path
193
+ return Shellwords.shelljoin( parts )
194
+ end
12
195
  end
196
+
197
+ class GrammarFile
198
+ LANGUAGES = {
199
+ "ActionScript" => [".as"],
200
+ "CSharp2" => [".cs"],
201
+ "C" => [".c", ".h"],
202
+ "ObjC" => [".m", ".h"],
203
+ "CSharp3" => [".cs"],
204
+ "Cpp" => [".cpp", ".h"],
205
+ "Ruby" => [".rb"],
206
+ "Java" => [".java"],
207
+ "JavaScript" => [".js"],
208
+ "Python" => [".py"],
209
+ "Delphi" => [".pas"],
210
+ "Perl5" => [".pm"]
211
+ }.freeze
212
+ GRAMMAR_TYPES = %w(lexer parser tree combined)
213
+
214
+ ##################################################################
215
+ ######## CONSTRUCTOR #############################################
216
+ ##################################################################
217
+
218
+ def initialize( group, path, options = {} )
219
+ @group = group
220
+ @path = path.to_s
221
+ @imports = []
222
+ @language = 'Java'
223
+ @token_vocab = nil
224
+ @tasks_defined = false
225
+ @extra_dependencies = []
226
+ if extra = options[ :extra_dependencies ]
227
+ extra = [ extra ].flatten
228
+ @extra_dependencies.concat( extra )
229
+ end
230
+
231
+ study
232
+ yield( self ) if block_given?
233
+ fetch_imports
234
+ end
235
+
236
+ ##################################################################
237
+ ######## ATTRIBUTES AND ATTRIBUTE-ISH METHODS ####################
238
+ ##################################################################
239
+ attr_reader :type, :name, :language, :source,
240
+ :token_vocab, :imports, :imported_grammars,
241
+ :path, :group
242
+
243
+ for attr in [ :output_directory, :load_path, :antlr_jar ]
244
+ class_eval( <<-END )
245
+ def #{ attr }
246
+ @group.#{ attr }
247
+ end
248
+ END
249
+ end
250
+
251
+ def lexer_files
252
+ if lexer? then base = @name
253
+ elsif combined? then base = @name + 'Lexer'
254
+ else return( [] )
255
+ end
256
+ return( file_names( base ) )
257
+ end
258
+
259
+ def parser_files
260
+ if parser? then base = @name
261
+ elsif combined? then base = @name + 'Parser'
262
+ else return( [] )
263
+ end
264
+ return( file_names( base ) )
265
+ end
266
+
267
+ def tree_parser_files
268
+ return( tree? ? file_names( @name ) : [] )
269
+ end
270
+
271
+ def file_names( base )
272
+ LANGUAGES.fetch( @language ).map do | ext |
273
+ File.join( output_directory, base + ext )
274
+ end
275
+ end
276
+
277
+ for type in GRAMMAR_TYPES
278
+ class_eval( <<-END )
279
+ def #{ type }?
280
+ @type == #{ type.inspect }
281
+ end
282
+ END
283
+ end
284
+
285
+ def delegate_files( delegate_suffix )
286
+ file_names( "#{ name }_#{ delegate_suffix }" )
287
+ end
288
+
289
+ def tokens_file
290
+ File.join( output_directory, name + '.tokens' )
291
+ end
292
+
293
+ def target_files( all = true )
294
+ targets = [ tokens_file ]
295
+
296
+ for target_type in %w( lexer parser tree_parser )
297
+ for file in self.send( :"#{ target_type }_files")
298
+ targets << file
299
+ end
300
+ end
301
+
302
+ if all
303
+ for grammar in @imported_grammars
304
+ targets.concat( grammar.target_files )
305
+ end
306
+ end
307
+
308
+ return targets
309
+ end
310
+
311
+ def update
312
+ touch( @path )
313
+ end
314
+
315
+ def all_imported_files
316
+ imported_files = []
317
+ for grammar in @imported_grammars
318
+ imported_files.push( grammar.path, *grammar.all_imported_files )
319
+ end
320
+ return imported_files
321
+ end
322
+
323
+ def clean
324
+ deleted = []
325
+ for target in target_files
326
+ if test( ?f, target )
327
+ rm( target )
328
+ deleted << target
329
+ end
330
+ end
331
+
332
+ for grammar in @imported_grammars
333
+ deleted.concat( grammar.clean )
334
+ end
335
+
336
+ return deleted
337
+ end
338
+
339
+ def define_tasks( shared_depends )
340
+ unless @tasks_defined
341
+ depends = [ @path, *all_imported_files ]
342
+ for f in depends
343
+ file( f )
344
+ end
345
+ depends = shared_depends + depends
346
+
347
+ target_files.each do | target |
348
+ file( target => depends ) do
349
+ @group.compile( self )
350
+ end
351
+ end
352
+
353
+ @tasks_defined = true
354
+ end
355
+ end
356
+
357
+ private
358
+
359
+ def fetch_imports
360
+ @imported_grammars = @imports.map do | imp |
361
+ file = group.locate( "#{ imp }.g" ) or raise( Util.tidy( <<-END ) )
362
+ | #{ @path }: unable to locate imported grammar file #{ imp }.g
363
+ | search directories ( @load_path ):
364
+ | - #{ load_path.join( "\n - " ) }
365
+ END
366
+ Imported.new( self, file )
367
+ end
368
+ end
369
+
370
+ def study
371
+ @source = File.read( @path )
372
+ @source =~ /^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/ or
373
+ raise Grammar::FormatError[ @source, @path ]
374
+ @name = $2
375
+ @type = $1 || 'combined'
376
+ if @source =~ /^\s*options\s*\{(.*?)\}/m
377
+ option_block = $1
378
+ if option_block =~ /\s*language\s*=\s*(\S+)\s*;/
379
+ @language = $1
380
+ LANGUAGES.has_key?( @language ) or
381
+ raise( Grammar::FormatError, "Unknown ANTLR target language: %p" % @language )
382
+ end
383
+ option_block =~ /\s*tokenVocab\s*=\s*(\S+)\s*;/ and
384
+ @token_vocab = $1
385
+ end
386
+
387
+ @source.scan(/^\s*import\s+(\w+\s*(?:,\s*\w+\s*)*);/) do
388
+ list = $1.strip
389
+ @imports.concat( list.split( /\s*,\s*/ ) )
390
+ end
391
+ end
392
+ end # class Grammar
393
+
394
+ class GrammarFile::Imported < GrammarFile
395
+ def initialize( owner, path )
396
+ @owner = owner
397
+ @path = path.to_s
398
+ @imports = []
399
+ @language = 'Java'
400
+ @token_vocab = nil
401
+ study
402
+ fetch_imports
403
+ end
404
+
405
+ for attr in [ :load_path, :output_directory, :antlr_jar, :verbose, :group ]
406
+ class_eval( <<-END )
407
+ def #{ attr }
408
+ @owner.#{ attr }
409
+ end
410
+ END
411
+ end
412
+
413
+ def delegate_files( suffix )
414
+ @owner.delegate_files( "#{ @name }_#{ suffix }" )
415
+ end
416
+
417
+ def target_files
418
+ targets = [ tokens_file ]
419
+ targets.concat( @owner.delegate_files( @name ) )
420
+ return( targets )
421
+ end
13
422
  end
423
+
424
+ class GrammarFile::FormatError < StandardError
425
+ attr_reader :file, :source
426
+
427
+ def self.[](*args)
428
+ new(*args)
429
+ end
430
+
431
+ def initialize(source, file = nil)
432
+ @file = file
433
+ @source = source
434
+ message = ''
435
+ if file.nil? # inline
436
+ message << "bad inline grammar source:\n"
437
+ message << ("-" * 80) << "\n"
438
+ message << @source
439
+ message[-1] == ?\n or message << "\n"
440
+ message << ("-" * 80) << "\n"
441
+ message << "could not locate a grammar name and type declaration matching\n"
442
+ message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
443
+ else
444
+ message << 'bad grammar source in file %p' % @file
445
+ message << ("-" * 80) << "\n"
446
+ message << @source
447
+ message[-1] == ?\n or message << "\n"
448
+ message << ("-" * 80) << "\n"
449
+ message << "could not locate a grammar name and type declaration matching\n"
450
+ message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
451
+ end
452
+ super(message)
453
+ end
454
+ end # error Grammar::FormatError
455
+ end # class CompileTask
456
+ end # module ANTLR3
@@ -5,7 +5,6 @@ require 'antlr3'
5
5
  require 'antlr3/test/core-extensions'
6
6
  require 'antlr3/test/call-stack'
7
7
 
8
-
9
8
  if RUBY_VERSION =~ /^1\.9/
10
9
  require 'digest/md5'
11
10
  MD5 = Digest::MD5
@@ -13,7 +12,6 @@ else
13
12
  require 'md5'
14
13
  end
15
14
 
16
-
17
15
  module ANTLR3
18
16
  module Test
19
17
  module DependantFile
@@ -696,6 +696,7 @@ Tree adaptors are tasked with:
696
696
  module TreeAdaptor
697
697
  include TokenFactory
698
698
  include Constants
699
+ include Error
699
700
 
700
701
  def add_child( tree, child )
701
702
  tree.add_child( child ) if tree and child
@@ -20,7 +20,7 @@ module ANTLR3
20
20
  #
21
21
  MAJOR_VERSION = 1
22
22
  MINOR_VERSION = 3
23
- PATCH_VERSION = 0
23
+ PATCH_VERSION = 1
24
24
  VERSION = [ MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION ]
25
25
  VERSION_STRING = VERSION.join('.').freeze
26
26
 
@@ -28,7 +28,7 @@ $:.unshift( this_directory ) unless $:.include?( this_directory )
28
28
 
29
29
  antlr_load_failed = proc do
30
30
  load_path = $LOAD_PATH.map { |dir| ' - ' \<\< dir }.join( $/ )
31
- raise LoadError, \<\<-'END'.strip!
31
+ raise LoadError, \<\<-END.strip!
32
32
 
33
33
  Failed to load the ANTLR3 runtime library (version <runtimeLibraryVersion()>):
34
34
 
@@ -1498,4 +1498,4 @@ placeAction(scope, name) ::= <<
1498
1498
  <endif>
1499
1499
  >>
1500
1500
 
1501
- runtimeLibraryVersion() ::= "1.3.0"
1501
+ runtimeLibraryVersion() ::= "1.3.1"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: antlr3
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Yetter
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-02 00:00:00 -05:00
12
+ date: 2010-01-07 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15