antlr3 1.2.3
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.
- data/ANTLR-LICENSE.txt +26 -0
- data/History.txt +66 -0
- data/README.txt +139 -0
- data/bin/antlr4ruby +33 -0
- data/java/RubyTarget.java +524 -0
- data/java/antlr-full-3.2.1.jar +0 -0
- data/lib/antlr3.rb +176 -0
- data/lib/antlr3/constants.rb +88 -0
- data/lib/antlr3/debug.rb +701 -0
- data/lib/antlr3/debug/event-hub.rb +210 -0
- data/lib/antlr3/debug/record-event-listener.rb +25 -0
- data/lib/antlr3/debug/rule-tracer.rb +55 -0
- data/lib/antlr3/debug/socket.rb +360 -0
- data/lib/antlr3/debug/trace-event-listener.rb +92 -0
- data/lib/antlr3/dfa.rb +247 -0
- data/lib/antlr3/dot.rb +174 -0
- data/lib/antlr3/error.rb +657 -0
- data/lib/antlr3/main.rb +561 -0
- data/lib/antlr3/modes/ast-builder.rb +41 -0
- data/lib/antlr3/modes/filter.rb +56 -0
- data/lib/antlr3/profile.rb +322 -0
- data/lib/antlr3/recognizers.rb +1280 -0
- data/lib/antlr3/streams.rb +985 -0
- data/lib/antlr3/streams/interactive.rb +91 -0
- data/lib/antlr3/streams/rewrite.rb +412 -0
- data/lib/antlr3/test/call-stack.rb +57 -0
- data/lib/antlr3/test/config.rb +23 -0
- data/lib/antlr3/test/core-extensions.rb +269 -0
- data/lib/antlr3/test/diff.rb +165 -0
- data/lib/antlr3/test/functional.rb +207 -0
- data/lib/antlr3/test/grammar.rb +371 -0
- data/lib/antlr3/token.rb +592 -0
- data/lib/antlr3/tree.rb +1415 -0
- data/lib/antlr3/tree/debug.rb +163 -0
- data/lib/antlr3/tree/visitor.rb +84 -0
- data/lib/antlr3/tree/wizard.rb +481 -0
- data/lib/antlr3/util.rb +149 -0
- data/lib/antlr3/version.rb +27 -0
- data/samples/ANTLRv3Grammar.g +621 -0
- data/samples/Cpp.g +749 -0
- data/templates/AST.stg +335 -0
- data/templates/ASTDbg.stg +40 -0
- data/templates/ASTParser.stg +153 -0
- data/templates/ASTTreeParser.stg +272 -0
- data/templates/Dbg.stg +192 -0
- data/templates/Ruby.stg +1514 -0
- data/test/functional/ast-output/auto-ast.rb +797 -0
- data/test/functional/ast-output/construction.rb +555 -0
- data/test/functional/ast-output/hetero-nodes.rb +753 -0
- data/test/functional/ast-output/rewrites.rb +1327 -0
- data/test/functional/ast-output/tree-rewrite.rb +1662 -0
- data/test/functional/debugging/debug-mode.rb +689 -0
- data/test/functional/debugging/profile-mode.rb +165 -0
- data/test/functional/debugging/rule-tracing.rb +74 -0
- data/test/functional/delegation/import.rb +379 -0
- data/test/functional/lexer/basic.rb +559 -0
- data/test/functional/lexer/filter-mode.rb +245 -0
- data/test/functional/lexer/nuances.rb +47 -0
- data/test/functional/lexer/properties.rb +104 -0
- data/test/functional/lexer/syn-pred.rb +32 -0
- data/test/functional/lexer/xml.rb +206 -0
- data/test/functional/main/main-scripts.rb +245 -0
- data/test/functional/parser/actions.rb +224 -0
- data/test/functional/parser/backtracking.rb +244 -0
- data/test/functional/parser/basic.rb +282 -0
- data/test/functional/parser/calc.rb +98 -0
- data/test/functional/parser/ll-star.rb +143 -0
- data/test/functional/parser/nuances.rb +165 -0
- data/test/functional/parser/predicates.rb +103 -0
- data/test/functional/parser/properties.rb +242 -0
- data/test/functional/parser/rule-methods.rb +132 -0
- data/test/functional/parser/scopes.rb +274 -0
- data/test/functional/token-rewrite/basic.rb +318 -0
- data/test/functional/token-rewrite/via-parser.rb +100 -0
- data/test/functional/tree-parser/basic.rb +750 -0
- data/test/unit/sample-input/file-stream-1 +2 -0
- data/test/unit/sample-input/teststreams.input2 +2 -0
- data/test/unit/test-dfa.rb +52 -0
- data/test/unit/test-exceptions.rb +44 -0
- data/test/unit/test-recognizers.rb +55 -0
- data/test/unit/test-scheme.rb +62 -0
- data/test/unit/test-streams.rb +459 -0
- data/test/unit/test-tree-wizard.rb +535 -0
- data/test/unit/test-trees.rb +854 -0
- metadata +205 -0
@@ -0,0 +1,207 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3'
|
5
|
+
require 'antlr3/test/core-extensions'
|
6
|
+
require 'antlr3/test/grammar'
|
7
|
+
require 'antlr3/test/call-stack'
|
8
|
+
|
9
|
+
require 'test/unit'
|
10
|
+
require 'spec'
|
11
|
+
|
12
|
+
module ANTLR3
|
13
|
+
module Test
|
14
|
+
module Location
|
15
|
+
attr_accessor :test_path
|
16
|
+
|
17
|
+
def test_group
|
18
|
+
File.basename( test_path, '.rb' )
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_directory
|
22
|
+
File.dirname( test_path )
|
23
|
+
end
|
24
|
+
|
25
|
+
def local_path(*parts)
|
26
|
+
File.join( test_directory, *parts )
|
27
|
+
end
|
28
|
+
|
29
|
+
def output_directory(name = test_group)
|
30
|
+
local_path( name )
|
31
|
+
end
|
32
|
+
|
33
|
+
end # module Location
|
34
|
+
|
35
|
+
module NameSpace
|
36
|
+
def import( ruby_file )
|
37
|
+
constants_before = constants
|
38
|
+
class_eval(File.read(ruby_file), ruby_file, 1)
|
39
|
+
constants - constants_before
|
40
|
+
end
|
41
|
+
|
42
|
+
def import_grammar_targets(grammar)
|
43
|
+
# reverse because delegates need to be in the test class's name
|
44
|
+
# space before the masters
|
45
|
+
for file in grammar.target_files
|
46
|
+
import(file)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module GrammarManager
|
52
|
+
include Location
|
53
|
+
include NameSpace
|
54
|
+
|
55
|
+
DEFAULT_COMPILE_OPTIONS = {}
|
56
|
+
|
57
|
+
def add_default_compile_option( name, value )
|
58
|
+
DEFAULT_COMPILE_OPTIONS[ name ] = value
|
59
|
+
end
|
60
|
+
module_function :add_default_compile_option
|
61
|
+
|
62
|
+
if ANTLR_JAR = ENV['ANTLR_JAR'] || ANTLR3.antlr_jar
|
63
|
+
add_default_compile_option( :antlr_jar, ANTLR_JAR )
|
64
|
+
|
65
|
+
Grammar.global_dependency( ANTLR_JAR )
|
66
|
+
end
|
67
|
+
|
68
|
+
def const_missing( name )
|
69
|
+
if g = grammars[ name.to_s ]
|
70
|
+
compile( g )
|
71
|
+
grammars.delete( name.to_s )
|
72
|
+
const_get( name )
|
73
|
+
else
|
74
|
+
super
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def grammars
|
79
|
+
@grammars ||= {}
|
80
|
+
end
|
81
|
+
|
82
|
+
def grammar_count
|
83
|
+
grammars.length
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_grammar( name )
|
87
|
+
path = local_path( name.to_s )
|
88
|
+
path =~ /\.g$/ or path << '.g'
|
89
|
+
grammar = Grammar.new( path, :output_directory => output_directory )
|
90
|
+
register_grammar( grammar )
|
91
|
+
return grammar
|
92
|
+
end
|
93
|
+
|
94
|
+
def inline_grammar(source, options = {})
|
95
|
+
call = call_stack.find { |call| call.file != __FILE__}
|
96
|
+
grammar = Grammar.inline source,
|
97
|
+
:output_directory => output_directory,
|
98
|
+
:file => (call.file rescue nil),
|
99
|
+
:line => (call.line rescue nil)
|
100
|
+
register_grammar(grammar)
|
101
|
+
return grammar
|
102
|
+
end
|
103
|
+
|
104
|
+
def compile_options(defaults = nil)
|
105
|
+
@compile_options ||= DEFAULT_COMPILE_OPTIONS.clone
|
106
|
+
@compile_options.update(defaults) if defaults
|
107
|
+
return @compile_options
|
108
|
+
end
|
109
|
+
|
110
|
+
def compile(grammar, options = {})
|
111
|
+
grammar.compile( compile_options.merge(options) )
|
112
|
+
import_grammar_targets( grammar )
|
113
|
+
return grammar
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def register_grammar(grammar)
|
119
|
+
name = grammar.name
|
120
|
+
|
121
|
+
if conflict = grammars[ name ] and conflict.source != grammar.source
|
122
|
+
message = "Multiple grammars exist with the name ``#{name}''"
|
123
|
+
raise NameError, message
|
124
|
+
else
|
125
|
+
grammars[ name ] = grammar
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end # module GrammarManager
|
129
|
+
|
130
|
+
class Functional < ::Test::Unit::TestCase
|
131
|
+
extend GrammarManager
|
132
|
+
|
133
|
+
def self.inherited(klass)
|
134
|
+
super
|
135
|
+
klass.test_path = call_stack[0].file
|
136
|
+
end
|
137
|
+
|
138
|
+
def local_path(*args)
|
139
|
+
self.class.local_path(*args)
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_path
|
143
|
+
self.class.test_path
|
144
|
+
end
|
145
|
+
|
146
|
+
def output_directory
|
147
|
+
self.class.output_directory
|
148
|
+
end
|
149
|
+
|
150
|
+
def inline_grammar( source )
|
151
|
+
call = call_stack.find { |call| call.file != __FILE__}
|
152
|
+
grammar = Grammar.inline source,
|
153
|
+
:output_directory => output_directory,
|
154
|
+
:file => call.file,
|
155
|
+
:line => call.line
|
156
|
+
end
|
157
|
+
|
158
|
+
def compile_and_load( grammar, options = {} )
|
159
|
+
self.class.compile( grammar, options )
|
160
|
+
end
|
161
|
+
end # class Functional
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
module CaptureOutput
|
166
|
+
require 'stringio'
|
167
|
+
def output_buffer
|
168
|
+
defined?(@output_buffer) or @output_buffer = StringIO.new('')
|
169
|
+
@output_buffer
|
170
|
+
end
|
171
|
+
|
172
|
+
def output
|
173
|
+
output_buffer.string
|
174
|
+
end
|
175
|
+
|
176
|
+
def say(*args)
|
177
|
+
output_buffer.puts(*args)
|
178
|
+
end
|
179
|
+
|
180
|
+
def capture(*args)
|
181
|
+
output_buffer.write(*args)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
module RaiseErrors
|
186
|
+
def emit_error_message(msg)
|
187
|
+
# do nothing
|
188
|
+
end
|
189
|
+
|
190
|
+
def report_error(error)
|
191
|
+
raise error
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module CollectErrors
|
196
|
+
def reported_errors
|
197
|
+
defined?(@reported_errors) or @reported_errors = []
|
198
|
+
return @reported_errors
|
199
|
+
end
|
200
|
+
|
201
|
+
def emit_error_message(msg)
|
202
|
+
reported_errors << msg
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end # module Test
|
207
|
+
end # module ANTLR3
|
@@ -0,0 +1,371 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3'
|
5
|
+
require 'antlr3/test/core-extensions'
|
6
|
+
require 'antlr3/test/call-stack'
|
7
|
+
|
8
|
+
|
9
|
+
if RUBY_VERSION =~ /^1\.9/
|
10
|
+
require 'digest/md5'
|
11
|
+
MD5 = Digest::MD5
|
12
|
+
else
|
13
|
+
require 'md5'
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
module ANTLR3
|
18
|
+
module Test
|
19
|
+
module DependantFile
|
20
|
+
attr_accessor :path, :force
|
21
|
+
alias force? force
|
22
|
+
|
23
|
+
GLOBAL_DEPENDENCIES = []
|
24
|
+
|
25
|
+
def dependencies
|
26
|
+
@dependencies ||= GLOBAL_DEPENDENCIES.clone
|
27
|
+
end
|
28
|
+
|
29
|
+
def depends_on(path)
|
30
|
+
path = File.expand_path path.to_s
|
31
|
+
dependencies << path if test(?f, path)
|
32
|
+
return path
|
33
|
+
end
|
34
|
+
|
35
|
+
def stale?
|
36
|
+
force and return(true)
|
37
|
+
target_files.any? do |target|
|
38
|
+
not test(?f, target) or
|
39
|
+
dependencies.any? { |dep| test(?>, dep, target) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end # module DependantFile
|
43
|
+
|
44
|
+
class Grammar
|
45
|
+
include DependantFile
|
46
|
+
|
47
|
+
GRAMMAR_TYPES = %w(lexer parser tree combined)
|
48
|
+
TYPE_TO_CLASS = {
|
49
|
+
'lexer' => 'Lexer',
|
50
|
+
'parser' => 'Parser',
|
51
|
+
'tree' => 'TreeParser'
|
52
|
+
}
|
53
|
+
CLASS_TO_TYPE = TYPE_TO_CLASS.invert
|
54
|
+
|
55
|
+
def self.global_dependency(path)
|
56
|
+
path = File.expand_path path.to_s
|
57
|
+
GLOBAL_DEPENDENCIES << path if test(?f, path)
|
58
|
+
return path
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.inline(source, *args)
|
62
|
+
InlineGrammar.new(source, *args)
|
63
|
+
end
|
64
|
+
|
65
|
+
##################################################################
|
66
|
+
######## CONSTRUCTOR #############################################
|
67
|
+
##################################################################
|
68
|
+
def initialize(path, options = {})
|
69
|
+
@path = path.to_s
|
70
|
+
@source = prepare_source(File.read(@path))
|
71
|
+
@output_directory = options.fetch(:output_directory, '.')
|
72
|
+
@verbose = options.fetch( :verbose, $VERBOSE )
|
73
|
+
study
|
74
|
+
build_dependencies
|
75
|
+
|
76
|
+
yield(self) if block_given?
|
77
|
+
end
|
78
|
+
|
79
|
+
##################################################################
|
80
|
+
######## ATTRIBUTES AND ATTRIBUTE-ISH METHODS ####################
|
81
|
+
##################################################################
|
82
|
+
attr_reader :type, :name, :source
|
83
|
+
attr_accessor :output_directory, :verbose
|
84
|
+
|
85
|
+
def lexer_class_name
|
86
|
+
self.name + "::Lexer"
|
87
|
+
end
|
88
|
+
|
89
|
+
def lexer_file_name
|
90
|
+
if lexer? then base = name
|
91
|
+
elsif combined? then base = name + 'Lexer'
|
92
|
+
else return(nil)
|
93
|
+
end
|
94
|
+
return(base + '.rb')
|
95
|
+
end
|
96
|
+
|
97
|
+
def parser_class_name
|
98
|
+
name + "::Parser"
|
99
|
+
end
|
100
|
+
|
101
|
+
def parser_file_name
|
102
|
+
if parser? then base = name
|
103
|
+
elsif combined? then base = name + 'Parser'
|
104
|
+
else return(nil)
|
105
|
+
end
|
106
|
+
return(base + '.rb')
|
107
|
+
end
|
108
|
+
|
109
|
+
def tree_parser_class_name
|
110
|
+
name + "::TreeParser"
|
111
|
+
end
|
112
|
+
|
113
|
+
def tree_parser_file_name
|
114
|
+
tree? and name + '.rb'
|
115
|
+
end
|
116
|
+
|
117
|
+
def has_lexer?
|
118
|
+
@type == 'combined' || @type == 'lexer'
|
119
|
+
end
|
120
|
+
|
121
|
+
def has_parser?
|
122
|
+
@type == 'combined' || @type == 'parser'
|
123
|
+
end
|
124
|
+
|
125
|
+
def lexer?
|
126
|
+
@type == "lexer"
|
127
|
+
end
|
128
|
+
|
129
|
+
def parser?
|
130
|
+
@type == "parser"
|
131
|
+
end
|
132
|
+
|
133
|
+
def tree?
|
134
|
+
@type == "tree"
|
135
|
+
end
|
136
|
+
|
137
|
+
alias has_tree? tree?
|
138
|
+
|
139
|
+
def combined?
|
140
|
+
@type == "combined"
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def target_files(include_imports = true)
|
145
|
+
targets = []
|
146
|
+
|
147
|
+
for target_type in %w(lexer parser tree_parser)
|
148
|
+
target_name = self.send(:"#{target_type}_file_name") and
|
149
|
+
targets.push( output_directory / target_name )
|
150
|
+
end
|
151
|
+
|
152
|
+
targets.concat( imported_target_files ) if include_imports
|
153
|
+
return targets
|
154
|
+
end
|
155
|
+
|
156
|
+
def imports
|
157
|
+
@source.scan(/^\s*import\s+(\w+)\s*;/).
|
158
|
+
tap { |list| list.flatten! }
|
159
|
+
end
|
160
|
+
|
161
|
+
def imported_target_files
|
162
|
+
imports.map! do |delegate|
|
163
|
+
output_directory / "#{@name}_#{delegate}.rb"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
##################################################################
|
168
|
+
##### COMMAND METHODS ############################################
|
169
|
+
##################################################################
|
170
|
+
def compile(options = {})
|
171
|
+
if options[:force] or stale?
|
172
|
+
compile!(options)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def compile!(options = {})
|
177
|
+
command = build_command(options)
|
178
|
+
|
179
|
+
blab( command )
|
180
|
+
output = IO.popen(command) do |pipe|
|
181
|
+
pipe.read
|
182
|
+
end
|
183
|
+
|
184
|
+
case status = $?.exitstatus
|
185
|
+
when 0, 130
|
186
|
+
post_compile(options)
|
187
|
+
else compilation_failure!(command, status, output)
|
188
|
+
end
|
189
|
+
|
190
|
+
return target_files
|
191
|
+
end
|
192
|
+
|
193
|
+
def clean!
|
194
|
+
deleted = []
|
195
|
+
for target in target_files
|
196
|
+
if test(?f, target)
|
197
|
+
File.delete(target)
|
198
|
+
deleted << target
|
199
|
+
end
|
200
|
+
end
|
201
|
+
return deleted
|
202
|
+
end
|
203
|
+
|
204
|
+
def inspect
|
205
|
+
sprintf( "grammar %s (%s)", @name, @path )
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
def post_compile(options)
|
210
|
+
# do nothing for now
|
211
|
+
end
|
212
|
+
|
213
|
+
def blab( string, *args )
|
214
|
+
$stderr.printf( string + "\n", *args ) if @verbose
|
215
|
+
end
|
216
|
+
|
217
|
+
def default_antlr_jar
|
218
|
+
ENV[ 'ANTLR_JAR' ] || ANTLR3.antlr_jar
|
219
|
+
end
|
220
|
+
|
221
|
+
def compilation_failure!(command, status, output)
|
222
|
+
for f in target_files
|
223
|
+
test(?f, f) and File.delete(f)
|
224
|
+
end
|
225
|
+
raise CompilationFailure.new(self, command, status, output)
|
226
|
+
end
|
227
|
+
|
228
|
+
def build_dependencies
|
229
|
+
depends_on(@path)
|
230
|
+
|
231
|
+
if @source =~ /tokenVocab\s*=\s*(\S+)\s*;/
|
232
|
+
foreign_grammar_name = $1
|
233
|
+
token_file = output_directory / foreign_grammar_name + '.tokens'
|
234
|
+
grammar_file = File.dirname( path ) / foreign_grammar_name << '.g'
|
235
|
+
depends_on(token_file)
|
236
|
+
depends_on(grammar_file)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def shell_escape(token)
|
241
|
+
token = token.to_s.dup
|
242
|
+
token.empty? and return "''"
|
243
|
+
token.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, '\\\1')
|
244
|
+
token.gsub!(/\n/, "'\n'")
|
245
|
+
return token
|
246
|
+
end
|
247
|
+
|
248
|
+
def build_command(options)
|
249
|
+
parts = %w(java)
|
250
|
+
jar_path = options.fetch( :antlr_jar, default_antlr_jar )
|
251
|
+
parts.push('-cp', jar_path)
|
252
|
+
parts << 'org.antlr.Tool'
|
253
|
+
parts.push('-fo', output_directory)
|
254
|
+
options[:profile] and parts << '-profile'
|
255
|
+
options[:debug] and parts << '-debug'
|
256
|
+
options[:trace] and parts << '-trace'
|
257
|
+
options[:debug_st] and parts << '-XdbgST'
|
258
|
+
parts << File.expand_path(@path)
|
259
|
+
parts.map! { |part| shell_escape(part) }.join(' ') << ' 2>&1'
|
260
|
+
end
|
261
|
+
|
262
|
+
def prepare_source(text)
|
263
|
+
text.gsub(/([^\\])%/,'\1\\%').freeze
|
264
|
+
end
|
265
|
+
|
266
|
+
def study
|
267
|
+
@source =~ /^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/ or
|
268
|
+
raise Grammar::FormatError[source, path]
|
269
|
+
@name = $2
|
270
|
+
@type = $1 || 'combined'
|
271
|
+
end
|
272
|
+
end # class Grammar
|
273
|
+
|
274
|
+
class Grammar::InlineGrammar < Grammar
|
275
|
+
attr_accessor :host_file, :host_line
|
276
|
+
|
277
|
+
def initialize(source, options = {})
|
278
|
+
host = call_stack.find { |call| call.file != __FILE__ }
|
279
|
+
|
280
|
+
@host_file = File.expand_path(options[:file] || host.file)
|
281
|
+
@host_line = (options[:line] || host.line)
|
282
|
+
@output_directory = options.fetch(:output_directory, File.dirname(@host_file))
|
283
|
+
@verbose = options.fetch( :verbose, $VERBOSE )
|
284
|
+
|
285
|
+
source = source.to_s.fixed_indent(0)
|
286
|
+
source.strip!
|
287
|
+
@source = prepare_source(source)
|
288
|
+
study
|
289
|
+
write_to_disk
|
290
|
+
build_dependencies
|
291
|
+
|
292
|
+
yield(self) if block_given?
|
293
|
+
end
|
294
|
+
|
295
|
+
def output_directory
|
296
|
+
@output_directory and return @output_directory
|
297
|
+
File.basename( @host_file )
|
298
|
+
end
|
299
|
+
|
300
|
+
def path=(v)
|
301
|
+
previous, @path = @path, v.to_s
|
302
|
+
previous == @path or write_to_disk
|
303
|
+
end
|
304
|
+
|
305
|
+
def inspect
|
306
|
+
sprintf( 'inline grammar %s (%s:%s)', name, @host_file, @host_line )
|
307
|
+
end
|
308
|
+
private
|
309
|
+
def write_to_disk
|
310
|
+
@path ||= output_directory / @name + '.g'
|
311
|
+
test(?d, output_directory) or Dir.mkdir( output_directory )
|
312
|
+
unless test(?f, @path) and MD5.digest(@source) == MD5.digest(File.read(@path))
|
313
|
+
open(@path, 'w') { |f| f.write(@source) }
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end # class Grammar::InlineGrammar
|
317
|
+
|
318
|
+
class Grammar::CompilationFailure < StandardError
|
319
|
+
JAVA_TRACE = /^(org\.)?antlr\.\S+\(\S+\.java:\d+\)\s*/
|
320
|
+
attr_reader :grammar, :command, :status, :output
|
321
|
+
|
322
|
+
def initialize(grammar, command, status, output)
|
323
|
+
@command = command
|
324
|
+
@status = status
|
325
|
+
@output = output.gsub( JAVA_TRACE, '' )
|
326
|
+
|
327
|
+
message = <<-END.here_indent! % [command, status, grammar, @output]
|
328
|
+
| command ``%s'' failed with status %s
|
329
|
+
| %p
|
330
|
+
| ~ ~ ~ command output ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
|
331
|
+
| %s
|
332
|
+
END
|
333
|
+
|
334
|
+
super( message.chomp! || message )
|
335
|
+
end
|
336
|
+
end # error Grammar::CompilationFailure
|
337
|
+
|
338
|
+
class Grammar::FormatError < StandardError
|
339
|
+
attr_reader :file, :source
|
340
|
+
|
341
|
+
def self.[](*args)
|
342
|
+
new(*args)
|
343
|
+
end
|
344
|
+
|
345
|
+
def initialize(source, file = nil)
|
346
|
+
@file = file
|
347
|
+
@source = source
|
348
|
+
message = ''
|
349
|
+
if file.nil? # inline
|
350
|
+
message << "bad inline grammar source:\n"
|
351
|
+
message << ("-" * 80) << "\n"
|
352
|
+
message << @source
|
353
|
+
message[-1] == ?\n or message << "\n"
|
354
|
+
message << ("-" * 80) << "\n"
|
355
|
+
message << "could not locate a grammar name and type declaration matching\n"
|
356
|
+
message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
|
357
|
+
else
|
358
|
+
message << 'bad grammar source in file %p' % @file
|
359
|
+
message << ("-" * 80) << "\n"
|
360
|
+
message << @source
|
361
|
+
message[-1] == ?\n or message << "\n"
|
362
|
+
message << ("-" * 80) << "\n"
|
363
|
+
message << "could not locate a grammar name and type declaration matching\n"
|
364
|
+
message << "/^\s*(lexer|parser|tree)?\s*grammar\s*(\S+)\s*;/"
|
365
|
+
end
|
366
|
+
super(message)
|
367
|
+
end
|
368
|
+
end # error Grammar::FormatError
|
369
|
+
|
370
|
+
end
|
371
|
+
end
|