Why perl parse $ x | $ y | $ z like $ z | ($ x | $ y)?

(EDIT: the pipe function below should return a blessed object for the overload to work properly). See accepted answer.)

I am trying to use perl overload to create a simple parsing tree. I don’t need much - in fact I need only one operator, which is left-associative. But there seems to be an inconsistency in how perl parses $x op $y is compared to a longer one like $x op $y op $z op ...

Here is what I have:

 package foo; use overload '|' => \&pipe, "**" => \&pipe, ">>" => \&pipe; sub pipe { [ $_[0], $_[1] ] } package main; my $x = bless ["x"], "foo"; my $y = bless ["y"], "foo"; my $z = bless ["z"], "foo"; my $w = bless ["w"], "foo"; # how perl parses it: my $p2 = $x | $y; # Cons xy my $p3 = $x | $y | $z; # Cons z (Cons xy) my $p4 = $x | $y | $z | $w; # Cons w (Cons z (Cons xy)) my $p5 = $z | ($x | $y); # same as p3??? my $s2 = $x ** $y; # Cons xy my $s3 = $x ** $y ** $z; # Cons x (Cons yz) my $s4 = $x ** $y ** $z ** $w; # Cons x (Cons y (Cons zw)) sub d { Dumper(\@_) } say "p2 = ".d($p2); say "p3 = ".d($p3); say "p4 = ".d($p4); say "p5 = ".d($p5); say "s2 = ".d($s2); say "s3 = ".d($s3); say "s4 = ".d($s4); 

The result looks something like this:

 p2 = [bless( ['x'], 'foo' ),bless( ['y'], 'foo' )] p3 = [bless( ['z'], 'foo' ),[bless( ['x'], 'foo' ),bless( ['y'], 'foo' )]] p4 = [bless( ['w'], 'foo' ),[bless( ['z'], 'foo' ),[bless( ['x'], 'foo' ),bless( ['y'], 'foo' )]]] p5 = [bless( ['z'], 'foo' ),[bless( ['x'], 'foo' ),bless( ['y'], 'foo' )]] s2 = [bless( ['x'], 'foo' ),bless( ['y'], 'foo' )] s3 = [bless( ['x'], 'foo' ),[bless( ['y'], 'foo' ),bless( ['z'], 'foo' )]] s4 = [bless( ['x'], 'foo' ),[bless( ['y'], 'foo' ),[bless( ['z'], 'foo' ),bless( ['w'], 'foo' )]]] 

Should p2 change x and y to suit other cases? Note that p3 and p5 produce the same result - so how can I tell them separately?

I do not see the same problem with the right-associative operator ** .

Is there any work for this?

+6
source share
3 answers
 use feature ":5.14"; use warnings FATAL => qw(all); use strict; use Data::Dump qw(dump pp); sub foo() {package foo; use overload '|' => \&p; sub p {bless [@{$_[0]},@{$_[1]}]} } my $x = bless ["x"], "foo"; my $y = bless ["y"], "foo"; my $z = bless ["z"], "foo"; my $p = $x | $y | $z; pp($p) 

It produces:

 bless(["x", "y", "z"], "foo") 
+3
source

Overloaded operator handlers sometimes receive operands in the reverse order, but Perl will notify the handler when it does, setting the changed argument to true.

overload :

Three arguments are passed to all the routines specified in the overload directive (with one exception - see the method). [...] The third argument is set to TRUE if (and only if) both operands have been replaced. Perl can do this to ensure that the first argument ($ self) is an object that implements an overloaded operation, in accordance with general conventions for calling objects. [...]

You ignored the third argument passed to the handler. The main problem is that you forgot to return the foo object from pipe .

+2
source

You should check out Marpa, I think it's better to understand such things.

http://blogs.perl.org/users/jeffrey_kegler/2010/05/bnf-parsing-and-linear-time.html

https://metacpan.org/pod/Marpa::PP

I think this is a typo in your script:

 my $p3 = $x || $y || $z; 
+1
source

Source: https://habr.com/ru/post/924696/


All Articles