Why is this "use" a syntax error?

Many Perl developers are unaware that they can assert a module version with an import list at the same time. For example:

use Test::More 0.96 tests => 13;

However, the following is a syntax error:

use Test::More  .96 tests => 13;

Frankly, I don't know why. Here's a program which demonstrates my confusion. It exhibits more or less the same behavior on 5.8.9, 5.10.1, 5.12.4 and 5.14.2.

And the output is:

SV = NV(0x7fc84083a5e8) at 0x7fc84082be90
  REFCNT = 2
  NV = 0.96
SV = NV(0x7fc84083a5e0) at 0x7fc840800fd0
  REFCNT = 2
  NV = 0.96
Argument "2.121_17" isn't numeric in subroutine entry at (eval 2) line 2.

Error is: none
String found where operator expected at (eval 3) line 1, near ".01 'Dumper'"
        (Missing operator before  'Dumper'?)

Error is: syntax error at (eval 3) line 1, near ".01 'Dumper'"

The Devel::Peek shows that both numbers are the same, as far as Perl is concerned, but using .96 for the version is a syntax error. Reading perldoc use hasn't cleared this up for me. Is this a parsing bug or is there something else going on?


I haven't looked at the parser yet, but what's almost certainly going on here is that the parser/tokenizer for the use statement has a mini ad-hoc parser to figure out whether the first parameter is a version number, and that check fails unless it matches /^[0-9]/, furthermore it has other restrictions that you haven't noted.

You can see that this is what's going on with these simple test programs:

$ perl -wle 'package Test; sub VERSION { warn "version: @_" } sub import { warn "import: @_" } use Test 10'
version: Test 10 at -e line 1.
import: Test at -e line 1.

$ perl -wle 'package Test; sub VERSION { warn "version: @_" } sub import { warn "import: @_" } use Test 010'
version: Test 8 at -e line 1.
import: Test at -e line 1.

$ perl -wle 'package Test; sub VERSION { warn "version: @_" } sub import { warn "import: @_" } use Test 0.10'
version: Test 0.1 at -e line 1.
import: Test at -e line 1.

$ perl -wle 'package Test; sub VERSION { warn "version: @_" } sub import { warn "import: @_" } use Test .10'
import: Test 0.1 at -e line 1.

Once the parser has decided that it's a version number it'll get eaten and passed to VERSION, and the rest, if any, will go to import.

But if it doesn't decide that it'll die for the same reason that:

use Test "foo" "bar";

dies, i.e. you have to supply a comma if you're supplying a list of two items where the first isn't a version number.

But yes, arguably this is a bug and the "use" statement should be accepting any number that you could pass to Test->VERSION(). This should at least be documented.

Update: I was right, check out S_tokenize_use() and S_force_version() in toke.c. They do their own manual numeric parsing.

Do note that a version number is not a float. 0.96.1 is valid as well, but isn't parsed as a floating point number. So, your argument that .96 is parsed the same way as 0.96 in numerical context doesn't hold.

version.pm does accept ".96" as a version number, but only under lax rules, not under strict rules.

Normally .96 is parsed exactly the same as 0.96. But you're right that the reason it's like this is because the "use" statement accepts a more restrictive "version number", not any number that would otherwise in Perl map to a version number.

$ perl -MDevel::Peek -wle 'Dump $_ for .96, 0.96'
SV = NV(0x100817408) at 0x100811318
  REFCNT = 2
  NV = 0.96
SV = NV(0x100817400) at 0x100811330
  REFCNT = 2
  NV = 0.96

I wrote two blogs posts ("Use and the Ruby Slippers" and "Beating up on the use statement") which go into some detail about how Perl parses it use statement. Ævar's summary is on target.

The syntax of Perl's use statement is a good example of a perfectly reasonable syntax, one which you'd expect to be able to parse, but which is hopelessly beyond the powers of bison/yacc/LALR. What you're seeing is one of the signs of a very ugly workaround. There are others.

Leave a comment

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/