# Copyright 2015 Koichi Murase <myoga.murase@gmail.com>. All rights reserved.
# This script is a part of blesh (https://github.com/akinomyoga/ble.sh)
# provided under the BSD-3-Clause license.  Do not edit this file because this
# is not the original source code: Various pre-processing has been applied.
# Also, the code comments and blank lines are stripped off in the installation
# process.  Please find the corresponding source file(s) in the repository
# "akinomyoga/ble.sh".
#
# Source: /lib/core-syntax.sh
function ble/syntax/util/is-directory {
  local path=$1
  if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then
    [[ $path == // ]]
  else
    [[ -d $path ]]
  fi
}
function ble/syntax/urange#update {
  local prefix=$1
  local p1=$2 p2=${3:-$2}
  ((0<=p1&&p1<p2)) || return 1
  (((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1),
    (${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2)))
}
function ble/syntax/wrange#update {
  local prefix=$1
  local p1=$2 p2=${3:-$2}
  ((0<=p1&&p1<=p2)) || return 1
  (((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1),
    (${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2)))
}
function ble/syntax/urange#shift {
  local prefix=$1
  ((${prefix}umin>=end0?(${prefix}umin+=shift):(
      ${prefix}umin>=beg&&(${prefix}umin=end)),
    ${prefix}umax>end0?(${prefix}umax+=shift):(
      ${prefix}umax>beg&&(${prefix}umax=beg)),
    ${prefix}umin>=${prefix}umax&&
      (${prefix}umin=${prefix}umax=-1)))
}
function ble/syntax/wrange#shift {
  local prefix=$1
  ((${prefix}umin>=end0?(${prefix}umin+=shift):(
       ${prefix}umin>beg&&(${prefix}umin=end)),
    ${prefix}umax>=end0?(${prefix}umax+=shift):(
      ${prefix}umax>=beg&&(${prefix}umax=beg)),
    ${prefix}umin==0&&++${prefix}umin,
    ${prefix}umin>${prefix}umax&&
      (${prefix}umin=${prefix}umax=-1)))
}
_ble_syntax_text=
_ble_syntax_stat=()
_ble_syntax_nest=()
_ble_syntax_tree=()
_ble_syntax_attr=()
_ble_syntax_TREE_WIDTH=5
function ble/syntax/tree-enumerate/.add-root-element {
  local wtype=$1 wlen=$2 tclen=$3 tplen=$4
  [[ ! ${wtype//[0-9]} && ${_ble_syntax_bash_command_EndWtype[wtype]} ]] &&
    wtype=${_ble_syntax_bash_command_EndWtype[wtype]}
  TE_root="$wtype $wlen $tclen $tplen -- $TE_root"
}
function ble/syntax/tree-enumerate/.initialize {
  if [[ ! ${_ble_syntax_stat[iN]} ]]; then
    TE_root= TE_i=-1 TE_nofs=0
    return 0
  fi
  local -a stat nest
  ble/string#split-words stat "${_ble_syntax_stat[iN]}"
  local wtype=${stat[2]}
  local wlen=${stat[1]}
  local nlen=${stat[3]} inest
  ((inest=nlen<0?nlen:iN-nlen))
  local tclen=${stat[4]}
  local tplen=${stat[5]}
  TE_root=
  ((iN>0)) && TE_root=${_ble_syntax_tree[iN-1]}
  while
    if ((wlen>=0)); then
      ble/syntax/tree-enumerate/.add-root-element "$wtype" "$wlen" "$tclen" "$tplen"
      tclen=0
    fi
    ((inest>=0))
  do
    ble/util/assert '[[ ${_ble_syntax_nest[inest]} ]]' "$FUNCNAME/FATAL1" || break
    ble/string#split-words nest "${_ble_syntax_nest[inest]}"
    local olen=$((iN-inest))
    tplen=${nest[4]}
    ((tplen>=0&&(tplen+=olen)))
    ble/syntax/tree-enumerate/.add-root-element "${nest[7]}" "$olen" "$tclen" "$tplen"
    wtype=${nest[2]} wlen=${nest[1]} nlen=${nest[3]} tclen=0 tplen=${nest[5]}
    ((wlen>=0&&(wlen+=olen),
      tplen>=0&&(tplen+=olen),
      nlen>=0&&(nlen+=olen),
      inest=nlen<0?nlen:iN-nlen))
    ble/util/assert '((nlen<0||nlen>olen))' "$FUNCNAME/FATAL2" || break
  done
  if [[ $TE_root ]]; then
    ((TE_i=iN))
  else
    ((TE_i=tclen>=0?iN-tclen:tclen))
  fi
  ((TE_nofs=0))
}
function ble/syntax/tree-enumerate/.impl {
  local islast=1
  while ((TE_i>0)); do
    local -a node
    if ((TE_i<iN)); then
      ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
    else
      ble/string#split-words node "${TE_root:-${_ble_syntax_tree[iN-1]}}"
    fi
    ble/util/assert '((TE_nofs<${#node[@]}))' "$FUNCNAME(i=$TE_i,iN=$iN,TE_nofs=$TE_nofs,node=${node[*]},command=$@)/FATAL1" || break
    local wtype=${node[TE_nofs]} wlen=${node[TE_nofs+1]} tclen=${node[TE_nofs+2]} tplen=${node[TE_nofs+3]} attr=${node[TE_nofs+4]}
    local wbegin=$((wlen<0?wlen:TE_i-wlen))
    local tchild=$((tclen<0?tclen:TE_i-tclen))
    local tprev=$((tplen<0?tplen:TE_i-tplen))
    "$@"
    ble/util/assert '((tprev<TE_i))' "$FUNCNAME/FATAL2" || break
    ((TE_i=tprev,TE_nofs=0,islast=0))
  done
}
function ble/syntax/tree-enumerate-children {
  ((0<tchild&&tchild<=TE_i)) || return 1
  local TE_nofs=$((TE_i==tchild?TE_nofs+_ble_syntax_TREE_WIDTH:0))
  local TE_i=$tchild
  ble/syntax/tree-enumerate/.impl "$@"
}
function ble/syntax/tree-enumerate-break { ((tprev=-1)); }
function ble/syntax/tree-enumerate {
  local TE_root TE_i TE_nofs
  [[ ${iN:+set} ]] || local iN=${#_ble_syntax_text}
  ble/syntax/tree-enumerate/.initialize
  ble/syntax/tree-enumerate/.impl "$@"
}
function ble/syntax/tree-enumerate-in-range {
  local beg=$1 end=$2
  local proc=$3
  local -a node
  local TE_i TE_nofs
  for ((TE_i=end;TE_i>=beg;TE_i--)); do
    ((TE_i>0)) && [[ ${_ble_syntax_tree[TE_i-1]} ]] || continue
    ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
    local flagUpdateNode=
    for ((TE_nofs=0;TE_nofs<${#node[@]};TE_nofs+=_ble_syntax_TREE_WIDTH)); do
      local wtype=${node[TE_nofs]} wlen=${node[TE_nofs+1]} wattr=${node[TE_nofs+4]}
      local wbeg=$((wlen<0?wlen:TE_i-wlen)) wend=$TE_i
      "${@:3}"
    done
  done
}
function ble/syntax/print-status/.graph {
  local char=$1
  if ble/util/isprint+ "$char"; then
    graph="'$char'"
    return 0
  else
    local ret
    ble/util/s2c "$char"; local code=$ret
    if ble/unicode/GraphemeCluster/ControlRepresentation "$code"; then
      graph=$_ble_term_rev$ret$_ble_term_sgr0
    else
      graph="'$char' ($code)"
    fi
  fi
}
function ble/syntax/print-status/.tree-prepend {
  local j=$1
  local value=$2${tree[j]}
  tree[j]=$value
  ((max_tree_width<${#value}&&(max_tree_width=${#value})))
}
function ble/syntax/print-status/.dump-arrays/.append-attr-char {
  if (($?==0)); then
    attr="${attr}$1"
  else
    attr="${attr} "
  fi
}
function ble/syntax/print-status/ctx#get-text {
  local sgr
  ble/syntax/ctx#get-name "$1"
  ret=${ret#BLE_}
  if [[ ! $ret ]]; then
    ble/color/face2sgr syntax_error
    ret="${ret}CTX$1$_ble_term_sgr0"
  fi
}
function ble/syntax/print-status/word.get-text {
  local index=$1
  ble/string#split-words word "${_ble_syntax_tree[index]}"
  local out= ret
  if [[ $word ]]; then
    local nofs=$((${#word[@]}/_ble_syntax_TREE_WIDTH*_ble_syntax_TREE_WIDTH))
    while (((nofs-=_ble_syntax_TREE_WIDTH)>=0)); do
      local axis=$((index+1))
      local wtype=${word[nofs]}
      if [[ $wtype =~ ^[0-9]+$ ]]; then
        ble/syntax/print-status/ctx#get-text "$wtype"; wtype=$ret
      elif [[ $wtype =~ ^n* ]]; then
        wtype=$sgr_quoted\"${wtype:1}\"$_ble_term_sgr0
      else
        wtype=$sgr_error${wtype}$_ble_term_sgr0
      fi
      local b=$((axis-word[nofs+1])) e=$axis
      local sprev=${word[nofs+3]} schild=${word[nofs+2]}
      if ((sprev>=0)); then
        sprev="@$((axis-sprev-1))>"
      else
        sprev=
      fi
      if ((schild>=0)); then
        schild=">@$((axis-schild-1))"
      else
        schild=
      fi
      local wattr=${word[nofs+4]}
      if [[ $wattr != - ]]; then
        wattr="/(wattr=$wattr)"
      else
        wattr=
      fi
      out=" word=$wtype:$sprev$b-$e$schild$wattr$out"
      for ((;b<index;b++)); do
        ble/syntax/print-status/.tree-prepend "$b" '|'
      done
      ble/syntax/print-status/.tree-prepend "$index" '+'
    done
    word=$out
  fi
}
function ble/syntax/print-status/nest.get-text {
  local index=$1
  ble/string#split-words nest "${_ble_syntax_nest[index]}"
  if [[ $nest ]]; then
    local ret
    ble/syntax/print-status/ctx#get-text "${nest[0]}"; local nctx=$ret
    local nword=-
    if ((nest[1]>=0)); then
      ble/syntax/print-status/ctx#get-text "${nest[2]}"; local swtype=$ret
      local wbegin=$((index-nest[1]))
      nword="$swtype:$wbegin-"
    fi
    local nnest=-
    ((nest[3]>=0)) && nnest="'${nest[7]}':$((index-nest[3]))-"
    local nchild=-
    if ((nest[4]>=0)); then
      local tchild=$((index-nest[4]))
      nchild='$'$tchild
      if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then
        nchild=$sgr_error$nchild$_ble_term_sgr0
      fi
    fi
    local nprev=-
    if ((nest[5]>=0)); then
      local tprev=$((index-nest[5]))
      nprev='$'$tprev
      if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then
        nprev=$sgr_error$nprev$_ble_term_sgr0
      fi
    fi
    local nparam=${nest[6]}
    if [[ $nparam == none ]]; then
      nparam=
    else
      nparam=${nparam//$_ble_term_FS/$'\e[7m^\\\e[m'}
      nparam=" nparam=$nparam"
    fi
    nest=" nest=($nctx w=$nword n=$nnest t=$nchild:$nprev$nparam)"
  fi
}
function ble/syntax/print-status/stat.get-text {
  local index=$1
  ble/string#split-words stat "${_ble_syntax_stat[index]}"
  if [[ $stat ]]; then
    local ret
    ble/syntax/print-status/ctx#get-text "${stat[0]}"; local stat_ctx=$ret
    local stat_word=-
    if ((stat[1]>=0)); then
      ble/syntax/print-status/ctx#get-text "${stat[2]}"; local stat_wtype=$ret
      stat_word="$stat_wtype:$((index-stat[1]))-"
    fi
    local stat_inest=-
    if ((stat[3]>=0)); then
      local inest=$((index-stat[3]))
      stat_inest="@$inest"
      if ((inest<0)) || [[ ! ${_ble_syntax_nest[inest]} ]]; then
        stat_inest=$sgr_error$stat_inest$_ble_term_sgr0
      fi
    fi
    local stat_child=-
    if ((stat[4]>=0)); then
      local tchild=$((index-stat[4]))
      stat_child='$'$tchild
      if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then
        stat_child=$sgr_error$stat_child$_ble_term_sgr0
      fi
    fi
    local stat_prev=-
    if ((stat[5]>=0)); then
      local tprev=$((index-stat[5]))
      stat_prev='$'$tprev
      if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then
        stat_prev=$sgr_error$stat_prev$_ble_term_sgr0
      fi
    fi
    local snparam=${stat[6]}
    if [[ $snparam == none ]]; then
      snparam=
    else
      snparam=${snparam//"$_ble_term_FS"/$'\e[7m^\\\e[m'}
      snparam=" nparam=$snparam"
    fi
    local stat_lookahead=
    ((stat[7]!=1)) && stat_lookahead=" >>${stat[7]}"
    stat=" stat=($stat_ctx w=$stat_word n=$stat_inest t=$stat_child:$stat_prev$snparam$stat_lookahead)"
  fi
}
function ble/syntax/print-status/.dump-arrays {
  local -a tree char line
  tree=()
  char=()
  line=()
  local ret
  ble/color/face2sgr syntax_error; local sgr_error=$ret
  ble/color/face2sgr syntax_quoted; local sgr_quoted=$ret
  local i max_tree_width=0
  for ((i=0;i<=iN;i++)); do
    local attr="  ${_ble_syntax_attr[i]:-|}"
    if ((_ble_syntax_attr_umin<=i&&i<_ble_syntax_attr_umax)); then
      attr="${attr:${#attr}-2:2}*"
    else
      attr="${attr:${#attr}-2:2} "
    fi
    [[ ${_ble_highlight_layer_syntax1_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax1_table[i]}"
    ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}a${_ble_term_sgr0}"
    [[ ${_ble_highlight_layer_syntax2_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax2_table[i]}"
    ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}w${_ble_term_sgr0}"
    [[ ${_ble_highlight_layer_syntax3_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax3_table[i]}"
    ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}e${_ble_term_sgr0}"
    [[ ${_ble_syntax_stat_shift[i]} ]]
    ble/syntax/print-status/.dump-arrays/.append-attr-char s
    local index=000$i
    index=${index:${#index}-3:3}
    local word nest stat
    ble/syntax/print-status/word.get-text "$i"
    ble/syntax/print-status/nest.get-text "$i"
    ble/syntax/print-status/stat.get-text "$i"
    local graph=
    ble/syntax/print-status/.graph "${_ble_syntax_text:i:1}"
    char[i]="$attr $index $graph"
    line[i]=$word$nest$stat
  done
  resultA='_ble_syntax_attr/tree/nest/stat?'$'\n'
  ble/string#reserve-prototype "$max_tree_width"
  for ((i=0;i<=iN;i++)); do
    local t=${tree[i]}${_ble_string_prototype::max_tree_width}
    resultA="$resultA${char[i]} ${t::max_tree_width}${line[i]}"$'\n'
  done
}
function ble/syntax/print-status/.dump-tree/proc1 {
  local tip="| "; tip=${tip:islast:1}
  prefix="$prefix$tip   " ble/syntax/tree-enumerate-children ble/syntax/print-status/.dump-tree/proc1
  resultB="$prefix\_ '${_ble_syntax_text:wbegin:wlen}'$nl$resultB"
}
function ble/syntax/print-status/.dump-tree {
  resultB=
  local nl=$_ble_term_nl
  local prefix=
  ble/syntax/tree-enumerate ble/syntax/print-status/.dump-tree/proc1
}
function ble/syntax/print-status {
  local iN=${#_ble_syntax_text}
  local resultA
  ble/syntax/print-status/.dump-arrays
  local resultB
  ble/syntax/print-status/.dump-tree
  local result=$resultA$resultB
  if [[ $1 == -v && $2 ]]; then
    local "${2%%\[*\]}" && ble/util/upvar "$2" "$result"
  else
    ble/util/print "$result"
  fi
}
function ble/syntax/print-layer-buffer.draw {
  local layer_name=$1
  local -a keys vals
  builtin eval "keys=(\"\${!_ble_highlight_layer_${layer_name}_buff[@]}\")"
  builtin eval "vals=(\"\${_ble_highlight_layer_${layer_name}_buff[@]}\")"
  local ret sgr0=$_ble_term_sgr0
  ble/color/face2sgr command_builtin; local sgr1=$ret
  ble/color/face2sgr syntax_varname; local sgr2=$ret
  ble/color/face2sgr syntax_quoted; local sgr3=$ret
  ble/color/face2sgr syntax_escape; local sgr4=$ret
  local quote_word_opts=quote-empty:sgrq=$sgr3:sgre=$sgr4:sgr0=$_ble_term_sgr0
  ble/canvas/put.draw "${sgr1}buffer${sgr0} ${sgr2}$layer_name${sgr0}=("
  local i count=0
  for ((i=0;i<${#keys[@]};i++)); do
    local key=${keys[i]} val=${vals[i]}
    while ((count++<key)); do
      ((count==1)) || ble/canvas/put.draw ' '
      ble/canvas/put.draw $'\e[91munset\e[m'
    done
    ((count==1)) || ble/canvas/put.draw ' '
    ble/string#quote-word "$val" "$quote_word_opts"
    ble/canvas/put.draw "$ret"
  done
  ble/canvas/put.draw ")$_ble_term_nl"
}
function ble/syntax/parse/serialize-stat {
  ((ilook<=i&&(ilook=i+1)))
  sstat="$ctx $((wbegin<0?wbegin:i-wbegin)) $wtype $((inest<0?inest:i-inest)) $((tchild<0?tchild:i-tchild)) $((tprev<0?tprev:i-tprev)) ${nparam:-none} $((ilook-i))"
}
function ble/syntax/parse/set-lookahead {
  ((i+$1>ilook&&(ilook=i+$1)))
}
function ble/syntax/parse/tree-append {
  [[ $debug_p1 ]] && ble/util/assert '((i-1>=debug_p1))' "Wrong call of tree-append: Condition violation (p1=$debug_p1 i=$i iN=$iN)."
  local type=$1
  local beg=$2 end=$i
  local len=$((end-beg))
  ((len==0)) && return 0
  local tchild=$3 tprev=$4
  local ochild=-1 oprev=-1
  ((tchild>=0&&(ochild=i-tchild)))
  ((tprev>=0&&(oprev=i-tprev)))
  [[ $type =~ ^[0-9]+$ ]] && ble/syntax/parse/touch-updated-word "$i"
  _ble_syntax_tree[i-1]="$type $len $ochild $oprev - ${_ble_syntax_tree[i-1]}"
}
function ble/syntax/parse/word-push {
  wtype=$1 wbegin=$2 tprev=$tchild tchild=-1
}
function ble/syntax/parse/word-pop {
  ble/syntax/parse/tree-append "$wtype" "$wbegin" "$tchild" "$tprev"
  ((wbegin=-1,wtype=-1,tchild=i))
  ble/syntax/parse/nest-reset-tprev
}
function ble/syntax/parse/word-cancel {
  local -a word
  ble/string#split-words word "${_ble_syntax_tree[i-1]}"
  local wlen=${word[1]} tplen=${word[3]}
  local wbegin=$((i-wlen))
  tchild=$((tplen<0?tplen:i-tplen))
  ble/array#fill-range _ble_syntax_tree "$wbegin" "$i" ''
}
function ble/syntax/parse/nest-push {
  local wlen=$((wbegin<0?wbegin:i-wbegin))
  local nlen=$((inest<0?inest:i-inest))
  local tclen=$((tchild<0?tchild:i-tchild))
  local tplen=$((tprev<0?tprev:i-tprev))
  _ble_syntax_nest[i]="$ctx $wlen $wtype $nlen $tclen $tplen ${nparam:-none} ${2:-none}"
  ((ctx=$1,inest=i,wbegin=-1,wtype=-1,tprev=tchild,tchild=-1))
  nparam=
}
function ble/syntax/parse/nest-pop {
  ((inest<0)) && return 1
  local -a parentNest
  ble/string#split-words parentNest "${_ble_syntax_nest[inest]}"
  local ntype=${parentNest[7]} nbeg=$inest
  ble/syntax/parse/tree-append "n$ntype" "$nbeg" "$tchild" "$tprev"
  local wlen=${parentNest[1]} nlen=${parentNest[3]} tplen=${parentNest[5]}
  ((ctx=parentNest[0]))
  ((wtype=parentNest[2]))
  ((wbegin=wlen<0?wlen:nbeg-wlen,
    inest=nlen<0?nlen:nbeg-nlen,
    tchild=i,
    tprev=tplen<0?tplen:nbeg-tplen))
  nparam=${parentNest[6]}
  [[ $nparam == none ]] && nparam=
}
function ble/syntax/parse/nest-type {
  local _ble_local_var=ntype
  [[ $1 == -v ]] && _ble_local_var=$2
  if ((inest<0)); then
    builtin eval "$_ble_local_var="
    return 1
  else
    builtin eval "$_ble_local_var=\"\${_ble_syntax_nest[inest]##* }\""
  fi
}
function ble/syntax/parse/nest-ctx {
  nctx=
  ((inest>=0)) || return 1
  nctx=${_ble_syntax_nest[inest]%% *}
}
function ble/syntax/parse/nest-reset-tprev {
  if ((inest<0)); then
    tprev=-1
  else
    local -a nest
    ble/string#split-words nest "${_ble_syntax_nest[inest]}"
    local tclen=${nest[4]}
    ((tprev=tclen<0?tclen:inest-tclen))
  fi
}
function ble/syntax/parse/nest-equals {
  local parent_inest=$1
  while ((1)); do
    ((parent_inest<i1)) && return 0 # 変更していない範囲 または -1
    ((parent_inest<i2)) && return 1 # 変更によって消えた範囲
    local onest=${tail_syntax_nest[parent_inest-i2]}
    local nnest=${_ble_syntax_nest[parent_inest]}
    [[ $onest != "$nnest" ]] && return 1
    ble/string#split-words onest "$onest"
    ble/util/assert \
      '((onest[3]!=0&&onest[3]<=parent_inest))' \
      "invalid nest onest[3]=${onest[3]} parent_inest=$parent_inest text=$text" || return 0
    ((parent_inest=onest[3]<0?onest[3]:(parent_inest-onest[3])))
  done
}
_ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1
_ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1
_ble_syntax_word_defer_umin=-1 _ble_syntax_word_defer_umax=-1
function ble/syntax/parse/touch-updated-attr {
  ble/syntax/urange#update _ble_syntax_attr_ "$1" "$(($1+1))"
}
function ble/syntax/parse/touch-updated-word {
  ble/util/assert "(($1>0))" "invalid word position $1"
  ble/syntax/wrange#update _ble_syntax_word_ "$1"
}
_ble_ctx_UNSPECIFIED=0
_ble_ctx_ARGX=3
_ble_ctx_ARGX0=18
_ble_ctx_ARGI=4
_ble_ctx_ARGQ=61
_ble_ctx_CMDX=1
_ble_ctx_CMDX0=82
_ble_ctx_CMDX1=17
_ble_ctx_CMDXT=49
_ble_ctx_CMDXC=26
_ble_ctx_CMDXE=43
_ble_ctx_CMDXD0=38
_ble_ctx_CMDXD=68
_ble_ctx_CMDXV=13
_ble_ctx_CMDI=2
_ble_ctx_VRHS=11
_ble_ctx_QUOT=5
_ble_ctx_EXPR=8
_ble_attr_ERR=6
_ble_attr_VAR=7
_ble_attr_QDEL=9
_ble_attr_QESC=81
_ble_attr_DEF=10
_ble_attr_DEL=12
_ble_attr_HISTX=21
_ble_ctx_PARAM=14
_ble_ctx_PWORD=15
_ble_ctx_PWORDE=73
_ble_ctx_PWORDR=72
_ble_ctx_RDRF=19
_ble_ctx_RDRD=20
_ble_ctx_RDRD2=80
_ble_ctx_RDRS=27
_ble_ctx_VALX=23
_ble_ctx_VALI=24
_ble_ctx_VALR=65
_ble_ctx_VALQ=66
_ble_attr_COMMENT=25
_ble_ctx_ARGVX=28
_ble_ctx_ARGVI=29
_ble_ctx_ARGVR=62
_ble_ctx_CONDX=32
_ble_ctx_CONDI=33
_ble_ctx_CONDQ=67
_ble_ctx_CASE=34
_ble_ctx_CPATX=76
_ble_ctx_CPATI=77
_ble_ctx_CPATQ=79
_ble_ctx_CPATX0=78
_ble_ctx_PATN=30
_ble_attr_GLOB=31
_ble_ctx_BRAX=54
_ble_attr_BRACE=55
_ble_ctx_BRACE1=56
_ble_ctx_BRACE2=57
_ble_attr_TILDE=60
_ble_ctx_FARGX1=16
_ble_ctx_FARGI1=35
_ble_ctx_FARGX2=36
_ble_ctx_FARGI2=37
_ble_ctx_FARGX3=58
_ble_ctx_FARGI3=59
_ble_ctx_FARGQ3=63
_ble_ctx_SARGX1=48
_ble_ctx_CARGX1=39
_ble_ctx_CARGI1=40
_ble_ctx_CARGQ1=64
_ble_ctx_CARGX2=41
_ble_ctx_CARGI2=42
_ble_ctx_TARGX1=50
_ble_ctx_TARGI1=51
_ble_ctx_TARGX2=52
_ble_ctx_TARGI2=53
_ble_ctx_FNAMEX=83
_ble_ctx_FNAMEI=22
_ble_ctx_RDRH=44
_ble_ctx_RDRI=45
_ble_ctx_HERE0=46
_ble_ctx_HERE1=47
_ble_ctx_ARGEX=69
_ble_ctx_ARGEI=70
_ble_ctx_ARGER=71
_ble_ctx_COARGX=74
_ble_ctx_COARGI=75
_ble_attr_CMD_BOLD=101
_ble_attr_CMD_BUILTIN=102
_ble_attr_CMD_ALIAS=103
_ble_attr_CMD_FUNCTION=104
_ble_attr_CMD_FILE=105
_ble_attr_KEYWORD=106
_ble_attr_KEYWORD_BEGIN=118
_ble_attr_KEYWORD_END=119
_ble_attr_KEYWORD_MID=120
_ble_attr_CMD_JOBS=107
_ble_attr_CMD_DIR=112
_ble_attr_CMD_SUFFIX=135
_ble_attr_CMD_SUFFIX_NEW=136
_ble_attr_FILE_DIR=108
_ble_attr_FILE_STICKY=124
_ble_attr_FILE_LINK=109
_ble_attr_FILE_ORPHAN=121
_ble_attr_FILE_FILE=111
_ble_attr_FILE_SETUID=122
_ble_attr_FILE_SETGID=123
_ble_attr_FILE_EXEC=110
_ble_attr_FILE_FIFO=114
_ble_attr_FILE_CHR=115
_ble_attr_FILE_BLK=116
_ble_attr_FILE_SOCK=117
_ble_attr_FILE_WARN=113
_ble_attr_FILE_URL=125
_ble_attr_VAR_UNSET=126
_ble_attr_VAR_EMPTY=127
_ble_attr_VAR_NUMBER=128
_ble_attr_VAR_EXPR=129
_ble_attr_VAR_ARRAY=130
_ble_attr_VAR_HASH=132
_ble_attr_VAR_READONLY=131
_ble_attr_VAR_TRANSFORM=133
_ble_attr_VAR_EXPORT=134
_ble_attr_VAR_NEW=137
_ble_ctx_EDIT_NamedCommand=201
_ble_syntax_bash_ctx_names=(
  [0]=_ble_ctx_UNSPECIFIED
  [3]=_ble_ctx_ARGX
  [18]=_ble_ctx_ARGX0
  [4]=_ble_ctx_ARGI
  [61]=_ble_ctx_ARGQ
  [1]=_ble_ctx_CMDX
  [82]=_ble_ctx_CMDX0
  [17]=_ble_ctx_CMDX1
  [49]=_ble_ctx_CMDXT
  [26]=_ble_ctx_CMDXC
  [43]=_ble_ctx_CMDXE
  [38]=_ble_ctx_CMDXD0
  [68]=_ble_ctx_CMDXD
  [13]=_ble_ctx_CMDXV
  [2]=_ble_ctx_CMDI
  [11]=_ble_ctx_VRHS
  [5]=_ble_ctx_QUOT
  [8]=_ble_ctx_EXPR
  [6]=_ble_attr_ERR
  [7]=_ble_attr_VAR
  [9]=_ble_attr_QDEL
  [81]=_ble_attr_QESC
  [10]=_ble_attr_DEF
  [12]=_ble_attr_DEL
  [21]=_ble_attr_HISTX
  [14]=_ble_ctx_PARAM
  [15]=_ble_ctx_PWORD
  [73]=_ble_ctx_PWORDE
  [72]=_ble_ctx_PWORDR
  [19]=_ble_ctx_RDRF
  [20]=_ble_ctx_RDRD
  [80]=_ble_ctx_RDRD2
  [27]=_ble_ctx_RDRS
  [23]=_ble_ctx_VALX
  [24]=_ble_ctx_VALI
  [65]=_ble_ctx_VALR
  [66]=_ble_ctx_VALQ
  [25]=_ble_attr_COMMENT
  [28]=_ble_ctx_ARGVX
  [29]=_ble_ctx_ARGVI
  [62]=_ble_ctx_ARGVR
  [32]=_ble_ctx_CONDX
  [33]=_ble_ctx_CONDI
  [67]=_ble_ctx_CONDQ
  [34]=_ble_ctx_CASE
  [76]=_ble_ctx_CPATX
  [77]=_ble_ctx_CPATI
  [79]=_ble_ctx_CPATQ
  [78]=_ble_ctx_CPATX0
  [30]=_ble_ctx_PATN
  [31]=_ble_attr_GLOB
  [54]=_ble_ctx_BRAX
  [55]=_ble_attr_BRACE
  [56]=_ble_ctx_BRACE1
  [57]=_ble_ctx_BRACE2
  [60]=_ble_attr_TILDE
  [16]=_ble_ctx_FARGX1
  [35]=_ble_ctx_FARGI1
  [36]=_ble_ctx_FARGX2
  [37]=_ble_ctx_FARGI2
  [58]=_ble_ctx_FARGX3
  [59]=_ble_ctx_FARGI3
  [63]=_ble_ctx_FARGQ3
  [48]=_ble_ctx_SARGX1
  [39]=_ble_ctx_CARGX1
  [40]=_ble_ctx_CARGI1
  [64]=_ble_ctx_CARGQ1
  [41]=_ble_ctx_CARGX2
  [42]=_ble_ctx_CARGI2
  [50]=_ble_ctx_TARGX1
  [51]=_ble_ctx_TARGI1
  [52]=_ble_ctx_TARGX2
  [53]=_ble_ctx_TARGI2
  [83]=_ble_ctx_FNAMEX
  [22]=_ble_ctx_FNAMEI
  [44]=_ble_ctx_RDRH
  [45]=_ble_ctx_RDRI
  [46]=_ble_ctx_HERE0
  [47]=_ble_ctx_HERE1
  [69]=_ble_ctx_ARGEX
  [70]=_ble_ctx_ARGEI
  [71]=_ble_ctx_ARGER
  [74]=_ble_ctx_COARGX
  [75]=_ble_ctx_COARGI
  [101]=_ble_attr_CMD_BOLD
  [102]=_ble_attr_CMD_BUILTIN
  [103]=_ble_attr_CMD_ALIAS
  [104]=_ble_attr_CMD_FUNCTION
  [105]=_ble_attr_CMD_FILE
  [106]=_ble_attr_KEYWORD
  [118]=_ble_attr_KEYWORD_BEGIN
  [119]=_ble_attr_KEYWORD_END
  [120]=_ble_attr_KEYWORD_MID
  [107]=_ble_attr_CMD_JOBS
  [112]=_ble_attr_CMD_DIR
  [135]=_ble_attr_CMD_SUFFIX
  [136]=_ble_attr_CMD_SUFFIX_NEW
  [108]=_ble_attr_FILE_DIR
  [124]=_ble_attr_FILE_STICKY
  [109]=_ble_attr_FILE_LINK
  [121]=_ble_attr_FILE_ORPHAN
  [111]=_ble_attr_FILE_FILE
  [122]=_ble_attr_FILE_SETUID
  [123]=_ble_attr_FILE_SETGID
  [110]=_ble_attr_FILE_EXEC
  [114]=_ble_attr_FILE_FIFO
  [115]=_ble_attr_FILE_CHR
  [116]=_ble_attr_FILE_BLK
  [117]=_ble_attr_FILE_SOCK
  [113]=_ble_attr_FILE_WARN
  [125]=_ble_attr_FILE_URL
  [126]=_ble_attr_VAR_UNSET
  [127]=_ble_attr_VAR_EMPTY
  [128]=_ble_attr_VAR_NUMBER
  [129]=_ble_attr_VAR_EXPR
  [130]=_ble_attr_VAR_ARRAY
  [132]=_ble_attr_VAR_HASH
  [131]=_ble_attr_VAR_READONLY
  [133]=_ble_attr_VAR_TRANSFORM
  [134]=_ble_attr_VAR_EXPORT
  [137]=_ble_attr_VAR_NEW
  [201]=_ble_ctx_EDIT_NamedCommand
)
function ble/syntax/ctx#get-name {
  ret=${_ble_syntax_bash_ctx_names[$1]#_ble_ctx_}
}
_ble_syntax_context_proc=()
_ble_syntax_context_end=()
function ble/syntax:text/ctx-unspecified {
  ((_ble_syntax_attr[i]=ctx,i+=${#tail}))
  return 0
}
_ble_syntax_context_proc[_ble_ctx_UNSPECIFIED]=ble/syntax:text/ctx-unspecified
function ble/syntax:text/initialize-ctx { ctx=$_ble_ctx_UNSPECIFIED; }
function ble/syntax:text/initialize-vars { return 0; }
_ble_syntax_bash_RexSpaces=$'[ \t]+'
_ble_syntax_bash_RexIFSs="[$_ble_term_IFS]+"
_ble_syntax_bash_RexDelimiter="[$_ble_term_IFS;|&<>()]"
_ble_syntax_bash_RexRedirect='((\{[_a-zA-Z][_a-zA-Z0-9]*\}|[0-9]+)?(&?>>?|>[|&]|<[>&]?|<<[-<]?))[ 	]*'
_ble_syntax_bash_chars=()
_ble_syntax_bashc_seed=
function ble/syntax:bash/cclass/update/reorder {
  builtin eval "local a=\"\${$1}\""
  [[ $a == *']'* ]] && a="]${a//]}"
  [[ $a == *'-'* ]] && a="${a//-}-"
  builtin eval "$1=\$a"
}
function ble/syntax:bash/cclass/update {
  local seed=$_ble_syntax_bash_histc12
  shopt -q extglob && seed=${seed}x
  [[ $seed == "$_ble_syntax_bashc_seed" ]] && return 1
  _ble_syntax_bashc_seed=$seed
  local key modified=
  if [[ $_ble_syntax_bash_histc12 == '!^' ]]; then
    for key in "${!_ble_syntax_bash_charsDef[@]}"; do
      _ble_syntax_bash_chars[key]=${_ble_syntax_bash_charsDef[key]}
    done
    _ble_syntax_bashc_simple=$_ble_syntax_bash_chars_simpleDef
  else
    modified=1
    local histc1=${_ble_syntax_bash_histc12:0:1}
    local histc2=${_ble_syntax_bash_histc12:1:1}
    for key in "${!_ble_syntax_bash_charsFmt[@]}"; do
      local a=${_ble_syntax_bash_charsFmt[key]}
      a=${a//@h/"$histc1"}
      a=${a//@q/"$histc2"}
      _ble_syntax_bash_chars[key]=$a
    done
    local a=$_ble_syntax_bash_chars_simpleFmt
    a=${a//@h/"$histc1"}
    a=${a//@q/"$histc2"}
    _ble_syntax_bashc_simple=$a
  fi
  if [[ $seed == *x ]]; then
    local extglob='@+!' # *? は既に登録されている筈
    _ble_syntax_bash_chars[_ble_ctx_ARGI]=${_ble_syntax_bash_chars[_ble_ctx_ARGI]}$extglob
    _ble_syntax_bash_chars[_ble_ctx_PATN]=${_ble_syntax_bash_chars[_ble_ctx_PATN]}$extglob
    _ble_syntax_bash_chars[_ble_ctx_PWORD]=${_ble_syntax_bash_chars[_ble_ctx_PWORD]}$extglob
    _ble_syntax_bash_chars[_ble_ctx_PWORDE]=${_ble_syntax_bash_chars[_ble_ctx_PWORDE]}$extglob
    _ble_syntax_bash_chars[_ble_ctx_PWORDR]=${_ble_syntax_bash_chars[_ble_ctx_PWORDR]}$extglob
  fi
  if [[ $modified ]]; then
    for key in "${!_ble_syntax_bash_chars[@]}"; do
      ble/syntax:bash/cclass/update/reorder _ble_syntax_bash_chars[key]
    done
    ble/syntax:bash/cclass/update/reorder _ble_syntax_bashc_simple
  fi
  return 0
}
_ble_syntax_bash_charsDef=()
_ble_syntax_bash_charsFmt=()
_ble_syntax_bash_chars_simpleDef=
_ble_syntax_bash_chars_simpleFmt=
function ble/syntax:bash/cclass/initialize {
  local delimiters="$_ble_term_IFS;|&()<>"
  local expansions="\$\"\`\\'"
  local glob='[*?'
  local tilde='~:'
  _ble_syntax_bash_charsDef[_ble_ctx_ARGI]="$delimiters$expansions$glob{$tilde^!"
  _ble_syntax_bash_charsDef[_ble_ctx_PATN]="$expansions$glob(|)<>{!" # <> はプロセス置換のため。
  _ble_syntax_bash_charsDef[_ble_ctx_QUOT]="\$\"\`\\!"         # 文字列 "～" で特別な意味を持つのは $ ` \ " のみ。+履歴展開の ! も。
  _ble_syntax_bash_charsDef[_ble_ctx_EXPR]="][}()$expansions!" # ()[] は入れ子を数える為。} は ${var:ofs:len} の為。
  _ble_syntax_bash_charsDef[_ble_ctx_PWORD]="}$expansions$glob!" # パラメータ展開 ${～}
  _ble_syntax_bash_charsDef[_ble_ctx_PWORDE]="}$expansions$glob!" # パラメータ展開 ${～} エラー
  _ble_syntax_bash_charsDef[_ble_ctx_PWORDR]="}/$expansions$glob!" # パラメータ展開 ${～} 置換前
  _ble_syntax_bash_charsDef[_ble_ctx_RDRH]="$delimiters$expansions"
  _ble_syntax_bash_charsDef[_ble_ctx_HERE1]="\\\$\`$_ble_term_nl!"
  _ble_syntax_bash_charsFmt[_ble_ctx_ARGI]="$delimiters$expansions$glob{$tilde@q@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_PATN]="$expansions$glob(|)<>{@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_QUOT]="\$\"\`\\@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_EXPR]="][}()$expansions@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_PWORD]="}$expansions$glob@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_PWORDE]="}$expansions$glob@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_PWORDR]="}/$expansions$glob@h"
  _ble_syntax_bash_charsFmt[_ble_ctx_RDRH]=${_ble_syntax_bash_charsDef[_ble_ctx_RDRH]}
  _ble_syntax_bash_charsFmt[_ble_ctx_HERE1]="\\\$\`$_ble_term_nl@h"
  _ble_syntax_bash_chars_simpleDef="$delimiters$expansions^!"
  _ble_syntax_bash_chars_simpleFmt="$delimiters$expansions@q@h"
  _ble_syntax_bash_histc12='!^'
  ble/syntax:bash/cclass/update
}
ble/syntax:bash/cclass/initialize
_ble_syntax_bash_simple_rex_letter=
_ble_syntax_bash_simple_rex_param=
_ble_syntax_bash_simple_rex_bquot=
_ble_syntax_bash_simple_rex_squot=
_ble_syntax_bash_simple_rex_dquot=
_ble_syntax_bash_simple_rex_literal=
_ble_syntax_bash_simple_rex_element=
_ble_syntax_bash_simple_rex_word=
_ble_syntax_bash_simple_rex_open_word=
_ble_syntax_bash_simple_rex_open_dquot=
_ble_syntax_bash_simple_rex_open_squot=
_ble_syntax_bash_simple_rex_incomplete_word1=
_ble_syntax_bash_simple_rex_incomplete_word2=
_ble_syntax_bash_simple_rex_noglob_word1=
_ble_syntax_bash_simple_rex_noglob_word2=
function ble/syntax:bash/simple-word/update {
  local q="'"
  local letter='\[[!^]|[^'${_ble_syntax_bashc_simple}']'
  local param1='\$([-*@#?$!0_]|[1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)'
  local param2='\$\{(#?[-*@#?$!0]|[#!]?([1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*))\}' # ${!!} ${!$} はエラーになる。履歴展開の所為?
  local param=$param1'|'$param2
  local bquot='\\.'
  local squot=$q'[^'$q']*'$q'|\$'$q'([^'$q'\]|\\.)*'$q
  local dquot='\$?"([^'${_ble_syntax_bash_chars[_ble_ctx_QUOT]}']|\\.|'$param')*"'
  _ble_syntax_bash_simple_rex_letter=$letter # 0 groups
  _ble_syntax_bash_simple_rex_param=$param   # 3 groups
  _ble_syntax_bash_simple_rex_bquot=$bquot   # 0 groups
  _ble_syntax_bash_simple_rex_squot=$squot   # 1 groups
  _ble_syntax_bash_simple_rex_dquot=$dquot   # 4 groups
  _ble_syntax_bash_simple_rex_literal='^('$letter')+$'
  _ble_syntax_bash_simple_rex_element='('$bquot'|'$squot'|'$dquot'|'$param'|'$letter')'
  _ble_syntax_bash_simple_rex_word='^'$_ble_syntax_bash_simple_rex_element'+$'
  local open_squot=$q'[^'$q']*|\$'$q'([^'$q'\]|\\.)*'
  local open_dquot='\$?"([^'${_ble_syntax_bash_chars[_ble_ctx_QUOT]}']|\\.|'$param')*'
  _ble_syntax_bash_simple_rex_open_word='^('$_ble_syntax_bash_simple_rex_element'*)(\\|'$open_squot'|'$open_dquot')$'
  _ble_syntax_bash_simple_rex_open_squot=$open_squot
  _ble_syntax_bash_simple_rex_open_dquot=$open_dquot
  local letter1='\[[!^]|[^{'${_ble_syntax_bashc_simple}']'
  local letter2='\[[!^]|[^'${_ble_syntax_bashc_simple}']'
  _ble_syntax_bash_simple_rex_incomplete_word1='^('$bquot'|'$squot'|'$dquot'|'$param'|'$letter1')+'
  _ble_syntax_bash_simple_rex_incomplete_word2='^(('$bquot'|'$squot'|'$dquot'|'$param'|'$letter2')*)(\\|'$open_squot'|'$open_dquot')?$'
  local noglob_letter='[^[?*'${_ble_syntax_bashc_simple}']'
  _ble_syntax_bash_simple_rex_noglob_word1='^('$bquot'|'$squot'|'$dquot'|'$noglob_letter')+$'
  _ble_syntax_bash_simple_rex_noglob_word2='^('$bquot'|'$squot'|'$dquot'|'$param'|'$noglob_letter')+$'
}
ble/syntax:bash/simple-word/update
function ble/syntax:bash/simple-word/is-literal {
  [[ $1 =~ $_ble_syntax_bash_simple_rex_literal ]]
}
function ble/syntax:bash/simple-word/is-simple {
  [[ $1 =~ $_ble_syntax_bash_simple_rex_word ]]
}
function ble/syntax:bash/simple-word/is-simple-or-open-simple {
  [[ $1 =~ $_ble_syntax_bash_simple_rex_word || $1 =~ $_ble_syntax_bash_simple_rex_open_word ]]
}
function ble/syntax:bash/simple-word/is-never-word {
  ble/syntax:bash/simple-word/is-simple-or-open-simple && return 1
  local rex=${_ble_syntax_bash_simple_rex_word%'$'}'[ |&;<>()]|^[ |&;<>()]'
  [[ $1 =~ $rex ]]
}
function ble/syntax:bash/simple-word/is-simple-noglob {
  [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word1 ]] && return 0
  if [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word2 ]]; then
    builtin eval -- "local expanded=$1" 2>/dev/null
    local rex='[*?]|\[.+\]|[*?@+!]\(.*\)'
    [[ $expanded =~ $rex ]] || return 0
  fi
  return 1
}
function ble/syntax:bash/simple-word/evaluate-last-brace-expansion {
  local value=$1
  local bquot=$_ble_syntax_bash_simple_rex_bquot
  local squot=$_ble_syntax_bash_simple_rex_squot
  local dquot=$_ble_syntax_bash_simple_rex_dquot
  local param=$_ble_syntax_bash_simple_rex_param
  local letter='\[[!^]|[^{,}'${_ble_syntax_bashc_simple}']'
  local symbol='[{,}]'
  local rex_range_expansion='^(([-+]?[0-9]+)\.\.\.[-+]?[0-9]+|([a-zA-Z])\.\.[a-zA-Z])(\.\.[-+]?[0-9]+)?$'
  local rex0='^('$bquot'|'$squot'|'$dquot'|'$param'|'$letter')+'
  local stack; stack=()
  local out= comma= index=0 iopen=0 no_brace_length=0
  while [[ $value ]]; do
    if [[ $value =~ $rex0 ]]; then
      local len=${#BASH_REMATCH}
      ((index+=len,no_brace_length+=len))
      out=$out${value::len}
      value=${value:len}
    elif [[ $value == '{'* ]]; then
      ((iopen=++index,no_brace_length=0))
      value=${value:1}
      ble/array#push stack "$comma:$out"
      out= comma=
    elif ((${#stack[@]})) && [[ $value == '}'* ]]; then
      ((++index))
      value=${value:1}
      ble/array#pop stack
      local out0=${ret#*:} comma0=${ret%%:*}
      if [[ $comma ]]; then
        ((iopen=index,no_brace_length=0))
        out=$out0$out
        comma=$comma0
      elif [[ $out =~ $rex_range_expansion ]]; then
        ((iopen=index,no_brace_length=0))
        out=$out0${2#+}$3
        comma=$comma0
      else
        ((++no_brace_length))
        ble/array#push stack "$comma0:$out0" # cancel pop
        out=$out'}'
      fi
    elif ((${#stack[@]})) && [[ $value == ','* ]]; then
      ((iopen=++index,no_brace_length=0))
      value=${value:1}
      out= comma=1
    else
      ((++index,++no_brace_length))
      out=$out${value::1}
      value=${value:1}
    fi
  done
  while ((${#stack[@]})); do
    ble/array#pop stack
    local out0=${ret#*:} comma0=${ret%%:*}
    out=$out0$out
  done
  ret=$out simple_ibrace=$iopen:$((${#out}-no_brace_length))
}
function ble/syntax:bash/simple-word/reconstruct-incomplete-word {
  local word=$1
  ret= simple_flags= simple_ibrace=0:0
  [[ $word ]] || return 0
  if [[ $word =~ $_ble_syntax_bash_simple_rex_incomplete_word1 ]]; then
    ret=${word::${#BASH_REMATCH}}
    word=${word:${#BASH_REMATCH}}
    [[ $word ]] || return 0
  fi
  if [[ $word =~ $_ble_syntax_bash_simple_rex_incomplete_word2 ]]; then
    local out=$ret
    local m_brace=${BASH_REMATCH[1]}
    local m_quote=${word:${#m_brace}}
    if [[ $m_brace ]]; then
      ble/syntax:bash/simple-word/evaluate-last-brace-expansion "$m_brace"
      simple_ibrace=$((${#out}+${simple_ibrace%:*})):$((${#out}+${simple_ibrace#*:}))
      out=$out$ret
    fi
    if [[ $m_quote ]]; then
      case $m_quote in
      ('$"'*) out=$out$m_quote\" simple_flags=I ;;
      ('"'*)  out=$out$m_quote\" simple_flags=D ;;
      ("$'"*) out=$out$m_quote\' simple_flags=E ;;
      ("'"*)  out=$out$m_quote\' simple_flags=S ;;
      ('\')   simple_flags=B ;;
      (*) return 1 ;;
      esac
    fi
    ret=$out
    return 0
  fi
  return 1
}
function ble/syntax:bash/simple-word/extract-parameter-names {
  ret=()
  local letter=$_ble_syntax_bash_simple_rex_letter
  local bquot=$_ble_syntax_bash_simple_rex_bquot
  local squot=$_ble_syntax_bash_simple_rex_squot
  local dquot=$_ble_syntax_bash_simple_rex_dquot
  local param=$_ble_syntax_bash_simple_rex_param
  local value=$1
  local rex0='^('$letter'|'$bquot'|'$squot')+'
  local rex1='^('$dquot')'
  local rex2='^('$param')'
  while [[ $value ]]; do
    [[ $value =~ $rex0 ]] && value=${value:${#BASH_REMATCH}}
    if [[ $value =~ $rex1 ]]; then
      value=${value:${#BASH_REMATCH}}
      ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot "$BASH_REMATCH"
    fi
    [[ $value =~ $rex2 ]] || break
    value=${value:${#BASH_REMATCH}}
    local var=${BASH_REMATCH[2]}${BASH_REMATCH[3]}
    [[ $var == [_a-zA-Z]* ]] && ble/array#push ret "$var"
  done
}
function ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot {
  local value=$1
  if [[ $value == '$"'*'"' ]]; then
    value=${value:2:${#value}-3}
  elif [[ $value == '"'*'"' ]]; then
    value=${value:1:${#value}-2}
  else
    return 0
  fi
  local rex0='^([^'${_ble_syntax_bash_chars[_ble_ctx_QUOT]}']|\\.)+'
  local rex2='^('$param')'
  while [[ $value ]]; do
    [[ $value =~ $rex0 ]] && value=${value:${#BASH_REMATCH}}
    [[ $value =~ $rex2 ]] || break
    value=${value:${#BASH_REMATCH}}
    local var=${BASH_REMATCH[2]}${BASH_REMATCH[3]}
    [[ $var == [_a-zA-Z]* ]] && ble/array#push ret "$var"
  done
}
function ble/syntax:bash/simple-word/eval/.set-result {
  if [[ $__ble_word_limit ]] && (($#>__ble_word_limit)); then
    set -- "${@::__ble_word_limit}"
  fi
  __ble_ret=("$@")
}
function ble/syntax:bash/simple-word/eval/.print-result {
  if [[ $__ble_word_limit ]] && (($#>__ble_word_limit)); then
    set -- "${@::__ble_word_limit}"
  fi
  if (($#>=1000)) && [[ $OSTYPE != cygwin && $OSTYPE != msys ]]; then
    if ((_ble_bash>=50200)); then
      printf '%s\0' "$@" >| "$__ble_simple_word_tmpfile"
      ble/util/print 'ble/util/readarray -d "" __ble_ret < "$__ble_simple_word_tmpfile"'
      return 0
    elif ((_ble_bash>=40000)); then
      ret=("$@")
      ble/util/writearray --nlfix ret >| "$__ble_simple_word_tmpfile"
      ble/util/print 'ble/util/readarray --nlfix __ble_ret < "$__ble_simple_word_tmpfile"'
      return 0
    fi
  fi
  local ret; ble/string#quote-words "$@"
  ble/util/print "__ble_ret=($ret)"
}
function ble/syntax:bash/simple-word/eval/.eval-set {
  if [[ ${_ble_edit_exec_lastparams[0]+set} ]]; then
    set -- "${_ble_edit_exec_lastparams[@]}"
  else
    set --
  fi
  local ext=0
  builtin eval -- "ble/syntax:bash/simple-word/eval/.set-result $__ble_simple_word" &>/dev/null; ext=$?
  builtin eval : # Note: bash 3.1/3.2 eval バグ対策 (#D1132)
  return "$ext"
}
function ble/syntax:bash/simple-word/eval/.eval-print {
  if [[ ${_ble_edit_exec_lastparams[0]+set} ]]; then
    set -- "${_ble_edit_exec_lastparams[@]}"
  else
    set --
  fi
  builtin eval -- "ble/syntax:bash/simple-word/eval/.print-result $__ble_simple_word"
}
function ble/syntax:bash/simple-word/eval/.impl {
  local __ble_word=$1 __ble_opts=$2 __ble_flags=
  local -a ret=()
  ble/syntax:bash/simple-word/extract-parameter-names "$__ble_word"
  if ((${#ret[@]})); then
    local __ble_defs
    ble/util/assign __ble_defs 'ble/util/print-global-definitions --hidden-only "${ret[@]}"'
    builtin eval -- "$__ble_defs" &>/dev/null # 読み取り専用の変数のこともある
  fi
  local __ble_word_limit=
  ble/string#match ":$__ble_opts:" ':limit=([^:]*):' &&
    ((__ble_word_limit=BASH_REMATCH[1]))
  if [[ $- != *f* ]] && ! ble/syntax:bash/simple-word/is-simple-noglob "$1"; then
    if [[ :$__ble_opts: == *:noglob:* ]]; then
      set -f
      __ble_flags=f
    elif ble/util/is-cygwin-slow-glob "$1"; then # Note: #D1168
      if shopt -q failglob &>/dev/null; then
        __ble_ret=()
        return 1
      elif shopt -q nullglob &>/dev/null; then
        __ble_ret=()
        return 0
      else
        set -f
        __ble_flags=f
      fi
    elif [[ :$__ble_opts: == *:stopcheck:* ]]; then
      ble/decode/has-input && return 148
      if ((_ble_bash>=40000)); then
        __ble_flags=s
      elif shopt -q globstar &>/dev/null; then
        if builtin eval "[[ $__ble_word == *'**'* ]]"; then
          [[ :$__ble_opts: == *:timeout=*:* ]] && return 142
          return 148
        fi
      fi
    fi
  fi
  __ble_ret=()
  local __ble_simple_word=$__ble_word
  if [[ $__ble_flags == *s* ]]; then
    local __ble_sync_command=ble/syntax:bash/simple-word/eval/.eval-print
    local __ble_sync_opts=progressive-weight
    local __ble_sync_weight=$bleopt_syntax_eval_polling_interval
    local __ble_sync_timeout=$_ble_syntax_bash_simple_eval_timeout
    if [[ $_ble_syntax_bash_simple_eval_timeout_carry ]]; then
      __ble_sync_timeout=0
    elif ble/string#match ":$__ble_opts:" ':timeout=([^:]*):'; then
      __ble_sync_timeout=${BASH_REMATCH[1]}
    fi
    [[ $__ble_sync_timeout ]] &&
      __ble_sync_opts=$__ble_sync_opts:timeout=$((__ble_sync_timeout))
    local _ble_local_tmpfile; ble/util/assign/mktmp
    local __ble_simple_word_tmpfile=$_ble_local_tmpfile
    local __ble_script
    ble/util/assign __ble_script 'ble/util/conditional-sync "$__ble_sync_command" "" "$__ble_sync_weight" "$__ble_sync_opts"' &>/dev/null; local ext=$?
    builtin eval -- "$__ble_script"
    ble/util/assign/rmtmp
  else
    ble/syntax:bash/simple-word/eval/.eval-set; local ext=$?
  fi
  [[ $__ble_flags == *f* ]] && set +f
  return "$ext"
}
_ble_syntax_bash_simple_eval_hash=
function ble/syntax:bash/simple-word/eval/.cache-clear {
  ble/gdict#clear _ble_syntax_bash_simple_eval
  ble/gdict#clear _ble_syntax_bash_simple_eval_full
}
function ble/syntax:bash/simple-word/eval/.cache-update {
  local hash=$-:${BASHOPTS-}:$_ble_edit_lineno:$_ble_textarea_version:$PWD
  if [[ $hash != "$_ble_syntax_bash_simple_eval_hash" ]]; then
    _ble_syntax_bash_simple_eval_hash=$hash
    ble/syntax:bash/simple-word/eval/.cache-clear
  fi
}
function ble/syntax:bash/simple-word/eval/.cache-save {
  ((ext==148||ext==142)) && return 0
  local ret; ble/string#quote-words "$3"
  ble/gdict#set _ble_syntax_bash_simple_eval "$1" "ext=$2 count=$(($#-2)) ret=$ret"
  local ret; ble/string#quote-words "${@:3}"
  ble/gdict#set _ble_syntax_bash_simple_eval_full "$1" "ext=$2 count=$(($#-2)) ret=($ret)"
}
function ble/syntax:bash/simple-word/eval/.cache-load {
  ext= ret=
  if [[ :$2: == *:single:* ]]; then
    ble/gdict#get _ble_syntax_bash_simple_eval "$1" || return 1
  else
    ble/gdict#get _ble_syntax_bash_simple_eval_full "$1" || return 1
  fi
  builtin eval -- "$ret"
  return 0
}
_ble_syntax_bash_simple_eval_timeout=
_ble_syntax_bash_simple_eval_timeout_carry=
function ble/syntax:bash/simple-word/eval {
  [[ :$2: != *:count:* ]] && local count
  if [[ :$2: == *:cached:* && :$2: != *:noglob:* ]]; then
    ble/syntax:bash/simple-word/eval/.cache-update
    local ext; ble/syntax:bash/simple-word/eval/.cache-load "$1" "$2" && return "$ext"
  fi
  local __ble_ret
  ble/syntax:bash/simple-word/eval/.impl "$1" "$2"; local ext=$?
  ret=("${__ble_ret[@]}")
  count=${#ret[@]}
  if [[ :$2: == *:cached:* && :$2: != *:noglob:* ]]; then
    ble/syntax:bash/simple-word/eval/.cache-save "$1" "$ext" "${ret[@]}"
  fi
  if ((ext==142)); then
    [[ :$2: == *:timeout-carry:* ]] &&
      _ble_syntax_bash_simple_eval_timeout_carry=1
    if [[ :$2: == *:retry-noglob-on-timeout:* ]]; then
      ble/syntax:bash/simple-word/eval "$1" "$2:noglob"
      return "$?"
    fi
  fi
  return "$ext"
}
function ble/syntax:bash/simple-word/safe-eval {
  local __ble_opts=$2
  if [[ :$__ble_opts: == *:reconstruct:* ]]; then
    local simple_flags simple_ibrace
    ble/syntax:bash/simple-word/reconstruct-incomplete-word "$1" || return 1
    ble/util/unlocal simple_flags simple_ibrace
  else
    ble/syntax:bash/simple-word/is-simple "$1" || return 1
  fi
  [[ :$__ble_opts: == *:limit=*:* ]] ||
    __ble_opts=$__ble_opts:limit=1
  ble/syntax:bash/simple-word/eval "$1" "$__ble_opts" &&
    { [[ :$__ble_opts: != *:nonull:* ]] || ((${#ret[@]})); }
}
function ble/syntax:bash/simple-word/get-rex_element {
  local sep=$1
  local param=$_ble_syntax_bash_simple_rex_param
  local bquot=$_ble_syntax_bash_simple_rex_bquot
  local squot=$_ble_syntax_bash_simple_rex_squot
  local dquot=$_ble_syntax_bash_simple_rex_dquot
  local letter1='\[[!^]|[^'$sep$_ble_syntax_bashc_simple']'
  rex_element='('$bquot'|'$squot'|'$dquot'|'$param'|'$letter1')+'
}
function ble/syntax:bash/simple-word/evaluate-path-spec {
  local word=$1 sep=${2:-'/:='} opts=$3
  ret=() spec=() path=()
  [[ $word ]] || return 0
  local eval_opts=$opts notilde=
  [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制
  local fixlen
  ble/opts#extract-last-optarg "$opts" fixlen 0
  local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep"
  local rex='^['$sep']?'$rex_element'|^['$sep']'
  [[ :$opts: == *:after-sep:* ]] &&
    local rex='^'$rex_element'['$sep']?|^['$sep']'
  local tail=${word:fixlen} s=${word::fixlen} p= ext=0
  while [[ $tail =~ $rex ]]; do
    local rematch=$BASH_REMATCH
    s=$s$rematch
    ble/syntax:bash/simple-word/eval "$notilde$s" "$eval_opts"; ext=$?
    ((ext==148||ext==142)) && return "$ext"
    p=$ret
    tail=${tail:${#rematch}}
    ble/array#push spec "$s"
    ble/array#push path "$p"
  done
  [[ $tail ]] && return 1
  ((ext)) && return "$ext"
  return 0
}
function ble/syntax:bash/simple-word/detect-separated-path {
  local word=$1 sep=${2:-':'} opts=$3
  [[ $word ]] || return 1
  local rex_url='^[a-z]+://'
  [[ :$opts: == *:url:* ]] && ble/string#match-safe "$word" "$rex_url" && return 1
  local eval_opts=$opts notilde=
  [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制
  local rex_element
  ble/syntax:bash/simple-word/get-rex_element /
  local rex='^'$rex_element'/?|^/'
  local tail=$word head=
  while [[ $tail =~ $rex ]]; do
    local rematch=$BASH_REMATCH
    ble/syntax:bash/simple-word/locate-filename/.exists "$notilde$head$rematch"; local ext=$?
    ((ext==148)) && return 148
    ((ext==0)) || break
    head=$head$rematch
    tail=${tail:${#rematch}}
  done
  ret=
  local i
  for ((i=0;i<${#sep};i++)); do
    local sep1=${sep:i:1}
    ble/syntax:bash/simple-word/get-rex_element "$sep1"
    local rex_nocolon='^('$rex_element')?$'
    local rex_hascolon='^('$rex_element')?['$sep1']'
    [[ $head =~ $rex_nocolon && $tail =~ $rex_hascolon ]] && ret=$ret$sep1
  done
  [[ $ret ]]
}
function ble/syntax:bash/simple-word/locate-filename/.exists {
  local word=$1 ret
  ble/syntax:bash/simple-word/eval "$word" "$eval_opts" || return "$?"
  local path=$ret
  if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then
    [[ $path == // ]]
  else
    [[ -e $path || -h $path ]]
  fi || [[ :$opts: == *:url:* ]] && ble/string#match-safe "$path" "$rex_url"
}
function ble/syntax:bash/simple-word/locate-filename {
  local word=$1 sep=${2:-':='} opts=$3
  ret=0
  [[ $word ]] || return 0
  local eval_opts=$opts
  local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep"
  local rex='^'$rex_element'['$sep']|^['$sep']'
  local rex_url='^[a-z]+://'
  local -a seppos=()
  local tail=$word p=0
  while [[ $tail =~ $rex ]]; do
    ((p+=${#BASH_REMATCH}))
    tail=${tail:${#BASH_REMATCH}}
    ble/array#push seppos "$((p-1))"
  done
  ble/syntax:bash/simple-word/is-simple "$tail" &&
    ble/array#push seppos "$((p+${#tail}))"
  local -a out=()
  for ((i=0;i<${#seppos[@]};i++)); do
    local j0=$i
    [[ :$opts: == *:greedy:* ]] && j0=${#seppos[@]}-1
    local j
    for ((j=j0;j>=i;j--)); do
      local f1=0 f2=${seppos[j]}
      ((i)) && ((f1=seppos[i-1]+1))
      if ((j>i)); then
        ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts"; local ext=$?
        ((ext==148)) && return 148
        if ((ext==0)); then
          ble/array#push out "$f1" "$f2"
          ((i=j))
        fi
      else
        if [[ :$opts: != *:exists:* ]] ||
             { ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts"
               local ext=$?; ((ext==148)) && return 148; ((ext==0)); }; then
          ble/array#push out "$f1" "$f2"
        fi
      fi
    done
  done
  ret=("${out[@]}")
  return 0
}
function ble/syntax:bash/simple-word#break-word {
  local word=$1 sep=${2:-':='}
  if [[ ! $word ]]; then
    ret=('')
    return 0
  fi
  sep=${sep//[\"\'\$\`]}
  local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep"
  local rex='^('$rex_element')?['$sep']+'
  local -a out=()
  local tail=$word p=0
  while [[ $tail =~ $rex ]]; do
    local rematch1=${BASH_REMATCH[1]}
    ble/array#push out "$rematch1"
    ble/array#push out "${BASH_REMATCH:${#rematch1}}"
    tail=${tail:${#BASH_REMATCH}}
  done
  ble/array#push out "$tail"
  ret=("${out[@]}")
  return 0
}
function ble/syntax:bash/initialize-ctx {
  ctx=$_ble_ctx_CMDX # _ble_ctx_CMDX が ble/syntax:bash の最初の文脈
}
function ble/syntax:bash/initialize-vars {
  local histc12
  if [[ ${histchars+set} ]]; then
    histc12=${histchars::2}
  else
    histc12='!^'
  fi
  _ble_syntax_bash_histc12=$histc12
  if ble/syntax:bash/cclass/update; then
    ble/syntax:bash/simple-word/update
  fi
  local histstop=$' \t\n='
  shopt -q extglob && histstop="$histstop("
  _ble_syntax_bash_histstop=$histstop
}
function ble/variable#load-user-state/.print-global-state {
  local __ble_set= __ble_val= __ble_att=
  if [[ :$2: == *:unset:* ]]; then
    __ble_set=unknown
    __ble_val=unknown
    __ble_att=
  else
    __ble_set=${!1+set}
    __ble_val=${!1-}
    ble/variable#get-attr -v __ble_att "$1"
    __ble_att=${__ble_att//r}
  fi
  declare -p __ble_set __ble_val __ble_att
}
function ble/variable#load-user-state {
  __ble_var_set= __ble_var_val= __ble_var_att=
  [[ $1 == __ble_* || $1 == _ble_local_* ]] && return 0
  ble/function#try ble/variable#load-user-state/variable:"$1" && return 0
  if [[ $1 == '?' ]]; then
    __ble_var_set=set
    __ble_var_val=$_ble_edit_exec_lastexit
    __ble_var_att=
    return 0
  elif ble/string#match "$1" '^[1-9][0-9]*$'; then
    local __ble_name=$1
    if [[ ${_ble_edit_exec_lastparams[0]+set} ]]; then
      set -- "${_ble_edit_exec_lastparams[@]}"
    else
      set --
    fi
    __ble_var_set=${!__ble_name+set}
    __ble_var_val=${!__ble_name-}
    __ble_var_att=
    return 0
  fi
  if [[ :$2: == *:global:* ]] && ! ble/variable#is-global "$1"; then
    local _ble_highlight_vartype_name=$1
    local __ble_var_state
    ble/util/assign __ble_var_state 'ble/util/for-global-variables ble/variable#load-user-state/.print-global-state "" "$_ble_highlight_vartype_name"'
    if [[ $__ble_var_state ]]; then
      local __ble_set __ble_val __ble_att
      builtin eval -- "$__ble_var_state"
      __ble_var_set=$__ble_set
      __ble_var_val=$__ble_val
      __ble_var_att=$__ble_att
      return 0
    fi
  fi
  __ble_var_set=${!1+set}
  __ble_var_val=${!1-}
  ble/variable#get-attr -v __ble_var_att "$1"
}
function ble/syntax/highlight/vartype {
  ret=$_ble_attr_VAR
  [[ $bleopt_highlight_variable ]] || return 0
  local __ble_name=$1 __ble_opts=${2-} __ble_tail=${3-}
  local __ble_var_set __ble_var_val __ble_var_att
  ble/variable#load-user-state "$__ble_name" "$__ble_opts"
  if [[ $__ble_var_set || $__ble_var_att == *[aA]* ]]; then
    if [[ $__ble_var_val && :$__ble_opts: == *:expr:* ]] && ! ble/string#match "$__ble_var_val" '^-?[0-9]+(#[_a-zA-Z0-9@]*)?$'; then
      ret=$_ble_attr_VAR_EXPR
    elif [[ $__ble_var_set && $__ble_var_att == *x* ]]; then
      ret=$_ble_attr_VAR_EXPORT
    elif [[ $__ble_var_att == *a* ]]; then
      ret=$_ble_attr_VAR_ARRAY
    elif [[ $__ble_var_att == *A* ]]; then
      ret=$_ble_attr_VAR_HASH
    elif [[ $__ble_var_att == *r* ]]; then
      ret=$_ble_attr_VAR_READONLY
    elif [[ $__ble_var_att == *i* ]]; then
      ret=$_ble_attr_VAR_NUMBER
    elif [[ $__ble_var_att == *[luc]* ]]; then
      ret=$_ble_attr_VAR_TRANSFORM
    elif [[ ! $__ble_var_val ]]; then
      [[ $__ble_tail == :* ]] && lookahead=2
      if [[ $__ble_tail == ':?'* ]]; then
        ret=$_ble_attr_ERR
      else
        ret=$_ble_attr_VAR_EMPTY
      fi
    else
      ret=$_ble_attr_VAR
    fi
  else
    if [[ :$__ble_opts: == *:newvar:* ]]; then
      ret=$_ble_attr_VAR_NEW
      return 0
    elif
      [[ $__ble_tail == :* ]] && lookahead=2
      [[ $__ble_tail == ':?'* || $__ble_tail == '?'* ]]
    then
      ret=$_ble_attr_ERR
      return 0
    fi
    if [[ :$__ble_opts: == *:readvar:* && $_ble_bash_set == *u* ]]; then
      if [[ ! $__ble_tail ]] || {
           [[ $__ble_tail == :* ]] && lookahead=2
           ! ble/string#match "$__ble_tail" '^:?[-+?=]'; }
      then
        ret=$_ble_attr_ERR
        return 0
      fi
    fi
    ret=$_ble_attr_VAR_UNSET
  fi
  return 0
}
function ble/syntax:bash/check-plain-with-escape {
  local rex='^('$1'|\\.)' is_quote=$2
  [[ $tail =~ $rex ]] || return 1
  if [[ $BASH_REMATCH == '\'? &&
          ( ! $is_quote || $BASH_REMATCH == '\'[$'\\`$\n"'] ) ]]; then
    ((_ble_syntax_attr[i]=_ble_attr_QESC))
  else
    ((_ble_syntax_attr[i]=ctx))
  fi
  ((i+=${#BASH_REMATCH}))
  return 0
}
function ble/syntax:bash/check-dollar {
  [[ $tail == '$'* ]] || return 1
  local rex
  if [[ $tail == '${'* ]]; then
    local rex1='^(\$\{#)([-*@#?$!0]\}?|[1-9][0-9]*\}?|[_a-zA-Z][_a-zA-Z0-9]*[[}]?)'
    local rex2='^(\$\{!?)([-*@#?$!0]|[1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*\[?)'
    if
      [[ $tail =~ $rex1 ]] && {
        [[ ${BASH_REMATCH[2]} == *['[}'] || $BASH_REMATCH == "$tail" ]] ||
          { ble/syntax/parse/set-lookahead "$((${#BASH_REMATCH}+1))"; builtin false; } } ||
        [[ $tail =~ $rex2 ]]
    then
      local rematch1=${BASH_REMATCH[1]}
      local rematch2=${BASH_REMATCH[2]}
      local varname=${rematch2%['[}']}
      local ntype='${'
      if ((ctx==_ble_ctx_QUOT)); then
        ntype='"${'
      elif ((ctx==_ble_ctx_PWORD||ctx==_ble_ctx_PWORDE||ctx==_ble_ctx_PWORDR||ctx==_ble_ctx_EXPR)); then
        local ntype2; ble/syntax/parse/nest-type -v ntype2
        [[ $ntype2 == '"${' ]] && ntype='"${'
      fi
      local ret lookahead= tail2=${tail:${#rematch1}+${#varname}}
      ble/syntax/highlight/vartype "$varname" readvar:global "$tail2"; local attr=$ret
      ble/syntax/parse/nest-push "$_ble_ctx_PARAM" "$ntype"
      ((_ble_syntax_attr[i]=ctx,
        i+=${#rematch1},
        _ble_syntax_attr[i]=attr,
        i+=${#varname}))
      [[ $lookahead ]] && ble/syntax/parse/set-lookahead "$lookahead"
      if rex='^\$\{![_a-zA-Z][_a-zA-Z0-9]*[*@]\}?'; [[ $tail =~ $rex ]]; then
        ble/syntax/parse/set-lookahead 2
        if [[ $BASH_REMATCH == *'}' ]]; then
          ((i++,ctx=_ble_ctx_PWORDE))
        fi
      elif [[ $rematch2 == *'[' ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_EXPR" 'v['
        ((_ble_syntax_attr[i++]=_ble_ctx_EXPR))
      fi
      return 0
    elif ((_ble_bash>=50300)) && [[ $tail == '${'[$' \t\n|']* ]]; then
      ((_ble_syntax_attr[i]=_ble_ctx_PARAM))
      ble/syntax/parse/nest-push "$_ble_ctx_CMDX" 'cmdsub_nofork'
      ((i+=2))
      [[ $tail == '${|'* ]] && ((i++))
      return 0
    else
      ((_ble_syntax_attr[i]=_ble_attr_ERR,i+=2))
      return 0
    fi
  elif [[ $tail == '$(('* ]]; then
    ((_ble_syntax_attr[i]=_ble_ctx_PARAM))
    ble/syntax/parse/nest-push "$_ble_ctx_EXPR" '$(('
    ((i+=3))
    return 0
  elif [[ $tail == '$['* ]]; then
    ((_ble_syntax_attr[i]=_ble_ctx_PARAM))
    ble/syntax/parse/nest-push "$_ble_ctx_EXPR" '$['
    ((i+=2))
    return 0
  elif [[ $tail == '$('* ]]; then
    ((_ble_syntax_attr[i]=_ble_ctx_PARAM))
    ble/syntax/parse/nest-push "$_ble_ctx_CMDX" '$('
    ((i+=2))
    return 0
  elif rex='^\$([-*@#?$!0_]|[1-9]|[_a-zA-Z][_a-zA-Z0-9]*)' && [[ $tail =~ $rex ]]; then
    local rematch=$BASH_REMATCH rematch1=${BASH_REMATCH[1]}
    ((_ble_syntax_attr[i++]=_ble_ctx_PARAM))
    if ((_ble_bash<40200)) && local tail=${tail:1} &&
         ble/syntax:bash/starts-with-histchars && ble/syntax:bash/check-history-expansion; then
      return 0
    else
      local ret; ble/syntax/highlight/vartype "$rematch1" readvar:global
      ((_ble_syntax_attr[i]=ret,i+=${#rematch}-1))
      return 0
    fi
  else
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
}
function ble/syntax:bash/check-quotes {
  local rex aqdel=$_ble_attr_QDEL aquot=$_ble_ctx_QUOT
  if ((ctx==_ble_ctx_EXPR)) && [[ $tail != \`* ]]; then
    local ntype
    ble/syntax/parse/nest-type
    case $ntype in
    ('${')
      if ((_ble_bash<50200)) || [[ $tail == \'* || $tail == \$\'* ]]; then
        ((aqdel=_ble_attr_ERR,aquot=_ble_ctx_EXPR))
      fi ;;
    ('$['|'$(('|expr-paren-ax)
      if [[ $tail == \'* ]] || ((_ble_bash<40400)); then
        ((aqdel=_ble_attr_ERR,aquot=_ble_ctx_EXPR))
      fi ;;
    ('(('|expr-paren|expr-brack)
      if [[ $tail == \'* ]] && ((_ble_bash>=50100)); then
        ((aqdel=_ble_attr_ERR,aquot=_ble_ctx_EXPR))
      fi ;;
    ('a['|'v['|expr-paren-ai|expr-brack-ai)
      if [[ $tail == \'* ]] && ((_ble_bash>=40400)); then
        ((aqdel=_ble_attr_ERR,aquot=_ble_ctx_EXPR))
      fi ;;
    ('"${')
      if ! { [[ $tail == '$'[\'\"]* ]] && shopt -q extquote; }; then
        ((aqdel=_ble_attr_ERR,aquot=_ble_ctx_EXPR))
      fi ;;
    esac
  elif ((ctx==_ble_ctx_PWORD||ctx==_ble_ctx_PWORDE||ctx==_ble_ctx_PWORDR)); then
    if [[ $tail == '$'[\'\"]* ]] && ! shopt -q extquote; then
      local ntype
      ble/syntax/parse/nest-type
      if [[ $ntype == '"${' ]]; then
        ((aqdel=ctx,aquot=ctx))
      fi
    fi
  fi
  if rex='^`([^`\]|\\(.|$))*(`?)|^'\''[^'\'']*('\''?)' && [[ $tail =~ $rex ]]; then
    ((_ble_syntax_attr[i]=aqdel,
      _ble_syntax_attr[i+1]=aquot,
      i+=${#BASH_REMATCH},
      _ble_syntax_attr[i-1]=${#BASH_REMATCH[3]}||${#BASH_REMATCH[4]}?aqdel:_ble_attr_ERR))
    return 0
  fi
  if ((ctx!=_ble_ctx_QUOT)); then
    if rex='^(\$?")([^'"${_ble_syntax_bash_chars[_ble_ctx_QUOT]}"']*)("?)' && [[ $tail =~ $rex ]]; then
      local rematch1=${BASH_REMATCH[1]} # for bash-3.1 ${#arr[n]} bug
      if [[ ${BASH_REMATCH[3]} ]]; then
        ((_ble_syntax_attr[i]=aqdel,
          _ble_syntax_attr[i+${#rematch1}]=aquot,
          i+=${#BASH_REMATCH},
          _ble_syntax_attr[i-1]=aqdel))
      else
        ble/syntax/parse/nest-push "$_ble_ctx_QUOT"
        if (((ctx==_ble_ctx_PWORD||ctx==_ble_ctx_PWORDE||ctx==_ble_ctx_PWORDR)&&aqdel!=_ble_attr_QDEL)); then
          ((_ble_syntax_attr[i]=aqdel,
            _ble_syntax_attr[i+${#rematch1}-1]=_ble_attr_QDEL,
            _ble_syntax_attr[i+${#rematch1}]=_ble_ctx_QUOT,
            i+=${#BASH_REMATCH}))
        else
          ((_ble_syntax_attr[i]=aqdel,
            _ble_syntax_attr[i+${#rematch1}]=_ble_ctx_QUOT,
            i+=${#BASH_REMATCH}))
        fi
      fi
      return 0
    elif rex='^\$'\''(([^'\''\]|\\(.|$))*)('\''?)' && [[ $tail =~ $rex ]]; then
      ((_ble_syntax_attr[i]=aqdel,i+=2))
      local t=${BASH_REMATCH[1]} rematch4=${BASH_REMATCH[4]}
      local rex='\\[abefnrtvE"'\''\?]|\\[0-7]{1,3}|\\c.|\\x[0-9a-fA-F]{1,2}'
      ((_ble_bash>=40200)) && rex=$rex'|\\u[0-9a-fA-F]{1,4}|\\U[0-9a-fA-F]{1,8}'
      local rex='^([^'\''\]*)('$rex'|(\\.))'
      while [[ $t =~ $rex ]]; do
        local m1=${BASH_REMATCH[1]} m2=${BASH_REMATCH[2]}
        [[ $m1 ]] && ((_ble_syntax_attr[i]=aquot,i+=${#m1}))
        if [[ ${BASH_REMATCH[3]} ]]; then
          ((_ble_syntax_attr[i]=aquot))
        else
          ((_ble_syntax_attr[i]=_ble_attr_QESC))
        fi
        ((i+=${#m2}))
        t=${t:${#BASH_REMATCH}}
      done
      [[ $t ]] && ((_ble_syntax_attr[i]=aquot,i+=${#t}))
      if [[ $rematch4 ]]; then
        ((_ble_syntax_attr[i++]=aqdel))
      else
        ((_ble_syntax_attr[i-1]=_ble_attr_ERR))
      fi
      return 0
    fi
  fi
  return 1
}
function ble/syntax:bash/check-process-subst {
  if [[ $tail == ['<>']'('* ]]; then
    ble/syntax/parse/nest-push "$_ble_ctx_CMDX" '('
    ((_ble_syntax_attr[i]=_ble_attr_DEL,i+=2))
    return 0
  fi
  return 1
}
function ble/syntax:bash/check-comment {
  if shopt -q interactive_comments; then
    if ((wbegin<0||wbegin==i)) && local rex=$'^#[^\n]*' && [[ $tail =~ $rex ]]; then
      ((_ble_syntax_attr[i]=_ble_attr_COMMENT,
        i+=${#BASH_REMATCH}))
      return 0
    fi
  fi
  return 1
}
function ble/syntax:bash/check-glob {
  [[ $tail == ['[?*@+!()|']* ]] || return 1
  local ntype= force_attr=
  if ((ctx==_ble_ctx_VRHS||ctx==_ble_ctx_ARGVR||ctx==_ble_ctx_ARGER||ctx==_ble_ctx_VALR||ctx==_ble_ctx_RDRS)); then
    force_attr=$ctx
    ntype="glob_attr=$force_attr"
  elif ((ctx==_ble_ctx_FARGX1||ctx==_ble_ctx_FARGI1)); then
    force_attr=$_ble_attr_ERR
    ntype="glob_attr=$force_attr"
  elif ((ctx==_ble_ctx_PWORD||ctx==_ble_ctx_PWORDE||ctx==_ble_ctx_PWORDR)); then
    ntype="glob_ctx=$ctx"
  elif ((ctx==_ble_ctx_PATN||ctx==_ble_ctx_BRAX)); then
    ble/syntax/parse/nest-type
    local exit_attr=
    if [[ $ntype == glob_attr=* ]]; then
      force_attr=${ntype#*=}
      exit_attr=$force_attr
    elif ((ctx==_ble_ctx_BRAX)); then
      force_attr=$ctx
      ntype="glob_attr=$force_attr"
    elif ((ctx==_ble_ctx_PATN)); then
      ((exit_attr=_ble_syntax_attr[inest]))
      [[ $ntype != glob_ctx=* ]] && ntype=
    else
      ntype=
    fi
  elif [[ $1 == assign ]]; then
    ntype='a['
  fi
  if [[ $tail == ['?*@+!']'('* ]] && shopt -q extglob; then
    ble/syntax/parse/nest-push "$_ble_ctx_PATN" "$ntype"
    ((_ble_syntax_attr[i]=${force_attr:-_ble_attr_GLOB},i+=2))
    return 0
  fi
  local histc1=${_ble_syntax_bash_histc12::1}
  [[ $histc1 && $tail == "$histc1"* ]] && return 1
  if [[ $tail == '['* ]]; then
    if ((ctx==_ble_ctx_BRAX)); then
      ((_ble_syntax_attr[i++]=force_attr))
      [[ $tail == '[!'* ]] && ((i++))
      return 0
    fi
    ble/syntax/parse/nest-push "$_ble_ctx_BRAX" "$ntype"
    ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_GLOB}))
    [[ $tail == '[!'* ]] && ((i++))
    if [[ ${text:i:1} == ']' ]]; then
      ((_ble_syntax_attr[i++]=${force_attr:-_ble_ctx_BRAX}))
    elif [[ ${text:i:1} == '[' ]]; then
      if [[ ${text:i+1:1} == [:=.] ]]; then
        ble/syntax/parse/set-lookahead 2
      else
        ((_ble_syntax_attr[i++]=${force_attr:-_ble_ctx_BRAX}))
        [[ ${text:i:1} == '!'* ]] && ((i++))
      fi
    fi
    return 0
  elif [[ $tail == ['?*']* ]]; then
    ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_GLOB}))
    return 0
  elif [[ $tail == ['@+!']* ]]; then
    ((_ble_syntax_attr[i++]=${force_attr:-ctx}))
    return 0
  elif ((ctx==_ble_ctx_PATN||ctx==_ble_ctx_BRAX)); then
    if [[ $tail == '('* ]]; then
      ble/syntax/parse/nest-push "$_ble_ctx_PATN" "$ntype"
      ((_ble_syntax_attr[i++]=${force_attr:-ctx}))
      return 0
    elif [[ $tail == ')'* ]]; then
      if ((ctx==_ble_ctx_PATN)); then
        ((_ble_syntax_attr[i++]=exit_attr))
        ble/syntax/parse/nest-pop
      else
        ((_ble_syntax_attr[i++]=${force_attr:-ctx}))
      fi
      return 0
    elif [[ $tail == '|'* ]]; then
      ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_GLOB}))
      return 0
    fi
  fi
  return 1
}
_ble_syntax_bash_histexpand_RexWord=
_ble_syntax_bash_histexpand_RexMods=
_ble_syntax_bash_histexpand_RexEventDef=
_ble_syntax_bash_histexpand_RexQuicksubDef=
_ble_syntax_bash_histexpand_RexEventFmt=
_ble_syntax_bash_histexpand_RexQuicksubFmt=
function ble/syntax:bash/check-history-expansion/.initialize {
  local spaces=$_ble_term_IFS nl=$'\n'
  local rex_event='-?[0-9]+|[!#]|[^-$^*%:'$spaces'=?!#;&|<>()]+|\?[^?'$nl']*\??'
  _ble_syntax_bash_histexpand_RexEventDef='^!('$rex_event')'
  local rex_word1='([0-9]+|[$%^])'
  local rex_wordsA=':('$rex_word1'?-'$rex_word1'?|\*|'$rex_word1'\*?)'
  local rex_wordsB='([$%^]?-'$rex_word1'?|\*|[$^%][*-]?)'
  _ble_syntax_bash_histexpand_RexWord='('$rex_wordsA'|'$rex_wordsB')?'
  local rex_modifier=':[htrepqx]|:[gGa]?&|:[gGa]?s(/([^\/]|\\.)*){0,2}(/|$)'
  _ble_syntax_bash_histexpand_RexMods='('$rex_modifier')*'
  _ble_syntax_bash_histexpand_RexQuicksubDef='\^([^^\]|\\.)*\^([^^\]|\\.)*\^'
  _ble_syntax_bash_histexpand_RexQuicksubFmt='@A([^@C\]|\\.)*@A([^@C\]|\\.)*@A'
  _ble_syntax_bash_histexpand_RexEventFmt='^@A('$rex_event'|@A)'
}
ble/syntax:bash/check-history-expansion/.initialize
function ble/syntax:bash/check-history-expansion/.initialize-event {
  local histc1=${_ble_syntax_bash_histc12::1}
  if [[ $histc1 == '!' ]]; then
    rex_event=$_ble_syntax_bash_histexpand_RexEventDef
  else
    local A="[$histc1]"
    [[ $histc1 == '^' ]] && A='\^'
    rex_event=$_ble_syntax_bash_histexpand_RexEventFmt
    rex_event=${rex_event//@A/"$A"}
  fi
}
function ble/syntax:bash/check-history-expansion/.initialize-quicksub {
  local histc2=${_ble_syntax_bash_histc12:1:1}
  if [[ $histc2 == '^' ]]; then
    rex_quicksub=$_ble_syntax_bash_histexpand_RexQuicksubDef
  else
    rex_quicksub=$_ble_syntax_bash_histexpand_RexQuicksubFmt
    rex_quicksub=${rex_quicksub//@A/"[$histc2]"}
    rex_quicksub=${rex_quicksub//@C/"$histc2"}
  fi
}
function ble/syntax:bash/check-history-expansion/.check-modifiers {
  [[ ${text:i} =~ $_ble_syntax_bash_histexpand_RexMods ]] &&
    ((i+=${#BASH_REMATCH}))
  if local rex='^:[gGa]?s(.)'; [[ ${text:i} =~ $rex ]]; then
    local del=${BASH_REMATCH[1]}
    local A="[$del]" B="[^$del]"
    [[ $del == '^' || $del == ']' ]] && A='\'$del
    [[ $del != '\' ]] && B=$B'|\\.'
    local rex_substitute='^:[gGa]?s('$A'('$B')*){0,2}('$A'|$)'
    if [[ ${text:i} =~ $rex_substitute ]]; then
      ((i+=${#BASH_REMATCH}))
      ble/syntax:bash/check-history-expansion/.check-modifiers
      return 0
    fi
  fi
  if [[ ${text:i} == ':'[gGa]* ]]; then
    ((_ble_syntax_attr[i+1]=_ble_attr_ERR,i+=2))
  elif [[ ${text:i} == ':'* ]]; then
    ((_ble_syntax_attr[i]=_ble_attr_ERR,i++))
  fi
}
function ble/syntax:bash/check-history-expansion {
  [[ -o histexpand ]] || return 1
  local histc1=${_ble_syntax_bash_histc12:0:1}
  local histc2=${_ble_syntax_bash_histc12:1:1}
  if [[ $histc1 && $tail == "$histc1"[^"$_ble_syntax_bash_histstop"]* ]]; then
    if ((_ble_bash>=40300&&ctx==_ble_ctx_QUOT)); then
      local tail=${tail%%'"'*}
      [[ $tail == '!' ]] && return 1
    fi
    ((_ble_syntax_attr[i]=_ble_attr_HISTX))
    local rex_event
    ble/syntax:bash/check-history-expansion/.initialize-event
    if [[ $tail =~ $rex_event ]]; then
      ((i+=${#BASH_REMATCH}))
    elif [[ $tail == "$histc1"['-:0-9^$%*']* ]]; then
      ((_ble_syntax_attr[i]=_ble_attr_HISTX,i++))
    else
      ((_ble_syntax_attr[i+1]=_ble_attr_ERR,i+=2))
      return 0
    fi
    [[ ${text:i} =~ $_ble_syntax_bash_histexpand_RexWord ]] &&
      ((i+=${#BASH_REMATCH}))
    ble/syntax:bash/check-history-expansion/.check-modifiers
    return 0
  elif ((i==0)) && [[ $histc2 && $tail == "$histc2"* ]]; then
    ((_ble_syntax_attr[i]=_ble_attr_HISTX))
    local rex_quicksub
    ble/syntax:bash/check-history-expansion/.initialize-quicksub
    if [[ $tail =~ $rex_quicksub ]]; then
      ((i+=${#BASH_REMATCH}))
      ble/syntax:bash/check-history-expansion/.check-modifiers
      return 0
    else
      ((i+=${#tail}))
      return 0
    fi
  fi
  return 1
}
function ble/syntax:bash/starts-with-histchars {
  [[ $_ble_syntax_bash_histc12 && $tail == ["$_ble_syntax_bash_histc12"]* ]]
}
_ble_syntax_context_proc[_ble_ctx_QUOT]=ble/syntax:bash/ctx-quot
function ble/syntax:bash/ctx-quot {
  if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[_ble_ctx_QUOT]}]+" 1; then
    return 0
  elif [[ $tail == '"'* ]]; then
    ((_ble_syntax_attr[i]=_ble_attr_QDEL,
      i+=1))
    ble/syntax/parse/nest-pop
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
_ble_syntax_context_proc[_ble_ctx_CASE]=ble/syntax:bash/ctx-case
function ble/syntax:bash/ctx-case {
  if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then
    ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
    return 0
  elif [[ $tail == '('* ]]; then
    ((_ble_syntax_attr[i++]=_ble_attr_GLOB,ctx=_ble_ctx_CPATX))
    return 0
  elif [[ $tail == 'esac'$_ble_syntax_bash_RexDelimiter* || $tail == 'esac' ]]; then
    ((ctx=_ble_ctx_CMDX))
    ble/syntax:bash/ctx-command
  else
    ((ctx=_ble_ctx_CPATX))
    ble/syntax:bash/ctx-command-case-pattern-expect
  fi
}
_ble_syntax_context_proc[_ble_ctx_PATN]=ble/syntax:bash/ctx-globpat
_ble_syntax_context_end[_ble_ctx_PATN]=ble/syntax:bash/ctx-globpat.end
function ble/syntax:bash/ctx-globpat/get-stop-chars {
  chars=${_ble_syntax_bash_chars[_ble_ctx_PATN]}
  local ntype; ble/syntax/parse/nest-type
  if [[ $ntype == glob_ctx=* ]]; then
    local gctx=${ntype#glob_ctx=}
    if ((gctx==_ble_ctx_PWORD||gctx==_ble_ctx_PWORDE)); then
      chars=}$chars
    elif ((gctx==_ble_ctx_PWORDR)); then
      chars=}/$chars
    fi
  fi
}
function ble/syntax:bash/ctx-globpat {
  local chars; ble/syntax:bash/ctx-globpat/get-stop-chars
  if ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif [[ $tail == ['<>']* ]]; then
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-globpat.end {
  local is_end= tail=${text:i}
  local ntype; ble/syntax/parse/nest-type
  if [[ $ntype == glob_ctx=* ]]; then
    local gctx=${ntype#glob_ctx=}
    if ((gctx==_ble_ctx_PWORD||gctx==_ble_ctx_PWORDE)); then
      [[ ! $tail || $tail == '}'* ]] && is_end=1
    elif ((gctx==_ble_ctx_PWORDR)); then
      [[ ! $tail || $tail == ['/}']* ]] && is_end=1
    fi
  fi
  if [[ $is_end ]]; then
    ble/syntax/parse/nest-pop
    ble/syntax/parse/check-end
    return 0
  fi
  return 0
}
_ble_syntax_context_proc[_ble_ctx_BRAX]=ble/syntax:bash/ctx-bracket-expression
_ble_syntax_context_end[_ble_ctx_BRAX]=ble/syntax:bash/ctx-bracket-expression.end
function ble/syntax:bash/ctx-bracket-expression {
  local nctx; ble/syntax/parse/nest-ctx
  if ((nctx==_ble_ctx_PATN)); then
    local chars; ble/syntax:bash/ctx-globpat/get-stop-chars
  elif ((nctx==_ble_ctx_PWORD||nctx==_ble_ctx_PWORDE||nctx==_ble_ctx_PWORDR)); then
    local chars=${_ble_syntax_bash_chars[nctx]}
  else
    local chars=${_ble_syntax_bash_chars[_ble_ctx_ARGI]//'~'}
  fi
  chars="][${chars#']'}"
  local ntype; ble/syntax/parse/nest-type
  local force_attr=; [[ $ntype == glob_attr=* ]] && force_attr=${ntype#*=}
  local rex
  if [[ $tail == ']'* ]]; then
    ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_GLOB}))
    ble/syntax/parse/nest-pop
    if [[ $ntype == 'a[' ]]; then
      local is_assign=
      if [[ $tail == ']='* ]]; then
        ((_ble_syntax_attr[i++]=ctx,is_assign=1))
      elif [[ $tail == ']+'* ]]; then
        ble/syntax/parse/set-lookahead 2
        [[ $tail == ']+='* ]] && ((_ble_syntax_attr[i]=ctx,i+=2,is_assign=1))
      fi
      if [[ $is_assign ]]; then
        ble/util/assert '[[ ${_ble_syntax_bash_command_CtxAssign[ctx]} ]]'
        ((ctx=_ble_syntax_bash_command_CtxAssign[ctx]))
        if local tail=${text:i}; [[ $tail == '~'* ]]; then
          ble/syntax:bash/check-tilde-expansion rhs
        fi
      fi
    fi
    return 0
  elif [[ $tail == '['* ]]; then
    rex='^\[@([^'$chars']+(@\]?)?)?'
    rex=${rex//@/:}'|'${rex//@/'\.'}'|'${rex//@/=}'|^\['
    [[ $tail =~ $rex ]]
    ((_ble_syntax_attr[i]=${force_attr:-ctx},
      i+=${#BASH_REMATCH}))
    return 0
  elif ctx=${force_attr:-$ctx} ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/check-tilde-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i++]=${force_attr:-ctx}))
    return 0
  elif ((nctx==_ble_ctx_PATN)) && [[ $tail == ['<>']* ]]; then
    ((_ble_syntax_attr[i++]=${force_attr:-ctx}))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-bracket-expression.end {
  local is_end=
  local tail=${text:i}
  if [[ ! $tail ]]; then
    is_end=1
  else
    local nctx; ble/syntax/parse/nest-ctx
    local external_ctx=$nctx
    if ((nctx==_ble_ctx_PATN)); then
      local ntype; ble/syntax/parse/nest-type
      [[ $ntype == glob_ctx=* ]] &&
        external_ctx=${ntype#glob_ctx=}
    fi
    if ((external_ctx==_ble_ctx_PATN)); then
      [[ $tail == ')'* ]] && is_end=1
    elif ((external_ctx==_ble_ctx_PWORD||external_ctx==_ble_ctx_PWORDE)); then
      [[ $tail == '}'* ]] && is_end=1
    elif ((external_ctx==_ble_ctx_PWORDR)); then
      [[ $tail == ['}/']* ]] && is_end=1
    else
      if ble/syntax:bash/check-word-end/is-delimiter; then
        is_end=1
      elif [[ $tail == ':'* && ${_ble_syntax_bash_command_IsAssign[ctx]} ]]; then
        is_end=1
      fi
    fi
  fi
  if [[ $is_end ]]; then
    ble/syntax/parse/nest-pop
    ble/syntax/parse/check-end
    return "$?"
  fi
  return 0
}
_ble_syntax_context_proc[_ble_ctx_PARAM]=ble/syntax:bash/ctx-param
_ble_syntax_context_proc[_ble_ctx_PWORD]=ble/syntax:bash/ctx-pword
_ble_syntax_context_proc[_ble_ctx_PWORDR]=ble/syntax:bash/ctx-pword
_ble_syntax_context_proc[_ble_ctx_PWORDE]=ble/syntax:bash/ctx-pword-error
function ble/syntax:bash/ctx-param {
  if [[ $tail == '}'* ]]; then
    ((_ble_syntax_attr[i]=_ble_syntax_attr[inest]))
    ((i+=1))
    ble/syntax/parse/nest-pop
    return 0
  fi
  local rex='##?|%%?|:?[-?=+]|:|/[/#%]?'
  ((_ble_bash>=40000)) && rex=$rex'|,,?|\^\^?|~~?'
  if ((_ble_bash>=50200)); then
    rex=$rex'|@[QEPAaUuLKk]?'
  elif ((_ble_bash>=50100)); then
    rex=$rex'|@[QEPAaUuLK]?'
  elif ((_ble_bash>=40400)); then
    rex=$rex'|@[QEPAa]?'
  fi
  rex='^('$rex')'
  if [[ $tail =~ $rex ]]; then
    ((_ble_syntax_attr[i]=_ble_ctx_PARAM,
      i+=${#BASH_REMATCH}))
    if [[ $BASH_REMATCH == '/'* ]]; then
      ((ctx=_ble_ctx_PWORDR))
    elif [[ $BASH_REMATCH == : ]]; then
      ((ctx=_ble_ctx_EXPR,_ble_syntax_attr[i-1]=_ble_ctx_EXPR))
    elif [[ $BASH_REMATCH == @* ]]; then
      ((ctx=_ble_ctx_PWORDE))
    else
      ((ctx=_ble_ctx_PWORD))
      [[ $BASH_REMATCH == [':-+=?#%']* ]] &&
        tail=${text:i} ble/syntax:bash/check-tilde-expansion pword
    fi
    return 0
  else
    local i0=$i
    ((ctx=_ble_ctx_PWORD))
    ble/syntax:bash/ctx-pword || return 1
    if ((i0+2<=i)); then
      ((_ble_syntax_attr[i0+1])) ||
        ((_ble_syntax_attr[i0+1]=_ble_syntax_attr[i0]))
    fi
    ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    return 0
  fi
}
function ble/syntax:bash/ctx-pword {
  if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[ctx]}]+"; then
    return 0
  elif ((ctx==_ble_ctx_PWORDR)) && [[ $tail == '/'* ]]; then
    ((_ble_syntax_attr[i++]=_ble_ctx_PARAM,ctx=_ble_ctx_PWORD))
    return 0
  elif [[ $tail == '}'* ]]; then
    ((_ble_syntax_attr[i]=_ble_syntax_attr[inest]))
    ((i+=1))
    ble/syntax/parse/nest-pop
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-pword-error {
  local i0=$i
  if ble/syntax:bash/ctx-pword; then
    [[ $tail == '}'* ]] ||
      ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    return 0
  else
    return 1
  fi
}
_ble_syntax_context_proc[_ble_ctx_EXPR]=ble/syntax:bash/ctx-expr
function ble/syntax:bash/ctx-expr/.count-paren {
  if [[ $char == ')' ]]; then
    if [[ $ntype == '((' || $ntype == '$((' ]]; then
      if [[ $tail == '))'* ]]; then
        ((_ble_syntax_attr[i]=_ble_syntax_attr[inest]))
        ((i+=2))
        ble/syntax/parse/nest-pop
      else
        ((ctx=_ble_ctx_ARGX0,
          _ble_syntax_attr[i++]=_ble_syntax_attr[inest]))
      fi
      return 0
    elif [[ $ntype == expr-paren* ]]; then
      ((_ble_syntax_attr[i++]=ctx))
      ble/syntax/parse/nest-pop
      return 0
    fi
  elif [[ $char == '(' ]]; then
    local ntype2=
    case $ntype in
    ('((')
      ntype2=expr-paren ;;
    ('$((')
      ntype2=expr-paren-ax ;;
    (expr-paren|expr-paren-ax|expr-paren-ai|expr-paren-di)
      ntype2=$ntype ;;
    ('$['|'a['|'v['|'d['|expr-brack|expr-brack-ai|expr-brack-di|'${'|'"${'|*)
      ble/util/assert-fail "unexpected ntype='$ntype' here" ;;
    esac
    ble/syntax/parse/nest-push "$_ble_ctx_EXPR" "$ntype2"
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-expr/.count-bracket {
  if [[ $char == ']' ]]; then
    if [[ $ntype == expr-brack* || $ntype == '$[' ]]; then
      ((_ble_syntax_attr[i]=_ble_syntax_attr[inest]))
      ((i++))
      ble/syntax/parse/nest-pop
      return 0
    elif [[ $ntype == [ad]'[' ]]; then
      ((_ble_syntax_attr[i++]=_ble_ctx_EXPR))
      ble/syntax/parse/nest-pop
      if [[ $tail == ']='* ]]; then
        ((i++))
        tail=${text:i} ble/syntax:bash/check-tilde-expansion rhs
      elif ((_ble_bash>=30100)) && [[ $tail == ']+'* ]]; then
        ble/syntax/parse/set-lookahead 2
        if [[ $tail == ']+='* ]]; then
          ((i+=2))
          tail=${text:i} ble/syntax:bash/check-tilde-expansion rhs
        fi
      else
        if [[ $ntype == 'a[' ]]; then
          if ((ctx==_ble_ctx_VRHS)); then
            ((ctx=_ble_ctx_CMDI,wtype=_ble_ctx_CMDI))
          elif ((ctx==_ble_ctx_ARGVR)); then
            ((ctx=_ble_ctx_ARGVI,wtype=_ble_ctx_ARGVI))
          elif ((ctx==_ble_ctx_ARGER)); then
            ((ctx=_ble_ctx_ARGEI,wtype=_ble_ctx_ARGEI))
          fi
        else # ntype == 'd['
          ((ctx=_ble_ctx_VALI,wtype=_ble_ctx_VALI))
        fi
      fi
      return 0
    elif [[ $ntype == 'v[' ]]; then
      ((_ble_syntax_attr[i++]=_ble_ctx_EXPR))
      ble/syntax/parse/nest-pop
      return 0
    fi
  elif [[ $char == '[' ]]; then
    local ntype2=
    case $ntype in
    ('$['|'a['|'v[')
      ntype2=expr-brack-ai ;;
    ('d[')
      ntype2=expr-brack-di ;;
    (expr-brack|expr-brack-ai|expr-brack-di)
      ntype2=$ntype ;;
    ('(('|'$(('|expr-paren|expr-paren-ax|expr-paren-ai|expr-paren-di|'${'|'"${'|*)
      ble/util/assert-fail "unexpected ntype='$ntype' here" ;;
    esac
    ble/syntax/parse/nest-push "$_ble_ctx_EXPR" "$ntype2"
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-expr/.count-brace {
  if [[ $char == '}' ]]; then
    ((_ble_syntax_attr[i]=_ble_syntax_attr[inest]))
    ((i++))
    ble/syntax/parse/nest-pop
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-expr/.check-plain-with-escape {
  local i0=$i
  ble/syntax:bash/check-plain-with-escape "$@" || return 1
  if [[ $tail == '\'* ]]; then
    case $ntype in
    ('$(('|'$['|expr-paren-ax|'${'|'"${')
      _ble_syntax_attr[i0]=$_ble_attr_ERR ;;
    ('(('|expr-paren|expr-brack)
      if ((_ble_bash>=50100)); then
        _ble_syntax_attr[i0]=$_ble_attr_ERR
      fi ;;
    ('a['|'v['|expr-paren-ai|expr-brack-ai)
      if ((_ble_bash>=40400)); then
        _ble_syntax_attr[i0]=$_ble_attr_ERR
      fi ;;
    esac
  fi
  return 0
}
function ble/syntax:bash/ctx-expr {
  local rex
  if rex='^[_a-zA-Z][_a-zA-Z0-9]*'; [[ $tail =~ $rex ]]; then
    local rematch=$BASH_REMATCH
    local ret; ble/syntax/highlight/vartype "$BASH_REMATCH" readvar:expr:global
    ((_ble_syntax_attr[i]=ret,i+=${#rematch}))
    return 0
  elif rex='^0[xX][0-9a-fA-F]*|^[0-9]+(#[_a-zA-Z0-9@]*)?'; [[ $tail =~ $rex ]]; then
    ((_ble_syntax_attr[i]=_ble_attr_VAR_NUMBER,i+=${#BASH_REMATCH}))
    return 0
  fi
  local ntype
  ble/syntax/parse/nest-type
  if ble/syntax:bash/ctx-expr/.check-plain-with-escape "[^${_ble_syntax_bash_chars[ctx]}_a-zA-Z0-9]+" 1; then
    return 0
  elif [[ $tail == ['][()}']* ]]; then
    local char=${tail::1}
    if [[ $ntype == *'(' || $ntype == expr-paren* ]]; then
      ble/syntax:bash/ctx-expr/.count-paren && return 0
    elif [[ $ntype == *'[' || $ntype == expr-brack* ]]; then
      ble/syntax:bash/ctx-expr/.count-bracket && return 0
    elif [[ $ntype == '${' || $ntype == '"${' ]]; then
      ble/syntax:bash/ctx-expr/.count-brace && return 0
    else
      ble/util/assert-fail "unexpected ntype=$ntype for arithmetic expression"
    fi
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
function ble/syntax:bash/check-brace-expansion {
  [[ $tail == '{'* ]] || return 1
  local rex='^\{[-+a-zA-Z0-9.]*(\}?)'
  [[ $tail =~ $rex ]]
  local str=$BASH_REMATCH
  local force_attr= inactive=
  if [[ $- != *B* ]]; then
    inactive=1
  elif ((ctx==_ble_ctx_CONDI||ctx==_ble_ctx_CONDQ||ctx==_ble_ctx_RDRS||ctx==_ble_ctx_VRHS)); then
    inactive=1
  elif ((_ble_bash>=50300&&ctx==_ble_ctx_VALR)); then
    inactive=1
  elif ((ctx==_ble_ctx_PATN||ctx==_ble_ctx_BRAX)); then
    local ntype; ble/syntax/parse/nest-type
    if [[ $ntype == glob_attr=* ]]; then
      force_attr=${ntype#*=}
      (((force_attr==_ble_ctx_RDRS||force_attr==_ble_ctx_VRHS||force_attr==_ble_ctx_ARGVR||force_attr==_ble_ctx_ARGER||force_attr==_ble_ctx_VALR)&&(inactive=1)))
    elif ((ctx==_ble_ctx_BRAX)); then
      local nctx; ble/syntax/parse/nest-ctx
      (((nctx==_ble_ctx_CONDI||octx==_ble_ctx_CONDQ)&&(inactive=1)))
    fi
  elif ((ctx==_ble_ctx_BRACE1||ctx==_ble_ctx_BRACE2)); then
    local ntype; ble/syntax/parse/nest-type
    if [[ $ntype == glob_attr=* ]]; then
      force_attr=${ntype#*=}
    fi
  fi
  if [[ $inactive ]]; then
    ((_ble_syntax_attr[i]=${force_attr:-ctx},i+=${#str}))
    return 0
  fi
  [[ ${_ble_syntax_bash_command_IsAssign[ctx]} ]] &&
    ctx=${_ble_syntax_bash_command_IsAssign[ctx]}
  if rex='^\{(([-+]?[0-9]+)\.\.[-+]?[0-9]+|[a-zA-Z]\.\.[a-zA-Z])(\.\.[-+]?[0-9]+)?\}$'; [[ $str =~ $rex ]]; then
    if [[ $force_attr ]]; then
      ((_ble_syntax_attr[i]=force_attr,i+=${#str}))
    else
      local rematch1=${BASH_REMATCH[1]}
      local rematch2=${BASH_REMATCH[2]}
      local rematch3=${BASH_REMATCH[3]}
      local len2=${#rematch2}; ((len2||(len2=1)))
      local attr=$_ble_attr_BRACE
      if ((ctx==_ble_ctx_RDRF||ctx==_ble_ctx_RDRD||ctx==_ble_ctx_RDRD2)); then
        local lhs=${rematch1::len2} rhs=${rematch1:len2+2}
        if [[ $rematch2 ]]; then
          local lhs1=$((10#0${lhs#[-+]})); [[ $lhs == -* ]] && ((lhs1=-lhs1))
          local rhs1=$((10#0${rhs#[-+]})); [[ $rhs == -* ]] && ((rhs1=-rhs1))
          lhs=$lhs1 rhs=$rhs1
        fi
        [[ $lhs != "$rhs" ]] && ((attr=_ble_attr_ERR))
      fi
      ((_ble_syntax_attr[i++]=attr))
      ((_ble_syntax_attr[i]=ctx,i+=len2,
        _ble_syntax_attr[i]=_ble_attr_BRACE,i+=2,
        _ble_syntax_attr[i]=ctx,i+=${#rematch1}-len2-2))
      if [[ $rematch3 ]]; then
        ((_ble_syntax_attr[i]=_ble_attr_BRACE,i+=2,
          _ble_syntax_attr[i]=ctx,i+=${#rematch3}-2))
      fi
      ((_ble_syntax_attr[i++]=attr))
    fi
    return 0
  fi
  local ntype=
  ((ctx==_ble_ctx_RDRF||ctx==_ble_ctx_RDRD||ctx==_ble_ctx_RDRD2)) && force_attr=$ctx
  [[ $force_attr ]] && ntype="glob_attr=$force_attr"
  ble/syntax/parse/nest-push "$_ble_ctx_BRACE1" "$ntype"
  local len=$((${#str}-1))
  ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_BRACE},
    len&&(_ble_syntax_attr[i]=${force_attr:-ctx},i+=len)))
  return 0
}
_ble_syntax_context_proc[_ble_ctx_BRACE1]=ble/syntax:bash/ctx-brace-expansion
_ble_syntax_context_proc[_ble_ctx_BRACE2]=ble/syntax:bash/ctx-brace-expansion
_ble_syntax_context_end[_ble_ctx_BRACE1]=ble/syntax:bash/ctx-brace-expansion.end
_ble_syntax_context_end[_ble_ctx_BRACE2]=ble/syntax:bash/ctx-brace-expansion.end
function ble/syntax:bash/ctx-brace-expansion {
  if [[ $tail == '}'* ]] && ((ctx==_ble_ctx_BRACE2)); then
    local force_attr=
    local ntype; ble/syntax/parse/nest-type
    [[ $ntype == glob_attr=* ]] && force_attr=$_ble_attr_ERR # ※${ntype#*=} ではなくエラー
    ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_BRACE}))
    ble/syntax/parse/nest-pop
    return 0
  elif [[ $tail == ','* ]]; then
    local force_attr=
    local ntype; ble/syntax/parse/nest-type
    [[ $ntype == glob_attr=* ]] && force_attr=${ntype#*=}
    ((_ble_syntax_attr[i++]=${force_attr:-_ble_attr_BRACE}))
    ((ctx=_ble_ctx_BRACE2))
    return 0
  fi
  local chars=",${_ble_syntax_bash_chars[_ble_ctx_ARGI]//'~:'}"
  ((ctx==_ble_ctx_BRACE2)) && chars="}$chars"
  ble/syntax:bash/cclass/update/reorder chars
  if ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
  return 1
}
function ble/syntax:bash/ctx-brace-expansion.end {
  if ((i==${#text})) || ble/syntax:bash/check-word-end/is-delimiter; then
    ble/syntax/parse/nest-pop
    ble/syntax/parse/check-end
    return "$?"
  fi
  return 0
}
function ble/syntax:bash/check-tilde-expansion {
  [[ $tail == ['~:']* ]] || return 1
  local rhs_enabled=
  { ((ctx==_ble_ctx_VRHS||ctx==_ble_ctx_ARGVR||ctx==_ble_ctx_VALR||ctx==_ble_ctx_ARGER)) ||
      ! ble/base/is-POSIXLY_CORRECT; } && rhs_enabled=1
  local tilde_enabled=$((i==wbegin||ctx==_ble_ctx_PWORD))
  [[ $1 == rhs && $rhs_enabled ]] && tilde_enabled=1 # = の直後
  if [[ $tail == ':'* ]]; then
    _ble_syntax_attr[i++]=$ctx
    if [[ $rhs_enabled ]]; then
      if ! ((tilde_enabled=_ble_syntax_bash_command_IsAssign[ctx])); then
        if ((ctx==_ble_ctx_BRAX)); then
          local nctx; ble/syntax/parse/nest-ctx
          ((tilde_enabled=_ble_syntax_bash_command_IsAssign[nctx]))
        fi
      fi
    fi
    local tail=${text:i}
    [[ $tail == '~'* ]] || return 0
  fi
  if ((tilde_enabled)); then
    local chars="${_ble_syntax_bash_chars[_ble_ctx_ARGI]}/:"
    ((ctx==_ble_ctx_PWORD)) && chars=${chars/'{'/'{}'}
    ble/syntax:bash/cclass/update/reorder chars
    local delimiters="$_ble_term_IFS;|&)<>"
    local rex='^(~\+|~[^'$chars']*)([^'$delimiters'/:]?)'; [[ $tail =~ $rex ]]
    local str=${BASH_REMATCH[1]}
    local path attr=$ctx
    builtin eval "path=$str"
    if [[ ! ${BASH_REMATCH[2]} && $path != "$str" ]]; then
      ((attr=_ble_attr_TILDE))
      if ((ctx==_ble_ctx_BRAX)); then
        ble/util/assert 'ble/util/unlocal tail; [[ $tail == ":~"* ]]'
        ble/syntax/parse/nest-pop
      fi
    else
      if [[ $str == '~+' ]]; then
        ble/syntax/parse/set-lookahead 3
        str='~'
      fi
    fi
    ((_ble_syntax_attr[i]=attr,i+=${#str}))
  else
    ((_ble_syntax_attr[i]=ctx,i++)) # skip tilde
    local chars=${_ble_syntax_bash_chars[_ble_ctx_ARGI]}
    ble/syntax:bash/check-plain-with-escape "[^$chars]+" # 追加(失敗してもOK)
  fi
  return 0
}
_ble_syntax_bash_command_CtxAssign[_ble_ctx_CMDI]=$_ble_ctx_VRHS
_ble_syntax_bash_command_CtxAssign[_ble_ctx_COARGI]=$_ble_ctx_VRHS
_ble_syntax_bash_command_CtxAssign[_ble_ctx_ARGVI]=$_ble_ctx_ARGVR
_ble_syntax_bash_command_CtxAssign[_ble_ctx_ARGEI]=$_ble_ctx_ARGER
_ble_syntax_bash_command_CtxAssign[_ble_ctx_ARGI]=$_ble_ctx_ARGQ
_ble_syntax_bash_command_CtxAssign[_ble_ctx_FARGI3]=$_ble_ctx_FARGQ3
_ble_syntax_bash_command_CtxAssign[_ble_ctx_CARGI1]=$_ble_ctx_CARGQ1
_ble_syntax_bash_command_CtxAssign[_ble_ctx_CPATI]=$_ble_ctx_CPATQ
_ble_syntax_bash_command_CtxAssign[_ble_ctx_VALI]=$_ble_ctx_VALQ
_ble_syntax_bash_command_CtxAssign[_ble_ctx_CONDI]=$_ble_ctx_CONDQ
_ble_syntax_bash_command_IsAssign[_ble_ctx_VRHS]=$_ble_ctx_CMDI
_ble_syntax_bash_command_IsAssign[_ble_ctx_ARGVR]=$_ble_ctx_ARGVI
_ble_syntax_bash_command_IsAssign[_ble_ctx_ARGER]=$_ble_ctx_ARGEI
_ble_syntax_bash_command_IsAssign[_ble_ctx_ARGQ]=$_ble_ctx_ARGI
_ble_syntax_bash_command_IsAssign[_ble_ctx_FARGQ3]=$_ble_ctx_FARGI3
_ble_syntax_bash_command_IsAssign[_ble_ctx_CARGQ1]=$_ble_ctx_CARGI1
_ble_syntax_bash_command_IsAssign[_ble_ctx_CPATQ]=$_ble_ctx_CPATI
_ble_syntax_bash_command_IsAssign[_ble_ctx_VALR]=$_ble_ctx_VALI
_ble_syntax_bash_command_IsAssign[_ble_ctx_VALQ]=$_ble_ctx_VALI
_ble_syntax_bash_command_IsAssign[_ble_ctx_CONDQ]=$_ble_ctx_CONDI
function ble/syntax:bash/check-variable-assignment {
  ((wbegin==i)) || return 1
  if ((ctx==_ble_ctx_VALI)) && [[ $tail == '['* ]]; then
    ((ctx=_ble_ctx_VALR))
    ble/syntax/parse/nest-push "$_ble_ctx_EXPR" 'd['
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
  [[ ${_ble_syntax_bash_command_CtxAssign[ctx]} ]] || return 1
  local suffix='[=[]'
  ((_ble_bash>=30100)) && suffix=$suffix'|\+=?'
  local rex_assign="^([_a-zA-Z][_a-zA-Z0-9]*)($suffix)"
  [[ $tail =~ $rex_assign ]] || return 1
  local rematch=$BASH_REMATCH
  local rematch1=${BASH_REMATCH[1]} # for bash-3.1 ${#arr[n]} bug
  local rematch2=${BASH_REMATCH[2]} # for bash-3.1 ${#arr[n]} bug
  if [[ $rematch2 == '+' ]]; then
    ble/syntax/parse/set-lookahead "$((${#rematch}+1))"
    return 1
  fi
  local variable_assign=
  if ((ctx==_ble_ctx_CMDI||ctx==_ble_ctx_ARGVI||ctx==_ble_ctx_ARGEI&&${#rematch2})); then
    local ret; ble/syntax/highlight/vartype "$rematch1" newvar:global
    ((wtype=_ble_attr_VAR,
      _ble_syntax_attr[i]=ret,
      i+=${#rematch},
      ${#rematch2}&&(_ble_syntax_attr[i-${#rematch2}]=_ble_ctx_EXPR),
      variable_assign=1,
      ctx=_ble_syntax_bash_command_CtxAssign[ctx]))
  else
    ((_ble_syntax_attr[i]=ctx,
      i+=${#rematch}))
  fi
  if [[ $rematch2 == '[' ]]; then
    if [[ $variable_assign ]]; then
      i=$((i-1)) ble/syntax/parse/nest-push "$_ble_ctx_EXPR" 'a['
    else
      ((i--))
      tail=${text:i} ble/syntax:bash/check-glob assign
    fi
  elif [[ $rematch2 == *'=' ]]; then
    if [[ $variable_assign && ${text:i} == '('* ]]; then
      ble/syntax:bash/ctx-values/enter
      ((_ble_syntax_attr[i++]=_ble_attr_DEL))
    else
      [[ $variable_assign ]] || ((ctx=_ble_syntax_bash_command_CtxAssign[ctx]))
      if local tail=${text:i}; [[ $tail == '~'* ]]; then
        ble/syntax:bash/check-tilde-expansion rhs
      fi
    fi
  fi
  return 0
}
_ble_syntax_context_proc[_ble_ctx_ARGX]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGX0]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDX]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDX0]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDX1]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXT]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXC]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXE]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXD]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXD0]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDXV]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGI]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGQ]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CMDI]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_VRHS]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGVR]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGER]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_CMDI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_ARGI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_ARGQ]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_VRHS]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_ARGVR]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_ARGER]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_ARGVX]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGVI]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_ARGVI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_ARGEX]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_ARGEI]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_ARGEI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_SARGX1]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_proc[_ble_ctx_FARGX1]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_proc[_ble_ctx_FARGX2]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_proc[_ble_ctx_FARGX3]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_FARGI1]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_FARGI2]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_FARGI3]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_FARGQ3]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_FARGI1]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_FARGI2]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_FARGI3]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_FARGQ3]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_CARGX1]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_proc[_ble_ctx_CARGX2]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_proc[_ble_ctx_CPATX]=ble/syntax:bash/ctx-command-case-pattern-expect
_ble_syntax_context_proc[_ble_ctx_CPATX0]=ble/syntax:bash/ctx-command-case-pattern-expect
_ble_syntax_context_proc[_ble_ctx_CARGI1]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CARGQ1]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CARGI2]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CPATI]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_CPATQ]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_CARGI1]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_CARGQ1]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_CARGI2]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_CPATI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_CPATQ]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_TARGX1]=ble/syntax:bash/ctx-command-time-expect
_ble_syntax_context_proc[_ble_ctx_TARGX2]=ble/syntax:bash/ctx-command-time-expect
_ble_syntax_context_proc[_ble_ctx_TARGI1]=ble/syntax:bash/ctx-command
_ble_syntax_context_proc[_ble_ctx_TARGI2]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_TARGI1]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_end[_ble_ctx_TARGI2]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_FNAMEX]=ble/syntax:bash/ctx-command-function-expect
_ble_syntax_context_proc[_ble_ctx_FNAMEI]=ble/syntax:bash/ctx-command
_ble_syntax_context_end[_ble_ctx_FNAMEI]=ble/syntax:bash/ctx-command/check-word-end
_ble_syntax_context_proc[_ble_ctx_COARGX]=ble/syntax:bash/ctx-command-compound-expect
_ble_syntax_context_end[_ble_ctx_COARGI]=ble/syntax:bash/ctx-coproc/check-word-end
function ble/syntax:bash/starts-with-delimiter-or-redirect {
  local delimiters=$_ble_syntax_bash_RexDelimiter
  local redirect=$_ble_syntax_bash_RexRedirect
  local cont='\\'$_ble_term_nl
  [[ ( $tail =~ ^$delimiters || $wbegin -lt 0 && $tail =~ ^$redirect || $wbegin -lt 0 && $tail =~ ^$cont ) && $tail != ['<>']'('* ]]
}
function ble/syntax:bash/starts-with-delimiter {
  [[ $tail == ["$_ble_term_IFS;|&<>()"]* && $tail != ['<>']'('* ]]
}
function ble/syntax:bash/check-word-end/is-delimiter {
  local tail=${text:i}
  if [[ $tail == [!"$_ble_term_IFS;|&<>()"]* ]]; then
    return 1
  elif [[ $tail == ['<>']* ]]; then
    ble/syntax/parse/set-lookahead 2
    [[ $tail == ['<>']'('* ]] && return 1
  fi
  return 0
}
function ble/syntax:bash/check-here-document-from {
  local spaces=$1
  [[ $nparam && $spaces == *$'\n'* ]] || return 1
  local rex="$_ble_term_FS@([RI][QH][^$_ble_term_FS]*)(.*$)" && [[ $nparam =~ $rex ]] || return 1
  local rematch1=${BASH_REMATCH[1]}
  local rematch2=${BASH_REMATCH[2]}
  local padding=${spaces%%$'\n'*}
  ((_ble_syntax_attr[i]=ctx,i+=${#padding}))
  nparam=${nparam::${#nparam}-${#BASH_REMATCH}}${nparam:${#nparam}-${#rematch2}}
  ble/syntax/parse/nest-push "$_ble_ctx_HERE0"
  ((i++))
  nparam=$rematch1
  return 0
}
function ble/syntax:bash/ctx-coproc/.is-next-compound {
  local p=$i ahead=1 tail=${text:i}
  if local rex=$'^[ \t]+'; [[ $tail =~ $rex ]]; then
    ((p+=${#BASH_REMATCH}))
    ahead=1 tail=${text:p}
  fi
  local is_compound=
  if [[ $tail == '('* ]]; then
    is_compound=1
  elif rex='^[a-z]+|^\[\[?|^[{}!]'; [[ $tail =~ $rex ]]; then
    local rematch=$BASH_REMATCH
    ((p+=${#rematch}))
    [[ $rematch == ['{}!'] || $rematch == '[[' ]]; ahead=$?
    rex='^(\[\[|for|select|case|if|while|until|fi|done|esac|then|elif|else|do|[{}!]|coproc|function)$'
    if  [[ $rematch =~ $rex ]]; then
      if rex='^[;|&()'$_ble_term_IFS']|^$|^[<>]\(?' ahead=1; [[ ${text:p} =~ $rex ]]; then
        local rematch=$BASH_REMATCH
        ((p+=${#rematch}))
        [[ $rematch && $rematch != ['<>'] ]]; ahead=$?
        [[ $rematch != ['<>']'(' ]] && is_compound=1
      fi
    fi
  fi
  ble/syntax/parse/set-lookahead "$((p+ahead-i))"
  [[ $is_compound ]]
}
function ble/syntax:bash/ctx-coproc/check-word-end {
  ble/util/assert '((ctx==_ble_ctx_COARGI))'
  ((wbegin<0)) && return 1
  ble/syntax:bash/check-word-end/is-delimiter || return 1
  local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i
  local word=${text:wbegin:wlen}
  local wt=$wtype
  if local rex='^[_a-zA-Z][_a-zA-Z0-9]*$'; [[ $word =~ $rex ]]; then
    if ble/syntax:bash/ctx-coproc/.is-next-compound; then
      local attr=$_ble_attr_VAR
      if ble/alias#active "$word"; then
        attr=
        local ret; ble/alias#expand "$word"
        case $word in
        ('if'|'while'|'until'|'for'|'select'|'case'|'{'|'[[') ;;
        ('fi'|'done'|'esac'|'then'|'elif'|'else'|'do'|'}'|'!'|'coproc'|'function'|'in') ;;
        (*)
          if ble/string#match "$word" '^[_a-zA-Z][_a-zA-Z0-9]*$'; then
            attr=$_ble_attr_CMD_ALIAS
          else
            attr=$_ble_attr_ERR
          fi
        esac
      fi
      if [[ $attr ]]; then
        _ble_syntax_attr[wbegin]=$attr
        ((ctx=_ble_ctx_CMDXC,wtype=_ble_ctx_ARGVI))
        ble/syntax/parse/word-pop
        return 0
      fi
    fi
  fi
  ((ctx=_ble_ctx_CMDI,wtype=_ble_ctx_CMDX))
  ble/syntax:bash/ctx-command/check-word-end
}
_ble_syntax_bash_command_EndCtx=()
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGI]=$_ble_ctx_ARGX
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGQ]=$_ble_ctx_ARGX
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGVI]=$_ble_ctx_ARGVX
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGVR]=$_ble_ctx_ARGVX
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGEI]=$_ble_ctx_ARGEX
_ble_syntax_bash_command_EndCtx[_ble_ctx_ARGER]=$_ble_ctx_ARGEX
_ble_syntax_bash_command_EndCtx[_ble_ctx_VRHS]=$_ble_ctx_CMDXV
_ble_syntax_bash_command_EndCtx[_ble_ctx_FARGI1]=$_ble_ctx_FARGX2
_ble_syntax_bash_command_EndCtx[_ble_ctx_FARGI2]=$_ble_ctx_FARGX3
_ble_syntax_bash_command_EndCtx[_ble_ctx_FARGI3]=$_ble_ctx_FARGX3
_ble_syntax_bash_command_EndCtx[_ble_ctx_FARGQ3]=$_ble_ctx_FARGX3
_ble_syntax_bash_command_EndCtx[_ble_ctx_CARGI1]=$_ble_ctx_CARGX2
_ble_syntax_bash_command_EndCtx[_ble_ctx_CARGQ1]=$_ble_ctx_CARGX2
_ble_syntax_bash_command_EndCtx[_ble_ctx_CARGI2]=$_ble_ctx_CASE
_ble_syntax_bash_command_EndCtx[_ble_ctx_CPATI]=$_ble_ctx_CPATX0
_ble_syntax_bash_command_EndCtx[_ble_ctx_CPATQ]=$_ble_ctx_CPATX0
_ble_syntax_bash_command_EndCtx[_ble_ctx_TARGI1]=$((_ble_bash>=40200?_ble_ctx_TARGX2:_ble_ctx_CMDXT)) #1
_ble_syntax_bash_command_EndCtx[_ble_ctx_TARGI2]=$_ble_ctx_CMDXT
_ble_syntax_bash_command_EndCtx[_ble_ctx_FNAMEI]=$_ble_ctx_CMDXC
_ble_syntax_bash_command_EndWtype[_ble_ctx_ARGX]=$_ble_ctx_ARGI
_ble_syntax_bash_command_EndWtype[_ble_ctx_ARGX0]=$_ble_ctx_ARGI
_ble_syntax_bash_command_EndWtype[_ble_ctx_ARGVX]=$_ble_ctx_ARGVI
_ble_syntax_bash_command_EndWtype[_ble_ctx_ARGEX]=$_ble_ctx_ARGEI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDX]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDX0]=$_ble_ctx_CMDX0
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDX1]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXT]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXC]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXE]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXD]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXD0]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CMDXV]=$_ble_ctx_CMDI
_ble_syntax_bash_command_EndWtype[_ble_ctx_FARGX1]=$_ble_ctx_FARGI1 # 変数名
_ble_syntax_bash_command_EndWtype[_ble_ctx_SARGX1]=$_ble_ctx_ARGI
_ble_syntax_bash_command_EndWtype[_ble_ctx_FARGX2]=$_ble_ctx_FARGI2 # in
_ble_syntax_bash_command_EndWtype[_ble_ctx_FARGX3]=$_ble_ctx_ARGI # in
_ble_syntax_bash_command_EndWtype[_ble_ctx_CARGX1]=$_ble_ctx_ARGI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CARGX2]=$_ble_ctx_CARGI2 # in
_ble_syntax_bash_command_EndWtype[_ble_ctx_CPATX]=$_ble_ctx_CPATI
_ble_syntax_bash_command_EndWtype[_ble_ctx_CPATX0]=$_ble_ctx_CPATI
_ble_syntax_bash_command_EndWtype[_ble_ctx_TARGX1]=$_ble_ctx_ARGI # -p
_ble_syntax_bash_command_EndWtype[_ble_ctx_TARGX2]=$_ble_ctx_ARGI # --
_ble_syntax_bash_command_EndWtype[_ble_ctx_FNAMEX]=$_ble_ctx_FNAMEI # function NAME
_ble_syntax_bash_command_Expect=()
_ble_syntax_bash_command_Expect[_ble_ctx_CMDXC]='^(\(|\{|\(\(|\[\[|for|select|case|if|while|until)$'
_ble_syntax_bash_command_Expect[_ble_ctx_CMDXE]='^(\}|fi|done|esac|then|elif|else|do)$'
_ble_syntax_bash_command_Expect[_ble_ctx_CMDXD]='^(\{|do)$'
_ble_syntax_bash_command_Expect[_ble_ctx_CMDXD0]='^(\{|do)$'
function ble/syntax:bash/ctx-command/check-word-end {
  ((wbegin<0)) && return 1
  ble/syntax:bash/check-word-end/is-delimiter || return 1
  local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i
  local word=${text:wbegin:wlen}
  local stat_wt=$wtype # 単語解析中の wtype
  [[ ${_ble_syntax_bash_command_EndWtype[stat_wt]} ]] &&
    wtype=${_ble_syntax_bash_command_EndWtype[stat_wt]}
  local rex_expect_command=${_ble_syntax_bash_command_Expect[stat_wt]}
  if [[ $rex_expect_command ]]; then
    [[ $word =~ $rex_expect_command ]] || ((wtype=_ble_ctx_CMDX0))
  elif ((stat_wt==_ble_ctx_ARGX0||stat_wt==_ble_ctx_CPATX0)); then
    ((wtype=_ble_attr_ERR))
  elif ((stat_wt==_ble_ctx_CMDX1)); then
    local rex='^(then|elif|else|do|\}|done|fi|esac)$'
    [[ $word =~ $rex ]] && ((wtype=_ble_ctx_CMDX0))
  fi
  local tree_wt=$wtype # 実際に単語として登録された wtype
  ble/syntax/parse/word-pop
  if ((ctx==_ble_ctx_CMDI)); then
    local ret
    ble/alias#expand "$word"; local word_expanded=$ret
    if ((tree_wt==_ble_ctx_CMDX0)); then
      ((_ble_syntax_attr[wbeg]=_ble_attr_ERR,ctx=_ble_ctx_ARGX))
      return 0
    elif ((stat_wt!=_ble_ctx_CMDXV)); then # Note: 変数代入の直後はキーワードは処理しない
      local processed=
      case $word_expanded in
      ('[[')
        ble/syntax/parse/touch-updated-attr "$wbeg"
        ((_ble_syntax_attr[wbeg]=_ble_attr_DEL,
          ctx=_ble_bash>=50200?_ble_ctx_CMDXE:_ble_ctx_ARGX0))
        ble/syntax/parse/word-cancel # 単語 "[[" (とその内部のノード全て) を削除
        if [[ $word == '[[' ]]; then
          _ble_syntax_attr[wbeg+1]= # 角括弧式として着色されているのを消去
        fi
        i=$wbeg ble/syntax/parse/nest-push "$_ble_ctx_CONDX"
        i=$wbeg ble/syntax/parse/word-push "$_ble_ctx_CMDI" "$wbeg"
        ble/syntax/parse/word-pop
        return 0 ;;
      ('time')               ((ctx=_ble_ctx_TARGX1)); processed=keyword ;;
      ('!')                  ((ctx=_ble_ctx_CMDXT)) ; processed=keyword ;;
      ('function')           ((ctx=_ble_ctx_FNAMEX)); processed=keyword ;;
      ('if'|'while'|'until') ((ctx=_ble_ctx_CMDX1)) ; processed=begin ;;
      ('for')                ((ctx=_ble_ctx_FARGX1)); processed=begin ;;
      ('select')             ((ctx=_ble_ctx_SARGX1)); processed=begin ;;
      ('case')               ((ctx=_ble_ctx_CARGX1)); processed=begin ;;
      ('{')
        ble/syntax/parse/touch-updated-attr "$wbeg"
        if ((stat_wt==_ble_ctx_CMDXD||stat_wt==_ble_ctx_CMDXD0)); then
          attr=$_ble_attr_KEYWORD_MID # "for ...; {" などの時
        else
          attr=$_ble_attr_KEYWORD_BEGIN
        fi
        ((_ble_syntax_attr[wbeg]=attr))
        ble/syntax/parse/word-cancel
        ((ctx=_ble_ctx_CMDXE))
        i=$wbeg ble/syntax/parse/nest-push "$_ble_ctx_CMDX1" 'cmd_brace'
        i=$wbeg ble/syntax/parse/word-push "$_ble_ctx_CMDI" "$wbeg"
        ble/syntax/parse/word-pop
        return 0 ;;
      ('then'|'elif'|'else'|'do') ((ctx=_ble_ctx_CMDX1)); processed=middle ;;
      ('done'|'fi'|'esac')        ((ctx=_ble_ctx_CMDXE)); processed=end ;;
      ('}')
        if local ntype; ble/syntax/parse/nest-type; [[ $ntype == 'cmd_brace' ]]; then
          ble/syntax/parse/touch-updated-attr "$wbeg"
          ((_ble_syntax_attr[wbeg]=_ble_attr_KEYWORD_END))
          ble/syntax/parse/nest-pop
        else
          ble/syntax/parse/touch-updated-attr "$wbeg"
          ((_ble_syntax_attr[wbeg]=_ble_attr_ERR))
          ((ctx=_ble_ctx_CMDXE))
        fi
        return 0 ;;
      ('coproc')
        if ((_ble_bash>=40000)); then
          if ble/syntax:bash/ctx-coproc/.is-next-compound; then
            ((ctx=_ble_ctx_CMDXC))
          else
            ((ctx=_ble_ctx_COARGX))
          fi
          processed=keyword
        fi ;;
      esac
      if [[ $processed ]]; then
        local attr=
        case $processed in
        (keyword) attr=$_ble_attr_KEYWORD ;;
        (begin)   attr=$_ble_attr_KEYWORD_BEGIN ;;
        (end)     attr=$_ble_attr_KEYWORD_END ;;
        (middle)  attr=$_ble_attr_KEYWORD_MID ;;
        esac
        if [[ $attr ]]; then
          ble/syntax/parse/touch-updated-attr "$wbeg"
          ((_ble_syntax_attr[wbeg]=attr))
        fi
        return 0
      fi
    fi
    ((ctx=_ble_ctx_ARGX))
    if local rex='^([ 	]*)(\([ 	]*\)?)?'; [[ ${text:i} =~ $rex && $BASH_REMATCH ]]; then
      local rematch1=${BASH_REMATCH[1]}
      local rematch2=${BASH_REMATCH[2]}
      if [[ $rematch2 == '('*')' ]]; then
        local attr=$_ble_attr_ERR
        if ((_ble_bash>=990000)) || [[ $word_expanded != *[\\\'\"\`\$\<\>\(\)]* ]]; then
          _ble_syntax_tree[i-1]="$_ble_ctx_FNAMEI ${_ble_syntax_tree[i-1]#* }"
          attr=$_ble_attr_DEL
        fi
        ((_ble_syntax_attr[i]=_ble_ctx_CMDX1,i+=${#rematch1},
          _ble_syntax_attr[i]=attr,i+=${#rematch2},
          ctx=_ble_ctx_CMDXC))
      elif [[ $rematch2 == '('* ]]; then
        ((_ble_syntax_attr[i]=_ble_ctx_ARGX0,i+=${#rematch1},
          _ble_syntax_attr[i]=_ble_attr_ERR,
          ctx=_ble_ctx_ARGX0))
        ble/syntax/parse/nest-push "$_ble_ctx_PATN"
        ((${#rematch2}>=2&&(_ble_syntax_attr[i+1]=_ble_ctx_CMDXC),
          i+=${#rematch2}))
      else
        ble/syntax/parse/set-lookahead "$((${#rematch1}+1))"
      fi
    fi
    case $word_expanded in
    ('declare'|'readonly'|'typeset'|'local'|'export'|'alias')
      ((ctx=_ble_ctx_ARGVX)) ;;
    ('eval')
      ((ctx=_ble_ctx_ARGEX)) ;;
    esac
    return 0
  fi
  if ((ctx==_ble_ctx_FARGI2)); then
    if [[ $word == do ]]; then
      ((ctx=_ble_ctx_CMDX1))
      return 0
    fi
  elif ((ctx==_ble_ctx_FARGI2||ctx==_ble_ctx_CARGI2)); then
    if [[ $word != in ]];  then
      ble/syntax/parse/touch-updated-attr "$wbeg"
      ((_ble_syntax_attr[wbeg]=_ble_attr_ERR))
    fi
  elif ((ctx==_ble_ctx_FNAMEI)); then
    if ((_ble_bash<990000)) && [[ $word == *[\\\'\"\`\$\<\>\(\)]* ]]; then
      _ble_syntax_attr[i-1]=$_ble_attr_ERR
    fi
    local rex_space=$'[ \t]'
    if ble/string#match "${text:i}" "^($rex_space*)(\(\(|\($rex_space*\)?)?"; then
      local rematch1=${BASH_REMATCH[1]}
      local rematch2=${BASH_REMATCH[2]}
      ((${#rematch1})) && ((_ble_syntax_attr[i]=_ble_ctx_FNAMEX,i+=${#rematch1}))
      if [[ $rematch2 == '('*')' ]]; then
        ((_ble_syntax_attr[i]=_ble_attr_DEL,i+=${#rematch2}))
      elif [[ $rematch2 == '((' ]]; then
        if ((_ble_bash>=40200)); then
          ble/syntax/parse/set-lookahead 2
        else
          ((_ble_syntax_attr[i]=_ble_attr_ERR,ctx=_ble_bash>=50200?_ble_ctx_CMDXE:_ble_ctx_ARGX0))
          ble/syntax/parse/nest-push "$_ble_ctx_EXPR" '(('
          ((i+=2))
        fi
      else
        if ((_ble_bash>=50100)) || [[ $rematch2 != '('* ]]; then
          ble/syntax/parse/set-lookahead "$((${#rematch2}+1))"
        else
          ((_ble_syntax_attr[i]=_ble_attr_ERR,ctx=_ble_ctx_CMDXE))
          ble/syntax/parse/nest-push "$_ble_ctx_CMDX1" '('
          ((${#rematch2}>=2&&(_ble_syntax_attr[i+1]=_ble_ctx_CMDX1),i+=${#rematch2}))
        fi
      fi
    fi
  fi
  if ((_ble_syntax_bash_command_EndCtx[ctx])); then
    ((ctx=_ble_syntax_bash_command_EndCtx[ctx]))
  fi
  return 0
}
_ble_syntax_bash_command_Opt=()
_ble_syntax_bash_command_Opt[_ble_ctx_ARGX]=1
_ble_syntax_bash_command_Opt[_ble_ctx_ARGX0]=1
_ble_syntax_bash_command_Opt[_ble_ctx_ARGVX]=1
_ble_syntax_bash_command_Opt[_ble_ctx_ARGEX]=1
_ble_syntax_bash_command_Opt[_ble_ctx_CMDX0]=1
_ble_syntax_bash_command_Opt[_ble_ctx_CMDXV]=1
_ble_syntax_bash_command_Opt[_ble_ctx_CMDXE]=1
_ble_syntax_bash_command_Opt[_ble_ctx_CMDXD0]=1
_ble_syntax_bash_is_command_form_for=
function ble/syntax:bash/ctx-command/.check-delimiter-or-redirect {
  if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs || $wbegin -lt 0 && $tail == $'\\\n'* ]]; then
    local spaces=$BASH_REMATCH
    if [[ $tail == $'\\\n'* ]]; then
      spaces=$'\\\n'
    elif [[ $spaces == *$'\n'* ]]; then
      ble/syntax:bash/check-here-document-from "$spaces" && return 0
      if ((ctx==_ble_ctx_ARGX||ctx==_ble_ctx_ARGX0||ctx==_ble_ctx_ARGVX||ctx==_ble_ctx_ARGEX||ctx==_ble_ctx_CMDX0||ctx==_ble_ctx_CMDXV||ctx==_ble_ctx_CMDXT||ctx==_ble_ctx_CMDXE)); then
        ((ctx=_ble_ctx_CMDX))
      elif ((ctx==_ble_ctx_FARGX2||ctx==_ble_ctx_FARGX3||ctx==_ble_ctx_CMDXD0)); then
        ((ctx=_ble_ctx_CMDXD))
      fi
    fi
    ((_ble_syntax_attr[i]=ctx,i+=${#spaces}))
    return 0
  elif [[ $tail =~ ^$_ble_syntax_bash_RexRedirect ]]; then
    local len=${#BASH_REMATCH}
    local rematch1=${BASH_REMATCH[1]}
    local rematch3=${BASH_REMATCH[3]}
    ((_ble_syntax_attr[i]=_ble_attr_DEL,
      ${#rematch1}<len&&(_ble_syntax_attr[i+${#rematch1}]=_ble_ctx_ARGX)))
    if ((ctx==_ble_ctx_CMDX||ctx==_ble_ctx_CMDX1||ctx==_ble_ctx_CMDXT)); then
      ((ctx=_ble_ctx_CMDXV))
    elif ((ctx==_ble_ctx_CMDXC||ctx==_ble_ctx_CMDXD||ctx==_ble_ctx_CMDXD0)); then
      ((ctx=_ble_ctx_CMDXV,
        _ble_syntax_attr[i]=_ble_attr_ERR))
    elif ((ctx==_ble_ctx_CMDXE)); then
      ((ctx=_ble_ctx_CMDX0))
    elif ((ctx==_ble_ctx_FARGX3)); then
      ((_ble_syntax_attr[i]=_ble_attr_ERR))
    fi
    if [[ ${text:i+len} != [!$'\n|&()']* ]]; then
      ((_ble_syntax_attr[i+len-1]=_ble_attr_ERR))
    else
      if [[ $rematch3 == '>&' ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_RDRD2" "$rematch3"
      elif [[ $rematch1 == *'&' ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_RDRD" "$rematch3"
      elif [[ $rematch1 == *'<<<' ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_RDRS" "$rematch3"
      elif [[ $rematch1 == *\<\< ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_RDRH" "$rematch3"
      elif [[ $rematch1 == *\<\<- ]]; then
        ble/syntax/parse/nest-push "$_ble_ctx_RDRI" "$rematch3"
      else
        ble/syntax/parse/nest-push "$_ble_ctx_RDRF" "$rematch3"
      fi
    fi
    ((i+=len))
    return 0
  elif local rex='^(&&|\|[|&]?)|^;(;&?|&)|^[;&]'
       ((_ble_bash<40000)) && rex='^(&&|\|\|?)|^;(;)|^[;&]'
       [[ $tail =~ $rex ]]
  then
    if [[ $BASH_REMATCH == ';' ]]; then
      if ((ctx==_ble_ctx_FARGX2||ctx==_ble_ctx_FARGX3||ctx==_ble_ctx_CMDXD0)); then
        ((_ble_syntax_attr[i++]=_ble_attr_DEL,ctx=_ble_ctx_CMDXD))
        return 0
      elif ((ctx==_ble_ctx_CMDXT)); then
        ((_ble_syntax_attr[i++]=_ble_attr_DEL,ctx=_ble_bash>=40400?_ble_ctx_CMDX:_ble_ctx_CMDXE))
        return 0
      fi
    fi
    local rematch1=${BASH_REMATCH[1]} rematch2=${BASH_REMATCH[2]}
    ((_ble_syntax_attr[i]=_ble_attr_DEL,
      (_ble_syntax_bash_command_Opt[ctx]||ctx==_ble_ctx_CMDX&&${#rematch2})||
        (_ble_syntax_attr[i]=_ble_attr_ERR)))
    ((ctx=${#rematch1}?_ble_ctx_CMDX1:(
         ${#rematch2}?_ble_ctx_CASE:
         _ble_ctx_CMDX)))
    ((i+=${#BASH_REMATCH}))
    return 0
  elif local rex='^\(\(?' && [[ $tail =~ $rex ]]; then
    local m=${BASH_REMATCH[0]}
    if ((ctx==_ble_ctx_CMDX||ctx==_ble_ctx_CMDX1||ctx==_ble_ctx_CMDXT||ctx==_ble_ctx_CMDXC)); then
      ((_ble_syntax_attr[i]=_ble_attr_DEL))
      ((ctx=_ble_bash>=50200||${#m}==1?_ble_ctx_CMDXE:_ble_ctx_ARGX0))
      [[ $_ble_syntax_bash_is_command_form_for && $tail == '(('* ]] && ((ctx=_ble_ctx_CMDXD0))
      ble/syntax/parse/nest-push "$((${#m}==1?_ble_ctx_CMDX1:_ble_ctx_EXPR))" "$m"
      ((i+=${#m}))
    else
      ble/syntax/parse/nest-push "$_ble_ctx_PATN"
      ((_ble_syntax_attr[i++]=_ble_attr_ERR))
    fi
    return 0
  elif [[ $tail == ')'* ]]; then
    local ntype
    ble/syntax/parse/nest-type
    local attr=
    if [[ $ntype == '(' || $ntype == '$(' || $ntype == '((' || $ntype == '$((' ]]; then
      ((attr=_ble_syntax_attr[inest]))
    fi
    if [[ $attr ]]; then
      ((_ble_syntax_attr[i]=(ctx==_ble_ctx_CMDX||ctx==_ble_ctx_CMDX0||ctx==_ble_ctx_CMDXV||ctx==_ble_ctx_CMDXE||ctx==_ble_ctx_ARGX||ctx==_ble_ctx_ARGX0||ctx==_ble_ctx_ARGVX||ctx==_ble_ctx_ARGEX)?attr:_ble_attr_ERR,
        i+=1))
      ble/syntax/parse/nest-pop
      return 0
    fi
  fi
  return 1
}
_ble_syntax_bash_command_BeginCtx=()
_ble_syntax_bash_command_BeginCtx[_ble_ctx_ARGX]=$_ble_ctx_ARGI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_ARGX0]=$_ble_ctx_ARGI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_ARGVX]=$_ble_ctx_ARGVI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_ARGEX]=$_ble_ctx_ARGEI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDX]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDX0]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDX1]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXT]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXC]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXE]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXD]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXD0]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CMDXV]=$_ble_ctx_CMDI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_FARGX1]=$_ble_ctx_FARGI1
_ble_syntax_bash_command_BeginCtx[_ble_ctx_SARGX1]=$_ble_ctx_FARGI1
_ble_syntax_bash_command_BeginCtx[_ble_ctx_FARGX2]=$_ble_ctx_FARGI2
_ble_syntax_bash_command_BeginCtx[_ble_ctx_FARGX3]=$_ble_ctx_FARGI3
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CARGX1]=$_ble_ctx_CARGI1
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CARGX2]=$_ble_ctx_CARGI2
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CPATX]=$_ble_ctx_CPATI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_CPATX0]=$_ble_ctx_CPATI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_TARGX1]=$_ble_ctx_TARGI1
_ble_syntax_bash_command_BeginCtx[_ble_ctx_TARGX2]=$_ble_ctx_TARGI2
_ble_syntax_bash_command_BeginCtx[_ble_ctx_FNAMEX]=$_ble_ctx_FNAMEI
_ble_syntax_bash_command_BeginCtx[_ble_ctx_COARGX]=$_ble_ctx_COARGI
_ble_syntax_bash_command_isARGI[_ble_ctx_CMDI]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_VRHS]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGI]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGQ]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGVI]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGVR]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGEI]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_ARGER]=1
_ble_syntax_bash_command_isARGI[_ble_ctx_FARGI1]=1 # var
_ble_syntax_bash_command_isARGI[_ble_ctx_FARGI2]=1 # in
_ble_syntax_bash_command_isARGI[_ble_ctx_FARGI3]=1 # args...
_ble_syntax_bash_command_isARGI[_ble_ctx_FARGQ3]=1 # args... (= の後)
_ble_syntax_bash_command_isARGI[_ble_ctx_CARGI1]=1 # value
_ble_syntax_bash_command_isARGI[_ble_ctx_CARGQ1]=1 # value (= の後)
_ble_syntax_bash_command_isARGI[_ble_ctx_CARGI2]=1 # in
_ble_syntax_bash_command_isARGI[_ble_ctx_CPATI]=1  # pattern
_ble_syntax_bash_command_isARGI[_ble_ctx_CPATQ]=1  # pattern
_ble_syntax_bash_command_isARGI[_ble_ctx_TARGI1]=1 # -p
_ble_syntax_bash_command_isARGI[_ble_ctx_TARGI2]=1 # --
_ble_syntax_bash_command_isARGI[_ble_ctx_FNAMEI]=1 # function NAME
_ble_syntax_bash_command_isARGI[_ble_ctx_COARGI]=1 # var (coproc の後)
function ble/syntax:bash/ctx-command/.check-funsub-end {
  ((_ble_bash>=50300)) || return 1
  ((wbegin<0&&_ble_syntax_bash_command_BeginCtx[ctx]==_ble_ctx_CMDI)) || return 1
  [[ $tail == '}'* ]] || return 1
  local ntype
  ble/syntax/parse/nest-type
  [[ $ntype == 'cmdsub_nofork' ]] || return 1
  ((_ble_syntax_attr[i++]=_ble_syntax_attr[inest]))
  ble/syntax/parse/nest-pop
  return 0
}
function ble/syntax:bash/ctx-command/.check-word-begin {
  if ((wbegin<0)); then
    local octx
    ((octx=ctx,
      wtype=octx,
      ctx=_ble_syntax_bash_command_BeginCtx[ctx]))
    ble/util/assert '((ctx!=0))' "invalid ctx=$octx at the beginning of words" ||
      ((ctx=wtype=_ble_ctx_ARGI))
    ble/syntax/parse/word-push "$wtype" "$i"
    ((octx!=_ble_ctx_ARGX0&&octx!=_ble_ctx_CPATX0)); return "$?" # return unexpectedWbegin
  fi
  ble/util/assert '((_ble_syntax_bash_command_isARGI[ctx]))' "invalid ctx=$ctx in words"
  return 0
}
function ble/syntax:bash/ctx-command {
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    ble/util/assert '
      ((ctx==_ble_ctx_ARGX||ctx==_ble_ctx_ARGX0||ctx==_ble_ctx_ARGVX||ctx==_ble_ctx_ARGEX||
          ctx==_ble_ctx_FARGX2||ctx==_ble_ctx_FARGX3||ctx==_ble_ctx_COARGX||
          ctx==_ble_ctx_CMDX||ctx==_ble_ctx_CMDX0||ctx==_ble_ctx_CMDX1||ctx==_ble_ctx_CMDXT||ctx==_ble_ctx_CMDXC||
          ctx==_ble_ctx_CMDXE||ctx==_ble_ctx_CMDXD||ctx==_ble_ctx_CMDXD0||ctx==_ble_ctx_CMDXV))'  "invalid ctx=$ctx @ i=$i"
    ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char."
    ble/syntax:bash/ctx-command/.check-delimiter-or-redirect; return "$?"
  fi
  ble/syntax:bash/check-comment && return 0
  ble/syntax:bash/ctx-command/.check-funsub-end && return 0
  local unexpectedWbegin=-1
  ble/syntax:bash/ctx-command/.check-word-begin || ((unexpectedWbegin=i))
  local wtype0=$wtype i0=$i
  local flagConsume=0
  if ble/syntax:bash/check-variable-assignment; then
    flagConsume=1
  elif local rex='^([^'${_ble_syntax_bash_chars[_ble_ctx_ARGI]}']+|\\.)'; [[ $tail =~ $rex ]]; then
    local rematch=$BASH_REMATCH
    local attr=$ctx
    [[ $BASH_REMATCH == '\'? ]] && attr=$_ble_attr_QESC
    ((_ble_syntax_attr[i]=attr,i+=${#rematch}))
    flagConsume=1
  elif ble/syntax:bash/check-process-subst; then
    flagConsume=1
  elif ble/syntax:bash/check-quotes; then
    flagConsume=1
  elif ble/syntax:bash/check-dollar; then
    flagConsume=1
  elif ble/syntax:bash/check-glob; then
    flagConsume=1
  elif ble/syntax:bash/check-brace-expansion; then
    flagConsume=1
  elif ble/syntax:bash/check-tilde-expansion; then
    flagConsume=1
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    flagConsume=1
  fi
  if ((flagConsume)); then
    ble/util/assert '((wtype0>=0))'
    if ((ctx==_ble_ctx_FARGI1)); then
      local rex='^[_a-zA-Z][_a-zA-Z0-9]*$' attr=$_ble_attr_ERR
      if ((i0==wbegin)) && [[ ${text:i0:i-i0} =~ $rex ]]; then
        local ret; ble/syntax/highlight/vartype "$BASH_REMATCH" global; attr=$ret
      fi
      ((_ble_syntax_attr[i0]=attr))
    fi
    [[ ${_ble_syntax_bash_command_Expect[wtype0]} ]] &&
      ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    if ((unexpectedWbegin>=0)); then
      ble/syntax/parse/touch-updated-attr "$unexpectedWbegin"
      ((_ble_syntax_attr[unexpectedWbegin]=_ble_attr_ERR))
    fi
    return 0
  else
    return 1
  fi
}
function ble/syntax:bash/ctx-command-compound-expect {
  ble/util/assert '((ctx==_ble_ctx_FARGX1||ctx==_ble_ctx_SARGX1||ctx==_ble_ctx_CARGX1||ctx==_ble_ctx_FARGX2||ctx==_ble_ctx_CARGX2||ctx==_ble_ctx_COARGX))'
  local _ble_syntax_bash_is_command_form_for=
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    if ((ctx==_ble_ctx_FARGX2)) && [[ $tail == [$';\n']* ]]; then
      ble/syntax:bash/ctx-command
      return "$?"
    elif ((ctx==_ble_ctx_FARGX1)) && [[ $tail == '(('* ]]; then
      ((ctx=_ble_ctx_CMDX1,_ble_syntax_bash_is_command_form_for=1))
    elif [[ $tail == $'\n'* ]]; then
      if ((ctx==_ble_ctx_CARGX2)); then
        ((_ble_syntax_attr[i++]=_ble_ctx_ARGX))
      else
        ((_ble_syntax_attr[i++]=_ble_attr_ERR,ctx=_ble_ctx_ARGX))
      fi
      return 0
    elif [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
      return 0
    elif ((ctx!=_ble_ctx_COARGX)); then
      local i0=$i
      ((ctx=_ble_ctx_ARGX))
      ble/syntax:bash/ctx-command/.check-delimiter-or-redirect || ((i++))
      ((_ble_syntax_attr[i0]=_ble_attr_ERR))
      return 0
    fi
  fi
  local i0=$i
  if ble/syntax:bash/check-comment; then
    if ((ctx==_ble_ctx_FARGX1||ctx==_ble_ctx_SARGX1||ctx==_ble_ctx_CARGX1||ctx==_ble_ctx_COARGX)); then
      ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    fi
    return 0
  fi
  ble/syntax:bash/ctx-command
}
function ble/syntax:bash/ctx-command-expect/.match-word {
  local word=$1 len=${#1}
  if [[ $tail == "$word"* ]]; then
    ble/syntax/parse/set-lookahead "$((len+1))"
    if ((${#tail}==len)) || i=$((i+len)) ble/syntax:bash/check-word-end/is-delimiter; then
      return 0
    fi
  fi
  return 1
}
function ble/syntax:bash/ctx-command-time-expect {
  ble/util/assert '((ctx==_ble_ctx_TARGX1||ctx==_ble_ctx_TARGX2))'
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    ble/util/assert '((wbegin<0&&wtype<0))'
    if [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
      return 0
    else
      ((ctx=_ble_ctx_CMDXT))
      ble/syntax:bash/ctx-command/.check-delimiter-or-redirect; return "$?"
    fi
  fi
  if ((ctx==_ble_ctx_TARGX1)); then
    ble/syntax:bash/ctx-command-expect/.match-word '-p' ||
      ((ctx=_ble_bash>=50100?_ble_ctx_TARGX2:_ble_ctx_CMDXT))
  fi
  if ((ctx==_ble_ctx_TARGX2)); then
    ble/syntax:bash/ctx-command-expect/.match-word '--' ||
      ((ctx=_ble_ctx_CMDXT))
  fi
  ble/syntax:bash/ctx-command
}
function ble/syntax:bash/ctx-command-case-pattern-expect {
  ble/util/assert '((ctx==_ble_ctx_CPATX||ctx==_ble_ctx_CPATX0))'
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    local delimiter=$BASH_REMATCH
    if [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
    elif [[ $tail == '|'* ]]; then
      ((_ble_syntax_attr[i++]=ctx==_ble_ctx_CPATX?_ble_attr_ERR:_ble_attr_GLOB,ctx=_ble_ctx_CPATX))
    elif [[ $tail == ')'* ]]; then
      ((_ble_syntax_attr[i++]=ctx==_ble_ctx_CPATX?_ble_attr_ERR:_ble_attr_GLOB,ctx=_ble_ctx_CMDX))
    elif [[ $tail == '('* ]]; then
      ble/syntax:bash/ctx-command/.check-delimiter-or-redirect
    else
      ((_ble_syntax_attr[i]=_ble_attr_ERR,i+=${#delimiter}))
    fi
    return "$?"
  fi
  local i0=$i
  if ble/syntax:bash/check-comment; then
    ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    return 0
  fi
  ble/syntax:bash/ctx-command
}
function ble/syntax:bash/ctx-command-function-expect {
  ble/util/assert '((ctx==_ble_ctx_FNAMEX))'
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    if [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
      return 0
    else
      local i0=$i
      ble/syntax:bash/ctx-command/.check-delimiter-or-redirect &&
        ((_ble_syntax_attr[i0]=_ble_attr_ERR))
      return "$?"
    fi
  fi
  local i0=$i
  if ble/syntax:bash/check-comment; then
    ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    return 0
  fi
  ble/syntax:bash/ctx-command
}
_ble_syntax_context_proc[_ble_ctx_VALX]=ble/syntax:bash/ctx-values
_ble_syntax_context_proc[_ble_ctx_VALI]=ble/syntax:bash/ctx-values
_ble_syntax_context_end[_ble_ctx_VALI]=ble/syntax:bash/ctx-values/check-word-end
_ble_syntax_context_proc[_ble_ctx_VALR]=ble/syntax:bash/ctx-values
_ble_syntax_context_end[_ble_ctx_VALR]=ble/syntax:bash/ctx-values/check-word-end
_ble_syntax_context_proc[_ble_ctx_VALQ]=ble/syntax:bash/ctx-values
_ble_syntax_context_end[_ble_ctx_VALQ]=ble/syntax:bash/ctx-values/check-word-end
function ble/syntax:bash/ctx-values/enter {
  local outer_nparam=$nparam
  ble/syntax/parse/nest-push "$_ble_ctx_VALX"
  nparam=$outer_nparam
}
function ble/syntax:bash/ctx-values/leave {
  local inner_nparam=$nparam
  ble/syntax/parse/nest-pop
  nparam=$inner_nparam
}
function ble/syntax:bash/ctx-values/check-word-end {
  ((wbegin<0)) && return 1
  [[ ${text:i:1} == [!"$_ble_term_IFS;|&<>()"] ]] && return 1
  local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i
  local word=${text:wbegin:wlen}
  ble/syntax/parse/word-pop
  ble/util/assert '((ctx==_ble_ctx_VALI||ctx==_ble_ctx_VALR||ctx==_ble_ctx_VALQ))' 'invalid context'
  ((ctx=_ble_ctx_VALX))
  return 0
}
function ble/syntax:bash/ctx-values {
  if ble/syntax:bash/starts-with-delimiter; then
    ble/util/assert '((ctx==_ble_ctx_VALX))' "invalid ctx=$ctx @ i=$i"
    ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char."
    if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then
      local spaces=$BASH_REMATCH
      ble/syntax:bash/check-here-document-from "$spaces" && return 0
      ((_ble_syntax_attr[i]=ctx,i+=${#spaces}))
      return 0
    elif [[ $tail == ')'* ]]; then
      ((_ble_syntax_attr[i++]=_ble_attr_DEL))
      ble/syntax:bash/ctx-values/leave
      return 0
    elif [[ $type == ';'* ]]; then
      ((_ble_syntax_attr[i++]=_ble_attr_ERR))
      return 0
    else
      ((_ble_syntax_attr[i++]=_ble_attr_ERR))
      return 0
    fi
  fi
  if ble/syntax:bash/check-comment; then
    return 0
  fi
  if ((wbegin<0)); then
    ((ctx=_ble_ctx_VALI))
    ble/syntax/parse/word-push "$ctx" "$i"
  fi
  ble/util/assert '((ctx==_ble_ctx_VALI||ctx==_ble_ctx_VALR||ctx==_ble_ctx_VALQ))' "invalid context ctx=$ctx"
  if ble/syntax:bash/check-variable-assignment; then
    return 0
  elif ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[_ble_ctx_ARGI]}]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/check-tilde-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
_ble_syntax_context_proc[_ble_ctx_CONDX]=ble/syntax:bash/ctx-conditions
_ble_syntax_context_proc[_ble_ctx_CONDI]=ble/syntax:bash/ctx-conditions
_ble_syntax_context_end[_ble_ctx_CONDI]=ble/syntax:bash/ctx-conditions/check-word-end
_ble_syntax_context_proc[_ble_ctx_CONDQ]=ble/syntax:bash/ctx-conditions
_ble_syntax_context_end[_ble_ctx_CONDQ]=ble/syntax:bash/ctx-conditions/check-word-end
function ble/syntax:bash/ctx-conditions/check-word-end {
  ((wbegin<0)) && return 1
  [[ ${text:i:1} == [!"$_ble_term_IFS;|&<>()"] ]] && return 1
  local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i
  local word=${text:wbegin:wlen}
  ble/syntax/parse/word-pop
  ble/util/assert '((ctx==_ble_ctx_CONDI||ctx==_ble_ctx_CONDQ))' 'invalid context'
  if [[ $word == ']]' ]]; then
    ble/syntax/parse/touch-updated-attr "$wbeg"
    ((_ble_syntax_attr[wbeg]=_ble_attr_DEL))
    ble/syntax/parse/nest-pop
  else
    ((ctx=_ble_ctx_CONDX))
  fi
  return 0
}
function ble/syntax:bash/ctx-conditions {
  if ble/syntax:bash/starts-with-delimiter; then
    ble/util/assert '((ctx==_ble_ctx_CONDX))' "invalid ctx=$ctx @ i=$i"
    ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char."
    if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
      return 0
    else
      ((_ble_syntax_attr[i++]=_ble_ctx_CONDI))
      return 0
    fi
  fi
  ble/syntax:bash/check-comment && return 0
  if ((wbegin<0)); then
    ((ctx=_ble_ctx_CONDI))
    ble/syntax/parse/word-push "$ctx" "$i"
  fi
  ble/util/assert '((ctx==_ble_ctx_CONDI||ctx==_ble_ctx_CONDQ))' "invalid context ctx=$ctx"
  if ble/syntax:bash/check-variable-assignment; then
    return 0
  elif ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[_ble_ctx_ARGI]}]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/check-tilde-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i++]=ctx))
    return 0
  else
    ((_ble_syntax_attr[i++]=ctx))
    return 0
  fi
  return 1
}
_ble_syntax_context_proc[_ble_ctx_RDRF]=ble/syntax:bash/ctx-redirect
_ble_syntax_context_proc[_ble_ctx_RDRD]=ble/syntax:bash/ctx-redirect
_ble_syntax_context_proc[_ble_ctx_RDRD2]=ble/syntax:bash/ctx-redirect
_ble_syntax_context_proc[_ble_ctx_RDRS]=ble/syntax:bash/ctx-redirect
_ble_syntax_context_end[_ble_ctx_RDRF]=ble/syntax:bash/ctx-redirect/check-word-end
_ble_syntax_context_end[_ble_ctx_RDRD]=ble/syntax:bash/ctx-redirect/check-word-end
_ble_syntax_context_end[_ble_ctx_RDRD2]=ble/syntax:bash/ctx-redirect/check-word-end
_ble_syntax_context_end[_ble_ctx_RDRS]=ble/syntax:bash/ctx-redirect/check-word-end
function ble/syntax:bash/ctx-redirect/check-word-begin {
  if ((wbegin<0)); then
    ble/syntax/parse/word-push "$ctx" "$i"
    ble/syntax/parse/touch-updated-word "$i" #■これは不要では?
  fi
}
function ble/syntax:bash/ctx-redirect/check-word-end {
  ((wbegin<0)) && return 1
  ble/syntax:bash/check-word-end/is-delimiter || return 1
  ble/syntax/parse/word-pop
  ble/syntax/parse/nest-pop
  ble/util/assert '((!_ble_syntax_bash_command_isARGI[ctx]))' "invalid ctx=$ctx in words"
  return 0
}
function ble/syntax:bash/ctx-redirect {
  if ble/syntax:bash/starts-with-delimiter-or-redirect; then
    ((_ble_syntax_attr[i++]=_ble_attr_ERR))
    [[ ${tail:1} =~ ^$_ble_syntax_bash_RexSpaces ]] &&
      ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH}))
    return 0
  fi
  if local i0=$i; ble/syntax:bash/check-comment; then
    ((_ble_syntax_attr[i0]=_ble_attr_ERR))
    return 0
  fi
  ble/syntax:bash/ctx-redirect/check-word-begin
  if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[_ble_ctx_ARGI]}]+"; then
    return 0
  elif ble/syntax:bash/check-process-subst; then
    return 0
  elif ble/syntax:bash/check-quotes; then
    return 0
  elif ble/syntax:bash/check-dollar; then
    return 0
  elif ble/syntax:bash/check-glob; then
    return 0
  elif ble/syntax:bash/check-brace-expansion; then
    return 0
  elif ble/syntax:bash/check-tilde-expansion; then
    return 0
  elif ble/syntax:bash/starts-with-histchars; then
    ble/syntax:bash/check-history-expansion ||
      ((_ble_syntax_attr[i]=ctx,i++))
    return 0
  fi
  return 1
}
_ble_syntax_bash_heredoc_EscSP='\040'
_ble_syntax_bash_heredoc_EscHT='\011'
_ble_syntax_bash_heredoc_EscLF='\012'
_ble_syntax_bash_heredoc_EscFS='\034'
function ble/syntax:bash/ctx-heredoc-word/initialize {
  local ret
  ble/util/s2c ' '
  ble/util/sprintf _ble_syntax_bash_heredoc_EscSP '\\%03o' "$ret"
  ble/util/s2c $'\t'
  ble/util/sprintf _ble_syntax_bash_heredoc_EscHT '\\%03o' "$ret"
  ble/util/s2c $'\n'
  ble/util/sprintf _ble_syntax_bash_heredoc_EscLF '\\%03o' "$ret"
  ble/util/s2c "$_ble_term_FS"
  ble/util/sprintf _ble_syntax_bash_heredoc_EscFS '\\%03o' "$ret"
}
ble/syntax:bash/ctx-heredoc-word/initialize
function ble/syntax:bash/ctx-heredoc-word/remove-quotes {
  local text=$1 result=
  local rex='^[^\$"'\'']+|^\$?["'\'']|^\\.?|^.'
  while [[ $text && $text =~ $rex ]]; do
    local rematch=$BASH_REMATCH
    if [[ $rematch == \" || $rematch == \$\" ]]; then
      if rex='^\$?"(([^\"]|\\.)*)(\\?$|")'; [[ $text =~ $rex ]]; then
        local str=${BASH_REMATCH[1]}
        local a b
        b='\`' a='`'; str=${str//"$b"/"$a"}
        b='\"' a='"'; str=${str//"$b"/"$a"} # WA #D1751 checked
        b='\$' a='$'; str=${str//"$b"/"$a"}
        b='\\' a='\'; str=${str//"$b"/"$a"}
        result=$result$str
        text=${text:${#BASH_REMATCH}}
        continue
      fi
    elif [[ $rematch == \' ]]; then
      if rex="^('[^']*)'?"; [[ $text =~ $rex ]]; then
        builtin eval "result=\$result${BASH_REMATCH[1]}'"
        text=${text:${#BASH_REMATCH}}
        continue
      fi
    elif [[ $rematch == \$\' ]]; then
      if rex='^(\$'\''([^\'\'']|\\.)*)('\''|\\?$)'; [[ $text =~ $rex ]]; then
        builtin eval "result=\$result${BASH_REMATCH[1]}'"
        text=${text:${#BASH_REMATCH}}
        continue
      fi
    elif [[ $rematch == \\* ]]; then
      result=$result${rematch:1}
      text=${text:${#rematch}}
      continue
    fi
    result=$result$rematch
    text=${text:${#rematch}}
  done
  delimiter=$result$text
}
function ble/syntax:bash/ctx-heredoc-word/escape-delimiter {
  local out=$1
  if [[ $out == *[\\\'$_ble_term_IFS$_ble_term_FS]* ]]; then
    local a b fs=$_ble_term_FS
    a=\\   ; b='\'$a; out=${out//"$a"/"$b"}
    a=\'   ; b='\'$a; out=${out//"$a"/"$b"}
    a=' '  ; b=$_ble_syntax_bash_heredoc_EscSP; out=${out//"$a"/"$b"}
    a=$'\t'; b=$_ble_syntax_bash_heredoc_EscHT; out=${out//"$a"/"$b"}
    a=$'\n'; b=$_ble_syntax_bash_heredoc_EscLF; out=${out//"$a"/"$b"}
    a=$fs  ; b=$_ble_syntax_bash_heredoc_EscFS; out=${out//"$a"/"$b"}
  fi
  escaped=$out
}
function ble/syntax:bash/ctx-heredoc-word/unescape-delimiter {
  builtin eval "delimiter=\$'$1'"
}
_ble_syntax_context_proc[_ble_ctx_RDRH]=ble/syntax:bash/ctx-heredoc-word
_ble_syntax_context_end[_ble_ctx_RDRH]=ble/syntax:bash/ctx-heredoc-word/check-word-end
_ble_syntax_context_proc[_ble_ctx_RDRI]=ble/syntax:bash/ctx-heredoc-word
_ble_syntax_context_end[_ble_ctx_RDRI]=ble/syntax:bash/ctx-heredoc-word/check-word-end
function ble/syntax:bash/ctx-heredoc-word/check-word-end {
  ((wbegin<0)) && return 1
  ble/syntax:bash/check-word-end/is-delimiter || return 1
  local octx=$ctx word=${text:wbegin:i-wbegin}
  ble/syntax/parse/word-pop
  ble/syntax/parse/nest-pop
  local I
  if ((octx==_ble_ctx_RDRI)); then I=I; else I=R; fi
  local Q delimiter
  if [[ $word == *[\'\"\\]* ]]; then
    Q=Q; ble/syntax:bash/ctx-heredoc-word/remove-quotes "$word"
  else
    Q=H; delimiter=$word
  fi
  local escaped; ble/syntax:bash/ctx-heredoc-word/escape-delimiter "$delimiter"
  nparam=$nparam$_ble_term_FS@$I$Q$escaped
  return 0
}
function ble/syntax:bash/ctx-heredoc-word {
  ble/syntax:bash/ctx-redirect
}
_ble_syntax_context_proc[_ble_ctx_HERE0]=ble/syntax:bash/ctx-heredoc-content
_ble_syntax_context_proc[_ble_ctx_HERE1]=ble/syntax:bash/ctx-heredoc-content
function ble/syntax:bash/ctx-heredoc-content {
  local indented= quoted= delimiter=
  ble/syntax:bash/ctx-heredoc-word/unescape-delimiter "${nparam:2}"
  [[ ${nparam::1} == I ]] && indented=1
  [[ ${nparam:1:1} == Q ]] && quoted=1
  local rex ht=$'\t' lf=$'\n'
  if ((ctx==_ble_ctx_HERE0)); then
    rex="^${indented:+$ht*}"$'([^\n]+\n?|\n)'
    [[ $tail =~ $rex ]] || return 1
    local line=${BASH_REMATCH%"$lf"}
    local rematch1=${BASH_REMATCH[1]}
    if [[ ${rematch1%"$lf"} == "$delimiter" ]]; then
      local indent
      ((indent=${#BASH_REMATCH}-${#rematch1},
        _ble_syntax_attr[i]=_ble_ctx_HERE0,
        _ble_syntax_attr[i+indent]=_ble_ctx_RDRH,
        i+=${#line}))
      ble/syntax/parse/nest-pop
      return 0
    fi
  fi
  if [[ $quoted ]]; then
    ble/util/assert '((ctx==_ble_ctx_HERE0))'
    ((_ble_syntax_attr[i]=_ble_ctx_HERE0,i+=${#BASH_REMATCH}))
    return 0
  else
    ((ctx=_ble_ctx_HERE1))
    if rex='^(\\[\$`'$lf'])|^([^'${_ble_syntax_bash_chars[_ble_ctx_HERE1]}']|\\[^\$`'$lf'])+'$lf'?|^'$lf && [[ $tail =~ $rex ]]; then
      if [[ ${BASH_REMATCH[1]} ]]; then
        ((_ble_syntax_attr[i]=_ble_attr_QESC))
      else
        ((_ble_syntax_attr[i]=_ble_ctx_HERE0))
        [[ $BASH_REMATCH == *"$lf" ]] && ((ctx=_ble_ctx_HERE0))
      fi
      ((i+=${#BASH_REMATCH}))
      return 0
    fi
    if ble/syntax:bash/check-dollar; then
      return 0
    elif [[ $tail == '`'* ]] && ble/syntax:bash/check-quotes; then
      return 0
    elif ble/syntax:bash/starts-with-histchars; then
      ble/syntax:bash/check-history-expansion ||
        ((_ble_syntax_attr[i]=_ble_ctx_HERE0,i++))
      return 0
    else
      ((_ble_syntax_attr[i]=_ble_ctx_HERE0,i++))
      return 0
    fi
  fi
}
function ble/syntax:bash/is-complete {
  local iN=${#_ble_syntax_text}
  ((iN>0)) && ((_ble_syntax_attr[iN-1]==_ble_attr_ERR)) && return 1
  local stat=${_ble_syntax_stat[iN]}
  if [[ $stat ]]; then
    ble/string#split-words stat "$stat"
    local nlen=${stat[3]}; ((nlen>=0)) && return 1
    local nparam=${stat[6]}; [[ $nparam == none ]] && nparam=
    local rex="$_ble_term_FS@([RI][QH][^$_ble_term_FS]*)(.*$)"
    [[ $nparam =~ $rex ]] && return 1
    local ctx=${stat[0]}
    ((ctx==_ble_ctx_ARGX||ctx==_ble_ctx_ARGX0||ctx==_ble_ctx_ARGVX||ctx==_ble_ctx_ARGEX||
        ctx==_ble_ctx_CMDX||ctx==_ble_ctx_CMDX0||ctx==_ble_ctx_CMDXT||ctx==_ble_ctx_CMDXE||ctx==_ble_ctx_CMDXV||
        ctx==_ble_ctx_TARGX1||ctx==_ble_ctx_TARGX2)) || return 1
  fi
  local attrs ret
  IFS= builtin eval 'attrs="::${_ble_syntax_attr[*]/%/::}"' # WA #D1570 checked
  ble/string#count-string "$attrs" ":$_ble_attr_KEYWORD_BEGIN:"; local nbeg=$ret
  ble/string#count-string "$attrs" ":$_ble_attr_KEYWORD_END:"; local nend=$ret
  ((nbeg>nend)) && return 1
  return 0
}
function ble/syntax:bash/find-end-of-array-index {
  local beg=$1 end=$2
  ret=
  local inest0=$beg nest0
  [[ ${_ble_syntax_nest[inest0]} ]] || return 1
  local q stat1 nlen1 inest1 r=
  for ((q=inest0+1;q<end;q++)); do
    local stat1=${_ble_syntax_stat[q]}
    [[ $stat1 ]] || continue
    ble/string#split-words stat1 "$stat1"
    ((nlen1=stat1[3])) # (workaround Bash-4.2 segfault)
    ((inest1=nlen1<0?nlen1:q-nlen1))
    ((inest1<inest0)) && break
    ((r=q))
  done
  [[ ${_ble_syntax_text:r:end-r} == ']'* ]] && ret=$r
  [[ $ret ]]
}
function ble/syntax:bash/find-rhs {
  local wtype=$1 wbeg=$2 wlen=$3 opts=$4
  local text=$_ble_syntax_text
  local word=${text:wbeg:wlen} wend=$((wbeg+wlen))
  local rex=
  if ((wtype==_ble_attr_VAR)); then
    rex='^[_a-zA-Z0-9]+(\+?=|\[)'
  elif ((wtype==_ble_ctx_VALI)); then
    if [[ :$opts: == *:element-assignment:* ]]; then
      rex='^[_a-zA-Z0-9]+(\+?=|\[)|^(\[)'
    else
      rex='^(\[)'
    fi
  fi
  if [[ :$opts: == *:long-option:* ]]; then
    rex=${rex:+$rex'|'}'^--[-_a-zA-Z0-9]+='
  fi
  if [[ $rex && $word =~ $rex ]]; then
    local last_char=${BASH_REMATCH:${#BASH_REMATCH}-1}
    if [[ $last_char == '[' ]]; then
      local p1=$((wbeg+${#BASH_REMATCH}-1))
      if ble/syntax:bash/find-end-of-array-index "$p1" "$wend"; then
        local p2=$ret
        case ${text:p2:wend-p2} in
        (']='*)  ((ret=p2+2)); return 0 ;;
        (']+='*) ((ret=p2+3)); return 0 ;;
        esac
      fi
    else
      ((ret=wbeg+${#BASH_REMATCH}))
      return 0
    fi
  fi
  ret=$wbeg
  return 1
}
_ble_syntax_vanishing_word_umin=-1
_ble_syntax_vanishing_word_umax=-1
function ble/syntax/vanishing-word/register {
  local tree_array=$1 tofs=$2
  local beg=$3 end=$4 lbeg=$5 lend=$6
  (((beg<=0)&&(beg=1)))
  local node i nofs
  for ((i=end;i>=beg;i--)); do
    builtin eval "node=(\${$tree_array[tofs+i-1]})"
    ((${#node[@]})) || continue
    for ((nofs=0;nofs<${#node[@]};nofs+=_ble_syntax_TREE_WIDTH)); do
      local wtype=${node[nofs]} wlen=${node[nofs+1]}
      local wbeg=$((wlen<0?wlen:i-wlen)) wend=$i
      ((wbeg<lbeg&&(wbeg=lbeg),
        wend>lend&&(wend=lend)))
      ble/syntax/urange#update _ble_syntax_vanishing_word_ "$wbeg" "$wend"
    done
  done
}
function ble/syntax/parse/shift.stat {
  if [[ ${_ble_syntax_stat[shift2_j]} ]]; then
    local -a stat; ble/string#split-words stat "${_ble_syntax_stat[shift2_j]}"
    local k klen kbeg
    for k in 1 3 4 5; do # wlen nlen tclen tplen
      (((klen=stat[k])<0)) && continue
      ((kbeg=shift2_j-klen))
      if ((kbeg<beg)); then
        ((stat[k]+=shift))
      elif ((kbeg<end0)); then
        ((stat[k]-=end0-kbeg))
      fi
    done
    _ble_syntax_stat[shift2_j]="${stat[*]}"
  fi
}
function ble/syntax/parse/shift.tree/1 {
  local k klen kbeg
  for k in 1 2 3; do # wlen/nlen tclen tplen
    ((klen=node[nofs+k]))
    ((klen<0||(kbeg=shift2_j-klen)>end0)) && continue
    if [[ $k == 1 && ${node[nofs]} =~ ^[0-9]$ ]]; then
      ble/syntax/parse/touch-updated-word "$shift2_j"
      node[nofs+4]='-'
    fi
    if ((kbeg<beg)); then
      ((node[nofs+k]+=shift))
    elif ((kbeg<end0)); then
      ((node[nofs+k]-=end0-kbeg))
    fi
  done
}
function ble/syntax/parse/shift.tree {
  [[ ${_ble_syntax_tree[shift2_j-1]} ]] || return 1
  local -a node
  ble/string#split-words node "${_ble_syntax_tree[shift2_j-1]}"
  local nofs
  if [[ $1 ]]; then
    nofs=$1 ble/syntax/parse/shift.tree/1
  else
    for ((nofs=0;nofs<${#node[@]};nofs+=_ble_syntax_TREE_WIDTH)); do
      ble/syntax/parse/shift.tree/1
    done
  fi
  _ble_syntax_tree[shift2_j-1]="${node[*]}"
}
function ble/syntax/parse/shift.nest {
  if [[ ${_ble_syntax_nest[shift2_j]} ]]; then
    local -a nest
    ble/string#split-words nest "${_ble_syntax_nest[shift2_j]}"
    local k klen kbeg
    for k in 1 3 4 5; do
      (((klen=nest[k])))
      ((klen<0||(kbeg=shift2_j-klen)<0)) && continue
      if ((kbeg<beg)); then
        ((nest[k]+=shift))
      elif ((kbeg<end0)); then
        ((nest[k]-=end0-kbeg))
      fi
    done
    _ble_syntax_nest[shift2_j]="${nest[*]}"
  fi
}
function ble/syntax/parse/shift.impl2/.shift-until {
  local limit=$1
  while ((shift2_j>=limit)); do
    [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift[shift2_j+shift]=1
    ble/syntax/parse/shift.stat
    ble/syntax/parse/shift.nest
    ((shift2_j--))
  done
}
function ble/syntax/parse/shift.impl2/.proc1 {
  if ((TE_i<j2)); then
    ((tprev=-1)) # 中断
    return 0
  fi
  ble/syntax/parse/shift.impl2/.shift-until "$((TE_i+1))"
  ble/syntax/parse/shift.tree "$TE_nofs"
  if ((tprev>end0&&wbegin>end0)) && [[ ${wtype//[0-9]} ]]; then
    [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift[shift2_j+shift]=1
    ble/syntax/parse/shift.stat
    ble/syntax/parse/shift.nest
    ((shift2_j=wbegin)) # skip
  elif ((tchild>=0)); then
    ble/syntax/tree-enumerate-children ble/syntax/parse/shift.impl2/.proc1
  fi
}
function ble/syntax/parse/shift.method1 {
  local i j
  for ((i=i2,j=j2;i<=iN;i++,j++)); do
    local shift2_j=$j
    ble/syntax/parse/shift.stat
    ((j>0))  && ble/syntax/parse/shift.tree
    ((i<iN)) && ble/syntax/parse/shift.nest
  done
}
function ble/syntax/parse/shift.method2 {
  [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift=()
  local iN=${#_ble_syntax_text} # tree-enumerate 起点は (古い text の長さ) である
  local shift2_j=$iN # proc1 に渡す変数
  ble/syntax/tree-enumerate ble/syntax/parse/shift.impl2/.proc1
  ble/syntax/parse/shift.impl2/.shift-until "$j2" # 未処理部分
}
function ble/syntax/parse/shift {
  ble/syntax/parse/shift.method2 # tree-enumerate による skip
  if ((shift!=0)); then
    ble/syntax/urange#shift _ble_syntax_attr_
    ble/syntax/wrange#shift _ble_syntax_word_
    ble/syntax/wrange#shift _ble_syntax_word_defer_
    ble/syntax/urange#shift _ble_syntax_vanishing_word_
  fi
}
_ble_syntax_dbeg=-1 _ble_syntax_dend=-1
function ble/syntax/parse/determine-parse-range {
  local flagSeekStat=0
  ((i1=_ble_syntax_dbeg,i1>=end0&&(i1+=shift),
    i2=_ble_syntax_dend,i2>=end0&&(i2+=shift),
    (i1<0||beg<i1)&&(i1=beg,flagSeekStat=1),
    (i2<0||i2<end)&&(i2=end),
    (i2>iN)&&(i2=iN),
    j2=i2-shift))
  if ((flagSeekStat)); then
    local lookahead='stat[7]'
    local -a stat
    while ((i1>0)); do
      if [[ ${_ble_syntax_stat[--i1]} ]]; then
        ble/string#split-words stat "${_ble_syntax_stat[i1]}"
        ((i1+lookahead<=beg)) && break
      fi
    done
  fi
  ble/util/assert '((0<=i1&&i1<=beg&&end<=i2&&i2<=iN))' "X2 0 <= $i1 <= $beg <= $end <= $i2 <= $iN"
}
function ble/syntax/parse/check-end {
  [[ ${_ble_syntax_context_end[ctx]} ]] && "${_ble_syntax_context_end[ctx]}"
}
function ble/syntax/parse {
  local text=$1 iN=${#1}
  local opts=$2
  local beg=${3:-0} end=${4:-$iN} end0=${5:-0}
  ((end==beg&&end0==beg&&_ble_syntax_dbeg<0)) && return 0
  local IFS=$_ble_term_IFS
  local shift=$((end-end0))
  ble/util/assert \
    '((0<=beg&&beg<=end&&end<=iN&&beg<=end0))' \
    "X1 0 <= beg:$beg <= end:$end <= iN:$iN, beg:$beg <= end0:$end0 (shift=$shift text=$text)" ||
    ((beg=0,end=iN))
  local i1 i2 j2
  ble/syntax/parse/determine-parse-range
  ble/syntax/vanishing-word/register _ble_syntax_tree 0 "$i1" "$j2" 0 "$i2"
  ble/syntax/parse/shift
  local ctx wbegin wtype inest tchild tprev nparam ilook
  if ((i1>0)) && [[ ${_ble_syntax_stat[i1]} ]]; then
    local -a stat
    ble/string#split-words stat "${_ble_syntax_stat[i1]}"
    local wlen=${stat[1]} nlen=${stat[3]} tclen=${stat[4]} tplen=${stat[5]}
    ctx=${stat[0]}
    wbegin=$((wlen<0?wlen:i1-wlen))
    wtype=${stat[2]}
    inest=$((nlen<0?nlen:i1-nlen))
    tchild=$((tclen<0?tclen:i1-tclen))
    tprev=$((tplen<0?tplen:i1-tplen))
    nparam=${stat[6]}; [[ $nparam == none ]] && nparam=
    ilook=$((i1+${stat[7]:-1}))
  else
    ctx=$_ble_ctx_UNSPECIFIED ##!< 現在の解析の文脈
    ble/syntax:"$_ble_syntax_lang"/initialize-ctx # ctx 初期化
    wbegin=-1       ##!< シェル単語内にいる時、シェル単語の開始位置
    wtype=-1        ##!< シェル単語内にいる時、シェル単語の種類
    inest=-1        ##!< 入れ子の時、親の開始位置
    tchild=-1
    tprev=-1
    nparam=
    ilook=1
  fi
  local -a tail_syntax_stat tail_syntax_tree tail_syntax_nest tail_syntax_attr
  tail_syntax_stat=("${_ble_syntax_stat[@]:j2:iN-i2+1}")
  tail_syntax_tree=("${_ble_syntax_tree[@]:j2:iN-i2}")
  tail_syntax_nest=("${_ble_syntax_nest[@]:j2:iN-i2}")
  tail_syntax_attr=("${_ble_syntax_attr[@]:j2:iN-i2}")
  ble/array#reserve-prototype "$iN"
  _ble_syntax_stat=("${_ble_syntax_stat[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 再開用データ
  _ble_syntax_tree=("${_ble_syntax_tree[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 単語
  _ble_syntax_nest=("${_ble_syntax_nest[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 入れ子の親
  _ble_syntax_attr=("${_ble_syntax_attr[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 文脈・色とか
  ble/syntax:"$_ble_syntax_lang"/initialize-vars
  _ble_syntax_text=$text
  local i sstat tail
  local debug_p1
  for ((i=i1;i<iN;)); do
    ble/syntax/parse/serialize-stat
    if ((i>=i2)) && [[ ${tail_syntax_stat[i-i2]} == "$sstat" ]]; then
      if ble/syntax/parse/nest-equals "$inest"; then
        _ble_syntax_stat=("${_ble_syntax_stat[@]::i}" "${tail_syntax_stat[@]:i-i2}")
        _ble_syntax_tree=("${_ble_syntax_tree[@]::i}" "${tail_syntax_tree[@]:i-i2}")
        _ble_syntax_nest=("${_ble_syntax_nest[@]::i}" "${tail_syntax_nest[@]:i-i2}")
        _ble_syntax_attr=("${_ble_syntax_attr[@]::i}" "${tail_syntax_attr[@]:i-i2}")
        break
      fi
    fi
    _ble_syntax_stat[i]=$sstat
    tail=${text:i}
    debug_p1=$i
    "${_ble_syntax_context_proc[ctx]}" || ((_ble_syntax_attr[i]=_ble_attr_ERR,i++))
    ble/syntax/parse/check-end
  done
  builtin unset -v debug_p1
  ble/syntax/vanishing-word/register tail_syntax_tree "$((-i2))" "$((i2+1))" "$i" 0 "$i"
  ble/syntax/urange#update _ble_syntax_attr_ "$i1" "$i"
  (((i>=i2)?(
      _ble_syntax_dbeg=_ble_syntax_dend=-1
    ):(
      _ble_syntax_dbeg=i,_ble_syntax_dend=i2)))
  if ((i>=iN)); then
    ((i=iN))
    ble/syntax/parse/serialize-stat
    _ble_syntax_stat[i]=$sstat
    if ((inest>0)); then
      ((_ble_syntax_attr[iN-1]=_ble_attr_ERR))
      while ((inest>=0)); do
        ((i=inest))
        ble/syntax/parse/nest-pop
        ((inest>=i&&(inest=i-1)))
      done
    fi
  fi
  ble/util/assert \
    '((${#_ble_syntax_stat[@]}==iN+1))' \
    "unexpected array length #arr=${#_ble_syntax_stat[@]} (expected to be $iN), #proto=${#_ble_array_prototype[@]} should be >= $iN"
}
function ble/syntax/highlight {
  local text=$1 lang=${2:-bash} cache_prefix=$3
  local -a _ble_highlight_layer_list=(plain syntax)
  local -a vars=()
  ble/array#push vars "${_ble_syntax_VARNAMES[@]}"
  ble/array#push vars "${_ble_highlight_layer_plain_VARNAMES[@]}"
  ble/array#push vars "${_ble_highlight_layer_syntax_VARNAMES[@]}"
  local "${vars[@]/%/=}" # WA #D1570 checked
  if [[ $cache_prefix ]] && ((${cache_prefix}_INITIALIZED++)); then
    ble/util/restore-vars "$cache_prefix" "${vars[@]}"
    ble/string#common-prefix "$_ble_syntax_text" "$text"
    local beg=${#ret}
    ble/string#common-suffix "${_ble_syntax_text:beg}" "${text:beg}"
    local end=$((${#text}-${#ret})) end0=$((${#_ble_syntax_text}-${#ret}))
  else
    ble/syntax/initialize-vars
    ble/highlight/layer:plain/initialize-vars
    ble/highlight/layer:syntax/initialize-vars
    _ble_syntax_lang=$lang
    local beg=0 end=${#text} end0=0
  fi
  ble/syntax/parse "$text" '' "$beg" "$end" "$end0"
  local HIGHLIGHT_BUFF HIGHLIGHT_UMIN HIGHLIGHT_UMAX
  ble/highlight/layer/update "$text" '' "$beg" "$end" "$end0"
  IFS= builtin eval "ret=\"\${$HIGHLIGHT_BUFF[*]}\""
  [[ $cache_prefix ]] &&
    ble/util/save-vars "$cache_prefix" "${vars[@]}"
  return 0
}
function ble/syntax/completion-context/add {
  local source=$1
  local comp1=$2
  ble/util/assert '[[ $source && comp1 -ge 0 ]]'
  sources[${#sources[*]}]="$source $comp1"
}
function ble/syntax/completion-context/.check/parameter-expansion {
  local rex_paramx='^(\$(\{[!#]?)?)([_a-zA-Z][_a-zA-Z0-9]*)?$'
  if [[ ${text:istat:index-istat} =~ $rex_paramx ]]; then
    local rematch1=${BASH_REMATCH[1]}
    local source=variable
    if [[ $rematch1 == '${'* ]]; then
      source=variable:b # suffix }
    elif ((ctx==_ble_ctx_BRACE1||ctx==_ble_ctx_BRACE2)); then
      source=variable:n # no suffix
    fi
    ble/syntax/completion-context/add "$source" "$((istat+${#rematch1}))"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDI]=inside-command
function ble/syntax/completion-context/prefix:inside-command {
  if ((wlen>=0)); then
    ble/syntax/completion-context/add command "$wbeg"
  fi
  ble/syntax/completion-context/.check/parameter-expansion
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGI]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGQ]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGI1]='inside-argument variable:w'
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGI3]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGQ3]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CARGI1]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CARGQ1]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CPATI]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CPATQ]='inside-argument argument'
_ble_syntax_completion_context_check_prefix[_ble_ctx_COARGI]='inside-argument variable command:V'
_ble_syntax_completion_context_check_prefix[_ble_ctx_VALI]='inside-argument sabbrev file'
_ble_syntax_completion_context_check_prefix[_ble_ctx_VALQ]='inside-argument sabbrev file'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CONDI]='inside-argument sabbrev file option'
_ble_syntax_completion_context_check_prefix[_ble_ctx_CONDQ]='inside-argument sabbrev file'
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGVI]='inside-argument sabbrev variable:='
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGEI]='inside-argument command:D file'
function ble/syntax/completion-context/prefix:inside-argument {
  if ((wlen>=0)); then
    local source
    for source; do
      ble/syntax/completion-context/add "$source" "$wbeg"
      if [[ $source != argument ]]; then
        local sub=${text:wbeg:index-wbeg}
        if [[ $sub == *[=:]* ]]; then
          sub=${sub##*[=:]}
          ble/syntax/completion-context/add "$source" "$((index-${#sub}))"
        fi
      fi
    done
  fi
  ble/syntax/completion-context/.check/parameter-expansion
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDX]=next-command
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDX1]=next-command
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXT]=next-command
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXV]=next-command
function ble/syntax/completion-context/.check-prefix/.test-redirection {
  local word=$1
  [[ $word =~ ^$_ble_syntax_bash_RexRedirect$ ]] || return 1
  ((ctx==_ble_ctx_CMDXC||ctx==_ble_ctx_CMDXD||ctx==_ble_ctx_CMDXD0||ctx==_ble_ctx_FARGX3)) && return 0
  local rematch3=${BASH_REMATCH[3]}
  case $rematch3 in
  ('>&')
    ble/syntax/completion-context/add fd "$index"
    ble/syntax/completion-context/add file:no-fd "$index" ;;
  (*'&')
    ble/syntax/completion-context/add fd "$index" ;;
  ('<<'|'<<-')
    ble/syntax/completion-context/add wordlist:EOF:END:HERE "$index" ;;
  ('<<<'|*)
    ble/syntax/completion-context/add file "$index" ;;
  esac
  return 0
}
function ble/syntax/completion-context/prefix:next-command {
  local word=${text:istat:index-istat}
  if ble/syntax:bash/simple-word/is-simple-or-open-simple "$word"; then
    ble/syntax/completion-context/add command "$istat"
    if ble/string#match "$word" '^[_a-zA-Z][_a-zA-Z0-9]*\+?=$'; then
      if ((_ble_bash>=30100)) || [[ $word != *+= ]]; then
        ble/syntax/completion-context/add argument "$index"
      fi
    fi
  elif ble/syntax/completion-context/.check-prefix/.test-redirection "$word"; then
    builtin true
  elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then
    shopt -q no_empty_cmd_completion ||
      ble/syntax/completion-context/add command "$index"
  fi
  ble/syntax/completion-context/.check/parameter-expansion
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_CARGX1]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_CPATX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGX3]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_COARGX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGVX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGEX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_VALX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_CONDX]=next-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRS]=next-argument
function ble/syntax/completion-context/prefix:next-argument {
  local source
  if ((ctx==_ble_ctx_ARGX||ctx==_ble_ctx_CARGX1||ctx==_ble_ctx_FARGX3)); then
    source=(argument)
  elif ((ctx==_ble_ctx_COARGX)); then
    source=(command:V variable)
  elif ((ctx==_ble_ctx_ARGVX)); then
    source=(sabbrev variable:= option)
  elif ((ctx==_ble_ctx_ARGEX)); then
    source=(command:D file)
  elif ((ctx==_ble_ctx_CONDX)); then
    source=(sabbrev file option)
  else
    source=(sabbrev file)
  fi
  local word=${text:istat:index-istat}
  if ble/syntax:bash/simple-word/is-simple-or-open-simple "$word"; then
    local src
    for src in "${source[@]}"; do
      ble/syntax/completion-context/add "$src" "$istat"
    done
    if [[ ${source[0]} != argument ]]; then
      local rex="^([^='\"\$\\{}]|\\.)*="
      if [[ $word =~ $rex ]]; then
        word=${word:${#BASH_REMATCH}}
        ble/syntax/completion-context/add rhs "$((index-${#word}))"
      fi
    fi
  elif ble/syntax/completion-context/.check-prefix/.test-redirection "$word"; then
    builtin true
  elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then
    local src
    for src in "${source[@]}"; do
      ble/syntax/completion-context/add "$src" "$index"
    done
  fi
  ble/syntax/completion-context/.check/parameter-expansion
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXC]=next-compound
function ble/syntax/completion-context/prefix:next-compound {
  local rex word=${text:istat:index-istat}
  if [[ ${text:istat:index-istat} =~ $rex_param ]]; then
    ble/syntax/completion-context/add wordlist:-r:'for:select:case:if:while:until' "$istat"
  elif rex='^[[({]+$'; [[ $word =~ $rex ]]; then
    ble/syntax/completion-context/add wordlist:-r:'(:{:((:[[' "$istat"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGX1]="next-identifier variable:w" # _ble_ctx_FARGX1 → (( でなければ 変数名
_ble_syntax_completion_context_check_prefix[_ble_ctx_SARGX1]="next-identifier variable:w"
function ble/syntax/completion-context/prefix:next-identifier {
  local source=$1 word=${text:istat:index-istat}
  if [[ $word =~ $rex_param ]]; then
    ble/syntax/completion-context/add "$source" "$istat"
  elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then
    ble/syntax/completion-context/add "$source" "$index"
  else
    ble/syntax/completion-context/add none "$istat"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGX0]="next-word sabbrev"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDX0]="next-word sabbrev"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CPATX0]="next-word sabbrev"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXD0]="next-word wordlist:-rs:';:{:do'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXD]="next-word wordlist:-rs:'{:do'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CMDXE]="next-word wordlist:-rs:'}:fi:done:esac:then:elif:else:do'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CARGX2]="next-word wordlist:-rs:'in'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_CARGI2]="next-word wordlist:-rs:'in'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGX2]="next-word wordlist:-rs:'in:do'"
_ble_syntax_completion_context_check_prefix[_ble_ctx_FARGI2]="next-word wordlist:-rs:'in:do'"
function ble/syntax/completion-context/prefix:next-word {
  local source=$1 word=${text:istat:index-istat} rex=$'^[^ \t]*$'
  if [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then
    ble/syntax/completion-context/add "$source" "$index"
  else
    ble/syntax/completion-context/add "$source" "$istat"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_TARGX1]=time-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_TARGI1]=time-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_TARGX2]=time-argument
_ble_syntax_completion_context_check_prefix[_ble_ctx_TARGI2]=time-argument
function ble/syntax/completion-context/prefix:time-argument {
  ble/syntax/completion-context/.check/parameter-expansion
  ble/syntax/completion-context/add command "$istat"
  if ((ctx==_ble_ctx_TARGX1)); then
    local rex='^-p?$' words='-p'
    ((_ble_bash>=50100)) &&
      rex='^-[-p]?$' words='-p':'--'
    [[ ${text:istat:index-istat} =~ $rex ]] &&
      ble/syntax/completion-context/add wordlist:--:"$words" "$istat"
  elif ((ctx==_ble_ctx_TARGX2)); then
    local rex='^--?$'
    [[ ${text:istat:index-istat} =~ $rex ]] &&
      ble/syntax/completion-context/add wordlist:--:'--' "$istat"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_FNAMEX]=function-name
_ble_syntax_completion_context_check_prefix[_ble_ctx_FNAMEI]=function-name
function ble/syntax/completion-context/prefix:function-name {
  ble/syntax/completion-context/.check/parameter-expansion
  ble/syntax/completion-context/add function "$istat"
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_QUOT]=quote
function ble/syntax/completion-context/prefix:quote {
  ble/syntax/completion-context/.check/parameter-expansion
  ble/syntax/completion-context/prefix:quote/.check-container-word
}
function ble/syntax/completion-context/prefix:quote/.check-container-word {
  local nlen=${stat[3]}; ((nlen>=0)) || return 1
  local inest=$((nlen<0?nlen:istat-nlen))
  local nest; ble/string#split-words nest "${_ble_syntax_nest[inest]}"
  [[ ${nest[0]} ]] || return 1
  local wlen2=${nest[1]}; ((wlen2>=0)) || return 1
  local wbeg2=$((wlen2<0?wlen2:inest-wlen2))
  if ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:wbeg2:index-wbeg2}"; then
    local wt=${nest[2]}
    [[ ${_ble_syntax_bash_command_EndWtype[wt]} ]] &&
      wt=${_ble_syntax_bash_command_EndWtype[wt]}
    if ((wt==_ble_ctx_CMDI)); then
      ble/syntax/completion-context/add command "$wbeg2"
    elif ((wt==_ble_ctx_ARGI||wt==_ble_ctx_ARGVI||wt==_ble_ctx_ARGEI||wt==_ble_ctx_FARGI2||wt==_ble_ctx_CARGI2)); then
      ble/syntax/completion-context/add argument "$wbeg2"
    elif ((wt==_ble_ctx_CPATI)); then # case pattern の内部
      return 0
    fi
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRF]=redirection
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRD2]=redirection
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRD]=redirection
function ble/syntax/completion-context/prefix:redirection {
  ble/syntax/completion-context/.check/parameter-expansion
  local p=$((wlen>=0?wbeg:istat))
  if ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:p:index-p}"; then
    if ((ctx==_ble_ctx_RDRF)); then
      ble/syntax/completion-context/add file "$p"
    elif ((ctx==_ble_ctx_RDRD)); then
      ble/syntax/completion-context/add fd "$p"
    elif ((ctx==_ble_ctx_RDRD2)); then
      ble/syntax/completion-context/add fd "$p"
      ble/syntax/completion-context/add file:no-fd "$p"
    fi
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRH]=here
_ble_syntax_completion_context_check_prefix[_ble_ctx_RDRI]=here
function ble/syntax/completion-context/prefix:here {
  local p=$((wlen>=0?wbeg:istat))
  ble/syntax/completion-context/add wordlist:EOF:END:HERE "$p"
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_VRHS]=rhs
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGVR]=rhs
_ble_syntax_completion_context_check_prefix[_ble_ctx_ARGER]=rhs
_ble_syntax_completion_context_check_prefix[_ble_ctx_VALR]=rhs
function ble/syntax/completion-context/prefix:rhs {
  ble/syntax/completion-context/.check/parameter-expansion
  if ((wlen>=0)); then
    local p=$wbeg
    local rex='^[_a-zA-Z0-9]+(\+?=|\[)'
    ((ctx==_ble_ctx_VALR)) && rex='^(\[)'
    if [[ ${text:p:index-p} =~ $rex ]]; then
      if [[ ${BASH_REMATCH[1]} == '[' ]]; then
        local p1=$((wbeg+${#BASH_REMATCH}-1))
        if local ret; ble/syntax:bash/find-end-of-array-index "$p1" "$index"; then
          local p2=$ret
          case ${_ble_syntax_text:p2:index-p2} in
          (']='*)  ((p=p2+2)) ;;
          (']+='*) ((p=p2+3)) ;;
          (']+')
            ble/syntax/completion-context/add wordlist:-rW:'+=' "$((p2+1))"
            p= ;;
          esac
        fi
      else
        ((p+=${#BASH_REMATCH}))
      fi
    fi
  else
    local p=$istat
  fi
  if [[ $p ]] && ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:p:index-p}"; then
    ble/syntax/completion-context/add rhs "$p"
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_PARAM]=param
function ble/syntax/completion-context/prefix:param {
  local tail=${text:istat:index-istat}
  if [[ $tail == : ]]; then
    return 0
  elif [[ $tail == '}'* ]]; then
    local nlen=${stat[3]}
    local inest=$((nlen<0?nlen:istat-nlen))
    ((0<=inest&&inest<istat)) &&
      ble/syntax/completion-context/.check-prefix "$inest"
    return "$?"
  else
    return 1
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_EXPR]=expr
function ble/syntax/completion-context/prefix:expr {
  local tail=${text:istat:index-istat} rex='[_a-zA-Z]+$'
  if [[ $tail =~ $rex ]]; then
    local p=$((index-${#BASH_REMATCH}))
    ble/syntax/completion-context/add variable:a "$p"
    return 0
  elif [[ $tail == ']'* ]]; then
    local inest=... ntype
    local nlen=${stat[3]}; ((nlen>=0)) || return 1
    local inest=$((istat-nlen))
    ble/syntax/parse/nest-type # ([in] inest; [out] ntype)
    if [[ $ntype == [ad]'[' ]]; then
      if [[ $tail == ']' ]]; then
        ble/syntax/completion-context/add wordlist:-rW:'=' "$((istat+1))"
      elif ((_ble_bash>=30100)) && [[ $tail == ']+' ]]; then
        ble/syntax/completion-context/add wordlist:-rW:'+=' "$((istat+1))"
      elif [[ $tail == ']=' || _ble_bash -ge 30100 && $tail == ']+=' ]]; then
        ble/syntax/completion-context/add rhs "$index"
      fi
    fi
  fi
}
_ble_syntax_completion_context_check_prefix[_ble_ctx_BRACE1]=brace
_ble_syntax_completion_context_check_prefix[_ble_ctx_BRACE2]=brace
function ble/syntax/completion-context/prefix:brace {
  local ctx1=$ctx istat1=$istat nlen1=${stat[3]}
  ((nlen1>=0)) || return 1
  local inest1=$((istat1-nlen1))
  while ((1)); do
    local nest=${_ble_syntax_nest[inest1]}
    [[ $nest ]] || return 1
    ble/string#split-words nest "$nest"
    ctx1=${nest[0]}
    ((ctx1==_ble_ctx_BRACE1||ctx1==_ble_ctx_BRACE2)) || break
    inest1=$((nest[3]<0?nest[3]:inest1-nest[3]))
    ((inest1>=0)) || return 1
  done
  for ((istat1=inest1;1;istat1--)); do
    ((istat1>=0)) || return 1
    [[ ${_ble_syntax_stat[istat1]} ]] && break
  done
  local stat1
  ble/string#split-words stat1 "${_ble_syntax_stat[istat1]}"
  local wlen=${stat1[1]}
  local wbeg=$((wlen>=0?istat1-wlen:istat1))
  ble/syntax/completion-context/.check/parameter-expansion
  ble/syntax/completion-context/add argument "$wbeg"
}
function ble/syntax/completion-context/.search-last-istat {
  local index=$1 istat
  for ((istat=index;istat>=0;istat--)); do
    if [[ ${_ble_syntax_stat[istat]} ]]; then
      ret=$istat
      return 0
    fi
  done
  ret=
  return 1
}
function ble/syntax/completion-context/.check-prefix {
  local rex_param='^[_a-zA-Z][_a-zA-Z0-9]*$'
  local from=${1:-$((index-1))}
  local ret
  ble/syntax/completion-context/.search-last-istat "$from" || return 1
  local istat=$ret stat
  ble/string#split-words stat "${_ble_syntax_stat[istat]}"
  [[ ${stat[0]} ]] || return 1
  local ctx=${stat[0]} wlen=${stat[1]}
  local wbeg=$((wlen<0?wlen:istat-wlen))
  local name=${_ble_syntax_completion_context_check_prefix[ctx]}
  if [[ $name ]]; then
    builtin eval ble/syntax/completion-context/prefix:"$name"
  fi
}
function ble/syntax/completion-context/here:add {
  local source
  for source; do
    ble/syntax/completion-context/add "$source" "$index"
  done
}
_ble_syntax_completion_context_check_here[_ble_ctx_CMDX]='command'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXV]='command'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDX1]='command'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXT]='command'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXC]='command compound'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXE]='command end'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXD0]='command for1'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDXD]='command for2'
function ble/syntax/completion-context/here:command {
  case $1 in
  (compound)
    ble/syntax/completion-context/add wordlist:-rs:'(:{:((:[[:for:select:case:if:while:until' "$index" ;;
  (end)
    ble/syntax/completion-context/add wordlist:-rs:'}:fi:done:esac:then:elif:else:do' "$index" ;;
  (for1)
    ble/syntax/completion-context/add wordlist:-rs:';:{:do' "$index" ;;
  (for2)
    ble/syntax/completion-context/add wordlist:-rs:'{:do' "$index" ;;
  (*)
    if ! shopt -q no_empty_cmd_completion; then
      ble/syntax/completion-context/add command "$index"
    fi ;;
  esac
}
_ble_syntax_completion_context_check_here[_ble_ctx_ARGX]='argument'
_ble_syntax_completion_context_check_here[_ble_ctx_CARGX1]='argument'
_ble_syntax_completion_context_check_here[_ble_ctx_FARGX3]='argument'
_ble_syntax_completion_context_check_here[_ble_ctx_ARGX0]='argument none'
_ble_syntax_completion_context_check_here[_ble_ctx_CPATX0]='argument none'
_ble_syntax_completion_context_check_here[_ble_ctx_CMDX0]='argument none'
_ble_syntax_completion_context_check_here[_ble_ctx_FARGX1]='argument for1'
_ble_syntax_completion_context_check_here[_ble_ctx_SARGX1]='argument for1'
_ble_syntax_completion_context_check_here[_ble_ctx_ARGVX]='argument declare'
_ble_syntax_completion_context_check_here[_ble_ctx_ARGEX]='argument eval'
_ble_syntax_completion_context_check_here[_ble_ctx_CARGX2]='argument case'
_ble_syntax_completion_context_check_here[_ble_ctx_CPATI]='argument case-pattern'
_ble_syntax_completion_context_check_here[_ble_ctx_FARGX2]='argument for2'
_ble_syntax_completion_context_check_here[_ble_ctx_TARGX1]='argument time1'
_ble_syntax_completion_context_check_here[_ble_ctx_TARGX2]='argument time2'
_ble_syntax_completion_context_check_here[_ble_ctx_FNAMEX]='argument function'
_ble_syntax_completion_context_check_here[_ble_ctx_COARGX]='argument coproc'
_ble_syntax_completion_context_check_here[_ble_ctx_CONDX]='argument cond'
function ble/syntax/completion-context/here:argument {
  case $1 in
  (none)
    ble/syntax/completion-context/add sabbrev "$index" ;;
  (for1)
    ble/syntax/completion-context/add variable:w "$index"
    ble/syntax/completion-context/add sabbrev "$index" ;;
  (declare)
    ble/syntax/completion-context/add variable:= "$index"
    ble/syntax/completion-context/add option "$index"
    ble/syntax/completion-context/add sabbrev "$index" ;;
  (eval)
    ble/syntax/completion-context/add command:D "$index"
    ble/syntax/completion-context/add file "$index" ;;
  (case)
    ble/syntax/completion-context/add wordlist:-rs:'in' "$index" ;;
  (case-pattern)
    ble/syntax/completion-context/add file "$index" ;;
  (for2)
    ble/syntax/completion-context/add wordlist:-rs:'in:do' "$index" ;;
  (time1)
    local words='-p'
    ((_ble_bash>=50100)) && words='-p':'--'
    ble/syntax/completion-context/add command "$index"
    ble/syntax/completion-context/add wordlist:--:"$words" "$index" ;;
  (time2)
    ble/syntax/completion-context/add command "$index"
    ble/syntax/completion-context/add wordlist:--:'--' "$index" ;;
  (function)
    ble/syntax/completion-context/add function "$index" ;;
  (coproc)
    ble/syntax/completion-context/add variable:w "$index"
    ble/syntax/completion-context/add command:V "$index" ;;
  (cond)
    ble/syntax/completion-context/add sabbrev "$index"
    ble/syntax/completion-context/add option "$index"
    ble/syntax/completion-context/add file "$index" ;;
  (*)
    ble/syntax/completion-context/add argument "$index" ;;
  esac
}
_ble_syntax_completion_context_check_here[_ble_ctx_RDRF]='add file'
_ble_syntax_completion_context_check_here[_ble_ctx_RDRS]='add file'
_ble_syntax_completion_context_check_here[_ble_ctx_RDRD]='add fd'
_ble_syntax_completion_context_check_here[_ble_ctx_RDRD2]='add fd file:no-fd'
_ble_syntax_completion_context_check_here[_ble_ctx_RDRH]='add wordlist:EOF:END:HERE'
_ble_syntax_completion_context_check_here[_ble_ctx_RDRI]='add wordlist:EOF:END:HERE'
_ble_syntax_completion_context_check_here[_ble_ctx_VRHS]='add rhs'
_ble_syntax_completion_context_check_here[_ble_ctx_ARGVR]='add rhs'
_ble_syntax_completion_context_check_here[_ble_ctx_ARGER]='add rhs'
_ble_syntax_completion_context_check_here[_ble_ctx_VALR]='add rhs'
function ble/syntax/completion-context/.check-here {
  ((${#sources[*]})) && return 0
  local -a stat
  ble/string#split-words stat "${_ble_syntax_stat[index]}"
  if [[ ${stat[0]-} && ${_ble_syntax_completion_context_check_here[stat[0]]-} ]]; then
    local proc=${_ble_syntax_completion_context_check_here[stat[0]]-}
    builtin eval ble/syntax/completion-context/here:"$proc"
  fi
}
function ble/syntax/completion-context/generate {
  local text=$1 index=$2
  sources=()
  ((index<0&&(index=0)))
  ble/syntax/completion-context/.check-prefix
  ble/syntax/completion-context/.check-here
}
function ble/syntax:bash/extract-command/.register-word {
  local wtxt=${_ble_syntax_text:wbegin:wlen}
  if [[ ! $comp_cword ]] && ((wbegin<=EC_pos)); then
    if ((EC_pos<=wbegin+wlen)); then
      comp_cword=${#comp_words[@]}
      comp_point=$((${#comp_line}+wbegin+wlen-EC_pos))
      comp_line="$wtxt$comp_line"
      ble/array#push comp_words "$wtxt"
    else
      comp_cword=${#comp_words[@]}
      comp_point=${#comp_line}
      comp_line="$wtxt $comp_line"
      ble/array#push comp_words "" "$wtxt"
    fi
  else
    comp_line="$wtxt$comp_line"
    ble/array#push comp_words "$wtxt"
  fi
  [[ $EC_opts == *:treeinfo:* ]] &&
    ble/array#push tree_words "$TE_i:$TE_nofs"
}
function ble/syntax:bash/extract-command/.construct-proc {
  if [[ $wtype =~ ^[0-9]+$ ]]; then
    if ((wtype==_ble_ctx_CMDI||wtype==_ble_ctx_CMDX0)); then
      if ((EC_pos<wbegin)); then
        comp_line= comp_point= comp_cword= comp_words=()
        [[ $EC_opts == *:treeinfo:* ]] && tree_words=()
      else
        ble/syntax:bash/extract-command/.register-word
        ble/syntax/tree-enumerate-break
        EC_found=1
        return 0
      fi
    elif ((wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGVI||wtype==_ble_ctx_ARGEI||wtype==_ble_attr_VAR)); then
      ble/syntax:bash/extract-command/.register-word
      comp_line=" $comp_line"
    fi
  fi
}
function ble/syntax:bash/extract-command/.construct {
  comp_line= comp_point= comp_cword= comp_words=()
  [[ $EC_opts == *:treeinfo:* ]] && tree_words=()
  if [[ $1 == nested ]]; then
    ble/syntax/tree-enumerate-children \
      ble/syntax:bash/extract-command/.construct-proc
  else
    ble/syntax/tree-enumerate \
      ble/syntax:bash/extract-command/.construct-proc
  fi
  ble/array#reverse comp_words
  ((comp_cword=${#comp_words[@]}-1-comp_cword,
    comp_point=${#comp_line}-comp_point))
  [[ $EC_opts == *:treeinfo:* ]] &&
    ble/array#reverse tree_words
}
function ble/syntax:bash/extract-command/.scan {
  ((EC_pos<wbegin)) && return 0
  if ((wbegin+wlen<EC_pos)); then
    ble/syntax/tree-enumerate-break
  else
    local EC_has_word=
    ble/syntax/tree-enumerate-children \
      ble/syntax:bash/extract-command/.scan
    local has_word=$EC_has_word
    ble/util/unlocal EC_has_word
    if [[ $has_word && ! $EC_found ]]; then
      ble/syntax:bash/extract-command/.construct nested
      ble/syntax/tree-enumerate-break
    fi
  fi
  if [[ $wtype =~ ^[0-9]+$ && ! $EC_has_word ]]; then
    EC_has_word=$wtype
    return 0
  fi
}
function ble/syntax:bash/extract-command {
  local EC_pos=$1 EC_opts=:$2:
  local EC_found=
  local EC_has_word=
  ble/syntax/tree-enumerate \
    ble/syntax:bash/extract-command/.scan
  if [[ ! $EC_found && $EC_has_word ]]; then
    ble/syntax:bash/extract-command/.construct
  fi
  [[ $EC_found ]]
}
function ble/syntax/tree#previous-sibling {
  local i0=${1%%:*} nofs0=0 opts=:$2:
  [[ $1 == *:* ]] && nofs0=${1#*:}
  local node
  ble/string#split-words node "${_ble_syntax_tree[i0-1]}"
  ble/util/assert '((${#node[@]}>nofs0))' "Broken AST: tree-node info missing at $((i0-1))[$nofs0]" || return 1
  local tplen=${node[nofs0+3]}
  ((tplen>=0)) || return 1
  local i=$((i0-tplen)) nofs=0
  ret=$i:$nofs
  if [[ $opts == *:wvars:* ]]; then
    ble/string#split-words node "${_ble_syntax_tree[i-1]}"
    ble/util/assert '((${#node[@]}>nofs))' "Broken AST: tree-node info missing at $((i-1))[$nofs]" || return 1
    wtype=${node[nofs]}
    wlen=${node[nofs+1]}
    ((wbeg=i-wlen,wend=i))
    wattr=${node[nofs+4]}
  fi
  return 0
}
function ble/syntax/tree#next-sibling {
  local i0=${1%%:*} nofs0=0 opts=:$2:
  [[ $1 == *:* ]] && nofs0=${1#*:}
  ((nofs0)) && return 1
  local iN=${#_ble_syntax_text} i nofs node
  for ((i=i0+1;i<=iN;i++)); do
    [[ ${_ble_syntax_tree[i-1]} ]] || continue
    ble/string#split-words node "${_ble_syntax_tree[i-1]}"
    nofs=${#node[@]}
    while (((nofs-=_ble_syntax_TREE_WIDTH)>=0)); do
      if ((i0==i-node[nofs+2])); then
        return 1
      elif ((i0==i-node[nofs+3])); then
        ret=$i:$nofs
        if [[ $opts == *:wvars:* ]]; then
          wtype=${node[nofs]}
          wlen=${node[nofs+1]}
          ((wbeg=i-wlen,wend=i))
          wattr=${node[nofs+4]}
        fi
        return 0
      fi
    done
  done
  return 1
}
function ble/syntax:bash/extract-command-by-noderef {
  local i=${1%%:*} nofs=0 opts=:$2:
  [[ $1 == *:* ]] && nofs=${1#*:}
  comp_words=()
  tree_words=()
  comp_line=
  comp_cword=0
  comp_point=0
  local ExprIsArgument='wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGVI||wtype==_ble_ctx_ARGEI||wtype==_ble_attr_VAR'
  local ret node wtype wlen wbeg wend wattr
  ble/string#split-words node "${_ble_syntax_tree[i-1]}"
  wtype=${node[nofs]} wlen=${node[nofs+1]}
  [[ ! ${wtype//[0-9]} ]] && ((wtype==_ble_ctx_CMDI||ExprIsArgument)) || return 1
  ble/array#push comp_words "${_ble_syntax_text:i-wlen:wlen}"
  [[ $opts == *:treeinfo:* ]] &&
    ble/array#push tree_words "$i:$nofs"
  ret=$i:$nofs
  while
    { [[ ${wtype//[0-9]} ]] || ((wtype!=_ble_ctx_CMDI)); } &&
      ble/syntax/tree#previous-sibling "$ret" wvars
  do
    [[ ! ${wtype//[0-9]} ]] || continue
    if ((wtype==_ble_ctx_CMDI||ExprIsArgument)); then
      ble/array#push comp_words "${_ble_syntax_text:wbeg:wlen}"
      [[ $opts == *:treeinfo:* ]] &&
        ble/array#push tree_words "$ret"
    fi
  done
  ble/array#reverse comp_words
  [[ $opts == *:treeinfo:* ]] &&
    ble/array#reverse tree_words
  ((comp_cword=${#comp_words[@]}-1))
  ret=$i:$nofs
  while ble/syntax/tree#next-sibling "$ret" wvars; do
    [[ ! ${wtype//[0-9]} ]] || continue
    ((wtype==_ble_ctx_CMDI)) && break
    if ((ExprIsArgument)); then
      ble/array#push comp_words "${_ble_syntax_text:wbeg:wlen}"
      [[ $opts == *:treeinfo:* ]] &&
        ble/array#push tree_words "$ret"
    fi
  done
  local IFS=$_ble_term_IFS
  comp_line="${comp_words[*]}"
  local tmp="${comp_words[*]::comp_cword+1}"
  comp_point=${#tmp}
}
_ble_syntax_attr2iface=()
function ble/syntax/attr2iface/color_defface.onload {
  function ble/syntax/attr2iface/.define {
    ((_ble_syntax_attr2iface[$1]=_ble_faces__$2))
  }
  ble/syntax/attr2iface/.define _ble_ctx_UNSPECIFIED syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGX     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGX0    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGI     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGQ     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGVX    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGVI    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGVR    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGEX    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGEI    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_ARGER    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDX     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDX0    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDX1    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXT    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXC    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXE    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXD    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXD0   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDXV    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CMDI     syntax_command
  ble/syntax/attr2iface/.define _ble_ctx_VRHS     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_QUOT     syntax_quoted
  ble/syntax/attr2iface/.define _ble_ctx_EXPR     syntax_expr
  ble/syntax/attr2iface/.define _ble_attr_ERR     syntax_error
  ble/syntax/attr2iface/.define _ble_attr_VAR     syntax_varname
  ble/syntax/attr2iface/.define _ble_attr_QDEL    syntax_quotation
  ble/syntax/attr2iface/.define _ble_attr_QESC    syntax_escape
  ble/syntax/attr2iface/.define _ble_attr_DEF     syntax_default
  ble/syntax/attr2iface/.define _ble_attr_DEL     syntax_delimiter
  ble/syntax/attr2iface/.define _ble_ctx_PARAM    syntax_param_expansion
  ble/syntax/attr2iface/.define _ble_ctx_PWORD    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_PWORDE   syntax_error
  ble/syntax/attr2iface/.define _ble_ctx_PWORDR   syntax_default
  ble/syntax/attr2iface/.define _ble_attr_HISTX   syntax_history_expansion
  ble/syntax/attr2iface/.define _ble_ctx_VALX     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_VALI     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_VALR     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_VALQ     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CONDX    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CONDI    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CONDQ    syntax_default
  ble/syntax/attr2iface/.define _ble_attr_COMMENT syntax_comment
  ble/syntax/attr2iface/.define _ble_ctx_CASE     syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_PATN     syntax_default
  ble/syntax/attr2iface/.define _ble_attr_GLOB    syntax_glob
  ble/syntax/attr2iface/.define _ble_ctx_BRAX     syntax_default
  ble/syntax/attr2iface/.define _ble_attr_BRACE   syntax_brace
  ble/syntax/attr2iface/.define _ble_ctx_BRACE1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_BRACE2   syntax_default
  ble/syntax/attr2iface/.define _ble_attr_TILDE   syntax_tilde
  ble/syntax/attr2iface/.define _ble_ctx_SARGX1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FARGX1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FARGX2   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FARGX3   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FARGI1   syntax_varname
  ble/syntax/attr2iface/.define _ble_ctx_FARGI2   command_keyword
  ble/syntax/attr2iface/.define _ble_ctx_FARGI3   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FARGQ3   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CARGX1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CARGX2   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CARGI1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CARGQ1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CARGI2   command_keyword
  ble/syntax/attr2iface/.define _ble_ctx_CPATX    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CPATI    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CPATQ    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_CPATX0   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_TARGX1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_TARGX2   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_TARGI1   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_TARGI2   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FNAMEX   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_FNAMEI   syntax_function_name
  ble/syntax/attr2iface/.define _ble_ctx_COARGX   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_COARGI   syntax_command
  ble/syntax/attr2iface/.define _ble_ctx_RDRF    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_RDRD    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_RDRD2   syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_RDRS    syntax_default
  ble/syntax/attr2iface/.define _ble_ctx_RDRH    syntax_document_begin
  ble/syntax/attr2iface/.define _ble_ctx_RDRI    syntax_document_begin
  ble/syntax/attr2iface/.define _ble_ctx_HERE0   syntax_document
  ble/syntax/attr2iface/.define _ble_ctx_HERE1   syntax_document
  ble/syntax/attr2iface/.define _ble_attr_CMD_BOLD       command_builtin_dot
  ble/syntax/attr2iface/.define _ble_attr_CMD_BUILTIN    command_builtin
  ble/syntax/attr2iface/.define _ble_attr_CMD_ALIAS      command_alias
  ble/syntax/attr2iface/.define _ble_attr_CMD_FUNCTION   command_function
  ble/syntax/attr2iface/.define _ble_attr_CMD_FILE       command_file
  ble/syntax/attr2iface/.define _ble_attr_CMD_JOBS       command_jobs
  ble/syntax/attr2iface/.define _ble_attr_CMD_DIR        command_directory
  ble/syntax/attr2iface/.define _ble_attr_CMD_SUFFIX     command_suffix
  ble/syntax/attr2iface/.define _ble_attr_CMD_SUFFIX_NEW command_suffix_new
  ble/syntax/attr2iface/.define _ble_attr_KEYWORD       command_keyword
  ble/syntax/attr2iface/.define _ble_attr_KEYWORD_BEGIN command_keyword
  ble/syntax/attr2iface/.define _ble_attr_KEYWORD_END   command_keyword
  ble/syntax/attr2iface/.define _ble_attr_KEYWORD_MID   command_keyword
  ble/syntax/attr2iface/.define _ble_attr_FILE_DIR      filename_directory
  ble/syntax/attr2iface/.define _ble_attr_FILE_STICKY   filename_directory_sticky
  ble/syntax/attr2iface/.define _ble_attr_FILE_LINK     filename_link
  ble/syntax/attr2iface/.define _ble_attr_FILE_ORPHAN   filename_orphan
  ble/syntax/attr2iface/.define _ble_attr_FILE_FILE     filename_other
  ble/syntax/attr2iface/.define _ble_attr_FILE_SETUID   filename_setuid
  ble/syntax/attr2iface/.define _ble_attr_FILE_SETGID   filename_setgid
  ble/syntax/attr2iface/.define _ble_attr_FILE_EXEC     filename_executable
  ble/syntax/attr2iface/.define _ble_attr_FILE_WARN     filename_warning
  ble/syntax/attr2iface/.define _ble_attr_FILE_FIFO     filename_pipe
  ble/syntax/attr2iface/.define _ble_attr_FILE_SOCK     filename_socket
  ble/syntax/attr2iface/.define _ble_attr_FILE_BLK      filename_block
  ble/syntax/attr2iface/.define _ble_attr_FILE_CHR      filename_character
  ble/syntax/attr2iface/.define _ble_attr_FILE_URL      filename_url
  ble/syntax/attr2iface/.define _ble_attr_VAR_UNSET     varname_unset
  ble/syntax/attr2iface/.define _ble_attr_VAR_EMPTY     varname_empty
  ble/syntax/attr2iface/.define _ble_attr_VAR_NUMBER    varname_number
  ble/syntax/attr2iface/.define _ble_attr_VAR_EXPR      varname_expr
  ble/syntax/attr2iface/.define _ble_attr_VAR_ARRAY     varname_array
  ble/syntax/attr2iface/.define _ble_attr_VAR_HASH      varname_hash
  ble/syntax/attr2iface/.define _ble_attr_VAR_READONLY  varname_readonly
  ble/syntax/attr2iface/.define _ble_attr_VAR_TRANSFORM varname_transform
  ble/syntax/attr2iface/.define _ble_attr_VAR_EXPORT    varname_export
  ble/syntax/attr2iface/.define _ble_attr_VAR_NEW       varname_new
}
blehook/eval-after-load color_defface ble/syntax/attr2iface/color_defface.onload
function ble/syntax/highlight/cmdtype1 {
  type=$1
  local cmd=$2
  case $type:$cmd in
  (builtin::|builtin:.)
    ((type=_ble_attr_CMD_BOLD)) ;;
  (builtin:*)
    ((type=_ble_attr_CMD_BUILTIN)) ;;
  (alias:*)
    ((type=_ble_attr_CMD_ALIAS)) ;;
  (function:*)
    ((type=_ble_attr_CMD_FUNCTION)) ;;
  (file:*)
    ((type=_ble_attr_CMD_FILE)) ;;
  (keyword:*)
    ((type=_ble_attr_KEYWORD)) ;;
  (*:%*)
    ble/util/joblist.check
    if jobs -- "$cmd" &>/dev/null; then
      ((type=_ble_attr_CMD_JOBS))
    else
      ((type=_ble_attr_ERR))
    fi ;;
  (*)
    if [[ -d $cmd ]] && shopt -q autocd &>/dev/null; then
      ((type=_ble_attr_CMD_DIR))
    elif [[ $cmd == *.* ]] && ble/function#try ble/complete/sabbrev#match "$cmd" 's'; then
      if [[ -e $cmd || -h $cmd ]]; then
        ((type=_ble_attr_CMD_SUFFIX))
      else
        ((type=_ble_attr_CMD_SUFFIX_NEW))
      fi
    else
      ((type=_ble_attr_ERR))
    fi ;;
  esac
}
function ble/syntax/highlight/cmdtype/.jobs { local LC_ALL=C; jobs; }
ble/function#suppress-stderr ble/syntax/highlight/cmdtype/.jobs
function ble/syntax/highlight/cmdtype/.is-job-name {
  ble/util/joblist.check
  local value=$1 word=$2
  if [[ $value == '%'* ]] && jobs -- "$value" &>/dev/null; then
    return 0
  fi
  local quote=\'\"\\\`
  if [[ ${auto_resume+set} && $word != *["$quote"]* ]]; then
    if [[ $auto_resume == exact ]]; then
      local jobs job ret
      ble/util/assign-array jobs 'ble/syntax/highlight/cmdtype/.jobs'
      for job in "${jobs[@]}"; do
        ble/string#trim "${job#*' '}"
        ble/string#trim "${ret#*' '}"
        [[ $value == "$ret" ]] && return 0
      done
      return 1
    elif [[ $auto_resume == substring ]]; then
      jobs -- "%?$value" &>/dev/null; return "$?"
    else
      jobs -- "%$value" &>/dev/null; return "$?"
    fi
  fi
  return 1
}
function ble/syntax/highlight/cmdtype/.impl {
  local cmd=$1 word=$2
  local cmd_type; ble/util/type cmd_type "$cmd"
  ble/syntax/highlight/cmdtype1 "$cmd_type" "$cmd"
  if [[ $type == "$_ble_attr_CMD_ALIAS" && $cmd != "$word" ]]; then
    type=$(
      builtin unalias "$cmd"
      ble/util/type cmd_type "$cmd"
      ble/syntax/highlight/cmdtype1 "$cmd_type" "$cmd"
      printf %s "$type")
  elif ble/syntax/highlight/cmdtype/.is-job-name "$cmd" "$word"; then
    ((type=_ble_attr_CMD_JOBS))
  elif [[ $type == "$_ble_attr_KEYWORD" ]]; then
    ble/syntax/highlight/cmdtype1 "${cmd_type[1]}" "$cmd"
  fi
}
_ble_syntax_highlight_filetype_version=-1
function ble/syntax/highlight/cmdtype {
  local cmd=$1 word=$2
  if ((_ble_syntax_highlight_filetype_version!=_ble_edit_LINENO)); then
    ble/gdict#clear _ble_syntax_highlight_filetype
    ((_ble_syntax_highlight_filetype_version=_ble_edit_LINENO))
  fi
  if local ret; ble/gdict#get _ble_syntax_highlight_filetype "$word"; then
    type=$ret
    return 0
  fi
  ble/syntax/highlight/cmdtype/.impl "$cmd" "$word"
  ble/gdict#set _ble_syntax_highlight_filetype "$word" "$type"
}
function ble/syntax/highlight/filetype {
  type=
  local file=$1
  if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $file == //* ]]; then
    [[ $file == // ]] && ((type=_ble_attr_FILE_DIR))
    [[ $type ]]; return "$?"
  fi
  if [[ :$2: != *:follow-symlink:* && -h $file ]]; then
    if [[ -e $file ]]; then
      ((type=_ble_attr_FILE_LINK))
    else
      ((type=_ble_attr_FILE_ORPHAN))
    fi
  elif [[ -e $file ]]; then
    if [[ -d $file ]]; then
      if [[ -k $file ]]; then
        ((type=_ble_attr_FILE_STICKY))
      elif [[ :$2: != *:follow-symlink:* && -h ${file%/} ]]; then
        ((type=_ble_attr_FILE_LINK))
      else
        ((type=_ble_attr_FILE_DIR))
      fi
    elif [[ -f $file ]]; then
      if [[ -u $file ]]; then
        ((type=_ble_attr_FILE_SETUID))
      elif [[ -g $file ]]; then
        ((type=_ble_attr_FILE_SETGID))
      elif [[ -x $file ]]; then
        ((type=_ble_attr_FILE_EXEC))
      else
        ((type=_ble_attr_FILE_FILE))
      fi
    elif [[ -c $file ]]; then
      ((type=_ble_attr_FILE_CHR))
    elif [[ -p $file ]]; then
      ((type=_ble_attr_FILE_FIFO))
    elif [[ -S $file ]]; then
      ((type=_ble_attr_FILE_SOCK))
    elif [[ -b $file ]]; then
      ((type=_ble_attr_FILE_BLK))
    fi
  elif local rex='^https?://[^ ^`"<>\{|}]+$'; [[ $file =~ $rex ]]; then
    ((type=_ble_attr_FILE_URL))
  fi
  [[ $type ]]
}
_ble_syntax_highlight_lscolors=()
_ble_syntax_highlight_lscolors_rl_colored_completion_prefix=
function ble/syntax/highlight/ls_colors/.clear {
  _ble_syntax_highlight_lscolors=()
  ble/gdict#clear _ble_syntax_highlight_lscolors_ext
  ble/gdict#clear _ble_syntax_highlight_lscolors_suffix
  _ble_syntax_highlight_lscolors_rl_colored_completion_prefix=
}
function ble/syntax/highlight/ls_colors/.register-suffix {
  local suffix=$1 value=$2
  if [[ $suffix == .readline-colored-completion-prefix ]]; then
    _ble_syntax_highlight_lscolors_rl_colored_completion_prefix=$value
  elif [[ $suffix == .* ]]; then
    ble/gdict#set _ble_syntax_highlight_lscolors_ext "$suffix" "$value"
  else
    ble/gdict#set _ble_syntax_highlight_lscolors_suffix "$suffix" "$value"
  fi
}
function ble/syntax/highlight/ls_colors/.has-suffix {
  ((${#_ble_syntax_highlight_lscolors_ext[@]})) ||
    ((${#_ble_syntax_highlight_lscolors_suffix[@]}))
}
function ble/syntax/highlight/ls_colors/.match-suffix {
  ret=
  local filename=${1##*/} suffix= g=
  local ext=$filename
  while [[ $ext == *.* ]]; do
    ext=${ext#*.}
    if ble/gdict#get _ble_syntax_highlight_lscolors_ext ".$ext" && [[ $ret ]]; then
      suffix=.$ext g=$ret
      break
    fi
  done
  local key keys
  ble/gdict#keys _ble_syntax_highlight_lscolors_suffix
  keys=("${ret[@]}")
  for key in "${keys[@]}"; do
    ((${#key}>${#suffix})) &&
      [[ $filename == *"$key" ]] &&
      ble/gdict#get _ble_syntax_highlight_lscolors_suffix "$key" &&
      [[ $ret ]] &&
      suffix=$key g=$ret
  done
  ret=$g
  [[ $ret ]]
}
function ble/syntax/highlight/ls_colors/.parse {
  ble/syntax/highlight/ls_colors/.clear
  local fields field
  ble/string#split fields : "$1"
  for field in "${fields[@]}"; do
    [[ $field == *=* ]] || continue
    if [[ $field == 'ln=target' ]]; then
      _ble_syntax_highlight_lscolors[_ble_attr_FILE_LINK]=target
      continue
    fi
    local lhs=${field%%=*}
    local ret; ble/color/sgrspec2g "${field#*=}"; local rhs=$ret
    case $lhs in
    ('di') _ble_syntax_highlight_lscolors[_ble_attr_FILE_DIR]=$rhs  ;;
    ('st') _ble_syntax_highlight_lscolors[_ble_attr_FILE_STICKY]=$rhs  ;;
    ('ln') _ble_syntax_highlight_lscolors[_ble_attr_FILE_LINK]=$rhs ;;
    ('or') _ble_syntax_highlight_lscolors[_ble_attr_FILE_ORPHAN]=$rhs ;;
    ('fi') _ble_syntax_highlight_lscolors[_ble_attr_FILE_FILE]=$rhs ;;
    ('su') _ble_syntax_highlight_lscolors[_ble_attr_FILE_SETUID]=$rhs ;;
    ('sg') _ble_syntax_highlight_lscolors[_ble_attr_FILE_SETGID]=$rhs ;;
    ('ex') _ble_syntax_highlight_lscolors[_ble_attr_FILE_EXEC]=$rhs ;;
    ('cd') _ble_syntax_highlight_lscolors[_ble_attr_FILE_CHR]=$rhs  ;;
    ('pi') _ble_syntax_highlight_lscolors[_ble_attr_FILE_FIFO]=$rhs ;;
    ('so') _ble_syntax_highlight_lscolors[_ble_attr_FILE_SOCK]=$rhs ;;
    ('bd') _ble_syntax_highlight_lscolors[_ble_attr_FILE_BLK]=$rhs  ;;
    (.*)   ble/syntax/highlight/ls_colors/.register-suffix "$lhs" "$rhs" ;;
    (\*?*) ble/syntax/highlight/ls_colors/.register-suffix "${lhs:1}" "$rhs" ;;
    esac
  done
}
function ble/syntax/highlight/ls_colors {
  local file=$1
  if ((type==_ble_attr_FILE_LINK)) && [[ ${_ble_syntax_highlight_lscolors[_ble_attr_FILE_LINK]} == target ]]; then
    ble/syntax/highlight/filetype "$file" follow-symlink ||
      type=$_ble_attr_FILE_ORPHAN
    if ((type==_ble_attr_FILE_FILE)) && ble/syntax/highlight/ls_colors/.has-suffix; then
      ble/util/readlink "$file"
      file=$ret
    fi
  fi
  if ((type==_ble_attr_FILE_FILE)); then
    local ret=
    if ble/syntax/highlight/ls_colors/.match-suffix "$file"; then
      local g1=$ret
      ble/color/face2g filename_ls_colors; g=$ret
      ble/color/g#append g "$g1"
      return 0
    fi
  fi
  local g1=${_ble_syntax_highlight_lscolors[type]}
  if [[ $g1 ]]; then
    ble/color/face2g filename_ls_colors; g=$ret
    ble/color/g#append g "$g1"
    return 0
  fi
  return 1
}
function ble/syntax/highlight/getg-from-filename {
  local filename=$1 type=
  ble/syntax/highlight/filetype "$filename"
  if [[ $bleopt_filename_ls_colors ]]; then
    if ble/syntax/highlight/ls_colors "$filename"; then
      return 0
    fi
  fi
  if [[ $type ]]; then
    ble/syntax/attr2g "$type"
  else
    g=
  fi
}
function bleopt/check:filename_ls_colors {
  ble/syntax/highlight/ls_colors/.parse "$value"
}
bleopt -I filename_ls_colors
_ble_syntax_progcolor_vars=(
  node TE_i TE_nofs wtype wlen wbeg wend wattr)
_ble_syntax_progcolor_wattr_vars=(
  wattr_buff wattr_pos wattr_g)
function ble/progcolor/load-word-data {
  TE_i=${1%%:*} TE_nofs=${1#*:}
  [[ $1 != *:* ]] && TE_nofs=0
  ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
  wtype=${node[TE_nofs]}
  wlen=${node[TE_nofs+1]}
  wattr=${node[TE_nofs+4]}
  wbeg=$((TE_i-wlen))
  wend=$TE_i
}
function ble/progcolor/set-wattr {
  ble/syntax/urange#update color_ "$wbeg" "$wend"
  ble/syntax/wrange#update _ble_syntax_word_ "$TE_i"
  node[TE_nofs+4]=$1
  local IFS=$_ble_term_IFS
  _ble_syntax_tree[TE_i-1]="${node[*]}"
}
function ble/progcolor/eval-word {
  local iword=${1:-progcolor_iword} opts=$2
  if [[ ${progcolor_stats[iword]+set} ]]; then
    ret=${progcolor_wvals[iword]}
    return "${progcolor_stats[iword]}"
  fi
  local wtxt=${comp_words[iword]}
  local simple_flags simple_ibrace
  if ! ble/syntax:bash/simple-word/reconstruct-incomplete-word "$wtxt"; then
    ret=
    progcolor_stats[iword]=2
    progcolor_wvals[iword]=
    return 2
  fi
  ble/syntax:bash/simple-word/eval "$ret" "$opts"; local ext=$?
  ((ext==148)) && return 148
  if ((ext!=0)); then
    ret=
    progcolor_stats[iword]=1
    progcolor_wvals[iword]=
    return 1
  fi
  progcolor_stats[iword]=0
  progcolor_wvals[iword]=$ret
  return 0
}
function ble/progcolor/load-cmdspec-opts {
  if [[ $progcolor_cmdspec_opts ]]; then
    cmdspec_opts=$progcolor_cmdspec_opts
  else
    ble/cmdspec/opts#load "${comp_words[0]}"
    progcolor_cmdspec_opts=${cmdspec_opts:-:}
  fi
}
function ble/progcolor/stop-option#init {
  rexrej='^--$' rexreq= stopat=
  local cmdspec_opts=$1
  if [[ $cmdspec_opts ]]; then
    if [[ :$cmdspec_opts: == *:no-options:* ]]; then
      stopat=0
      return 1
    elif ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-at && [[ $ret ]]; then
      ((stopat=ret))
      return 1
    fi
    local ret
    if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-on && [[ $ret ]]; then
      rexrej=$ret
    elif [[ :$cmdspec_opts: == *:disable-double-hyphen:* ]]; then
      rexrej=
    fi
    if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-unless && [[ $ret ]]; then
      rexreq=$ret
    elif [[ :$cmdspec_opts: == *:stop-options-postarg:* ]]; then
      rexreq='^-.+'
      ble/opts#has "$cmdspec_opts" plus-options && rexreq='^[-+].+'
    fi
  fi
}
function ble/progcolor/stop-option#test {
  [[ $rexrej && $1 =~ $rexrej || $rexreq && ! ( $1 =~ $rexreq ) ]]
}
function ble/progcolor/is-option-context {
  if [[ ${progcolor_optctx[1]} ]]; then
    ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1])))
    return "$?"
  fi
  local rexrej rexreq stopat
  if [[ ! ${progcolor_optctx[0]} ]]; then
    progcolor_optctx[0]=1
    local cmdspec_opts
    ble/progcolor/load-cmdspec-opts
    ble/progcolor/stop-option#init "$cmdspec_opts"
    if [[ ! $rexrej$rexreq ]]; then
      progcolor_optctx[1]=${stopat:--1}
      ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1])))
      return "$?"
    fi
    progcolor_optctx[2]=$rexrej
    progcolor_optctx[3]=$rexreq
    progcolor_optctx[4]=$stopat
  else
    rexrej=${progcolor_optctx[2]}
    rexreq=${progcolor_optctx[3]}
    stopat=${progcolor_optctx[4]}
  fi
  [[ $stopat ]] && ((progcolor_iword>stopat)) && return 1
  local iword
  for ((iword=progcolor_optctx[0];iword<progcolor_iword;iword++)); do
    ble/progcolor/eval-word "$iword" "$highlight_eval_opts"
    if ble/progcolor/stop-option#test "$ret"; then
      progcolor_optctx[1]=$iword
      return 1
    fi
  done
  progcolor_optctx[0]=$iword
  return 0
}
function ble/progcolor/wattr#initialize {
  wattr_buff=()
  wattr_pos=$wbeg
  wattr_g=d
}
function ble/progcolor/wattr#setg {
  local pos=$1 g=$2
  local len=$((pos-wattr_pos))
  ((len>0)) && ble/array#push wattr_buff "$len:$wattr_g"
  wattr_pos=$pos
  wattr_g=$g
}
function ble/progcolor/wattr#setattr {
  local pos=$1 attr=$2 g
  ble/syntax/attr2g "$attr"
  ble/progcolor/wattr#setg "$pos" "$g"
}
function ble/progcolor/wattr#finalize {
  local wattr
  if ((${#wattr_buff[@]})); then
    local len=$((wend-wattr_pos))
    ((len>0)) && ble/array#push wattr_buff \$:"$wattr_g"
    wattr_pos=$wend
    wattr_g=d
    IFS=, builtin eval 'wattr="m${wattr_buff[*]}"'
  else
    wattr=$wattr_g
  fi
  ble/progcolor/set-wattr "$wattr"
}
function ble/progcolor/highlight-filename/.detect-separated-path {
  local word=$1
  ((wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGEI||wtype==_ble_ctx_VALI||wtype==_ble_attr_VAR||wtype==_ble_ctx_RDRS)) || return 1
  local detect_opts=url:$highlight_eval_opts
  ((wtype==_ble_ctx_RDRS)) && detect_opts=$detect_opts:noglob
  [[ $word == '~'* ]] && ((_ble_syntax_attr[p0]!=_ble_attr_TILDE)) && detect_opts=$detect_opts:notilde
  ble/syntax:bash/simple-word/detect-separated-path "$word" :, "$detect_opts"
}
function ble/progcolor/highlight-filename/.pathspec.wattr {
  local p=$p0 opts=$2
  if [[ :$opts: != *:no-path-color:* ]]; then
    local ipath npath=${#path[@]}
    for ((ipath=0;ipath<npath-1;ipath++)); do
      local epath=${path[ipath]} espec=${spec[ipath]}
      local g=d
      if ble/syntax/util/is-directory "$epath"; then
        local type
        if ble/syntax/highlight/filetype "$epath"; then
          ble/syntax/highlight/ls_colors ||
            ble/syntax/attr2g "$type"
        fi
      elif ((wtype==_ble_ctx_CMDI)); then
        ble/syntax/attr2g "$_ble_attr_ERR"
      fi
      ((wtype==_ble_ctx_CMDI&&(g&=~_ble_color_gflags_Underline)))
      ble/progcolor/wattr#setg "$p" "$g"
      ((p=p0+${#espec}))
    done
  fi
  ble/progcolor/wattr#setg "$p" "$1"
  [[ $1 != d ]] &&
    ble/progcolor/wattr#setg "$p1" d
  return 0
}
function ble/progcolor/highlight-filename/.pathspec-with-attr.wattr {
  local g; ble/syntax/attr2g "$1"
  ble/progcolor/highlight-filename/.pathspec.wattr "$g"
  return 0
}
function ble/progcolor/highlight-filename/.pathspec-by-name.wattr {
  local value=$1
  local highlight_opts=
  local type=; ble/syntax/highlight/filetype "$value"
  ((type==_ble_attr_FILE_URL)) && highlight_opts=no-path-color
  if ((wtype==_ble_ctx_RDRF||wtype==_ble_ctx_RDRD2)); then
    if ((type==_ble_attr_FILE_DIR)); then
      type=$_ble_attr_ERR
    elif ((_ble_syntax_TREE_WIDTH<=TE_nofs)); then
      local redirect_ntype=${node[TE_nofs-_ble_syntax_TREE_WIDTH]:1}
      if [[ ( $redirect_ntype == *'>' || $redirect_ntype == '>'[\|\&] ) ]]; then
        if [[ -e $value || -h $value ]]; then
          if [[ -d $value || ! -w $value ]]; then
            type=$_ble_attr_ERR
          elif [[ ( $redirect_ntype == [\<\&]'>' || $redirect_ntype == '>' || $redirect_ntype == '>&' ) && -f $value ]]; then
            if [[ -o noclobber ]]; then
              type=$_ble_attr_ERR
            else
              type=$_ble_attr_FILE_WARN
            fi
          fi
        elif [[ $value == */* && ! -w ${value%/*}/ || $value != */* && ! -w ./ ]]; then
          type=$_ble_attr_ERR
        fi
      elif [[ $redirect_ntype == '<' && ! -r $value ]]; then
        type=$_ble_attr_ERR
      fi
    fi
  fi
  local g=
  if [[ $bleopt_filename_ls_colors ]]; then
    ble/syntax/highlight/ls_colors "$value"
  fi
  [[ $type && ! $g ]] && ble/syntax/attr2g "$type"
  ble/progcolor/highlight-filename/.pathspec.wattr "${g:-d}" "$highlight_opts"
  return 0
}
function ble/progcolor/highlight-filename/.single.wattr {
  local p0=${1%%:*} p1=${1#*:}
  local wtxt=${text:p0:p1-p0}
  if ((wtype==_ble_ctx_CMDI)) && ble/alias#active "$wtxt"; then
    ble/progcolor/wattr#setattr "$p0" "$_ble_attr_CMD_ALIAS"
    return 0
  fi
  local path_opts=after-sep:$highlight_eval_opts
  [[ $wtxt == '~'* ]] && ((_ble_syntax_attr[p0]!=_ble_attr_TILDE)) && path_opts=$path_opts:notilde
  ((wtype==_ble_ctx_RDRS||wtype==_ble_attr_VAR||wtype==_ble_ctx_VALI&&wbeg<p0)) && path_opts=$path_opts:noglob
  local ret path spec ext value count
  ble/syntax:bash/simple-word/evaluate-path-spec "$wtxt" / "count:$path_opts"; ext=$? value=("${ret[@]}")
  ((ext==148)) && return 148
  if ((ext==142)); then
    if [[ $ble_textarea_render_defer_running ]]; then
      ble/progcolor/wattr#setg "$p0" d
    else
      return 148
    fi
  elif ((ext&&(wtype==_ble_ctx_CMDI||wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGEI||wtype==_ble_ctx_RDRF||wtype==_ble_ctx_RDRS||wtype==_ble_ctx_RDRD||wtype==_ble_ctx_RDRD2||wtype==_ble_ctx_VALI))); then
    ble/progcolor/highlight-filename/.pathspec-with-attr.wattr "$_ble_attr_ERR"
  elif (((wtype==_ble_ctx_RDRF||wtype==_ble_ctx_RDRD||wtype==_ble_ctx_RDRD2)&&count>=2)); then
    ble/progcolor/wattr#setattr "$p0" "$_ble_attr_ERR"
  elif ((wtype==_ble_ctx_CMDI)); then
    local attr=${_ble_syntax_attr[wbeg]}
    if ((attr!=_ble_attr_KEYWORD&&attr!=_ble_attr_KEYWORD_BEGIN&&attr!=_ble_attr_KEYWORD_END&&attr!=_ble_attr_KEYWORD_MID&&attr!=_ble_attr_DEL)); then
      local type=; ble/syntax/highlight/cmdtype "$value" "$wtxt"
      if ((type==_ble_attr_CMD_FILE||type==_ble_attr_CMD_FILE||type==_ble_attr_ERR)); then
        ble/progcolor/highlight-filename/.pathspec-with-attr.wattr "$type"
      elif [[ $type ]]; then
        ble/progcolor/wattr#setattr "$p0" "$type"
      fi
    fi
  elif ((wtype==_ble_ctx_RDRD||wtype==_ble_ctx_RDRD2)); then
    if local rex='^[0-9]+-?$|^-$'; [[ $value =~ $rex ]]; then
      ble/progcolor/wattr#setattr "$p0" "$_ble_attr_DEL"
    elif ((wtype==_ble_ctx_RDRD2)); then
      ble/progcolor/highlight-filename/.pathspec-by-name.wattr "$value"
    else
      ble/progcolor/wattr#setattr "$p0" "$_ble_attr_ERR"
    fi
  elif ((wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGEI||wtype==_ble_ctx_VALI||wtype==_ble_attr_VAR||wtype==_ble_ctx_RDRS||wtype==_ble_ctx_RDRF)); then
    ble/progcolor/highlight-filename/.pathspec-by-name.wattr "$value"
  fi
}
function ble/progcolor/highlight-filename.wattr {
  local p0=$1 p1=$2
  if ((p0<p1)) && [[ $bleopt_highlight_filename ]]; then
    local wtxt=${text:p0:p1-p0}
    local ret; ble/progcolor/highlight-filename/.detect-separated-path "$wtxt"; local ext=$?
    ((ext==148)) && return 148
    if ((ext==0)); then
      local sep=$ret ranges i
      ble/syntax:bash/simple-word/locate-filename "$wtxt" "$sep" "url:$highlight_eval_opts"
      (($?==148)) && return 148; ranges=("${ret[@]}")
      for ((i=0;i<${#ranges[@]};i+=2)); do
        ble/progcolor/highlight-filename/.single.wattr "$((p0+ranges[i])):$((p0+ranges[i+1]))"
        (($?==148)) && return 148
      done
    elif ble/syntax:bash/simple-word/is-simple "$wtxt"; then
      ble/progcolor/highlight-filename/.single.wattr "$p0":"$p1"
      (($?==148)) && return 148
    fi
  fi
}
function ble/progcolor/@wattr {
  [[ $wtype =~ ^[0-9]+$ ]] || return 1
  [[ $wattr == - ]] || return 1
  local "${_ble_syntax_progcolor_wattr_vars[@]/%/=}" # WA #D1570 checked
  ble/progcolor/wattr#initialize
  "$@"; local ext=$?
  if ((ext==148)); then
    _ble_textarea_render_defer=1
    ble/syntax/wrange#update _ble_syntax_word_defer_ "$wend"
  else
    ble/progcolor/wattr#finalize
  fi
  return "$ext"
}
function ble/progcolor/word:default/.is-option {
  ((wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGEI||wtype==_ble_ctx_ARGVI)) &&
    ble/string#match "$1" '^(-[-_a-zA-Z0-9!#$%&:;.,^~|\?/*@]*)=?' && # 高速な判定を先に済ませる
    ble/progcolor/is-option-context &&
    ble/string#match "$1" '^(-[-_a-zA-Z0-9!#$%&:;.,^~|\?/*@]*)=?' # 再実行 for BASH_REMATCH
}
function ble/progcolor/word:default/impl.wattr {
  if ((wtype==_ble_ctx_RDRH||wtype==_ble_ctx_RDRI||wtype==_ble_attr_ERR)); then
    ble/progcolor/wattr#setattr "$wbeg" "$wtype"
  elif ((wtype==_ble_ctx_FNAMEI)); then
    if ((_ble_bash<990000)) && [[ ${text:wbeg:wlen} == *[\\\'\"\`\$\<\>\(\)]* ]]; then
      ble/progcolor/wattr#setattr "$wbeg" "$_ble_attr_ERR"
    else
      ble/progcolor/wattr#setattr "$wbeg" "$wtype"
    fi
  else
    local p0=$wbeg p1=$wend wtxt=${text:wbeg:wlen}
    if ((wtype==_ble_attr_VAR||wtype==_ble_ctx_VALI)); then
      local ret
      ble/syntax:bash/find-rhs "$wtype" "$wbeg" "$wlen" element-assignment && p0=$ret
    elif ((wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGEI||wtype==_ble_ctx_VALI)) && { local rex='^[_a-zA-Z][_a-zA-Z0-9]*='; [[ $wtxt =~ $rex ]]; }; then
      ((p0+=${#BASH_REMATCH}))
    elif ble/progcolor/word:default/.is-option "$wtxt"; then
      local rematch=$BASH_REMATCH rematch1=${BASH_REMATCH[1]}
      local ret; ble/color/face2g argument_option
      ble/progcolor/wattr#setg "$p0" "$ret"
      ble/progcolor/wattr#setg "$((p0+${#rematch1}))" d
      ((p0+=${#rematch}))
    fi
    ble/progcolor/highlight-filename.wattr "$p0" "$p1"
    (($?==148)) && return 148
  fi
  return 0
}
function ble/progcolor/word:default {
  ble/progcolor/@wattr ble/progcolor/word:default/impl.wattr
}
function ble/progcolor/default {
  local i "${_ble_syntax_progcolor_vars[@]/%/=}" # WA #D1570 checked
  for ((i=1;i<${#comp_words[@]};i++)); do
    local ref=${tree_words[i]}
    [[ $ref ]] || continue
    local progcolor_iword=$i
    ble/progcolor/load-word-data "$ref"
    ble/progcolor/word:default
  done
}
function ble/progcolor/.compline-rewrite-command {
  local ocmd=${comp_words[0]}
  [[ $1 != "$ocmd" ]] || (($#>=2)) || return 1
  local IFS=$_ble_term_IFS
  local ins="$*"
  comp_line=$ins${comp_line:${#ocmd}}
  ((comp_point-=${#ocmd},comp_point<0&&(comp_point=0),comp_point+=${#ins}))
  comp_words=("$@" "${comp_words[@]:1}")
  ((comp_cword&&(comp_cword+=$#-1)))
  ble/array#reserve-prototype "$#"
  tree_words=("${tree_words[0]}" "${_ble_array_prototype[@]::$#-1}" "${tree_words[@]:1}")
}
function ble/progcolor {
  local cmd=$1 opts=$2
  local -a progcolor_stats=()
  local -a progcolor_wvals=()
  local progcolor_cmdspec_opts=
  local -a progcolor_optctx=()
  local -a alias_args=()
  local checked=" " processed=
  while ((1)); do
    if ble/is-function ble/cmdinfo/cmd:"$cmd"/chroma; then
      ble/progcolor/.compline-rewrite-command "$cmd" "${alias_args[@]}"
      ble/cmdinfo/cmd:"$cmd"/chroma "$opts"
      processed=1
      break
    elif [[ $cmd == */?* ]] && ble/is-function ble/cmdinfo/cmd:"${cmd##*/}"/chroma; then
      ble/progcolor/.compline-rewrite-command "${cmd##*/}" "${alias_args[@]}"
      ble/cmdinfo/cmd:"${cmd##*/}"/chroma "$opts"
      processed=1
      break
    fi
    checked="$checked$cmd "
    local ret
    ble/alias#expand "$cmd"
    ble/string#split-words ret "$ret"
    [[ $checked == *" $ret "* ]] && break
    cmd=$ret
    ((${#ret[@]}>=2)) &&
      alias_args=("${ret[@]:1}" "${alias_args[@]}")
  done
  [[ $processed ]] ||
    ble/progcolor/default
  if [[ ${tree_words[0]} ]]; then
    local "${_ble_syntax_progcolor_vars[@]/%/=}" # WA #D1570 checked
    ble/progcolor/load-word-data "${tree_words[0]}"
    [[ $wattr == - ]] && ble/progcolor/word:default
  fi
}
function ble/highlight/layer:syntax/touch-range {
  ble/syntax/urange#update '' "$@"
}
function ble/highlight/layer:syntax/fill {
  local _ble_local_script='
    local iNAME=0 i1NAME=$2 i2NAME=$3
    for ((iNAME=i1NAME;iNAME<i2NAME;iNAME++)); do
      NAME[iNAME]=$4
    done
  '; builtin eval -- "${_ble_local_script//NAME/$1}"
}
_ble_highlight_layer_syntax_VARNAMES=(
  _ble_highlight_layer_syntax_buff
  _ble_highlight_layer_syntax_active
  _ble_highlight_layer_syntax1_table
  _ble_highlight_layer_syntax2_table
  _ble_highlight_layer_syntax3_list
  _ble_highlight_layer_syntax3_table)
function ble/highlight/layer:syntax/initialize-vars {
  local prev_iN=${#_ble_highlight_layer_plain_buff[*]}
  ble/array#reserve-prototype "$prev_iN"
  _ble_highlight_layer_syntax_buff=("${_ble_array_prototype[@]::prev_iN}")
  _ble_highlight_layer_syntax1_table=("${_ble_array_prototype[@]::prev_iN}")
  _ble_highlight_layer_syntax2_table=("${_ble_array_prototype[@]::prev_iN}")
  _ble_highlight_layer_syntax3_table=("${_ble_array_prototype[@]::prev_iN}") # errors
  _ble_highlight_layer_syntax3_list=()
}
ble/highlight/layer:syntax/initialize-vars
function ble/highlight/layer:syntax/update-attribute-table {
  ble/highlight/layer/update/shift _ble_highlight_layer_syntax1_table
  if ((_ble_syntax_attr_umin>=0)); then
    ble/highlight/layer:syntax/touch-range _ble_syntax_attr_umin _ble_syntax_attr_umax
    local i g=0
    ((_ble_syntax_attr_umin>0)) &&
      ((g=_ble_highlight_layer_syntax1_table[_ble_syntax_attr_umin-1]))
    for ((i=_ble_syntax_attr_umin;i<_ble_syntax_attr_umax;i++)); do
      if [[ ${_ble_syntax_attr[i]} ]]; then
        ble/syntax/attr2g "${_ble_syntax_attr[i]}"
      fi
      _ble_highlight_layer_syntax1_table[i]=$g
    done
    _ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1
  fi
}
function ble/highlight/layer:syntax/word/.update-attributes/.proc {
  [[ $wtype =~ ^[0-9]+$ ]] || return 1
  [[ ${node[TE_nofs+4]} == - ]] || return 1
  if ((wtype==_ble_ctx_CMDI||wtype==_ble_ctx_ARGI||wtype==_ble_ctx_ARGVI||wtype==_ble_ctx_ARGEI)); then
    local comp_line comp_point comp_words comp_cword tree_words
    if ble/syntax:bash/extract-command-by-noderef "$TE_i:$TE_nofs" treeinfo; then
      local cmd=${comp_words[0]}
      ble/progcolor "$cmd"
      return 0
    fi
  fi
  ble/progcolor/word:default
}
function ble/highlight/layer:syntax/word/.update-attributes {
  ((_ble_syntax_word_umin>=0)) || return 1
  local _ble_syntax_bash_simple_eval_timeout=$bleopt_highlight_timeout_sync
  local highlight_eval_opts=cached:single:stopcheck
  [[ $bleopt_highlight_eval_word_limit ]] &&
    highlight_eval_opts=$highlight_eval_opts:limit=$((bleopt_highlight_eval_word_limit))
  if [[ ! $ble_textarea_render_defer_running ]]; then
    local _ble_syntax_bash_simple_eval_timeout_carry=
    highlight_eval_opts=$highlight_eval_opts:timeout-carry
  fi
  ble/syntax/tree-enumerate-in-range "$_ble_syntax_word_umin" "$_ble_syntax_word_umax" \
    ble/highlight/layer:syntax/word/.update-attributes/.proc
}
function ble/highlight/layer:syntax/word/.apply-attribute {
  local wbeg=$1 wend=$2 wattr=$3
  ((wbeg<color_umin&&(wbeg=color_umin),
    wend>color_umax&&(wend=color_umax),
    wbeg<wend)) || return 1
  if [[ $wattr =~ ^[0-9]+$ ]]; then
    ble/array#fill-range _ble_highlight_layer_syntax2_table "$wbeg" "$wend" "$wattr"
  elif [[ $wattr == m* ]]; then
    local ranges; ble/string#split ranges , "${wattr:1}"
    local i=$wbeg j range
    for range in "${ranges[@]}"; do
      local len=${range%%:*} sub_wattr=${range#*:}
      if [[ $len == '$' ]]; then
        j=$wend
      else
        ((j=i+len,j>wend&&(j=wend)))
      fi
      ble/highlight/layer:syntax/word/.apply-attribute "$i" "$j" "$sub_wattr"
      (((i=j)<wend)) || break
    done
  elif [[ $wattr == d ]]; then
    ble/array#fill-range _ble_highlight_layer_syntax2_table "$wbeg" "$wend" ''
  fi
}
function ble/highlight/layer:syntax/word/.proc-childnode {
  if [[ $wtype =~ ^[0-9]+$ ]]; then
    local wbeg=$wbegin wend=$TE_i
    ble/highlight/layer:syntax/word/.apply-attribute "$wbeg" "$wend" "$attr"
  fi
  ((tchild>=0)) && ble/syntax/tree-enumerate-children "$proc_children"
}
function ble/highlight/layer:syntax/update-word-table {
  local color_umin=-1 color_umax=-1 iN=${#_ble_syntax_text}
  ble/highlight/layer:syntax/word/.update-attributes
  ble/highlight/layer/update/shift _ble_highlight_layer_syntax2_table
  ble/syntax/wrange#update _ble_syntax_word_ "$_ble_syntax_vanishing_word_umin" "$_ble_syntax_vanishing_word_umax"
  ble/syntax/wrange#update color_ "$_ble_syntax_vanishing_word_umin" "$_ble_syntax_vanishing_word_umax"
  _ble_syntax_vanishing_word_umin=-1 _ble_syntax_vanishing_word_umax=-1
  ble/highlight/layer:syntax/word/.apply-attribute 0 "$iN" d # clear word color
  local TE_i
  for ((TE_i=_ble_syntax_word_umax;TE_i>=_ble_syntax_word_umin;)); do
    if ((TE_i>0)) && [[ ${_ble_syntax_tree[TE_i-1]} ]]; then
      local -a node
      ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
      local wlen=${node[1]}
      local wbeg=$((TE_i-wlen)) wend=$TE_i
      if [[ ${node[0]} =~ ^[0-9]+$ ]]; then
        local attr=${node[4]}
        ble/highlight/layer:syntax/word/.apply-attribute "$wbeg" "$wend" "$attr"
      fi
      local tclen=${node[2]}
      if ((tclen>=0)); then
        local tchild=$((TE_i-tclen))
        local tree= TE_nofs=0 proc_children=ble/highlight/layer:syntax/word/.proc-childnode
        ble/syntax/tree-enumerate-children "$proc_children"
      fi
      ((TE_i=wbeg))
    else
      ((TE_i--))
    fi
  done
  ((color_umin>=0)) && ble/highlight/layer:syntax/touch-range "$color_umin" "$color_umax"
  _ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1
}
function ble/highlight/layer:syntax/update-error-table/set {
  local i1=$1 i2=$2 g=$3
  if ((i1<i2)); then
    ble/highlight/layer:syntax/touch-range "$i1" "$i2"
    ble/highlight/layer:syntax/fill _ble_highlight_layer_syntax3_table "$i1" "$i2" "$g"
    _ble_highlight_layer_syntax3_list[${#_ble_highlight_layer_syntax3_list[@]}]="$i1 $i2"
  fi
}
function ble/highlight/layer:syntax/update-error-table {
  ble/highlight/layer/update/shift _ble_highlight_layer_syntax3_table
  local j=0 jN=${#_ble_highlight_layer_syntax3_list[*]}
  if ((jN)); then
    for ((j=0;j<jN;j++)); do
      local -a range
      ble/string#split-words range "${_ble_highlight_layer_syntax3_list[j]}"
      local a=${range[0]} b=${range[1]}
      ((a>=DMAX0?(a+=DMAX-DMAX0):(a>=DMIN&&(a=DMIN)),
        b>=DMAX0?(b+=DMAX-DMAX0):(b>=DMIN&&(b=DMIN))))
      if ((a<b)); then
        ble/highlight/layer:syntax/fill _ble_highlight_layer_syntax3_table "$a" "$b" ''
        ble/highlight/layer:syntax/touch-range "$a" "$b"
      fi
    done
    _ble_highlight_layer_syntax3_list=()
  fi
  if ((iN>0)) && [[ ${_ble_syntax_stat[iN]} ]]; then
    local ret; ble/color/face2g syntax_error; local g=$ret
    local -a stat
    ble/string#split-words stat "${_ble_syntax_stat[iN]}"
    local ctx=${stat[0]} nlen=${stat[3]} nparam=${stat[6]}
    [[ $nparam == none ]] && nparam=
    local i inest
    if ((nlen>0)) || [[ $nparam ]]; then
      ble/highlight/layer:syntax/update-error-table/set "$((iN-1))" "$iN" "$g"
      if ((nlen>0)); then
        ((inest=iN-nlen))
        while ((inest>=0)); do
          local inest2
          for ((inest2=inest+1;inest2<iN;inest2++)); do
            [[ ${_ble_syntax_attr[inest2]} ]] && break
          done
          ble/highlight/layer:syntax/update-error-table/set "$inest" "$inest2" "$g"
          ((i=inest))
          local wtype wbegin tchild tprev
          ble/syntax/parse/nest-pop
          ((inest>=i&&(inest=i-1)))
        done
      fi
    fi
    if ((ctx==_ble_ctx_CMDX1||ctx==_ble_ctx_CMDXC||ctx==_ble_ctx_FARGX1||ctx==_ble_ctx_SARGX1||ctx==_ble_ctx_FARGX2||ctx==_ble_ctx_CARGX1||ctx==_ble_ctx_CARGX2||ctx==_ble_ctx_COARGX)); then
      ble/highlight/layer:syntax/update-error-table/set "$((iN-1))" "$iN" "$g"
    fi
  fi
}
function ble/highlight/layer:syntax/update {
  local text=$1 player=$2
  local i iN=${#text}
  local umin=-1 umax=-1
  ((DMIN>=0)) && umin=$DMIN umax=$DMAX
  if [[ ! $bleopt_highlight_syntax ]]; then
    if [[ $_ble_highlight_layer_syntax_active ]]; then
      _ble_highlight_layer_syntax_active=
      PREV_UMIN=0 PREV_UMAX=${#1}
    fi
    return 0
  fi
  if [[ ! $_ble_highlight_layer_syntax_active ]]; then
    _ble_highlight_layer_syntax_active=1
    umin=0 umax=${#text}
  fi
  if [[ $bleopt_syntax_debug ]]; then
    local debug_attr_umin=$_ble_syntax_attr_umin
    local debug_attr_uend=$_ble_syntax_attr_umax
  fi
  ble/cmdspec/initialize # load chroma
  ble/highlight/layer:syntax/update-attribute-table
  ble/highlight/layer:syntax/update-word-table
  ble/highlight/layer:syntax/update-error-table
  if ((DMIN>=0)); then
    ble/highlight/layer/update/shift _ble_highlight_layer_syntax_buff
    if ((DMAX>0)); then
      local g sgr ch ret
      ble/highlight/layer:syntax/getg "$DMAX"
      ble/color/g2sgr "$g"; sgr=$ret
      ch=${_ble_highlight_layer_plain_buff[DMAX]}
      _ble_highlight_layer_syntax_buff[DMAX]=$sgr$ch
    fi
  fi
  local i j g gprev=0
  if ((umin>0)); then
    ble/highlight/layer:syntax/getg "$((umin-1))"
    gprev=$g
  fi
  if ((umin>=0)); then
    local ret
    for ((i=umin;i<=umax;i++)); do
      local ch=${_ble_highlight_layer_plain_buff[i]}
      ble/highlight/layer:syntax/getg "$i"
      [[ $g ]] || ble/highlight/layer/update/getg "$i"
      if ((gprev!=g)); then
        ble/color/g2sgr "$g"
        ch=$ret$ch
        ((gprev=g))
      fi
      _ble_highlight_layer_syntax_buff[i]=$ch
    done
  fi
  PREV_UMIN=$umin PREV_UMAX=$umax
  PREV_BUFF=_ble_highlight_layer_syntax_buff
  if [[ $bleopt_syntax_debug ]]; then
    local status buff= nl=$'\n'
    _ble_syntax_attr_umin=$debug_attr_umin _ble_syntax_attr_umax=$debug_attr_uend ble/syntax/print-status -v status
    local -a DRAW_BUFF=()
    ble/syntax/print-layer-buffer.draw plain
    ble/syntax/print-layer-buffer.draw syntax
    ble/syntax/print-layer-buffer.draw disabled
    ble/syntax/print-layer-buffer.draw region
    ble/syntax/print-layer-buffer.draw overwrite
    local ret; ble/canvas/sflush.draw
    status=$status$ret
    ble/edit/info/show ansi "$status"
  fi
}
function ble/highlight/layer:syntax/getg {
  [[ $bleopt_highlight_syntax ]] || return 1
  local i=$1
  if [[ ${_ble_highlight_layer_syntax3_table[i]} ]]; then
    g=${_ble_highlight_layer_syntax3_table[i]}
  elif [[ ${_ble_highlight_layer_syntax2_table[i]} ]]; then
    g=${_ble_highlight_layer_syntax2_table[i]}
  elif [[ ${_ble_highlight_layer_syntax1_table[i]} ]]; then
    g=${_ble_highlight_layer_syntax1_table[i]}
  fi
}
function ble/highlight/layer:syntax/textarea_render_defer.hook {
  ble/syntax/wrange#update _ble_syntax_word_ "$_ble_syntax_word_defer_umin" "$_ble_syntax_word_defer_umax"
  _ble_syntax_word_defer_umin=-1
  _ble_syntax_word_defer_umax=-1
}
blehook textarea_render_defer!=ble/highlight/layer:syntax/textarea_render_defer.hook
function ble/syntax/import { return 0; }
blehook/invoke syntax_load
ble/function#try ble/textarea#invalidate str
return 0
