#!/usr/bin/ruby require 'erb' require 'antlr3' module ANTLR3 module Template module Builder extend ClassMacros module ClassMethods attr_writer :template_library def template_library @template_library ||= ANTLR3::Template::Group.new end def return_scope_members super.push( :template ) end def load_templates( group_file ) @template_library = ANTLR3::Template::Group.load( group_file ) end def define_template( name, source, &block ) template_library.define_template( name, source, &block ) end end def self.included( klass ) super Class === klass and klass.extend( ClassMethods ) end def initialize( input, options = {} ) templates = @templates || options.fetch( :templates ) do self.class.template_library or ANTLR3::Template::Group.new end super( input, options ) self.templates = templates end shared_attribute( :templates ) def create_template( source, values = {} ) @templates.new( source, values ) end def fetch_template( name, values = {} ) @templates.fetch( name, values ) end end module RewriteBuilder include Builder def self.included( klass ) super Class === klass and klass.extend( Builder::ClassMethods ) end private def cast_input( input, options ) case input when TokenSource then TokenRewriteStream.new( input, options ) when IO, String if lexer_class = self.class.associated_lexer TokenRewriteStream.new( lexer_class.new( input, options ), options ) else raise ArgumentError, Util.tidy( <<-END, true ) | unable to automatically convert input #{ input.inspect } | to a ANTLR3::TokenStream object as #{ self.class } | does not appear to have an associated lexer class END end else super end end end autoload :GroupFile, 'antlr3/template/group-file' class Group < Module autoload :Lexer, 'antlr3/template/group-file' autoload :Parser, 'antlr3/template/group-file' def self.parse( source, options = {} ) namespace = options.fetch( :namespace, ::Object ) lexer = Lexer.new( source, options ) parser = Parser.new( lexer, options ) return( parser.group( namespace ) ) end def self.load( group_file, options = {} ) unless( File.file?( group_file ) ) dir = $LOAD_PATH.find do | d | File.file?( File.join( dir, group_file ) ) end or raise( LoadError, "no such template group file to load %s" % group_file ) group_file = File.join( dir, group_file ) end namespace = options.fetch( :namespace, ::Object ) input = ANTLR3::FileStream.new( group_file, options ) lexer = Lexer.new( input, options ) parser = Parser.new( lexer, options ) return( parser.group( namespace ) ) end def self.new( &block ) super do const_set( :TEMPLATES, {} ) block_given? and module_eval( &block ) end end def new( source, values = {} ) erb = ERB.new( source, nil, '%' ) template = Context.new( values ) template.extend( self ) sclass = class << template; self; end erb.def_method( sclass, 'to_s' ) return( template ) end def fetch( name, values = {} ) self::TEMPLATES.fetch( name.to_s ).new( values ) end def templates self::TEMPLATES end def template_defined?( name ) self::TEMPLATES.has_key?( name.to_s ) end def define_template( name, source, parameters = nil, &block ) name = name.to_s.dup.freeze Context.define( self, name, parameters ) do | tclass | self::TEMPLATES[ name ] = tclass ERB.new( source, nil, '%' ).def_method( tclass, 'to_s' ) define_template_methods( tclass ) end return( self ) end def alias_template( new_name, old_name ) new_name, old_name = new_name.to_s.dup.freeze, old_name.to_s context = self::TEMPLATES.fetch( old_name.to_s ) do raise( NameError, "undefined template `%s' for template group %p" % [ old_name, self ] ) end context.define_alias( new_name ) do | tclass | self::TEMPLATES[ new_name ] = tclass define_template_methods( tclass ) end return( self ) end private def define_template_methods( context ) name = context.name if params = context.parameters init = params.names.map do | param | "___[ #{ param.inspect } ] = #{ param }" end.join( "\n" ) module_eval( <<-END ) module_function def #{ name }( #{ params } ) TEMPLATES[ #{ name.inspect } ].new do | ___ | #{ init } end end def #{ name }!( #{ params } ) TEMPLATES[ #{ name.inspect } ].new do | ___ | #{ init } end.to_s end END else module_eval( <<-END ) module_function def #{ name }( values = {} ) TEMPLATES[ #{ name.inspect } ].new( values ) end def #{ name }!( values = {} ) TEMPLATES[ #{ name.inspect } ].new( values ).to_s end END end end end class Context VARIABLE_FORM = /^(@)?[a-z_\x80-\xff][\w\x80-\xff]*$/ SETTER_FORM = /^([a-z_\x80-\xff][\w\x80-\xff]*)=$/ ATTR_FORM = /^[a-z_\x80-\xff][\w\x80-\xff]*$/ class << self attr_accessor :group, :name, :parameters protected :group=, :name= def define_alias( name ) new = clone new.name = name new.group = @group block_given? and yield( new ) return( new ) end def define( group, name, parameters ) Class.new( self ) do include( group ) @group = group @name = name @parameters = parameters block_given? and yield( self ) end end end def method_missing( method, *args ) case name = method.to_s when SETTER_FORM then return( self[ $1 ] = args.first ) when ATTR_FORM args.empty? and has_ivar?( name ) and return( self[ name ] ) end super end def []=( name, value ) instance_variable_set( make_ivar( name ), value ) end def []( name ) name = make_ivar( name ) instance_variable_defined?( name ) ? instance_variable_get( name ) : nil end def <<( variable_map ) variable_map.each_pair do | name, value | self[ name ] = value end return( self ) end def initialize( variable_map = nil ) variable_map and self << variable_map block_given? and yield( self ) end private def has_ivar?( name ) instance_variable_defined?( make_ivar( name ) ) end def make_ivar( name ) name = name.to_s VARIABLE_FORM =~ name or raise ArgumentError, "cannot convert %p to an instance variable name" % name $1 ? name : "@#{ name }" end end Parameter = Struct.new( :name, :default ) class Parameter def to_s default ? "#{ name } = #{ default }" : "#{ name }" end end class ParameterList < ::Array attr_accessor :splat, :block def self.default new.add( :values ) do | p | p.default = '{}' end end def names names = map { | param | param.name.to_s } @splat and names << @splat.to_s @block and names << @block.to_s return( names ) end def add( name, options = nil ) param = case name when Parameter then name else Parameter.new( name.to_s ) end if options default = options[ :default ] and param.default = default param.splat = options.fetch( :splat, false ) param.block = options.fetch( :block, false ) end block_given? and yield( param ) push( param ) return( self ) end def to_s signature = join( ', ' ) @splat and signature << ", *" << @splat.to_s @block and signature << ", &" << @block.to_s return( signature ) end end end end