lucy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mar...@apache.org
Subject svn commit: r806976 - in /lucene/lucy/trunk/boilerplater: lib/Boilerplater/Method.pm lib/Boilerplater/Parser.pm t/201-method.t
Date Sun, 23 Aug 2009 14:29:03 GMT
Author: marvin
Date: Sun Aug 23 14:29:02 2009
New Revision: 806976

URL: http://svn.apache.org/viewvc?rev=806976&view=rev
Log:
Commit LUCY-22, adding Boilerplater::Method.

Added:
    lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm   (with props)
    lucene/lucy/trunk/boilerplater/t/201-method.t   (with props)
Modified:
    lucene/lucy/trunk/boilerplater/lib/Boilerplater/Parser.pm

Added: lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm
URL: http://svn.apache.org/viewvc/lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm?rev=806976&view=auto
==============================================================================
--- lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm (added)
+++ lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm Sun Aug 23 14:29:02 2009
@@ -0,0 +1,332 @@
+use strict;
+use warnings;
+
+package Boilerplater::Method;
+use base qw( Boilerplater::Function );
+use Boilerplater::Util qw( verify_args );
+use Carp;
+
+my %new_PARAMS = (
+    return_type => undef,
+    class_name  => undef,
+    class_cnick => undef,
+    param_list  => undef,
+    macro_sym   => undef,
+    docucomment => undef,
+    parcel      => undef,
+    abstract    => undef,
+    final       => undef,
+    exposure    => 'parcel',
+    # Private, used only by finalize().
+    novel         => undef,
+    short_typedef => undef,
+);
+
+sub new {
+    my ( $class, %args ) = @_;
+    verify_args( \%new_PARAMS, %args ) or confess $@;
+    my $abstract      = delete $args{abstract};
+    my $final         = delete $args{final};
+    my $macro_sym     = delete $args{macro_sym};
+    my $novel         = delete $args{novel};
+    my $short_typedef = delete $args{short_typedef};
+
+    # Validate macro_sym, derive micro_sym.
+    confess("macro_sym is required") unless defined $macro_sym;
+    confess("Invalid macro_sym: '$macro_sym'")
+        unless $macro_sym =~ /^[A-Z][A-Za-z0-9]*(?:_[A-Z0-9][A-Za-z0-9]*)*$/;
+    $args{micro_sym} = lc($macro_sym);
+
+    # Create self, add in novel member vars.
+    my $self = $class->SUPER::new(%args);
+    $self->{macro_sym} = $macro_sym;
+    $self->{abstract}  = $abstract;
+    $self->{final}     = $final;
+
+    # Assume that this method is novel until we discover when applying
+    # inheritance that it was overridden.
+    $self->{novel} = defined $novel ? $novel : 1;
+
+    # Verify that the first element in the arg list is a self.
+    my $args = $self->get_param_list->get_variables;
+    confess "Not enough args" unless @$args;
+    my $specifier = $args->[0]->get_type->get_specifier;
+    my ($struct_name) = $self->{class_name} =~ /(\w+)$/;
+    confess
+        "First arg type doesn't match class: $self->{class_name} $specifier"
+        unless $specifier eq $self->get_prefix . $struct_name;
+
+    # Cache typedef.
+    $self->{short_typedef}
+        = defined $short_typedef
+        ? $short_typedef
+        : "$self->{class_cnick}_$self->{micro_sym}_t";
+
+    return $self;
+}
+
+sub abstract      { shift->{abstract} }
+sub novel         { shift->{novel} }
+sub final         { shift->{final} }
+sub get_macro_sym { shift->{macro_sym} }
+
+sub self_type { shift->get_param_list->get_variables->[0]->get_type }
+
+sub short_method_sym {
+    my ( $self, $invoker ) = @_;
+    confess("Missing invoker") unless $invoker;
+    return $invoker . "_$self->{macro_sym}";
+}
+
+sub full_method_sym {
+    my ( $self, $invoker ) = @_;
+    return $self->get_Prefix . $self->short_method_sym($invoker);
+}
+
+# The name of the variable which stores the method's vtable offset.
+sub full_offset_sym {
+    my ( $self, $invoker ) = @_;
+    confess("Missing invoker") unless $invoker;
+    return $self->get_Prefix . "$invoker\_$self->{macro_sym}_OFFSET";
+}
+
+sub full_callback_sym { shift->full_func_sym . "_CALLBACK" }
+sub full_override_sym { shift->full_func_sym . "_OVERRIDE" }
+
+sub short_typedef { shift->{short_typedef} }
+sub full_typedef {
+    my $self = shift;
+    return $self->get_prefix . $self->{short_typedef};
+}
+
+sub override {
+    my ( $self, $orig ) = @_;
+
+    # Check that the override attempt is legal.
+    confess(  "Attempt to override final method '$orig->{micro_sym}' from "
+            . "$orig->{class_cnick} by $self->{class_cnick}" )
+        if $orig->final;
+    if ( !$self->compatible($orig) ) {
+        my $func_name = $self->full_func_sym;
+        my $orig_func = $orig->full_func_sym;
+        confess("Non-matching signatures for $func_name and $orig_func");
+    }
+
+    # Mark the Method as no longer novel.
+    $self->{novel} = 0;
+}
+
+sub compatible {
+    my ( $self, $other ) = @_;
+    return 0 unless $self->{macro_sym} eq $other->{macro_sym};
+    return 0 if ( $self->public xor $other->public );
+    my $param_list       = $self->get_param_list;
+    my $other_param_list = $other->get_param_list;
+    my $arg_vars         = $param_list->get_variables;
+    my $other_vars       = $other_param_list->get_variables;
+    my $initial_vals     = $param_list->get_initial_values;
+    my $other_vals       = $other_param_list->get_initial_values;
+    return 0 unless @$arg_vars == @$other_vars;
+    return 0 unless @$initial_vals == @$other_vals;
+
+    # Validate initial values.
+    for ( my $i = 1; $i <= $#$arg_vars; $i++ ) {
+        return 0 unless $other_vars->[$i]->equals( $arg_vars->[$i] );
+        my $val       = $initial_vals->[$i];
+        my $other_val = $other_vals->[$i];
+        if ( defined $val ) {
+            return 0 unless defined $other_val;
+            return 0 unless $val eq $other_val;
+        }
+        else {
+            return 0 if defined $other_val;
+        }
+    }
+
+    return 1;
+}
+
+sub finalize {
+    my $self = shift;
+    my %args;
+    $args{$_} = $self->{$_} for keys %new_PARAMS;    # including short_typedef
+    $args{final} = 1;
+    my $finalized = $self->new(%args);
+    $finalized->{novel} = $self->{novel};
+    return $finalized;
+}
+
+1;
+
+__END__
+
+__POD__
+
+=head1 NAME
+
+Boilerplater::Method - Metadata describing an instance method.
+
+=head1 DESCRIPTION
+
+Boilerplater::Method is a specialized subclass of Boilerplater::Function, with
+the first argument required to be an Obj.
+
+When compiling Boilerplater code to C, Method objects generate all the code
+that Function objects do, but also create symbols for indirect invocation via
+VTable.
+
+=head1 METHODS
+
+=head2 new
+
+    my $type = Boilerplater::Method->new(
+        parcel      => 'MyProject'                 # default: special
+        class_name  => 'MyProject::FooFactory',    # required
+        class_cnick => 'FooFact ',                 # default: special
+        macro_sym   => 'Count',                    # required
+        return_type => $void_type                  # required
+        param_list  => $param_list,                # required
+        exposure    => undef,                      # default: 'parcel'
+        docucomment => $docucomment,               # default: undef
+        abstract    => undef,                      # default: undef
+        final       => 1,                          # default: undef 
+    );
+
+=over
+
+=item * B<param_list> - A Boilerplater::ParamList.  The first element must be an
+object of the class identified by C<class_name>.
+
+=item * B<macro_sym> - The mixed case name which will be used when invoking the
+method.
+
+=item * B<abstract> - Indicate whether the method is abstract.
+
+=item * B<final> - Indicate whether the method is final.
+
+=item * B<parcel>, B<class_name>, B<class_cnick>, B<return_type>,
+B<docucomment>, - see L<Boilerplater::Function>.
+
+=back
+
+=head2 abstract final get_macro_sym 
+
+Getters.
+
+=head2 novel
+
+Returns true if the method's class is the first in the inheritance hierarchy
+in which the method was declared -- i.e. the method is neither inherited nor
+overridden.
+
+=head2 self_type
+
+Return the L<Boilerplater::Type> for C<self>.
+
+=head2 short_method_sym
+
+    # e.g. "FooFactJr_Do_Stuff"
+    my $short_sym = $method->short_method_sym("FooFactJr");
+
+Returns the symbol used to invoke the method (minus the parcel Prefix).
+
+=head2 full_method_sym
+
+    # e.g. "MyProj_FooFactJr_Do_Stuff"
+    my $full_sym = $method->full_method_sym("FooFactJr");
+
+Returns the fully-qualified symbol used to invoke the method.
+
+=head2 full_offset_sym
+
+    # e.g. "MyProj_FooFactJr_Do_Stuff_OFFSET"
+    my $offset_sym = $method->full_offset_sym("FooFactJr");
+
+Returns the fully qualified name of the variable which stores the method's
+vtable offset.
+
+=head2 full_callback_sym
+
+    # e.g. "myproj_FooFactJr_do_stuff_CALLBACK"
+    my $callback_sym = $method->full_calback_sym("FooFactJr");
+
+Returns the fully qualified name of the variable which stores the method's
+Callback object.
+
+=head2 full_override_sym
+
+    # e.g. "myproj_FooFactJr_do_stuff_OVERRIDE"
+    my $override_func_sym = $method->full_override_sym("FooFactJr");
+
+Returns the fully qualified name of the function which implements the callback
+to the host in the event that a host method has been defined which overrides
+this method.
+
+=head2 short_typedef
+
+    # e.g. "FooFactJr_do_stuff_t"
+    my $short_typedef = $method->short_typedef;
+
+Returns the typedef symbol for this method, which is derived from the class
+nick of the first class in which the method was declared.
+
+=head2 full_typedef
+
+    # e.g. "myproj_FooFactJr_do_stuff_t"
+    my $full_typedef = $method->full_typedef;
+
+Returns the fully-qualified typedef symbol including parcel prefix.
+
+=head2 override
+
+    $method->override($method_being_overridden);
+
+Let the Method know that it is overriding a method which was defined in a
+parent class, and verify that the override is valid.
+
+All methods start out believing that they are "novel", because we don't know
+about inheritance until we build the hierarchy after all files have been
+parsed.  override() is a way of going back and relabeling a method as
+overridden when new information has become available: in this case, that a
+parent class has defined a method with the same name.
+
+=head2 finalize
+
+    my $final_method = $method->finalize;
+
+As with override, above, this is for going back and changing the nature of a
+Method after new information has become available -- typically, when we
+discover that the method has been inherited by a "final" class.
+
+However, we don't modify the original Method as with override().  Inherited
+Method objects are shared between parent and child classes; if a shared Method
+object were to become final, it would interfere with its own inheritance.  So,
+we make a copy, slightly modified to indicate that it is "final".
+
+=head2 compatible
+
+    confess("Can't override") unless $method->compatible($other);
+
+Returns true if the methods have signatures and attributes which allow
+one to override the other.
+
+=head1 COPYRIGHT AND LICENSE
+
+    /**
+     * Copyright 2009 The Apache Software Foundation
+     *
+     * Licensed under the Apache License, Version 2.0 (the "License");
+     * you may not use this file except in compliance with the License.
+     * You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+     * implied.  See the License for the specific language governing
+     * permissions and limitations under the License.
+     */
+
+=cut
+

