blob: 6d2c270a68c415d91f9b60c6ea4edf63d5ac9b74 [file] [log] [blame]
#!/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