This commit is contained in:
Tomas Mirchev 2025-10-31 21:36:52 +02:00
parent 6726f04f77
commit dacb3aeb09

100
barg Normal file → Executable file
View File

@ -1,55 +1,61 @@
# barg - Bash argument parser + tiny CLI framework
# Bash 5+
#!/usr/bin/env bash
# --- helpers -----------------------------------------------------------------
join() {
local out="" sep=""
for x in "$@"; do [[ -n "$x" ]] && {
out+="${sep}${x}"
sep="."
}; done
for x in "$@"; do
if [[ -n "$x" ]]; then
out+="${sep}${x}"
sep="."
fi
done
printf '%s' "$out"
}
# --- tokenizer ---------------------------------------------------------------
tokenize_argv() {
local prog=$1
shift
TOKENS=()
TOKENS+=("root::${prog}")
local -a args=("$@")
while ((${#args[@]})); do
local t=${args[0]}
args=("${args[@]:1}")
if [[ "$t" == "--" ]]; then
TOKENS+=("rest::${args[*]}")
TOKENS=("root::dev")
local -a argv=("$@")
while ((${#argv[@]})); do
local token=${argv[0]}
argv=("${argv[@]:1}")
# Rest
if [[ "$token" == "--" ]]; then
TOKENS+=("rest::${argv[*]}")
break
fi
if [[ "$t" == --* ]]; then
local kv=${t#--}
local key=${kv%%=*}
TOKENS+=("narg::${key}")
[[ "$kv" == *"="* ]] && TOKENS+=("value::${kv#*=}")
# Named Argument + Value: Option or Flag (long form)
if [[ "$token" == --* ]]; then
local key=${token#--}
TOKENS+=("narg::${key%%=*}")
[[ "$key" == *"="* ]] && TOKENS+=("value::${key#*=}")
continue
fi
if [[ "$t" == -* ]]; then
local sv=${t#-}
local flags=${sv%%=*}
local val=
[[ "$sv" == *"="* ]] && val=${sv#*=}
local i ch
for ((i = 0; i < ${#flags}; i++)); do
ch=${flags:i:1}
TOKENS+=("narg::${ch}")
# Named Argument + Value: Option or Flag (short form)
if [[ "$token" == -* ]]; then
local key=${token#-}
local flags=${key%%=*}
local value=
[[ "$key" == *"="* ]] && value=${key#*=}
local index flag
for ((index = 0; index < ${#flags}; index++)); do
flag=${flags:index:1}
TOKENS+=("narg::${flag}")
done
[[ -n "$val" ]] && TOKENS+=("value::${val}")
[[ -n "$value" ]] && TOKENS+=("value::${value}")
continue
fi
TOKENS+=("unk::${t}")
# Unknown: Command, Positional or Value
TOKENS+=("unk::${token}")
done
}
# --- schema + parse ----------------------------------------------------------
declare -A S A
_generate_schema() {
@ -57,29 +63,37 @@ _generate_schema() {
A=()
local spec_name=$1
local -n SPEC_REF="$spec_name"
local _id=100
local -a pos=() cmds=()
local root_added_help=0
for entry in "${SPEC_REF[@]}"; do
IFS=';' read -r -a parts <<<"$entry"
case "${parts[0]}" in
IFS=';' read -r -a elements <<<"$entry"
case "${elements[0]}" in
command)
local id=$_id
((_id++))
IFS=',' read -r -a aliases <<<"${parts[1]}"
IFS=',' read -r -a aliases <<<"${elements[1]}"
# Schema
S["$id.entryType"]="command"
S["$id.name"]="${aliases[0]}"
S["$id.help"]="${parts[2]}"
S["$id.help"]="${elements[2]}"
S["$id.args"]=""
# Aliases
if ((${#cmds[@]} == 0)); then
A["cmd::root"]=$id
else
local last=$((${#cmds[@]} - 1))
for alias in "${aliases[@]}"; do A["$(join "${cmds[$last]}" "cmd::${alias}")"]=$id; done
for alias in "${aliases[@]}"; do
A["$(join "${cmds[$last]}" "cmd::${alias}")"]=$id
done
fi
# Control
cmds+=("$id")
pos+=(0)
@ -108,12 +122,12 @@ _generate_schema() {
argument)
local id=$_id
((_id++))
IFS=',' read -r -a aliases <<<"${parts[1]}"
IFS=',' read -r -a aliases <<<"${elements[1]}"
local name="${aliases[0]}"
local dest="$name" required="false" atype="positional" value="" help=""
local i kv k v
for ((i = 2; i < ${#parts[@]}; i++)); do
kv=${parts[i]}
for ((i = 2; i < ${#elements[@]}; i++)); do
kv=${elements[i]}
if [[ "$kv" == *":"* ]]; then
k=${kv%%:*}
v=${kv#*:}
@ -156,7 +170,7 @@ _generate_schema() {
esac
;;
*)
printf 'Error: Invalid entry type: "%s"\n' "${parts[0]}" >&2
printf 'Error: Invalid entry type: "%s"\n' "${elements[0]}" >&2
return 1
;;
esac