1#autoload 2 3# A simple compiler for _arguments descriptions. The first argument of 4# _arg_compile is the name of an array parameter in which the parse is 5# returned. The remaining arguments form a series of `phrases'. Each 6# `phrase' begins with one of the keywords "argument", "option", or "help" 7# and consists of a series of keywords and/or values. The syntax is as 8# free-form as possible, but "argument" phrases generally must appear in 9# the same relative position as the corresponding argument on the command 10# line to be completed, and there are some restrictions on ordering of 11# keywords and values within each phrase. 12# 13# Anything appearing before the first phrase or after the last is passed 14# through verbatim. (See TODO.) If more detailed mixing of compiled and 15# uncompiled fragments is necessary, use two or more calls, either with 16# different array names or by passing the output of each previous call 17# through the next. 18# 19# In the documentation below, brackets [ ] indicate optional elements and 20# braces { } indicate elements that may be repeated zero or more times. 21# Except as noted, bracketed or braced elements may appear in any order 22# relative to each other, but tokens within each element are ordered. 23# 24# argument [POS] [means MSG] [action ACT] 25# 26# POS may be an integer N for the Nth argument or "*" for all, and 27# must appear first if it appears at all. 28# MSG is a string to be displayed above the matches in a listing. 29# ACT is (currently) as described in the compsys manual. 30# 31# option OPT [follow HOW] [explain STR] {unless XOR} \ 32# {[means MSG] [action ACT]} [through PAT [means MSG] [action ACT]] 33# 34# OPT is the option, prefixed with "*" if it may appear more than once. 35# HOW refers to a following argument, and may be one of: 36# "close" must appear in the same word (synonyms "join" or "-") 37# "next" the argument must appear in the next word (aka "split") 38# "loose" the argument may appear in the same or the next word ("+") 39# "assign" as loose, but must follow an "=" in the same word ("=") 40# HOW should be suffixed with a colon if the following argument is 41# _not_ required to appear. 42# STR is to be displayed based on style `description' 43# XOR is another option in combination with which OPT may not appear. 44# It may be ":" to disable non-option completions when OPT is present. 45# MSG is a string to be displayed above the matches in a listing. 46# ACT is (currently) as described in the compsys manual. 47# PAT is either "*" for "all remaining words on the line" or a pattern 48# that, if matched, marks the end of the arguments of this option. 49# The "through PAT ..." description must be the last. 50# PAT may be suffixed with one colon to narrow the $words array to 51# the remainder of the command line, or with two colons to narrow 52# to the words before (not including) the next that matches PAT. 53# 54# help PAT [means MSG] action ACT 55# 56# ACT is applied to any option output by --help that matches PAT. 57# Do not use "help" with commands that do not support --help. 58# PAT may be suffixed with a colon if the following argument is 59# _not_ required to appear (this is usually inferred from --help). 60# MSG is a string to be displayed above the matches in a listing. 61 62# EXAMPLE: 63# This is from _gprof in the standard distribution. Note that because of 64# the brace expansion trick used in the "function name" case, no attempt 65# is made to use `phrase' form; that part gets passed through unchanged. 66# It could simply be moved to the _arguments call ahead of "$args[@]". 67# 68# _arg_compile args -s -{a,b,c,D,h,i,l,L,s,T,v,w,x,y,z} \ 69# -{A,C,e,E,f,F,J,n,N,O,p,P,q,Q,Z}:'function name:->funcs' \ 70# option -I means directory action _dir_list \ 71# option -d follow close means "debug level" \ 72# option -k means "function names" action '->pair' \ 73# option -m means "minimum execution count" \ 74# argument means executable action '_files -g \*\(-\*\)' \ 75# argument means "profile file" action '_files -g gmon.\*' \ 76# help '*=name*' means "function name" action '->funcs' \ 77# help '*=dirs*' means "directory" action _dir_list 78# _arguments "$args[@]" 79 80# TODO: 81# Verbose forms of various actions, e.g. (but not exactly) 82# "state foo" becomes "->foo" 83# "completion X explain Y ..." becomes "((X\:Y ...))" 84# etc. 85# Represent leading "*" in OPT some other way. 86# Represent trailing colons in HOW and PAT some other way. 87# Stricter syntax checking on HOW, sanity checks on XOR. 88# Something less obscure than "unless :" would be nice. 89# Warning or other syntax check for stuff after the last phrase. 90 91emulate -L zsh 92local -h argspec dspec helpspec prelude xor 93local -h -A amap dmap safe 94 95[[ -n "$1" ]] || return 1 96[[ ${(tP)${1}} = *-local ]] && { print -R NAME CONFLICT: $1 1>&2; return 1 } 97safe[reply]="$1"; shift 98 99# First consume and save anything before the argument phrases 100 101helpspec=() 102prelude=() 103 104while (($#)) 105do 106 case $1 in 107 (argument|help|option) break;; 108 (*) prelude=("$prelude[@]" "$1"); shift;; 109 esac 110done 111 112# Consume all the argument phrases and build the argspec array 113 114while (($#)) 115do 116 amap=() 117 dspec=() 118 case $1 in 119 120 # argument [POS] [means MSG] [action ACT] 121 (argument) 122 shift 123 while (($#)) 124 do 125 case $1 in 126 (<1->|\*) amap[position]="$1"; shift;; 127 (means|action) amap[$1]="$2"; shift 2;; 128 (argument|option|help) break;; 129 (*) print -R SYNTAX ERROR at "$@" 1>&2; return 1;; 130 esac 131 done 132 if (( $#amap )) 133 then 134 argspec=("$argspec[@]" "${amap[position]}:${amap[means]}:${amap[action]}") 135 fi;; 136 137 # option OPT [follow HOW] [explain STR] {unless XOR} \ 138 # {[through PAT] [means MSG] [action ACT]} 139 (option) 140 amap[option]="$2"; shift 2 141 dmap=() 142 xor=() 143 while (( $# )) 144 do 145 (( ${+amap[$1]} || ${+dmap[through]} )) && break; 146 case $1 in 147 (follow) 148 amap[follow]="${2:s/join/-/:s/close/-/:s/next//:s/split//:s/loose/+/:s/assign/=/:s/none//}" 149 shift 2;; 150 (explain) amap[explain]="[$2]" ; shift 2;; 151 (unless) xor=("$xor[@]" "${(@)=2}"); shift 2;; 152 (through|means|action) 153 while (( $# )) 154 do 155 (( ${+dmap[$1]} )) && break 2 156 case $1 in 157 (through|means|action) dmap[$1]=":${2}"; shift 2;; 158 (argument|option|help|follow|explain|unless) break;; 159 (*) print -R SYNTAX ERROR at "$@" 1>&2; return 1;; 160 esac 161 done;; 162 (argument|option|help) break;; 163 (*) print -R SYNTAX ERROR at "$@" 1>&2; return 1;; 164 esac 165 if (( $#dmap )) 166 then 167 dspec=("$dspec[@]" "${dmap[through]}${dmap[means]:-:}${dmap[action]:-:}") 168 fi 169 done 170 if (( $#amap )) 171 then 172 argspec=("$argspec[@]" "${xor:+($xor)}${amap[option]}${amap[follow]}${amap[explain]}${dspec}") 173 fi;; 174 175 # help PAT [means MSG] action ACT 176 (help) 177 amap[pattern]="$2"; shift 2 178 while (($#)) 179 do 180 (( ${+amap[$1]} )) && break; 181 case $1 in 182 (means|action) amap[$1]="$2"; shift 2;; 183 (argument|option|help) break;; 184 (*) print -R SYNTAX ERROR at "$@" 1>&2; return 1;; 185 esac 186 done 187 if (( $#amap )) 188 then 189 helpspec=("$helpspec[@]" "${amap[pattern]}:${amap[means]}:${amap[action]}") 190 fi;; 191 (*) break;; 192 esac 193done 194 195eval $safe[reply]'=( "${prelude[@]}" "${argspec[@]}" ${helpspec:+"-- ${helpspec[@]}"} "$@" )' 196 197# print -R _arguments "${prelude[@]:q}" "${argspec[@]:q}" ${helpspec:+"-- ${helpspec[@]:q}"} "$@:q" 198 199return 0 200