Propchange: lucene/lucy/trunk/boilerplater/lib/Boilerplater/Method.pm
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/lucy/trunk/boilerplater/lib/Boilerplater/Parser.pm
URL: http://svn.apache.org/viewvc/lucene/lucy/trunk/boilerplater/lib/Boilerplater/Parser.pm?rev=806976&r1=806975&r2=806976&view=diff
==============================================================================
--- lucene/lucy/trunk/boilerplater/lib/Boilerplater/Parser.pm (original)
+++ lucene/lucy/trunk/boilerplater/lib/Boilerplater/Parser.pm Sun Aug 23 14:29:02 2009
@@ -17,6 +17,7 @@
 use Boilerplater::Variable;
 use Boilerplater::DocuComment;
 use Boilerplater::Function;
+use Boilerplater::Method;
 use Carp;
 
 our $grammar = <<'END_GRAMMAR';

Added: lucene/lucy/trunk/boilerplater/t/201-method.t
URL: http://svn.apache.org/viewvc/lucene/lucy/trunk/boilerplater/t/201-method.t?rev=806976&view=auto
==============================================================================
--- lucene/lucy/trunk/boilerplater/t/201-method.t (added)
+++ lucene/lucy/trunk/boilerplater/t/201-method.t Sun Aug 23 14:29:02 2009
@@ -0,0 +1,122 @@
+use strict;
+use warnings;
+
+use Test::More tests => 30;
+
+BEGIN { use_ok('Boilerplater::Method') }
+use Boilerplater::Parser;
+
+my $parser = Boilerplater::Parser->new;
+$parser->parcel_definition('parcel Boil;')
+    or die "failed to process parcel_definition";
+
+my %args = (
+    parcel      => 'Boil',
+    return_type => $parser->type('Obj*'),
+    class_name  => 'Boil::Foo',
+    class_cnick => 'Foo',
+    param_list  => $parser->param_list('(Foo *self, i32_t count = 0)'),
+    macro_sym   => 'Return_An_Obj',
+);
+
+my $method = Boilerplater::Method->new(%args);
+isa_ok( $method, "Boilerplater::Method" );
+
+ok( $method->parcel, "parcel exposure by default" );
+
+eval { my $death = Boilerplater::Method->new( %args, extra_arg => undef ) };
+like( $@, qr/extra_arg/, "Extra arg kills constructor" );
+
+eval { Boilerplater::Method->new( %args, macro_sym => 'return_an_obj' ); };
+like( $@, qr/macro_sym/, "Invalid macro_sym kills constructor" );
+
+my $dupe = Boilerplater::Method->new(%args);
+ok( $method->compatible($dupe), "compatible()" );
+
+my $macro_sym_differs
+    = Boilerplater::Method->new( %args, macro_sym => 'Eat' );
+ok( !$method->compatible($macro_sym_differs),
+    "different macro_sym spoils compatible()"
+);
+ok( !$macro_sym_differs->compatible($method), "... reversed" );
+
+my $extra_param = Boilerplater::Method->new( %args,
+    param_list => $parser->param_list('(Foo *self, i32_t count = 0, int b)'),
+);
+ok( !$method->compatible($macro_sym_differs),
+    "extra param spoils compatible()"
+);
+ok( !$extra_param->compatible($method), "... reversed" );
+
+my $default_differs = Boilerplater::Method->new( %args,
+    param_list => $parser->param_list('(Foo *self, i32_t count = 1)'), );
+ok( !$method->compatible($default_differs),
+    "different initial_value spoils compatible()"
+);
+ok( !$default_differs->compatible($method), "... reversed" );
+
+my $missing_default = Boilerplater::Method->new( %args,
+    param_list => $parser->param_list('(Foo *self, i32_t count)'), );
+ok( !$method->compatible($missing_default),
+    "missing initial_value spoils compatible()"
+);
+ok( !$missing_default->compatible($method), "... reversed" );
+
+my $param_name_differs = Boilerplater::Method->new( %args,
+    param_list => $parser->param_list('(Foo *self, i32_t countess)'), );
+ok( !$method->compatible($param_name_differs),
+    "different param name spoils compatible()"
+);
+ok( !$param_name_differs->compatible($method), "... reversed" );
+
+my $param_type_differs = Boilerplater::Method->new( %args,
+    param_list => $parser->param_list('(Foo *self, u32_t count)'), );
+ok( !$method->compatible($param_type_differs),
+    "different param type spoils compatible()"
+);
+ok( !$param_type_differs->compatible($method), "... reversed" );
+
+my $self_type_differs = Boilerplater::Method->new(
+    %args,
+    class_name  => 'Boil::Bar',
+    class_cnick => 'Bar',
+    param_list  => $parser->param_list('(Bar *self, i32_t count = 0)'),
+);
+ok( $method->compatible($self_type_differs),
+    "different self type still compatible(), since can't test inheritance" );
+ok( $self_type_differs->compatible($method), "... reversed" );
+
+my $not_final = Boilerplater::Method->new(%args);
+my $final     = $not_final->finalize;
+
+eval { $method->override($final); };
+like( $@, qr/final/i, "Can't override final method" );
+
+delete $not_final->{final};
+delete $final->{final};
+is_deeply( $not_final, $final, "Finalize clones properly" );
+
+for my $meth_meth (qw( short_method_sym full_method_sym full_offset_sym)) {
+    eval { my $blah = $method->$meth_meth; };
+    like( $@, qr/invoker/, "$meth_meth requires invoker" );
+}
+
+my %sub_args = ( class => 'Boil::Obj', cnick => 'Obj' );
+
+isa_ok(
+    $parser->subroutine_declaration_statement( $_, 0, %sub_args )->{declared},
+    "Boilerplater::Method",
+    "method declaration: $_"
+    )
+    for (
+    'public int Do_Foo(Obj *self);',
+    'parcel Obj* Gimme_An_Obj(Obj *self);',
+    'void Do_Whatever(Obj *self, u32_t a_num, float real);',
+    'private Foo* Fetch_Foo(Obj *self, int num);',
+    );
+
+ok( $parser->subroutine_declaration_statement( $_, 0, %sub_args )->{declared}
+        ->final,
+    "final method: $_"
+) for ( 'public final void The_End(Obj *self);', );
+

Propchange: lucene/lucy/trunk/boilerplater/t/201-method.t
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message