Skip to content

Commit

Permalink
Implement default arguments in RakuAST
Browse files Browse the repository at this point in the history
This is the first consumer of the expresison thunking support: since we
need to install the default value producer into the Signature object for
non-trivial cases, we need to thunk it. Much of the infrastructure for
building that thunk is factored out, so hopefully dealing with further
such situations will be relatively straightforward (some more than
others).
  • Loading branch information
jnthn committed Jun 14, 2021
1 parent 0d51fa5 commit 8a931d1
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 50 deletions.
13 changes: 9 additions & 4 deletions src/Raku/Actions.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,8 +1349,8 @@ class Raku::Actions is HLL::Actions does Raku::CommonActions {
}
if $<quant> {
my str $q := ~$<quant>;
$parameter.set-optional(1) if $q eq '?';
$parameter.set-optional(0) if $q eq '!';
$parameter.set-optional() if $q eq '?';
$parameter.set-required() if $q eq '!';
$parameter.set-slurpy(self.r('Parameter', 'Slurpy', 'Flattened'))
if $q eq '*';
$parameter.set-slurpy(self.r('Parameter', 'Slurpy', 'Unflattened'))
Expand All @@ -1363,6 +1363,9 @@ class Raku::Actions is HLL::Actions does Raku::CommonActions {
for $<trait> {
$parameter.add-trait($_.ast);
}
if $<default_value> {
$parameter.set-default($<default_value>.ast);
}
self.attach: $/, $parameter;
}

Expand Down Expand Up @@ -1401,20 +1404,22 @@ class Raku::Actions is HLL::Actions does Raku::CommonActions {
}
else {
$parameter := $<param_var>.ast;
$parameter.set-optional(1);
}
$parameter.add-name(~$<name>);
}
else {
# Name comes from the parameter variable.
$parameter := $<param_var>.ast;
$parameter.set-optional(1);
my $name-match := $<param_var><name>;
$parameter.add-name($name-match ?? ~$name-match !! '');
}
self.attach: $/, $parameter;
}

method default_value($/) {
make $<EXPR>.ast;
}

