Skip to content

Commit

Permalink
- Added support for class attributes
Browse files Browse the repository at this point in the history
In addition to class paraterms, which are also instance class attributes, a new way of declaring attributes has been added, using the `has (attr1, attr2, ...)` syntax.

Example:
	class Foo {
	   has value = 42
	}

	var a = Foo()
	var b = Foo()

	a.value = 99		# changes value of `a` to 99

	say a.value;		# prints: 99
	say b.value;		# prints: 42

- Minor fixed for Array.first(n) when array has less than n elements.

Example:
	var arr = [1,2,3];
	say arr.first(10);	# returns: [1,2,3]
  • Loading branch information
trizen committed Nov 25, 2015
1 parent 3da1dbc commit 7de49f0
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 35 deletions.
5 changes: 5 additions & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ lib/Sidef/Types/Regex/Regex.pm
lib/Sidef/Types/Regex/Regex.pod
lib/Sidef/Types/String/String.pm
lib/Sidef/Types/String/String.pod
lib/Sidef/Variable/ClassAttr.pm
lib/Sidef/Variable/ClassInit.pm
lib/Sidef/Variable/ClassInit.pod
lib/Sidef/Variable/Const.pm
Expand Down Expand Up @@ -461,6 +462,8 @@ scripts/RosettaCode/maze_generation.sf
scripts/RosettaCode/md5.sf
scripts/RosettaCode/md5_1.sf
scripts/RosettaCode/middle_three_digits.sf
scripts/RosettaCode/move_to_front_algorithm_1.sf
scripts/RosettaCode/move_to_front_algorithm_2.sf
scripts/RosettaCode/multifactorial.sf
scripts/RosettaCode/multiple_distinct_objects.sf
scripts/RosettaCode/multiplication_tables.sf
Expand Down Expand Up @@ -553,6 +556,7 @@ scripts/RosettaCode/stack.sf
scripts/RosettaCode/stack_1.sf
scripts/RosettaCode/standard_deviation.sf
scripts/RosettaCode/standard_deviation_2.sf
scripts/RosettaCode/string_comparison.sf
scripts/RosettaCode/the_isaac_cipher.sf
scripts/RosettaCode/universal_turing_machine.sf
scripts/RosettaCode/van_der_corput_sequence.sf
Expand Down Expand Up @@ -593,6 +597,7 @@ scripts/Tests/calendar.sf
scripts/Tests/call_an_object_method.sf
scripts/Tests/catalan_numbers.sf
scripts/Tests/catalan_numbers_2.sf
scripts/Tests/class_attributes.sf
scripts/Tests/class_inheritance.sf
scripts/Tests/class_redefinition.sf
scripts/Tests/classes.sf
Expand Down
3 changes: 3 additions & 0 deletions META.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@
"Sidef::Types::String::String" : {
"file" : "lib/Sidef/Types/String/String.pm"
},
"Sidef::Variable::ClassAttr" : {
"file" : "lib/Sidef/Variable/ClassAttr.pm"
},
"Sidef::Variable::ClassInit" : {
"file" : "lib/Sidef/Variable/ClassInit.pm"
},
Expand Down
2 changes: 2 additions & 0 deletions META.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ provides:
file: lib/Sidef/Types/Regex/Regex.pm
Sidef::Types::String::String:
file: lib/Sidef/Types/String/String.pm
Sidef::Variable::ClassAttr:
file: lib/Sidef/Variable/ClassAttr.pm
Sidef::Variable::ClassInit:
file: lib/Sidef/Variable/ClassInit.pm
Sidef::Variable::Const:
Expand Down
55 changes: 44 additions & 11 deletions lib/Sidef/Deparse/Perl.pm
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,35 @@ HEADER
'(' . join(',', @code) . ')';
}

