Skip to content

Commit

Permalink
Perl deparser: more efficient code generation for array and hash look…
Browse files Browse the repository at this point in the history
…ups.

When the value is known at compile-time.

Example:

	var arr = [1,2,3]
	say arr[0]		# this is now faster

The literal index is now unboxed at compile-time.

Same is true for hash lookups:

	var hash = (a => 42, b => 99)
	say hash{:a}		# this is now faster
  • Loading branch information
trizen committed Mar 18, 2023
1 parent fb173a4 commit f137849
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 36 deletions.
110 changes: 86 additions & 24 deletions lib/Sidef/Deparse/Perl.pm
Original file line number Diff line number Diff line change
Expand Up @@ -462,36 +462,98 @@ HEADER
sub _dump_indices {
my ($self, $array) = @_;

# TODO: use only one `map {}` statement for all indices.
my @indices;

foreach my $entry (@{$array}) {

# Optimization: when the index is just a number parsed as an expression
if (ref($entry) eq 'HASH' and exists($entry->{self}) and scalar(keys %$entry) == 1) {
my $obj = $entry->{self};
if (ref($obj) eq 'HASH' and scalar(keys %$obj) == 1 and !exists($obj->{self})) {
foreach my $class (keys %$obj) {
my $statements = $obj->{$class};
scalar(@$statements) == 1 or next;
$obj = $statements->[0];
ref($obj) eq 'HASH' or next;
scalar(keys %$obj) == 1 or next;
exists($obj->{self}) or next;
$obj = $obj->{self};
if (ref($obj) eq 'Sidef::Types::Number::Number') {
$entry = $obj;
}
}
}
}

join(
',',
grep { $_ ne '' } map {
ref($_) eq 'Sidef::Types::Number::Number'
? (ref($$_) ? Sidef::Types::Number::Number::__numify__($$_) : $$_)
: ref($_) ? ('(map { ref($_) eq "Sidef::Types::Number::Number" ? '
. '(ref($$_) ? Sidef::Types::Number::Number::__numify__($$_) : $$_) '
. ': do {my$sub=UNIVERSAL::can($_, "..."); '
. 'defined($sub) ? $sub->($_) : CORE::int($_) } } '
. ($self->deparse_expr(ref($_) eq 'HASH' ? $_ : {self => $_})) . ')')
: $_
} @{$array}
);
if (ref($entry) eq 'Sidef::Types::Number::Number') {
push @indices, (ref($$entry) ? CORE::int(Sidef::Types::Number::Number::__numify__($$entry)) : $$entry);
}
elsif (ref($entry)) {
my $str = $self->deparse_expr(ref($entry) eq 'HASH' ? $entry : {self => $entry});

if ($str ne '') {
push @indices,
( '(map { ref($_) eq "Sidef::Types::Number::Number" ? '
. '(ref($$_) ? Sidef::Types::Number::Number::__numify__($$_) : $$_) '
. ': do {my$sub=UNIVERSAL::can($_, "..."); '
. 'defined($sub) ? $sub->($_) : CORE::int($_) } } '
. $str
. ')');
}
}
else {
push @indices, $entry;
}
}

join(',', @indices);
}

sub _dump_lookups {
my ($self, $array) = @_;

join(
',',
grep { $_ ne '' } map {
(ref($_) eq 'Sidef::Types::String::String' or ref($_) eq 'Sidef::Types::Number::Number')
? $self->_dump_string("$_")
: ref($_) ? ('(map { ref($_) eq "Sidef::Types::String::String" ? $$_ : "$_" }'
. ($self->deparse_expr(ref($_) eq 'HASH' ? $_ : {self => $_})) . ')')
: qq{"\Q$_\E"}
} @{$array}
);
my @indices;

foreach my $entry (@{$array}) {

# Optimization: when the index is just a string or a number parsed as an expression
if (ref($entry) eq 'HASH' and exists($entry->{self}) and scalar(keys %$entry) == 1) {
my $obj = $entry->{self};
if (ref($obj) eq 'HASH' and scalar(keys %$obj) == 1 and !exists($obj->{self})) {
foreach my $class (keys %$obj) {
my $statements = $obj->{$class};
scalar(@$statements) == 1 or next;
$obj = $statements->[0];
ref($obj) eq 'HASH' or next;
scalar(keys %$obj) == 1 or next;
exists($obj->{self}) or next;
$obj = $obj->{self};
if (ref($obj) eq 'Sidef::Types::String::String' or ref($obj) eq 'Sidef::Types::Number::Number') {
$entry = $obj;
}
}
}
}

if (ref($entry) eq 'Sidef::Types::String::String') {
push @indices, $self->_dump_string($$entry);
}
elsif (ref($entry) eq 'Sidef::Types::Number::Number') {
push @indices, $self->_dump_string("$entry");
}
elsif (ref($entry)) {
my $str = $self->deparse_expr(ref($entry) eq 'HASH' ? $entry : {self => $entry});

if ($str ne '') {
push @indices, ('(map { ref($_) eq "Sidef::Types::String::String" ? $$_ : "$_" } ' . $str . ')');
}
}
else {
push @indices, $self->_dump_string($entry);
}
}

join(',', @indices);
}

