blob: cb91acccf7a640452bc6bb2c4ac019be4fdaf4fd [file] [log] [blame]
#!/usr/bin/ruby
# encoding: utf-8
require 'antlr3'
require 'antlr3/test/core-extensions'
require 'antlr3/test/grammar'
require 'antlr3/test/call-stack'
require 'test/unit'
require 'spec'
module ANTLR3
module Test
module Location
attr_accessor :test_path
def test_group
File.basename( test_path, '.rb' )
end
def test_directory
File.dirname( test_path )
end
def local_path( *parts )
File.join( test_directory, *parts )
end
def output_directory( name = test_group )
local_path( name )
end
end # module Location
module NameSpace
#
# import( ruby_file ) => [ new constants, ... ]
# Read the source code from the path given by +ruby_file+ and
# evaluate it within the class body. Return new constants
# created in the class after the evaluation.
#
def import( ruby_file )
constants_before = constants
class_eval( File.read( ruby_file ), ruby_file, 1 )
constants - constants_before
end
def import_grammar_targets( grammar )
for file in grammar.target_files
import( file )
end
end
end
module GrammarManager
include Location
include NameSpace
DEFAULT_COMPILE_OPTIONS = {}
def add_default_compile_option( name, value )
DEFAULT_COMPILE_OPTIONS[ name ] = value
end
module_function :add_default_compile_option
if ANTLR_JAR = ENV[ 'ANTLR_JAR' ] || ANTLR3.antlr_jar
add_default_compile_option( :antlr_jar, ANTLR_JAR )
Grammar.global_dependency( ANTLR_JAR )
end
#
# Compile and load inline grammars on demand when their constant name
# is referenced in the code. This makes it easier to catch big errors
# quickly as test cases are run, instead of waiting a few minutes
# for all grammars to compile, and then discovering there's a big dumb
# error ruining most of the grammars.
#
def const_missing( name )
if g = grammars[ name.to_s ]
compile( g )
grammars.delete( name.to_s )
const_get( name )
elsif superclass.respond_to?( :grammars )
superclass.const_missing( name )
# ^-- for some reason, in ruby 1.9, rspec runs examples as instances of
# anonymous subclasses, of the actual test class, which messes up the
# assumptions made in the test code. Grammars are stored in @grammars belonging
# to the test class, so in 1.9, this method is called with @grammars = {}
# since it's a subclass
else
super
end
end
#
# An index of grammar file objects created in the test class
# (defined inline or loaded from a file)
#
def grammars
@grammars ||= {}
end
def grammar_count
grammars.length
end
def load_grammar( name )
path = local_path( name.to_s )
path =~ /\.g$/ or path << '.g'
grammar = Grammar.new( path, :output_directory => output_directory )
register_grammar( grammar )
return grammar
end
def inline_grammar( source, options = {} )
call = call_stack.find { |call| call.file != __FILE__ }
grammar = Grammar.inline source,
:output_directory => output_directory,
:file => ( call.file rescue nil ),
:line => ( call.line rescue nil )
register_grammar( grammar )
return grammar
end
def compile_options( defaults = nil )
@compile_options ||= DEFAULT_COMPILE_OPTIONS.clone
@compile_options.update( defaults ) if defaults
return @compile_options
end
def compile( grammar, options = {} )
grammar.compile( compile_options.merge( options ) )
import_grammar_targets( grammar )
return grammar
end
private
def register_grammar( grammar )
name = grammar.name
@grammars ||= {}
if conflict = @grammars[ name ] and conflict.source != grammar.source
message = "Multiple grammars exist with the name ``#{ name }''"
raise NameError, message
else
@grammars[ name ] = grammar
end
end
end # module GrammarManager
class Functional < ::Test::Unit::TestCase
extend GrammarManager
def self.inherited( klass )
super
klass.test_path = call_stack[ 0 ].file
end
def local_path( *args )
self.class.local_path( *args )
end
def test_path
self.class.test_path
end
def output_directory
self.class.output_directory
end
def inline_grammar( source )
call = call_stack.find { |call| call.file != __FILE__ }
grammar = Grammar.inline source,
:output_directory => output_directory,
:file => call.file,
:line => call.line
end
def compile_and_load( grammar, options = {} )
self.class.compile( grammar, options )
end
end # class Functional
module CaptureOutput
require 'stringio'
def output_buffer
defined?( @output_buffer ) or @output_buffer = StringIO.new( '' )
@output_buffer
end
def output
output_buffer.string
end
def say( *args )
output_buffer.puts( *args )
end
def capture( *args )
output_buffer.write( *args )
end
end
module RaiseErrors
def emit_error_message( msg )
# do nothing
end
def report_error( error )
raise error
end
end
module CollectErrors
def reported_errors
defined?( @reported_errors ) or @reported_errors = []
return @reported_errors
end
def emit_error_message( msg )
reported_errors << msg
end
end
end # module Test
end # module ANTLR3