Skip to content

Commit

Permalink
- Improved the given/when construct
Browse files Browse the repository at this point in the history
It will now include a `case` statement for testing expressions for trueness.

Example:
	given(42) {
		when(value) { }		# does 42 ~~ value
		case(expr)		# will test the expression in boolean context
	}
  • Loading branch information
trizen committed Dec 2, 2015
1 parent ade5b55 commit e9f70c7
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 29 deletions.
2 changes: 2 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ lib/Sidef/Types/Array/Pair.pm
lib/Sidef/Types/Array/Pair.pod
lib/Sidef/Types/Black/Hole.pm
lib/Sidef/Types/Block/Break.pm
lib/Sidef/Types/Block/Case.pm
lib/Sidef/Types/Block/Code.pm
lib/Sidef/Types/Block/Code.pod
lib/Sidef/Types/Block/CodeInit.pm
Expand Down Expand Up @@ -642,6 +643,7 @@ scripts/Tests/functional_style.sf
scripts/Tests/gather_take.sf
scripts/Tests/gcd.sf
scripts/Tests/getopt.sf
scripts/Tests/given_when.sf
scripts/Tests/greatest_subsequential_sum.sf
scripts/Tests/group_precedence.sf
scripts/Tests/hailstone.sf
Expand Down
3 changes: 3 additions & 0 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@
"Sidef::Types::Block::Break" : {
"file" : "lib/Sidef/Types/Block/Break.pm"
},
"Sidef::Types::Block::Case" : {
"file" : "lib/Sidef/Types/Block/Case.pm"
},
"Sidef::Types::Block::Code" : {
"file" : "lib/Sidef/Types/Block/Code.pm"
},
Expand Down
2 changes: 2 additions & 0 deletions META.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ provides:
file: lib/Sidef/Types/Black/Hole.pm
Sidef::Types::Block::Break:
file: lib/Sidef/Types/Block/Break.pm
Sidef::Types::Block::Case:
file: lib/Sidef/Types/Block/Case.pm
Sidef::Types::Block::Code:
file: lib/Sidef/Types/Block/Code.pm
Sidef::Types::Block::CodeInit:
Expand Down
19 changes: 12 additions & 7 deletions lib/Sidef/Deparse/Perl.pm
Original file line number Diff line number Diff line change
Expand Up @@ -948,16 +948,20 @@ HEADER
}
}
elsif ($ref eq 'Sidef::Types::Block::Do') {
$code = 'do {' . join(';', $self->deparse_script($obj->{block}{code})) . '}';
$code = 'do ' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Loop') {
$code = 'while(1) {' . join(';', $self->deparse_script($obj->{block}{code})) . '}';
$code = 'while(1) ' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Given') {
$self->top_add(qq{use experimental 'smartmatch';\n});
$code = 'do{given ' . $self->deparse_args($obj->{expr}) . $self->deparse_bare_block($obj->{block}{code}) . '}';
}
elsif ($ref eq 'Sidef::Types::Block::When') {
$self->top_add(qq{use experimental 'smartmatch';\n});
$code = 'when($_ ~~ ' . $self->deparse_args($obj->{expr}) . ')' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Case') {
$code = 'when(!!' . $self->deparse_args($obj->{expr}) . ')' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Default') {
$code = 'default' . $self->deparse_bare_block($obj->{block}->{code});
Expand Down Expand Up @@ -1277,7 +1281,7 @@ HEADER
my $var = $self->deparse_args(@{$call->{arg}});

#$code = "($var=$var\->$self->{inc_dec_ops}{$method})[0]";
$code = "do{my\$ref=\\$var;\$\$ref=\$\$ref\->$self->{inc_dec_ops}{$method}}";
$code = "do{my \$ref=\\$var; \$\$ref=\$\$ref\->$self->{inc_dec_ops}{$method}}";
next;
}
}
Expand All @@ -1287,7 +1291,7 @@ HEADER

#$code = "do{my \$old=$code; $code=$code\->$self->{inc_dec_ops}{$method}; \$old}";
$code =
"do{my\$ref=\\$code;my\$value=\$\$ref;\$\$ref=\$value\->$self->{inc_dec_ops}{$method}; \$value}";
"do{my \$ref=\\$code; my \$value=\$\$ref; \$\$ref=\$value\->$self->{inc_dec_ops}{$method}; \$value}";
next;
}

