| #!/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 |