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