From cffa56ed750ee1e7ca634e316464b0f07f23dce8 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Date: Sat, 11 Aug 2012 14:10:33 +0530 Subject: [PATCH] Added function to handle argument parsing. --- antigen.zsh | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/arg-parser.t | 48 +++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 tests/arg-parser.t diff --git a/antigen.zsh b/antigen.zsh index 30ff392..bef9542 100644 --- a/antigen.zsh +++ b/antigen.zsh @@ -364,6 +364,118 @@ antigen () { "antigen-$cmd" "$@" } +-antigen-parse-args () { + # An argument parsing functionality to parse arguments the *antigen* way :). + # Takes one first argument (called spec), which dictates how to parse and + # the rest of the arguments are parsed. Outputs a piece of valid shell code + # that can be passed to `eval` inside a function which creates the arguments + # and their values as local variables. Suggested use is to set the defaults + # to all arguments first and then eval the output of this function. + + # Spec: Only long argument supported. No support for parsing short options. + # The spec must have two sections, separated by a `;`. + # ';' + # Positional arguments are passed as just values, like `command a b`. + # Keyword arguments are passed as a `--name=value` pair, like `command + # --arg1=a --arg2=b`. + + # Each argument in the spec is separated by a `,`. Each keyword argument can + # end in a `:` to specifiy that this argument wants a value, otherwise it + # doesn't take a value. (The value in the output when the keyword argument + # doesn't have a `:` is `true`). + + # Arguments in either section can end with a `?` (should come after `:`, if + # both are present), means optional. FIXME: Not yet implemented. + + # See the test file, tests/arg-parser.t for (working) examples. + + local spec="$1" + shift + + local code='' + + --add-var () { + test -z "$code" || code="$code\n" + code="${code}local $1='$2'" + } + + local positional_args="$(echo "$spec" | cut -d\; -f1)" + local positional_args_count="$(echo $positional_args | + awk -F, '{print NF}')" + + # Set spec values based on the positional arguments. + local i=1 + while ! [[ -z $1 || $1 == --* ]]; do + + if (( $i > $positional_args_count )); then + echo "Only $positional_args_count positional arguments allowed." >&2 + echo "Found at least one more: '$1'" >&2 + return + fi + + local name_spec="$(echo "$positional_args" | cut -d, -f$i)" + local name="${${name_spec%\?}%:}" + local value="$1" + + --add-var $name "$value" + + shift + i=$(($i + 1)) + done + + local keyword_args="$(echo "$spec" | cut -d\; -f2 | tr , '\n')" + local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')" + + # Set spec values from keyword arguments, if any. The remaining arguments + # are all assumed to be keyword arguments. + while [[ $1 == --* ]]; do + # Remove the `--` at the start. + local arg="${1#--}" + + # Get the argument name and value. + if [[ $arg != *=* ]]; then + local name="$arg" + local value='' + else + local name="${arg%\=*}" + local value="${arg#*=}" + fi + + # The specification for this argument, used for validations. + local arg_line="$(echo "$keyword_args" | grep -m1 "^$name:\??\?")" + + # Validate argument and value. + if [[ -z $arg_line ]]; then + # This argument is not known to us. + echo "Unknown argument '$name'." >&2 + return + + elif (echo "$arg_line" | grep -qm1 ':') && [[ -z $value ]]; then + # This argument needs a value, but is not provided. + echo "Required argument for '$name' not provided." >&2 + return + + elif (echo "$arg_line" | grep -vqm1 ':') && [[ ! -z $value ]]; then + # This argument doesn't need a value, but is provided. + echo "No argument required for '$name', but provided '$value'." >&2 + return + + fi + + if [[ -z $value ]]; then + value=true + fi + + --add-var "${name//-/_}" "$value" + shift + done + + echo "$code" + + unfunction -- --add-var + +} + # Echo the bundle specs as in the record. The first line is not echoed since it # is a blank line. -antigen-echo-record () { diff --git a/tests/arg-parser.t b/tests/arg-parser.t new file mode 100644 index 0000000..ac7cda8 --- /dev/null +++ b/tests/arg-parser.t @@ -0,0 +1,48 @@ +Helper alias. + + $ alias parse='-antigen-parse-args "url?,loc?;btype:?,no-local-clone?"' + +No arguments (since all are specified as optional). + + $ parse + (glob) + +One positional argument. + + $ parse name + local url='name' + +Two arguments. + + $ parse url location + local url='url' + local loc='location' + +Three arguments. + + $ parse url location crap + Only 2 positional arguments allowed. + Found at least one more: 'crap' + +Keywordo magic. + + $ parse url location --btype=1 --no-local-clone + local url='url' + local loc='location' + local btype='1' + local no_local_clone='true' + +Unknown keyword argument. + + $ parse --me=genius + Unknown argument 'me'. + +Missed value for keyword argument. + + $ parse --btype + Required argument for 'btype' not provided. + +Provide value for keyword argument, that shouldn't be there. + + $ parse --no-local-clone=yes + No argument required for 'no-local-clone', but provided 'yes'. -- 2.0.0