Difference between eof with and without parentheses? (Perl5) - perl

I'm trying to make a perl one-liner mimic awk's file-relative line number counter "FNR". In itself this is not a problem. In one attempt, I used the following command:
perl -lnE 'say "$. : $_"; $.=0 if eof' testfiles
This works: the (automatically incremented) cross-file line counter $. is reset upon reaching the last line in each of the testfiles. When I add parentheses to the eof function call, like so:
perl -lnE 'say "$. : $_"; $.=0 if eof()' testfiles
the program doesn't work anymore as intended, since eof() only returns true when processing the last line of the last of the testfiles. This is properly documented here.
My question is: How does eof know if it is called with or without (), and thus what to do? Is the function somehow special-cased, or can any function find out if it is called with or without parentheses?

It's an operator whose semantics can't be replicated by subs.
Just like + and and, functions in perlfunc are operators.
The functions in this section can serve as terms in an expression. They fall into two major categories: list operators and named unary operators.
As operators, they can provide any syntax they please.
Prototypes can be used to replicate the syntax of some of the functions. You can determine which using prototype("CORE::funcname").
$ perl -M5.010 -e'say prototype("CORE::length") // "[undef]"'
_
$ perl -M5.010 -e'say prototype("CORE::sysread") // "[undef]"'
*\$$;$
$ perl -M5.010 -e'say prototype("CORE::say") // "[undef]"'
[undef]
Technically, eof's syntax can be replicated by subs. So prototype does return a value for eof.
$ perl -M5.010 -e'say prototype("CORE::eof") // "[undef]"'
;*
Its semantics can't be replicated, however. There's no way for a sub to tell if it was called with or without parens. Well, it might be possible using Devel::CallParser or the keyword plugin, but this would require C code, would require a lot work, and they have problems of their own.
As a side note, open's syntax is misreported as being able to be reproduced.
$ perl -M5.010 -e'say prototype("CORE::open") // "[undef]"'
*;$#

Built-in unary functions are special-cased in the parser. There's no perl-level way to do the same for subroutine calls.

Related

perl: upper case evaluation for subroutine

I need to pass system variable in upper case to perl subroutine.
For example, if the variable with name VARNAME (value 'super'), i need to pass "SUPER_MAN".
In general, if we use 'uc' option like in the example below, we can convert to upper case
perl -e 'print uc"$ENV{VARNAME}\n"'
But when we try to pass it in subroutine, we need to include uc function in the syntax and evaluate during runtime. To emulate that I was trying the below but not working, Where am I going wrong?
perl -e 'print ".uc($ENV{VARNAME})_MAN\n"'
.uc(super)_MAN
Alternate methods/approach is also welcome.
Take the uc out of the quotes "", since perl thinks you want the literal letters uc:
FOO=abc perl -e 'print "." . uc($ENV{FOO}) . "_MAN\n"'
.ABC_MAN
perldoc perlop - Quote and Quote like Operators

How does this Perl code work?

I found this Perl program:
''=~('(?{'.(']])#+}'^'-/#._]').'"'.('/<[*-_<+>?}{>]#}+#}]])#+}#<[*-_<+>?}{>]#^'^'`^=_^<]_[[]+[/,]_/]-/#._]/^=_^<]_[[]+[/,|').',$/})')
It prints "Obfuscated Perl to print obfuscated Perl"
I want to know how it actually prints this.
It is making good use of the bitwise string XOR operator ^.
']])#+}' ^ '-/#._]'
evaluates to print,
'/<[*-_<+>?}{>]#}+#}]])#+}#<[*-_<+>?}{>]#^'
^ '`^=_^<]_[[]+[/,]_/]-/#._]/^=_^<]_[[]+[/,|'
evaluates to Obfuscated Perl to print obfuscated Perl" and the whole program reduces to
$ perl -MO=Deparse ...
'' =~ m[(?{print "Obfuscated Perl to print obfuscated Perl",$/})];
... syntax OK
Related: Acme::EyeDrops

How can I convert Perl script into one-liner

