334 lines
7.8 KiB
Ruby
334 lines
7.8 KiB
Ruby
#!/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
|