Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed arguments to MAIN interact poorly #3029

Open
kjpye opened this issue Jul 4, 2019 · 4 comments
Open

Typed arguments to MAIN interact poorly #3029

kjpye opened this issue Jul 4, 2019 · 4 comments
Labels
CLI perl6 executable and user programs command-line interface

Comments

@kjpye
Copy link

kjpye commented Jul 4, 2019

Problem

When MAIN has both typed named arguments and named array arguments the argument parsing can fail, producing a usage message rather than running the routine.

For example, the script

#!/usr/bin/env perl6

sub MAIN(:@a, Bool :$b) { say 'ran'; }

when run, produces the following...

# ./testcli
ran

# ./testcli --a=c
ran

# ./testcli --a=c --a=d
ran

# ./testcli --b
ran

# ./testcli --/b
ran

# ./testcli --a=c --b
Usage:
  ./testcli [-a=<Any> ...] [-b]

# ./testcli --a=c --/b
Usage:
  ./testcli [-a=<Any> ...] [-b]

# ./testcli --a=c --a=d --b
ran

# ./testcli --a=c --a=d --/b
ran

# ./testcli --a=c --a=d --a=e --b
ran

# ./testcli --a=c --a=d --a=e --/b
ran

# ./testcli --b --a=c
Usage:
  ./testcli [-a=<Any> ...] [-b]

#2917  ./testcli --b --a=c --a=d
ran

Expected behaviour

It is expected that each case would produce "ran".

Analysis

It appears that when there is exactly one named parameter corresponding to an array argument, and another named argument which is restricted in type are both specified, then a usage message is produced rather than running the code.

This failure is also evident if you replace "Bool" with "Int".

A workaround is to remove the type restriction, in which case the code runs as expected.
[Update: The code does not run as expected in this case; the scalar variable may actually be a single-element array. See comments below.]

Environment

The above examples were run on OpenSuse Leap15 and Tumbleweed systems with the following Rakudo version:

This is Rakudo version 2019.03.1-667-gd4ceb97e0 built on MoarVM version 2019.05-95-g065b2b427
implementing Perl 6.d.

The behaviour is the same (with a differently worded usage message) on the following rakudo versions:

This is Rakudo version 2016.09 built on MoarVM version 2016.09
implementing Perl 6.c.

This is Rakudo version 2017.01 built on MoarVM version 2017.01
implementing Perl 6.c.

This is Rakudo version 2018.01 built on MoarVM version 2018.01
implementing Perl 6.c.

This is Rakudo version 2019.03.1 built on MoarVM version 2019.03
implementing Perl 6.d.
@treyharris
Copy link
Collaborator

This appears to overlap with issues raised in #2794.

@kjpye
Copy link
Author

kjpye commented Jul 18, 2019

Conjecture, based on a reading of the rakudo source (core/Main.pm6 in particular), but not tested in practice:

The basic flow of RUN-MAIN (ignoring all sorts of side issues) is:

  1. Parse the arguments into a hash ($positional) and then convert that to a capture (default-args-to-capture).
  2. Attempt to use that capture to find a MAIN candidate. If that fails
  3. Convert all the scalar named arguments to named arguments with a single-element array (scalars-into-arrays).
  4. Attempt to use the new capture to find a MAIN candidate.

Then the candidate is run or a usage message displayed.

What appears to be happening here, with a single argument for the array, is...

  1. Generate a capture with "a=>'c', b=>1". This fails, because a is not an array.
  2. Convert the capture to "a=>['c'], b=>[1]".

The final step will succeed if the scalar variable b has no type associated with it, although it will contain an array rather than the expected value.

If the scalar variable has an associated type then the final step will fail, because the type of b (array) will fail to match the specified type ("Bool" in the example above).

Note that if no arguments for the array variable are specified the first step will find a valid candidate and the problem will not occur. If multiple arguments for the array variable are specified then the first capture will contain an array and the problem does not occur.

Fixing this will be interesting, but is way above my pay grade.

@kjpye
Copy link
Author

kjpye commented Jul 18, 2019

To confirm:

Consider a script similar to that above which does not contain the typed argument, but prints the variable contents:

#!/usr/bin/env perl6

sub MAIN(:@a, :$b) { dd @a; dd $b; }

When run with similar arguments as before...

# ./testcli
Array element = []
Any
# ./testcli --b
Array element = []
Bool::True
# ./testcli --a=c
Array element = ["c"]
Any
# ./testcli --a=c --a=d
Array element = ["c", "d"]
Any
# ./testcli --a=c --b
Array element = ["c"]
$[Bool::True]
# ./testcli --a=c --a=d --b
Array element = ["c", "d"]
Bool::True

Note the incorrect output for the "./testcli --a=c --b" case.

@lucasbuchala lucasbuchala added the CLI perl6 executable and user programs command-line interface label Aug 16, 2019
@Leont
Copy link
Contributor

Leont commented Mar 2, 2020

Quite frankly, if you want typed arguments or array arguments in MAIN I would recommend using Getopt::Long. It has much more chance of succeeding than any blind parser.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLI perl6 executable and user programs command-line interface
Projects
None yet
Development

No branches or pull requests

4 participants