Expand Down Expand Up @@ -1428,8 +1432,9 @@ HEADER
}

if (exists $call->{block}) {
if ($ref eq 'Sidef::Types::Block::Given'
or ($ref eq 'Sidef::Types::Block::If' and $i == $#{$expr->{call}})) {

# TODO: move above the deparsing of `Block::If`
if ($ref eq 'Sidef::Types::Block::If' and $i == $#{$expr->{call}}) {
$code = "do {\n" . (' ' x $Sidef::SPACES) . $code . $self->deparse_bare_block(@{$call->{block}}) . '}';
}
else {
Expand Down
17 changes: 10 additions & 7 deletions lib/Sidef/Deparse/Sidef.pm
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,15 @@ package Sidef::Deparse::Sidef {
elsif ($ref eq 'Sidef::Types::Block::Break') {
$code = 'break';
}
elsif ($ref eq 'Sidef::Types::Block::Given') {
$code = 'given ' . $self->deparse_args($obj->{expr}) . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::When') {
$code = 'when(' . $self->deparse_args($obj->{expr}) . ')' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Case') {
$code = 'case(' . $self->deparse_args($obj->{expr}) . ')' . $self->deparse_bare_block($obj->{block}{code});
}
elsif ($ref eq 'Sidef::Types::Block::Default') {
$code = 'default' . $self->deparse_bare_block($obj->{block}->{code});
}
Expand Down Expand Up @@ -664,13 +673,7 @@ package Sidef::Deparse::Sidef {
}

if (exists $call->{block}) {
if ($ref eq 'Sidef::Types::Block::Given'
or ($ref eq 'Sidef::Types::Block::If' and $i == $#{$expr->{call}})) {
$code .= $self->deparse_bare_block(@{$call->{block}});
}
else {
$code .= $self->deparse_bare_block(@{$call->{block}});
}
$code .= $self->deparse_bare_block(@{$call->{block}});
next;
}
}
Expand Down
96 changes: 90 additions & 6 deletions lib/Sidef/Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ package Sidef::Parser {
'Sidef::Types::Block::While' => 1,
'Sidef::Types::Block::If' => 1,
'Sidef::Types::Block::For' => 1,
'Sidef::Types::Block::Given' => 1,
'Sidef::Types::Block::When' => 1,
},

static_obj_re => qr{\G
Expand Down Expand Up @@ -131,8 +129,6 @@ package Sidef::Parser {
| return\b (?{ Sidef::Types::Block::Return->new })
#| next\b (?{ Sidef::Types::Block::Next->new })
#| break\b (?{ Sidef::Types::Block::Break->new })
| given\b (?{ Sidef::Types::Block::Given->new })
| when\b (?{ Sidef::Types::Block::When->new })
| (?:defined|read|say|print)\b (?{ state $x = Sidef::Sys::Sys->new })
| goto\b (?{ state $x = Sidef::Perl::Builtin->new })
| (?:[*\\&]|\+\+|--) (?{ state $x = Sidef::Variable::Ref->new })
Expand Down Expand Up @@ -228,7 +224,7 @@ package Sidef::Parser {
return
for foreach
if while
given when
given
try
continue
import
Expand Down Expand Up @@ -1544,8 +1540,96 @@ package Sidef::Parser {
return $obj;
}

# "given(expr) {...}" construct
if (/\Ggiven\b\h*/gc) {
my $expr = (
/\G(?=\()/
? $self->parse_arguments(code => $opt{code})
: $self->parse_obj(code => $opt{code})
);

$expr // $self->fatal_error(
error => "invalid declaration of the `given/when` construct",
expected => "expected `given(expr) {...}`",
code => $_,
pos => pos($_),
);

my $given_obj = Sidef::Types::Block::Given->new(expr => $expr);
local $self->{current_given} = $given_obj;
my $block = (
/\G\h*(?=\{)/gc
? $self->parse_block(code => $opt{code})
: $self->fatal_error(
error => "expected a block after `given(expr)`",
code => $_,
pos => pos($_),
)
);

$given_obj->{block} = $block;

return $given_obj;
}

# "when(expr) {...}" construct
if (exists($self->{current_given}) && /\Gwhen\b\h*/gc) {
my $expr = (
/\G(?=\()/
? $self->parse_arguments(code => $opt{code})
: $self->parse_obj(code => $opt{code})
);

$expr // $self->fatal_error(
error => "invalid declaration of the `when` construct",
expected => "expected `when(expr) {...}`",
code => $_,
pos => pos($_),
);

my $block = (
/\G\h*(?=\{)/gc
? $self->parse_block(code => $opt{code})
: $self->fatal_error(
error => "expected a block after `when(expr)`",
code => $_,
pos => pos($_),
)
);

return Sidef::Types::Block::When->new(expr => $expr, block => $block);
}

# "case(expr) {...}" construct
if (exists($self->{current_given}) && /\Gcase\b\h*/gc) {
my $expr = (
/\G(?=\()/
? $self->parse_arguments(code => $opt{code})
: $self->parse_obj(code => $opt{code})
);

$expr // $self->fatal_error(
error => "invalid declaration of the `case` construct",
expected => "expected `case(expr) {...}`",
code => $_,
pos => pos($_),
);

my $block = (
/\G\h*(?=\{)/gc
? $self->parse_block(code => $opt{code})
: $self->fatal_error(
error => "expected a block after `case(expr)`",
code => $_,
pos => pos($_),
)
);

return Sidef::Types::Block::Case->new(expr => $expr, block => $block);
}

# "default {...}" construct
if (/\Gdefault\h*(?=\{)/gc) {
if (exists($self->{current_given}) && /\Gdefault\h*(?=\{)/gc) {
my $block = $self->parse_block(code => $opt{code});
return Sidef::Types::Block::Default->new(block => $block);
}
Expand Down
8 changes: 8 additions & 0 deletions lib/Sidef/Types/Block/Case.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package Sidef::Types::Block::Case {
sub new {
my (undef, %opt) = @_;
bless \%opt, __PACKAGE__;
}
}

1;
3 changes: 2 additions & 1 deletion lib/Sidef/Types/Block/Given.pm
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package Sidef::Types::Block::Given {

sub new {
bless {}, __PACKAGE__;
my (undef, %opt) = @_;
bless \%opt, __PACKAGE__;
}
}

Expand Down
4 changes: 3 additions & 1 deletion lib/Sidef/Types/Block/When.pm
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package Sidef::Types::Block::When {

sub new {
bless {}, __PACKAGE__;
my (undef, %opt) = @_;
bless \%opt, __PACKAGE__;
}
}

Expand Down
4 changes: 2 additions & 2 deletions scripts/Games/rock-paper-scissors.sf
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ loop {
 
# compute ai choice for next play
given (var rn = (plays.rand.int)) {
when (rn < pcf[0]) { aChoice = 1 }
when (pcf[0]+pcf[1] > rn) { aChoice = 2 }
case (rn < pcf[0]) { aChoice = 1 }
case (pcf[0]+pcf[1] > rn) { aChoice = 2 }
default { aChoice = 0 }
}
}
2 changes: 1 addition & 1 deletion scripts/RosettaCode/primality_by_trial_division.sf
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
func is_prime(a) {
given (a) {
when (2) { true }
when (a <= 1 || a.is_even) { false }
case (a <= 1 || a.is_even) { false }
default { 3 ... a.sqrt -> any { .divides(a) } -> not }
}
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/RosettaCode/quickselect_algorithm.sf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func quickselect(a, k) {
 
given(var l = left.len) {
when(k) { pivot }
when(k < l) { __FUNC__(left, k) }
case(k < l) { __FUNC__(left, k) }
default { __FUNC__(right, k - l - 1) }
}
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/RosettaCode/sequence_of_primes_by_trial_division.sf
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

func is_prime(a) {
given (a) {
when (a == 2) { true }
when (a <= 1 || a.is_even) { false }
case (a == 2) { true }
case (a <= 1 || a.is_even) { false }
default { 3 .. a.sqrt -> any { a %% _ } -> not };
}
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/Tests/arithmetic_geometric_mean.sf
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func agm1(a, g) {
loop {
given (var ag = [a+g / 2, Math.sqrt(a*g)]) {
when (a, g) { return a }
default { (a, g) = ag... }
default { (a, g) = ag... }
}
}
}
Expand Down
Loading

0 comments on commit e9f70c7

Please sign in to comment.