sub _dump_var_attr {
Expand Down
2 changes: 1 addition & 1 deletion lib/Sidef/Parser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1159,7 +1159,7 @@ package Sidef::Parser {
my @array;
my $obj = $self->parse_array(code => $opt{code});

if (ref $obj->{$self->{class}} eq 'ARRAY') {
if (ref($obj->{$self->{class}}) eq 'ARRAY') {
push @array, @{$obj->{$self->{class}}};
}

Expand Down
8 changes: 5 additions & 3 deletions scripts/Graphical/barnsley_fern.sf
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

require('Imager')

local Num!PREC = 16.numify

var w = 640
var h = 640

var img = %s<Imager>.new(xsize => w, ysize => h, channels => 3)
var green = %s<Imager::Color>.new('#00FF00')
var img = %O<Imager>.new(xsize => w, ysize => h, channels => 3)
var green = %O<Imager::Color>.new('#00FF00')

var (x, y) = (float(0), float(0))
var (x, y) = (0f, 0f)

for r in (^1e4 -> lazy.map { 100.irand }) {
(x, y) = (
Expand Down
4 changes: 2 additions & 2 deletions scripts/Graphical/barnsley_fern_ifs.sf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
require('Imager')

var (width, height) = (500, 500)
var img = %s<Imager>.new(xsize => width, ysize => height)
var img = %O<Imager>.new(xsize => width, ysize => height)

struct Rule {
a, b, c, d, tx, ty, w
Expand Down Expand Up @@ -52,7 +52,7 @@ var rules = [
]

func plot(x, y) {
static green = %s<Imager::Color>.new('#00ff00')
static green = %O<Imager::Color>.new('#00ff00')
img.setpixel(
x => width/2 + Math.map(2*x, 0, 10, 0, width/2),
y => height - Math.map(2*y, 0, 10, 0, height/2),
Expand Down
2 changes: 1 addition & 1 deletion scripts/Graphical/circle_from_polygons.sf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var iter = 20; # the number of polygons
var size = 200; # the diameter of each polygon in pixels

var (width=(iter*size + size), height=700);
var img = %s<Imager>.new(xsize => width, ysize => height);
var img = %O<Imager>.new(xsize => width, ysize => height);
img.box(filled => 1, color => 'white');

iter.times { |k|
Expand Down
9 changes: 7 additions & 2 deletions scripts/Graphical/color_wheel.sf
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ var center = Complex(width/2 , height/2)

var img = %O<Imager>.new(xsize => width, ysize => height)

define(
PI = Num.pi,
TAU = Num.tau,
)

for y=(^height), x=(^width) {
var vector = (center - x - y.i)
var magnitude = (vector.abs * 2 / width)
var direction = ((Num.pi + atan2(vector.real, vector.imag)) / Num.tau)
var magnitude = (2*vector.abs / width)
var direction = ((PI + atan2(vector.real, vector.imag)) / TAU)
img.setpixel(x => x, y => y,
color => Hash(hsv => [360*direction, magnitude, magnitude < 1 ? 1 : 0])
)
Expand Down
2 changes: 1 addition & 1 deletion scripts/Graphical/munching_squares.sf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

require('GD');
 
var img = %s'GD::Image'.new(256, 256, 1);
var img = %O'GD::Image'.new(256, 256, 1);

var r = 0..255;

Expand Down
4 changes: 2 additions & 2 deletions scripts/Graphical/plasma_effect.sf
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class Plasma(width=400, height=400) {

method generate {
for y=(^height), x=(^width) {
var hue = (4 + sin(x/19) + sin(y/9) + sin((x+y)/25) + sin(hypot(x, y)/8))
img.setpixel(x => x, y => y, color => Hash(hsv => [360 * hue / 8, 1, 1]))
var hue = (4 + sin(x/19f) + sin(y/9f) + sin((x+y)/25f) + sin(hypot(x, y)/8f))
img.setpixel(x => x, y => y, color => Hash(hsv => [360 * hue / 8f, 1, 1]))
}
}

Expand Down

0 comments on commit f137849

Please sign in to comment.