I know of Perl compiler back-end that allows you to convert any one-liner into script on following matter:
perl -MO=Deparse -pe 's/(\d+)/localtime($1)/e'
Which would give the following output
LINE: while (defined($_ = <ARGV>)) {
s/(\d+)/localtime($1);/e;
}
continue {
print $_;
}
Is there possibly a reverse tool, usable from command-line, which provided full script will generate one-liner version of it?
Note: The above example was taken from https://stackoverflow.com/a/2822721/4313369.
Perl is a free-form syntax language with clear statement and block separators, so there is nothing preventing you from simply folding a normal script up into a single line.
To use your example in reverse, you could write it as:
$ perl -e 'LINE: while (defined($_ = <ARGV>)) { s/(\d+)/localtime($1);/e; } continue { print $_; }'
This is a rather contrived example, since there is a shorter and clearer way to write it. Presumably you're starting with scripts that are already as short and clear as they should be.
Any use statements in your program can be turned into -M flags.
Obviously you have to be careful about quoting and other characters that are special to the shell. You mention running this on a remote system, by which I assume you mean SSH, which means you now have two levels of shell to sneak any escaping through. It can be tricky to work out the proper sequence of escapes, but it's always doable.
This method may work for automatically translating a Perl script on disk into a one-liner:
$ perl -e "$(tr '\n' ' ' < myscript.pl)"
The Perl script can't have comments in it, since that would comment out the entire rest of the script. If that's a problem, a bit of egrep -v '\w+#' type hackery could solve the problem.

Meaning of LINE: in Perl

I was looking at this Perl one-liner
perl -n -e 'print "$. - $_"' file
and it says that this one liner gets converted to this:
LINE:
while (<>) {
print "$. - $_"
}
Which is fine, Ijust don't know what LINE: is. It doesn't seem like a filehandle, and if it is a variable, it does not have a $sign in front of it.
My guess is that it is something like #F: an idiom that is just used in Perl one liners. Is LINE just something that Perl uses in one-liners from the command line?
It's a label. They provide a way to mark a place in your code. Using labels is not idiomatic as they are rarely needed. Labels can be used with certain commands, namely next, last, redo and goto.
A label is a bareword followed by a colon, such as LINE:
More information can be found in perldoc perlsyn

Does Perl optimize based on specific arguments, while parsing the source code?

Is perl only checking for syntax errors during the parsing of the source code, or also doing some optimizations based on arguments/parameters?
E.g. if we run:
perl source.pl debug=0
and inside source.pl there is an if condition:
if ($debug == 1) {...} else {...}
Would the "precompilation/parsing" optimize the code so that the "if" check is skipped (of course assuming that $debug is assigned only at the beginning of the code etc, etc.)?
By the way, any idea if TCL does that?
Giorgos
Thanks
Optimizations in Perl are rather limited. This is mostly due to the very permissive type system, and the absence of static typing. Features like eval etc. don't make it any easier, either.
Perl does not optimize code like
my $foo = 1;
if ($foo) { ... }
to
do { ... };
However, one can declare compile time constants:
use constant FOO => 1;
if (FOO) { ... }
which is then optimized (constant folding). Constants are implemented as special subroutines, with the assumption that subs won't be redefined. Literals will be folded as well, so print 1 + 2 + 3 will actually be compiled as print 6
Interesting runtime optimizations include method caching, and regex optimizations.
However, perl won't try to prove certain properties about your code, and will always assume that variables are truly variable, even if they are only ever assigned once.
Given a Perl script, you can look at the way it was parsed and compiled by passing perl the -MO=Deparse option. This turns the compiled opcodes back to Perl code. The output isn't always runnable. When '???' turns up, this indicates code that was optimized away, but is irrelevant. Examples:
$ perl -MO=Deparse -e' "constant" ' # literal in void context
'???';
$ perl -MO=Deparse -e' print 1 + 2 + 3 ' # constant folding
print 6;
$ perl -MO=Deparse -e' print 1 ? "yep" : "nope" ' # constant folding removes branches
print 'yep';