X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FFunction%2FParameters.pm;h=27c018e306af75c5fa296ef1cd74593ff4b78758;hb=65c90cb1d1e0f41cbdeb4b84996b400841a6f31e;hp=87cbd3b5c20d4fff99b0cf6388c69237a1acb62a;hpb=7dd355352bd7dbd40d3c364250cd9d1b905cb262;p=p5sagit%2FFunction-Parameters.git diff --git a/lib/Function/Parameters.pm b/lib/Function/Parameters.pm index 87cbd3b..27c018e 100644 --- a/lib/Function/Parameters.pm +++ b/lib/Function/Parameters.pm @@ -2,19 +2,18 @@ package Function::Parameters; use v5.14.0; -use strict; use warnings; +use Carp qw(confess); + use XSLoader; BEGIN { - our $VERSION = '0.05_01'; + our $VERSION = '1.0101_01'; + our $XS_VERSION = $VERSION; + $VERSION = eval $VERSION; XSLoader::load; } -use B::Hooks::EndOfScope qw(on_scope_end); -use Carp qw(confess); -use bytes (); - sub _assert_valid_identifier { my ($name, $with_dollar) = @_; my $bonus = $with_dollar ? '\$' : ''; @@ -22,19 +21,66 @@ sub _assert_valid_identifier { or confess qq{"$name" doesn't look like a valid identifier}; } +sub _assert_valid_attributes { + my ($attrs) = @_; + $attrs =~ /^\s*:\s*[^\W\d]\w*\s*(?:(?:\s|:\s*)[^\W\d]\w*\s*)*(?:\(|\z)/ + or confess qq{"$attrs" doesn't look like valid attributes}; +} + my @bare_arms = qw(function method); my %type_map = ( - function => { name => 'optional' }, - method => { name => 'optional', shift => '$self' }, + function => { + name => 'optional', + default_arguments => 1, + check_argument_count => 0, + named_parameters => 1, + types => 1, + }, + method => { + name => 'optional', + default_arguments => 1, + check_argument_count => 0, + named_parameters => 1, + types => 1, + attrs => ':method', + shift => '$self', + invocant => 1, + }, + classmethod => { + name => 'optional', + default_arguments => 1, + check_argument_count => 0, + named_parameters => 1, + types => 1, + attributes => ':method', + shift => '$class', + invocant => 1, + }, ); +for my $k (keys %type_map) { + $type_map{$k . '_strict'} = { + %{$type_map{$k}}, + check_argument_count => 1, + }; +} sub import { my $class = shift; - @_ or @_ = ('fun', 'method'); + if (!@_) { + @_ = { + fun => 'function', + method => 'method', + }; + } + if (@_ == 1 && $_[0] eq ':strict') { + @_ = { + fun => 'function_strict', + method => 'method_strict', + }; + } if (@_ == 1 && ref($_[0]) eq 'HASH') { - @_ = map [$_, $_[0]{$_}], keys %{$_[0]} - or return; + @_ = map [$_, $_[0]{$_}], keys %{$_[0]}; } my %spec; @@ -45,35 +91,59 @@ sub import { ? $proto : [$proto, $bare_arms[$bare++] || confess(qq{Don't know what to do with "$proto"})] ; - my ($name, $type) = @$item; + my ($name, $proto_type) = @$item; _assert_valid_identifier $name; - unless (ref $type) { - # use '||' instead of 'or' to preserve $type in the error message - $type = $type_map{$type} - || confess qq["$type" doesn't look like a valid type (one of ${\join ', ', sort keys %type_map})]; - } - $type->{name} ||= 'optional'; - $type->{name} =~ /^(?:optional|required|prohibited)\z/ - or confess qq["$type->{name}" doesn't look like a valid name attribute (one of optional, required, prohibited)]; - if ($type->{shift}) { - _assert_valid_identifier $type->{shift}, 1; - bytes::length($type->{shift}) < SHIFT_NAME_LIMIT - or confess qq["$type->{shift}" is longer than I can handle]; + unless (ref $proto_type) { + # use '||' instead of 'or' to preserve $proto_type in the error message + $proto_type = $type_map{$proto_type} + || confess qq["$proto_type" doesn't look like a valid type (one of ${\join ', ', sort keys %type_map})]; } + + my %type = %$proto_type; + my %clean; + + $clean{name} = delete $type{name} || 'optional'; + $clean{name} =~ /^(?:optional|required|prohibited)\z/ + or confess qq["$clean{name}" doesn't look like a valid name attribute (one of optional, required, prohibited)]; + + $clean{shift} = delete $type{shift} || ''; + _assert_valid_identifier $clean{shift}, 1 if $clean{shift}; + + $clean{attrs} = join ' ', map delete $type{$_} || (), qw(attributes attrs); + _assert_valid_attributes $clean{attrs} if $clean{attrs}; - $spec{$name} = $type; + $clean{default_arguments} = + exists $type{default_arguments} + ? !!delete $type{default_arguments} + : 1 + ; + $clean{check_argument_count} = !!delete $type{check_argument_count}; + $clean{invocant} = !!delete $type{invocant}; + $clean{named_parameters} = !!delete $type{named_parameters}; + $clean{types} = !!delete $type{types}; + + %type and confess "Invalid keyword property: @{[keys %type]}"; + + $spec{$name} = \%clean; } for my $kw (keys %spec) { my $type = $spec{$kw}; - $^H{HINTK_SHIFT_ . $kw} = $type->{shift} || ''; - $^H{HINTK_NAME_ . $kw} = - $type->{name} eq 'prohibited' ? FLAG_NAME_PROHIBITED : - $type->{name} eq 'required' ? FLAG_NAME_REQUIRED : - FLAG_NAME_OPTIONAL + my $flags = + $type->{name} eq 'prohibited' ? FLAG_ANON_OK : + $type->{name} eq 'required' ? FLAG_NAME_OK : + FLAG_ANON_OK | FLAG_NAME_OK ; + $flags |= FLAG_DEFAULT_ARGS if $type->{default_arguments}; + $flags |= FLAG_CHECK_NARGS | FLAG_CHECK_TARGS if $type->{check_argument_count}; + $flags |= FLAG_INVOCANT if $type->{invocant}; + $flags |= FLAG_NAMED_PARAMS if $type->{named_parameters}; + $flags |= FLAG_TYPES_OK if $type->{types}; + $^H{HINTK_FLAGS_ . $kw} = $flags; + $^H{HINTK_SHIFT_ . $kw} = $type->{shift}; + $^H{HINTK_ATTRS_ . $kw} = $type->{attrs}; $^H{+HINTK_KEYWORDS} .= "$kw "; } } @@ -91,30 +161,92 @@ sub unimport { } } -sub _fini { - on_scope_end { - xs_fini; + +our %metadata; + +sub _register_info { + my ( + $key, + $declarator, + $invocant, + $invocant_type, + $positional_required, + $positional_optional, + $named_required, + $named_optional, + $slurpy, + $slurpy_type, + ) = @_; + + my $info = { + declarator => $declarator, + invocant => defined $invocant ? [$invocant, $invocant_type] : undef, + slurpy => defined $slurpy ? [$slurpy , $slurpy_type ] : undef, + positional_required => $positional_required, + positional_optional => $positional_optional, + named_required => $named_required, + named_optional => $named_optional, }; + + $metadata{$key} = $info; } +sub _mkparam1 { + my ($pair) = @_; + my ($v, $t) = @{$pair || []} or return undef; + Function::Parameters::Param->new( + name => $v, + type => $t, + ) +} + +sub _mkparams { + my @r; + while (my ($v, $t) = splice @_, 0, 2) { + push @r, Function::Parameters::Param->new( + name => $v, + type => $t, + ); + } + \@r +} + +sub info { + my ($func) = @_; + my $key = _cv_root $func or return undef; + my $info = $metadata{$key} or return undef; + require Function::Parameters::Info; + Function::Parameters::Info->new( + keyword => $info->{declarator}, + invocant => _mkparam1($info->{invocant}), + slurpy => _mkparam1($info->{slurpy}), + (map +("_$_" => _mkparams @{$info->{$_}}), glob '{positional,named}_{required,optional}') + ) +} 'ok' __END__ +=encoding UTF-8 + =head1 NAME Function::Parameters - subroutine definitions with parameter lists =head1 SYNOPSIS - use Function::Parameters; + use Function::Parameters qw(:strict); + # simple function fun foo($bar, $baz) { return $bar + $baz; } - fun mymap($fun, @args) :(&@) { + # function with prototype + fun mymap($fun, @args) + :(&@) + { my @res; for (@args) { push @res, $fun->($_); @@ -124,140 +256,507 @@ Function::Parameters - subroutine definitions with parameter lists print "$_\n" for mymap { $_ * 2 } 1 .. 4; + # method with implicit $self method set_name($name) { $self->{name} = $name; } + + # method with explicit invocant + method new($class: %init) { + return bless { %init }, $class; + } + + # function with optional parameters + fun search($haystack, $needle = qr/^(?!)/, $offset = 0) { + ... + } + + # method with named parameters + method resize(:$width, :$height) { + $self->{width} = $width; + $self->{height} = $height; + } + + $obj->resize(height => 4, width => 5); + + # function with named optional parameters + fun search($haystack, :$needle = qr/^(?!)/, :$offset = 0) { + ... + } + + my $results = search $text, offset => 200; -=cut +=head1 DESCRIPTION -=pod +This module extends Perl with keywords that let you define functions with +parameter lists. It uses Perl's L +API, so it works reliably and doesn't require a source filter. + +=head2 Basics + +The anatomy of a function (as recognized by this module): + +=over + +=item 1. + +The keyword introducing the function. - use Function::Parameters 'proc', 'meth'; +=item 2. + +The function name (optional). + +=item 3. + +The parameter list (optional). + +=item 4. + +The prototype (optional). + +=item 5. + +The attribute list (optional). + +=item 6. + +The function body. + +=back + +Example: + + # (1) (2) (3) (4) (5) (6) + fun foo ($x, $y) :($$) :lvalue { ... } - my $f = proc ($x) { $x * 2 }; - meth get_age() { - return $self->{age}; - } + # (1) (6) + my $f = fun { ... }; -=head1 DESCRIPTION +In the following section I'm going to describe all parts in order from simplest to most complex. -This module lets you use parameter lists in your subroutines. Thanks to -L it works without source filters. +=head3 Body -WARNING: This is my first attempt at writing L and I have -almost no experience with perl's internals. So while this module might -appear to work, it could also conceivably make your programs segfault. -Consider this module alpha quality. +This is just a normal block of statements, as with L|perlsub>. No surprises here. -=head2 Basic stuff +=head3 Name -To use this new functionality, you have to use C instead of C - -C continues to work as before. The syntax is almost the same as for -C, but after the subroutine name (or directly after C if you're -writing an anonymous sub) you can write a parameter list in parentheses. This -list consists of comma-separated variables. +If present, it specifies the name of the function being defined. As with +L|perlsub>, if a name is present, the whole declaration is syntactically +a statement and its effects are performed at compile time (i.e. at runtime you +can call functions whose definitions only occur later in the file). If no name +is present, the declaration is an expression that evaluates to a reference to +the function in question. No surprises here either. -The effect of C is as if you'd written -C, i.e. the parameter list is simply -copied into C and initialized from L<@_|perlvar/"@_">. +=head3 Attributes -In addition you can use C, which understands the same syntax as C -but automatically creates a C<$self> variable for you. So by writing -C you get the same effect as -C. +Attributes are relatively unusual in Perl code, but if you want them, they work +exactly the same as with L|perlsub/Subroutine-Attributes>. -=head2 Customizing the generated keywords +=head3 Prototype -You can customize the names of the keywords injected in your package. To do that -you pass a hash reference in the import list: +As with L|perlsub/Prototypes>, a prototype, if present, contains hints as to how +the compiler should parse calls to this function. This means prototypes have no +effect if the function call is compiled before the function declaration has +been seen by the compiler or if the function to call is only determined at +runtime (e.g. because it's called as a method or through a reference). - use Function::Parameters { proc => 'function', meth => 'method' }; # -or- - use Function::Parameters { proc => 'function' }; # -or- - use Function::Parameters { meth => 'method' }; +With L|perlsub>, a prototype comes directly after the function name (if +any). C reserves this spot for the +L. To specify a prototype, put it as the +first attribute (e.g. C). This is syntactically unambiguous +because normal L need a name after the colon. -The first line creates two keywords, C and C (for defining -functions and methods, respectively). The last two lines only create one -keyword. Generally the hash keys can be any identifiers you want while the -values have to be either C, C, or a hash reference (see -below). The difference between C and C is that Cs -automatically L their first argument into C<$self>. +=head3 Parameter list -The following shortcuts are available: +The parameter list is a list of variables enclosed in parentheses, except it's +actually a bit more complicated than that. A parameter list can include the +following 6 parts, all of which are optional: - use Function::Parameters; - # is equivalent to # - use Function::Parameters { fun => 'function', method => 'method' }; +=over -=cut +=item 1. Invocant -=pod +This is a scalar variable followed by a colon (C<:>) and no comma. If an +invocant is present in the parameter list, the first element of +L|perlvar/@ARG> is automatically Led|perlfunc/shift> off and +placed in this variable. This is intended for methods: + + method new($class: %init) { + return bless { %init }, $class; + } + + method throw($self:) { + die $self; + } + +=item 2. Required positional parameters + +The most common kind of parameter. This is simply a comma-separated list of +scalars, which are filled from left to right with the arguments that the caller +passed in: - use Function::Parameters 'foo'; - # is equivalent to # - use Function::Parameters { 'foo' => 'function' }; + fun add($x, $y) { + return $x + $y; + } + + say add(2, 3); # "5" + +=item 3. Optional positional parameters + +Parameters can be marked as optional by putting an equals sign (C<=>) and an +expression (the "default argument") after them. If no corresponding argument is +passed in by the caller, the default argument will be used to initialize the +parameter: + + fun scale($base, $factor = 2) { + return $base * $factor; + } + + say scale(3, 5); # "15" + say scale(3); # "6" + +The default argument is I cached. Every time a function is called with +some optional arguments missing, the corresponding default arguments are +evaluated from left to right. This makes no difference for a value like C<2> +but it is important for expressions with side effects, such as reference +constructors (C<[]>, C<{}>) or function calls. + +Default arguments see not only the surrounding lexical scope of their function +but also any preceding parameters. This allows the creation of dynamic defaults +based on previous arguments: + + method set_name($self: $nick = $self->default_nick, $real_name = $nick) { + $self->{nick} = $nick; + $self->{real_name} = $real_name; + } + + $obj->set_name("simplicio"); # same as: $obj->set_name("simplicio", "simplicio"); + +Because default arguments are actually evaluated as part of the function body, +you can also do silly things like this: + + fun foo($n = return "nope") { + "you gave me $n" + } + + say foo(2 + 2); # "you gave me 4" + say foo(); # "nope" + +=item 4. Required named parameters + +By putting a colon (C<:>) in front of a parameter you can make it named +instead of positional: + + fun rectangle(:$width, :$height) { + ... + } + + rectangle(width => 2, height => 5); + rectangle(height => 5, width => 2); # same thing! + +That is, the caller must specify a key name in addition to the value, but in +exchange the order of the arguments doesn't matter anymore. As with hash +initialization, you can specify the same key multiple times and the last +occurrence wins: + + rectangle(height => 1, width => 2, height => 2, height => 5; + # same as: rectangle(width => 2, height => 5); + +You can combine positional and named parameters as long as the positional +parameters come first: + + fun named_rectangle($name, :$width, :$height) { + ... + } + + named_rectangle("Avocado", width => 0.5, height => 1.2); + +=item 5. Optional named parameters + +As with positional parameters, you can make named parameters optional by +specifying a default argument after an equals sign (C<=>): + + fun rectangle(:$width, :$height, :$color = "chartreuse") { + ... + } + + rectangle(height => 10, width => 5); + # same as: rectangle(height => 10, width => 5, color => "chartreuse"); =cut =pod + + fun get($url, :$cookie_jar = HTTP::Cookies->new(), :$referrer = $url) { + ... + } + + my $data = get "http://www.example.com/", referrer => undef; # overrides $referrer = $url + +The above example shows that passing any value (even C) will override +the default argument. + +=item 6. Slurpy parameter + +Finally you can put an array or hash in the parameter list, which will gobble +up the remaining arguments (if any): + + fun foo($x, $y, @rest) { ... } + + foo "a", "b"; # $x = "a", $y = "b", @rest = () + foo "a", "b", "c"; # $x = "a", $y = "b", @rest = ("c") + foo "a", "b", "c", "d"; # $x = "a", $y = "b", @rest = ("c", "d") - use Function::Parameters 'foo', 'bar'; - # is equivalent to # - use Function::Parameters { 'foo' => 'function', 'bar' => 'method' }; +If you combine this with named parameters, the slurpy parameter will end up +containing all unrecognized keys: -You can customize things even more by passing a hashref instead of C -or C. This hash can have the following keys: + fun bar(:$size, @whatev) { ... } + + bar weight => 20, size => 2, location => [0, -3]; + # $size = 2, @whatev = ('weight', 20, 'location', [0, -3]) + +=back + +Apart from the L|perlfunc/shift> performed by the L, all of the above leave L|perlvar/@ARG> unchanged; and if you +don't specify a parameter list at all, L|perlvar/@ARG> is all you get. + +=head3 Keyword + +The keywords provided by C are customizable. Since +C is actually a L, the provided +keywords have lexical scope. The following import variants can be used: + +=over + +=item C + +Provides the keywords C and C (described below) and enables +argument checks so that calling a function and omitting a required argument (or +passing too many arguments) will throw an error. + +=item C + +Provides the keywords C and C (described below) and enables +"lax" mode: Omitting a required argument sets it to C while excess +arguments are silently ignored. + +=item C<< use Function::Parameters { KEYWORD1 => TYPE1, KEYWORD2 => TYPE2, ... } >> + +Provides completely custom keywords as described by their types. A "type" is +either a string (one of the predefined types C, C, +C, C, C, C) or +a reference to a hash with the following keys: =over =item C -Valid values: C (default), C (all uses of this keyword must -specify a function name), and C (all uses of this keyword must not -specify a function name). This means a C<< name => 'prohibited' >> keyword can -only be used for defining anonymous functions. +Valid values: C (default), C (all functions defined with +this keyword must have a name), and C (functions defined with this +keyword must be anonymous). =item C -Valid values: strings that look like a scalar variable. Any function created by -this keyword will automatically L its first argument into -a local variable with the name specified here. +Valid values: strings that look like scalar variables. This lets you specify a +default L, i.e. a function defined with this keyword +that doesn't have an explicit invocant in its parameter list will automatically +L|perlfunc/shift> its first argument into the variable specified here. + +=item C + +Valid values: booleans. If you set this to a true value, the keyword will +accept L in parameter lists; otherwise specifying +an invocant in a function defined with this keyword is a syntax error. + +=item C + +Valid values: strings containing (source code for) attributes. This causes any +function defined with this keyword to have the specified +L (in addition to any attributes specified in the +function definition itself). + +=item C + +Valid values: booleans. This property is on by default; use +C<< default_arguments => 0 >> to turn it off. This controls whether optional +parameters are allowed. If it is turned off, using C<=> in parameter lists is +a syntax error. + +=item C + +Valid values: booleans. If turned on, functions defined with this keyword will +automatically check that they have been passed all required arguments and no +excess arguments. If this check fails, an exception will by thrown via +L|Carp>. =back -Plain C is equivalent to C<< { name => 'optional' } >>, and plain -C is equivalent to C<< { name => 'optional', shift => '$self'} >>. - -=head2 Other advanced stuff - -Normally, Perl subroutines are not in scope in their own body, meaning the -parser doesn't know the name C or its prototype while processing -C, parsing it as -C<$bar-Efoo([1], $bar[0])>. Yes. You can add parens to change the -interpretation of this code, but C will only trigger -a I warning. This module attempts -to fix all of this by adding a subroutine declaration before the definition, -so the parser knows the name (and possibly prototype) while it processes the -body. Thus C really turns into -C. - -If you need L, you can -put them after the parameter list with their usual syntax. - -Syntactically, these new parameter lists live in the spot normally occupied -by L. However, you can include a prototype by -specifying it as the first attribute (this is syntactically unambiguous -because normal attributes have to start with a letter). - -If you want to wrap L, you just have to call its -C method. It always applies to the file that is currently being parsed -and its effects are lexical (i.e. it works like L or L); - - package Some::Wrapper; - use Function::Parameters (); - sub import { - Function::Parameters->import; - # or Function::Parameters->import(@other_import_args); - } +The predefined type C is equivalent to: + + { + name => 'optional', + invocant => 0, + default_arguments => 1, + check_argument_count => 0, + } + +These are all default values, so C is also equivalent to C<{}>. + +C is equivalent to: + + { + name => 'optional', + shift => '$self', + invocant => 1, + attributes => ':method', + default_arguments => 1, + check_argument_count => 0, + } + + +C is equivalent to: + + { + name => 'optional', + shift => '$class', + invocant => 1, + attributes => ':method', + default_arguments => 1, + check_argument_count => 0, + } + +C, C, and +C are like C, C, and +C, respectively, but with C<< check_argument_count => 1 >>. + +=back + +Plain C is equivalent to +C<< use Function::Parameters { fun => 'function', method => 'method' } >>. + +C is equivalent to +C<< use Function::Parameters { fun => 'function_strict', method => 'method_strict' } >>. + +=head2 Introspection + +You can ask a function at runtime what parameters it has. This functionality is +available through the function C (which is not +exported, so you have to call it by its full name). It takes a reference to a +function, and returns either C (if it knows nothing about the function) +or a L object describing the parameter list. + +Note: This feature is implemented using L, so you'll need to have L +installed if you want to call C (alternatively, if +L is already loaded by the time C is first +called, it will use that instead). + +See L for examples. + +=head2 Wrapping C + +If you want to write a wrapper around C, you only have to +call its C method. Due to its L nature it always +affects the file that is currently being compiled. + + package Some::Wrapper; + use Function::Parameters (); + sub import { + Function::Parameters->import; + # or Function::Parameters->import(@custom_import_args); + } + +=head2 Experimental feature: Types + +An experimental feature is now available: You can annotate parameters with +L. That is, before each parameter you can put +a type specification consisting of identifiers (C), unions (C<... | ...>), +and parametric types (C<...[...]>). Example: + + fun foo(Int $n, ArrayRef[String | CodeRef] $cb) { ... } + +If you do this, L will be loaded automatically (if that hasn't happened +yet). These specifications are parsed and validated using +L|Moose::Util::TypeConstraints/find_or_parse_type_constraint>. + +If you are in "lax" mode, nothing further happens and the types are ignored. If +you are in "strict" mode, C generates code to make sure +any values passed in conform to the type (via +L<< C<< $constraint->check($value) >>|Moose::Meta::TypeConstraint/$constraint->check($value) >>). + +In addition, these type constraints are inspectable through the +L object returned by +L|/Introspection>. + +=head2 Experimental experimental feature: Type expressions + +An even more experimental feature is the ability to specify arbitrary +expressions as types. The syntax for this is like the literal types described +above, but with an expression wrapped in parentheses (C<( EXPR )>). Example: + + fun foo(('Int') $n, ($othertype) $x) { ... } + +Every type expression must return either a string (which is resolved as for +literal types), or a L +(providing C and C methods). + +Note that these expressions are evaluated (once) at parse time (similar to +C blocks), so make sure that any variables you use are set and any +functions you call are defined at parse time. + +=head2 How it works + +The module is actually written in L and uses +L|perlapi/PL_keyword_plugin> to generate opcodes directly. +However, you can run L|B::Deparse> on your code to see +what happens under the hood. In the simplest case (no argument checks, possibly +an L, required positional/slurpy parameters only), the +generated code corresponds to: + + fun foo($x, $y, @z) { ... } + # ... turns into ... + sub foo { my ($x, $y, @z) = @_; sub foo; ... } + + method bar($x, $y, @z) { ... } + # ... turns into ... + sub bar :method { my $self = shift; my ($x, $y, @z) = @_; sub bar; ... } + +=head1 SUPPORT AND DOCUMENTATION + +After installing, you can find documentation for this module with the +perldoc command. + + perldoc Function::Parameters + +You can also look for information at: + +=over + +=item MetaCPAN + +L + +=item RT, CPAN's request tracker + +L + +=item AnnoCPAN, Annotated CPAN documentation + +L + +=item CPAN Ratings + +L + +=item Search CPAN + +L + +=back + +=head1 SEE ALSO + +L =head1 AUTHOR @@ -265,7 +764,7 @@ Lukas Mai, C<< >> =head1 COPYRIGHT & LICENSE -Copyright 2010, 2011, 2012 Lukas Mai. +Copyright 2010-2013 Lukas Mai. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published