sub _dump_class_attributes {
my ($self, @attrs) = @_;

my @code;
foreach my $attr (@attrs) {

my @vars = @{$attr->{vars}};
@vars || next;

my @dumped_vars = map { ref($_) ? $self->_dump_var($_) : $_ } @vars;

push @code,
( 'my('
. join(', ', @dumped_vars) . ')'
. (exists($attr->{args}) ? '=' . $self->deparse_args($attr->{args}) : ''));
foreach my $var (@vars) {
if (exists $var->{value}) {
my $value = $self->deparse_expr({self => $var->{value}});
if ($value ne '') {
push @code, "\$$var->{name}" . refaddr($var) . " //= " . $value . ";";
}
}
}

}

(' ' x $Sidef::SPACES) . join(";\n" . (' ' x $Sidef::SPACES), @code) . ";\n";
}

sub _dump_sub_init_vars {
my ($self, %opt) = @_;

Expand Down Expand Up @@ -645,14 +674,14 @@ HEADER
$code .= ($package_name = $self->_dump_class_name($obj));
}

my $vars = $obj->{vars};
local $self->{class} = refaddr($block);
local $self->{class_name} = $obj->{name};
local $self->{parent_name} = ['class initialization', $obj->{name}];
local $self->{package_name} = $package_name;
local $self->{inherit} = $obj->{inherit} if exists $obj->{inherit};
local $self->{class_vars} = $vars;
local $self->{ref_class} = 1 if ref($obj->{name});
local $self->{class} = refaddr($block);
local $self->{class_name} = $obj->{name};
local $self->{parent_name} = ['class initialization', $obj->{name}];
local $self->{package_name} = $package_name;
local $self->{inherit} = $obj->{inherit} if exists $obj->{inherit};
local $self->{class_vars} = $obj->{vars} if exists $obj->{vars};
local $self->{class_attributes} = $obj->{attributes} if exists $obj->{attributes};
local $self->{ref_class} = 1 if ref($obj->{name});
$code .= $self->deparse_expr({self => $block});
$code .= '; ' . $self->_dump_string($package_name) . '}';
}
Expand Down Expand Up @@ -686,16 +715,17 @@ HEADER
. ($self->{package_name} eq 'Sidef::Object::Object' ? '' : "Sidef::Object::Object") . ");\n";
}

