Skip to content

Commit

Permalink
- Added support for implicit block calls.
Browse files Browse the repository at this point in the history
Example:
	f(a,b) {...};   # means: f(a, b, {...});
	f {...};        # means: f({...});

This new syntax allows the user to extend the language freely, creating new constructs using blocks, classes, methods and functions.
See: `scripts/block_implicit_call.sf` for examples.

However, this new feature comes at a cost, which is that it will always require parentheses around if/while/for conditions.

Example:
	if true   { ... };    # this used to work
	if (true) { ... };    # this works

Anyway, the parentheses make the code much easier to read and to reason about, so I think it's a good change of the syntax. It, also, makes the language more consistent.
  • Loading branch information
trizen committed Sep 20, 2015
1 parent f46fe9b commit a0c1e81
Show file tree
Hide file tree
Showing 37 changed files with 335 additions and 225 deletions.
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ scripts/best_shuffle_alg_permute.sf
scripts/binary_search.sf
scripts/binary_search_recursive.sf
scripts/block_all_and_any.sf
scripts/block_implicit_call.sf
scripts/box_the_compass.sf
scripts/box_the_compass_2.sf
scripts/break.sf
Expand Down
16 changes: 10 additions & 6 deletions lib/Sidef/Deparse/Sidef.pm
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ package Sidef::Deparse::Sidef {
elsif ($ref eq 'Sidef::Types::Array::Array' or $ref eq 'Sidef::Types::Array::HCArray') {
$code = $self->_dump_array($obj);
}
elsif ($obj->can('dump')) {
elsif ($ref =~ /^Sidef::/ and $obj->can('dump')) {
$code = $obj->dump->get_value;

if ($ref eq 'Sidef::Types::Glob::Backtick') {
Expand Down Expand Up @@ -455,17 +455,21 @@ package Sidef::Deparse::Sidef {
}

if (exists $call->{arg}) {
my $i = 0;
$code .= '(' . join(
', ',
map {
map { $_ eq '' ? '()' : $_ }
map {
++$i;
ref($_) eq 'HASH' ? $self->deparse_script($_)
: exists($self->{obj_with_block}{$ref})
: $i == 1
&& exists($self->{obj_with_block}{$ref})
&& exists($self->{obj_with_block}{$ref}{$method}) ? $self->deparse_expr({self => $_->{code}})
: $ref eq 'Sidef::Types::Block::For'
&& $#{$call->{arg}} == 2
&& ref($_) eq 'Sidef::Types::Block::Code' ? $self->deparse_expr($_->{code})
&& (($#{$call->{arg}} == 2) || $#{$call->{arg}} == 3)
&& $i <= 3 && ref($_) eq 'Sidef::Types::Block::Code' ? $self->deparse_expr($_->{code})
: ref($_) ? $self->deparse_expr({self => $_})
: Sidef::Types::String::String->new($_)->dump
: Sidef::Types::String::String->new($_)->dump->get_value
} @{$call->{arg}}
)
. ')';
Expand Down
94 changes: 58 additions & 36 deletions lib/Sidef/Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ package Sidef::Parser {
'...' => 1,
},
obj_with_do => {
'Sidef::Types::Block::For' => 1,
'Sidef::Types::Bool::While' => 1,
'Sidef::Types::Bool::If' => 1,
'Sidef::Types::Block::Given' => 1,
'Sidef::Types::Block::Given' => 1, # TODO: find a better way and remove this line
},
obj_with_block => {
'Sidef::Types::Bool::While' => 1,
Expand Down Expand Up @@ -2036,31 +2033,6 @@ package Sidef::Parser {
return $obj;
}

while (
# (ref($obj) eq 'Sidef::Variable::Variable' and ($obj->{type} eq 'func' || $obj->{type} eq 'method'))
# || (ref($obj) eq 'Sidef::Variable::ClassInit')
# || (ref($obj) eq 'Sidef::Types::Block::Code')
# and
/\G\h*(?=\()/gc
) {
my $arg = $self->parse_arguments(code => $opt{code});
$obj = {
$self->{class} => [
{
self => $obj,
call => [
{
method => ref($obj) eq 'Sidef::Variable::ClassInit'
? 'new'
: 'call',
(%{$arg} ? (arg => [$arg]) : ())
}
]
}
]
};
}

if (defined $obj) {
push @{$struct{$self->{class}}}, {self => $obj};

Expand All @@ -2085,6 +2057,15 @@ package Sidef::Parser {
my @arg = ($arg);
if (exists $self->{obj_with_block}{ref $struct{$self->{class}}[-1]{self}}
and ref($arg) eq 'HASH') {

my $while_block;
if ($#{$arg->{$self->{class}}} > 0
and ref($arg->{$self->{class}}[-1]{self}{$self->{class}}[-1]{self}) eq
'Sidef::Types::Block::Code') {
$while_block = pop(@{$arg->{$self->{class}}});
$while_block = $while_block->{self}{$self->{class}}[-1]{self};
}

my $block = Sidef::Types::Block::Code->new($arg);

if ($before != $after) {
Expand All @@ -2097,12 +2078,19 @@ package Sidef::Parser {
}
}

@arg = ($block);
@arg = ($block, defined($while_block) ? $while_block : ());
}
elsif ( ref($struct{$self->{class}}[-1]{self}) eq 'Sidef::Types::Block::For'
and ref($arg) eq 'HASH'
and $#{$arg->{$self->{class}}} == 2) {
@arg = (map { Sidef::Types::Block::Code->new($_) } @{$arg->{$self->{class}}});
and ref($arg) eq 'HASH') {
if ($#{$arg->{$self->{class}}} == 2) {
@arg = (map { Sidef::Types::Block::Code->new($_) } @{$arg->{$self->{class}}});
}
elsif ($#{$arg->{$self->{class}}} == 3
and ref($arg->{$self->{class}}[-1]{self}{$self->{class}}[-1]{self}) eq
'Sidef::Types::Block::Code') {
my $block = pop(@{$arg->{$self->{class}}})->{self};
@arg = ((map { Sidef::Types::Block::Code->new($_) } @{$arg->{$self->{class}}}), $block);
}
}

push @{$struct{$self->{class}}[-1]{call}}, {method => $method, arg => \@arg};
Expand All @@ -2119,14 +2107,15 @@ package Sidef::Parser {
push @{$struct{$self->{class}}[-1]{ind}}, $ind;
}

my $chain = 0;
my @methods;
{
if (/\G(?=\.(?:$self->{method_name_re}|[(\$]))/o) {
my $methods = $self->parse_methods(code => $opt{code});
push @{$struct{$self->{class}}[-1]{call}}, @{$methods};
}

if (/\G(?=\()/) {
if (/\G\h*(?=\()/gc) {
my $arg = $self->parse_arguments(code => $opt{code});

push @{$struct{$self->{class}}[-1]{call}},
Expand All @@ -2138,6 +2127,41 @@ package Sidef::Parser {
redo;
}

if (exists($struct{$self->{class}}[-1]{call}) and /\G\h*(?=\{)/gc) {
my $arg = $self->parse_block(code => $opt{code});
push @{$struct{$self->{class}}[-1]{call}[-1]{arg}}, $arg;
$chain = 1;
redo;
}

if ($chain) {
my $pos = pos($_);
if (not(($self->parse_whitespace(code => $opt{code}))[1]) and /\G(?=$self->{method_name_re})/o) {
my $code = q{. } . substr($_, pos($_));
my $methods = $self->parse_methods(code => \$code);
pos($_) += pos($code) - 2;
push @{$struct{$self->{class}}[-1]{call}}, @{$methods};
redo;
}
else {
$chain = 0;
pos($_) = $pos;
}
}

if (/\G\h*(?=\{)/gc) {
my $arg = $self->parse_block(code => $opt{code});

push @{$struct{$self->{class}}[-1]{call}},
{
method => 'call',
(%{$arg} ? (arg => [$arg]) : ())
};

$chain = 1;
redo;
}

if (/\G(?=\[)/) {
$struct{$self->{class}}[-1]{self} = {
$self->{class} => [
Expand All @@ -2164,7 +2188,6 @@ package Sidef::Parser {

my $has_arg;
if ($req_arg or exists $self->{binpost_ops}{$method}) {

my $lonely_obj = /\G\h*(?=\()/gc;

my $code = substr($_, pos);
Expand Down Expand Up @@ -2568,7 +2591,6 @@ package Sidef::Parser {

if (defined $arg) {
push @{$struct{$self->{class}}[-1]{call}}, {method => 'do', arg => [$arg]};

if (not(($self->parse_whitespace(code => $opt{code}))[1])
and /(?=$self->{method_name_re}|$self->{operators_re})/o) {

Expand Down
44 changes: 4 additions & 40 deletions lib/Sidef/Types/Block/Code.pm
Original file line number Diff line number Diff line change
Expand Up @@ -179,21 +179,8 @@ package Sidef::Types::Block::Code {
}

sub while {
my ($self, $condition, $old_self) = @_;

if (exists($condition->{_special_stack_vars}) and not exists($self->{_specialized})) {
$self->{_specialized} = 1;
push @{$self->{vars}}, @{$condition->{_special_stack_vars}};
}

while ($condition->run) {
defined($old_self) && ($old_self->{did_while} //= 1);
if (defined(my $res = $self->_run_code)) {
return (ref($res) eq ref($self) && defined($old_self) ? $old_self : $res);
}
}

$old_self // $self;
my ($self, $condition) = @_;
Sidef::Types::Bool::While->new->while($condition, $self);
}

sub loop {
Expand Down Expand Up @@ -426,31 +413,8 @@ package Sidef::Types::Block::Code {
*thr = \&thread;

sub for {
my ($self, $arg, @rest) = @_;

if ( $#_ == 3
and ref($_[1]) eq __PACKAGE__
and ref($_[2]) eq __PACKAGE__
and ref($_[3]) eq __PACKAGE__) {
my ($one, $two, $three) = ($_[1], $_[2], $_[3]);
for ($one->_execute_expr ; $two->_execute_expr ; $three->_execute_expr) {
if (defined(my $res = $self->_run_code)) {
return $res;
}
}
$self;
}
elsif ($#_ == 1 and $arg->can('each')) {
$arg->each($self);
}
else {
foreach my $item ($arg, @rest) {
if (defined(my $res = $self->_run_code($item))) {
return $res;
}
}
$self;
}
my ($self, @args) = @_;
Sidef::Types::Block::For->new->for(@args, $self);
}
};

Expand Down
40 changes: 27 additions & 13 deletions lib/Sidef/Types/Block/For.pm
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
package Sidef::Types::Block::For {

use 5.014;

sub new {
bless {}, __PACKAGE__;
}

sub for {
my ($self, @args) = @_;
$self->{arg} = \@args;
$self;
}

sub foreach {
my ($self, $arr) = @_;
$self->{arg} = $arr;
$self;
if ( $#args == 3
and ref($args[0]) eq 'Sidef::Types::Block::Code'
and ref($args[1]) eq 'Sidef::Types::Block::Code'
and ref($args[2]) eq 'Sidef::Types::Block::Code') {
my ($one, $two, $three) = @args[0 .. 2];
for ($one->_execute_expr ; $two->_execute_expr ; $three->_execute_expr) {
if (defined(my $res = $args[3]->_run_code)) {
return $res;
}
}
$args[-1];
}
elsif ($#args == 1 and $args[0]->can('each')) {
$args[0]->each($args[1]);
}
else {
my $block = pop @args;
foreach my $item (@args) {
if (defined(my $res = $block->_run_code($item))) {
return $res;
}
}
$block;
}
}

sub do {
my ($self, $code) = @_;
ref($self->{arg}) eq 'ARRAY'
? $code->for(@{$self->{arg}})
: $self->{arg}->each($code);
}
*foreach = \&for;
};

1
14 changes: 3 additions & 11 deletions lib/Sidef/Types/Bool/If.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,11 @@ package Sidef::Types::Bool::If {

sub if {
my ($self, @args) = @_;
$self->{do_block} = $args[-1] ? 1 : 0;
$self;
($self->{do_block} = $args[-2] ? 1 : 0) ? $self->do($args[-1]) : $self;
}

*call = \&if;

sub elsif {
my ($self, @args) = @_;
$self->{do_block} = $args[-1] ? 1 : 0;
$self;
}

*elseif = \&elsif;
*call = \&if;
*elsif = \&if;

sub else {
my ($self, $code) = @_;
Expand Down
21 changes: 14 additions & 7 deletions lib/Sidef/Types/Bool/While.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ package Sidef::Types::Bool::While {
}

sub while {
my ($self, $code) = @_;
$self->{arg} = $code;
$self;
}
my ($self, $condition, $block) = @_;

sub do {
my ($self, $code) = @_;
$code->while($self->{arg}, $self);
if (exists($condition->{_special_stack_vars}) and not exists($block->{_specialized})) {
$block->{_specialized} = 1;
push @{$block->{vars}}, @{$condition->{_special_stack_vars}};
}

while ($condition->run) {
$self->{did_while} //= 1;
if (defined(my $res = $block->_run_code)) {
return $res;
}
}

$self;
}

sub else {
Expand Down
18 changes: 9 additions & 9 deletions scripts/Expensive/fibonacci_validation.sf
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ func is_prob_fib(n) {
fib(fib_pos(n)) == n;
}

for [
[12, 144, true],
[12, 143, false],
[12, 145, false],
[13, 233, true],
[49, 1337, false],
[32, 2178309, true],
[100, 354224848179261915075, true],
] { |group|
[
[12, 144, true],
[12, 143, false],
[12, 145, false],
[13, 233, true],
[49, 1337, false],
[32, 2178309, true],
[100, 354224848179261915075, true],
].each { |group|

var(pos, num, bool) = group...;
is_fib(pos, num) == bool || die "Validation error (1)!";
Expand Down
Loading

0 comments on commit a0c1e81

Please sign in to comment.