New upstream version 0.21
This commit is contained in:
parent
50b956e507
commit
30ed21a535
|
@ -0,0 +1,4 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*.{pm,pl,t}]
|
||||||
|
indent_size = 3
|
20
Build.PL
20
Build.PL
|
@ -1,9 +1,23 @@
|
||||||
|
use v5;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use lib 'inc';
|
use lib 'inc';
|
||||||
use Module::Build::with::XSTests;
|
use Module::Build::with::XSTests;
|
||||||
|
|
||||||
|
my @extra_compiler_flags = qw( -I. -Iinclude -Ihax );
|
||||||
|
|
||||||
|
# Perl 5.36 made -std=c99 standard; before then we'll have to request it specially
|
||||||
|
push @extra_compiler_flags, qw( -std=c99 ) if $^V lt v5.36.0;
|
||||||
|
|
||||||
|
push @extra_compiler_flags, qw( -DDEBUGGING=-g ) if $^X =~ m|/debugperl|;
|
||||||
|
|
||||||
|
use Config;
|
||||||
|
if( $Config{ccname} eq "gcc" ) {
|
||||||
|
# Enable some extra gcc warnings, largely just for author interest
|
||||||
|
push @extra_compiler_flags, qw( -Wall -Wno-unused-function -Wno-unused-value );
|
||||||
|
}
|
||||||
|
|
||||||
my $build = Module::Build::with::XSTests->new(
|
my $build = Module::Build::with::XSTests->new(
|
||||||
module_name => 'XS::Parse::Sublike',
|
module_name => 'XS::Parse::Sublike',
|
||||||
requires => {
|
requires => {
|
||||||
|
@ -12,8 +26,7 @@ my $build = Module::Build::with::XSTests->new(
|
||||||
},
|
},
|
||||||
test_requires => {
|
test_requires => {
|
||||||
'Sub::Util' => 0,
|
'Sub::Util' => 0,
|
||||||
'Test::Fatal' => 0,
|
'Test2::V0' => 0,
|
||||||
'Test::More' => '0.88', # done_testing
|
|
||||||
},
|
},
|
||||||
configure_requires => {
|
configure_requires => {
|
||||||
'Module::Build' => '0.4004', # test_requires
|
'Module::Build' => '0.4004', # test_requires
|
||||||
|
@ -22,7 +35,8 @@ my $build = Module::Build::with::XSTests->new(
|
||||||
create_license => 1,
|
create_license => 1,
|
||||||
create_readme => 1,
|
create_readme => 1,
|
||||||
|
|
||||||
extra_compiler_flags => [qw( -I. -Ihax )],
|
extra_compiler_flags => \@extra_compiler_flags,
|
||||||
|
c_source => [ "src/" ],
|
||||||
);
|
);
|
||||||
|
|
||||||
$build->create_build_script;
|
$build->create_build_script;
|
||||||
|
|
42
Changes
42
Changes
|
@ -1,5 +1,47 @@
|
||||||
Revision history for XS-Parse-Sublike
|
Revision history for XS-Parse-Sublike
|
||||||
|
|
||||||
|
0.21 2023-10-11
|
||||||
|
[BUGFIXES]
|
||||||
|
* Ensure that exceptions thrown from runtime signature handling
|
||||||
|
appear to come from the callsite and not declaration
|
||||||
|
* Fix signedness of printf format when complaining about
|
||||||
|
mismatched `->ver`
|
||||||
|
|
||||||
|
0.20 2023-09-09
|
||||||
|
[BUGFIXES]
|
||||||
|
* Correct ->VERSION check for Object::Pad or Future::AsyncAwait
|
||||||
|
cross-module tests (RT149700)
|
||||||
|
* Don't upset gcc's -Wformat by passing an unbounded STRLEN into
|
||||||
|
%.*s format (RT133035)
|
||||||
|
* Avoid colons in filename of t/71extended+Object-Pad.t because
|
||||||
|
Windows doesn't like them (RT149712)
|
||||||
|
* Ensure that mixed positional + named params in signatures work
|
||||||
|
properly (thanks alh)
|
||||||
|
* Ensure name shadowing of param names prints the right diagnostic
|
||||||
|
warning (thanks alh)
|
||||||
|
|
||||||
|
0.19 2023-09-07
|
||||||
|
[CHANGES]
|
||||||
|
* Added `Sublike::Extended`, a prefix keyword to enable the extended
|
||||||
|
signature parser for named params and attributes
|
||||||
|
* Beginnings of an (experimental) XS-level API for attributes on
|
||||||
|
parameters
|
||||||
|
* Complain on attempts to register a sublike keyword with neither a
|
||||||
|
permit function nor hinthash key
|
||||||
|
|
||||||
|
0.18 2023-06-14
|
||||||
|
[CHANGES]
|
||||||
|
* Swap all the unit tests from Test::More to Test2::V0
|
||||||
|
|
||||||
|
[BUGFIXES]
|
||||||
|
* Remember to set `-std=c99` compiler flag on Perls before v5.36
|
||||||
|
|
||||||
|
0.17 2023-03-21
|
||||||
|
[CHANGES]
|
||||||
|
* Experimental support for named param syntax in parse_subsignature()
|
||||||
|
* Support core perl's `method` syntax when available by setting
|
||||||
|
CVf_IsMETHOD (perl >= 5.37.10)
|
||||||
|
|
||||||
0.16 2021-12-16
|
0.16 2021-12-16
|
||||||
[CHANGES]
|
[CHANGES]
|
||||||
* No longer support ABI version 3 even in back-compatibility mode
|
* No longer support ABI version 3 even in back-compatibility mode
|
||||||
|
|
18
LICENSE
18
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
This software is copyright (c) 2021 by Paul Evans <leonerd@leonerd.org.uk>.
|
This software is copyright (c) 2023 by Paul Evans <leonerd@leonerd.org.uk>.
|
||||||
|
|
||||||
This is free software; you can redistribute it and/or modify it under
|
This is free software; you can redistribute it and/or modify it under
|
||||||
the same terms as the Perl 5 programming language system itself.
|
the same terms as the Perl 5 programming language system itself.
|
||||||
|
@ -12,7 +12,7 @@ b) the "Artistic License"
|
||||||
|
|
||||||
--- The GNU General Public License, Version 1, February 1989 ---
|
--- The GNU General Public License, Version 1, February 1989 ---
|
||||||
|
|
||||||
This software is Copyright (c) 2021 by Paul Evans <leonerd@leonerd.org.uk>.
|
This software is Copyright (c) 2023 by Paul Evans <leonerd@leonerd.org.uk>.
|
||||||
|
|
||||||
This is free software, licensed under:
|
This is free software, licensed under:
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ That's all there is to it!
|
||||||
|
|
||||||
--- The Artistic License 1.0 ---
|
--- The Artistic License 1.0 ---
|
||||||
|
|
||||||
This software is Copyright (c) 2021 by Paul Evans <leonerd@leonerd.org.uk>.
|
This software is Copyright (c) 2023 by Paul Evans <leonerd@leonerd.org.uk>.
|
||||||
|
|
||||||
This is free software, licensed under:
|
This is free software, licensed under:
|
||||||
|
|
||||||
|
@ -292,21 +292,21 @@ Definitions:
|
||||||
|
|
||||||
- "Package" refers to the collection of files distributed by the Copyright
|
- "Package" refers to the collection of files distributed by the Copyright
|
||||||
Holder, and derivatives of that collection of files created through
|
Holder, and derivatives of that collection of files created through
|
||||||
textual modification.
|
textual modification.
|
||||||
- "Standard Version" refers to such a Package if it has not been modified,
|
- "Standard Version" refers to such a Package if it has not been modified,
|
||||||
or has been modified in accordance with the wishes of the Copyright
|
or has been modified in accordance with the wishes of the Copyright
|
||||||
Holder.
|
Holder.
|
||||||
- "Copyright Holder" is whoever is named in the copyright or copyrights for
|
- "Copyright Holder" is whoever is named in the copyright or copyrights for
|
||||||
the package.
|
the package.
|
||||||
- "You" is you, if you're thinking about copying or distributing this Package.
|
- "You" is you, if you're thinking about copying or distributing this Package.
|
||||||
- "Reasonable copying fee" is whatever you can justify on the basis of media
|
- "Reasonable copying fee" is whatever you can justify on the basis of media
|
||||||
cost, duplication charges, time of people involved, and so on. (You will
|
cost, duplication charges, time of people involved, and so on. (You will
|
||||||
not be required to justify it to the Copyright Holder, but only to the
|
not be required to justify it to the Copyright Holder, but only to the
|
||||||
computing community at large as a market that must bear the fee.)
|
computing community at large as a market that must bear the fee.)
|
||||||
- "Freely Available" means that no fee is charged for the item itself, though
|
- "Freely Available" means that no fee is charged for the item itself, though
|
||||||
there may be fees involved in handling the item. It also means that
|
there may be fees involved in handling the item. It also means that
|
||||||
recipients of the item may redistribute it under the same conditions they
|
recipients of the item may redistribute it under the same conditions they
|
||||||
received it.
|
received it.
|
||||||
|
|
||||||
1. You may make and give away verbatim copies of the source form of the
|
1. You may make and give away verbatim copies of the source form of the
|
||||||
Standard Version of this Package without restriction, provided that you
|
Standard Version of this Package without restriction, provided that you
|
||||||
|
@ -373,7 +373,7 @@ products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||||
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||||
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
The End
|
The End
|
||||||
|
|
||||||
|
|
15
MANIFEST
15
MANIFEST
|
@ -1,17 +1,23 @@
|
||||||
|
.editorconfig
|
||||||
Build.PL
|
Build.PL
|
||||||
Changes
|
Changes
|
||||||
hax/block_end.c.inc
|
hax/block_end.c.inc
|
||||||
hax/block_start.c.inc
|
hax/block_start.c.inc
|
||||||
hax/COP_SEQ_RANGE_HIGH_set.c.inc
|
hax/COP_SEQ_RANGE_HIGH_set.c.inc
|
||||||
|
hax/croak_from_caller.c.inc
|
||||||
hax/lexer-additions.c.inc
|
hax/lexer-additions.c.inc
|
||||||
|
hax/LOGOP_ANY.c.inc
|
||||||
hax/make_argcheck_aux.c.inc
|
hax/make_argcheck_aux.c.inc
|
||||||
|
hax/newSV_with_free.c.inc
|
||||||
hax/pad_block_start.c.inc
|
hax/pad_block_start.c.inc
|
||||||
hax/pad_leavemy.c.inc
|
hax/pad_leavemy.c.inc
|
||||||
hax/parse_subsignature.c.inc
|
|
||||||
hax/scalarseq.c.inc
|
hax/scalarseq.c.inc
|
||||||
hax/sv_setrv.c.inc
|
hax/sv_setrv.c.inc
|
||||||
hax/wrap_keyword_plugin.c.inc
|
hax/wrap_keyword_plugin.c.inc
|
||||||
inc/Module/Build/with/XSTests.pm
|
inc/Module/Build/with/XSTests.pm
|
||||||
|
include/parse_subsignature_ex.h
|
||||||
|
lib/Sublike/Extended.pm
|
||||||
|
lib/Sublike/Extended.xs
|
||||||
lib/XS/Parse/Sublike.pm
|
lib/XS/Parse/Sublike.pm
|
||||||
lib/XS/Parse/Sublike.xs
|
lib/XS/Parse/Sublike.xs
|
||||||
lib/XS/Parse/Sublike/Builder.pm
|
lib/XS/Parse/Sublike/Builder.pm
|
||||||
|
@ -21,10 +27,13 @@ MANIFEST This list of files
|
||||||
META.json
|
META.json
|
||||||
META.yml
|
META.yml
|
||||||
README
|
README
|
||||||
|
src/parse_subsignature_ex.c
|
||||||
t/00use.t
|
t/00use.t
|
||||||
t/01func.t
|
t/01func.t
|
||||||
t/02func-signatures.t
|
t/02func-signatures.t
|
||||||
t/03func-deparse.t
|
t/03func-deparse.t
|
||||||
|
t/04func-named-params.t
|
||||||
|
t/05func-sig-attributes.t
|
||||||
t/10stages-permit.t
|
t/10stages-permit.t
|
||||||
t/11stages-pre_subparse.t
|
t/11stages-pre_subparse.t
|
||||||
t/12stages-post_blockstart.t
|
t/12stages-post_blockstart.t
|
||||||
|
@ -39,6 +48,10 @@ t/41parts-attrs.t
|
||||||
t/42parts-signature.t
|
t/42parts-signature.t
|
||||||
t/50flags-no-body.t
|
t/50flags-no-body.t
|
||||||
t/60actions.t
|
t/60actions.t
|
||||||
|
t/70extended.t
|
||||||
|
t/80extended+async.t
|
||||||
|
t/80extended+feature-class.t
|
||||||
|
t/80extended+Object-Pad.t
|
||||||
t/99pod.t
|
t/99pod.t
|
||||||
t/actions.xs
|
t/actions.xs
|
||||||
t/any.xs
|
t/any.xs
|
||||||
|
|
17
META.json
17
META.json
|
@ -4,7 +4,7 @@
|
||||||
"Paul Evans <leonerd@leonerd.org.uk>"
|
"Paul Evans <leonerd@leonerd.org.uk>"
|
||||||
],
|
],
|
||||||
"dynamic_config" : 1,
|
"dynamic_config" : 1,
|
||||||
"generated_by" : "Module::Build version 0.4231",
|
"generated_by" : "Module::Build version 0.4234",
|
||||||
"license" : [
|
"license" : [
|
||||||
"perl_5"
|
"perl_5"
|
||||||
],
|
],
|
||||||
|
@ -32,19 +32,22 @@
|
||||||
"test" : {
|
"test" : {
|
||||||
"requires" : {
|
"requires" : {
|
||||||
"Sub::Util" : "0",
|
"Sub::Util" : "0",
|
||||||
"Test::Fatal" : "0",
|
"Test2::V0" : "0"
|
||||||
"Test::More" : "0.88"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"provides" : {
|
"provides" : {
|
||||||
|
"Sublike::Extended" : {
|
||||||
|
"file" : "lib/Sublike/Extended.pm",
|
||||||
|
"version" : "0.21"
|
||||||
|
},
|
||||||
"XS::Parse::Sublike" : {
|
"XS::Parse::Sublike" : {
|
||||||
"file" : "lib/XS/Parse/Sublike.pm",
|
"file" : "lib/XS/Parse/Sublike.pm",
|
||||||
"version" : "0.16"
|
"version" : "0.21"
|
||||||
},
|
},
|
||||||
"XS::Parse::Sublike::Builder" : {
|
"XS::Parse::Sublike::Builder" : {
|
||||||
"file" : "lib/XS/Parse/Sublike/Builder.pm",
|
"file" : "lib/XS/Parse/Sublike/Builder.pm",
|
||||||
"version" : "0.16"
|
"version" : "0.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"release_status" : "stable",
|
"release_status" : "stable",
|
||||||
|
@ -53,6 +56,6 @@
|
||||||
"http://dev.perl.org/licenses/"
|
"http://dev.perl.org/licenses/"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version" : "0.16",
|
"version" : "0.21",
|
||||||
"x_serialization_backend" : "JSON::PP version 4.05"
|
"x_serialization_backend" : "JSON::PP version 4.07"
|
||||||
}
|
}
|
||||||
|
|
14
META.yml
14
META.yml
|
@ -5,27 +5,29 @@ author:
|
||||||
build_requires:
|
build_requires:
|
||||||
ExtUtils::CBuilder: '0'
|
ExtUtils::CBuilder: '0'
|
||||||
Sub::Util: '0'
|
Sub::Util: '0'
|
||||||
Test::Fatal: '0'
|
Test2::V0: '0'
|
||||||
Test::More: '0.88'
|
|
||||||
configure_requires:
|
configure_requires:
|
||||||
Module::Build: '0.4004'
|
Module::Build: '0.4004'
|
||||||
dynamic_config: 1
|
dynamic_config: 1
|
||||||
generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010'
|
generated_by: 'Module::Build version 0.4234, CPAN::Meta::Converter version 2.150010'
|
||||||
license: perl
|
license: perl
|
||||||
meta-spec:
|
meta-spec:
|
||||||
url: http://module-build.sourceforge.net/META-spec-v1.4.html
|
url: http://module-build.sourceforge.net/META-spec-v1.4.html
|
||||||
version: '1.4'
|
version: '1.4'
|
||||||
name: XS-Parse-Sublike
|
name: XS-Parse-Sublike
|
||||||
provides:
|
provides:
|
||||||
|
Sublike::Extended:
|
||||||
|
file: lib/Sublike/Extended.pm
|
||||||
|
version: '0.21'
|
||||||
XS::Parse::Sublike:
|
XS::Parse::Sublike:
|
||||||
file: lib/XS/Parse/Sublike.pm
|
file: lib/XS/Parse/Sublike.pm
|
||||||
version: '0.16'
|
version: '0.21'
|
||||||
XS::Parse::Sublike::Builder:
|
XS::Parse::Sublike::Builder:
|
||||||
file: lib/XS/Parse/Sublike/Builder.pm
|
file: lib/XS/Parse/Sublike/Builder.pm
|
||||||
version: '0.16'
|
version: '0.21'
|
||||||
requires:
|
requires:
|
||||||
perl: '5.016'
|
perl: '5.016'
|
||||||
resources:
|
resources:
|
||||||
license: http://dev.perl.org/licenses/
|
license: http://dev.perl.org/licenses/
|
||||||
version: '0.16'
|
version: '0.21'
|
||||||
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
|
||||||
|
|
11
README
11
README
|
@ -227,12 +227,17 @@ PARSE HOOKS
|
||||||
|
|
||||||
The parameter signature of the function.
|
The parameter signature of the function.
|
||||||
|
|
||||||
This part can be skipped, but the bit is ignored when in
|
This part can be skipped, but it is always permitted not to provide a
|
||||||
require_parts. It is always permitted not to provide a signature for
|
signature for a function definition even if the bit it set in
|
||||||
a function definition, because such syntax only applies when use
|
require_parts. This is because such syntax only applies when use
|
||||||
feature 'signatures' is in effect, and only on supporting perl
|
feature 'signatures' is in effect, and only on supporting perl
|
||||||
versions.
|
versions.
|
||||||
|
|
||||||
|
However, setting the bit in require_parts instead has the effect of
|
||||||
|
enabling use feature 'signatures' (at least on supporting perl
|
||||||
|
versions), thus permitting the syntax to use a signature even if the
|
||||||
|
signatures feature was not previously enabled.
|
||||||
|
|
||||||
XS_PARSE_SUBLIKE_PART_BODY
|
XS_PARSE_SUBLIKE_PART_BODY
|
||||||
|
|
||||||
The actual body of the function, expressed as a brace-delimited
|
The actual body of the function, expressed as a brace-delimited
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef __XS_PARSE_SUBLIKE_H__
|
#ifndef __XS_PARSE_SUBLIKE_H__
|
||||||
#define __XS_PARSE_SUBLIKE_H__
|
#define __XS_PARSE_SUBLIKE_H__
|
||||||
|
|
||||||
#define XSPARSESUBLIKE_ABI_VERSION 4
|
#define XSPARSESUBLIKE_ABI_VERSION 5
|
||||||
|
|
||||||
struct XSParseSublikeContext {
|
struct XSParseSublikeContext {
|
||||||
SV *name; /* may be NULL for anon subs */
|
SV *name; /* may be NULL for anon subs */
|
||||||
|
@ -25,6 +25,11 @@ enum {
|
||||||
XS_PARSE_SUBLIKE_FLAG_BODY_OPTIONAL = 1<<1,
|
XS_PARSE_SUBLIKE_FLAG_BODY_OPTIONAL = 1<<1,
|
||||||
XS_PARSE_SUBLIKE_FLAG_PREFIX = 1<<2,
|
XS_PARSE_SUBLIKE_FLAG_PREFIX = 1<<2,
|
||||||
|
|
||||||
|
/* *Experimental* named parameter parsing support */
|
||||||
|
XS_PARSE_SUBLIKE_FLAG_SIGNATURE_NAMED_PARAMS = 1<<3,
|
||||||
|
/* *Experimental* parameter attribute parsing support */
|
||||||
|
XS_PARSE_SUBLIKE_FLAG_SIGNATURE_PARAM_ATTRIBUTES = 1<<4,
|
||||||
|
|
||||||
/* Back-compat flags we hope to remove in the next ABI version */
|
/* Back-compat flags we hope to remove in the next ABI version */
|
||||||
XS_PARSE_SUBLIKE_COMPAT_FLAG_DYNAMIC_ACTIONS = 1<<15,
|
XS_PARSE_SUBLIKE_COMPAT_FLAG_DYNAMIC_ACTIONS = 1<<15,
|
||||||
};
|
};
|
||||||
|
@ -92,6 +97,43 @@ static int S_xs_parse_sublike_any(pTHX_ const struct XSParseSublikeHooks *hooks,
|
||||||
return (*parseany_xs_parse_sublike_func)(aTHX_ hooks, hookdata, op_ptr);
|
return (*parseany_xs_parse_sublike_func)(aTHX_ hooks, hookdata, op_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Experimental support for subroutine parameter attributes.
|
||||||
|
* Only supported on Perl v5.26 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct XPSSignatureParamContext {
|
||||||
|
bool is_named;
|
||||||
|
PADOFFSET padix;
|
||||||
|
OP *varop;
|
||||||
|
/* apply phase runs here */
|
||||||
|
OP *defop;
|
||||||
|
OP *op;
|
||||||
|
/* post_defop phase runs here */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XPSSignatureAttributeFuncs {
|
||||||
|
U32 ver; /* caller must initialise to XSPARSESUBLIKE_ABI_VERSION */
|
||||||
|
U32 flags;
|
||||||
|
const char *permit_hintkey;
|
||||||
|
|
||||||
|
void (*apply)(pTHX_ struct XPSSignatureParamContext *ctx, SV *attrvalue, void **attrdata_ptr, void *funcdata);
|
||||||
|
void (*post_defop)(pTHX_ struct XPSSignatureParamContext *ctx, void *attrdata, void *funcdata);
|
||||||
|
|
||||||
|
void (*free)(pTHX_ void *attrdata, void *funcdata);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void (*register_xps_signature_attribute_func)(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata);
|
||||||
|
#define register_xps_signature_attribute(name, funcs, funcdata) S_register_xps_signature_attribute(aTHX_ name, funcs, funcdata)
|
||||||
|
static void S_register_xps_signature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
|
||||||
|
{
|
||||||
|
if(!register_xps_signature_attribute_func)
|
||||||
|
croak("Must call boot_xs_parse_sublike() first");
|
||||||
|
|
||||||
|
(*register_xps_signature_attribute_func)(aTHX_ name, funcs, funcdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define boot_xs_parse_sublike(ver) S_boot_xs_parse_sublike(aTHX_ ver)
|
#define boot_xs_parse_sublike(ver) S_boot_xs_parse_sublike(aTHX_ ver)
|
||||||
static void S_boot_xs_parse_sublike(pTHX_ double ver) {
|
static void S_boot_xs_parse_sublike(pTHX_ double ver) {
|
||||||
SV **svp;
|
SV **svp;
|
||||||
|
@ -121,6 +163,9 @@ static void S_boot_xs_parse_sublike(pTHX_ double ver) {
|
||||||
|
|
||||||
parseany_xs_parse_sublike_func = INT2PTR(int (*)(pTHX_ const struct XSParseSublikeHooks *, void *, OP**),
|
parseany_xs_parse_sublike_func = INT2PTR(int (*)(pTHX_ const struct XSParseSublikeHooks *, void *, OP**),
|
||||||
SvUV(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parseany()@4", 0)));
|
SvUV(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parseany()@4", 0)));
|
||||||
|
|
||||||
|
register_xps_signature_attribute_func = INT2PTR(void (*)(pTHX_ const char *, const struct XPSSignatureAttributeFuncs *, void *),
|
||||||
|
SvUV(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/register_sigattr()@5", 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* vi: set ft=c : */
|
||||||
|
|
||||||
|
struct logop_any {
|
||||||
|
BASEOP
|
||||||
|
OP *op_first;
|
||||||
|
OP *op_other;
|
||||||
|
ANY *op_any;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct logop_any LOGOP_ANY;
|
||||||
|
|
||||||
|
#define cLOGOP_ANYx(o) ((LOGOP_ANY *)o)
|
||||||
|
#define cLOGOP_ANY cLOGOP_ANYx(PL_op)
|
||||||
|
#define cLOGOP_ANYo cLOGOP_ANYx(o)
|
||||||
|
|
||||||
|
#ifndef OpTYPE_set
|
||||||
|
# define OpTYPE_set(op, type) \
|
||||||
|
STMT_START { \
|
||||||
|
op->op_type = (OPCODE)type; \
|
||||||
|
op->op_ppaddr = PL_ppaddr[type]; \
|
||||||
|
} STMT_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define alloc_LOGOP_ANY(type, first, other) MY_alloc_LOGOP_ANY(aTHX_ type, first, other)
|
||||||
|
static LOGOP_ANY *MY_alloc_LOGOP_ANY(pTHX_ I32 type, OP *first, OP *other)
|
||||||
|
{
|
||||||
|
dVAR;
|
||||||
|
LOGOP_ANY *logop;
|
||||||
|
OP *kid = first;
|
||||||
|
NewOp(1101, logop, 1, LOGOP_ANY);
|
||||||
|
OpTYPE_set(logop, type);
|
||||||
|
logop->op_first = first;
|
||||||
|
logop->op_other = other;
|
||||||
|
if (first)
|
||||||
|
logop->op_flags = OPf_KIDS;
|
||||||
|
while (kid && OpHAS_SIBLING(kid))
|
||||||
|
kid = OpSIBLING(kid);
|
||||||
|
if (kid)
|
||||||
|
OpLASTSIB_set(kid, (OP*)logop);
|
||||||
|
return logop;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* vi: set ft=c : */
|
||||||
|
|
||||||
|
static void S_croaksv_from_caller(pTHX_ SV *msg_sv)
|
||||||
|
__attribute__noreturn__;
|
||||||
|
|
||||||
|
static void S_croaksv_from_caller(pTHX_ SV *msg_sv)
|
||||||
|
{
|
||||||
|
I32 count = 0;
|
||||||
|
const PERL_CONTEXT *cx;
|
||||||
|
while((cx = caller_cx(count, NULL))) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
/* TODO: Skip internal call frames? */
|
||||||
|
/* warn("TODO: maybe croak from caller where caller stash is %s\n",
|
||||||
|
* HvNAME(CopSTASH(cx->blk_oldcop)));
|
||||||
|
*/
|
||||||
|
PL_curcop = cx->blk_oldcop;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
croak_sv(msg_sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define croak_from_caller(fmt, ...) S_croak_from_caller(aTHX_ fmt, __VA_ARGS__)
|
||||||
|
static void S_croak_from_caller(pTHX_ const char *fmt, ...)
|
||||||
|
__attribute__noreturn__;
|
||||||
|
|
||||||
|
static void S_croak_from_caller(pTHX_ const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
SV *msg_sv = sv_2mortal(vnewSVpvf(fmt, &args));
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
S_croaksv_from_caller(aTHX_ msg_sv);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* vi: set ft=c : */
|
||||||
|
|
||||||
|
static int magic_free(pTHX_ SV *sv, MAGIC *mg)
|
||||||
|
{
|
||||||
|
void (*freefunc)(pTHX_ SV *sv) = (void *)mg->mg_ptr;
|
||||||
|
(*freefunc)(aTHX_ sv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MGVTBL vtbl_sv_with_free = {
|
||||||
|
.svt_free = magic_free,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define newSV_with_free(size, freefunc) S_newSV_with_free(aTHX_ size, freefunc)
|
||||||
|
static SV *S_newSV_with_free(pTHX_ STRLEN size, void (*freefunc)(pTHX_ SV *sv))
|
||||||
|
{
|
||||||
|
SV *sv = newSV(size);
|
||||||
|
sv_magicext(sv, NULL, PERL_MAGIC_ext, &vtbl_sv_with_free, (void *)freefunc, 0);
|
||||||
|
return sv;
|
||||||
|
}
|
|
@ -1,220 +0,0 @@
|
||||||
/* vi: set ft=c : */
|
|
||||||
|
|
||||||
#ifndef parse_subsignature
|
|
||||||
|
|
||||||
#define PERL_EXT
|
|
||||||
#include "feature.h"
|
|
||||||
|
|
||||||
#include "make_argcheck_aux.c.inc"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Need to grab some things that aren't quite core perl API
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* yyerror() is a long function and hard to emulate or copy-paste for our
|
|
||||||
* purposes; we'll reïmplement a smaller version of it
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LEX_IGNORE_UTF8_HINTS 0x00000002
|
|
||||||
|
|
||||||
#define PL_linestr (PL_parser->linestr)
|
|
||||||
|
|
||||||
#ifdef USE_UTF8_SCRIPTS
|
|
||||||
# define UTF cBOOL(!IN_BYTES)
|
|
||||||
#else
|
|
||||||
# define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || ( !(PL_parser->lex_flags & LEX_IGNORE_UTF8_HINTS) && (PL_hints & HINT_UTF8)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define yyerror(s) S_yyerror(aTHX_ s)
|
|
||||||
void S_yyerror(pTHX_ const char *s)
|
|
||||||
{
|
|
||||||
SV *message = sv_2mortal(newSVpvs_flags("", 0));
|
|
||||||
|
|
||||||
char *context = PL_parser->oldbufptr;
|
|
||||||
STRLEN contlen = PL_parser->bufptr - PL_parser->oldbufptr;
|
|
||||||
|
|
||||||
sv_catpvf(message, "%s at %s line %" IVdf,
|
|
||||||
s, OutCopFILE(PL_curcop), (IV)CopLINE(PL_curcop));
|
|
||||||
|
|
||||||
if(context)
|
|
||||||
sv_catpvf(message, ", near \"%" UTF8f "\"",
|
|
||||||
UTF8fARG(UTF, contlen, context));
|
|
||||||
|
|
||||||
sv_catpvf(message, "\n");
|
|
||||||
|
|
||||||
PL_parser->error_count++;
|
|
||||||
warn_sv(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stolen from op.c */
|
|
||||||
#define OpTYPE_set(op, type) \
|
|
||||||
STMT_START { \
|
|
||||||
op->op_type = (OPCODE)type; \
|
|
||||||
op->op_ppaddr = PL_ppaddr[type]; \
|
|
||||||
} STMT_END
|
|
||||||
|
|
||||||
#define alloc_LOGOP(a,b,c) S_alloc_LOGOP(aTHX_ a,b,c)
|
|
||||||
static LOGOP *S_alloc_LOGOP(pTHX_ I32 type, OP *first, OP* other)
|
|
||||||
{
|
|
||||||
dVAR;
|
|
||||||
LOGOP *logop;
|
|
||||||
OP *kid = first;
|
|
||||||
NewOp(1101, logop, 1, LOGOP);
|
|
||||||
OpTYPE_set(logop, type);
|
|
||||||
logop->op_first = first;
|
|
||||||
logop->op_other = other;
|
|
||||||
if (first)
|
|
||||||
logop->op_flags = OPf_KIDS;
|
|
||||||
while (kid && OpHAS_SIBLING(kid))
|
|
||||||
kid = OpSIBLING(kid);
|
|
||||||
if (kid)
|
|
||||||
OpLASTSIB_set(kid, (OP*)logop);
|
|
||||||
return logop;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define parse_sigelem() S_parse_sigelem(aTHX)
|
|
||||||
static OP *S_parse_sigelem(pTHX)
|
|
||||||
{
|
|
||||||
yy_parser *parser = PL_parser;
|
|
||||||
|
|
||||||
int c = lex_peek_unichar(0);
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case '$': flags = OPpARGELEM_SV; break;
|
|
||||||
case '@': flags = OPpARGELEM_AV; break;
|
|
||||||
case '%': flags = OPpARGELEM_HV; break;
|
|
||||||
default:
|
|
||||||
croak("Expected a signature element at <%s>\n", parser->bufptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *lexname = parser->bufptr;
|
|
||||||
OP *varop = NULL;
|
|
||||||
|
|
||||||
/* Consume sigil */
|
|
||||||
lex_read_unichar(0);
|
|
||||||
|
|
||||||
if(isIDFIRST_uni(lex_peek_unichar(0))) {
|
|
||||||
lex_read_unichar(0);
|
|
||||||
while(isALNUM_uni(lex_peek_unichar(0)))
|
|
||||||
lex_read_unichar(0);
|
|
||||||
|
|
||||||
varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (parser->sig_elems)));
|
|
||||||
varop->op_private |= flags;
|
|
||||||
|
|
||||||
varop->op_targ = pad_add_name_pvn(lexname, PL_parser->bufptr - lexname, 0, NULL, NULL);
|
|
||||||
|
|
||||||
lex_read_space(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(c == '$') {
|
|
||||||
if(parser->sig_slurpy)
|
|
||||||
yyerror("Slurpy parameters not last");
|
|
||||||
|
|
||||||
parser->sig_elems++;
|
|
||||||
|
|
||||||
if(lex_peek_unichar(0) == '=') {
|
|
||||||
lex_read_unichar(0);
|
|
||||||
lex_read_space(0);
|
|
||||||
|
|
||||||
parser->sig_optelems++;
|
|
||||||
|
|
||||||
OP *defexpr = parse_termexpr(0);
|
|
||||||
|
|
||||||
OP *defop = (OP *)alloc_LOGOP(OP_ARGDEFELEM, defexpr, LINKLIST(defexpr));
|
|
||||||
defop->op_targ = (PADOFFSET)(parser->sig_elems - 1);
|
|
||||||
|
|
||||||
varop->op_flags |= OPf_STACKED;
|
|
||||||
op_sibling_splice(varop, NULL, 0, defop);
|
|
||||||
defop = op_contextualize(defop, G_SCALAR);
|
|
||||||
|
|
||||||
LINKLIST(varop);
|
|
||||||
|
|
||||||
varop->op_next = defop;
|
|
||||||
defexpr->op_next = varop;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(parser->sig_optelems)
|
|
||||||
yyerror("Mandatory parameter follows optional parameter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(parser->sig_slurpy)
|
|
||||||
yyerror("Multiple slurpy parameters not allowed");
|
|
||||||
|
|
||||||
parser->sig_slurpy = c;
|
|
||||||
|
|
||||||
if(lex_peek_unichar(0) == '=')
|
|
||||||
yyerror("A slurpy parameter may not have a default value");
|
|
||||||
}
|
|
||||||
|
|
||||||
return varop ? newSTATEOP(0, NULL, varop) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define parse_subsignature(flags) S_parse_subsignature(aTHX_ flags)
|
|
||||||
static OP *S_parse_subsignature(pTHX_ int flags)
|
|
||||||
{
|
|
||||||
/* Mostly reconstructed logic from perl 5.28.0's toke.c and perly.y
|
|
||||||
*/
|
|
||||||
yy_parser *parser = PL_parser;
|
|
||||||
|
|
||||||
ENTER;
|
|
||||||
SAVEIV(parser->sig_elems);
|
|
||||||
SAVEIV(parser->sig_optelems);
|
|
||||||
SAVEI8(parser->sig_slurpy);
|
|
||||||
|
|
||||||
parser->sig_elems = 0;
|
|
||||||
parser->sig_optelems = 0;
|
|
||||||
parser->sig_slurpy = 0;
|
|
||||||
|
|
||||||
OP *elems = NULL;
|
|
||||||
while(lex_peek_unichar(0) != ')') {
|
|
||||||
lex_read_space(0);
|
|
||||||
OP *elem = parse_sigelem();
|
|
||||||
elems = op_append_list(OP_LINESEQ, elems, elem);
|
|
||||||
|
|
||||||
if(PL_parser->error_count) {
|
|
||||||
LEAVE;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
lex_read_space(0);
|
|
||||||
switch(lex_peek_unichar(0)) {
|
|
||||||
case ')': goto endofelems;
|
|
||||||
case ',': break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "ARGH unsure how to proceed parse_subsignature at <%s>\n",
|
|
||||||
parser->bufptr);
|
|
||||||
croak("ARGH");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lex_read_unichar(0);
|
|
||||||
lex_read_space(0);
|
|
||||||
}
|
|
||||||
endofelems:
|
|
||||||
|
|
||||||
if (!FEATURE_SIGNATURES_IS_ENABLED)
|
|
||||||
croak("Experimental subroutine signatures not enabled");
|
|
||||||
|
|
||||||
Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__SIGNATURES),
|
|
||||||
"The signatures feature is experimental");
|
|
||||||
|
|
||||||
UNOP_AUX_item *aux = make_argcheck_aux(
|
|
||||||
parser->sig_elems, parser->sig_optelems, parser->sig_slurpy);
|
|
||||||
|
|
||||||
OP *checkop = newUNOP_AUX(OP_ARGCHECK, 0, NULL, aux);
|
|
||||||
|
|
||||||
checkop = op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, NULL),
|
|
||||||
op_prepend_elem(OP_LINESEQ, checkop, elems));
|
|
||||||
|
|
||||||
/* a nextstate at the end handles context correctly for an empty
|
|
||||||
* sub body */
|
|
||||||
checkop = op_append_elem(OP_LINESEQ, checkop, newSTATEOP(0, NULL, NULL));
|
|
||||||
|
|
||||||
LEAVE;
|
|
||||||
|
|
||||||
return checkop;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
package Module::Build::with::XSTests;
|
package Module::Build::with::XSTests;
|
||||||
|
|
||||||
use strict;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use base qw( Module::Build );
|
use base qw( Module::Build );
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* vi: set ft=c : */
|
||||||
|
|
||||||
|
/* Some experimental extension flags. Not (currently) part of core perl API
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
PARSE_SUBSIGNATURE_NAMED_PARAMS = (1<<0),
|
||||||
|
/* Permits ( :$foo, :$bar, :$splot = "default" ) named params
|
||||||
|
* They are accumulated into a slurpy hash then extracted afterwards
|
||||||
|
* As with positional params, any param without a defaulting expression is
|
||||||
|
* required; an error is thrown if the caller did not provide it
|
||||||
|
*/
|
||||||
|
|
||||||
|
PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES = (1<<1),
|
||||||
|
/* Permits ( $param :Attribute(Value) ) attributes on params
|
||||||
|
* These must be registered by calling register_subsignature_attribute()
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
#define parse_subsignature_ex(flags) XPS_parse_subsignature_ex(aTHX_ flags)
|
||||||
|
OP *XPS_parse_subsignature_ex(pTHX_ int flags);
|
||||||
|
|
||||||
|
#define register_subsignature_attribute(name, funcs, funcdata) XPS_register_subsignature_attribute(aTHX_ name, funcs, funcdata)
|
||||||
|
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata);
|
|
@ -0,0 +1,122 @@
|
||||||
|
# You may distribute under the terms of either the GNU General Public License
|
||||||
|
# or the Artistic License (the same terms as Perl itself)
|
||||||
|
#
|
||||||
|
# (C) Paul Evans, 2023 -- leonerd@leonerd.org.uk
|
||||||
|
|
||||||
|
package Sublike::Extended 0.21;
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
require XSLoader;
|
||||||
|
XSLoader::load( __PACKAGE__, our $VERSION );
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
C<Sublike::Extended> - enable extended features when parsing C<sub>-like syntax
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use v5.26;
|
||||||
|
use Sublike::Extended;
|
||||||
|
use experimental 'signatures';
|
||||||
|
|
||||||
|
extended sub greet (:$name = "world") {
|
||||||
|
say "Hello, $name";
|
||||||
|
}
|
||||||
|
|
||||||
|
greet( name => $ENV{USER} );
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This module extends the syntax for declaring named or anonymous subroutines
|
||||||
|
using Perl's builtin C<sub> keyword, or other similar keywords provided by
|
||||||
|
third-party modules, to enable parsing of extra features.
|
||||||
|
|
||||||
|
Currently, the only extended features that are provided are related to the
|
||||||
|
parsing of a subroutine signature. Since signatures are only available on Perl
|
||||||
|
version 5.26 or later, this module is unlikely to be useful in earlier
|
||||||
|
versions of Perl.
|
||||||
|
|
||||||
|
=head2 Named parameters
|
||||||
|
|
||||||
|
Extended subroutines can be declare named parameters in the signature, after
|
||||||
|
any positional ones. These take the form of a name prefixed by a colon
|
||||||
|
character. The caller of such a function should pass values for these
|
||||||
|
parameters by the usual name-value pair syntax that would be used for passing
|
||||||
|
into a regular hash. Within the body of the subroutine the values passed into
|
||||||
|
these are unpacked into regular lexical variables.
|
||||||
|
|
||||||
|
extended sub colour (:$red, :$green, :$blue) {
|
||||||
|
... # $red, $green and $blue are available as regular lexicals
|
||||||
|
}
|
||||||
|
|
||||||
|
# argument order at the caller site is not important
|
||||||
|
colour(green => 1, blue => 2, red => 3);
|
||||||
|
|
||||||
|
As with positional parameters, they are normally mandatory, but can be made
|
||||||
|
optional by supplying a defaulting expression. If the caller fails to pass a
|
||||||
|
value corresponding to the parameter, the default expression is evaluated and
|
||||||
|
used instead.
|
||||||
|
|
||||||
|
extended sub f (:$x0, :$x1, :$x2 = 0) { ... }
|
||||||
|
# The caller must provide x0 and x1, but x2 is optional
|
||||||
|
|
||||||
|
An optional slurpy hash is also permitted after all of these. It will contain
|
||||||
|
the values of any other name-value pairs given by the caller, after those
|
||||||
|
corresponding to named parameters have already been extracted.
|
||||||
|
|
||||||
|
extended sub g (:$alpha, :$beta, %rest) { ... }
|
||||||
|
|
||||||
|
=head2 Parameter Attributes
|
||||||
|
|
||||||
|
Parameters to extended subroutines can use attribute syntax to apply extra
|
||||||
|
attributes to individual parameters.
|
||||||
|
|
||||||
|
extended sub info ($x :Attribute) { ... }
|
||||||
|
|
||||||
|
Any attributes that are available are ones that have been previously
|
||||||
|
registered with L<XS::Parse::Sublike> using its XS-level API. The particular
|
||||||
|
behaviour of such an attribute would be defined by whatever module provided
|
||||||
|
the attribute.
|
||||||
|
|
||||||
|
=head1 KEYWORDS
|
||||||
|
|
||||||
|
=head2 extended
|
||||||
|
|
||||||
|
extended sub NAME (SIGNATURE...) { BODY... }
|
||||||
|
|
||||||
|
extended sub (SIGNATURE...) { BODY... };
|
||||||
|
|
||||||
|
This prefix keyword enables extra parsing features when handling a C<sub> (or
|
||||||
|
other sub-like function keyword).
|
||||||
|
|
||||||
|
This keyword can be freely mixed with other C<sub>-prefix keywords, such as
|
||||||
|
C<async> from L<Future::AsyncAwait>
|
||||||
|
|
||||||
|
async extended sub f (:$param) { ... }
|
||||||
|
|
||||||
|
This can also be used with other keywords that provide C<sub>-like syntax,
|
||||||
|
such as C<method> from L<Object::Pad> or the core C<use feature 'class'>.
|
||||||
|
|
||||||
|
extended method f (:$param) { ... }
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub import
|
||||||
|
{
|
||||||
|
$^H{"Sublike::Extended/extended"}++;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unimport
|
||||||
|
{
|
||||||
|
delete $^H{"Sublike::Extended/extended"};
|
||||||
|
}
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Evans <leonerd@leonerd.org.uk>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
0x55AA;
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* You may distribute under the terms of either the GNU General Public License
|
||||||
|
* or the Artistic License (the same terms as Perl itself)
|
||||||
|
*
|
||||||
|
* (C) Paul Evans, 2023 -- leonerd@leonerd.org.uk
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "EXTERN.h"
|
||||||
|
#include "perl.h"
|
||||||
|
#include "XSUB.h"
|
||||||
|
|
||||||
|
#include "XSParseSublike.h"
|
||||||
|
|
||||||
|
static struct XSParseSublikeHooks hooks_extended = {
|
||||||
|
.permit_hintkey = "Sublike::Extended/extended",
|
||||||
|
.flags = XS_PARSE_SUBLIKE_FLAG_PREFIX|
|
||||||
|
XS_PARSE_SUBLIKE_FLAG_SIGNATURE_NAMED_PARAMS|
|
||||||
|
XS_PARSE_SUBLIKE_FLAG_SIGNATURE_PARAM_ATTRIBUTES,
|
||||||
|
|
||||||
|
/* No hooks */
|
||||||
|
};
|
||||||
|
|
||||||
|
MODULE = Sublike::Extended PACKAGE = Sublike::Extended
|
||||||
|
|
||||||
|
BOOT:
|
||||||
|
boot_xs_parse_sublike(0); // TODO
|
||||||
|
|
||||||
|
register_xs_parse_sublike("extended", &hooks_extended, NULL);
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# (C) Paul Evans, 2020-2021 -- leonerd@leonerd.org.uk
|
# (C) Paul Evans, 2020-2021 -- leonerd@leonerd.org.uk
|
||||||
|
|
||||||
package XS::Parse::Sublike 0.16;
|
package XS::Parse::Sublike 0.21;
|
||||||
|
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
@ -242,10 +242,16 @@ definition.
|
||||||
|
|
||||||
The parameter signature of the function.
|
The parameter signature of the function.
|
||||||
|
|
||||||
This part can be skipped, but the bit is ignored when in I<require_parts>. It
|
This part can be skipped, but it is always permitted not to provide a
|
||||||
is always permitted not to provide a signature for a function definition,
|
signature for a function definition even if the bit it set in
|
||||||
because such syntax only applies when C<use feature 'signatures'> is in
|
I<require_parts>. This is because such syntax only applies when
|
||||||
effect, and only on supporting perl versions.
|
C<use feature 'signatures'> is in effect, and only on supporting perl
|
||||||
|
versions.
|
||||||
|
|
||||||
|
However, setting the bit in I<require_parts> instead has the effect of
|
||||||
|
enabling C<use feature 'signatures'> (at least on supporting perl versions),
|
||||||
|
thus permitting the syntax to use a signature even if the signatures feature
|
||||||
|
was not previously enabled.
|
||||||
|
|
||||||
=item XS_PARSE_SUBLIKE_PART_BODY
|
=item XS_PARSE_SUBLIKE_PART_BODY
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,41 @@
|
||||||
/* You may distribute under the terms of either the GNU General Public License
|
/* You may distribute under the terms of either the GNU General Public License
|
||||||
* or the Artistic License (the same terms as Perl itself)
|
* or the Artistic License (the same terms as Perl itself)
|
||||||
*
|
*
|
||||||
* (C) Paul Evans, 2019-2021 -- leonerd@leonerd.org.uk
|
* (C) Paul Evans, 2019-2023 -- leonerd@leonerd.org.uk
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EXTERN.h"
|
#include "EXTERN.h"
|
||||||
#include "perl.h"
|
#include "perl.h"
|
||||||
#include "XSUB.h"
|
#include "XSUB.h"
|
||||||
|
|
||||||
|
/* We need to be able to see FEATURE_*_IS_ENABLED */
|
||||||
|
#define PERL_EXT
|
||||||
|
#include "feature.h"
|
||||||
|
|
||||||
#include "XSParseSublike.h"
|
#include "XSParseSublike.h"
|
||||||
|
|
||||||
#define HAVE_PERL_VERSION(R, V, S) \
|
#define HAVE_PERL_VERSION(R, V, S) \
|
||||||
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
|
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
|
||||||
|
|
||||||
#if HAVE_PERL_VERSION(5, 26, 0)
|
#if HAVE_PERL_VERSION(5, 37, 10)
|
||||||
# if !HAVE_PERL_VERSION(5, 31, 3)
|
/* feature 'class' first became available in 5.37.9 but it wasn't until
|
||||||
# include "parse_subsignature.c.inc"
|
* 5.37.10 that we could pass CVf_IsMETHOD to start_subparse()
|
||||||
# endif
|
*/
|
||||||
|
# define HAVE_FEATURE_CLASS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* We always need this included to get the struct and function definitions
|
||||||
|
* visible, even though we won't be calling it
|
||||||
|
*/
|
||||||
|
#include "parse_subsignature_ex.h"
|
||||||
|
|
||||||
|
#if HAVE_PERL_VERSION(5, 26, 0)
|
||||||
# include "make_argcheck_aux.c.inc"
|
# include "make_argcheck_aux.c.inc"
|
||||||
|
|
||||||
|
# if !HAVE_PERL_VERSION(5, 31, 3)
|
||||||
|
# define parse_subsignature(flags) parse_subsignature_ex(0) /* ignore core flags as there are none */
|
||||||
|
# endif
|
||||||
|
|
||||||
# define HAVE_PARSE_SUBSIGNATURE
|
# define HAVE_PARSE_SUBSIGNATURE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -34,6 +50,9 @@
|
||||||
|
|
||||||
#include "lexer-additions.c.inc"
|
#include "lexer-additions.c.inc"
|
||||||
|
|
||||||
|
#define QUOTED_PVNf "\"%.*s\"%s"
|
||||||
|
#define QUOTED_PVNfARG(pv,len) ((len) <= 255 ? (int)(len) : 255), (pv), ((len) <= 255 ? "" : "...")
|
||||||
|
|
||||||
struct HooksAndData {
|
struct HooksAndData {
|
||||||
const struct XSParseSublikeHooks *hooks;
|
const struct XSParseSublikeHooks *hooks;
|
||||||
void *data;
|
void *data;
|
||||||
|
@ -49,6 +68,11 @@ struct HooksAndData {
|
||||||
(hooki >= 0) && (hooks = hooksanddata[hooki].hooks, hookdata = hooksanddata[hooki].data), (hooki >= 0); \
|
(hooki >= 0) && (hooks = hooksanddata[hooki].hooks, hookdata = hooksanddata[hooki].data), (hooki >= 0); \
|
||||||
hooki--)
|
hooki--)
|
||||||
|
|
||||||
|
/* Non-documented internal flags we use for our own purposes */
|
||||||
|
enum {
|
||||||
|
XS_PARSE_SUBLIKE_ACTION_CVf_IsMETHOD = (1<<31), /* do we set CVf_IsMETHOD? */
|
||||||
|
};
|
||||||
|
|
||||||
static int parse(pTHX_
|
static int parse(pTHX_
|
||||||
struct HooksAndData hooksanddata[],
|
struct HooksAndData hooksanddata[],
|
||||||
size_t nhooks,
|
size_t nhooks,
|
||||||
|
@ -109,7 +133,15 @@ static int parse(pTHX_
|
||||||
ctx.actions |= XS_PARSE_SUBLIKE_ACTION_CVf_ANON;
|
ctx.actions |= XS_PARSE_SUBLIKE_ACTION_CVf_ANON;
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 floor_ix = start_subparse(FALSE, ctx.actions & XS_PARSE_SUBLIKE_ACTION_CVf_ANON ? CVf_ANON : 0);
|
int subparse_flags = 0;
|
||||||
|
if(ctx.actions & XS_PARSE_SUBLIKE_ACTION_CVf_ANON)
|
||||||
|
subparse_flags |= CVf_ANON;
|
||||||
|
#ifdef HAVE_FEATURE_CLASS
|
||||||
|
if(ctx.actions & XS_PARSE_SUBLIKE_ACTION_CVf_IsMETHOD)
|
||||||
|
subparse_flags |= CVf_IsMETHOD;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
I32 floor_ix = start_subparse(FALSE, subparse_flags);
|
||||||
SAVEFREESV(PL_compcv);
|
SAVEFREESV(PL_compcv);
|
||||||
|
|
||||||
if(!(skip_parts & XS_PARSE_SUBLIKE_PART_ATTRS) && (lex_peek_unichar(0) == ':')) {
|
if(!(skip_parts & XS_PARSE_SUBLIKE_PART_ATTRS) && (lex_peek_unichar(0) == ':')) {
|
||||||
|
@ -175,6 +207,18 @@ static int parse(pTHX_
|
||||||
lex_read_unichar(0);
|
lex_read_unichar(0);
|
||||||
lex_read_space(0);
|
lex_read_space(0);
|
||||||
|
|
||||||
|
if(require_parts & XS_PARSE_SUBLIKE_PART_SIGNATURE) {
|
||||||
|
#if HAVE_PERL_VERSION(5, 32, 0)
|
||||||
|
SAVEI32(PL_compiling.cop_features);
|
||||||
|
PL_compiling.cop_features |= FEATURE_SIGNATURES_BIT;
|
||||||
|
#else
|
||||||
|
/* So far this is only used by the "method" keyword hack for perl 5.38
|
||||||
|
* onwards so this doesn't technically matter. Yet...
|
||||||
|
*/
|
||||||
|
croak("TODO: import_pragma(\"feature\", \"signatures\")");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if HAVE_PERL_VERSION(5, 31, 3)
|
#if HAVE_PERL_VERSION(5, 31, 3)
|
||||||
/* core's parse_subsignature doesn't seem able to handle empty sigs
|
/* core's parse_subsignature doesn't seem able to handle empty sigs
|
||||||
* RT132284
|
* RT132284
|
||||||
|
@ -206,7 +250,18 @@ static int parse(pTHX_
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
sigop = parse_subsignature(0);
|
U32 flags = 0;
|
||||||
|
FOREACH_HOOKS_FORWARD {
|
||||||
|
if(hooks->flags & XS_PARSE_SUBLIKE_FLAG_SIGNATURE_NAMED_PARAMS)
|
||||||
|
flags |= PARSE_SUBSIGNATURE_NAMED_PARAMS;
|
||||||
|
if(hooks->flags & XS_PARSE_SUBLIKE_FLAG_SIGNATURE_PARAM_ATTRIBUTES)
|
||||||
|
flags |= PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flags)
|
||||||
|
sigop = parse_subsignature_ex(flags);
|
||||||
|
else
|
||||||
|
sigop = parse_subsignature(0);
|
||||||
|
|
||||||
if(PL_parser->error_count) {
|
if(PL_parser->error_count) {
|
||||||
assert(PL_scopestack_ix == was_scopestack_ix);
|
assert(PL_scopestack_ix == was_scopestack_ix);
|
||||||
|
@ -413,11 +468,14 @@ static void register_sublike(pTHX_ const char *kw, const void *hooks, void *hook
|
||||||
reg->hooks = hooks;
|
reg->hooks = hooks;
|
||||||
reg->hookdata = hookdata;
|
reg->hookdata = hookdata;
|
||||||
|
|
||||||
if(reg->ver >= 4 && reg->hooks->permit_hintkey)
|
if(reg->hooks->permit_hintkey)
|
||||||
reg->permit_hintkey_len = strlen(reg->hooks->permit_hintkey);
|
reg->permit_hintkey_len = strlen(reg->hooks->permit_hintkey);
|
||||||
else
|
else
|
||||||
reg->permit_hintkey_len = 0;
|
reg->permit_hintkey_len = 0;
|
||||||
|
|
||||||
|
if(!reg->hooks->permit && !reg->hooks->permit_hintkey)
|
||||||
|
croak("Third-party sublike keywords require a permit callback or hinthash key");
|
||||||
|
|
||||||
REGISTRATIONS_LOCK;
|
REGISTRATIONS_LOCK;
|
||||||
{
|
{
|
||||||
reg->next = registrations;
|
reg->next = registrations;
|
||||||
|
@ -476,8 +534,8 @@ static int IMPL_xs_parse_sublike_any_v4(pTHX_ const struct XSParseSublikeHooks *
|
||||||
if(kwlen != 3 || !strEQ(kw, "sub")) {
|
if(kwlen != 3 || !strEQ(kw, "sub")) {
|
||||||
reg = find_permitted(aTHX_ kw, kwlen);
|
reg = find_permitted(aTHX_ kw, kwlen);
|
||||||
if(!reg)
|
if(!reg)
|
||||||
croak("Expected a keyword to introduce a sub or sub-like construction, found \"%.*s\"",
|
croak("Expected a keyword to introduce a sub or sub-like construction, found " QUOTED_PVNf,
|
||||||
kwlen, kw);
|
QUOTED_PVNfARG(kw, kwlen));
|
||||||
}
|
}
|
||||||
|
|
||||||
SvREFCNT_dec(kwsv);
|
SvREFCNT_dec(kwsv);
|
||||||
|
@ -486,7 +544,6 @@ static int IMPL_xs_parse_sublike_any_v4(pTHX_ const struct XSParseSublikeHooks *
|
||||||
{ .hooks = hooksA, .data = hookdataA },
|
{ .hooks = hooksA, .data = hookdataA },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
struct XSParseSublikeHooks hooks;
|
|
||||||
|
|
||||||
if(reg) {
|
if(reg) {
|
||||||
hd[1].hooks = reg->hooks;
|
hd[1].hooks = reg->hooks;
|
||||||
|
@ -501,6 +558,42 @@ static int IMPL_xs_parse_sublike_any_v3(pTHX_ const void *hooksA, void *hookdata
|
||||||
croak("XS::Parse::Sublike ABI v3 is no longer supported; the caller should be rebuilt to use v4");
|
croak("XS::Parse::Sublike ABI v3 is no longer supported; the caller should be rebuilt to use v4");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void IMPL_register_xps_signature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
|
||||||
|
{
|
||||||
|
if(funcs->ver < 5)
|
||||||
|
croak("Mismatch in signature param attribute ABI version field: module wants %u; we require >= 5\n",
|
||||||
|
funcs->ver);
|
||||||
|
if(funcs->ver > XSPARSESUBLIKE_ABI_VERSION)
|
||||||
|
croak("Mismatch in signature param attribute ABI version field: module wants %u; we support <= %d\n",
|
||||||
|
funcs->ver, XSPARSESUBLIKE_ABI_VERSION);
|
||||||
|
|
||||||
|
if(!name || !(name[0] >= 'A' && name[0] <= 'Z'))
|
||||||
|
croak("Signature param attribute names must begin with a capital letter");
|
||||||
|
|
||||||
|
if(!funcs->permit_hintkey)
|
||||||
|
croak("Signature param attributes require a permit hinthash key");
|
||||||
|
|
||||||
|
register_subsignature_attribute(name, funcs, funcdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_FEATURE_CLASS
|
||||||
|
static bool permit_core_method(pTHX_ void *hookdata)
|
||||||
|
{
|
||||||
|
return FEATURE_CLASS_IS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pre_subparse_core_method(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
|
||||||
|
{
|
||||||
|
ctx->actions |= XS_PARSE_SUBLIKE_ACTION_CVf_IsMETHOD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct XSParseSublikeHooks hooks_core_method = {
|
||||||
|
.permit = &permit_core_method,
|
||||||
|
.pre_subparse = &pre_subparse_core_method,
|
||||||
|
.require_parts = XS_PARSE_SUBLIKE_PART_SIGNATURE, /* enable signatures feature */
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
|
static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
|
||||||
|
|
||||||
static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op_ptr)
|
static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op_ptr)
|
||||||
|
@ -543,8 +636,8 @@ static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op_ptr)
|
||||||
|
|
||||||
reg = find_permitted(aTHX_ kw, kwlen);
|
reg = find_permitted(aTHX_ kw, kwlen);
|
||||||
if(!reg)
|
if(!reg)
|
||||||
croak("Expected a keyword to introduce a sub or sub-like construction, found \"%.*s\"",
|
croak("Expected a keyword to introduce a sub or sub-like construction, found " QUOTED_PVNf,
|
||||||
kwlen, kw);
|
QUOTED_PVNfARG(kw, kwlen));
|
||||||
|
|
||||||
hooks = (struct XSParseSublikeHooks *)reg->hooks;
|
hooks = (struct XSParseSublikeHooks *)reg->hooks;
|
||||||
|
|
||||||
|
@ -572,8 +665,14 @@ BOOT:
|
||||||
/* Newer mechanism */
|
/* Newer mechanism */
|
||||||
sv_setiv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/ABIVERSION_MIN", 1), 4);
|
sv_setiv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/ABIVERSION_MIN", 1), 4);
|
||||||
sv_setiv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/ABIVERSION_MAX", 1), XSPARSESUBLIKE_ABI_VERSION);
|
sv_setiv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/ABIVERSION_MAX", 1), XSPARSESUBLIKE_ABI_VERSION);
|
||||||
|
|
||||||
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parse()@4", 1), PTR2UV(&IMPL_xs_parse_sublike_v4));
|
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parse()@4", 1), PTR2UV(&IMPL_xs_parse_sublike_v4));
|
||||||
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/register()@4", 1), PTR2UV(&IMPL_register_xs_parse_sublike_v4));
|
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/register()@4", 1), PTR2UV(&IMPL_register_xs_parse_sublike_v4));
|
||||||
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parseany()@4", 1), PTR2UV(&IMPL_xs_parse_sublike_any_v4));
|
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/parseany()@4", 1), PTR2UV(&IMPL_xs_parse_sublike_any_v4));
|
||||||
|
|
||||||
|
sv_setuv(*hv_fetchs(PL_modglobal, "XS::Parse::Sublike/register_sigattr()@5", 1), PTR2UV(&IMPL_register_xps_signature_attribute));
|
||||||
|
#ifdef HAVE_FEATURE_CLASS
|
||||||
|
register_sublike(aTHX_ "method", &hooks_core_method, NULL, 4);
|
||||||
|
#endif
|
||||||
|
|
||||||
wrap_keyword_plugin(&my_keyword_plugin, &next_keyword_plugin);
|
wrap_keyword_plugin(&my_keyword_plugin, &next_keyword_plugin);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# (C) Paul Evans, 2020-2021 -- leonerd@leonerd.org.uk
|
# (C) Paul Evans, 2020-2021 -- leonerd@leonerd.org.uk
|
||||||
|
|
||||||
package XS::Parse::Sublike::Builder 0.16;
|
package XS::Parse::Sublike::Builder 0.21;
|
||||||
|
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
@ -46,7 +46,7 @@ require XS::Parse::Sublike::Builder_data;
|
||||||
|
|
||||||
=head2 write_XSParseSublike_h
|
=head2 write_XSParseSublike_h
|
||||||
|
|
||||||
XS::Parse::Sublike::Builder->write_XSParseSublike_h
|
XS::Parse::Sublike::Builder->write_XSParseSublike_h;
|
||||||
|
|
||||||
Writes the F<XSParseSublike.h> file to the current working directory. To cause
|
Writes the F<XSParseSublike.h> file to the current working directory. To cause
|
||||||
the compiler to actually find this file, see L</extra_compiler_flags>.
|
the compiler to actually find this file, see L</extra_compiler_flags>.
|
||||||
|
@ -65,7 +65,7 @@ sub write_XSParseSublike_h
|
||||||
|
|
||||||
=head2 extra_compiler_flags
|
=head2 extra_compiler_flags
|
||||||
|
|
||||||
@flags = XS::Parse::Sublike::Builder->extra_compiler_flags
|
@flags = XS::Parse::Sublike::Builder->extra_compiler_flags;
|
||||||
|
|
||||||
Returns a list of extra flags that the build scripts should add to the
|
Returns a list of extra flags that the build scripts should add to the
|
||||||
compiler invocation. This enables the C compiler to find the
|
compiler invocation. This enables the C compiler to find the
|
||||||
|
@ -81,7 +81,7 @@ sub extra_compiler_flags
|
||||||
|
|
||||||
=head2 extend_module_build
|
=head2 extend_module_build
|
||||||
|
|
||||||
XS::Parse::Sublike::Builder->extend_module_build( $build )
|
XS::Parse::Sublike::Builder->extend_module_build( $build );
|
||||||
|
|
||||||
A convenient shortcut for performing all the tasks necessary to make a
|
A convenient shortcut for performing all the tasks necessary to make a
|
||||||
L<Module::Build>-based distribution use the helper.
|
L<Module::Build>-based distribution use the helper.
|
||||||
|
|
|
@ -22,7 +22,7 @@ $outh->print( scalar do {
|
||||||
<$in_h> } );
|
<$in_h> } );
|
||||||
|
|
||||||
__DATA__
|
__DATA__
|
||||||
package XS::Parse::Sublike::Builder_data 0.16;
|
package XS::Parse::Sublike::Builder_data 0.21;
|
||||||
|
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
|
@ -0,0 +1,617 @@
|
||||||
|
/* vi: set ft=c : */
|
||||||
|
|
||||||
|
#include "EXTERN.h"
|
||||||
|
#include "perl.h"
|
||||||
|
#include "XSUB.h"
|
||||||
|
|
||||||
|
#define HAVE_PERL_VERSION(R, V, S) \
|
||||||
|
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
|
||||||
|
|
||||||
|
#include "XSParseSublike.h"
|
||||||
|
|
||||||
|
/* Skip this entire file on perls older than OP_ARGCHECK */
|
||||||
|
#if HAVE_PERL_VERSION(5, 26, 0)
|
||||||
|
|
||||||
|
#define PERL_EXT
|
||||||
|
/* We need to be able to see FEATURE_*_IS_ENABLED */
|
||||||
|
#include "feature.h"
|
||||||
|
/* Also need KEY_sigvar */
|
||||||
|
#include "keywords.h"
|
||||||
|
|
||||||
|
#include "parse_subsignature_ex.h"
|
||||||
|
|
||||||
|
#include "lexer-additions.c.inc"
|
||||||
|
|
||||||
|
#include "LOGOP_ANY.c.inc"
|
||||||
|
#include "croak_from_caller.c.inc"
|
||||||
|
#include "make_argcheck_aux.c.inc"
|
||||||
|
#include "newSV_with_free.c.inc"
|
||||||
|
|
||||||
|
#define newSVpvx(ptr) S_newSVpvx(aTHX_ ptr)
|
||||||
|
static SV *S_newSVpvx(pTHX_ void *ptr)
|
||||||
|
{
|
||||||
|
SV *sv = newSV(0);
|
||||||
|
sv_upgrade(sv, SVt_PV);
|
||||||
|
SvPVX(sv) = ptr;
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need to grab some things that aren't quite core perl API
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* yyerror() is a long function and hard to emulate or copy-paste for our
|
||||||
|
* purposes; we'll reïmplement a smaller version of it
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LEX_IGNORE_UTF8_HINTS 0x00000002
|
||||||
|
|
||||||
|
#define PL_linestr (PL_parser->linestr)
|
||||||
|
|
||||||
|
#ifdef USE_UTF8_SCRIPTS
|
||||||
|
# define UTF cBOOL(!IN_BYTES)
|
||||||
|
#else
|
||||||
|
# define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || ( !(PL_parser->lex_flags & LEX_IGNORE_UTF8_HINTS) && (PL_hints & HINT_UTF8)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define yyerror(s) S_yyerror(aTHX_ s)
|
||||||
|
void S_yyerror(pTHX_ const char *s)
|
||||||
|
{
|
||||||
|
SV *message = sv_2mortal(newSVpvs_flags("", 0));
|
||||||
|
|
||||||
|
char *context = PL_parser->oldbufptr;
|
||||||
|
STRLEN contlen = PL_parser->bufptr - PL_parser->oldbufptr;
|
||||||
|
|
||||||
|
sv_catpvf(message, "%s at %s line %" IVdf,
|
||||||
|
s, OutCopFILE(PL_curcop), (IV)CopLINE(PL_curcop));
|
||||||
|
|
||||||
|
if(context)
|
||||||
|
sv_catpvf(message, ", near \"%" UTF8f "\"",
|
||||||
|
UTF8fARG(UTF, contlen, context));
|
||||||
|
|
||||||
|
sv_catpvf(message, "\n");
|
||||||
|
|
||||||
|
PL_parser->error_count++;
|
||||||
|
warn_sv(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stolen from op.c */
|
||||||
|
#ifndef OpTYPE_set
|
||||||
|
# define OpTYPE_set(op, type) \
|
||||||
|
STMT_START { \
|
||||||
|
op->op_type = (OPCODE)type; \
|
||||||
|
op->op_ppaddr = PL_ppaddr[type]; \
|
||||||
|
} STMT_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define alloc_LOGOP(a,b,c) S_alloc_LOGOP(aTHX_ a,b,c)
|
||||||
|
static LOGOP *S_alloc_LOGOP(pTHX_ I32 type, OP *first, OP* other)
|
||||||
|
{
|
||||||
|
dVAR;
|
||||||
|
LOGOP *logop;
|
||||||
|
OP *kid = first;
|
||||||
|
NewOp(1101, logop, 1, LOGOP);
|
||||||
|
OpTYPE_set(logop, type);
|
||||||
|
logop->op_first = first;
|
||||||
|
logop->op_other = other;
|
||||||
|
if (first)
|
||||||
|
logop->op_flags = OPf_KIDS;
|
||||||
|
while (kid && OpHAS_SIBLING(kid))
|
||||||
|
kid = OpSIBLING(kid);
|
||||||
|
if (kid)
|
||||||
|
OpLASTSIB_set(kid, (OP*)logop);
|
||||||
|
return logop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copypaste from core's pp.c */
|
||||||
|
static SV *
|
||||||
|
S_find_runcv_name(pTHX)
|
||||||
|
{
|
||||||
|
CV *cv;
|
||||||
|
GV *gv;
|
||||||
|
SV *sv;
|
||||||
|
|
||||||
|
cv = find_runcv(0);
|
||||||
|
if (!cv)
|
||||||
|
return &PL_sv_no;
|
||||||
|
|
||||||
|
gv = CvGV(cv);
|
||||||
|
if (!gv)
|
||||||
|
return &PL_sv_no;
|
||||||
|
|
||||||
|
sv = sv_newmortal();
|
||||||
|
gv_fullname4(sv, gv, NULL, TRUE);
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static OP *pp_namedargdefelem(pTHX)
|
||||||
|
{
|
||||||
|
dSP;
|
||||||
|
ANY *op_any = cLOGOP_ANY->op_any;
|
||||||
|
SV *keysv = op_any[0].any_sv;
|
||||||
|
HV *slurpy_hv = (HV *)PAD_SVl(op_any[1].any_iv);
|
||||||
|
|
||||||
|
assert(slurpy_hv && SvTYPE(slurpy_hv) == SVt_PVHV);
|
||||||
|
|
||||||
|
/* TODO: we could precompute the hash and store it in the ANY vector */
|
||||||
|
SV *value = hv_delete_ent(slurpy_hv, keysv, 0, 0);
|
||||||
|
|
||||||
|
if(value) {
|
||||||
|
EXTEND(SP, 1);
|
||||||
|
PUSHs(value);
|
||||||
|
RETURN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cLOGOP->op_other)
|
||||||
|
return cLOGOP->op_other;
|
||||||
|
|
||||||
|
croak_from_caller("Missing argument '%" SVf "' for subroutine %" SVf,
|
||||||
|
SVfARG(keysv), SVfARG(S_find_runcv_name(aTHX)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static OP *pp_checknomorenamed(pTHX)
|
||||||
|
{
|
||||||
|
HV *slurpy_hv = (HV *)PAD_SVl(PL_op->op_targ);
|
||||||
|
|
||||||
|
if(!hv_iterinit(slurpy_hv))
|
||||||
|
return NORMAL;
|
||||||
|
|
||||||
|
/* There are remaining named arguments; concat their names into a message */
|
||||||
|
|
||||||
|
HE *he = hv_iternext(slurpy_hv);
|
||||||
|
|
||||||
|
SV *keynames = newSVpvn("", 0);
|
||||||
|
SAVEFREESV(keynames);
|
||||||
|
|
||||||
|
sv_catpvf(keynames, "'%" SVf "'", SVfARG(HeSVKEY_force(he)));
|
||||||
|
|
||||||
|
IV nkeys = 1;
|
||||||
|
|
||||||
|
while((he = hv_iternext(slurpy_hv)))
|
||||||
|
sv_catpvf(keynames, ", '%" SVf "'", SVfARG(HeSVKEY_force(he))), nkeys++;
|
||||||
|
|
||||||
|
croak_from_caller("Unrecognised %s %" SVf " for subroutine %" SVf,
|
||||||
|
nkeys > 1 ? "arguments" : "argument",
|
||||||
|
SVfARG(keynames), SVfARG(S_find_runcv_name(aTHX)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OP_IS_NAMED_PARAM(o) (o->op_type == OP_ARGELEM && cUNOPx(o)->op_first && \
|
||||||
|
cUNOPx(o)->op_first->op_type == OP_CUSTOM && \
|
||||||
|
cUNOPx(o)->op_first->op_ppaddr == &pp_namedargdefelem)
|
||||||
|
|
||||||
|
/* Parameter attribute extensions */
|
||||||
|
typedef struct SignatureAttributeRegistration SignatureAttributeRegistration;
|
||||||
|
|
||||||
|
struct SignatureAttributeRegistration {
|
||||||
|
SignatureAttributeRegistration *next;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
STRLEN permit_hintkeylen;
|
||||||
|
|
||||||
|
const struct XPSSignatureAttributeFuncs *funcs;
|
||||||
|
void *funcdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
static SignatureAttributeRegistration *sigattrs = NULL;
|
||||||
|
|
||||||
|
#define find_registered_attribute(name) S_find_registered_attribute(aTHX_ name)
|
||||||
|
static SignatureAttributeRegistration *S_find_registered_attribute(pTHX_ const char *name)
|
||||||
|
{
|
||||||
|
HV *hints = GvHV(PL_hintgv);
|
||||||
|
|
||||||
|
SignatureAttributeRegistration *reg;
|
||||||
|
for(reg = sigattrs; reg; reg = reg->next) {
|
||||||
|
if(!strEQ(name, reg->name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(reg->funcs->permit_hintkey &&
|
||||||
|
(!hints || !hv_fetch(hints, reg->funcs->permit_hintkey, reg->permit_hintkeylen, 0)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
croak("Unrecognised signature parameter attribute :%s", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PendingSignatureFunc {
|
||||||
|
const struct XPSSignatureAttributeFuncs *funcs;
|
||||||
|
void *funcdata;
|
||||||
|
void *attrdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PENDING_FROM_SV(sv) ((struct PendingSignatureFunc *)SvPVX(sv))
|
||||||
|
|
||||||
|
static void pending_free(pTHX_ SV *sv)
|
||||||
|
{
|
||||||
|
struct PendingSignatureFunc *p = PENDING_FROM_SV(sv);
|
||||||
|
|
||||||
|
if(p->funcs->free)
|
||||||
|
(*p->funcs->free)(aTHX_ p->attrdata, p->funcdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NEW_SV_PENDING() newSV_with_free(sizeof(struct PendingSignatureFunc), &pending_free)
|
||||||
|
|
||||||
|
struct SignatureParsingContext {
|
||||||
|
AV *named_varops; /* SV ptrs to the varop of every named parameter */
|
||||||
|
|
||||||
|
OP *last_varop; /* the most recently-constructed varop */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_parsing_ctx(pTHX_ void *_ctx)
|
||||||
|
{
|
||||||
|
struct SignatureParsingContext *ctx = _ctx;
|
||||||
|
if(ctx->named_varops)
|
||||||
|
SvREFCNT_dec((SV *)ctx->named_varops);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define parse_sigelem(ctx, flags) S_parse_sigelem(aTHX_ ctx, flags)
|
||||||
|
static OP *S_parse_sigelem(pTHX_ struct SignatureParsingContext *ctx, U32 flags)
|
||||||
|
{
|
||||||
|
bool permit_attributes = flags & PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES;
|
||||||
|
|
||||||
|
yy_parser *parser = PL_parser;
|
||||||
|
|
||||||
|
int c = lex_peek_unichar(0);
|
||||||
|
int private;
|
||||||
|
struct XPSSignatureParamContext paramctx = {};
|
||||||
|
|
||||||
|
AV *pending = NULL;
|
||||||
|
|
||||||
|
if((flags & PARSE_SUBSIGNATURE_NAMED_PARAMS) && c == ':') {
|
||||||
|
lex_read_unichar(0);
|
||||||
|
lex_read_space(0);
|
||||||
|
|
||||||
|
paramctx.is_named = true;
|
||||||
|
c = lex_peek_unichar(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(c) {
|
||||||
|
case '$': private = OPpARGELEM_SV; break;
|
||||||
|
case '@': private = OPpARGELEM_AV; break;
|
||||||
|
case '%': private = OPpARGELEM_HV; break;
|
||||||
|
default:
|
||||||
|
croak("Expected a signature element at <%s>\n", parser->bufptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *lexname = parser->bufptr;
|
||||||
|
|
||||||
|
/* Consume sigil */
|
||||||
|
lex_read_unichar(0);
|
||||||
|
|
||||||
|
char *lexname_end = NULL;
|
||||||
|
|
||||||
|
if(isIDFIRST_uni(lex_peek_unichar(0))) {
|
||||||
|
lex_read_unichar(0);
|
||||||
|
while(isALNUM_uni(lex_peek_unichar(0)))
|
||||||
|
lex_read_unichar(0);
|
||||||
|
|
||||||
|
paramctx.varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (parser->sig_elems)));
|
||||||
|
paramctx.varop->op_private |= private;
|
||||||
|
|
||||||
|
if(paramctx.is_named) {
|
||||||
|
if(!ctx->named_varops)
|
||||||
|
ctx->named_varops = newAV();
|
||||||
|
|
||||||
|
av_push(ctx->named_varops, newSVpvx(paramctx.varop));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->last_varop = paramctx.varop;
|
||||||
|
|
||||||
|
ENTER;
|
||||||
|
SAVEI16(PL_parser->in_my);
|
||||||
|
PL_parser->in_my = KEY_sigvar;
|
||||||
|
|
||||||
|
lexname_end = PL_parser->bufptr;
|
||||||
|
paramctx.padix = paramctx.varop->op_targ =
|
||||||
|
pad_add_name_pvn(lexname, lexname_end - lexname, 0, NULL, NULL);
|
||||||
|
|
||||||
|
LEAVE;
|
||||||
|
|
||||||
|
lex_read_space(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(permit_attributes && lex_peek_unichar(0) == ':') {
|
||||||
|
lex_read_unichar(0);
|
||||||
|
lex_read_space(0);
|
||||||
|
|
||||||
|
SV *attrname = sv_newmortal(), *attrval = sv_newmortal();
|
||||||
|
|
||||||
|
while(lex_scan_attrval_into(attrname, attrval)) {
|
||||||
|
lex_read_space(0);
|
||||||
|
|
||||||
|
SignatureAttributeRegistration *reg = find_registered_attribute(SvPV_nolen(attrname));
|
||||||
|
|
||||||
|
void *attrdata = NULL;
|
||||||
|
if(reg->funcs->apply)
|
||||||
|
(*reg->funcs->apply)(aTHX_ ¶mctx, attrval, &attrdata, reg->funcdata);
|
||||||
|
|
||||||
|
if(attrdata || reg->funcs->post_defop) {
|
||||||
|
if(!pending) {
|
||||||
|
pending = newAV();
|
||||||
|
SAVEFREESV(pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
SV *psv;
|
||||||
|
av_push(pending, psv = NEW_SV_PENDING());
|
||||||
|
|
||||||
|
PENDING_FROM_SV(psv)->funcs = reg->funcs;
|
||||||
|
PENDING_FROM_SV(psv)->funcdata = reg->funcdata;
|
||||||
|
PENDING_FROM_SV(psv)->attrdata = attrdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lex_peek_unichar(0) == ':') {
|
||||||
|
lex_read_unichar(0);
|
||||||
|
lex_read_space(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(c == '$') {
|
||||||
|
SV *argname = NULL;
|
||||||
|
|
||||||
|
if(paramctx.is_named) {
|
||||||
|
parser->sig_slurpy = '+';
|
||||||
|
argname = newSVpvn(lexname + 1, lexname_end - lexname - 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(parser->sig_slurpy)
|
||||||
|
yyerror("Slurpy parameters not last");
|
||||||
|
|
||||||
|
parser->sig_elems++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lex_peek_unichar(0) == '=') {
|
||||||
|
lex_read_unichar(0);
|
||||||
|
lex_read_space(0);
|
||||||
|
|
||||||
|
if(!paramctx.is_named)
|
||||||
|
parser->sig_optelems++;
|
||||||
|
|
||||||
|
OP *defexpr = parse_termexpr(0);
|
||||||
|
|
||||||
|
if(paramctx.is_named) {
|
||||||
|
paramctx.defop = (OP *)alloc_LOGOP_ANY(OP_CUSTOM, defexpr, LINKLIST(defexpr));
|
||||||
|
paramctx.defop->op_ppaddr = &pp_namedargdefelem;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
paramctx.defop = (OP *)alloc_LOGOP(OP_ARGDEFELEM, defexpr, LINKLIST(defexpr));
|
||||||
|
paramctx.defop->op_targ = (PADOFFSET)(parser->sig_elems - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
paramctx.varop->op_flags |= OPf_STACKED;
|
||||||
|
op_sibling_splice(paramctx.varop, NULL, 0, paramctx.defop);
|
||||||
|
paramctx.defop = op_contextualize(paramctx.defop, G_SCALAR);
|
||||||
|
|
||||||
|
LINKLIST(paramctx.varop);
|
||||||
|
|
||||||
|
paramctx.varop->op_next = paramctx.defop;
|
||||||
|
defexpr->op_next = paramctx.varop;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(parser->sig_optelems)
|
||||||
|
yyerror("Mandatory parameter follows optional parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(paramctx.is_named) {
|
||||||
|
OP *defop = paramctx.defop;
|
||||||
|
if(!defop) {
|
||||||
|
defop = (OP *)alloc_LOGOP_ANY(OP_CUSTOM, NULL, NULL);
|
||||||
|
defop->op_ppaddr = &pp_namedargdefelem;
|
||||||
|
|
||||||
|
paramctx.varop->op_flags |= OPf_STACKED;
|
||||||
|
op_sibling_splice(paramctx.varop, NULL, 0, defop);
|
||||||
|
|
||||||
|
LINKLIST(paramctx.varop);
|
||||||
|
|
||||||
|
paramctx.varop->op_next = defop;
|
||||||
|
}
|
||||||
|
|
||||||
|
ANY *op_any;
|
||||||
|
Newx(op_any, 2, ANY);
|
||||||
|
|
||||||
|
op_any[0].any_sv = argname;
|
||||||
|
/* [1] is filled in later */
|
||||||
|
|
||||||
|
cLOGOP_ANYx(defop)->op_any = op_any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(paramctx.is_named)
|
||||||
|
yyerror("Slurpy parameters may not be named");
|
||||||
|
if(parser->sig_slurpy && parser->sig_slurpy != '+')
|
||||||
|
yyerror("Multiple slurpy parameters not allowed");
|
||||||
|
|
||||||
|
parser->sig_slurpy = c;
|
||||||
|
|
||||||
|
if(lex_peek_unichar(0) == '=')
|
||||||
|
yyerror("A slurpy parameter may not have a default value");
|
||||||
|
}
|
||||||
|
|
||||||
|
paramctx.op = paramctx.varop;
|
||||||
|
|
||||||
|
if(pending) {
|
||||||
|
for(int i = 0; i <= AvFILL(pending); i++) {
|
||||||
|
struct PendingSignatureFunc *p = PENDING_FROM_SV(AvARRAY(pending)[i]);
|
||||||
|
|
||||||
|
if(p->funcs->post_defop)
|
||||||
|
(*p->funcs->post_defop)(aTHX_ ¶mctx, p->attrdata, p->funcdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paramctx.op ? newSTATEOP(0, NULL, paramctx.op) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
OP *XPS_parse_subsignature_ex(pTHX_ int flags)
|
||||||
|
{
|
||||||
|
/* Mostly reconstructed logic from perl 5.28.0's toke.c and perly.y
|
||||||
|
*/
|
||||||
|
yy_parser *parser = PL_parser;
|
||||||
|
struct SignatureParsingContext ctx = {};
|
||||||
|
|
||||||
|
assert((flags & ~(PARSE_SUBSIGNATURE_NAMED_PARAMS|PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES)) == 0);
|
||||||
|
|
||||||
|
ENTER;
|
||||||
|
SAVEDESTRUCTOR_X(&free_parsing_ctx, &ctx);
|
||||||
|
|
||||||
|
SAVEIV(parser->sig_elems);
|
||||||
|
SAVEIV(parser->sig_optelems);
|
||||||
|
SAVEI8(parser->sig_slurpy);
|
||||||
|
|
||||||
|
parser->sig_elems = 0;
|
||||||
|
parser->sig_optelems = 0;
|
||||||
|
parser->sig_slurpy = 0;
|
||||||
|
|
||||||
|
OP *elems = NULL;
|
||||||
|
OP *namedelems = NULL;
|
||||||
|
OP *final_elem = NULL;
|
||||||
|
|
||||||
|
while(lex_peek_unichar(0) != ')') {
|
||||||
|
lex_read_space(0);
|
||||||
|
OP *elem = parse_sigelem(&ctx, flags);
|
||||||
|
|
||||||
|
/* placeholder anonymous elems are NULL */
|
||||||
|
if(elem) {
|
||||||
|
/* elem should be an OP_LINESEQ[ OP_NEXTSTATE. actual elem ] */
|
||||||
|
assert(elem->op_type == OP_LINESEQ);
|
||||||
|
assert(cLISTOPx(elem)->op_first);
|
||||||
|
assert(OpSIBLING(cLISTOPx(elem)->op_first));
|
||||||
|
|
||||||
|
final_elem = OpSIBLING(cLISTOPx(elem)->op_first);
|
||||||
|
|
||||||
|
if(OP_IS_NAMED_PARAM(ctx.last_varop))
|
||||||
|
namedelems = op_append_list(OP_LIST, namedelems, elem);
|
||||||
|
else
|
||||||
|
elems = op_append_list(OP_LINESEQ, elems, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(PL_parser->error_count) {
|
||||||
|
LEAVE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lex_read_space(0);
|
||||||
|
switch(lex_peek_unichar(0)) {
|
||||||
|
case ')': goto endofelems;
|
||||||
|
case ',': break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "ARGH unsure how to proceed parse_subsignature at <%s>\n",
|
||||||
|
parser->bufptr);
|
||||||
|
croak("ARGH");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lex_read_unichar(0);
|
||||||
|
lex_read_space(0);
|
||||||
|
}
|
||||||
|
endofelems:
|
||||||
|
|
||||||
|
if (!FEATURE_SIGNATURES_IS_ENABLED)
|
||||||
|
croak("Experimental subroutine signatures not enabled");
|
||||||
|
|
||||||
|
#if !HAVE_PERL_VERSION(5, 37, 0)
|
||||||
|
Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__SIGNATURES),
|
||||||
|
"The signatures feature is experimental");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool allow_extras_after_named = true;
|
||||||
|
if(ctx.named_varops) {
|
||||||
|
switch(PL_parser->sig_slurpy) {
|
||||||
|
case 0:
|
||||||
|
case '@':
|
||||||
|
NOT_REACHED;
|
||||||
|
case '+':
|
||||||
|
{
|
||||||
|
/* Pretend we have a new, unnamed slurpy hash */
|
||||||
|
OP *varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (parser->sig_elems)));
|
||||||
|
varop->op_private |= OPpARGELEM_HV;
|
||||||
|
varop->op_targ = pad_add_name_pvs("%(params)", 0, NULL, NULL);
|
||||||
|
|
||||||
|
final_elem = varop;
|
||||||
|
|
||||||
|
OP *elem = newSTATEOP(0, NULL, varop);
|
||||||
|
elems = op_append_list(OP_LINESEQ, elems, elem);
|
||||||
|
|
||||||
|
PL_parser->sig_slurpy = '%';
|
||||||
|
allow_extras_after_named = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UNOP_AUX_item *aux = make_argcheck_aux(
|
||||||
|
parser->sig_elems, parser->sig_optelems, parser->sig_slurpy);
|
||||||
|
|
||||||
|
OP *checkop = newUNOP_AUX(OP_ARGCHECK, 0, NULL, aux);
|
||||||
|
|
||||||
|
OP *ops = op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, NULL),
|
||||||
|
op_prepend_elem(OP_LINESEQ, checkop, elems));
|
||||||
|
|
||||||
|
if(ctx.named_varops) {
|
||||||
|
assert(final_elem->op_type == OP_ARGELEM);
|
||||||
|
assert(final_elem->op_private == OPpARGELEM_HV);
|
||||||
|
|
||||||
|
PADOFFSET slurpy_padix = final_elem->op_targ;
|
||||||
|
|
||||||
|
/* Tell all the pp_namedargdefelem()s where to find the slurpy hash */
|
||||||
|
for(int i = 0; i <= AvFILL(ctx.named_varops); i++) {
|
||||||
|
OP *elemop = (OP *)(SvPVX(AvARRAY(ctx.named_varops)[i]));
|
||||||
|
assert(elemop);
|
||||||
|
assert(OP_IS_NAMED_PARAM(elemop));
|
||||||
|
|
||||||
|
OP *defelemop = cUNOPx(elemop)->op_first;
|
||||||
|
assert(defelemop);
|
||||||
|
assert(defelemop->op_type == OP_CUSTOM &&
|
||||||
|
defelemop->op_ppaddr == &pp_namedargdefelem);
|
||||||
|
ANY *op_any = cLOGOP_ANYx(defelemop)->op_any;
|
||||||
|
op_any[1].any_iv = slurpy_padix;
|
||||||
|
}
|
||||||
|
|
||||||
|
ops = op_append_list(OP_LINESEQ, ops,
|
||||||
|
namedelems);
|
||||||
|
|
||||||
|
if(!allow_extras_after_named) {
|
||||||
|
ops = op_append_list(OP_LINESEQ, ops,
|
||||||
|
newSTATEOP(0, NULL, checkop = newOP(OP_CUSTOM, 0)));
|
||||||
|
checkop->op_ppaddr = &pp_checknomorenamed;
|
||||||
|
checkop->op_targ = slurpy_padix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a nextstate at the end handles context correctly for an empty
|
||||||
|
* sub body */
|
||||||
|
ops = op_append_elem(OP_LINESEQ, ops, newSTATEOP(0, NULL, NULL));
|
||||||
|
|
||||||
|
LEAVE;
|
||||||
|
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
|
||||||
|
{
|
||||||
|
SignatureAttributeRegistration *reg;
|
||||||
|
Newx(reg, 1, struct SignatureAttributeRegistration);
|
||||||
|
|
||||||
|
*reg = (struct SignatureAttributeRegistration){
|
||||||
|
.name = name,
|
||||||
|
.funcs = funcs,
|
||||||
|
.funcdata = funcdata,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(funcs->permit_hintkey)
|
||||||
|
reg->permit_hintkeylen = strlen(funcs->permit_hintkey);
|
||||||
|
|
||||||
|
reg->next = sigattrs;
|
||||||
|
sigattrs = reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !HAVE_PERL_VERSION(5, 26, 0) */
|
||||||
|
|
||||||
|
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
|
||||||
|
{
|
||||||
|
croak("Custom subroutine signature attributes are not supported on this verison of Perl");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,8 +3,9 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use_ok( "XS::Parse::Sublike" );
|
require XS::Parse::Sublike;
|
||||||
|
|
||||||
|
pass( "Modules loaded" );
|
||||||
done_testing;
|
done_testing;
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::func";
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/func"}++ }
|
||||||
|
|
||||||
# named func
|
# named func
|
||||||
{
|
{
|
||||||
func example { return 123; }
|
func example { return 123; }
|
||||||
|
@ -30,7 +32,7 @@ use testcase "t::func";
|
||||||
{
|
{
|
||||||
my ( $pkg, $sub, $attr ) = @_;
|
my ( $pkg, $sub, $attr ) = @_;
|
||||||
$modify_invoked++;
|
$modify_invoked++;
|
||||||
Test::More::is( $attr, "MyCustomAttribute(value here)",
|
::is( $attr, "MyCustomAttribute(value here)",
|
||||||
'MODIFY_CODE_ATTRIBUTES takes attribute' );
|
'MODIFY_CODE_ATTRIBUTES takes attribute' );
|
||||||
|
|
||||||
return ();
|
return ();
|
||||||
|
|
|
@ -3,11 +3,10 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
BEGIN {
|
BEGIN {
|
||||||
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
}
|
}
|
||||||
use Test::Fatal;
|
|
||||||
|
|
||||||
use feature 'signatures';
|
use feature 'signatures';
|
||||||
no warnings 'experimental';
|
no warnings 'experimental';
|
||||||
|
@ -15,6 +14,8 @@ no warnings 'experimental';
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::func";
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/func"}++ }
|
||||||
|
|
||||||
# basic sig
|
# basic sig
|
||||||
{
|
{
|
||||||
func withparam($x) { return $x + 1 }
|
func withparam($x) { return $x + 1 }
|
||||||
|
@ -56,7 +57,7 @@ use testcase "t::func";
|
||||||
func noparams() { return "constant" }
|
func noparams() { return "constant" }
|
||||||
|
|
||||||
is( noparams, "constant", 'func with no params' );
|
is( noparams, "constant", 'func with no params' );
|
||||||
like( exception { noparams( 1, 2, 3 ) },
|
like( dies { noparams( 1, 2, 3 ) },
|
||||||
# message was extended somewhere in perl 5.33
|
# message was extended somewhere in perl 5.33
|
||||||
qr/^Too many arguments for subroutine 'main::noparams' (\(.*\) )?at /,
|
qr/^Too many arguments for subroutine 'main::noparams' (\(.*\) )?at /,
|
||||||
'Exception thrown from empty signature validation failure' );
|
'Exception thrown from empty signature validation failure' );
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
BEGIN {
|
BEGIN {
|
||||||
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ no warnings 'experimental';
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::func";
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/func"}++ }
|
||||||
|
|
||||||
use B::Deparse;
|
use B::Deparse;
|
||||||
|
|
||||||
my $deparser = B::Deparse->new();
|
my $deparser = B::Deparse->new();
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
}
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
no warnings 'experimental';
|
||||||
|
|
||||||
|
use lib "t";
|
||||||
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/nfunc"}++ }
|
||||||
|
|
||||||
|
# a signature with experimental named parameter support
|
||||||
|
{
|
||||||
|
my %was_rest;
|
||||||
|
nfunc withx(:$x, %rest) { %was_rest = %rest; return $x }
|
||||||
|
|
||||||
|
is( withx( x => 123 ), 123, 'named param extracts value' );
|
||||||
|
is( \%was_rest, {}, 'named param not visible in %rest' );
|
||||||
|
|
||||||
|
withx( x => 1, y => 2 );
|
||||||
|
is( \%was_rest, { y => 2 }, 'other params still visible in %rest' );
|
||||||
|
|
||||||
|
my $LINE = __LINE__+1;
|
||||||
|
like( dies { withx() },
|
||||||
|
qr/^Missing argument 'x' for subroutine main::withx at \S+ line $LINE\./,
|
||||||
|
'complaint from missing named param' );
|
||||||
|
|
||||||
|
nfunc with2x(:$x1, :$x2) { return "x1=$x1 x2=$x2"; }
|
||||||
|
is( with2x( x1 => 10, x2 => 20 ), "x1=10 x2=20",
|
||||||
|
'supports multiple named params' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# named params can still have defaults
|
||||||
|
{
|
||||||
|
nfunc withy(:$y = "Y", %rest) { return $y }
|
||||||
|
|
||||||
|
is( withy( y => 456 ), 456, 'named param with default' );
|
||||||
|
is( withy(), "Y", 'named param applies default' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# named params still work without a slurpy
|
||||||
|
{
|
||||||
|
nfunc withz(:$z) { return $z }
|
||||||
|
|
||||||
|
is( withz( z => 789 ), 789, 'named param without slurpy' );
|
||||||
|
|
||||||
|
my $LINE = __LINE__+1;
|
||||||
|
like( dies { withz( z => 1, w => 1 ); 1 },
|
||||||
|
qr/^Unrecognised argument 'w' for subroutine main::withz at \S+ line $LINE\./,
|
||||||
|
'complaint from unknown param' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# mixed positional+named
|
||||||
|
{
|
||||||
|
nfunc withboth($x, :$y = "def") { return "x=$x y=$y"; }
|
||||||
|
|
||||||
|
is( withboth(1, y => 2), "x=1 y=2",
|
||||||
|
'supports mixed positional + named' );
|
||||||
|
is( withboth(1), "x=1 y=def",
|
||||||
|
'mixed still applies defaults' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# diagnostics on duplicates
|
||||||
|
{
|
||||||
|
sub warnings_from ( $code ) {
|
||||||
|
my $warnings = "";
|
||||||
|
local $SIG{__WARN__} = sub { $warnings .= $_[0] };
|
||||||
|
eval( "$code; 1" ) or die $@;
|
||||||
|
return $warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
like( warnings_from( 'nfunc diag1($x, :$x) { }' ),
|
||||||
|
qr/^"my" variable \$x masks earlier declaration in same scope at /,
|
||||||
|
'warning from duplicated parameter name' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
}
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
no warnings 'experimental';
|
||||||
|
|
||||||
|
use lib "t";
|
||||||
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/afunc"}++ }
|
||||||
|
BEGIN { $^H{"t::func/Attribute"}++ }
|
||||||
|
|
||||||
|
our @ATTRIBUTE_APPLIED;
|
||||||
|
|
||||||
|
{
|
||||||
|
afunc withattr($x :Attribute, $y :Attribute(Value)) { }
|
||||||
|
|
||||||
|
is( \@ATTRIBUTE_APPLIED,
|
||||||
|
[ '$x' => undef, '$y' => "Value" ],
|
||||||
|
':Attribute applied to subroutine parameters' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
@ -14,7 +14,7 @@ sub stages { return $_[0] }
|
||||||
{
|
{
|
||||||
my $ret = stages { one => "one" };
|
my $ret = stages { one => "one" };
|
||||||
|
|
||||||
is_deeply( $ret, { one => "one" },
|
is( $ret, { one => "one" },
|
||||||
'not permitted keyword falls through to regular symbol lookup' );
|
'not permitted keyword falls through to regular symbol lookup' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::stages";
|
use testcase "t::stages";
|
||||||
|
@ -16,7 +16,7 @@ BEGIN { $^H{"t::stages/permit"} = 1; }
|
||||||
|
|
||||||
stages withattr :atname(atvalue) { }
|
stages withattr :atname(atvalue) { }
|
||||||
|
|
||||||
is_deeply( $t::stages::captured, [ atname => "atvalue" ],
|
is( $t::stages::captured, [ atname => "atvalue" ],
|
||||||
'captured an attribute by attr filter' );
|
'captured an attribute by attr filter' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::registrations";
|
use testcase "t::registrations";
|
||||||
|
|
|
@ -3,11 +3,13 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::any";
|
use testcase "t::any";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::any/func"}++ }
|
||||||
|
|
||||||
our $LOG; BEGIN { $LOG = "" };
|
our $LOG; BEGIN { $LOG = "" };
|
||||||
|
|
||||||
prefixed func example {
|
prefixed func example {
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::prefix";
|
use testcase "t::prefix";
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
$^H{"t::prefix/func"}++;
|
||||||
|
$^H{"t::prefix/prefixed"}++;
|
||||||
|
}
|
||||||
|
|
||||||
our $LOG; BEGIN { $LOG = "" };
|
our $LOG; BEGIN { $LOG = "" };
|
||||||
|
|
||||||
prefixed func example {
|
prefixed func example {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::parts";
|
use testcase "t::parts";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::parts";
|
use testcase "t::parts";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
BEGIN {
|
BEGIN {
|
||||||
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
use testcase "t::flags";
|
use testcase "t::flags";
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
use Sub::Util 'subname';
|
use Sub::Util 'subname';
|
||||||
|
|
||||||
use lib "t";
|
use lib "t";
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
}
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
no warnings 'experimental';
|
||||||
|
|
||||||
|
use Sublike::Extended;
|
||||||
|
|
||||||
|
# basic sub
|
||||||
|
{
|
||||||
|
extended sub t1 { return "named" }
|
||||||
|
my $t2 = extended sub { return "anon" };
|
||||||
|
|
||||||
|
is( t1(), "named",
|
||||||
|
'extended sub can make basic named' );
|
||||||
|
is( $t2->(), "anon",
|
||||||
|
'extended sub can make basic anon' );
|
||||||
|
}
|
||||||
|
|
||||||
|
# sub with named params
|
||||||
|
{
|
||||||
|
extended sub t3 (:$x, :$y) { return "x=$x y=$y"; }
|
||||||
|
my $t4 = extended sub ( :$i, :$j ) { return "i=$i j=$j"; };
|
||||||
|
|
||||||
|
is( t3( x => 10, y => 20 ), "x=10 y=20",
|
||||||
|
'extended sub can make named param subs' );
|
||||||
|
is( $t4->( j => 40, i => 30 ), "i=30 j=40",
|
||||||
|
'extended sub can make named param anon subs' );
|
||||||
|
}
|
||||||
|
|
||||||
|
use lib "t";
|
||||||
|
use testcase "t::func";
|
||||||
|
|
||||||
|
BEGIN { $^H{"t::func/Attribute"}++ }
|
||||||
|
|
||||||
|
our @ATTRIBUTE_APPLIED;
|
||||||
|
|
||||||
|
{
|
||||||
|
extended sub t5 ($x :Attribute, $y :Attribute(Value)) { }
|
||||||
|
|
||||||
|
# No need to call it just to see these
|
||||||
|
|
||||||
|
is( \@ATTRIBUTE_APPLIED,
|
||||||
|
[ '$x' => undef, '$y' => "Value" ],
|
||||||
|
':Attribute applied to subroutine parameters' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
|
||||||
|
eval { require Object::Pad;
|
||||||
|
Object::Pad->VERSION( '0.800' );
|
||||||
|
1; } or
|
||||||
|
plan skip_all => "No Object::Pad";
|
||||||
|
|
||||||
|
Object::Pad->import;
|
||||||
|
}
|
||||||
|
|
||||||
|
use Sublike::Extended;
|
||||||
|
|
||||||
|
# extended method
|
||||||
|
{
|
||||||
|
class C1 {
|
||||||
|
extended method f (:$x, :$y) { return "x=$x y=$y" }
|
||||||
|
}
|
||||||
|
|
||||||
|
is( C1->new->f( x => "first", y => "second" ), "x=first y=second",
|
||||||
|
'async method' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
|
||||||
|
eval { require Future::AsyncAwait;
|
||||||
|
Future::AsyncAwait->VERSION( '0.66' );
|
||||||
|
1; } or
|
||||||
|
plan skip_all => "No Future::AsyncAwait";
|
||||||
|
|
||||||
|
Future::AsyncAwait->import;
|
||||||
|
}
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
no warnings 'experimental';
|
||||||
|
|
||||||
|
use Sublike::Extended;
|
||||||
|
|
||||||
|
# async extended sub
|
||||||
|
{
|
||||||
|
async extended sub f (:$x, :$y) { return "x=$x y=$y" }
|
||||||
|
|
||||||
|
is( await f( x => "first", y => "second" ), "x=first y=second",
|
||||||
|
'async extended sub' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
BEGIN {
|
||||||
|
$] >= 5.026000 or plan skip_all => "No parse_subsignature()";
|
||||||
|
|
||||||
|
# version 5.37.10 added the ability to start_subparse() with CVf_IsMETHOD,
|
||||||
|
# which we need
|
||||||
|
plan skip_all => "feature 'class' is not available"
|
||||||
|
unless $^V ge v5.37.10;
|
||||||
|
}
|
||||||
|
|
||||||
|
use Sublike::Extended;
|
||||||
|
|
||||||
|
use feature 'class';
|
||||||
|
no warnings 'experimental::class';
|
||||||
|
|
||||||
|
# extended method
|
||||||
|
{
|
||||||
|
class C1 {
|
||||||
|
extended method f (:$x, :$y) { return "x=$x y=$y" }
|
||||||
|
}
|
||||||
|
|
||||||
|
is( C1->new->f( x => "first", y => "second" ), "x=first y=second",
|
||||||
|
'async method' );
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing;
|
|
@ -3,7 +3,7 @@
|
||||||
use v5.14;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Test::More;
|
use Test2::V0;
|
||||||
|
|
||||||
eval "use Test::Pod 1.00";
|
eval "use Test::Pod 1.00";
|
||||||
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
|
plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
|
||||||
|
|
3
t/any.xs
3
t/any.xs
|
@ -1,7 +1,7 @@
|
||||||
/* You may distribute under the terms of either the GNU General Public License
|
/* You may distribute under the terms of either the GNU General Public License
|
||||||
* or the Artistic License (the same terms as Perl itself)
|
* or the Artistic License (the same terms as Perl itself)
|
||||||
*
|
*
|
||||||
* (C) Paul Evans, 2020 -- leonerd@leonerd.org.uk
|
* (C) Paul Evans, 2020-2023 -- leonerd@leonerd.org.uk
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EXTERN.h"
|
#include "EXTERN.h"
|
||||||
|
@ -39,6 +39,7 @@ static void func_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *_logs
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct XSParseSublikeHooks parse_func_hooks = {
|
static const struct XSParseSublikeHooks parse_func_hooks = {
|
||||||
|
.permit_hintkey = "t::any/func",
|
||||||
.pre_subparse = func_pre_subparse,
|
.pre_subparse = func_pre_subparse,
|
||||||
.post_blockstart = func_post_blockstart,
|
.post_blockstart = func_post_blockstart,
|
||||||
.pre_blockend = func_pre_blockend,
|
.pre_blockend = func_pre_blockend,
|
||||||
|
|
43
t/func.xs
43
t/func.xs
|
@ -1,7 +1,7 @@
|
||||||
/* You may distribute under the terms of either the GNU General Public License
|
/* You may distribute under the terms of either the GNU General Public License
|
||||||
* or the Artistic License (the same terms as Perl itself)
|
* or the Artistic License (the same terms as Perl itself)
|
||||||
*
|
*
|
||||||
* (C) Paul Evans, 2020 -- leonerd@leonerd.org.uk
|
* (C) Paul Evans, 2020-2023 -- leonerd@leonerd.org.uk
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "EXTERN.h"
|
#include "EXTERN.h"
|
||||||
|
@ -10,13 +10,52 @@
|
||||||
|
|
||||||
#include "XSParseSublike.h"
|
#include "XSParseSublike.h"
|
||||||
|
|
||||||
|
#if PERL_REVISION > 5 || (PERL_REVISION == 5 && PERL_VERSION >= 26)
|
||||||
|
# define HAVE_SUB_PARAM_ATTRIBUTES
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct XSParseSublikeHooks parse_func_hooks = {
|
static const struct XSParseSublikeHooks parse_func_hooks = {
|
||||||
/* empty */
|
.permit_hintkey = "t::func/func",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct XSParseSublikeHooks parse_nfunc_hooks = {
|
||||||
|
.permit_hintkey = "t::func/nfunc",
|
||||||
|
.flags = XS_PARSE_SUBLIKE_FLAG_SIGNATURE_NAMED_PARAMS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct XSParseSublikeHooks parse_afunc_hooks = {
|
||||||
|
.permit_hintkey = "t::func/afunc",
|
||||||
|
.flags = XS_PARSE_SUBLIKE_FLAG_SIGNATURE_PARAM_ATTRIBUTES,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_SUB_PARAM_ATTRIBUTES
|
||||||
|
static void apply_Attribute(pTHX_ struct XPSSignatureParamContext *ctx, SV *attrvalue, void **attrdata_ptr, void *funcdata)
|
||||||
|
{
|
||||||
|
/* TODO: maybe the context should store a lexname string? */
|
||||||
|
PADNAME *pn = PadnamelistARRAY(PL_comppad_name)[ctx->padix];
|
||||||
|
|
||||||
|
AV *av = get_av("main::ATTRIBUTE_APPLIED", GV_ADD);
|
||||||
|
|
||||||
|
av_push(av, newSVsv(PadnameSV(pn)));
|
||||||
|
av_push(av, newSVsv(attrvalue));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct XPSSignatureAttributeFuncs attr_funcs = {
|
||||||
|
.ver = XSPARSESUBLIKE_ABI_VERSION,
|
||||||
|
.permit_hintkey = "t::func/Attribute",
|
||||||
|
|
||||||
|
.apply = apply_Attribute,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
MODULE = t::func PACKAGE = t::func
|
MODULE = t::func PACKAGE = t::func
|
||||||
|
|
||||||
BOOT:
|
BOOT:
|
||||||
boot_xs_parse_sublike(0);
|
boot_xs_parse_sublike(0);
|
||||||
|
|
||||||
register_xs_parse_sublike("func", &parse_func_hooks, NULL);
|
register_xs_parse_sublike("func", &parse_func_hooks, NULL);
|
||||||
|
register_xs_parse_sublike("nfunc", &parse_nfunc_hooks, NULL);
|
||||||
|
register_xs_parse_sublike("afunc", &parse_afunc_hooks, NULL);
|
||||||
|
#ifdef HAVE_SUB_PARAM_ATTRIBUTES
|
||||||
|
register_xps_signature_attribute("Attribute", &attr_funcs, NULL);
|
||||||
|
#endif
|
||||||
|
|
|
@ -35,6 +35,7 @@ static void func_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *_logs
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct XSParseSublikeHooks parse_func_hooks = {
|
static const struct XSParseSublikeHooks parse_func_hooks = {
|
||||||
|
.permit_hintkey = "t::prefix/func",
|
||||||
.pre_subparse = func_pre_subparse,
|
.pre_subparse = func_pre_subparse,
|
||||||
.post_blockstart = func_post_blockstart,
|
.post_blockstart = func_post_blockstart,
|
||||||
.pre_blockend = func_pre_blockend,
|
.pre_blockend = func_pre_blockend,
|
||||||
|
@ -67,6 +68,7 @@ static void prefixed_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *_
|
||||||
|
|
||||||
static const struct XSParseSublikeHooks parse_prefixed_hooks = {
|
static const struct XSParseSublikeHooks parse_prefixed_hooks = {
|
||||||
.flags = XS_PARSE_SUBLIKE_FLAG_PREFIX,
|
.flags = XS_PARSE_SUBLIKE_FLAG_PREFIX,
|
||||||
|
.permit_hintkey = "t::prefix/prefixed",
|
||||||
|
|
||||||
.pre_subparse = prefixed_pre_subparse,
|
.pre_subparse = prefixed_pre_subparse,
|
||||||
.post_blockstart = prefixed_post_blockstart,
|
.post_blockstart = prefixed_post_blockstart,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package testcase;
|
package testcase;
|
||||||
|
|
||||||
use strict;
|
use v5.14;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use lib "t/blib", "t/blib/arch";
|
use lib "t/blib", "t/blib/arch";
|
||||||
|
|
Loading…
Reference in New Issue