Skip to content

Instantly share code, notes, and snippets.

@APIUM
Created August 21, 2018 01:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save APIUM/d5b2a49f3092ac39b648a7831c99046d to your computer and use it in GitHub Desktop.
Save APIUM/d5b2a49f3092ac39b648a7831c99046d to your computer and use it in GitHub Desktop.
-antigen-parse-args () {
576 # An argument parsing functionality to parse arguments the *antigen* way :).
577 # Takes one first argument (called spec), which dictates how to parse and
578 # the rest of the arguments are parsed. Outputs a piece of valid shell code
579 # that can be passed to `eval` inside a function which creates the arguments
580 # and their values as local variables. Suggested use is to set the defaults
581 # to all arguments first and then eval the output of this function.
582
583 # Spec: Only long argument supported. No support for parsing short options.
584 # The spec must have two sections, separated by a `;`.
585 # '<positional-arguments>;<keyword-only-arguments>'
586 # Positional arguments are passed as just values, like `command a b`.
587 # Keyword arguments are passed as a `--name=value` pair, like `command
588 # --arg1=a --arg2=b`.
589
590 # Each argument in the spec is separated by a `,`. Each keyword argument can
591 # end in a `:` to specifiy that this argument wants a value, otherwise it
592 # doesn't take a value. (The value in the output when the keyword argument
593 # doesn't have a `:` is `true`).
594
595 # Arguments in either section can end with a `?` (should come after `:`, if
596 # both are present), means optional. FIXME: Not yet implemented.
597
598 # See the test file, tests/arg-parser.t for (working) examples.
599
600 local spec="$1"
601 shift
602
603 # Sanitize the spec
604 spec="$(echo "$spec" | tr '\n' ' ' | sed 's/[[:space:]]//g')"
605
606 local code=''
607
608 --add-var () {
609 test -z "$code" || code="$code\n"
610 code="${code}local $1='$2'"
611 }
612
613 local positional_args="$(echo "$spec" | cut -d\; -f1)"
614 local positional_args_count="$(echo $positional_args |
615 awk -F, '{print NF}')"
616
617 # Set spec values based on the positional arguments.
618 local i=1
619 while [[ -n $1 && $1 != --* ]]; do
620
621 if (( $i > $positional_args_count )); then
622 echo "Only $positional_args_count positional arguments allowed." >&2
623 echo "Found at least one more: '$1'" >&2
624 return
625 fi
626
627 local name_spec="$(echo "$positional_args" | cut -d, -f$i)"
628 local name="${${name_spec%\?}%:}"
629 local value="$1"
630
631 if echo "$code" | grep -l "^local $name=" &> /dev/null; then
632 echo "Argument '$name' repeated with the value '$value'". >&2
633 return
634 fi
635
636 --add-var $name "$value"
637
638 shift
639 i=$(($i + 1))
640 done
641
642 local keyword_args="$(
643 # Positional arguments can double up as keyword arguments too.
644 echo "$positional_args" | tr , '\n' |
645 while read line; do
646 if [[ $line == *\? ]]; then
647 echo "${line%?}:?"
648 else
649 echo "$line:"
650 fi
651 done
652
653 # Specified keyword arguments.
654 echo "$spec" | cut -d\; -f2 | tr , '\n'
655 )"
656 local keyword_args_count="$(echo $keyword_args | awk -F, '{print NF}')"
657
658 # Set spec values from keyword arguments, if any. The remaining arguments
659 # are all assumed to be keyword arguments.
660 while [[ $1 == --* ]]; do
661 # Remove the `--` at the start.
662 local arg="${1#--}"
663
664 # Get the argument name and value.
665 if [[ $arg != *=* ]]; then
666 local name="$arg"
667 local value=''
668 else
669 local name="${arg%\=*}"
670 local value="${arg#*=}"
671 fi
672
673 if echo "$code" | grep -l "^local $name=" &> /dev/null; then
674 echo "Argument '$name' repeated with the value '$value'". >&2
675 return
676 fi
677
678 # The specification for this argument, used for validations.
679 local arg_line="$(echo "$keyword_args" |
680 egrep "^$name:?\??" | head -n1)"
681
682 # Validate argument and value.
683 if [[ -z $arg_line ]]; then
684 # This argument is not known to us.
685 echo "Unknown argument '$name'." >&2
686 return
687
688 elif (echo "$arg_line" | grep -l ':' &> /dev/null) &&
689 [[ -z $value ]]; then
690 # This argument needs a value, but is not provided.
691 echo "Required argument for '$name' not provided." >&2
692 return
693
694 elif (echo "$arg_line" | grep -vl ':' &> /dev/null) &&
695 [[ -n $value ]]; then
696 # This argument doesn't need a value, but is provided.
697 echo "No argument required for '$name', but provided '$value'." >&2
698 return
699
700 fi
701
702 if [[ -z $value ]]; then
703 value=true
704 fi
705
706 --add-var "${name//-/_}" "$value"
707 shift
708 done
709
710 echo "$code"
711
712 unfunction -- --add-var
713
714 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment