Skip to content

Conversation

@hdwalters
Copy link
Contributor

Fixes #613.

@hdwalters hdwalters requested review from Ph0enixKM and b1ek January 7, 2025 20:07
@hdwalters hdwalters changed the base branch from main to staging January 7, 2025 20:10
@hdwalters hdwalters requested a review from mks-h January 7, 2025 20:11

/// Arguments passed to Amber script
#[arg(trailing_var_arg = true)]
#[arg(allow_hyphen_values = true)]
Copy link
Contributor Author

@hdwalters hdwalters Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows long options --xyz and short options -x to be passed to the Amber script, without being rejected as invalid by the clap library; but clap still consumes the --help option with amber script.ab --help or ./script.ab --help. I would like to make it pass all options following a positional argument to the Amber script, but I'm not sure how. All suggestions appreciated!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is of course possible to force options to be passed to the script with amber script.ab -- --help. I've tried moving this Clap argument to the end of the struct and adding #[arg(last = true)], but that makes it necessary to call amber script.ab -- arg1 arg2 for all arguments, not just --help.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hdwalters does allow_external_subcommands solve this issue?

Copy link
Contributor Author

@hdwalters hdwalters Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it would. According to the docs, it handles "unexpected positional arguments" (such as foo) not optional ones (such as --help).

I will try it, however.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may have to put up with ./script.ab -- --help.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... I think that this solution of adding the -- is good enough. It adds the separation between the Amber's arguments and the script args.


impl ParamKind {
fn from(option: String) -> Option<Self> {
let regex = regex!(r"^(?:(\w+)|-(\w)|--(\w+))$");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regular expression is only compiled once; see definition of the regex macro, which uses the once_cell crate to do cached lazy evaluation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth supporting long options with hyphens in the name, like --no-foo. If so, will need to modify the regular expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now supports --no-foo, but not ---no-foo, --no--foo or --no-foo-.

}

impl ParamCli {
pub fn get_payload(&self) -> Option<Payload> {
Copy link
Contributor Author

@hdwalters hdwalters Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this PR, we introduce the new concept of a "payload", which can be attached to a variable declaration. This allows us to store reference counted CLI parameter data in this struct (where it provides type information for variable getters) and in the parser struct (where it provides parameter information for the help text and command line parsing code).

}

impl Typed for ParamCli {
fn get_type(&self) -> Type {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expose the type of the default value, e.g. Text, [Text], Num, Bool etc, to variable getters, so we can get the values of parameter objects in the same way as ordinary variables.

Some(parser) => parser,
None => return error!(meta, parser_tok, "Expected parser object"),
};
let option = match option.get_literal_text() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option text, e.g. -x|--xyz, and help text need to be known at compile time, so they can be baked into the case statement and help information in the translated parser code.

}
}

fn create_run_name(meta: &TranslateMetadata, args: &Expr) -> String {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns the compile time Amber script name if run with amber run or shebang, or the run time Bash script name if built with amber build.

}

impl Text {
pub fn get_literal_text(&self) -> Option<String> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns the literal text if compiled from "literal text" and not "interpolated {foo} text".

self.is_const,
);
if let Some(mut payload) = self.expr.get_payload() {
payload.set_var_name(&self.name, self.global_id);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter payloads require the original variable name, so it can be baked into the translated parser code.

.join("\n")
}

fn trim_comment(line: &str) -> &str {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be a bit cleverer about trimming expected output text, because the CLI parser validity tests need to check for CLI help text with leading whitespace.

@hdwalters hdwalters self-assigned this Jan 7, 2025
Copy link
Member

@b1ek b1ek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like how this is implemented, but i'd like to talk about the feature itself in the issue

@Mte90
Copy link
Member

Mte90 commented Apr 10, 2025

@hdwalters needs an update as I see various conflicts

@Ph0enixKM
Copy link
Member

Ph0enixKM commented Apr 22, 2025

@hdwalters The only reason this must be implemented as built-ins is the fact that it needs a type as a parameter?

@hdwalters
Copy link
Contributor Author

@hdwalters The only reason this must be implemented as built-ins is the fact that it needs a type as a parameter?

That's the major blocker in the Rust code, but there are other features mentioned in #613 (comment), which are considerably easier to write in Rust than in Amber.

@hdwalters
Copy link
Contributor Author

If we're ok going ahead with this PR, I would like to do the refactoring myself; it will help me familiarise myself with the new translation layer.

@b1ek
Copy link
Member

b1ek commented May 21, 2025

@hdwalters are you still going to continue work on this PR?

@hdwalters
Copy link
Contributor Author

Hi, sorry, I've had to step away from Amber for a bit. I have to focus on my family right now. I'll post on Discord when things have settled down.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Implement command line parser builtin

4 participants