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 37 # 38 # import( ruby_file ) => [ new constants, ... ] 39 # Read the source code from the path given by +ruby_file+ and 40 # evaluate it within the class body. Return new constants 41 # created in the class after the evaluation. 42 # 43 def import( ruby_file ) 44 constants_before = constants 45 class_eval( File.read( ruby_file ), ruby_file, 1 ) 46 constants - constants_before 47 end 48 49 def import_grammar_targets( grammar ) 50 for file in grammar.target_files 51 import( file ) 52 end 53 end 54 end 55 56 module GrammarManager 57 include Location 58 include NameSpace 59 60 DEFAULT_COMPILE_OPTIONS = {} 61 62 def add_default_compile_option( name, value ) 63 DEFAULT_COMPILE_OPTIONS[ name ] = value 64 end 65 module_function :add_default_compile_option 66 67 if ANTLR_JAR = ENV[ 'ANTLR_JAR' ] || ANTLR3.antlr_jar 68 add_default_compile_option( :antlr_jar, ANTLR_JAR ) 69 70 Grammar.global_dependency( ANTLR_JAR ) 71 end 72 73 # 74 # Compile and load inline grammars on demand when their constant name 75 # is referenced in the code. This makes it easier to catch big errors 76 # quickly as test cases are run, instead of waiting a few minutes 77 # for all grammars to compile, and then discovering there's a big dumb 78 # error ruining most of the grammars. 79 # 80 def const_missing( name ) 81 if g = grammars[ name.to_s ] 82 compile( g ) 83 grammars.delete( name.to_s ) 84 const_get( name ) 85 elsif superclass.respond_to?( :grammars ) 86 superclass.const_missing( name ) 87 # ^-- for some reason, in ruby 1.9, rspec runs examples as instances of 88 # anonymous subclasses, of the actual test class, which messes up the 89 # assumptions made in the test code. Grammars are stored in @grammars belonging 90 # to the test class, so in 1.9, this method is called with @grammars = {} 91 # since it's a subclass 92 else 93 super 94 end 95 end 96 97 # 98 # An index of grammar file objects created in the test class 99 # (defined inline or loaded from a file) 100 # 101 def grammars 102 @grammars ||= {} 103 end 104 105 def grammar_count 106 grammars.length 107 end 108 109 def load_grammar( name ) 110 path = local_path( name.to_s ) 111 path =~ /\.g$/ or path << '.g' 112 grammar = Grammar.new( path, :output_directory => output_directory ) 113 register_grammar( grammar ) 114 return grammar 115 end 116 117 def inline_grammar( source, options = {} ) 118 call = call_stack.find { |call| call.file != __FILE__ } 119 grammar = Grammar.inline source, 120 :output_directory => output_directory, 121 :file => ( call.file rescue nil ), 122 :line => ( call.line rescue nil ) 123 register_grammar( grammar ) 124 return grammar 125 end 126 127 def compile_options( defaults = nil ) 128 @compile_options ||= DEFAULT_COMPILE_OPTIONS.clone 129 @compile_options.update( defaults ) if defaults 130 return @compile_options 131 end 132 133 def compile( grammar, options = {} ) 134 grammar.compile( compile_options.merge( options ) ) 135 import_grammar_targets( grammar ) 136 return grammar 137 end 138 139 private 140 141 def register_grammar( grammar ) 142 name = grammar.name 143 @grammars ||= {} 144 145 if conflict = @grammars[ name ] and conflict.source != grammar.source 146 message = "Multiple grammars exist with the name ``#{ name }''" 147 raise NameError, message 148 else 149 @grammars[ name ] = grammar 150 end 151 end 152 end # module GrammarManager 153 154 class Functional < ::Test::Unit::TestCase 155 extend GrammarManager 156 157 def self.inherited( klass ) 158 super 159 klass.test_path = call_stack[ 0 ].file 160 end 161 162 def local_path( *args ) 163 self.class.local_path( *args ) 164 end 165 166 def test_path 167 self.class.test_path 168 end 169 170 def output_directory 171 self.class.output_directory 172 end 173 174 def inline_grammar( source ) 175 call = call_stack.find { |call| call.file != __FILE__ } 176 grammar = Grammar.inline source, 177 :output_directory => output_directory, 178 :file => call.file, 179 :line => call.line 180 end 181 182 def compile_and_load( grammar, options = {} ) 183 self.class.compile( grammar, options ) 184 end 185 end # class Functional 186 187 188 189 module CaptureOutput 190 require 'stringio' 191 def output_buffer 192 defined?( @output_buffer ) or @output_buffer = StringIO.new( '' ) 193 @output_buffer 194 end 195 196 def output 197 output_buffer.string 198 end 199 200 def say( *args ) 201 output_buffer.puts( *args ) 202 end 203 204 def capture( *args ) 205 output_buffer.write( *args ) 206 end 207 end 208 209 module RaiseErrors 210 def emit_error_message( msg ) 211 # do nothing 212 end 213 214 def report_error( error ) 215 raise error 216 end 217 end 218 219 module CollectErrors 220 def reported_errors 221 defined?( @reported_errors ) or @reported_errors = [] 222 return @reported_errors 223 end 224 225 def emit_error_message( msg ) 226 reported_errors << msg 227 end 228 end 229 230 end # module Test 231 end # module ANTLR3 232