Skip to content

Commit

Permalink
- Added support for named parameters.
Browse files Browse the repository at this point in the history
Example:
	func foo(a, b, c) {
		say a;		# prints: 1
		say b;		# prints: 2
		say c;		# prints: 3
	}
	foo(b: 2, c: 3, a: 1);

- Default values of parameters of functions and methods can now default to previous declared arguments.

Example:
	func test(a=1, b=a+2) {
		say a;			# prints: 1
		say b;			# prints: 3 (the result of a+2)
	}
	test();
  • Loading branch information
trizen committed Oct 23, 2015
1 parent 2827b5e commit 8097b21
Show file tree
Hide file tree
Showing 11 changed files with 247 additions and 42 deletions.
3 changes: 3 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ lib/Sidef/Variable/LocalMagic.pm
lib/Sidef/Variable/LocalMagic.pod
lib/Sidef/Variable/Magic.pm
lib/Sidef/Variable/Magic.pod
lib/Sidef/Variable/NamedParam.pm
lib/Sidef/Variable/Ref.pm
lib/Sidef/Variable/Static.pm
lib/Sidef/Variable/Static.pod
Expand Down Expand Up @@ -548,6 +549,7 @@ scripts/Tests/count_in_factors.sf
scripts/Tests/cpp_input_output.sf
scripts/Tests/cramers_method.sf
scripts/Tests/data_handle.sf
scripts/Tests/default_param_values.sf
scripts/Tests/default_var_values.sf
scripts/Tests/echo.sf
scripts/Tests/eq_g2.sf
Expand Down Expand Up @@ -625,6 +627,7 @@ scripts/Tests/multi_file_edit.sf
scripts/Tests/multi_var_assignment.sf
scripts/Tests/multisplit.sf
scripts/Tests/named_block_arguments.sf
scripts/Tests/named_parameters.sf
scripts/Tests/object_copy.sf
scripts/Tests/objects_init.sf
scripts/Tests/one-dimensional_cellular_automata.sf
Expand Down
3 changes: 3 additions & 0 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,9 @@
"Sidef::Variable::Magic" : {
"file" : "lib/Sidef/Variable/Magic.pm"
},
"Sidef::Variable::NamedParam" : {
"file" : "lib/Sidef/Variable/NamedParam.pm"
},
"Sidef::Variable::Ref" : {
"file" : "lib/Sidef/Variable/Ref.pm"
},
Expand Down
2 changes: 2 additions & 0 deletions META.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ provides:
file: lib/Sidef/Variable/LocalMagic.pm
Sidef::Variable::Magic:
file: lib/Sidef/Variable/Magic.pm
Sidef::Variable::NamedParam:
file: lib/Sidef/Variable/NamedParam.pm
Sidef::Variable::Ref:
file: lib/Sidef/Variable/Ref.pm
Sidef::Variable::Static:
Expand Down
44 changes: 43 additions & 1 deletion lib/Sidef/Deparse/Perl.pm
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,46 @@ HEADER
return '';
}

