1324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#!/usr/bin/ruby
2324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver# encoding: utf-8
3324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
4324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverrequire 'antlr3'
5324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverrequire 'set'
6324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverrequire 'rake'
7324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverrequire 'rake/tasklib'
8324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverrequire 'shellwords'
9324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
10324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruvermodule ANTLR3
11324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
12324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver=begin rdoc ANTLR3::CompileTask
13324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
14324c4644fee44b9898524c09511bd33c3f12e2dfBen GruverA rake task-generating utility concerning ANTLR grammar file
15324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruvercompilation. This is a general utility -- the grammars do
16324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruvernot have to be targetted for Ruby output; it handles all
17324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverknown ANTLR language targets.
18324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
19324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  require 'antlr3/task'
20324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
21324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ANTLR3::CompileTask.define(
22324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    :name => 'grammars', :output_directory => 'lib/parsers'
23324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ) do | t |
24324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    t.grammar_set( 'antlr/MainParser.g', 'antlr/MainTree.g' )
25324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
26324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    t.grammar_set( 'antlr/Template.g' ) do | gram |
27324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      gram.output_directory = 'lib/parsers/template'
28324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      gram.debug = true
29324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
30324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
31324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
32324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
33324c4644fee44b9898524c09511bd33c3f12e2dfBen GruverTODO: finish documentation
34324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
35324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver=end
36324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
37324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass CompileTask < Rake::TaskLib
38324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_reader :grammar_sets, :options
39324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_accessor :name
40324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
41324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def self.define( *grammar_files )
42324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    lib = new( *grammar_files )
43324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    block_given? and yield( lib )
44324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    lib.define
45324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( lib )
46324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
47324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
48324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def initialize( *grammar_files )
49324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    grammar_files = [ grammar_files ].flatten!
50324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    options = Hash === grammar_files.last ? grammar_files.pop : {}
51324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @grammar_sets = []
52324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @name = options.fetch( :name, 'antlr-grammars' )
53324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @options = options
54324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @namespace = Rake.application.current_scope
55324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    grammar_files.empty? or grammar_set( grammar_files )
56324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
57324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
58324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def target_files
59324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @grammar_sets.inject( [] ) do | list, set |
60324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      list.concat( set.target_files )
61324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
62324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
63324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
64324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def grammar_set( *grammar_files )
65324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    grammar_files = [ grammar_files ].flatten!
66324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    options = @options.merge( 
67324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      Hash === grammar_files.last ? grammar_files.pop : {}
68324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    )
69324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    set = GrammarSet.new( grammar_files, options )
70324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    block_given? and yield( set )
71324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @grammar_sets << set
72324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( set )
73324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
74324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
75324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def compile_task
76324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    full_name = ( @namespace + [ @name, 'compile' ] ).join( ':' )
77324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    Rake::Task[ full_name ]
78324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
79324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
80324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def compile!
81324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    compile_task.invoke
82324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
83324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
84324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def clobber_task
85324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    full_name = ( @namespace + [ @name, 'clobber' ] ).join( ':' )
86324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    Rake::Task[ full_name ]
87324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
88324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
89324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def clobber!
90324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    clobber_task.invoke
91324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
92324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
93324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def define
94324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    namespace( @name ) do
95324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      desc( "trash all ANTLR-generated source code" )
96324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      task( 'clobber' ) do
97324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        for set in @grammar_sets
98324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver          set.clean
99324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        end
100324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
101324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      
102324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      for set in @grammar_sets
103324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        set.define_tasks
104324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
105324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      
106324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      desc( "compile ANTLR grammars" )
107324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      task( 'compile' => target_files )
108324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
109324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
110324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
111324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
112324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver#class CompileTask::GrammarSet
113324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass GrammarSet
114324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_accessor :antlr_jar, :debug,
115324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver                :trace, :profile, :compile_options,
116324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver                :java_options
117324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_reader :load_path, :grammars
118324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_writer :output_directory
119324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
120324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def initialize( grammar_files, options = {} )
121324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @load_path = grammar_files.map { | f | File.dirname( f ) }
122324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @load_path.push( '.', @output_directory )
123324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
124324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if extra_load = options[ :load_path ]
125324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      extra_load = [ extra_load ].flatten
126324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      @load_path.unshift( extra_load )
127324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
128324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @load_path.uniq!
129324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
130324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @grammars = grammar_files.map do | file |
131324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      GrammarFile.new( self, file )
132324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
133324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @output_directory = '.'
134324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    dir = options[ :output_directory ] and @output_directory = dir.to_s
135324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
136324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @antlr_jar = options.fetch( :antlr_jar, ANTLR3.antlr_jar )
137324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @debug = options.fetch( :debug, false )
138324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @trace = options.fetch( :trace, false )
139324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @profile = options.fetch( :profile, false )
140324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @compile_options =
141324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      case opts = options[ :compile_options ]
142324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      when Array then opts
143324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      else Shellwords.shellwords( opts.to_s )
144324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
145324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @java_options =
146324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      case opts = options[ :java_options ]
147324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      when Array then opts
148324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      else Shellwords.shellwords( opts.to_s )
149324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
150324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
151324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
152324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def target_files
153324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @grammars.map { | gram | gram.target_files }.flatten
154324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
155324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
156324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def output_directory
157324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @output_directory || '.'
158324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
159324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
160324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def define_tasks
161324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    file( @antlr_jar )
162324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
163324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for grammar in @grammars
164324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      deps = [ @antlr_jar ]
165324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      if  vocab = grammar.token_vocab and
166324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver          tfile = find_tokens_file( vocab, grammar )
167324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        file( tfile )
168324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        deps << tfile
169324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
170324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      grammar.define_tasks( deps )
171324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
172324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
173324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
174324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def clean
175324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for grammar in @grammars
176324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      grammar.clean
177324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
178324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if test( ?d, output_directory ) and ( Dir.entries( output_directory ) - %w( . .. ) ).empty?
179324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      rmdir( output_directory )
180324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
181324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
182324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
183324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def find_tokens_file( vocab, grammar )
184324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    gram = @grammars.find { | gram | gram.name == vocab } and
185324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      return( gram.tokens_file )
186324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    file = locate( "#{ vocab }.tokens" ) and return( file )
187324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    warn( Util.tidy( <<-END, true ) )
188324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    | unable to locate .tokens file `#{ vocab }' referenced in #{ grammar.path }
189324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    | -- ignoring dependency
190324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    END
191324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( nil )
192324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
193324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
194324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def locate( file_name )
195324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    dir = @load_path.find do | dir |
196324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      File.file?( File.join( dir, file_name ) )
197324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
198324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    dir and return( File.join( dir, file_name ) )
199324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
200324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
201324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def compile( grammar )
202324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    dir = output_directory
203324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    test( ?d, dir ) or FileUtils.mkpath( dir )
204324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    sh( build_command( grammar ) )
205324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
206324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
207324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def build_command( grammar )
208324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts = [ 'java', '-cp', @antlr_jar ]
209324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts.concat( @java_options )
210324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts << 'org.antlr.Tool' << '-fo' << output_directory
211324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts << '-debug' if @debug
212324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts << '-profile' if @profile
213324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts << '-trace' if @trace
214324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts.concat( @compile_options )
215324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    parts << grammar.path
216324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return parts.map! { | t | escape( t ) }.join( ' ' )
217324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
218324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
219324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def escape( token )
220324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    token = token.to_s.dup
221324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    token.empty? and return( %('') )
222324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    token.gsub!( /([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1" )
223324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    token.gsub!( /\n/, "'\n'" )
224324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( token )
225324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
226324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
227324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend
228324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
229324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass GrammarFile
230324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  LANGUAGES = { 
231324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "ActionScript" => [ ".as" ],
232324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "CSharp2" => [ ".cs" ],
233324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "C" => [ ".c", ".h" ],
234324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "ObjC" => [ ".m", ".h" ],
235324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "CSharp3" => [ ".cs" ],
236324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Cpp" => [ ".cpp", ".h" ],
237324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Ruby" => [ ".rb" ],
238324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Java" => [ ".java" ],
239324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "JavaScript" => [ ".js" ],
240324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Python" => [ ".py" ],
241324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Delphi" => [ ".pas" ],
242324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    "Perl5" => [ ".pm" ]
243324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  }.freeze
244324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  GRAMMAR_TYPES = %w(lexer parser tree combined)
245324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
246324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ##################################################################
247324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ######## CONSTRUCTOR #############################################
248324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ##################################################################
249324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
250324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def initialize( group, path, options = {} )
251324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @group = group
252324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @path = path.to_s
253324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @imports = []
254324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @language = 'Java'
255324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @token_vocab = nil
256324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @tasks_defined = false
257324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @extra_dependencies = []
258324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if extra = options[ :extra_dependencies ]
259324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      extra = [ extra ].flatten
260324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      @extra_dependencies.concat( extra )
261324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
262324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
263324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    study
264324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    yield( self ) if block_given?
265324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    fetch_imports
266324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
267324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
268324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ##################################################################
269324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ######## ATTRIBUTES AND ATTRIBUTE-ISH METHODS ####################
270324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  ##################################################################
271324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_reader :type, :name, :language, :source,
272324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver              :token_vocab, :imports, :imported_grammars,
273324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver              :path, :group
274324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
275324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  for attr in [ :output_directory, :load_path, :antlr_jar ]
276324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    class_eval( <<-END )
277324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      def #{ attr }
278324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        @group.#{ attr }
279324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
280324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    END
281324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
282324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
283324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def lexer_files
284324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if lexer? then base = @name
285324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    elsif combined? then base = @name + 'Lexer'
286324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    else return( [] )
287324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
288324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( file_names( base ) )
289324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
290324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
291324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def parser_files
292324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if parser? then base = @name
293324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    elsif combined? then base = @name + 'Parser'
294324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    else return( [] )
295324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
296324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( file_names( base ) )
297324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
298324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
299324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def tree_parser_files
300324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( tree? ? file_names( @name ) : [] )
301324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
302324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
303324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def file_names( base )
304324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    LANGUAGES.fetch( @language ).map do | ext |
305324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      File.join( output_directory, base + ext )
306324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
307324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
308324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
309324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  for type in GRAMMAR_TYPES
310324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    class_eval( <<-END )
311324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      def #{ type }?
312324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        @type == #{ type.inspect }
313324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
314324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    END
315324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
316324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
317324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def delegate_files( delegate_suffix )
318324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    file_names( "#{ name }_#{ delegate_suffix }" )
319324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
320324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
321324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def tokens_file
322324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    File.join( output_directory, name + '.tokens' )
323324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
324324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
325324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def target_files( all = true )
326324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    targets = [ tokens_file ]
327324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
328324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for target_type in %w( lexer parser tree_parser )
329324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      for file in self.send( :"#{ target_type }_files" )
330324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        targets << file
331324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
332324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
333324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
334324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if all
335324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      for grammar in @imported_grammars
336324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        targets.concat( grammar.target_files )
337324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
338324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
339324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
340324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return targets
341324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
342324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
343324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def update
344324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    touch( @path )
345324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
346324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
347324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def all_imported_files
348324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    imported_files = []
349324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for grammar in @imported_grammars
350324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      imported_files.push( grammar.path, *grammar.all_imported_files )
351324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
352324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return imported_files
353324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
354324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
355324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def clean
356324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    deleted = []
357324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for target in target_files
358324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      if test( ?f, target )
359324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        rm( target )
360324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        deleted << target
361324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
362324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
363324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
364324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    for grammar in @imported_grammars
365324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      deleted.concat( grammar.clean )
366324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
367324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
368324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return deleted
369324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
370324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
371324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def define_tasks( shared_depends )
372324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    unless @tasks_defined
373324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      depends = [ @path, *all_imported_files ]
374324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      for f in depends
375324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        file( f )
376324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
377324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      depends = shared_depends + depends
378324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      
379324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      target_files.each do | target |
380324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        file( target => ( depends - [ target ] ) ) do   # prevents recursive .tokens file dependencies
381324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver          @group.compile( self )
382324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        end
383324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
384324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      
385324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      @tasks_defined = true
386324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
387324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
388324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
389324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverprivate
390324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
391324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def fetch_imports
392324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @imported_grammars = @imports.map do | imp |
393324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      file = group.locate( "#{ imp }.g" ) or raise( Util.tidy( <<-END ) )
394324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      | #{ @path }: unable to locate imported grammar file #{ imp }.g
395324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      | search directories ( @load_path ):
396324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      |   - #{ load_path.join( "\n  - " ) }
397324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      END
398324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      Imported.new( self, file )
399324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
400324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
401324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
402324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def study
403324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @source = File.read( @path )
404324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @source =~ /^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/ or
405324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      raise Grammar::FormatError[ @source, @path ]
406324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @name = $2
407324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @type = $1 || 'combined'
408324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if @source =~ /^\s*options\s*\{(.*?)\}/m
409324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      option_block = $1
410324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      if option_block =~ /\s*language\s*=\s*(\S+)\s*;/
411324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        @language = $1
412324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        LANGUAGES.has_key?( @language ) or
413324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver          raise( Grammar::FormatError, "Unknown ANTLR target language: %p" % @language )
414324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
415324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      option_block =~ /\s*tokenVocab\s*=\s*(\S+)\s*;/ and
416324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        @token_vocab = $1
417324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
418324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    
419324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @source.scan( /^\s*import\s+(\w+\s*(?:,\s*\w+\s*)*);/ ) do
420324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      list = $1.strip
421324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      @imports.concat( list.split( /\s*,\s*/ ) )
422324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
423324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
424324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend # class Grammar
425324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
426324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass GrammarFile::Imported < GrammarFile
427324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def initialize( owner, path )
428324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @owner = owner
429324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @path = path.to_s
430324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @imports = []
431324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @language = 'Java'
432324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @token_vocab = nil
433324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    study
434324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    fetch_imports
435324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
436324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
437324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  for attr in [ :load_path, :output_directory, :antlr_jar, :verbose, :group ]
438324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    class_eval( <<-END )
439324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      def #{ attr }
440324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver        @owner.#{ attr }
441324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      end
442324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    END
443324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
444324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
445324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def delegate_files( suffix )
446324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @owner.delegate_files( "#{ @name }_#{ suffix }" )
447324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
448324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
449324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def target_files
450324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    targets = [ tokens_file ]
451324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    targets.concat( @owner.delegate_files( @name ) )
452324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    return( targets )
453324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
454324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend
455324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver
456324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverclass GrammarFile::FormatError < StandardError
457324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  attr_reader :file, :source
458324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
459324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def self.[]( *args )
460324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    new( *args )
461324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
462324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  
463324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  def initialize( source, file = nil )
464324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @file = file
465324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    @source = source
466324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    message = ''
467324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    if file.nil? # inline
468324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << "bad inline grammar source:\n"
469324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << ( "-" * 80 ) << "\n"
470324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << @source
471324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message[ -1 ] == ?\n or message << "\n"
472324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << ( "-" * 80 ) << "\n"
473324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << "could not locate a grammar name and type declaration matching\n"
474324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
475324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    else
476324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << 'bad grammar source in file %p\n' % @file
477324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << ( "-" * 80 ) << "\n"
478324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << @source
479324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message[ -1 ] == ?\n or message << "\n"
480324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << ( "-" * 80 ) << "\n"
481324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << "could not locate a grammar name and type declaration matching\n"
482324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver      message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
483324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    end
484324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver    super( message )
485324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruver  end
486324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend # error Grammar::FormatError
487324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend # class CompileTask
488324c4644fee44b9898524c09511bd33c3f12e2dfBen Gruverend # module ANTLR3
489