Skip to content

Commit

Permalink
Implement named arguments
Browse files Browse the repository at this point in the history
Those are fairly straightforward, since they (mostly) mimic the semantics of Raku's.
The only extra effort we do is ensure at template parse time that we're not mix-matching nameds and positionals in a way that's not allowed by the underlying compiler.
  • Loading branch information
vendethiel committed Nov 15, 2019
1 parent 8463af1 commit f1ed436
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 3 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,17 @@ And then call it with arguments:
The arguments may be an expression as valid in a <?{ ... }> condition - that is,
literals, variable access, dereferences, and some basic operators are allowed.

As in Perl 6, you can have named - optional - arguments as well:

```
<:sub haz(:$name)>
I can haz <$name>!
</:>
<&haz(:name('named arguments'))>
```


A template macro works somewhat like a template subroutine, except that the usage
of it has a body. This body is passed as a thunk, meaning that the macro can choose
to render it 0 or more times), optionally setting a new default target. For example,
Expand Down
20 changes: 19 additions & 1 deletion lib/Cro/WebApp/Template/AST.pm6
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,27 @@ my class TemplateSub does ContainerNode is export {
}
}

my role Argument does Node is export {
has Node $.argument;
}

my class ByPosArgument does Argument is export {
method compile {
$.argument.compile
}
}

my class ByNameArgument does Argument is export {
has Str $.name;

method compile {
':' ~ $!name ~ '(' ~ $.argument.compile ~ ')'
}
}

my class Call does Node is export {
has Str $.target is required;
has Node @.arguments;
has Argument @.arguments;

method compile() {
'__TEMPLATE__' ~ $!target ~ '(' ~ @!arguments.map(*.compile).join(", ") ~ ')'
Expand Down
10 changes: 9 additions & 1 deletion lib/Cro/WebApp/Template/ASTBuilder.pm6
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,15 @@ class Cro::WebApp::Template::ASTBuilder {
}

method arglist($/) {
make $<argument>.map(*.ast).list;
make $<arg>.map(*.ast);
}

method arg:by-pos ($/) {
make ByPosArgument.new(argument => $<expression>.ast);
}

method arg:by-name ($/) {
make ByNameArgument.new(name => ~$<identifier>, argument => $<expression>.ast);
}

method term:sym<single-quote-string>($/) {
Expand Down
17 changes: 16 additions & 1 deletion lib/Cro/WebApp/Template/Parser.pm6
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,30 @@ grammar Cro::WebApp::Template::Parser {
}

token signature {
:my $*seen-by-name-arguments = False;
'(' \s* <parameter>* % [\s* ',' \s*] \s* ')' \h*
}

token parameter {
[
|| ':' { $*seen-by-name-arguments = True; }
|| <?{ $*seen-by-name-arguments }> <.panic('Positional argument after named argument')>
]?
'$' <.identifier>
}

token arglist {
'(' \s* <argument=.expression>* % [\s* ',' \s*] \s* ')' \h*
'(' \s* <arg>* % [\s* ',' \s*] \s* ')' \h*
}

proto token arg { * }

token arg:by-pos { <expression> }

token arg:by-name {
':' <identifier>
'(' ~ ')'
<expression>
}

rule expression {
Expand Down
3 changes: 3 additions & 0 deletions t/error-data/sub-pos-after-pos-named.crotmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<:sub foo(:$named, $pos)>
<$named> - <$pos>
</:>
13 changes: 13 additions & 0 deletions t/template-basic.t
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use Cro::WebApp::Template;
use Test;

my constant $base = $*PROGRAM.parent.add('test-data');
my constant $error-base = $*PROGRAM.parent.add('error-data');

is render-template($base.add('literal.crotmp'), {}), q:to/EXPECTED/, 'Literal text passed through';
<div>
Expand Down Expand Up @@ -156,6 +157,18 @@ is norm-ws(render-template($base.add('sub-3.crotmp'), { t => 'b' })),
bs and bbb
EXPECTED

is norm-ws(render-template($base.add('sub-4.crotmp'), { t => 'b' })),
norm-ws(q:to/EXPECTED/), 'Subs can have named arguments';
this - is
43 - 30
bs - bbb
Both notnamed and named
EXPECTED

throws-like { render-template($error-base.add('sub-pos-after-pos-named.crotmp'), {}) },
X::Cro::WebApp::Template::SyntaxError,
'Positional argument after named argument at line 1 near \'$a)>';

is norm-ws(render-template($base.add('macro-1.crotmp'), { foo => 'xxx', bar => 'yyy' })),
norm-ws(q:to/EXPECTED/), 'Basic no-argument macro works';
<ul>
Expand Down
11 changes: 11 additions & 0 deletions t/test-data/sub-4.crotmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<:sub foo(:$first, :$second)>
<$first> - <$second>
</:>
<:sub other($a, :$b)>
Both <$a> and <$b>
</:>
<&foo(:first('this'), :second('is'))>
<&foo(:first(1 + 42), :second(2 * (3 * 5)))>
<&foo(:first(.t ~ 's'), :second(.t x 3))>

<&other('notnamed', :b('named'))>

0 comments on commit f1ed436

Please sign in to comment.