my $code = ' ' x $Sidef::SPACES . "my (" . join(', ', @dumped_vars) . ') = @_;' . "\n";
#my $code = (' ' x $Sidef::SPACES) . "my (" . join(', ', @dumped_vars) . ') = @_;' . "\n";
my $code = (' ' x $Sidef::SPACES) . join(
"\n" . (' ' x $Sidef::SPACES),
"my \@vars = (" . join(', ', map { $_ eq 'undef' ? $_ : "\\my $_" } @dumped_vars) . ');',
"state \$table = {" . join(', ', map { ref($vars[$_]) ? "$vars[$_]{name} => $_" : () } 0 .. $#vars) . "};",
'my @left;',
'my %seen;',

q(foreach my $arg(@_) {),
q( if (ref($arg) eq 'Sidef::Variable::NamedParam') {),
q( if (exists $table->{$arg->[0]}) {),
q( my $var = $vars[$table->{$arg->[0]}] // next;),
q( if (ref($var) eq 'SCALAR') { ),
q( ${$var} = ${$arg->[1]}[-1];),
q( } elsif (ref($var) eq 'ARRAY') {),
q( @{$var} = @{$arg->[1]};),
q( } else {),
q( %{$var} = @{$arg->[1]};),
q( }),
q( undef $seen{$var};),
q( } else {),
q( die "No such named argument: <<$arg->[0]>>";),
q( }),
q( } else {),
q( push @left, $arg;),
q( }),
q(}),
q(foreach my $var(@vars) {),
q( next if exists $seen{$var // do{shift @left; next}};),
q( @left || last;),
q( if (ref($var) eq 'SCALAR') {),
q( ${$var} = shift @left;),
q( } elsif (ref($var) eq 'ARRAY') {),
q( @{$var} = @left; last;),
q( } else {),
q( %{$var} = @left; last;),
q( }),
q(}),
)
. "\n";

foreach my $var (@vars) {

Expand Down Expand Up @@ -838,6 +877,9 @@ HEADER
elsif ($ref eq 'Sidef::Types::Array::Pair') {
$code = $ref . '->new';
}
elsif ($ref eq 'Sidef::Variable::NamedParam') {
$code = $ref . '->new(' . $self->_dump_string($obj->[0]) . ',' . $self->deparse_args(@{$obj->[1]}) . ')';
}
elsif ($ref eq 'Sidef::Types::Regex::Regex') {
$code =
$self->make_constant($ref, 'new', "Regex$refaddr",
Expand Down
52 changes: 46 additions & 6 deletions lib/Sidef/Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -607,19 +607,47 @@ package Sidef::Parser {
my $end_delim = $self->parse_delim(%opt);

my @vars;
my %classes;

while (/\G([*:]?$self->{var_name_re})/goc) {
push @vars, $1;
if ($opt{with_vals} && defined($end_delim) && /$self->{var_init_sep_re}/goc) {
my $code = substr($_, pos);
$self->parse_obj(code => \$code);
$vars[-1] .= '=' . substr($_, pos($_), pos($code));
pos($_) += pos($code);

if ($opt{with_vals} && defined($end_delim)) {

# Add the variables into the symbol table
my ($name, $class_name) = $self->get_name_and_class($vars[-1]);

undef $classes{$class_name};
unshift @{$self->{vars}{$class_name}},
{
obj => '',
name => $name,
count => 0,
type => $opt{type},
line => $self->{line},
};

if (/$self->{var_init_sep_re}/goc) {
my $code = substr($_, pos);
$self->parse_obj(code => \$code);
$vars[-1] .= '=' . substr($_, pos($_), pos($code));
pos($_) += pos($code);
}
}

defined($end_delim) && (/\G\h*,\h*/gc || last);
$self->parse_whitespace(code => $opt{code});
}

# Remove the newly added variables
foreach my $class_name (keys %classes) {
for (my $i = 0 ; $i <= $#{$self->{vars}{$class_name}} ; $i++) {
if (ref($self->{vars}{$class_name}[$i]) eq 'HASH' and not ref($self->{vars}{$class_name}[$i]{obj})) {
splice(@{$self->{vars}{$class_name}}, $i--, 1);
}
}
}

$self->parse_whitespace(code => $opt{code});

defined($end_delim)
Expand Down Expand Up @@ -898,12 +926,24 @@ package Sidef::Parser {
return $array;
}

# Bareword followed by a fat comma or a colon character
# Bareword followed by a fat comma or preceded by a colon
if ( /\G:([_\pL\pN]+)/gc
|| /\G([_\pL][_\pL\pN]*)(?=\h*=>)/gc) {

# || /\G([_\pL][_\pL\pN]*)(?=\h*=>|:(?![=:]))/gc) {
return Sidef::Types::String::String->new($1);
}

if (/\G([_\pL][_\pL\pN]*):(?![=:])/gc) {
my $name = $1;
my $obj = (
/\G\s*(?=\()/gc
? $self->parse_arguments(code => $opt{code})
: $self->parse_obj(code => $opt{code})
);
return Sidef::Variable::NamedParam->new($name, $obj);
}

# Declaration of variable types
if (/\Gvar\b\h*/gc) {
my $type = 'var';
Expand Down
9 changes: 9 additions & 0 deletions lib/Sidef/Variable/NamedParam.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package Sidef::Variable::NamedParam {

sub new {
my (undef, $name, @args) = @_;
bless [$name, \@args], __PACKAGE__;
}
}

1;
37 changes: 15 additions & 22 deletions scripts/RosettaCode/universal_turing_machine.sf
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@
## http://rosettacode.org/wiki/Universal_Turing_machine#Sidef
#

func run_utm(:args) {

var state = (args{:state} \\ "");
var blank = (args{:blank} \\ "");
var rules = (args{:rules} \\ []);
var tape = (args{:tape} \\ [blank]);
var halt = (args{:halt} \\ "");
var pos = (args{:pos} \\ 0);
func run_utm(state="", blank="", rules=[], tape=[blank], halt="", pos=0) {

if (pos < 0) {
pos += tape.len;
Expand Down Expand Up @@ -64,21 +57,21 @@ func run_utm(:args) {

print "incr machine\n";
run_utm(
halt => 'qf',
state => 'q0',
tape => %w(1 1 1),
blank => 'B',
rules => [
halt: 'qf',
state: 'q0',
tape: %w(1 1 1),
blank: 'B',
rules: [
%w(q0 1 1 right q0),
%w(q0 B 1 stay qf),
]);

say "\nbusy beaver";
run_utm(
halt => 'halt',
state => 'a',
blank => '0',
rules => [
halt: 'halt',
state: 'a',
blank: '0',
rules: [
%w(a 0 1 right b),
%w(a 1 1 left c),
%w(b 0 1 left a),
Expand All @@ -89,11 +82,11 @@ run_utm(

say "\nsorting test";
run_utm(
halt => 'STOP',
state => 'A',
blank => '0',
tape => %w(2 2 2 1 2 2 1 2 1 2 1 2 1 2),
rules => [
halt: 'STOP',
state: 'A',
blank: '0',
tape: %w(2 2 2 1 2 2 1 2 1 2 1 2 1 2),
rules: [
%w(A 1 1 right A),
%w(A 2 3 right B),
%w(A 0 0 left E),
Expand Down
13 changes: 6 additions & 7 deletions scripts/Tests/cramers_method.sf
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,10 @@ func make_det_matrix (matrix) {
matrix + [matrix.@[range(matrix.end-1)]];
}

func calculate_delta (:opts) {
func calculate_delta (matrix, free_terms, position) {

var matrix = opts{:matrix}.copy; # making a copy of the matrix
var free_terms = opts{:free_terms};
var position = opts{:position};
# Make a copy of the matrix
matrix = matrix.copy;

free_terms.range.each { |i|
matrix[i][position] = free_terms[i]; # substituting the 'pos' column with the free terms
Expand Down Expand Up @@ -75,9 +74,9 @@ var delta = (right - left);
var xyz = [];
free_terms.range.each { |i|
xyz[i] = calculate_delta(
matrix => matrix,
free_terms => free_terms,
position => i,
matrix: matrix,
free_terms: free_terms,
position: i,
)
}

Expand Down
48 changes: 48 additions & 0 deletions scripts/Tests/default_param_values.sf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/ruby

#
## Default values for function and method parameters
#

#
## Test 1
#

func test1(a, b, c=a+b, d=c+b) {
assert_eq(c, a+b);
assert_eq(d, c+b);
}

test1(1, 2);
test1(1, 2, 3, 5);
test1(42, 99);

#
## Test 2
#

func test2(a, b=(a+42), c=21+a+b) {
assert_eq(b, a+42);
assert_eq(c, 21+a+b);
}

test2(100);
test2(100, 100+42, 100+100+42+21);

#
## Test 3
#

func test3(a, b: 21, c=42) {
assert_eq(b, 21);
assert_eq(c, 42);
assert_eq(a, 13) if defined(a);
}

test3();
test3(b: 21);
test3(a: 13);
test3(13);


say "** Test passed!";
12 changes: 6 additions & 6 deletions scripts/Tests/happy_2_years_old.sf
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
# but it got its actual name only 6 days later,
# on 30th March 2013.

class Singer(:data) {
class Singer(name="", age=0) {

method sing {
{
say "Happy birthday #{data{:name}}!";
say "Happy birthday #{name}!";
say "Happy birthday to you...";
} * 2;
say "Happy #{data{:age}} years old!!!";
say "Happy #{age} years old!!!";
}
}

var s = Singer(name => "Sidef", age => 2);
var s = Singer(name: "Sidef", age: 2);
s.sing;

assert_eq(s{:data}{:name}, "Sidef");
assert_eq(s{:data}{:age}, 2);
assert_eq(s.name, "Sidef");
assert_eq(s.age, 2);
Loading

0 comments on commit 8097b21

Please sign in to comment.