Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

experimental: implement expressions as well as statements #3

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ that's currently being compiled.

- `Keyword::Simple::define`

Takes two arguments, the name of a keyword and a coderef. Injects the keyword
Takes three arguments, the name of a keyword, a coderef, and a boolean flag if
the result of the keyword handler is an expression. Injects the keyword
in the lexical scope currently being compiled. For every occurrence of the
keyword, your coderef will be called with one argument: A reference to a scalar
holding the rest of the source code (following the keyword).
Expand All @@ -60,7 +61,7 @@ that's currently being compiled.
This module depends on the [pluggable keyword](https://metacpan.org/pod/perlapi.html#PL_keyword_plugin)
API introduced in perl 5.12. Older versions of perl are not supported.

Every new keyword is actually a complete statement by itself. The parsing magic
Every new keyword is actually a complete statement or an expression by itself. The parsing magic
only happens afterwards. This means that e.g. the code in the ["SYNOPSIS"](#synopsis)
actually does this:

Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: perl
install: cpanm --quiet --installdeps --with-develop --notest .
perl:
- "5.12"
- "5.14"
- "5.16"
- "5.18"
Expand Down
2 changes: 1 addition & 1 deletion Makefile_PL_settings.plx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ return {
NAME => 'Keyword::Simple',
AUTHOR => q{Lukas Mai <[email protected]>},

MIN_PERL_VERSION => '5.12.0',
MIN_PERL_VERSION => '5.14.0',
CONFIGURE_REQUIRES => {},
BUILD_REQUIRES => {},
TEST_REQUIRES => {
Expand Down
33 changes: 27 additions & 6 deletions Simple.xs
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ WARNINGS_ENABLE

static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);

static SV *kw_handler(pTHX_ const char *kw_ptr, STRLEN kw_len) {
static SV *kw_handler(pTHX_ const char *kw_ptr, STRLEN kw_len, int * is_expr) {
HV *hints;
SV **psv, *sv, *sv2;
AV *av;
I32 kw_xlen;


Expand Down Expand Up @@ -152,10 +153,24 @@ static SV *kw_handler(pTHX_ const char *kw_ptr, STRLEN kw_len) {
}

sv = *psv;
if (!(SvROK(sv) && (sv2 = SvRV(sv), SvTYPE(sv2) == SVt_PVCV))) {
croak("%s: internal error: $^H{'%s'}{'%.*s'} not a coderef: %"SVf, MY_PKG, HINTK_KEYWORDS, (int)kw_len, kw_ptr, SVfARG(sv));
if (!(SvROK(sv) && (av = (AV*)SvRV(sv), SvTYPE((SV*)av) == SVt_PVAV))) {
croak("%s: internal error: $^H{'%s'}{'%.*s'} not an arrayref: %"SVf, MY_PKG, HINTK_KEYWORDS, (int)kw_len, kw_ptr, SVfARG(sv));
}

if (av_len(av) != 1) {
croak("%s: internal error: $^H{'%s'}{'%.*s'} bad arrayref: %"SVf, MY_PKG, HINTK_KEYWORDS, (int)kw_len, kw_ptr, SVfARG(sv));
}

if ( !( psv = av_fetch(av, 0, 0))) {
croak("%s: internal error: $^H{'%s'}{'%.*s'} bad item #0: %"SVf, MY_PKG, HINTK_KEYWORDS, (int)kw_len, kw_ptr, SVfARG(sv));
}
sv2 = *psv;

if ( !( psv = av_fetch(av, 1, 0))) {
croak("%s: internal error: $^H{'%s'}{'%.*s'} bad item #1: %"SVf, MY_PKG, HINTK_KEYWORDS, (int)kw_len, kw_ptr, SVfARG(sv));
}
*is_expr = SvIV(*psv);

return sv2;
}

Expand Down Expand Up @@ -240,11 +255,17 @@ static void total_recall(pTHX_ SV *cb) {

static int my_keyword_plugin(pTHX_ char *keyword_ptr, STRLEN keyword_len, OP **op_ptr) {
SV *cb;
int is_expr;

if ((cb = kw_handler(aTHX_ keyword_ptr, keyword_len))) {
if ((cb = kw_handler(aTHX_ keyword_ptr, keyword_len, &is_expr))) {
total_recall(aTHX_ cb);
*op_ptr = newOP(OP_NULL, 0);
return KEYWORD_PLUGIN_STMT;
if ( is_expr ) {
*op_ptr = parse_fullexpr(0);
return KEYWORD_PLUGIN_EXPR;
} else {
*op_ptr = newOP(OP_NULL, 0);
return KEYWORD_PLUGIN_STMT;
}
}

return next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
Expand Down
15 changes: 9 additions & 6 deletions lib/Keyword/Simple.pm
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package Keyword::Simple;

use v5.12.0;
use v5.14.0;
use warnings;
our %kw;

use Carp qw(croak);

Expand All @@ -12,12 +13,12 @@ BEGIN {
}

sub define {
my ($kw, $sub) = @_;
my ($kw, $sub, $expression) = @_;
$kw =~ /^\p{XIDS}\p{XIDC}*\z/ or croak "'$kw' doesn't look like an identifier";
ref($sub) eq 'CODE' or croak "'$sub' doesn't look like a coderef";

my %keywords = %{$^H{+HINTK_KEYWORDS} // {}};
$keywords{$kw} = $sub;
$keywords{$kw} = [ $sub, $expression ? 1 : 0 ];
$^H{+HINTK_KEYWORDS} = \%keywords;
}

Expand Down Expand Up @@ -80,7 +81,8 @@ that's currently being compiled.

=item C<Keyword::Simple::define>

Takes two arguments, the name of a keyword and a coderef. Injects the keyword
Takes three arguments, the name of a keyword, a coderef, and a boolean flag if
the result of the keyword handler is an expression. Injects the keyword
in the lexical scope currently being compiled. For every occurrence of the
keyword, your coderef will be called with one argument: A reference to a scalar
holding the rest of the source code (following the keyword).
Expand All @@ -100,9 +102,10 @@ method to make the C<no Foo;> syntax work.
=head1 BUGS AND LIMITATIONS

This module depends on the L<pluggable keyword|perlapi.html/PL_keyword_plugin>
API introduced in perl 5.12. Older versions of perl are not supported.
API introduced in perl 5.12. C<parse_> functions were introduced in 5.14.
Older versions of perl are not supported.

Every new keyword is actually a complete statement by itself. The parsing magic
Every new keyword is actually a complete statement or an expression by itself. The parsing magic
only happens afterwards. This means that e.g. the code in the L</SYNOPSIS>
actually does this:

Expand Down
7 changes: 6 additions & 1 deletion t/basic.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use warnings FATAL => 'all';
use strict;

use Test::More tests => 2;
use Test::More tests => 3;

{
package Foo;
Expand All @@ -13,10 +13,14 @@ use Test::More tests => 2;
Keyword::Simple::define peek => sub {
substr ${$_[0]}, 0, 0, "ok 1, 'synthetic test';";
};
Keyword::Simple::define poke => sub {
substr ${$_[0]}, 0, 0, "ok 2, 'expression' + ' test';";
}, 1;
}

sub unimport {
Keyword::Simple::undefine 'peek';
Keyword::Simple::undefine 'poke';
}

BEGIN { $INC{"Foo.pm"} = 1; }
Expand All @@ -26,3 +30,4 @@ use Foo;

peek
ok 1, "natural test";
ok 2, "expression test";