if ($is_class and exists $self->{class_vars} and not $self->{ref_class}) {
if ($is_class and not $self->{ref_class}) {

$code .= (" " x $Sidef::SPACES) . 'sub new {' . "\n";

$Sidef::SPACES += $Sidef::SPACES_INCR;
$code .= $self->_dump_sub_init_vars(extended => 1, vars => ['undef', @{$self->{class_vars}}]);
$code .= $self->_dump_class_attributes(@{$self->{class_attributes}});

$code .= " " x $Sidef::SPACES;
$code .= 'my $self = bless {';
foreach my $var (@{$self->{class_vars}}) {
foreach my $var (@{$self->{class_vars}}, map { @{$_->{vars}} } @{$self->{class_attributes}}) {
$code .= qq{"\Q$var->{name}\E"=>} . $self->_dump_var($var) . ',';
}

Expand All @@ -707,7 +737,7 @@ HEADER
$code .= " " x $Sidef::SPACES . "}";
$code .= "\n" . (' ' x $Sidef::SPACES) . "*call = \\&new;\n";

foreach my $var (@{$self->{class_vars}}) {
foreach my $var (@{$self->{class_vars}}, map { @{$_->{vars}} } @{$self->{class_attributes}}) {
$code .= " " x $Sidef::SPACES;
$code .= qq{sub $var->{name} : lvalue { \$_[0]->{"\Q$var->{name}\E"} }\n};
}
Expand Down Expand Up @@ -794,6 +824,9 @@ HEADER
}
}
}
elsif ($ref eq 'Sidef::Variable::ClassAttr') {
## ok
}
elsif ($ref eq 'Sidef::Variable::Struct') {
my $name = $self->_dump_class_name($obj);
if ($addr{$refaddr}++) {
Expand Down
9 changes: 6 additions & 3 deletions lib/Sidef/Deparse/Sidef.pm
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ package Sidef::Deparse::Sidef {
}

sub _dump_init_vars {
my ($self, $init_obj) = @_;
my $code = 'var(' . $self->_dump_vars(@{$init_obj->{vars}}) . ')';
my ($self, $init_obj, $type) = @_;
my $code = $type . '(' . $self->_dump_vars(@{$init_obj->{vars}}) . ')';

if (exists $init_obj->{args}) {
$code .= '=' . $self->deparse_args($init_obj->{args});
Expand Down Expand Up @@ -196,6 +196,9 @@ package Sidef::Deparse::Sidef {
}
}
}
elsif ($ref eq 'Sidef::Variable::ClassAttr') {
$code = $self->_dump_init_vars($obj, 'has');
}
elsif ($ref eq 'Sidef::Variable::Struct') {
if ($addr{refaddr($obj)}++) {
$code = $obj->{name};
Expand All @@ -211,7 +214,7 @@ package Sidef::Deparse::Sidef {
$code = 'global ' . $obj->{class} . '::' . $obj->{name},;
}
elsif ($ref eq 'Sidef::Variable::Init') {
$code = $self->_dump_init_vars($obj);
$code = $self->_dump_init_vars($obj, 'var');
}
elsif ($ref eq 'Sidef::Variable::ConstInit') {
$code = join(";\n" . (" " x $Sidef::SPACES), map { $self->deparse_expr({self => $_}) } @{$obj->{vars}});
Expand Down
35 changes: 29 additions & 6 deletions lib/Sidef/Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,30 @@ package Sidef::Parser {
return $init_obj;
}

# "has" class attributes
if (exists($self->{current_class}) and /\Ghas\b\h*/gc) {
my $vars = $self->parse_init_vars(
code => $opt{code},
type => 'var',
private => 1,
in_use => 1,
);

my $args;
if (/\G\h*=\h*/gc) {
$args = $self->parse_obj(code => $opt{code});
$args // $self->fatal_error(
code => $_,
pos => pos,
error => qq{expected an expression after "=" in `has` declaration},
);
}

my $obj = Sidef::Variable::ClassAttr->new(vars => $vars, defined($args) ? (args => $args) : ());
push @{$self->{current_class}{attributes}}, $obj;
return $obj;
}

# Declaration of constants and static variables
if (/\G(define|const|static)\b\h*/gc) {
my $type = $1;
Expand Down Expand Up @@ -1813,12 +1837,11 @@ package Sidef::Parser {
if (
ref($self->{current_class}) eq 'Sidef::Variable::ClassInit'
and defined(
my $var = List::Util::first(
sub { $_->{name} eq $name },
@{$self->{current_class}{vars}},

#@{$self->{current_class}{__DEF_VARS__}}
)
my $var = List::Util::first(
sub { $_->{name} eq $name },
@{$self->{current_class}{vars}},
map { @{$_->{vars}} } @{$self->{current_class}{attributes}}
)
)
) {
if (exists $self->{current_method}) {
Expand Down
29 changes: 16 additions & 13 deletions lib/Sidef/Types/Array/Array.pm
Original file line number Diff line number Diff line change
Expand Up @@ -443,17 +443,6 @@ package Sidef::Types::Array::Array {
$_[0]->_min_max_by($_[1], -1);
}

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

if (defined $arg) {
my $from = @{$self} - $arg->get_value;
return $self->new(@{$self}[($from < 0 ? 0 : $from) .. $#{$self}]);
}

@{$self} ? $self->[-1] : ();
}

sub swap {
my ($self, $i, $j) = @_;
@{$self}[$i, $j] = @{$self}[$j, $i];
Expand All @@ -474,12 +463,25 @@ package Sidef::Types::Array::Array {
return return $self->find($arg);
}

return $self->new(@{$self}[0 .. $arg->get_value - 1]);
my $max = $#{$self};
$arg = $arg->get_value - 1;
return $self->new(@{$self}[0 .. ($arg > $max ? $max : $arg)]);
}

@{$self} ? $self->[0] : ();
}

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

if (defined $arg) {
my $from = @{$self} - $arg->get_value;
return $self->new(@{$self}[($from < 0 ? 0 : $from) .. $#{$self}]);
}

@{$self} ? $self->[-1] : ();
}

sub _flatten { # this exists for performance reasons
my ($self) = @_;

Expand Down Expand Up @@ -1141,7 +1143,8 @@ package Sidef::Types::Array::Array {
CORE::splice(@{$self}, $offset->get_value, 1);
}
*pop_at = \&delete_index;
*pop_at = \&delete_index;
*delete_at = \&delete_index;
sub splice {
my ($self, $offset, $length, @objects) = @_;
Expand Down
9 changes: 9 additions & 0 deletions lib/Sidef/Variable/ClassAttr.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package Sidef::Variable::ClassAttr {

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

1;
4 changes: 2 additions & 2 deletions scripts/Applications/smart-units.sf
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ while (!ARGV.is_empty) {
var units = Units(precision);

func main(command) {
var match = command.match(/^
var match = command.match(/^\s*
# command or question
(?:(?:convert|how\s+much\s+is|what(?:\s+is|\W*s)(?:\s+the)?(?:\s+equivalent\s+of)?)\s+)?
Expand All @@ -211,7 +211,7 @@ func main(command) {
# unit to (up to 3 words)
(\S+(?:\s+\S+){0,2})
$/ix);
\s*$/ix);

if (match) {
var (amount, from, to) = match.captures...;
Expand Down
32 changes: 32 additions & 0 deletions scripts/RosettaCode/move_to_front_algorithm_1.sf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/ruby

#
## http://rosettacode.org/wiki/Move-to-front_algorithm#Sidef
#

func encode(str) {
var table = ('a'..'z' -> join);
str.chars.map { |c|
var s = '';
table.sub!(Regex('(.*?)' + c), {|s1| s=s1; c + s1});
s.len;
}
}

func decode(nums) {
var table = ('a'..'z' -> join);
nums.map { |n|
var s = '';
table.sub!(Regex('(.{' + n + '})(.)'), {|s1, s2| s=s2; s2 + s1});
s;
}.join;
}

%w(broood bananaaa hiphophiphop).each { |test|
var encoded = encode($test);
say "#{test}: #{encoded}";
var decoded = decode(encoded);
print "in" if (decoded != test);
say "correctly decoded to #{decoded}";
assert_eq(test, decoded);
}
43 changes: 43 additions & 0 deletions scripts/RosettaCode/move_to_front_algorithm_2.sf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/ruby

#
## http://rosettacode.org/wiki/Move-to-front_algorithm#Sidef
#

module MoveToFront {

define ABC = "a".."z"

func m2f(ar,i) {
[ar.delete_index(i)] + ar
}

func encode(str) {
var ar = ABC+[]
gather {
str.each_char { |char|
take(var i = ar.index(char))
ar = m2f(ar, i);
}
}
}

func decode(indices) {
var ar = ABC+[]
gather {
indices.each { |i|
take ar[i];
ar = m2f(ar, i)
}
}.join
}
}

%w(broood bananaaa hiphophiphop).each { |test|
var encoded = MoveToFront::encode($test);
say "#{test}: #{encoded}";
var decoded = MoveToFront::decode(encoded);
print "in" if (decoded != test);
say "correctly decoded to #{decoded}";
assert_eq(test, decoded);
}
9 changes: 9 additions & 0 deletions scripts/RosettaCode/string_comparison.sf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/ruby

var methods = %w(== != > >= < <= <=>);

[%w(YUP YUP),%w(YUP Yup),%w(bot bat),%w(aaa zz)].each {|arr|
var (s1, s2) = arr...;
methods.each{|m| "%s %s %s\t%s\n".printf(s1, m, s2, s1.(m)(s2))};
print "\n";
}
Loading

0 comments on commit 7de49f0

Please sign in to comment.