method type_constraint($/) {
self.attach: $/, $<typename>.ast;
}
Expand Down
14 changes: 14 additions & 0 deletions src/Raku/Grammar.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -2052,6 +2052,15 @@ grammar Raku::Grammar is HLL::Grammar does Raku::Common {
]
<.ws>
<trait>*
[
<default_value>
[ <modifier=.trait> {
self.typed_panic: "X::Parameter::AfterDefault", type => "trait", modifier => $<modifier>, default => $<default_value>
}]?
# [ <modifier=.post_constraint('param')> {
# self.typed_panic: "X::Parameter::AfterDefault", type => "post constraint", modifier => $<modifier>, default => $<default_value>
# }]?
]?
}
token param_var {
Expand Down Expand Up @@ -2099,6 +2108,11 @@ grammar Raku::Grammar is HLL::Grammar does Raku::Common {
]
}
rule default_value {
:my $*IN_DECL := '';
'=' <EXPR('i=')>
}
token type_constraint {
:my $*IN_DECL := '';
[
Expand Down
80 changes: 80 additions & 0 deletions src/Raku/ast/code.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,86 @@ class RakuAST::Code is RakuAST::Node {
method signature() { Nil }
}

# The base of all expression thunks, which produce a code object of some kind
# that wraps the thunk.
class RakuAST::ExpressionThunk is RakuAST::Code is RakuAST::Meta {
has RakuAST::ExpressionThunk $.next;
has RakuAST::Signature $!signature;

method set-next(RakuAST::ExpressionThunk $next) {
nqp::bindattr(self, RakuAST::ExpressionThunk, '$!next', $next);
Nil
}

method thunk-kind() {
self.HOW.name(self)
}

# Called to produce the QAST::Block for the thunk, which should be pushed
# into the passed `$target`. If there is a next thunk in `$!next` then it
# should be compiled recursively and the expression passed along; otherwise,
# the expression itself should be compiled and used as the body.
method IMPL-THUNK-CODE-QAST(RakuAST::IMPL::QASTContext $context, Mu $target,
RakuAST::Expression $expression) {
# From the block, compiling the signature.
my $signature := self.IMPL-GET-OR-PRODUCE-SIGNATURE;
my $block := QAST::Block.new(
:blocktype('declaration_static'),
QAST::Stmts.new(
$signature.IMPL-TO-QAST($context)
));
$block.arity($signature.arity);

# If there's an inner thunk the body evaluates to that.
if $!next {
$!next.IMPL-THUNK-CODE-QAST($context, $block[0], $expression);
$block.push($!next.IMPL-THUNK-VALUE-QAST($context));
}

# Otherwise, we evaluate to the expression.
else {
$block.push($expression.IMPL-EXPR-QAST($context));
}

# Link and push the produced code block.
self.IMPL-LINK-META-OBJECT($context, $block);
$target.push($block);
}

# Produces a Code object that corresponds to the thunk.
method IMPL-THUNK-VALUE-QAST(RakuAST::IMPL::QASTContext $context) {
self.IMPL-CLOSURE-QAST($context)
}

# The type of code object produced. Defaults to Code; override to produce
# something else.
method IMPL-THUNK-OBJECT-TYPE() { Code }

# The signature for the code object produced. Defaults to the empty
# signature; override to produce something else
method IMPL-THUNK-SIGNATURE() {
RakuAST::Signature.new
}

# A callback for when the thunk meta-object is produced, potentially to
# update some other meta-object that wants to reference it.
method IMPL-THUNK-META-OBJECT-PRODUCED(Mu $meta) {
}

method IMPL-GET-OR-PRODUCE-SIGNATURE() {
$!signature // nqp::bindattr(self, RakuAST::ExpressionThunk, '$!signature',
self.IMPL-THUNK-SIGNATURE)
}

method PRODUCE-META-OBJECT() {
my $code := nqp::create(self.IMPL-THUNK-OBJECT-TYPE);
my $signature := self.IMPL-GET-OR-PRODUCE-SIGNATURE;
nqp::bindattr($code, Code, '$!signature', $signature.meta-object);
self.IMPL-THUNK-META-OBJECT-PRODUCED($code);
$code
}
}

# A code object that can have placeholder parameters.
class RakuAST::PlaceholderParameterOwner is RakuAST::Node {
# Any placeholder parameters that have been attached
Expand Down
31 changes: 1 addition & 30 deletions src/Raku/ast/expressions.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class RakuAST::Expression is RakuAST::Node {

method IMPL-QAST-ADD-THUNK-DECL-CODE(RakuAST::IMPL::QASTContext $context, Mu $target) {
if $!thunks {
$!thunks.IMPL-THUNK-CODE-QAST($context, $target, $!thunks.next, self);
$!thunks.IMPL-THUNK-CODE-QAST($context, $target, self);
}
}

Expand All @@ -43,35 +43,6 @@ class RakuAST::Expression is RakuAST::Node {
}
}

# The base of all expression thunks.
class RakuAST::ExpressionThunk is RakuAST::Node {
has RakuAST::ExpressionThunk $.next;

method set-next(RakuAST::ExpressionThunk $next) {
nqp::bindattr(self, RakuAST::ExpressionThunk, '$!next', $next);
Nil
}

method thunk-kind() {
self.HOW.name(self)
}

# Called to produce the QAST::Block for the thunk, which should be pushed
# into the passed `$target`. If there is an immediate inner thunk, then the
# `$inner-thunk` argument will be a defined value, and should be handled
# recursively. Otherwise, the expression itself should be compiled and used
# as the body.
method IMPL-THUNK-CODE-QAST(RakuAST::IMPL::QASTContext $context, Mu $target,
RakuAST::ExpressionThunk $inner-thunk, RakuAST::Expression $expression) {
nqp::die('Missing IMPL-THUNK-CODE-QAST method on ' ~ self.HOW.name(self))
}

# Produces a Code object that corresponds to the thunk.
method IMPL-THUNK-VALUE-QAST(RakuAST::IMPL::QASTContext $context) {
nqp::die('Missing IMPL-THUNK-VALUE-QAST method on ' ~ self.HOW.name(self))
}
}

# Everything that is termish (a term with prefixes or postfixes applied).
class RakuAST::Termish is RakuAST::Expression is RakuAST::CaptureSource {
}
Expand Down
Loading

0 comments on commit 8a931d1

Please sign in to comment.