core-syntax.sh (285716B)
1 #!/bin/bash 2 #%[release = 0] 3 #%m main ( 4 5 function ble/syntax/util/is-directory { 6 local path=$1 7 # Note: #D1168 Cygwin では // で始まるパスの判定は遅い 8 if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then 9 [[ $path == // ]] 10 else 11 [[ -d $path ]] 12 fi 13 } 14 15 ## @fn ble/syntax/urange#update prefix p1 p2 16 ## @fn ble/syntax/wrange#update prefix p1 [p2] 17 ## @param[in] prefix 18 ## @param[in] p1 p2 19 ## @var[in,out] {prefix}umin {prefix}umax 20 ## 21 ## ble/syntax/urange#update に関しては、 22 ## ble/urange#update --prefix=prefix p1 p2 に等価である。 23 ## ble/syntax/wrange#update に対応するものはない。 24 ## 25 function ble/syntax/urange#update { 26 local prefix=$1 27 local p1=$2 p2=${3:-$2} 28 ((0<=p1&&p1<p2)) || return 1 29 (((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1), 30 (${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2))) 31 } 32 function ble/syntax/wrange#update { 33 local prefix=$1 34 local p1=$2 p2=${3:-$2} 35 ((0<=p1&&p1<=p2)) || return 1 36 (((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1), 37 (${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2))) 38 } 39 40 ## @fn ble/syntax/urange#shift prefix 41 ## @fn ble/syntax/wrange#shift prefix 42 ## @param[in] prefix 43 ## @var[in] beg end end0 shift 44 ## @var[in,out] {prefix}umin {prefix}umax 45 ## 46 ## ble/syntax/urange#shift に関しては、 47 ## ble/urange#shift --prefix=prefix "$beg" "$end" "$end0" "$shift" に等価である。 48 ## ble/syntax/wrange#shift に対応するものはない。 49 ## 50 function ble/syntax/urange#shift { 51 local prefix=$1 52 ((${prefix}umin>=end0?(${prefix}umin+=shift):( 53 ${prefix}umin>=beg&&(${prefix}umin=end)), 54 ${prefix}umax>end0?(${prefix}umax+=shift):( 55 ${prefix}umax>beg&&(${prefix}umax=beg)), 56 ${prefix}umin>=${prefix}umax&& 57 (${prefix}umin=${prefix}umax=-1))) 58 } 59 function ble/syntax/wrange#shift { 60 local prefix=$1 61 62 # ※以下の不等号について (動作を見ながら) 63 # もう一度考え直した方が良いかも。 64 ((${prefix}umin>=end0?(${prefix}umin+=shift):( 65 ${prefix}umin>beg&&(${prefix}umin=end)), 66 ${prefix}umax>=end0?(${prefix}umax+=shift):( 67 ${prefix}umax>=beg&&(${prefix}umax=beg)), 68 ${prefix}umin==0&&++${prefix}umin, 69 ${prefix}umin>${prefix}umax&& 70 (${prefix}umin=${prefix}umax=-1))) 71 } 72 73 ## @var _ble_syntax_text 74 ## 解析対象の文字列を保持する。 75 ## @var _ble_syntax_lang 76 ## 解析対象の言語を保持する。 77 ## 78 ## @var _ble_syntax_stat[i] 79 ## 文字 #i を解釈しようとする直前の状態を記録する。 80 ## 各要素は "ctx wlen wtype nlen tclen tplen nparam lookahead" の形式をしている。 81 ## 82 ## @var ctx = int (stat[0]) 83 ## 現在の文脈。 84 ## @var wlen = int (stat[1]) 85 ## 現在のシェル単語の継続している長さ。 86 ## @var wtype = string (stat[2]) 87 ## 現在のシェル単語の種類。 88 ## @var nlen = int (stat[3]) 89 ## 現在の入れ子状態が継続している長さ。 90 ## @var tclen,tplen = int (stat[4], stat[5]) 91 ## tchild, tprev の負オフセット。 92 ## @var nparam = string (stat[6]) 93 ## その入れ子レベルに特有のデータ一般を記録する文字列。 94 ## ヒアドキュメントの開始情報を記録するのに使用する。 95 ## 将来的に `{ .. }` や `do .. done` の対応を取るのにも使うかもしれない。 96 ## 解析変数の nparam が空文字列のときは "none" という値を格納する。 97 ## @var lookahead = int (stat[7]) 98 ## 先読みの文字数を指定します。通常は 1 です。 99 ## この _ble_syntax_stat 要素の情報に影響を与えた、 100 ## 対応する点以降の文字数を格納します。文字列末端も 1 文字と数えます。 101 ## 102 ## @var _ble_syntax_nest[inest] 103 ## 入れ子の情報 104 ## 各要素は "ctx wlen wtype inest tclen tplen nparam ntype" の形式をしている。 105 ## ctx wbegin inest wtype nparam は入れ子を抜けた時の状態を表す。 106 ## ntype は入れ子の種類を表す文字列。 107 ## nparam は復帰時の入れ子レベルにおける nparam 値を保持する。 108 ## nparam 値が空文字列の場合には代わりに none という文字列が格納される。 109 ## 110 ## @var _ble_syntax_tree[i-1] 111 ## 境界 #i で終端した範囲 (単語・入れ子) についての情報を保持する。 112 ## 同じ位置で複数の階層の範囲が終端した場合は、それらの情報が連結されて格納される。 113 ## 各要素は "( wtype wlen tclen tplen wattr )*" の形式をしている。 114 ## より外側の範囲の情報はより左側に格納される。 115 ## 116 ## tclen tplen を用いて他の _ble_syntax_tree 要素を参照する。 117 ## 別の位置から或る位置を参照するとき、一番左側の範囲情報を参照する。 118 ## 或る位置から自分自身を参照するとき、同じ要素の一つ右の範囲情報を参照する。 119 ## 120 ## wtype (ntype) 121 ## 範囲の種類を保持する。範囲が単語のとき、文脈値を整数で保持する。 122 ## 範囲が入れ子範囲のとき、整数以外の文字列になる。 123 ## 124 ## wlen (nlen) 125 ## 範囲の長さを保持する。範囲の開始点は i-wlen である。 126 ## 127 ## tclen 128 ## 0 以上の時、一つ内側の要素の終端位置までの offset を保持する。 129 ## _ble_syntax_tree[i-1-tclen] に子要素の情報が格納されている。 130 ## 子要素がないとき負の値。 131 ## 132 ## tplen 133 ## 0 以上の時、一つ前の兄弟要素までの offset を保持する。 134 ## _ble_syntax_tree[i-1-tplen] に兄要素の情報が格納されている。 135 ## 兄要素が同じ位置で終端することはないので必ず正の値になるはず。 136 ## 兄要素がないとき (自分が長男要素のとき) 負の値。 137 ## 138 ## attr (wattr) または - or -- 139 ## 単語の着色に関する情報を保持する。 140 ## 以下の何れかの形式を持つ。 141 ## 142 ## "-" 143 ## 単語の着色が未だ計算されていない事を表す。 144 ## g 145 ## 描画属性値を保持する。 146 ## 'm' len ':' attr (',' len ':' attr)* 147 ## 長さ len の部分列と属性の組。 148 ## 長さ len として '$' を指定した場合は単語終端までを意味する。 149 ## 'd' 150 ## 描画属性を削除することを意味する。 151 ## 152 ## @var _ble_syntax_TREE_WIDTH 153 ## _ble_syntax_tree に格納される一つの範囲情報のフィールドの数。 154 ## 155 ## @var _ble_syntax_attr[i] 156 ## 文脈・属性の情報 157 _ble_syntax_text= 158 _ble_syntax_lang=bash 159 _ble_syntax_stat=() 160 _ble_syntax_nest=() 161 _ble_syntax_tree=() 162 _ble_syntax_attr=() 163 164 _ble_syntax_TREE_WIDTH=5 165 166 #-------------------------------------- 167 # ble/syntax/tree-enumerate proc 168 # ble/syntax/tree-enumerate-children proc 169 # ble/syntax/tree-enumerate-in-range beg end proc 170 171 function ble/syntax/tree-enumerate/.add-root-element { 172 local wtype=$1 wlen=$2 tclen=$3 tplen=$4 173 174 # Note: wtype は単語に格納される時に EndWtype テーブルで変換される。 175 # ble/syntax:bash/ctx-command/check-word-end の実装を参照の事。 176 [[ ! ${wtype//[0-9]} && ${_ble_syntax_bash_command_EndWtype[wtype]} ]] && 177 wtype=${_ble_syntax_bash_command_EndWtype[wtype]} 178 179 TE_root="$wtype $wlen $tclen $tplen -- $TE_root" 180 } 181 182 ## @fn ble/syntax/tree-enumerate/.initialize 183 ## 184 ## @var[in] iN 185 ## 文字列の長さ、つまり現在の解析終端位置を指定します。 186 ## 187 ## @var[out] TE_root 188 ## ${_ble_syntax_tree[iN-1]} を調整して返します。 189 ## 閉じていない範囲 (word, nest) を終端位置で閉じたときの値を計算します。 190 ## 191 ## @var[out] TE_i 192 ## 一番最後の範囲の終端位置を返します。 193 ## 解析情報がない場合は -1 を返します。 194 ## 195 ## @var[out] TE_nofs 196 ## 0 に初期化します。 197 ## 198 function ble/syntax/tree-enumerate/.initialize { 199 if [[ ! ${_ble_syntax_stat[iN]} ]]; then 200 TE_root= TE_i=-1 TE_nofs=0 201 return 0 202 fi 203 204 local -a stat nest 205 ble/string#split-words stat "${_ble_syntax_stat[iN]}" 206 local wtype=${stat[2]} 207 local wlen=${stat[1]} 208 local nlen=${stat[3]} inest 209 ((inest=nlen<0?nlen:iN-nlen)) 210 local tclen=${stat[4]} 211 local tplen=${stat[5]} 212 213 TE_root= 214 ((iN>0)) && TE_root=${_ble_syntax_tree[iN-1]} 215 216 while 217 if ((wlen>=0)); then 218 # TE_root に単語ノードを追加 219 ble/syntax/tree-enumerate/.add-root-element "$wtype" "$wlen" "$tclen" "$tplen" 220 tclen=0 221 fi 222 ((inest>=0)) 223 do 224 ble/util/assert '[[ ${_ble_syntax_nest[inest]} ]]' "$FUNCNAME/FATAL1" || break 225 226 ble/string#split-words nest "${_ble_syntax_nest[inest]}" 227 228 local olen=$((iN-inest)) 229 tplen=${nest[4]} 230 ((tplen>=0&&(tplen+=olen))) 231 232 # TE_root にネストノードを追加 233 ble/syntax/tree-enumerate/.add-root-element "${nest[7]}" "$olen" "$tclen" "$tplen" 234 235 wtype=${nest[2]} wlen=${nest[1]} nlen=${nest[3]} tclen=0 tplen=${nest[5]} 236 ((wlen>=0&&(wlen+=olen), 237 tplen>=0&&(tplen+=olen), 238 nlen>=0&&(nlen+=olen), 239 inest=nlen<0?nlen:iN-nlen)) 240 241 ble/util/assert '((nlen<0||nlen>olen))' "$FUNCNAME/FATAL2" || break 242 done 243 244 if [[ $TE_root ]]; then 245 ((TE_i=iN)) 246 else 247 ((TE_i=tclen>=0?iN-tclen:tclen)) 248 fi 249 ((TE_nofs=0)) 250 } 251 252 ## @fn ble/syntax/tree-enumerate/.impl command... 253 ## @param[in] command... 254 ## 各ノードについて呼び出すコマンドを指定します。 255 ## @var[in] iN 256 ## @var[in] TE_root,TE_i,TE_nofs 257 function ble/syntax/tree-enumerate/.impl { 258 local islast=1 259 while ((TE_i>0)); do 260 local -a node 261 if ((TE_i<iN)); then 262 ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}" 263 else 264 ble/string#split-words node "${TE_root:-${_ble_syntax_tree[iN-1]}}" 265 fi 266 267 ble/util/assert '((TE_nofs<${#node[@]}))' "$FUNCNAME(i=$TE_i,iN=$iN,TE_nofs=$TE_nofs,node=${node[*]},command=$@)/FATAL1" || break 268 269 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]} 270 local wbegin=$((wlen<0?wlen:TE_i-wlen)) 271 local tchild=$((tclen<0?tclen:TE_i-tclen)) 272 local tprev=$((tplen<0?tplen:TE_i-tplen)) 273 "$@" 274 275 ble/util/assert '((tprev<TE_i))' "$FUNCNAME/FATAL2" || break 276 277 ((TE_i=tprev,TE_nofs=0,islast=0)) 278 done 279 } 280 281 ## @var[in] iN 282 ## @var[in] TE_root,TE_i,TE_nofs 283 ## @var[in] tchild 284 function ble/syntax/tree-enumerate-children { 285 ((0<tchild&&tchild<=TE_i)) || return 1 286 local TE_nofs=$((TE_i==tchild?TE_nofs+_ble_syntax_TREE_WIDTH:0)) 287 local TE_i=$tchild 288 ble/syntax/tree-enumerate/.impl "$@" 289 } 290 function ble/syntax/tree-enumerate-break { ((tprev=-1)); } 291 292 ## @fn ble/syntax/tree-enumerate command... 293 ## 現在の解析状態 _ble_syntax_tree に基いて、 294 ## 指定したコマンド command... を 295 ## トップレベルの各ノードに対して末尾にあるノードから順に呼び出します。 296 ## 297 ## @param[in] command... 298 ## 呼び出すコマンドを指定します。 299 ## 300 ## コマンドは以下のシェル変数を入力・出力とします。 301 ## @var[in] TE_i TE_nofs 302 ## @var[in] wtype wbegin wlen attr tchild 303 ## @var[in,out] tprev 304 ## 列挙を中断する時は ble/syntax/tree-enumerate-break 305 ## を呼び出す事によって、tprev=-1 を設定します。 306 ## 307 ## 内部で ble/syntax/tree-enumerate-children を呼び出すと、 308 ## 更に入れ子になった単語について処理を実行する事ができます。 309 ## 310 ## @var[in] iN 311 ## 解析の起点を指定します。_ble_syntax_stat が設定されている必要があります。 312 ## 指定を省略した場合は _ble_syntax_stat の末尾が使用されます。 313 function ble/syntax/tree-enumerate { 314 local TE_root TE_i TE_nofs 315 [[ ${iN:+set} ]] || local iN=${#_ble_syntax_text} 316 ble/syntax/tree-enumerate/.initialize 317 ble/syntax/tree-enumerate/.impl "$@" 318 } 319 320 ## @fn ble/syntax/tree-enumerate-in-range beg end proc 321 ## 入れ子構造に従わず或る範囲内に登録されている節を列挙します。 322 ## @param[in] beg,end 323 ## @param[in] proc 324 ## 以下の変数を使用する関数を指定します。 325 ## @var[in] wtype wlen wbeg wend wattr 326 ## @var[in] node 327 ## @var[in] TE_i TE_nofs 328 function ble/syntax/tree-enumerate-in-range { 329 local beg=$1 end=$2 330 local proc=$3 331 local -a node 332 local TE_i TE_nofs 333 for ((TE_i=end;TE_i>=beg;TE_i--)); do 334 ((TE_i>0)) && [[ ${_ble_syntax_tree[TE_i-1]} ]] || continue 335 ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}" 336 local flagUpdateNode= 337 for ((TE_nofs=0;TE_nofs<${#node[@]};TE_nofs+=_ble_syntax_TREE_WIDTH)); do 338 local wtype=${node[TE_nofs]} wlen=${node[TE_nofs+1]} wattr=${node[TE_nofs+4]} 339 local wbeg=$((wlen<0?wlen:TE_i-wlen)) wend=$TE_i 340 "${@:3}" 341 done 342 done 343 } 344 345 #-------------------------------------- 346 # ble/syntax/print-status 347 348 function ble/syntax/print-status/.graph { 349 local char=$1 350 if ble/util/isprint+ "$char"; then 351 graph="'$char'" 352 return 0 353 else 354 local ret 355 ble/util/s2c "$char" 356 local code=$ret 357 if ((code<32)); then 358 ble/util/c2s "$((code+64))" 359 graph="$_ble_term_rev^$ret$_ble_term_sgr0" 360 elif ((code==127)); then 361 graph="$_ble_term_rev^?$_ble_term_sgr0" 362 elif ((128<=code&&code<160)); then 363 ble/util/c2s "$((code-64))" 364 graph="${_ble_term_rev}M-^$ret$_ble_term_sgr0" 365 else 366 graph="'$char' ($code)" 367 fi 368 fi 369 } 370 371 ## @var[in,out] word 372 function ble/syntax/print-status/.tree-prepend { 373 local j=$1 374 local value=$2${tree[j]} 375 tree[j]=$value 376 ((max_tree_width<${#value}&&(max_tree_width=${#value}))) 377 } 378 379 function ble/syntax/print-status/.dump-arrays/.append-attr-char { 380 if (($?==0)); then 381 attr="${attr}$1" 382 else 383 attr="${attr} " 384 fi 385 } 386 387 ## @fn ble/syntax/print-status/ctx#get-text ctx 388 ## @var[out] ret 389 function ble/syntax/print-status/ctx#get-text { 390 local sgr 391 ble/syntax/ctx#get-name "$1" 392 ret=${ret#BLE_} 393 if [[ ! $ret ]]; then 394 ble/color/face2sgr syntax_error 395 ret="${ret}CTX$1$_ble_term_sgr0" 396 fi 397 } 398 ## @fn ble/syntax/print-status/word.get-text index 399 ## _ble_syntax_tree[index] の内容を文字列にします。 400 ## @param[in] index 401 ## @var[out] word 402 function ble/syntax/print-status/word.get-text { 403 local index=$1 404 ble/string#split-words word "${_ble_syntax_tree[index]}" 405 local out= ret 406 if [[ $word ]]; then 407 local nofs=$((${#word[@]}/_ble_syntax_TREE_WIDTH*_ble_syntax_TREE_WIDTH)) 408 while (((nofs-=_ble_syntax_TREE_WIDTH)>=0)); do 409 local axis=$((index+1)) 410 411 local wtype=${word[nofs]} 412 if [[ $wtype =~ ^[0-9]+$ ]]; then 413 ble/syntax/print-status/ctx#get-text "$wtype"; wtype=$ret 414 elif [[ $wtype =~ ^n* ]]; then 415 # Note: nest-pop 時の tree-append では prefix n を付けている。 416 wtype=$sgr_quoted\"${wtype:1}\"$_ble_term_sgr0 417 else 418 wtype=$sgr_error${wtype}$_ble_term_sgr0 419 fi 420 421 local b=$((axis-word[nofs+1])) e=$axis 422 local sprev=${word[nofs+3]} schild=${word[nofs+2]} 423 if ((sprev>=0)); then 424 sprev="@$((axis-sprev-1))>" 425 else 426 sprev= 427 fi 428 if ((schild>=0)); then 429 schild=">@$((axis-schild-1))" 430 else 431 schild= 432 fi 433 434 local wattr=${word[nofs+4]} 435 if [[ $wattr != - ]]; then 436 wattr="/(wattr=$wattr)" 437 else 438 wattr= 439 fi 440 441 out=" word=$wtype:$sprev$b-$e$schild$wattr$out" 442 for ((;b<index;b++)); do 443 ble/syntax/print-status/.tree-prepend "$b" '|' 444 done 445 ble/syntax/print-status/.tree-prepend "$index" '+' 446 done 447 word=$out 448 fi 449 } 450 ## @fn ble/syntax/print-status/nest.get-text index 451 ## _ble_syntax_nest[index] の内容を文字列にします。 452 ## @param[in] index 453 ## @var[out] nest 454 function ble/syntax/print-status/nest.get-text { 455 local index=$1 456 ble/string#split-words nest "${_ble_syntax_nest[index]}" 457 if [[ $nest ]]; then 458 local ret 459 ble/syntax/print-status/ctx#get-text "${nest[0]}"; local nctx=$ret 460 461 local nword=- 462 if ((nest[1]>=0)); then 463 ble/syntax/print-status/ctx#get-text "${nest[2]}"; local swtype=$ret 464 local wbegin=$((index-nest[1])) 465 nword="$swtype:$wbegin-" 466 fi 467 468 local nnest=- 469 ((nest[3]>=0)) && nnest="'${nest[7]}':$((index-nest[3]))-" 470 471 local nchild=- 472 if ((nest[4]>=0)); then 473 local tchild=$((index-nest[4])) 474 nchild='$'$tchild 475 if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then 476 nchild=$sgr_error$nchild$_ble_term_sgr0 477 fi 478 fi 479 480 local nprev=- 481 if ((nest[5]>=0)); then 482 local tprev=$((index-nest[5])) 483 nprev='$'$tprev 484 if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then 485 nprev=$sgr_error$nprev$_ble_term_sgr0 486 fi 487 fi 488 489 local nparam=${nest[6]} 490 if [[ $nparam == none ]]; then 491 nparam= 492 else 493 # Note #D1774: bash-3.0 bug "${var//../$'...'}" とすると $'' の引用符が残 494 # る問題の回避の為に行を分けて代入する。 495 nparam=${nparam//$_ble_term_FS/$'\e[7m^\\\e[m'} 496 nparam=" nparam=$nparam" 497 fi 498 499 nest=" nest=($nctx w=$nword n=$nnest t=$nchild:$nprev$nparam)" 500 fi 501 } 502 ## @fn ble/syntax/print-status/stat.get-text index 503 ## _ble_syntax_stat[index] の内容を文字列にします。 504 ## @param[in] index 505 ## @var[out] stat 506 function ble/syntax/print-status/stat.get-text { 507 local index=$1 508 ble/string#split-words stat "${_ble_syntax_stat[index]}" 509 if [[ $stat ]]; then 510 local ret 511 ble/syntax/print-status/ctx#get-text "${stat[0]}"; local stat_ctx=$ret 512 513 local stat_word=- 514 if ((stat[1]>=0)); then 515 ble/syntax/print-status/ctx#get-text "${stat[2]}"; local stat_wtype=$ret 516 stat_word="$stat_wtype:$((index-stat[1]))-" 517 fi 518 519 local stat_inest=- 520 if ((stat[3]>=0)); then 521 local inest=$((index-stat[3])) 522 stat_inest="@$inest" 523 if ((inest<0)) || [[ ! ${_ble_syntax_nest[inest]} ]]; then 524 stat_inest=$sgr_error$stat_inest$_ble_term_sgr0 525 fi 526 fi 527 528 local stat_child=- 529 if ((stat[4]>=0)); then 530 local tchild=$((index-stat[4])) 531 stat_child='$'$tchild 532 if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then 533 stat_child=$sgr_error$stat_child$_ble_term_sgr0 534 fi 535 fi 536 537 local stat_prev=- 538 if ((stat[5]>=0)); then 539 local tprev=$((index-stat[5])) 540 stat_prev='$'$tprev 541 if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then 542 stat_prev=$sgr_error$stat_prev$_ble_term_sgr0 543 fi 544 fi 545 546 local snparam=${stat[6]} 547 if [[ $snparam == none ]]; then 548 snparam= 549 else 550 # Note #D1774: bash-3.0 bug "${var//$'...'}" とすると余分な引用符が残る問 551 # 題を回避する為に行を分けて代入している。 552 snparam=${snparam//"$_ble_term_FS"/$'\e[7m^\\\e[m'} 553 snparam=" nparam=$snparam" 554 fi 555 556 local stat_lookahead= 557 ((stat[7]!=1)) && stat_lookahead=" >>${stat[7]}" 558 stat=" stat=($stat_ctx w=$stat_word n=$stat_inest t=$stat_child:$stat_prev$snparam$stat_lookahead)" 559 fi 560 } 561 562 ## @var[out] resultA 563 ## @var[in] iN 564 function ble/syntax/print-status/.dump-arrays { 565 local -a tree char line 566 tree=() 567 char=() 568 line=() 569 570 local ret 571 ble/color/face2sgr syntax_error; local sgr_error=$ret 572 ble/color/face2sgr syntax_quoted; local sgr_quoted=$ret 573 574 local i max_tree_width=0 575 for ((i=0;i<=iN;i++)); do 576 local attr=" ${_ble_syntax_attr[i]:-|}" 577 if ((_ble_syntax_attr_umin<=i&&i<_ble_syntax_attr_umax)); then 578 attr="${attr:${#attr}-2:2}*" 579 else 580 attr="${attr:${#attr}-2:2} " 581 fi 582 583 [[ ${_ble_highlight_layer_syntax1_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax1_table[i]}" 584 ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}a${_ble_term_sgr0}" 585 [[ ${_ble_highlight_layer_syntax2_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax2_table[i]}" 586 ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}w${_ble_term_sgr0}" 587 [[ ${_ble_highlight_layer_syntax3_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax3_table[i]}" 588 ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}e${_ble_term_sgr0}" 589 590 [[ ${_ble_syntax_stat_shift[i]} ]] 591 ble/syntax/print-status/.dump-arrays/.append-attr-char s 592 593 local index=000$i 594 index=${index:${#index}-3:3} 595 596 local word nest stat 597 ble/syntax/print-status/word.get-text "$i" 598 ble/syntax/print-status/nest.get-text "$i" 599 ble/syntax/print-status/stat.get-text "$i" 600 601 local graph= 602 ble/syntax/print-status/.graph "${_ble_syntax_text:i:1}" 603 char[i]="$attr $index $graph" 604 line[i]=$word$nest$stat 605 done 606 607 resultA='_ble_syntax_attr/tree/nest/stat?'$'\n' 608 ble/string#reserve-prototype "$max_tree_width" 609 for ((i=0;i<=iN;i++)); do 610 local t=${tree[i]}${_ble_string_prototype::max_tree_width} 611 resultA="$resultA${char[i]} ${t::max_tree_width}${line[i]}"$'\n' 612 done 613 } 614 615 ## @fn ble/syntax/print-status/.dump-tree/proc1 616 ## @var[out] resultB 617 ## @var[in] prefix 618 ## @var[in] nl 619 function ble/syntax/print-status/.dump-tree/proc1 { 620 local tip="| "; tip=${tip:islast:1} 621 prefix="$prefix$tip " ble/syntax/tree-enumerate-children ble/syntax/print-status/.dump-tree/proc1 622 resultB="$prefix\_ '${_ble_syntax_text:wbegin:wlen}'$nl$resultB" 623 } 624 625 ## @fn ble/syntax/print-status/.dump-tree 626 ## @var[out] resultB 627 ## @var[in] iN 628 function ble/syntax/print-status/.dump-tree { 629 resultB= 630 631 local nl=$_ble_term_nl 632 local prefix= 633 ble/syntax/tree-enumerate ble/syntax/print-status/.dump-tree/proc1 634 } 635 636 function ble/syntax/print-status { 637 local iN=${#_ble_syntax_text} 638 639 local resultA 640 ble/syntax/print-status/.dump-arrays 641 642 local resultB 643 ble/syntax/print-status/.dump-tree 644 645 local result=$resultA$resultB 646 if [[ $1 == -v && $2 ]]; then 647 local "${2%%\[*\]}" && ble/util/upvar "$2" "$result" 648 else 649 ble/util/print "$result" 650 fi 651 } 652 653 function ble/syntax/print-layer-buffer.draw { 654 local layer_name=$1 655 local -a keys vals 656 builtin eval "keys=(\"\${!_ble_highlight_layer_${layer_name}_buff[@]}\")" 657 builtin eval "vals=(\"\${_ble_highlight_layer_${layer_name}_buff[@]}\")" 658 659 local ret sgr0=$_ble_term_sgr0 660 ble/color/face2sgr command_builtin; local sgr1=$ret 661 ble/color/face2sgr syntax_varname; local sgr2=$ret 662 ble/color/face2sgr syntax_quoted; local sgr3=$ret 663 664 ble/canvas/put.draw "${sgr1}buffer${sgr0} ${sgr2}$layer_name${sgr0}=(" 665 local i count=0 666 for ((i=0;i<${#keys[@]};i++)); do 667 local key=${keys[i]} val=${vals[i]} 668 while ((count++<key)); do 669 ((count==1)) || ble/canvas/put.draw ' ' 670 ble/canvas/put.draw $'\e[91munset\e[m' 671 done 672 673 ((count==1)) || ble/canvas/put.draw ' ' 674 ble/string#quote-word "$val" quote-empty:sgrq="$sgr3" 675 ble/canvas/put.draw "$ret" 676 done 677 ble/canvas/put.draw ")$_ble_term_nl" 678 } 679 680 #-------------------------------------- 681 682 function ble/syntax/parse/serialize-stat { 683 ((ilook<=i&&(ilook=i+1))) 684 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))" 685 } 686 687 688 ## @fn ble/syntax/parse/set-lookahead count 689 ## 690 ## @param[in] count 691 ## 現在位置の何文字先まで参照して動作を決定したかを指定します。 692 ## @var[out] i 693 ## @var[out] ilook 694 ## 695 ## 例えば "a@bcdx" の @ の位置に i があって、 696 ## x の文字を見て c 直後までしか読み取らない事を決定したとき、 697 ## set-lookahead 4 を実行して i を 2 進めるか、 698 ## i を 2 進めてから set-lookahead 2 を実行します。 699 ## 700 ## 最終的に i の次の 1 文字までしか参照しない時、 701 ## set-lookahead を呼び出す必要はありません。 702 ## 703 function ble/syntax/parse/set-lookahead { 704 ((i+$1>ilook&&(ilook=i+$1))) 705 } 706 707 # 構文木の管理 (_ble_syntax_tree) 708 709 ## @fn ble/syntax/parse/tree-append 710 ## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。 711 function ble/syntax/parse/tree-append { 712 #%if !release 713 [[ $debug_p1 ]] && ble/util/assert '((i-1>=debug_p1))' "Wrong call of tree-append: Condition violation (p1=$debug_p1 i=$i iN=$iN)." 714 #%end 715 local type=$1 716 local beg=$2 end=$i 717 local len=$((end-beg)) 718 ((len==0)) && return 0 719 720 local tchild=$3 tprev=$4 721 722 # 子情報・兄情報 723 local ochild=-1 oprev=-1 724 ((tchild>=0&&(ochild=i-tchild))) 725 ((tprev>=0&&(oprev=i-tprev))) 726 727 [[ $type =~ ^[0-9]+$ ]] && ble/syntax/parse/touch-updated-word "$i" 728 729 # 追加する要素の数は _ble_syntax_TREE_WIDTH と一致している必要がある。 730 _ble_syntax_tree[i-1]="$type $len $ochild $oprev - ${_ble_syntax_tree[i-1]}" 731 } 732 733 function ble/syntax/parse/word-push { 734 wtype=$1 wbegin=$2 tprev=$tchild tchild=-1 735 } 736 ## @fn ble/syntax/parse/word-pop 737 ## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。 738 # 仮定: 1つ上の level は nest-push による level か top level のどちらかである。 739 # この場合に限って ble/syntax/parse/nest-reset-tprev を用いて、tprev 740 # を適切な値に復元することができる。 741 function ble/syntax/parse/word-pop { 742 ble/syntax/parse/tree-append "$wtype" "$wbegin" "$tchild" "$tprev" 743 ((wbegin=-1,wtype=-1,tchild=i)) 744 ble/syntax/parse/nest-reset-tprev 745 } 746 ## '[[' 専用の関数: 747 ## word-push/word-pop と nest-push の順序を反転させる為に。 748 ## 具体的にどう使われているかは使っている箇所を参照すると良い。 749 ## ※本当は [[ が見付かった時点でコマンドとして読み取るのではなく、 750 ## 特別扱いするべきな気もするが、面倒なので今の実装になっている。 751 ## 仮定: 一番最後に設置されたのが単語である事。 752 ## かつ、キャンセルされる単語は今回の解析ステップで設置された物である事。 753 function ble/syntax/parse/word-cancel { 754 local -a word 755 ble/string#split-words word "${_ble_syntax_tree[i-1]}" 756 local wlen=${word[1]} tplen=${word[3]} 757 local wbegin=$((i-wlen)) 758 tchild=$((tplen<0?tplen:i-tplen)) 759 ble/array#fill-range _ble_syntax_tree "$wbegin" "$i" '' 760 } 761 762 # 入れ子構造の管理 763 764 ## @fn ble/syntax/parse/nest-push newctx ntype 765 ## @param[in] newctx 新しい ctx を指定します。 766 ## @param[in,opt] ntype 文法要素の種類を指定します。 767 ## @var [in] i 現在の位置を指定します。 768 ## @var [in out] inest 親 nest の位置を指定します。新しい nest の位置 (i) を返します。 769 ## @var [in,out] ctx 復帰時の ctx を指定します。新しい ctx (newctx) を返します。 770 ## @var [in,out] wbegin 復帰時の wbegin を指定します。新しい wbegin (-1) を返します。 771 ## @var [in,out] wtype 復帰時の wtype を指定します。新しい wtype (-1) を返します。 772 ## @var [in,out] tchild 復帰時の tchild を指定します。新しい tchild (-1) を返します。 773 ## @var [in,out] tprev 復帰時の tprev を指定します。新しい tprev (tchild) を返します。 774 ## @var [in,out] nparam 復帰時の nparam を指定します。新しい nparam (空文字列) を返します。 775 function ble/syntax/parse/nest-push { 776 local wlen=$((wbegin<0?wbegin:i-wbegin)) 777 local nlen=$((inest<0?inest:i-inest)) 778 local tclen=$((tchild<0?tchild:i-tchild)) 779 local tplen=$((tprev<0?tprev:i-tprev)) 780 _ble_syntax_nest[i]="$ctx $wlen $wtype $nlen $tclen $tplen ${nparam:-none} ${2:-none}" 781 ((ctx=$1,inest=i,wbegin=-1,wtype=-1,tprev=tchild,tchild=-1)) 782 nparam= 783 } 784 ## @fn ble/syntax/parse/nest-pop 785 ## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。 786 ## 現在の入れ子を閉じます。現在の入れ子情報を記録して、一つ上の入れ子情報を復元します。 787 ## @var[ out] ctx 上の入れ子階層の ctx を復元します。 788 ## @var[ out] wbegin 上の入れ子階層の wbegin を復元します。 789 ## @var[ out] wtype 上の入れ子階層の wtype を復元します。 790 ## @var[in,out] inest 記録する入れ子情報を指定します。上の入れ子階層の inest を復元します。 791 ## @var[in,out] tchild 記録する入れ子情報を指定します。上の入れ子階層の tchild を復元します。 792 ## @var[in,out] tprev 記録する入れ子情報を指定します。上の入れ子階層の tprev を復元します。 793 ## @var[ out] nparam 上の入れ子階層の nparam を復元します。 794 function ble/syntax/parse/nest-pop { 795 ((inest<0)) && return 1 796 797 local -a parentNest 798 ble/string#split-words parentNest "${_ble_syntax_nest[inest]}" 799 800 local ntype=${parentNest[7]} nbeg=$inest 801 ble/syntax/parse/tree-append "n$ntype" "$nbeg" "$tchild" "$tprev" 802 803 local wlen=${parentNest[1]} nlen=${parentNest[3]} tplen=${parentNest[5]} 804 ((ctx=parentNest[0])) 805 ((wtype=parentNest[2])) 806 ((wbegin=wlen<0?wlen:nbeg-wlen, 807 inest=nlen<0?nlen:nbeg-nlen, 808 tchild=i, 809 tprev=tplen<0?tplen:nbeg-tplen)) 810 nparam=${parentNest[6]} 811 [[ $nparam == none ]] && nparam= 812 } 813 function ble/syntax/parse/nest-type { 814 local _ble_local_var=ntype 815 [[ $1 == -v ]] && _ble_local_var=$2 816 if ((inest<0)); then 817 builtin eval "$_ble_local_var=" 818 return 1 819 else 820 builtin eval "$_ble_local_var=\"\${_ble_syntax_nest[inest]##* }\"" 821 fi 822 } 823 ## @fn ble/syntax/parse/nest-ctx 824 ## @var[out] nctx 825 function ble/syntax/parse/nest-ctx { 826 nctx= 827 ((inest>=0)) || return 1 828 nctx=${_ble_syntax_nest[inest]%% *} 829 } 830 function ble/syntax/parse/nest-reset-tprev { 831 if ((inest<0)); then 832 tprev=-1 833 else 834 local -a nest 835 ble/string#split-words nest "${_ble_syntax_nest[inest]}" 836 local tclen=${nest[4]} 837 ((tprev=tclen<0?tclen:inest-tclen)) 838 fi 839 } 840 ## @fn ble/syntax/parse/nest-equals 841 ## 現在のネスト状態と前回のネスト状態が一致するか判定します。 842 ## @var i1 更新開始点 843 ## @var i2 更新終了点 844 ## @var tail_syntax_stat[i-i2] i2 以降の更新前状態 845 ## @var _ble_syntax_stat[i] 新しい状態 846 function ble/syntax/parse/nest-equals { 847 local parent_inest=$1 848 while :; do 849 ((parent_inest<i1)) && return 0 # 変更していない範囲 または -1 850 ((parent_inest<i2)) && return 1 # 変更によって消えた範囲 851 852 local onest=${tail_syntax_nest[parent_inest-i2]} 853 local nnest=${_ble_syntax_nest[parent_inest]} 854 [[ $onest != "$nnest" ]] && return 1 855 856 ble/string#split-words onest "$onest" 857 #%if !release 858 ble/util/assert \ 859 '((onest[3]!=0&&onest[3]<=parent_inest))' \ 860 "invalid nest onest[3]=${onest[3]} parent_inest=$parent_inest text=$text" || return 0 861 #%end 862 ((parent_inest=onest[3]<0?onest[3]:(parent_inest-onest[3]))) 863 done 864 } 865 866 # 属性値の変更範囲 867 868 ## @var _ble_syntax_attr_umin, _ble_syntax_attr_umax は更新された文法属性の範囲を記録する。 869 ## @var _ble_syntax_word_umin, _ble_syntax_word_umax は更新された単語の先頭位置の範囲を記録する。 870 ## attr については [_ble_syntax_attr_umin, _ble_syntax_attr_umax) が範囲である。 871 ## word については [_ble_syntax_word_umin, _ble_syntax_word_umax] が範囲である。 872 _ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1 873 _ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1 874 _ble_syntax_word_defer_umin=-1 _ble_syntax_word_defer_umax=-1 875 function ble/syntax/parse/touch-updated-attr { 876 ble/syntax/urange#update _ble_syntax_attr_ "$1" "$(($1+1))" 877 } 878 function ble/syntax/parse/touch-updated-word { 879 #%if !release 880 ble/util/assert "(($1>0))" "invalid word position $1" 881 #%end 882 ble/syntax/wrange#update _ble_syntax_word_ "$1" 883 } 884 885 #============================================================================== 886 # 887 # 文脈値 888 # 889 890 # 文脈値達 from lib/core-syntax-ctx.def 891 #%$ sed 's/[[:space:]]*#.*//;/^$/d' lib/core-syntax-ctx.def | awk '$2 ~ /^[0-9]+$/ {print $1 "=" $2;}' 892 893 # for debug 894 _ble_syntax_bash_ctx_names=( 895 #%$ sed 's/[[:space:]]*#.*//;/^$/d' lib/core-syntax-ctx.def | awk '$2 ~ /^[0-9]+$/ {print " [" $2 "]=" $1;}' 896 ) 897 898 ## @fn ble/syntax/ctx#get-name ctx 899 ## @param[in] ctx 900 ## @var[out] ret 901 function ble/syntax/ctx#get-name { 902 ret=${_ble_syntax_bash_ctx_names[$1]#_ble_ctx_} 903 } 904 905 # @var _ble_syntax_context_proc[] 906 # @var _ble_syntax_context_end[] 907 # 以上の二つの配列を通して文法要素は最終的に登録される。 908 # (逆に言えば上の二つの配列を弄れば別の文法の解析を実行する事もできる) 909 _ble_syntax_context_proc=() 910 _ble_syntax_context_end=() 911 912 #============================================================================== 913 # 914 # 空文法 915 # 916 #------------------------------------------------------------------------------ 917 918 function ble/syntax:text/ctx-unspecified { 919 ((i+=${#tail})) 920 return 0 921 } 922 _ble_syntax_context_proc[CTX_UNSPECIFIED]=ble/syntax:text/ctx-unspecified 923 924 function ble/syntax:text/initialize-ctx { ctx=$CTX_UNSPECIFIED; } 925 function ble/syntax:text/initialize-vars { :; } 926 927 #============================================================================== 928 # 929 # Bash Script 文法 930 # 931 #------------------------------------------------------------------------------ 932 933 _ble_syntax_bash_RexSpaces=$'[ \t]+' 934 _ble_syntax_bash_RexIFSs="[$_ble_term_IFS]+" 935 _ble_syntax_bash_RexDelimiter="[$_ble_term_IFS;|&<>()]" 936 _ble_syntax_bash_RexRedirect='((\{[_a-zA-Z][_a-zA-Z0-9]*\}|[0-9]+)?(&?>>?|>[|&]|<[>&]?|<<[-<]?))[ ]*' 937 938 ## @var _ble_syntax_bash_chars[] 939 ## 特定の役割を持つ文字の集合。Bracket expression [~] に入れて使う為の物。 940 ## histchars に依存しているので変化があった時に更新する。 941 _ble_syntax_bash_chars=() 942 _ble_syntax_bashc_seed= 943 944 function ble/syntax:bash/cclass/update/reorder { 945 builtin eval "local a=\"\${$1}\"" 946 947 # Bracket expression として安全な順に並び替える 948 [[ $a == *']'* ]] && a="]${a//]}" 949 [[ $a == *'-'* ]] && a="${a//-}-" 950 951 builtin eval "$1=\$a" 952 } 953 954 ## @fn ble/syntax:bash/cclass/update 955 ## 956 ## @var[in] _ble_syntax_bash_histc12 957 ## @var[in,out] _ble_syntax_bashc_seed 958 ## @var[in,out] _ble_syntax_bash_chars[] 959 ## 960 ## @exit 更新があった時に正常終了します。 961 ## 更新の必要がなかった時に 1 を返します。 962 ## 963 function ble/syntax:bash/cclass/update { 964 local seed=$_ble_syntax_bash_histc12 965 shopt -q extglob && seed=${seed}x 966 [[ $seed == "$_ble_syntax_bashc_seed" ]] && return 1 967 _ble_syntax_bashc_seed=$seed 968 969 local key modified= 970 if [[ $_ble_syntax_bash_histc12 == '!^' ]]; then 971 for key in "${!_ble_syntax_bash_charsDef[@]}"; do 972 _ble_syntax_bash_chars[key]=${_ble_syntax_bash_charsDef[key]} 973 done 974 _ble_syntax_bashc_simple=$_ble_syntax_bash_chars_simpleDef 975 else 976 modified=1 977 978 local histc1=${_ble_syntax_bash_histc12:0:1} 979 local histc2=${_ble_syntax_bash_histc12:1:1} 980 for key in "${!_ble_syntax_bash_charsFmt[@]}"; do 981 local a=${_ble_syntax_bash_charsFmt[key]} 982 a=${a//@h/"$histc1"} 983 a=${a//@q/"$histc2"} 984 _ble_syntax_bash_chars[key]=$a 985 done 986 987 local a=$_ble_syntax_bash_chars_simpleFmt 988 a=${a//@h/"$histc1"} 989 a=${a//@q/"$histc2"} 990 _ble_syntax_bashc_simple=$a 991 fi 992 993 if [[ $seed == *x ]]; then 994 # extglob: ?() *() +() @() !() 995 local extglob='@+!' # *? は既に登録されている筈 996 _ble_syntax_bash_chars[CTX_ARGI]=${_ble_syntax_bash_chars[CTX_ARGI]}$extglob 997 _ble_syntax_bash_chars[CTX_PATN]=${_ble_syntax_bash_chars[CTX_PATN]}$extglob 998 _ble_syntax_bash_chars[CTX_PWORD]=${_ble_syntax_bash_chars[CTX_PWORD]}$extglob 999 _ble_syntax_bash_chars[CTX_PWORDE]=${_ble_syntax_bash_chars[CTX_PWORDE]}$extglob 1000 _ble_syntax_bash_chars[CTX_PWORDR]=${_ble_syntax_bash_chars[CTX_PWORDR]}$extglob 1001 fi 1002 1003 if [[ $modified ]]; then 1004 for key in "${!_ble_syntax_bash_chars[@]}"; do 1005 ble/syntax:bash/cclass/update/reorder _ble_syntax_bash_chars[key] 1006 done 1007 ble/syntax:bash/cclass/update/reorder _ble_syntax_bashc_simple 1008 fi 1009 return 0 1010 } 1011 1012 _ble_syntax_bash_charsDef=() 1013 _ble_syntax_bash_charsFmt=() 1014 _ble_syntax_bash_chars_simpleDef= 1015 _ble_syntax_bash_chars_simpleFmt= 1016 function ble/syntax:bash/cclass/initialize { 1017 local delimiters="$_ble_term_IFS;|&()<>" 1018 local expansions="\$\"\`\\'" 1019 local glob='[*?' 1020 local tilde='~:' 1021 1022 # _ble_syntax_bash_chars[CTX_ARGI] は以下で使われている 1023 # ctx-command (色々) 1024 # ctx-redirect (CTX_RDRF, CTX_RDRD, CTX_RDRD2, CTX_RDRS) 1025 # ctx-values (CTX_VALI, CTX_VALR, CTX_VALQ) 1026 # ctx-conditions (CTX_CONDI, CTX_CONDQ) 1027 # 更に以下でも使われている 1028 # ctx-bracket-expression 1029 # ctx-brace-expansion 1030 # check-tilde-expansion 1031 1032 # default values 1033 _ble_syntax_bash_charsDef[CTX_ARGI]="$delimiters$expansions$glob{$tilde^!" 1034 _ble_syntax_bash_charsDef[CTX_PATN]="$expansions$glob(|)<>{!" # <> はプロセス置換のため。 1035 _ble_syntax_bash_charsDef[CTX_QUOT]="\$\"\`\\!" # 文字列 "~" で特別な意味を持つのは $ ` \ " のみ。+履歴展開の ! も。 1036 _ble_syntax_bash_charsDef[CTX_EXPR]="][}()$expansions!" # ()[] は入れ子を数える為。} は ${var:ofs:len} の為。 1037 _ble_syntax_bash_charsDef[CTX_PWORD]="}$expansions$glob!" # パラメータ展開 ${~} 1038 _ble_syntax_bash_charsDef[CTX_PWORDE]="}$expansions$glob!" # パラメータ展開 ${~} エラー 1039 _ble_syntax_bash_charsDef[CTX_PWORDR]="}/$expansions$glob!" # パラメータ展開 ${~} 置換前 1040 _ble_syntax_bash_charsDef[CTX_RDRH]="$delimiters$expansions" 1041 _ble_syntax_bash_charsDef[CTX_HERE1]="\\\$\`$_ble_term_nl!" 1042 1043 # templates 1044 _ble_syntax_bash_charsFmt[CTX_ARGI]="$delimiters$expansions$glob{$tilde@q@h" 1045 _ble_syntax_bash_charsFmt[CTX_PATN]="$expansions$glob(|)<>{@h" 1046 _ble_syntax_bash_charsFmt[CTX_QUOT]="\$\"\`\\@h" 1047 _ble_syntax_bash_charsFmt[CTX_EXPR]="][}()$expansions@h" 1048 _ble_syntax_bash_charsFmt[CTX_PWORD]="}$expansions$glob@h" 1049 _ble_syntax_bash_charsFmt[CTX_PWORDE]="}$expansions$glob@h" 1050 _ble_syntax_bash_charsFmt[CTX_PWORDR]="}/$expansions$glob@h" 1051 _ble_syntax_bash_charsFmt[CTX_RDRH]=${_ble_syntax_bash_charsDef[CTX_RDRH]} 1052 _ble_syntax_bash_charsFmt[CTX_HERE1]="\\\$\`$_ble_term_nl@h" 1053 1054 _ble_syntax_bash_chars_simpleDef="$delimiters$expansions^!" 1055 _ble_syntax_bash_chars_simpleFmt="$delimiters$expansions@q@h" 1056 1057 _ble_syntax_bash_histc12='!^' 1058 ble/syntax:bash/cclass/update 1059 } 1060 1061 ble/syntax:bash/cclass/initialize 1062 1063 #------------------------------------------------------------------------------ 1064 # ble/syntax:bash/simple-word 1065 1066 ## @var _ble_syntax_bash_simple_rex_word 1067 ## @var _ble_syntax_bash_simple_rex_element 1068 ## 単純な単語のパターンとその構成要素を表す正規表現 1069 ## histchars に依存しているので変化があった時に更新する。 1070 _ble_syntax_bash_simple_rex_letter= 1071 _ble_syntax_bash_simple_rex_param= 1072 _ble_syntax_bash_simple_rex_bquot= 1073 _ble_syntax_bash_simple_rex_squot= 1074 _ble_syntax_bash_simple_rex_dquot= 1075 _ble_syntax_bash_simple_rex_literal= 1076 _ble_syntax_bash_simple_rex_element= 1077 _ble_syntax_bash_simple_rex_word= 1078 _ble_syntax_bash_simple_rex_open_word= 1079 _ble_syntax_bash_simple_rex_open_dquot= 1080 _ble_syntax_bash_simple_rex_open_squot= 1081 _ble_syntax_bash_simple_rex_incomplete_word1= 1082 _ble_syntax_bash_simple_rex_incomplete_word2= 1083 1084 _ble_syntax_bash_simple_rex_noglob_word1= 1085 _ble_syntax_bash_simple_rex_noglob_word2= 1086 1087 function ble/syntax:bash/simple-word/update { 1088 local q="'" 1089 1090 local letter='\[[!^]|[^'${_ble_syntax_bashc_simple}']' 1091 local param1='\$([-*@#?$!0_]|[1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)' 1092 local param2='\$\{(#?[-*@#?$!0]|[#!]?([1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*))\}' # ${!!} ${!$} はエラーになる。履歴展開の所為? 1093 local param=$param1'|'$param2 1094 local bquot='\\.' 1095 local squot=$q'[^'$q']*'$q'|\$'$q'([^'$q'\]|\\.)*'$q 1096 local dquot='\$?"([^'${_ble_syntax_bash_chars[CTX_QUOT]}']|\\.|'$param')*"' 1097 _ble_syntax_bash_simple_rex_letter=$letter # 0 groups 1098 _ble_syntax_bash_simple_rex_param=$param # 3 groups 1099 _ble_syntax_bash_simple_rex_bquot=$bquot # 0 groups 1100 _ble_syntax_bash_simple_rex_squot=$squot # 1 groups 1101 _ble_syntax_bash_simple_rex_dquot=$dquot # 4 groups 1102 1103 # @var _ble_syntax_bash_simple_rex_element 1104 # @var _ble_syntax_bash_simple_rex_word 1105 _ble_syntax_bash_simple_rex_literal='^('$letter')+$' 1106 _ble_syntax_bash_simple_rex_element='('$bquot'|'$squot'|'$dquot'|'$param'|'$letter')' 1107 _ble_syntax_bash_simple_rex_word='^'$_ble_syntax_bash_simple_rex_element'+$' 1108 1109 # @var _ble_syntax_bash_simple_rex_open_word 1110 local open_squot=$q'[^'$q']*|\$'$q'([^'$q'\]|\\.)*' 1111 local open_dquot='\$?"([^'${_ble_syntax_bash_chars[CTX_QUOT]}']|\\.|'$param')*' 1112 _ble_syntax_bash_simple_rex_open_word='^('$_ble_syntax_bash_simple_rex_element'*)(\\|'$open_squot'|'$open_dquot')$' 1113 _ble_syntax_bash_simple_rex_open_squot=$open_squot 1114 _ble_syntax_bash_simple_rex_open_dquot=$open_dquot 1115 1116 # @var _ble_syntax_bash_simple_rex_incomplete_word1 1117 # @var _ble_syntax_bash_simple_rex_incomplete_word2 1118 local letter1='\[[!^]|[^{'${_ble_syntax_bashc_simple}']' 1119 local letter2='\[[!^]|[^'${_ble_syntax_bashc_simple}']' 1120 _ble_syntax_bash_simple_rex_incomplete_word1='^('$bquot'|'$squot'|'$dquot'|'$param'|'$letter1')+' 1121 _ble_syntax_bash_simple_rex_incomplete_word2='^(('$bquot'|'$squot'|'$dquot'|'$param'|'$letter2')*)(\\|'$open_squot'|'$open_dquot')?$' 1122 1123 # @var _ble_syntax_bash_simple_rex_noglob_word{1,2} 1124 local noglob_letter='[^[?*'${_ble_syntax_bashc_simple}']' 1125 _ble_syntax_bash_simple_rex_noglob_word1='^('$bquot'|'$squot'|'$dquot'|'$noglob_letter')+$' 1126 _ble_syntax_bash_simple_rex_noglob_word2='^('$bquot'|'$squot'|'$dquot'|'$param'|'$noglob_letter')+$' 1127 } 1128 ble/syntax:bash/simple-word/update 1129 1130 function ble/syntax:bash/simple-word/is-literal { 1131 [[ $1 =~ $_ble_syntax_bash_simple_rex_literal ]] 1132 } 1133 function ble/syntax:bash/simple-word/is-simple { 1134 [[ $1 =~ $_ble_syntax_bash_simple_rex_word ]] 1135 } 1136 function ble/syntax:bash/simple-word/is-simple-or-open-simple { 1137 [[ $1 =~ $_ble_syntax_bash_simple_rex_word || $1 =~ $_ble_syntax_bash_simple_rex_open_word ]] 1138 } 1139 function ble/syntax:bash/simple-word/is-never-word { 1140 ble/syntax:bash/simple-word/is-simple-or-open-simple && return 1 1141 local rex=${_ble_syntax_bash_simple_rex_word%'$'}'[ |&;<>()]|^[ |&;<>()]' 1142 [[ $1 =~ $rex ]] 1143 } 1144 function ble/syntax:bash/simple-word/is-simple-noglob { 1145 [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word1 ]] && return 0 1146 if [[ $1 =~ $_ble_syntax_bash_simple_rex_noglob_word2 ]]; then 1147 builtin eval -- "local expanded=$1" 2>/dev/null 1148 local rex='[*?]|\[.+\]|[*?@+!]\(.*\)' 1149 [[ $expanded =~ $rex ]] || return 0 1150 fi 1151 return 1 1152 } 1153 1154 ## @fn ble/syntax:bash/simple-word/evaluate-last-brace-expansion simple_word 1155 ## @param[in] simple_word 1156 ## @var[out] ret simple_ibrace 1157 function ble/syntax:bash/simple-word/evaluate-last-brace-expansion { 1158 local value=$1 1159 local bquot=$_ble_syntax_bash_simple_rex_bquot 1160 local squot=$_ble_syntax_bash_simple_rex_squot 1161 local dquot=$_ble_syntax_bash_simple_rex_dquot 1162 local param=$_ble_syntax_bash_simple_rex_param 1163 local letter='\[[!^]|[^{,}'${_ble_syntax_bashc_simple}']' 1164 local symbol='[{,}]' 1165 1166 local rex_range_expansion='^(([-+]?[0-9]+)\.\.\.[-+]?[0-9]+|([a-zA-Z])\.\.[a-zA-Z])(\.\.[-+]?[0-9]+)?$' 1167 1168 local rex0='^('$bquot'|'$squot'|'$dquot'|'$param'|'$letter')+' 1169 local stack; stack=() 1170 local out= comma= index=0 iopen=0 no_brace_length=0 1171 while [[ $value ]]; do 1172 if [[ $value =~ $rex0 ]]; then 1173 local len=${#BASH_REMATCH} 1174 ((index+=len,no_brace_length+=len)) 1175 out=$out${value::len} 1176 value=${value:len} 1177 elif [[ $value == '{'* ]]; then 1178 ((iopen=++index,no_brace_length=0)) 1179 value=${value:1} 1180 ble/array#push stack "$comma:$out" 1181 out= comma= 1182 elif ((${#stack[@]})) && [[ $value == '}'* ]]; then 1183 ((++index)) 1184 value=${value:1} 1185 ble/array#pop stack 1186 local out0=${ret#*:} comma0=${ret%%:*} 1187 if [[ $comma ]]; then 1188 ((iopen=index,no_brace_length=0)) 1189 out=$out0$out 1190 comma=$comma0 1191 elif [[ $out =~ $rex_range_expansion ]]; then 1192 ((iopen=index,no_brace_length=0)) 1193 out=$out0${2#+}$3 1194 comma=$comma0 1195 else 1196 ((++no_brace_length)) 1197 ble/array#push stack "$comma0:$out0" # cancel pop 1198 out=$out'}' 1199 fi 1200 elif ((${#stack[@]})) && [[ $value == ','* ]]; then 1201 ((iopen=++index,no_brace_length=0)) 1202 value=${value:1} 1203 out= comma=1 1204 else 1205 ((++index,++no_brace_length)) 1206 out=$out${value::1} 1207 value=${value:1} 1208 fi 1209 done 1210 1211 while ((${#stack[@]})); do 1212 ble/array#pop stack 1213 local out0=${ret#*:} comma0=${ret%%:*} 1214 out=$out0$out 1215 done 1216 1217 ret=$out simple_ibrace=$iopen:$((${#out}-no_brace_length)) 1218 } 1219 1220 ## @fn ble/syntax:bash/simple-word/reconstruct-incomplete-word 1221 ## word について不完全なブレース展開と不完全な引用符を閉じ、 1222 ## 更にブレース展開を実行して最後の単語を取得します。 1223 ## 1224 ## @param[in] word 1225 ## 不完全な単語を指定します。 1226 ## 1227 ## @var[out] ret 1228 ## word に対して不完全なブレース展開と引用符を閉じ、ブレース展開した結果を返します。 1229 ## 1230 ## @var[out] simple_flags 1231 ## 引用符 $"..." を閉じた時に simple_flags=I を設定します。 1232 ## 引用符 "..." を閉じた時に simple_flags=D を設定します。 1233 ## 引用符 $'...' を閉じた時に simple_flags=E を設定します。 1234 ## 引用符 '...' を閉じた時に simple_flags=S を設定します。 1235 ## 不完全な末端 \ があった時に simple_flags=B を設定します。 1236 ## 1237 ## @var[out] simple_ibrace=ibrace:jbrace 1238 ## ブレース展開の構造を破壊せずに変更できる最初の位置を返します。 1239 ## ibrace には word 内の位置を返し、jbrace には ret 内の位置を返します。 1240 ## 1241 ## @exit 1242 ## ブレース展開及び引用符を閉じることによってシェルの完全な単語になる時に成功します。 1243 ## それ以外の場合に失敗します。 1244 ## 1245 function ble/syntax:bash/simple-word/reconstruct-incomplete-word { 1246 local word=$1 1247 ret= simple_flags= simple_ibrace=0:0 1248 1249 [[ $word ]] || return 0 1250 1251 if [[ $word =~ $_ble_syntax_bash_simple_rex_incomplete_word1 ]]; then 1252 ret=${word::${#BASH_REMATCH}} 1253 word=${word:${#BASH_REMATCH}} 1254 [[ $word ]] || return 0 1255 fi 1256 1257 if [[ $word =~ $_ble_syntax_bash_simple_rex_incomplete_word2 ]]; then 1258 local out=$ret 1259 1260 local m_brace=${BASH_REMATCH[1]} 1261 local m_quote=${word:${#m_brace}} 1262 1263 if [[ $m_brace ]]; then 1264 ble/syntax:bash/simple-word/evaluate-last-brace-expansion "$m_brace" 1265 simple_ibrace=$((${#out}+${simple_ibrace%:*})):$((${#out}+${simple_ibrace#*:})) 1266 out=$out$ret 1267 fi 1268 1269 if [[ $m_quote ]]; then 1270 case $m_quote in 1271 ('$"'*) out=$out$m_quote\" simple_flags=I ;; 1272 ('"'*) out=$out$m_quote\" simple_flags=D ;; 1273 ("$'"*) out=$out$m_quote\' simple_flags=E ;; 1274 ("'"*) out=$out$m_quote\' simple_flags=S ;; 1275 ('\') simple_flags=B ;; 1276 (*) return 1 ;; 1277 esac 1278 fi 1279 1280 ret=$out 1281 return 0 1282 fi 1283 1284 return 1 1285 } 1286 1287 ## @fn ble/syntax:bash/simple-word/extract-parameter-names word 1288 ## 単純単語に含まれるパラメータ展開のパラメータ名を抽出します。 1289 ## @var[in] word 1290 ## @var[out] ret 1291 function ble/syntax:bash/simple-word/extract-parameter-names { 1292 ret=() 1293 local letter=$_ble_syntax_bash_simple_rex_letter 1294 local bquot=$_ble_syntax_bash_simple_rex_bquot 1295 local squot=$_ble_syntax_bash_simple_rex_squot 1296 local dquot=$_ble_syntax_bash_simple_rex_dquot 1297 local param=$_ble_syntax_bash_simple_rex_param 1298 1299 local value=$1 1300 local rex0='^('$letter'|'$bquot'|'$squot')+' 1301 local rex1='^('$dquot')' 1302 local rex2='^('$param')' 1303 while [[ $value ]]; do 1304 [[ $value =~ $rex0 ]] && value=${value:${#BASH_REMATCH}} 1305 if [[ $value =~ $rex1 ]]; then 1306 value=${value:${#BASH_REMATCH}} 1307 ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot "$BASH_REMATCH" 1308 fi 1309 [[ $value =~ $rex2 ]] || break 1310 value=${value:${#BASH_REMATCH}} 1311 local var=${BASH_REMATCH[2]}${BASH_REMATCH[3]} 1312 [[ $var == [_a-zA-Z]* ]] && ble/array#push ret "$var" 1313 done 1314 } 1315 ## @fn ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot match 1316 ## @var[in,out] ret 1317 function ble/syntax:bash/simple-word/extract-parameter-names/.process-dquot { 1318 local value=$1 1319 if [[ $value == '$"'*'"' ]]; then 1320 value=${value:2:${#value}-3} 1321 elif [[ $value == '"'*'"' ]]; then 1322 value=${value:1:${#value}-2} 1323 else 1324 return 0 1325 fi 1326 1327 local rex0='^([^'${_ble_syntax_bash_chars[CTX_QUOT]}']|\\.)+' 1328 local rex2='^('$param')' 1329 while [[ $value ]]; do 1330 [[ $value =~ $rex0 ]] && value=${value:${#BASH_REMATCH}} 1331 [[ $value =~ $rex2 ]] || break 1332 value=${value:${#BASH_REMATCH}} 1333 local var=${BASH_REMATCH[2]}${BASH_REMATCH[3]} 1334 [[ $var == [_a-zA-Z]* ]] && ble/array#push ret "$var" 1335 done 1336 } 1337 1338 function ble/syntax:bash/simple-word/eval/.set-result { __ble_ret=("$@"); } 1339 function ble/syntax:bash/simple-word/eval/.print-result { 1340 if (($#>=1000)) && [[ $OSTYPE != cygwin ]]; then 1341 # ファイル数が少ない場合は fork コストを避ける為に多少遅くても quote&eval 1342 # でデータを親シェルに転送する。Cygwin では mapfile/read が unbuffered で遅 1343 # いので、ファイル数が遅くても quote&eval を使う。 1344 1345 if ((_ble_bash>=50200)); then 1346 printf '%s\0' "$@" >| "$__ble_simple_word_tmpfile" 1347 ble/util/print 'ble/util/readarray -d "" __ble_ret < "$__ble_simple_word_tmpfile"' 1348 return 0 1349 elif ((_ble_bash>=40000)); then 1350 ret=("$@") 1351 ble/util/writearray --nlfix ret >| "$__ble_simple_word_tmpfile" 1352 ble/util/print 'ble/util/readarray --nlfix __ble_ret < "$__ble_simple_word_tmpfile"' 1353 return 0 1354 fi 1355 fi 1356 1357 local ret; ble/string#quote-words "$@" 1358 ble/util/print "__ble_ret=($ret)" 1359 } 1360 ## @fn ble/syntax:bash/simple-word/eval/.impl word opts 1361 ## @param[in] word 1362 ## @param[in,opt] opts 1363 ## @var[out] __ble_ret 1364 function ble/syntax:bash/simple-word/eval/.impl { 1365 local __ble_word=$1 __ble_opts=$2 __ble_flags= 1366 1367 # グローバル変数の復元 1368 local -a ret=() 1369 ble/syntax:bash/simple-word/extract-parameter-names "$__ble_word" 1370 if ((${#ret[@]})); then 1371 local __ble_defs 1372 ble/util/assign __ble_defs 'ble/util/print-global-definitions --hidden-only "${ret[@]}"' 1373 builtin eval -- "$__ble_defs" &>/dev/null # 読み取り専用の変数のこともある 1374 fi 1375 1376 # glob パターンを含む可能性がある時の処理 (Note: is-simple-noglob の 1377 # 判定で変数を参照するので、グローバル変数の復元よりも後で処理する必 1378 # 要がある) 1379 if [[ $- != *f* ]] && ! ble/syntax:bash/simple-word/is-simple-noglob "$1"; then 1380 if [[ :$__ble_opts: == *:noglob:* ]]; then 1381 set -f 1382 __ble_flags=f 1383 elif ble/util/is-cygwin-slow-glob "$1"; then # Note: #D1168 1384 if shopt -q failglob &>/dev/null; then 1385 __ble_ret=() 1386 return 1 1387 elif shopt -q nullglob &>/dev/null; then 1388 __ble_ret=() 1389 return 0 1390 else 1391 # noglob で処理する 1392 set -f 1393 __ble_flags=f 1394 fi 1395 elif [[ :$__ble_opts: == *:stopcheck:* ]]; then 1396 ble/decode/has-input && return 148 1397 if ((_ble_bash>=40000)); then 1398 __ble_flags=s 1399 elif shopt -q globstar &>/dev/null; then 1400 # conditional-sync が使えない場合はせめて globstar だけでも撥ねる 1401 if builtin eval "[[ $__ble_word == *'**'* ]]"; then 1402 [[ :$__ble_opts: == *:timeout=*:* ]] && return 142 1403 return 148 1404 fi 1405 fi 1406 fi 1407 fi 1408 1409 # Note: failglob 時に一致がないと実行されないので予め __ble_ret=() をする。 1410 # また、エラーメッセージが生じるので /dev/null に繋ぐ。 1411 __ble_ret=() 1412 if [[ $__ble_flags == *s* ]]; then 1413 local __ble_sync_command="ble/syntax:bash/simple-word/eval/.print-result $__ble_word" 1414 local __ble_sync_opts=progressive-weight 1415 local __ble_sync_weight=$bleopt_syntax_eval_polling_interval 1416 1417 # determine timeout 1418 local __ble_sync_timeout=$_ble_syntax_bash_simple_eval_timeout 1419 if [[ $_ble_syntax_bash_simple_eval_timeout_carry ]]; then 1420 __ble_sync_timeout=0 1421 elif local __ble_rex=':timeout=([^:]*):'; [[ :$__ble_opts: =~ $__ble_rex ]]; then 1422 __ble_sync_timeout=${BASH_REMATCH[1]} 1423 fi 1424 [[ $__ble_sync_timeout ]] && 1425 __ble_sync_opts=$__ble_sync_opts:timeout=$((__ble_sync_timeout)) 1426 1427 local _ble_local_tmpfile; ble/util/assign/mktmp 1428 local __ble_simple_word_tmpfile=$_ble_local_tmpfile 1429 local __ble_script 1430 ble/util/assign __ble_script 'ble/util/conditional-sync "$__ble_sync_command" "" "$__ble_sync_weight" "$__ble_sync_opts"' &>/dev/null; local ext=$? 1431 builtin eval -- "$__ble_script" 1432 ble/util/assign/rmtmp 1433 else 1434 builtin eval "ble/syntax:bash/simple-word/eval/.set-result $__ble_word" &>/dev/null; local ext=$? 1435 builtin eval : # Note: bash 3.1/3.2 eval バグ対策 (#D1132) 1436 fi 1437 1438 [[ $__ble_flags == *f* ]] && set +f 1439 return "$ext" 1440 } 1441 1442 _ble_syntax_bash_simple_eval_hash= 1443 ## @fn ble/syntax:bash/simple-word/eval/.cache-clear 1444 ## @var[in,out] _ble_syntax_bash_simple_eval 1445 function ble/syntax:bash/simple-word/eval/.cache-clear { 1446 ble/gdict#clear _ble_syntax_bash_simple_eval 1447 ble/gdict#clear _ble_syntax_bash_simple_eval_full 1448 } 1449 ## @fn ble/syntax:bash/simple-word/eval/.cache-update 1450 ## @var[in,out] _ble_syntax_bash_simple_eval 1451 ## @var[in,out] _ble_syntax_bash_simple_eval_hash 1452 function ble/syntax:bash/simple-word/eval/.cache-update { 1453 local hash=$-:$BASHOPTS:$_ble_edit_lineno:$_ble_textarea_version:$PWD 1454 if [[ $hash != "$_ble_syntax_bash_simple_eval_hash" ]]; then 1455 _ble_syntax_bash_simple_eval_hash=$hash 1456 ble/syntax:bash/simple-word/eval/.cache-clear 1457 fi 1458 } 1459 ## @fn ble/syntax:bash/simple-word/eval/.save word ext ret... 1460 ## @var[in] ext 1461 ## @var[in,out] _ble_syntax_bash_simple_eval 1462 function ble/syntax:bash/simple-word/eval/.cache-save { 1463 ((ext==148||ext==142)) && return 0 1464 local ret; ble/string#quote-words "$3" 1465 ble/gdict#set _ble_syntax_bash_simple_eval "$1" "ext=$2 count=$(($#-2)) ret=$ret" 1466 local ret; ble/string#quote-words "${@:3}" 1467 ble/gdict#set _ble_syntax_bash_simple_eval_full "$1" "ext=$2 count=$(($#-2)) ret=($ret)" 1468 } 1469 ## @fn ble/syntax:bash/simple-word/eval/.cache-load word opts 1470 function ble/syntax:bash/simple-word/eval/.cache-load { 1471 ext= ret= 1472 if [[ :$2: == *:single:* ]]; then 1473 ble/gdict#get _ble_syntax_bash_simple_eval "$1" || return 1 1474 else 1475 ble/gdict#get _ble_syntax_bash_simple_eval_full "$1" || return 1 1476 fi 1477 builtin eval -- "$ret" 1478 return 0 1479 } 1480 1481 ## @fn ble/syntax:bash/simple-word/eval word opts 1482 ## @param[in] word 1483 ## @param[in,opt] opts 1484 ## コロン区切りのオプション指定です。 1485 ## 1486 ## noglob 1487 ## パス名展開を抑制します。 1488 ## 1489 ## 以下は (成功時の) 評価結果に影響を与えないオプションです。 1490 ## 1491 ## single 1492 ## 最初の展開結果のみを ret に設定します。 1493 ## count 1494 ## 変数 count に展開結果の単語数を返します。 1495 ## cached 1496 ## 展開結果をキャッシュします。 1497 ## 1498 ## 以下はパス名展開の起こる可能性にある単語に対して有効です。 1499 ## 1500 ## stopcheck 1501 ## ユーザー入力があった場合に中断します。 1502 ## timeout=NUM 1503 ## stopcheck を指定している時に有効です。timeout を指定します。 1504 ## retry-noglob-on-timeout 1505 ## timeout した時に noglob で改めて展開を試行します。 1506 ## timeout-carry 1507 ## timeout が発生した場合に後続の eval にタイムアウトを伝播します。 1508 ## 1509 ## @var[in] _ble_syntax_bash_simple_eval_timeout 1510 ## パス名展開のタイムアウトの既定値を指定します。空文字列が指定さ 1511 ## れている時、既定でタイムアウトは起こりません。 1512 ## @var[in,out] _ble_syntax_bash_simple_eval_timeout_carry 1513 ## この値が設定されている時、パス名展開に対して強制的にタイムアウ 1514 ## トが起こります。opts に timeout-carry が指定されている時に値が設定されます。 1515 ## 1516 ## @arr[out] ret 1517 ## 展開結果を返します。複数の単語に評価される場合にはそれを全て返します。 1518 ## opts に single が指定されている時、最初の展開結果のみを返します。 1519 ## @var[out] count 1520 ## opts に count が指定されている時に展開結果の数を返します。 1521 ## 1522 ## @exit 1523 ## ユーザー入力により中断した場合は 148 を返します。timeout を起こ 1524 ## した場合 142 を返します。例えば failglob など、その他の理由でパ 1525 ## ス名展開に失敗した時 0 以外の終了ステータスを返します。 1526 ## 1527 _ble_syntax_bash_simple_eval_timeout= 1528 _ble_syntax_bash_simple_eval_timeout_carry= 1529 function ble/syntax:bash/simple-word/eval { 1530 [[ :$2: != *:count:* ]] && local count 1531 if [[ :$2: == *:cached:* && :$2: != *:noglob:* ]]; then 1532 ble/syntax:bash/simple-word/eval/.cache-update 1533 local ext; ble/syntax:bash/simple-word/eval/.cache-load "$1" "$2" && return "$ext" 1534 fi 1535 1536 local __ble_ret 1537 ble/syntax:bash/simple-word/eval/.impl "$1" "$2"; local ext=$? 1538 ret=("${__ble_ret[@]}") 1539 count=${#ret[@]} 1540 1541 if [[ :$2: == *:cached:* && :$2: != *:noglob:* ]]; then 1542 ble/syntax:bash/simple-word/eval/.cache-save "$1" "$ext" "${ret[@]}" 1543 fi 1544 if ((ext==142)); then 1545 [[ :$2: == *:timeout-carry:* ]] && 1546 _ble_syntax_bash_simple_eval_timeout_carry=1 1547 if [[ :$2: == *:retry-noglob-on-timeout:* ]]; then 1548 ble/syntax:bash/simple-word/eval "$1" "$2:noglob" 1549 return "$?" 1550 fi 1551 fi 1552 return "$ext" 1553 } 1554 1555 ## @fn ble/syntax:bash/simple-word/get-rex_element sep 1556 ## 指定した分割文字 (sep) で区切られた単純単語片に一致する正規表現を構築します。 1557 ## @var[out] rex_element 1558 function ble/syntax:bash/simple-word/get-rex_element { 1559 local sep=$1 1560 local param=$_ble_syntax_bash_simple_rex_param 1561 local bquot=$_ble_syntax_bash_simple_rex_bquot 1562 local squot=$_ble_syntax_bash_simple_rex_squot 1563 local dquot=$_ble_syntax_bash_simple_rex_dquot 1564 local letter1='\[[!^]|[^'$sep$_ble_syntax_bashc_simple']' 1565 rex_element='('$bquot'|'$squot'|'$dquot'|'$param'|'$letter1')+' 1566 } 1567 ## @fn ble/syntax:bash/simple-word/evaluate-path-spec path_spec [sep] [opts] 1568 ## @param[in] path_spec 1569 ## @param[in,opt] sep (default: '/:=') 1570 ## @param[in,opt] opts 1571 ## noglob 1572 ## stopcheck 1573 ## timeout=* 1574 ## timeout-carry 1575 ## cached 1576 ## これらは simple-word/eval に対するオプションです。 1577 ## 1578 ## notilde 1579 ## 評価時にチルダ展開を抑制します。 1580 ## after-sep 1581 ## 分割位置を分割子の前ではなく後に変更します。 1582 ## fixlen=LEN 1583 ## 分割の対象とならない固定接頭辞の長さを指定します。 1584 ## 1585 ## @arr[out] spec 1586 ## @arr[out] path 1587 ## @arr[out] ret 1588 ## path_spec 全体を評価した時の結果を返します。 1589 ## パス名展開によって複数のパスに展開された場合に、 1590 ## 全ての展開結果を含む配列になります。 1591 ## 1592 ## 指定した path_spec を sep に含まれる文字で区切ってルートから末端まで順に評価します。 1593 ## 各階層までの評価の対象を spec に評価の結果を path に追加します。 1594 ## 例えば path_spec='~/a/b' の時、 1595 ## spec=(~ ~/a ~/a/b) 1596 ## path=(/home/user /home/user/a /home/user/a/b) 1597 ## という結果が得られます。 1598 ## 1599 function ble/syntax:bash/simple-word/evaluate-path-spec { 1600 local word=$1 sep=${2:-'/:='} opts=$3 1601 ret=() spec=() path=() 1602 [[ $word ]] || return 0 1603 1604 # read options 1605 local eval_opts=$opts notilde= 1606 [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制 1607 local fixlen 1608 ble/opts#extract-last-optarg "$opts" fixlen 0 1609 1610 # compose regular expressions 1611 local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep" 1612 local rex='^['$sep']?'$rex_element'|^['$sep']' 1613 [[ :$opts: == *:after-sep:* ]] && 1614 local rex='^'$rex_element'['$sep']?|^['$sep']' 1615 1616 local tail=${word:fixlen} s=${word::fixlen} p= ext=0 1617 while [[ $tail =~ $rex ]]; do 1618 local rematch=$BASH_REMATCH 1619 s=$s$rematch 1620 ble/syntax:bash/simple-word/eval "$notilde$s" "$eval_opts"; ext=$? 1621 ((ext==148||ext==142)) && return "$ext" 1622 p=$ret 1623 tail=${tail:${#rematch}} 1624 ble/array#push spec "$s" 1625 ble/array#push path "$p" 1626 done 1627 [[ $tail ]] && return 1 1628 ((ext)) && return "$ext" 1629 return 0 1630 } 1631 1632 ## @fn ble/syntax:bash/simple-word/detect-separated-path word [sep] [opts] 1633 ## 指定した単語が単一のパス名か sep 区切りのパス名刺低下の判定を行います。 1634 ## @param[in] word 1635 ## @param[in,opt] sep 1636 ## @param[in] opts 1637 ## noglob 1638 ## stopcheck 1639 ## timeout=* 1640 ## timeout-carry 1641 ## cached 1642 ## url 1643 ## notilde 1644 ## @var[out] ret 1645 ## 有効な区切り文字の集合を返します。 1646 function ble/syntax:bash/simple-word/detect-separated-path { 1647 local word=$1 sep=${2:-':'} opts=$3 1648 [[ $word ]] || return 1 1649 1650 local rex_url='^[a-z]+://' 1651 [[ :$opts: == *:url:* && $word =~ $rex_url ]] && return 1 1652 1653 # read eval options 1654 local eval_opts=$opts notilde= 1655 [[ :$opts: == *:notilde:* ]] && notilde=\'\' # チルダ展開の抑制 1656 1657 # compose regular expressions 1658 local rex_element 1659 ble/syntax:bash/simple-word/get-rex_element / 1660 local rex='^'$rex_element'/?|^/' 1661 1662 local tail=$word head= 1663 while [[ $tail =~ $rex ]]; do 1664 local rematch=$BASH_REMATCH 1665 ble/syntax:bash/simple-word/locate-filename/.exists "$notilde$head$rematch"; local ext=$? 1666 ((ext==148)) && return 148 1667 ((ext==0)) || break 1668 head=$head$rematch 1669 tail=${tail:${#rematch}} 1670 done 1671 1672 ret= 1673 local i 1674 for ((i=0;i<${#sep};i++)); do 1675 local sep1=${sep:i:1} 1676 ble/syntax:bash/simple-word/get-rex_element "$sep1" 1677 local rex_nocolon='^('$rex_element')?$' 1678 local rex_hascolon='^('$rex_element')?['$sep1']' 1679 [[ $head =~ $rex_nocolon && $tail =~ $rex_hascolon ]] && ret=$ret$sep1 1680 done 1681 [[ $ret ]] 1682 } 1683 1684 ## @fn ble/syntax:bash/simple-word/locate-filename/.exists word opts 1685 ## @param[in] word 1686 ## @param[in] opts 1687 ## @var[in] eval_opts 1688 ## @var[in] rex_url 1689 function ble/syntax:bash/simple-word/locate-filename/.exists { 1690 local word=$1 ret 1691 ble/syntax:bash/simple-word/eval "$word" "$eval_opts" || return "$?" 1692 local path=$ret 1693 # Note: #D1168 Cygwin では // で始まるパスの判定は遅いので直接文字列で判定する 1694 if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then 1695 [[ $path == // ]] 1696 else 1697 [[ -e $path || -h $path ]] 1698 fi || [[ :$opts: == *:url:* && $path =~ $rex_url ]] 1699 } 1700 ## @fn ble/syntax:bash/simple-word/locate-filename word [sep] [opts] 1701 ## @param[in] word 1702 ## @param[in] sep 1703 ## @param[in] opts 1704 ## exists 1705 ## 存在する有効なファイル名のみを抽出します。 1706 ## greedy 1707 ## : を区切りと見做さずに結合したパスが有効であれば、その結合パスを採用します。 1708 ## url 1709 ## [a-z]+:// で始まるパスを無条件に有効なパスと判定します。 1710 ## stopcheck 1711 ## 時間のかかる可能性のあるパス名展開をユーザ入力により中断します。 1712 ## timeout=* 1713 ## stopcheck に際して timeout を指定します。 1714 ## timeout-carry 1715 ## タイムアウトを後続のパス名展開に伝播させます。 1716 ## cached 1717 ## 展開内容をキャッシュします。 1718 ## 1719 ## @arr[out] ret 1720 ## 偶数個の要素を含みます。偶数 index (0, 2, 4, ...) が範囲の開始で、 1721 ## 奇数 index (1, 3, 5, ...) が範囲の終了です。 1722 ## 1723 function ble/syntax:bash/simple-word/locate-filename { 1724 local word=$1 sep=${2:-':='} opts=$3 1725 ret=0 1726 [[ $word ]] || return 0 1727 1728 # prepare evaluator 1729 local eval_opts=$opts 1730 1731 # compose regular expressions 1732 local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep" 1733 local rex='^'$rex_element'['$sep']|^['$sep']' 1734 local rex_url='^[a-z]+://' 1735 1736 local -a seppos=() 1737 local tail=$word p=0 1738 while [[ $tail =~ $rex ]]; do 1739 ((p+=${#BASH_REMATCH})) 1740 tail=${tail:${#BASH_REMATCH}} 1741 ble/array#push seppos "$((p-1))" 1742 done 1743 ble/syntax:bash/simple-word/is-simple "$tail" && 1744 ble/array#push seppos "$((p+${#tail}))" 1745 1746 local -a out=() 1747 for ((i=0;i<${#seppos[@]};i++)); do 1748 local j0=$i 1749 [[ :$opts: == *:greedy:* ]] && j0=${#seppos[@]}-1 1750 for ((j=j0;j>=i;j--)); do 1751 local f1=0 f2=${seppos[j]} 1752 ((i)) && ((f1=seppos[i-1]+1)) 1753 1754 if ((j>i)); then 1755 # もし繋げて存在するファイル名になるのであればそれを採用 1756 ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts"; local ext=$? 1757 ((ext==148)) && return 148 1758 if ((ext==0)); then 1759 ble/array#push out "$f1" "$f2" 1760 ((i=j)) 1761 fi 1762 else 1763 # ファイルが見つからなかった場合は単一区間を登録 1764 if [[ :$opts: != *:exists:* ]] || 1765 { ble/syntax:bash/simple-word/locate-filename/.exists "${word:f1:f2-f1}" "$opts" 1766 local ext=$?; ((ext==148)) && return 148; ((ext==0)); }; then 1767 ble/array#push out "$f1" "$f2" 1768 fi 1769 fi 1770 done 1771 done 1772 1773 ret=("${out[@]}") 1774 return 0 1775 } 1776 1777 ## @fn ble/syntax:bash/simple-word#break-word word sep 1778 ## 単語を指定した分割子で分割します。評価は行いません。 1779 ## progcomp で単語を COMP_WORDBREAKS で分割するのに使います。 1780 ## 例えば a==b:c\=d に対して ret=(a == b : c=d) という結果を生成します。 1781 ## 1782 ## @param[in] word 1783 ## 前提: simple-word/is-simple である必要があります。 1784 ## 1785 ## @arr[out] ret 1786 ## 単語片を含む配列を返します。 1787 ## 偶数番目の要素は分割子以外の文字列です。 1788 ## 奇数番目の要素は分割子からなる文字列です。 1789 ## 1790 function ble/syntax:bash/simple-word#break-word { 1791 local word=$1 sep=${2:-':='} 1792 if [[ ! $word ]]; then 1793 ret=('') 1794 return 0 1795 fi 1796 1797 sep=${sep//[\"\'\$\`]} 1798 1799 # compose regular expressions 1800 local rex_element; ble/syntax:bash/simple-word/get-rex_element "$sep" 1801 local rex='^('$rex_element')?['$sep']+' 1802 1803 local -a out=() 1804 local tail=$word p=0 1805 while [[ $tail =~ $rex ]]; do 1806 local rematch1=${BASH_REMATCH[1]} 1807 ble/array#push out "$rematch1" 1808 ble/array#push out "${BASH_REMATCH:${#rematch1}}" 1809 tail=${tail:${#BASH_REMATCH}} 1810 done 1811 ble/array#push out "$tail" 1812 ret=("${out[@]}") 1813 return 0 1814 } 1815 1816 #------------------------------------------------------------------------------ 1817 1818 function ble/syntax:bash/initialize-ctx { 1819 ctx=$CTX_CMDX # CTX_CMDX が ble/syntax:bash の最初の文脈 1820 } 1821 1822 ## @fn ble/syntax:bash/initialize-vars 1823 ## @var[in,out] _ble_syntax_bash_histc12 1824 ## @var[in,out] _ble_syntax_bash_histstop 1825 function ble/syntax:bash/initialize-vars { 1826 # シェル変数 histchars の解釈について 1827 # 1828 # - 1文字目 [既定値 !] は履歴展開の開始を表す。 1829 # イベント指示子の中に含まれる ! も対象となる。 1830 # 但し、histchars の 1 文字目が既に別の意味を持っている場合 ([-#?0-9^$%*]) は、 1831 # そちらの方が優先される様だ。 1832 # - 2文字目 [既定値 ^] は履歴展開(置換)の開始を表す。 1833 # ^aaa^bbb^ は =aaa=bbb= となる。 1834 # - 3文字目 [既定値 #] は .bash_history 1835 # に時刻を出力する時の区切り文字に使われる。 1836 # ここでは関係ない。 1837 # 1838 local histc12 1839 if [[ ${histchars+set} ]]; then 1840 histc12=${histchars::2} 1841 else 1842 histc12='!^' 1843 fi 1844 _ble_syntax_bash_histc12=$histc12 1845 1846 if ble/syntax:bash/cclass/update; then 1847 ble/syntax:bash/simple-word/update 1848 fi 1849 1850 local histstop=$' \t\n=' 1851 shopt -q extglob && histstop="$histstop(" 1852 _ble_syntax_bash_histstop=$histstop 1853 } 1854 1855 1856 #------------------------------------------------------------------------------ 1857 # 共通の字句の一致判定 1858 1859 ## @fn ble/syntax/highlight/vartype/.impl name [opts [tail]] 1860 ## @arr[out] __ble_vartype_ret=(ret [lookahead]) 1861 ## 属性値 ret と先読み文字数 lookahead を返します。 1862 function ble/syntax/highlight/vartype/.impl { 1863 if [[ ! $bleopt_highlight_variable ]]; then 1864 __ble_vartype_ret=$ATTR_VAR 1865 return 0 1866 fi 1867 1868 local __ble_name=$1 __ble_opts=$2 __ble_tail=$3 1869 local __ble_attr; ble/variable#get-attr -v __ble_attr "$__ble_name" 1870 if [[ ${!__ble_name+set} || $__ble_attr == *[aA]* ]]; then 1871 local __ble_rex='^-?[0-9]+(#[_a-zA-Z0-9@]*)?$' 1872 if [[ ${!__ble_name-} && :$__ble_opts: == *:expr:* && ! ( ${!__ble_name} =~ $__ble_rex ) ]]; then 1873 __ble_vartype_ret=$ATTR_VAR_EXPR 1874 elif [[ ${!__ble_name+set} && $__ble_attr == *x* ]]; then 1875 # Note: 配列の場合には第0要素が設定されている時のみ。 1876 __ble_vartype_ret=$ATTR_VAR_EXPORT 1877 elif [[ $__ble_attr == *a* ]]; then 1878 __ble_vartype_ret=$ATTR_VAR_ARRAY 1879 elif [[ $__ble_attr == *A* ]]; then 1880 __ble_vartype_ret=$ATTR_VAR_HASH 1881 elif [[ $__ble_attr == *r* && :$__ble_opts: != *:no-readonly:* ]]; then 1882 __ble_vartype_ret=$ATTR_VAR_READONLY 1883 elif [[ $__ble_attr == *i* ]]; then 1884 __ble_vartype_ret=$ATTR_VAR_NUMBER 1885 elif [[ $__ble_attr == *[luc]* ]]; then 1886 __ble_vartype_ret=$ATTR_VAR_TRANSFORM 1887 elif [[ ! ${!__ble_name} ]]; then 1888 __ble_vartype_ret=$ATTR_VAR_EMPTY 1889 else 1890 __ble_vartype_ret=$ATTR_VAR 1891 fi 1892 else 1893 # set -u のチェック 1894 if [[ :$__ble_opts: == *:readvar:* && $_ble_bash_set == *u* ]]; then 1895 if [[ ! $__ble_tail ]] || { 1896 local __ble_rex='^:?[-+?=]' 1897 [[ $__ble_tail == :* ]] && __ble_vartype_ret[1]=2 1898 ! [[ $__ble_tail =~ $__ble_rex ]]; } 1899 then 1900 __ble_vartype_ret=$ATTR_ERR 1901 return 0 1902 fi 1903 fi 1904 1905 __ble_vartype_ret=$ATTR_VAR_UNSET 1906 fi 1907 } 1908 function ble/syntax/highlight/vartype/.print { 1909 if [[ :$2: == *:unset:* ]]; then 1910 # local readonly で被覆されていて分からない時にここに来る。グローバル変数が 1911 # 存在するかしないかもわからないのでデフォルトの変数着色にする。 1912 ble/util/print "$ATTR_VAR" 1913 else 1914 local -a __ble_vartype_ret=() 1915 ble/syntax/highlight/vartype/.impl "$1" "$_ble_highlight_vartype_opts" "$_ble_highlight_vartype_tail" 1916 ble/util/print "${__ble_vartype_ret[@]}" 1917 fi 1918 } 1919 1920 ## @fn ble/syntax/highlight/vartype varname [opts [tail]] 1921 ## 変数の種類・状態に応じた属性値を決定します。 1922 ## 1923 ## @param[in] varname 1924 ## 判定対象の変数名を指定します。 1925 ## @param[in,opt] opts 1926 ## コロン区切りのオプションを指定します。 1927 ## 1928 ## readvar ... ユーザー文脈で "set -u" が指定されていてかつ存在しない変数に 1929 ## 対してエラー着色を適用します。 1930 ## 1931 ## global ... グローバル変数の状態を参照します。 1932 ## 1933 ## no-readonly ... readonly 状態を無視します。 1934 ## 1935 ## @param[in,opt] tail 1936 ## ${var...} 形式の内部を解析している時に変数名に続く文字列を指定します。 1937 ## 1938 ## @var[out] ret 1939 ## 属性値を返します。 1940 ## 1941 ## @var[out,opt] lookahead 1942 ## tail が指定された時にのみ設定されます。属性値を決定する際に参照した tail 1943 ## 内の先読み文字数を返します。 1944 ## 1945 function ble/syntax/highlight/vartype { 1946 local -a __ble_vartype_ret=() 1947 if [[ :$2: == *:global:* && $1 != __ble_* ]] && ! ble/variable#is-global "$1"; then 1948 # Note: readonly を global 変数取得に使っているので readonly は正しく判定で 1949 # きない。何れにしてもローカル変数がある時点で global readonly ではないと 1950 # 考えるのが (ローカル変数を定義した後に declare -gr するのでなければ) 普 1951 # 通である。 1952 local _ble_highlight_vartype_name=$1 1953 local _ble_highlight_vartype_opts=$__ble_opts:no-readonly 1954 local _ble_highlight_vartype_tail=$__ble_tail 1955 ble/util/assign-words __ble_vartype_ret 'ble/util/for-global-variables ble/syntax/highlight/vartype/.print "" "$_ble_highlight_vartype_name"' 1956 else 1957 ble/syntax/highlight/vartype/.impl "$@" 1958 fi 1959 ret=${__ble_vartype_ret:-$ATTR_VAR} 1960 [[ ${__ble_vartype_ret[1]+set} ]] && lookahead=${__ble_vartype_ret[1]} 1961 return 0 1962 } 1963 1964 function ble/syntax:bash/check-plain-with-escape { 1965 local rex='^('$1'|\\.)' is_quote=$2 1966 [[ $tail =~ $rex ]] || return 1 1967 if [[ $BASH_REMATCH == '\'? && 1968 ( ! $is_quote || $BASH_REMATCH == '\'[$'\\`$\n"'] ) ]]; then 1969 ((_ble_syntax_attr[i]=ATTR_QESC)) 1970 else 1971 ((_ble_syntax_attr[i]=ctx)) 1972 fi 1973 ((i+=${#BASH_REMATCH})) 1974 return 0 1975 } 1976 1977 function ble/syntax:bash/check-dollar { 1978 [[ $tail == '$'* ]] || return 1 1979 1980 local rex 1981 if [[ $tail == '${'* ]]; then 1982 # Note: パラメータ展開の中で許される物: 1983 # 決まったパターン + 数式や文字列に途中で切り替わる事も。 1984 # Note: 初めに文字数 ${#param} の形式を試す (失敗するとしても lookahead は設定する)。 1985 # その次に ${param...} 及び ${!param...} の形式を試す。 1986 # これにより ${#...} の # が文字数か或いは $# か判定する。 1987 local rex1='^(\$\{#)([-*@#?$!0]\}?|[1-9][0-9]*\}?|[_a-zA-Z][_a-zA-Z0-9]*[[}]?)' 1988 local rex2='^(\$\{!?)([-*@#?$!0]|[1-9][0-9]*|[_a-zA-Z][_a-zA-Z0-9]*\[?)' 1989 if 1990 [[ $tail =~ $rex1 ]] && { 1991 [[ ${BASH_REMATCH[2]} == *['[}'] || $BASH_REMATCH == "$tail" ]] || 1992 { ble/syntax/parse/set-lookahead "$((${#BASH_REMATCH}+1))"; false; } } || 1993 [[ $tail =~ $rex2 ]] 1994 then 1995 # <parameter> = [-*@#?-$!0] | [1-9][0-9]* | <varname> | <varname> [ ... ] | <varname> [ <@> ] 1996 # <@> = * | @ 1997 # ${<parameter>} ${#<parameter>} ${!<parameter>} 1998 # ${<parameter>:-<word>} ${<parameter>:=<word>} ${<parameter>:+<word>} ${<parameter>:?<word>} 1999 # ${<parameter>-<word>} ${<parameter>=<word>} ${<parameter>+<word>} ${<parameter>?<word>} 2000 # ${<parameter>:expr} ${<parameter>:expr:expr} etc 2001 # ${!head<@>} ${!varname[<@>]} 2002 2003 # for bash-3.1 ${#arr[n]} bug 2004 local rematch1=${BASH_REMATCH[1]} 2005 local rematch2=${BASH_REMATCH[2]} 2006 local varname=${rematch2%['[}']} 2007 2008 local ntype='${' 2009 if ((ctx==CTX_QUOT)); then 2010 ntype='"${' 2011 elif ((ctx==CTX_PWORD||ctx==CTX_PWORDE||ctx==CTX_PWORDR||ctx==CTX_EXPR)); then 2012 local ntype2; ble/syntax/parse/nest-type -v ntype2 2013 [[ $ntype2 == '"${' ]] && ntype='"${' 2014 fi 2015 2016 local ret lookahead= tail2=${tail:${#rematch1}+${#varname}} 2017 ble/syntax/highlight/vartype "$varname" readvar:global "$tail2"; local attr=$ret 2018 2019 ble/syntax/parse/nest-push "$CTX_PARAM" "$ntype" 2020 ((_ble_syntax_attr[i]=ctx, 2021 i+=${#rematch1}, 2022 _ble_syntax_attr[i]=attr, 2023 i+=${#varname})) 2024 [[ $lookahead ]] && ble/syntax/parse/set-lookahead "$lookahead" 2025 2026 if rex='^\$\{![_a-zA-Z][_a-zA-Z0-9]*[*@]\}?'; [[ $tail =~ $rex ]]; then 2027 ble/syntax/parse/set-lookahead 2 2028 if [[ $BASH_REMATCH == *'}' ]]; then 2029 # ${!head<@>} の時は末尾の @* を個別に読み取る。 2030 ((i++,ctx=CTX_PWORDE)) 2031 fi 2032 elif [[ $rematch2 == *'[' ]]; then 2033 ble/syntax/parse/nest-push "$CTX_EXPR" 'v[' 2034 ((_ble_syntax_attr[i++]=CTX_EXPR)) 2035 fi 2036 return 0 2037 elif ((_ble_bash>=50300)) && [[ $tail == '${'[$' \t\n|']* ]]; then 2038 ((_ble_syntax_attr[i]=CTX_PARAM)) 2039 ble/syntax/parse/nest-push "$CTX_CMDX" 'cmdsub_nofork' 2040 ((i+=2)) 2041 [[ $tail == '${|'* ]] && ((i++)) 2042 return 0 2043 else 2044 ((_ble_syntax_attr[i]=ATTR_ERR,i+=2)) 2045 return 0 2046 fi 2047 elif [[ $tail == '$(('* ]]; then 2048 ((_ble_syntax_attr[i]=CTX_PARAM)) 2049 ble/syntax/parse/nest-push "$CTX_EXPR" '$((' 2050 ((i+=3)) 2051 return 0 2052 elif [[ $tail == '$['* ]]; then 2053 ((_ble_syntax_attr[i]=CTX_PARAM)) 2054 ble/syntax/parse/nest-push "$CTX_EXPR" '$[' 2055 ((i+=2)) 2056 return 0 2057 elif [[ $tail == '$('* ]]; then 2058 ((_ble_syntax_attr[i]=CTX_PARAM)) 2059 ble/syntax/parse/nest-push "$CTX_CMDX" '$(' 2060 ((i+=2)) 2061 return 0 2062 elif rex='^\$([-*@#?$!0_]|[1-9]|[_a-zA-Z][_a-zA-Z0-9]*)' && [[ $tail =~ $rex ]]; then 2063 local rematch=$BASH_REMATCH rematch1=${BASH_REMATCH[1]} 2064 ((_ble_syntax_attr[i++]=CTX_PARAM)) 2065 2066 if ((_ble_bash<40200)) && local tail=${tail:1} && 2067 ble/syntax:bash/starts-with-histchars && ble/syntax:bash/check-history-expansion; then 2068 # bash-4.1 以下では $!" や $!a 等が履歴展開の対象になる。 2069 return 0 2070 else 2071 local ret; ble/syntax/highlight/vartype "$rematch1" readvar:global 2072 ((_ble_syntax_attr[i]=ret,i+=${#rematch}-1)) 2073 return 0 2074 fi 2075 else 2076 # if dollar doesn't match any patterns it is treated as a normal character 2077 ((_ble_syntax_attr[i++]=ctx)) 2078 return 0 2079 fi 2080 } 2081 2082 function ble/syntax:bash/check-quotes { 2083 local rex aqdel=$ATTR_QDEL aquot=$CTX_QUOT 2084 2085 # 字句的に解釈されるが除去はされない場合 2086 if ((ctx==CTX_EXPR)) && [[ $tail != \`* ]]; then 2087 local ntype 2088 ble/syntax/parse/nest-type 2089 case $ntype in 2090 ('${') 2091 # ${var:...} の中では如何なる quote も除去されない (字句的には解釈される)。 2092 # 除去されない quote は算術式エラーである。Note: bash >= 5.2 では "..." 2093 # と $"..." は許される。 2094 if ((_ble_bash<50200)) || [[ $tail == \'* || $tail == \$\'* ]]; then 2095 ((aqdel=ATTR_ERR,aquot=CTX_EXPR)) 2096 fi ;; 2097 ('$['|'$(('|expr-paren-ax) 2098 if [[ $tail == \'* ]] || ((_ble_bash<40400)); then 2099 ((aqdel=ATTR_ERR,aquot=CTX_EXPR)) 2100 fi ;; 2101 ('(('|expr-paren|expr-brack) 2102 if [[ $tail == \'* ]] && ((_ble_bash>=50100)); then 2103 ((aqdel=ATTR_ERR,aquot=CTX_EXPR)) 2104 fi ;; 2105 ('a['|'v['|expr-paren-ai|expr-brack-ai) 2106 if [[ $tail == \'* ]] && ((_ble_bash>=40400)); then 2107 ((aqdel=ATTR_ERR,aquot=CTX_EXPR)) 2108 fi ;; 2109 ('"${') 2110 if ! { [[ $tail == '$'[\'\"]* ]] && shopt -q extquote; }; then 2111 # "${var:...}" の中では 〈extquote が設定されている時の $'' $""〉 を例 2112 # 外としてquote は除去されない (字句的には解釈される)。 2113 ((aqdel=ATTR_ERR,aquot=CTX_EXPR)) 2114 fi ;; 2115 # ('d['|expr-paren-di|expr-brack-di) ;; # nop 2116 esac 2117 elif ((ctx==CTX_PWORD||ctx==CTX_PWORDE||ctx==CTX_PWORDR)); then 2118 # "${var ~}" の中では $'' $"" は ! shopt -q extquote の時除去されない。 2119 if [[ $tail == '$'[\'\"]* ]] && ! shopt -q extquote; then 2120 local ntype 2121 ble/syntax/parse/nest-type 2122 if [[ $ntype == '"${' ]]; then 2123 ((aqdel=ctx,aquot=ctx)) 2124 fi 2125 fi 2126 fi 2127 2128 if rex='^`([^`\]|\\(.|$))*(`?)|^'\''[^'\'']*('\''?)' && [[ $tail =~ $rex ]]; then 2129 ((_ble_syntax_attr[i]=aqdel, 2130 _ble_syntax_attr[i+1]=aquot, 2131 i+=${#BASH_REMATCH}, 2132 _ble_syntax_attr[i-1]=${#BASH_REMATCH[3]}||${#BASH_REMATCH[4]}?aqdel:ATTR_ERR)) 2133 return 0 2134 fi 2135 2136 if ((ctx!=CTX_QUOT)); then 2137 if rex='^(\$?")([^'"${_ble_syntax_bash_chars[CTX_QUOT]}"']*)("?)' && [[ $tail =~ $rex ]]; then 2138 local rematch1=${BASH_REMATCH[1]} # for bash-3.1 ${#arr[n]} bug 2139 if [[ ${BASH_REMATCH[3]} ]]; then 2140 # 終端まで行った場合 2141 ((_ble_syntax_attr[i]=aqdel, 2142 _ble_syntax_attr[i+${#rematch1}]=aquot, 2143 i+=${#BASH_REMATCH}, 2144 _ble_syntax_attr[i-1]=aqdel)) 2145 else 2146 # 中に構造がある場合 2147 ble/syntax/parse/nest-push "$CTX_QUOT" 2148 if (((ctx==CTX_PWORD||ctx==CTX_PWORDE||ctx==CTX_PWORDR)&&aqdel!=ATTR_QDEL)); then 2149 # CTX_PWORD (パラメータ展開) でクォート除去が有効でない文脈の場合、 2150 # 「$」 だけ aqdel で着色し、「" ... "」 は通常通り着色する。 2151 ((_ble_syntax_attr[i]=aqdel, 2152 _ble_syntax_attr[i+${#rematch1}-1]=ATTR_QDEL, 2153 _ble_syntax_attr[i+${#rematch1}]=CTX_QUOT, 2154 i+=${#BASH_REMATCH})) 2155 else 2156 ((_ble_syntax_attr[i]=aqdel, 2157 _ble_syntax_attr[i+${#rematch1}]=CTX_QUOT, 2158 i+=${#BASH_REMATCH})) 2159 fi 2160 fi 2161 return 0 2162 elif rex='^\$'\''(([^'\''\]|\\(.|$))*)('\''?)' && [[ $tail =~ $rex ]]; then 2163 ((_ble_syntax_attr[i]=aqdel,i+=2)) 2164 local t=${BASH_REMATCH[1]} rematch4=${BASH_REMATCH[4]} 2165 2166 local rex='\\[abefnrtvE"'\''\?]|\\[0-7]{1,3}|\\c.|\\x[0-9a-fA-F]{1,2}' 2167 ((_ble_bash>=40200)) && rex=$rex'|\\u[0-9a-fA-F]{1,4}|\\U[0-9a-fA-F]{1,8}' 2168 local rex='^([^'\''\]*)('$rex'|(\\.))' 2169 while [[ $t =~ $rex ]]; do 2170 local m1=${BASH_REMATCH[1]} m2=${BASH_REMATCH[2]} 2171 [[ $m1 ]] && ((_ble_syntax_attr[i]=aquot,i+=${#m1})) 2172 if [[ ${BASH_REMATCH[3]} ]]; then 2173 ((_ble_syntax_attr[i]=aquot)) 2174 else 2175 ((_ble_syntax_attr[i]=ATTR_QESC)) 2176 fi 2177 ((i+=${#m2})) 2178 t=${t:${#BASH_REMATCH}} 2179 done 2180 [[ $t ]] && ((_ble_syntax_attr[i]=aquot,i+=${#t})) 2181 if [[ $rematch4 ]]; then 2182 ((_ble_syntax_attr[i++]=aqdel)) 2183 else 2184 ((_ble_syntax_attr[i-1]=ATTR_ERR)) 2185 fi 2186 return 0 2187 fi 2188 fi 2189 2190 return 1 2191 } 2192 2193 function ble/syntax:bash/check-process-subst { 2194 # プロセス置換 2195 if [[ $tail == ['<>']'('* ]]; then 2196 ble/syntax/parse/nest-push "$CTX_CMDX" '(' 2197 ((_ble_syntax_attr[i]=ATTR_DEL,i+=2)) 2198 return 0 2199 fi 2200 2201 return 1 2202 } 2203 2204 function ble/syntax:bash/check-comment { 2205 # コメント 2206 if shopt -q interactive_comments; then 2207 if ((wbegin<0||wbegin==i)) && local rex=$'^#[^\n]*' && [[ $tail =~ $rex ]]; then 2208 # 空白と同様に ctx は変えずに素通り (末端の改行は残す) 2209 ((_ble_syntax_attr[i]=ATTR_COMMENT, 2210 i+=${#BASH_REMATCH})) 2211 return 0 2212 fi 2213 fi 2214 2215 return 1 2216 } 2217 2218 function ble/syntax:bash/check-glob { 2219 [[ $tail == ['[?*@+!()|']* ]] || return 1 2220 2221 local ntype= force_attr= 2222 if ((ctx==CTX_VRHS||ctx==CTX_ARGVR||ctx==CTX_ARGER||ctx==CTX_VALR||ctx==CTX_RDRS)); then 2223 force_attr=$ctx 2224 ntype="glob_attr=$force_attr" 2225 elif ((ctx==CTX_FARGX1||ctx==CTX_FARGI1)); then 2226 # for [xxx] / for a[xxx] の場合 2227 force_attr=$ATTR_ERR 2228 ntype="glob_attr=$force_attr" 2229 elif ((ctx==CTX_PWORD||ctx==CTX_PWORDE||ctx==CTX_PWORDR)); then 2230 ntype="glob_ctx=$ctx" 2231 elif ((ctx==CTX_PATN||ctx==CTX_BRAX)); then 2232 ble/syntax/parse/nest-type 2233 local exit_attr= 2234 if [[ $ntype == glob_attr=* ]]; then 2235 force_attr=${ntype#*=} 2236 exit_attr=$force_attr 2237 elif ((ctx==CTX_BRAX)); then 2238 force_attr=$ctx 2239 ntype="glob_attr=$force_attr" 2240 elif ((ctx==CTX_PATN)); then 2241 ((exit_attr=_ble_syntax_attr[inest])) 2242 2243 # glob_ctx=* の時は ntype は子に継承する 2244 [[ $ntype != glob_ctx=* ]] && ntype= 2245 else 2246 ntype= 2247 fi 2248 elif [[ $1 == assign ]]; then 2249 # $1 == assign の時、arr[... の "[" の位置で呼び出されたことを意味する。 2250 ntype='a[' 2251 fi 2252 2253 if [[ $tail == ['?*@+!']'('* ]] && shopt -q extglob; then 2254 ble/syntax/parse/nest-push "$CTX_PATN" "$ntype" 2255 ((_ble_syntax_attr[i]=${force_attr:-ATTR_GLOB},i+=2)) 2256 return 0 2257 fi 2258 2259 # 履歴展開の解釈の方が強い 2260 local histc1=${_ble_syntax_bash_histc12::1} 2261 [[ $histc1 && $tail == "$histc1"* ]] && return 1 2262 2263 if [[ $tail == '['* ]]; then 2264 if ((ctx==CTX_BRAX)); then 2265 # 角括弧式の中の [ or [! はそのまま読み飛ばす。 2266 ((_ble_syntax_attr[i++]=force_attr)) 2267 [[ $tail == '[!'* ]] && ((i++)) 2268 return 0 2269 fi 2270 2271 ble/syntax/parse/nest-push "$CTX_BRAX" "$ntype" 2272 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_GLOB})) 2273 [[ $tail == '[!'* ]] && ((i++)) 2274 if [[ ${text:i:1} == ']' ]]; then 2275 ((_ble_syntax_attr[i++]=${force_attr:-CTX_BRAX})) 2276 elif [[ ${text:i:1} == '[' ]]; then 2277 # Note: 条件コマンド [[ に変換する為に [[ の連なりは一度に読み取る。 2278 if [[ ${text:i+1:1} == [:=.] ]]; then 2279 # Note: glob bracket expression が POSIX 括弧で始まっている時は 2280 # [[ が一まとまりになっていると困るので除外。 2281 ble/syntax/parse/set-lookahead 2 2282 else 2283 ((_ble_syntax_attr[i++]=${force_attr:-CTX_BRAX})) 2284 [[ ${text:i:1} == '!'* ]] && ((i++)) 2285 fi 2286 fi 2287 2288 return 0 2289 elif [[ $tail == ['?*']* ]]; then 2290 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_GLOB})) 2291 return 0 2292 elif [[ $tail == ['@+!']* ]]; then 2293 ((_ble_syntax_attr[i++]=${force_attr:-ctx})) 2294 return 0 2295 elif ((ctx==CTX_PATN||ctx==CTX_BRAX)); then 2296 if [[ $tail == '('* ]]; then 2297 ble/syntax/parse/nest-push "$CTX_PATN" "$ntype" 2298 ((_ble_syntax_attr[i++]=${force_attr:-ctx})) 2299 return 0 2300 elif [[ $tail == ')'* ]]; then 2301 if ((ctx==CTX_PATN)); then 2302 ((_ble_syntax_attr[i++]=exit_attr)) 2303 ble/syntax/parse/nest-pop 2304 else 2305 ((_ble_syntax_attr[i++]=${force_attr:-ctx})) 2306 fi 2307 return 0 2308 elif [[ $tail == '|'* ]]; then 2309 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_GLOB})) 2310 return 0 2311 fi 2312 fi 2313 2314 return 1 2315 } 2316 2317 _ble_syntax_bash_histexpand_RexWord= 2318 _ble_syntax_bash_histexpand_RexMods= 2319 _ble_syntax_bash_histexpand_RexEventDef= 2320 _ble_syntax_bash_histexpand_RexQuicksubDef= 2321 _ble_syntax_bash_histexpand_RexEventFmt= 2322 _ble_syntax_bash_histexpand_RexQuicksubFmt= 2323 function ble/syntax:bash/check-history-expansion/.initialize { 2324 local spaces=$_ble_term_IFS nl=$'\n' 2325 local rex_event='-?[0-9]+|[!#]|[^-$^*%:'$spaces'=?!#;&|<>()]+|\?[^?'$nl']*\??' 2326 _ble_syntax_bash_histexpand_RexEventDef='^!('$rex_event')' 2327 2328 local rex_word1='([0-9]+|[$%^])' 2329 local rex_wordsA=':('$rex_word1'?-'$rex_word1'?|\*|'$rex_word1'\*?)' 2330 local rex_wordsB='([$%^]?-'$rex_word1'?|\*|[$^%][*-]?)' 2331 _ble_syntax_bash_histexpand_RexWord='('$rex_wordsA'|'$rex_wordsB')?' 2332 2333 # ※本当は /s(.)([^\]|\\.)*?\1([^\]|\\.)*?\1/ 等としたいが *? は ERE にない。 2334 # 仕方がないので ble/syntax:bash/check-history-expansion/.check-modifiers 2335 # にて繰り返し正規表現を適用して s?..?..? を読み取る。 2336 local rex_modifier=':[htrepqx]|:[gGa]?&|:[gGa]?s(/([^\/]|\\.)*){0,2}(/|$)' 2337 _ble_syntax_bash_histexpand_RexMods='('$rex_modifier')*' 2338 2339 _ble_syntax_bash_histexpand_RexQuicksubDef='\^([^^\]|\\.)*\^([^^\]|\\.)*\^' 2340 2341 # for histchars 2342 _ble_syntax_bash_histexpand_RexQuicksubFmt='@A([^@C\]|\\.)*@A([^@C\]|\\.)*@A' 2343 _ble_syntax_bash_histexpand_RexEventFmt='^@A('$rex_event'|@A)' 2344 } 2345 ble/syntax:bash/check-history-expansion/.initialize 2346 2347 ## @fn ble/syntax:bash/check-history-expansion/.initialize-event 2348 ## @var[out] rex_event 2349 function ble/syntax:bash/check-history-expansion/.initialize-event { 2350 local histc1=${_ble_syntax_bash_histc12::1} 2351 if [[ $histc1 == '!' ]]; then 2352 rex_event=$_ble_syntax_bash_histexpand_RexEventDef 2353 else 2354 local A="[$histc1]" 2355 [[ $histc1 == '^' ]] && A='\^' 2356 rex_event=$_ble_syntax_bash_histexpand_RexEventFmt 2357 rex_event=${rex_event//@A/"$A"} 2358 fi 2359 } 2360 ## @fn ble/syntax:bash/check-history-expansion/.initialize-quicksub 2361 ## @var[out] rex_quicksub 2362 function ble/syntax:bash/check-history-expansion/.initialize-quicksub { 2363 local histc2=${_ble_syntax_bash_histc12:1:1} 2364 if [[ $histc2 == '^' ]]; then 2365 rex_quicksub=$_ble_syntax_bash_histexpand_RexQuicksubDef 2366 else 2367 rex_quicksub=$_ble_syntax_bash_histexpand_RexQuicksubFmt 2368 rex_quicksub=${rex_quicksub//@A/"[$histc2]"} 2369 rex_quicksub=${rex_quicksub//@C/"$histc2"} 2370 fi 2371 } 2372 function ble/syntax:bash/check-history-expansion/.check-modifiers { 2373 # check simple modifiers 2374 [[ ${text:i} =~ $_ble_syntax_bash_histexpand_RexMods ]] && 2375 ((i+=${#BASH_REMATCH})) 2376 2377 # check :s?..?..? form modifier 2378 if local rex='^:[gGa]?s(.)'; [[ ${text:i} =~ $rex ]]; then 2379 local del=${BASH_REMATCH[1]} 2380 local A="[$del]" B="[^$del]" 2381 [[ $del == '^' || $del == ']' ]] && A='\'$del 2382 [[ $del != '\' ]] && B=$B'|\\.' 2383 2384 local rex_substitute='^:[gGa]?s('$A'('$B')*){0,2}('$A'|$)' 2385 if [[ ${text:i} =~ $rex_substitute ]]; then 2386 ((i+=${#BASH_REMATCH})) 2387 ble/syntax:bash/check-history-expansion/.check-modifiers 2388 return 0 2389 fi 2390 fi 2391 2392 # ErrMsg 'unrecognized modifier' 2393 if [[ ${text:i} == ':'[gGa]* ]]; then 2394 ((_ble_syntax_attr[i+1]=ATTR_ERR,i+=2)) 2395 elif [[ ${text:i} == ':'* ]]; then 2396 ((_ble_syntax_attr[i]=ATTR_ERR,i++)) 2397 fi 2398 } 2399 ## @fn ble/syntax:bash/check-history-expansion 2400 ## @var[in] i tail 2401 function ble/syntax:bash/check-history-expansion { 2402 [[ -o histexpand ]] || return 1 2403 2404 local histc1=${_ble_syntax_bash_histc12:0:1} 2405 local histc2=${_ble_syntax_bash_histc12:1:1} 2406 if [[ $histc1 && $tail == "$histc1"[^"$_ble_syntax_bash_histstop"]* ]]; then 2407 2408 # "~" 文字列中では一致可能範囲を制限する。 2409 if ((_ble_bash>=40300&&ctx==CTX_QUOT)); then 2410 local tail=${tail%%'"'*} 2411 [[ $tail == '!' ]] && return 1 2412 fi 2413 2414 ((_ble_syntax_attr[i]=ATTR_HISTX)) 2415 local rex_event 2416 ble/syntax:bash/check-history-expansion/.initialize-event 2417 if [[ $tail =~ $rex_event ]]; then 2418 ((i+=${#BASH_REMATCH})) 2419 elif [[ $tail == "$histc1"['-:0-9^$%*']* ]]; then 2420 ((_ble_syntax_attr[i]=ATTR_HISTX,i++)) 2421 else 2422 # ErrMsg 'unrecognized event' 2423 ((_ble_syntax_attr[i+1]=ATTR_ERR,i+=2)) 2424 return 0 2425 fi 2426 2427 # word-designator 2428 [[ ${text:i} =~ $_ble_syntax_bash_histexpand_RexWord ]] && 2429 ((i+=${#BASH_REMATCH})) 2430 2431 ble/syntax:bash/check-history-expansion/.check-modifiers 2432 return 0 2433 elif ((i==0)) && [[ $histc2 && $tail == "$histc2"* ]]; then 2434 ((_ble_syntax_attr[i]=ATTR_HISTX)) 2435 local rex_quicksub 2436 ble/syntax:bash/check-history-expansion/.initialize-quicksub 2437 if [[ $tail =~ $rex_quicksub ]]; then 2438 ((i+=${#BASH_REMATCH})) 2439 2440 ble/syntax:bash/check-history-expansion/.check-modifiers 2441 return 0 2442 else 2443 # 末端まで 2444 ((i+=${#tail})) 2445 return 0 2446 fi 2447 fi 2448 2449 return 1 2450 } 2451 ## @fn ble/syntax:bash/starts-with-histchars 2452 ## @var[in] tail 2453 function ble/syntax:bash/starts-with-histchars { 2454 [[ $_ble_syntax_bash_histc12 && $tail == ["$_ble_syntax_bash_histc12"]* ]] 2455 } 2456 2457 #------------------------------------------------------------------------------ 2458 # 文脈: 各種文脈 2459 2460 _ble_syntax_context_proc[CTX_QUOT]=ble/syntax:bash/ctx-quot 2461 function ble/syntax:bash/ctx-quot { 2462 # 文字列の中身 2463 if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[CTX_QUOT]}]+" 1; then 2464 return 0 2465 elif [[ $tail == '"'* ]]; then 2466 ((_ble_syntax_attr[i]=ATTR_QDEL, 2467 i+=1)) 2468 ble/syntax/parse/nest-pop 2469 return 0 2470 elif ble/syntax:bash/check-quotes; then 2471 return 0 2472 elif ble/syntax:bash/check-dollar; then 2473 return 0 2474 elif ble/syntax:bash/starts-with-histchars; then 2475 ble/syntax:bash/check-history-expansion || 2476 ((_ble_syntax_attr[i]=ctx,i++)) 2477 return 0 2478 fi 2479 2480 return 1 2481 } 2482 2483 _ble_syntax_context_proc[CTX_CASE]=ble/syntax:bash/ctx-case 2484 function ble/syntax:bash/ctx-case { 2485 if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then 2486 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 2487 return 0 2488 elif [[ $tail == '('* ]]; then 2489 ((_ble_syntax_attr[i++]=ATTR_GLOB,ctx=CTX_CPATX)) 2490 return 0 2491 elif [[ $tail == 'esac'$_ble_syntax_bash_RexDelimiter* || $tail == 'esac' ]]; then 2492 ((ctx=CTX_CMDX)) 2493 ble/syntax:bash/ctx-command 2494 else 2495 ((ctx=CTX_CPATX)) 2496 ble/syntax:bash/ctx-command-case-pattern-expect 2497 fi 2498 } 2499 2500 # 文脈 CTX_PATN (extglob/case-pattern) 2501 _ble_syntax_context_proc[CTX_PATN]=ble/syntax:bash/ctx-globpat 2502 _ble_syntax_context_end[CTX_PATN]=ble/syntax:bash/ctx-globpat.end 2503 2504 ## @fn ble/syntax:bash/ctx-globpat/get-stop-chars 2505 ## @var[out] chars 2506 function ble/syntax:bash/ctx-globpat/get-stop-chars { 2507 chars=${_ble_syntax_bash_chars[CTX_PATN]} 2508 local ntype; ble/syntax/parse/nest-type 2509 if [[ $ntype == glob_ctx=* ]]; then 2510 local gctx=${ntype#glob_ctx=} 2511 if ((gctx==CTX_PWORD||gctx==CTX_PWORDE)); then 2512 chars=}$chars 2513 elif ((gctx==CTX_PWORDR)); then 2514 chars=}/$chars 2515 fi 2516 fi 2517 } 2518 function ble/syntax:bash/ctx-globpat { 2519 # glob () の中身 (extglob @(...) や case in (...) の中) 2520 local chars; ble/syntax:bash/ctx-globpat/get-stop-chars 2521 if ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then 2522 return 0 2523 elif ble/syntax:bash/check-process-subst; then 2524 return 0 2525 elif [[ $tail == ['<>']* ]]; then 2526 ((_ble_syntax_attr[i++]=ctx)) 2527 return 0 2528 elif ble/syntax:bash/check-quotes; then 2529 return 0 2530 elif ble/syntax:bash/check-dollar; then 2531 return 0 2532 elif ble/syntax:bash/check-glob; then 2533 return 0 2534 elif ble/syntax:bash/check-brace-expansion; then 2535 return 0 2536 elif ble/syntax:bash/starts-with-histchars; then 2537 ble/syntax:bash/check-history-expansion || 2538 ((_ble_syntax_attr[i]=ctx,i++)) 2539 return 0 2540 fi 2541 2542 return 1 2543 } 2544 function ble/syntax:bash/ctx-globpat.end { 2545 local is_end= tail=${text:i} 2546 local ntype; ble/syntax/parse/nest-type 2547 if [[ $ntype == glob_ctx=* ]]; then 2548 local gctx=${ntype#glob_ctx=} 2549 if ((gctx==CTX_PWORD||gctx==CTX_PWORDE)); then 2550 [[ ! $tail || $tail == '}'* ]] && is_end=1 2551 elif ((gctx==CTX_PWORDR)); then 2552 [[ ! $tail || $tail == ['/}']* ]] && is_end=1 2553 fi 2554 fi 2555 2556 if [[ $is_end ]]; then 2557 ble/syntax/parse/nest-pop 2558 ble/syntax/parse/check-end 2559 return 0 2560 fi 2561 2562 return 0 2563 } 2564 2565 # 文脈 CTX_BRAX (bracket expression) 2566 _ble_syntax_context_proc[CTX_BRAX]=ble/syntax:bash/ctx-bracket-expression 2567 _ble_syntax_context_end[CTX_BRAX]=ble/syntax:bash/ctx-bracket-expression.end 2568 function ble/syntax:bash/ctx-bracket-expression { 2569 local nctx; ble/syntax/parse/nest-ctx 2570 if ((nctx==CTX_PATN)); then 2571 local chars; ble/syntax:bash/ctx-globpat/get-stop-chars 2572 elif ((nctx==CTX_PWORD||nctx==CTX_PWORDE||nctx==CTX_PWORDR)); then 2573 local chars=${_ble_syntax_bash_chars[nctx]} 2574 else 2575 # 以下の文脈では ctx-command と同様の処理で問題ない。 2576 # 2577 # ctx-command (色々) 2578 # ctx-redirect (CTX_RDRF CTX_RDRD CTX_RDRD2 CTX_RDRS) 2579 # ctx-values (CTX_VALI, CTX_VALR, CTX_VALQ) 2580 # ctx-conditions (CTX_CONDI, CTX_CONDQ) 2581 # この文脈では例外として && || < > など一部の演算子で delimiters 2582 # が単語中に許されるが、この例外は [...] を含む単語には当てはまらない。 2583 # 2584 # is-delimiters の時に [... は其処で不完全終端する。 2585 local chars=${_ble_syntax_bash_chars[CTX_ARGI]//'~'} 2586 fi 2587 chars="][${chars#']'}" 2588 2589 local ntype; ble/syntax/parse/nest-type 2590 local force_attr=; [[ $ntype == glob_attr=* ]] && force_attr=${ntype#*=} 2591 2592 local rex 2593 if [[ $tail == ']'* ]]; then 2594 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_GLOB})) 2595 ble/syntax/parse/nest-pop 2596 2597 # 通常引数が配列代入の形式を持つとき、以降でチルダ展開が有効 2598 # 例: echo arr[i]=... arr[i]+=... 2599 if [[ $ntype == 'a[' ]]; then 2600 local is_assign= 2601 if [[ $tail == ']='* ]]; then 2602 ((_ble_syntax_attr[i++]=ctx,is_assign=1)) 2603 elif [[ $tail == ']+'* ]]; then 2604 ble/syntax/parse/set-lookahead 2 2605 [[ $tail == ']+='* ]] && ((_ble_syntax_attr[i]=ctx,i+=2,is_assign=1)) 2606 fi 2607 2608 if [[ $is_assign ]]; then 2609 ble/util/assert '[[ ${_ble_syntax_bash_command_CtxAssign[ctx]} ]]' 2610 ((ctx=_ble_syntax_bash_command_CtxAssign[ctx])) 2611 if local tail=${text:i}; [[ $tail == '~'* ]]; then 2612 ble/syntax:bash/check-tilde-expansion rhs 2613 fi 2614 fi 2615 fi 2616 return 0 2617 elif [[ $tail == '['* ]]; then 2618 rex='^\[@([^'$chars']+(@\]?)?)?' 2619 rex=${rex//@/:}'|'${rex//@/'\.'}'|'${rex//@/=}'|^\[' 2620 [[ $tail =~ $rex ]] 2621 ((_ble_syntax_attr[i]=${force_attr:-ctx}, 2622 i+=${#BASH_REMATCH})) 2623 return 0 2624 elif ctx=${force_attr:-$ctx} ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then 2625 return 0 2626 elif ble/syntax:bash/check-process-subst; then 2627 return 0 2628 elif ble/syntax:bash/check-quotes; then 2629 return 0 2630 elif ble/syntax:bash/check-dollar; then 2631 return 0 2632 elif ble/syntax:bash/check-glob; then 2633 return 0 2634 elif ble/syntax:bash/check-brace-expansion; then 2635 return 0 2636 elif ble/syntax:bash/check-tilde-expansion; then 2637 return 0 2638 elif ble/syntax:bash/starts-with-histchars; then 2639 ble/syntax:bash/check-history-expansion || 2640 ((_ble_syntax_attr[i++]=${force_attr:-ctx})) 2641 return 0 2642 elif ((nctx==CTX_PATN)) && [[ $tail == ['<>']* ]]; then 2643 ((_ble_syntax_attr[i++]=${force_attr:-ctx})) 2644 return 0 2645 fi 2646 2647 return 1 2648 } 2649 function ble/syntax:bash/ctx-bracket-expression.end { 2650 local is_end= 2651 2652 local tail=${text:i} 2653 if [[ ! $tail ]]; then 2654 is_end=1 2655 else 2656 local nctx; ble/syntax/parse/nest-ctx 2657 local external_ctx=$nctx 2658 if ((nctx==CTX_PATN)); then 2659 local ntype; ble/syntax/parse/nest-type 2660 [[ $ntype == glob_ctx=* ]] && 2661 external_ctx=${ntype#glob_ctx=} 2662 fi 2663 2664 if ((external_ctx==CTX_PATN)); then 2665 [[ $tail == ')'* ]] && is_end=1 2666 elif ((external_ctx==CTX_PWORD||external_ctx==CTX_PWORDE)); then 2667 [[ $tail == '}'* ]] && is_end=1 2668 elif ((external_ctx==CTX_PWORDR)); then 2669 [[ $tail == ['}/']* ]] && is_end=1 2670 else 2671 # 外側は ctx-command など。 2672 if ble/syntax:bash/check-word-end/is-delimiter; then 2673 is_end=1 2674 elif [[ $tail == ':'* && ${_ble_syntax_bash_command_IsAssign[ctx]} ]]; then 2675 is_end=1 2676 fi 2677 fi 2678 fi 2679 2680 if [[ $is_end ]]; then 2681 ble/syntax/parse/nest-pop 2682 ble/syntax/parse/check-end 2683 return "$?" 2684 fi 2685 2686 return 0 2687 } 2688 2689 _ble_syntax_context_proc[CTX_PARAM]=ble/syntax:bash/ctx-param 2690 _ble_syntax_context_proc[CTX_PWORD]=ble/syntax:bash/ctx-pword 2691 _ble_syntax_context_proc[CTX_PWORDR]=ble/syntax:bash/ctx-pword 2692 _ble_syntax_context_proc[CTX_PWORDE]=ble/syntax:bash/ctx-pword-error 2693 function ble/syntax:bash/ctx-param { 2694 # パラメータ展開 - パラメータの直後 2695 if [[ $tail == '}'* ]]; then 2696 ((_ble_syntax_attr[i]=_ble_syntax_attr[inest])) 2697 ((i+=1)) 2698 ble/syntax/parse/nest-pop 2699 return 0 2700 fi 2701 2702 local rex='##?|%%?|:?[-?=+]|:|/[/#%]?' 2703 ((_ble_bash>=40000)) && rex=$rex'|,,?|\^\^?|~~?' 2704 if ((_ble_bash>=50200)); then 2705 rex=$rex'|@[QEPAaUuLKk]?' 2706 elif ((_ble_bash>=50100)); then 2707 rex=$rex'|@[QEPAaUuLK]?' 2708 elif ((_ble_bash>=40400)); then 2709 rex=$rex'|@[QEPAa]?' 2710 fi 2711 rex='^('$rex')' 2712 if [[ $tail =~ $rex ]]; then 2713 ((_ble_syntax_attr[i]=CTX_PARAM, 2714 i+=${#BASH_REMATCH})) 2715 if [[ $BASH_REMATCH == '/'* ]]; then 2716 ((ctx=CTX_PWORDR)) 2717 elif [[ $BASH_REMATCH == : ]]; then 2718 ((ctx=CTX_EXPR,_ble_syntax_attr[i-1]=CTX_EXPR)) 2719 elif [[ $BASH_REMATCH == @* ]]; then 2720 ((ctx=CTX_PWORDE)) 2721 else 2722 ((ctx=CTX_PWORD)) 2723 [[ $BASH_REMATCH == [':-+=?#%']* ]] && 2724 tail=${text:i} ble/syntax:bash/check-tilde-expansion pword 2725 fi 2726 return 0 2727 else 2728 local i0=$i 2729 ((ctx=CTX_PWORD)) 2730 ble/syntax:bash/ctx-pword || return 1 2731 2732 # 一文字だけエラー着色 2733 if ((i0+2<=i)); then 2734 ((_ble_syntax_attr[i0+1])) || 2735 ((_ble_syntax_attr[i0+1]=_ble_syntax_attr[i0])) 2736 fi 2737 ((_ble_syntax_attr[i0]=ATTR_ERR)) 2738 return 0 2739 fi 2740 } 2741 function ble/syntax:bash/ctx-pword { 2742 # パラメータ展開 - word 部 2743 if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[ctx]}]+"; then 2744 return 0 2745 elif ((ctx==CTX_PWORDR)) && [[ $tail == '/'* ]]; then 2746 ((_ble_syntax_attr[i++]=CTX_PARAM,ctx=CTX_PWORD)) 2747 return 0 2748 elif [[ $tail == '}'* ]]; then 2749 ((_ble_syntax_attr[i]=_ble_syntax_attr[inest])) 2750 ((i+=1)) 2751 ble/syntax/parse/nest-pop 2752 return 0 2753 elif ble/syntax:bash/check-quotes; then 2754 return 0 2755 elif ble/syntax:bash/check-dollar; then 2756 return 0 2757 elif ble/syntax:bash/check-glob; then 2758 return 0 2759 elif ble/syntax:bash/starts-with-histchars; then 2760 ble/syntax:bash/check-history-expansion || 2761 ((_ble_syntax_attr[i]=ctx,i++)) 2762 return 0 2763 fi 2764 2765 return 1 2766 } 2767 function ble/syntax:bash/ctx-pword-error { 2768 local i0=$i 2769 if ble/syntax:bash/ctx-pword; then 2770 [[ $tail == '}'* ]] || 2771 ((_ble_syntax_attr[i0]=ATTR_ERR)) 2772 return 0 2773 else 2774 return 1 2775 fi 2776 } 2777 2778 ## @const CTX_EXPR 2779 ## 算術式の文脈値 2780 ## 2781 ## 対応する nest types (ntype) の一覧 2782 ## 2783 ## NTYPE NEST-PUSH LOCATION QUOTE DESC 2784 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2785 ## '$((' @ check-dollar x 算術式展開 $(()) の中身 2786 ## '$[' @ check-dollar x 算術式展開 $[] の中身 2787 ## '((' @ .check-delimiter-or-redirect o 算術式評価コマンド (()) の中身 2788 ## 'a[' @ check-variable-assignment o a[...]= の中身 2789 ## 'd[' @ ctx-values o a=([...]=) の中身 2790 ## 'v[' @ check-dollar o ${a[...]} の中身 2791 ## '${' @ check-dollar o ${v:...} の中身 2792 ## '"${' @ check-dollar x "${v:...}" の中身 2793 ## 'expr-paren' @ .count-paren o () によるネスト (quote 除去有効) 2794 ## 'expr-paren-ax' @ .count-paren + () によるネスト / $(( $[ の内部 2795 ## 'expr-paren-ai' @ .count-paren + () によるネスト / a[ v[ の内部 (unused) 2796 ## 'expr-paren-di' @ .count-paren o () によるネスト / d[ の内部 (unused) 2797 ## 'expr-brack' @ .count-bracket o [] によるネスト (quote 除去常時有効) (unused) 2798 ## 'expr-brack-ai' @ .count-bracket + [] によるネスト / $(( $[ a[ v[ [ の内部 2799 ## 'expr-brack-di' @ .count-bracket o [] によるネスト / d[ の内部 2800 ## 2801 ## '$(' @ check-dollar o $(command) 2802 ## 'cmdsub_nofork' @ check-dollar o ${ command; } 2803 ## 'cmd_brace' @ ctx-command/check-word-end o { command; } 2804 ## 2805 ## QUOTE = o ... 内部で quote 除去が有効 2806 ## QUOTE = x ... 内部で quote 除去は無効 2807 ## 2808 _ble_syntax_context_proc[CTX_EXPR]=ble/syntax:bash/ctx-expr 2809 ## @fn ble/syntax:bash/ctx-expr/.count-paren 2810 ## 算術式中の括弧の数 () を数えます。 2811 ## @var ntype 現在の算術式の入れ子の種類を指定します。 2812 ## @var char 括弧文字を指定します。 2813 function ble/syntax:bash/ctx-expr/.count-paren { 2814 if [[ $char == ')' ]]; then 2815 if [[ $ntype == '((' || $ntype == '$((' ]]; then 2816 if [[ $tail == '))'* ]]; then 2817 ((_ble_syntax_attr[i]=_ble_syntax_attr[inest])) 2818 ((i+=2)) 2819 ble/syntax/parse/nest-pop 2820 else 2821 # ((echo) > /dev/null) や $((echo) > /dev/null) などの 2822 # 紛らわしいサブシェル・コマンド置換だったとみなす。 2823 # それまでに算術式と思っていた部分については仕方がないのでそのまま。 2824 ((ctx=CTX_ARGX0, 2825 _ble_syntax_attr[i++]=_ble_syntax_attr[inest])) 2826 fi 2827 return 0 2828 elif [[ $ntype == expr-paren* ]]; then 2829 ((_ble_syntax_attr[i++]=ctx)) 2830 ble/syntax/parse/nest-pop 2831 return 0 2832 fi 2833 elif [[ $char == '(' ]]; then 2834 # determine nested ntype 2835 local ntype2= 2836 case $ntype in 2837 ('((') 2838 ntype2=expr-paren ;; 2839 ('$((') 2840 ntype2=expr-paren-ax ;; 2841 (expr-paren|expr-paren-ax|expr-paren-ai|expr-paren-di) 2842 ntype2=$ntype ;; 2843 ('$['|'a['|'v['|'d['|expr-brack|expr-brack-ai|expr-brack-di|'${'|'"${'|*) 2844 ble/util/assert 'false' "unexpected ntype='$ntype' here" ;; 2845 esac 2846 2847 ble/syntax/parse/nest-push "$CTX_EXPR" "$ntype2" 2848 ((_ble_syntax_attr[i++]=ctx)) 2849 return 0 2850 fi 2851 2852 return 1 2853 } 2854 ## @fn ble/syntax:bash/ctx-expr/.count-bracket 2855 ## 算術式中の括弧の数 [] を数えます。 2856 ## @var ntype 現在の算術式の入れ子の種類を指定します。 2857 ## @var char 括弧文字を指定します。 2858 function ble/syntax:bash/ctx-expr/.count-bracket { 2859 if [[ $char == ']' ]]; then 2860 if [[ $ntype == expr-brack* || $ntype == '$[' ]]; then 2861 # 算術式展開 $[...] や入れ子 ((a[...]=123)) などの場合。 2862 ((_ble_syntax_attr[i]=_ble_syntax_attr[inest])) 2863 ((i++)) 2864 ble/syntax/parse/nest-pop 2865 return 0 2866 elif [[ $ntype == [ad]'[' ]]; then 2867 ((_ble_syntax_attr[i++]=CTX_EXPR)) 2868 ble/syntax/parse/nest-pop 2869 if [[ $tail == ']='* ]]; then 2870 # a[...]=, a=([...]=) の場合 2871 ((i++)) 2872 tail=${text:i} ble/syntax:bash/check-tilde-expansion rhs 2873 elif ((_ble_bash>=30100)) && [[ $tail == ']+'* ]]; then 2874 ble/syntax/parse/set-lookahead 2 2875 if [[ $tail == ']+='* ]]; then 2876 # a[...]+=, a+=([...]+=) の場合 2877 ((i+=2)) 2878 tail=${text:i} ble/syntax:bash/check-tilde-expansion rhs 2879 fi 2880 else 2881 if [[ $ntype == 'a[' ]]; then 2882 # a[...]... という唯のコマンドの場合。 2883 if ((ctx==CTX_VRHS)); then 2884 # 例: arr[123]aaa 2885 ((ctx=CTX_CMDI,wtype=CTX_CMDI)) 2886 elif ((ctx==CTX_ARGVR)); then 2887 # 例: declare arr[123]aaa 2888 ((ctx=CTX_ARGVI,wtype=CTX_ARGVI)) 2889 elif ((ctx==CTX_ARGER)); then 2890 # 例: eval arr[123]aaa 2891 ((ctx=CTX_ARGEI,wtype=CTX_ARGEI)) 2892 fi 2893 else # ntype == 'd[' 2894 # '[...]...' という唯の値の場合。 2895 ((ctx=CTX_VALI,wtype=CTX_VALI)) 2896 fi 2897 fi 2898 return 0 2899 elif [[ $ntype == 'v[' ]]; then 2900 # ${v[]...} などの場合。 2901 ((_ble_syntax_attr[i++]=CTX_EXPR)) 2902 ble/syntax/parse/nest-pop 2903 return 0 2904 fi 2905 elif [[ $char == '[' ]]; then 2906 local ntype2= 2907 case $ntype in 2908 ('$['|'a['|'v[') 2909 ntype2=expr-brack-ai ;; 2910 ('d[') 2911 ntype2=expr-brack-di ;; 2912 (expr-brack|expr-brack-ai|expr-brack-di) 2913 ntype2=$ntype ;; 2914 ('(('|'$(('|expr-paren|expr-paren-ax|expr-paren-ai|expr-paren-di|'${'|'"${'|*) 2915 ble/util/assert 'false' "unexpected ntype='$ntype' here" ;; 2916 esac 2917 ble/syntax/parse/nest-push "$CTX_EXPR" "$ntype2" 2918 ((_ble_syntax_attr[i++]=ctx)) 2919 return 0 2920 fi 2921 2922 return 1 2923 } 2924 ## @fn ble/syntax:bash/ctx-expr/.count-brace 2925 ## 算術式中に閉じ波括弧 '}' が来たら算術式を抜けます。 2926 ## @var ntype 現在の算術式の入れ子の種類を指定します。 2927 ## @var char 括弧文字を指定します。 2928 function ble/syntax:bash/ctx-expr/.count-brace { 2929 if [[ $char == '}' ]]; then 2930 ((_ble_syntax_attr[i]=_ble_syntax_attr[inest])) 2931 ((i++)) 2932 ble/syntax/parse/nest-pop 2933 return 0 2934 fi 2935 2936 return 1 2937 } 2938 ## @fn ble/syntax:bash/ctx-expr/.check-plain-with-escape rex is_quote 2939 ## @var[in] ntype 2940 function ble/syntax:bash/ctx-expr/.check-plain-with-escape { 2941 local i0=$i 2942 ble/syntax:bash/check-plain-with-escape "$@" || return 1 2943 2944 if [[ $tail == '\'* ]]; then 2945 case $ntype in 2946 ('$(('|'$['|expr-paren-ax|'${'|'"${') 2947 _ble_syntax_attr[i0]=$ATTR_ERR ;; 2948 ('(('|expr-paren|expr-brack) 2949 if ((_ble_bash>=50100)); then 2950 _ble_syntax_attr[i0]=$ATTR_ERR 2951 fi ;; 2952 ('a['|'v['|expr-paren-ai|expr-brack-ai) 2953 if ((_ble_bash>=40400)); then 2954 _ble_syntax_attr[i0]=$ATTR_ERR 2955 fi ;; 2956 # ('d['|expr-paren-di|expr-brack-di) ;; # d[ (designated init) 内部では常に \ は OK 2957 esac 2958 fi 2959 2960 return 0 2961 } 2962 2963 function ble/syntax:bash/ctx-expr { 2964 # 式の中身 2965 local rex 2966 if rex='^[_a-zA-Z][_a-zA-Z0-9]*'; [[ $tail =~ $rex ]]; then 2967 local rematch=$BASH_REMATCH 2968 local ret; ble/syntax/highlight/vartype "$BASH_REMATCH" readvar:expr:global 2969 ((_ble_syntax_attr[i]=ret,i+=${#rematch})) 2970 return 0 2971 elif rex='^0[xX][0-9a-fA-F]*|^[0-9]+(#[_a-zA-Z0-9@]*)?'; [[ $tail =~ $rex ]]; then 2972 ((_ble_syntax_attr[i]=ATTR_VAR_NUMBER,i+=${#BASH_REMATCH})) 2973 return 0 2974 fi 2975 2976 local ntype 2977 ble/syntax/parse/nest-type 2978 if ble/syntax:bash/ctx-expr/.check-plain-with-escape "[^${_ble_syntax_bash_chars[ctx]}_a-zA-Z0-9]+" 1; then 2979 return 0 2980 elif [[ $tail == ['][()}']* ]]; then 2981 local char=${tail::1} 2982 if [[ $ntype == *'(' || $ntype == expr-paren* ]]; then 2983 # ntype = '((' # ((...)) 2984 # = '$((' # $((...)) 2985 # = 'expr-paren' # 式中の (..) 2986 # = 'expr-paren-ax' # $(( $[ 中の (..) 2987 # = 'expr-paren-ai' # a[ v[ 中の (..) 2988 # = 'expr-paren-di' # d[ 中の (..) 2989 ble/syntax:bash/ctx-expr/.count-paren && return 0 2990 elif [[ $ntype == *'[' || $ntype == expr-brack* ]]; then 2991 # ntype = 'a[' # a[...]= 2992 # = 'v[' # ${a[...]} 2993 # = 'd[' # a=([...]=) 2994 # = '$[' # $[...] 2995 # = 'expr-brack' # 式中の [...] 2996 # = 'expr-brack-ai' # $(( $[ a[ v[ 中の [...] 2997 # = 'expr-brack-di' # d[ 中の [...] 2998 ble/syntax:bash/ctx-expr/.count-bracket && return 0 2999 elif [[ $ntype == '${' || $ntype == '"${' ]]; then 3000 # ntype = '${' # ${var:offset:length} 3001 # = '"${' # "${var:offset:length}" 3002 ble/syntax:bash/ctx-expr/.count-brace && return 0 3003 else 3004 ble/util/assert 'false' "unexpected ntype=$ntype for arithmetic expression" 3005 fi 3006 3007 # 入れ子処理されなかった文字は通常文字として処理 3008 ((_ble_syntax_attr[i++]=ctx)) 3009 return 0 3010 elif ble/syntax:bash/check-quotes; then 3011 return 0 3012 elif ble/syntax:bash/check-dollar; then 3013 return 0 3014 elif ble/syntax:bash/starts-with-histchars; then 3015 # 恐ろしい事に数式中でも履歴展開が有効…。 3016 ble/syntax:bash/check-history-expansion || 3017 ((_ble_syntax_attr[i]=ctx,i++)) 3018 return 0 3019 fi 3020 3021 return 1 3022 } 3023 3024 #------------------------------------------------------------------------------ 3025 # ブレース展開 3026 3027 ## CTX_CONDI 及び CTX_RDRS の時は不活性化したブレース展開として振る舞う。 3028 ## CTX_RDRF 及び CTX_RDRD, CTX_RDRD2 の時は複数語に展開されるブレース展開はエラーなので、 3029 ## nest-push して解析だけ行いブレース展開であるということが確定した時点でエラーを設定する。 3030 3031 function ble/syntax:bash/check-brace-expansion { 3032 [[ $tail == '{'* ]] || return 1 3033 3034 local rex='^\{[-+a-zA-Z0-9.]*(\}?)' 3035 [[ $tail =~ $rex ]] 3036 local str=$BASH_REMATCH 3037 3038 local force_attr= inactive= 3039 3040 # 特定の文脈では完全に不活性 3041 # Note: {fd}> リダイレクトの先読みに合わせて、 3042 # 不活性であっても一気に読み取る必要がある。 3043 # cf ble/syntax:bash/starts-with-delimiter-or-redirect 3044 if [[ $- != *B* ]]; then 3045 inactive=1 3046 elif ((ctx==CTX_CONDI||ctx==CTX_CONDQ||ctx==CTX_RDRS||ctx==CTX_VRHS)); then 3047 inactive=1 3048 elif ((_ble_bash>=50300&&ctx==CTX_VALR)); then 3049 # bash-5.3 以降では arr=([9]={1..10}) 等のブレース展開は不活性 3050 inactive=1 3051 elif ((ctx==CTX_PATN||ctx==CTX_BRAX)); then 3052 local ntype; ble/syntax/parse/nest-type 3053 if [[ $ntype == glob_attr=* ]]; then 3054 force_attr=${ntype#*=} 3055 (((force_attr==CTX_RDRS||force_attr==CTX_VRHS||force_attr==CTX_ARGVR||force_attr==CTX_ARGER||force_attr==CTX_VALR)&&(inactive=1))) 3056 elif ((ctx==CTX_BRAX)); then 3057 local nctx; ble/syntax/parse/nest-ctx 3058 (((nctx==CTX_CONDI||octx==CTX_CONDQ)&&(inactive=1))) 3059 fi 3060 elif ((ctx==CTX_BRACE1||ctx==CTX_BRACE2)); then 3061 local ntype; ble/syntax/parse/nest-type 3062 if [[ $ntype == glob_attr=* ]]; then 3063 force_attr=${ntype#*=} 3064 fi 3065 fi 3066 3067 if [[ $inactive ]]; then 3068 ((_ble_syntax_attr[i]=${force_attr:-ctx},i+=${#str})) 3069 return 0 3070 fi 3071 3072 # ブレース展開がある時チルダ展開は無効化される 3073 # Note: CTX_VRHS 等のときは inactive なので此処には来ないので OK 3074 [[ ${_ble_syntax_bash_command_IsAssign[ctx]} ]] && 3075 ctx=${_ble_syntax_bash_command_IsAssign[ctx]} 3076 3077 # {a..b..c} の形式のブレース展開 3078 if rex='^\{(([-+]?[0-9]+)\.\.[-+]?[0-9]+|[a-zA-Z]\.\.[a-zA-Z])(\.\.[-+]?[0-9]+)?\}$'; [[ $str =~ $rex ]]; then 3079 if [[ $force_attr ]]; then 3080 ((_ble_syntax_attr[i]=force_attr,i+=${#str})) 3081 else 3082 local rematch1=${BASH_REMATCH[1]} 3083 local rematch2=${BASH_REMATCH[2]} 3084 local rematch3=${BASH_REMATCH[3]} 3085 local len2=${#rematch2}; ((len2||(len2=1))) 3086 local attr=$ATTR_BRACE 3087 if ((ctx==CTX_RDRF||ctx==CTX_RDRD||ctx==CTX_RDRD2)); then 3088 # リダイレクトで複数語に展開される時はエラー 3089 local lhs=${rematch1::len2} rhs=${rematch1:len2+2} 3090 if [[ $rematch2 ]]; then 3091 local lhs1=$((10#0${lhs#[-+]})); [[ $lhs == -* ]] && ((lhs1=-lhs1)) 3092 local rhs1=$((10#0${rhs#[-+]})); [[ $rhs == -* ]] && ((rhs1=-rhs1)) 3093 lhs=$lhs1 rhs=$rhs1 3094 fi 3095 [[ $lhs != "$rhs" ]] && ((attr=ATTR_ERR)) 3096 fi 3097 3098 ((_ble_syntax_attr[i++]=attr)) 3099 ((_ble_syntax_attr[i]=ctx,i+=len2, 3100 _ble_syntax_attr[i]=ATTR_BRACE,i+=2, 3101 _ble_syntax_attr[i]=ctx,i+=${#rematch1}-len2-2)) 3102 if [[ $rematch3 ]]; then 3103 ((_ble_syntax_attr[i]=ATTR_BRACE,i+=2, 3104 _ble_syntax_attr[i]=ctx,i+=${#rematch3}-2)) 3105 fi 3106 ((_ble_syntax_attr[i++]=attr)) 3107 fi 3108 3109 return 0 3110 fi 3111 3112 # それ以外 3113 # Note: {aa},bb} は {"aa}","bb"} と解釈されるので、 3114 # ここでは終端の "}" の有無に拘らず nest-push する。 3115 local ntype= 3116 ((ctx==CTX_RDRF||ctx==CTX_RDRD||ctx==CTX_RDRD2)) && force_attr=$ctx 3117 [[ $force_attr ]] && ntype="glob_attr=$force_attr" 3118 ble/syntax/parse/nest-push "$CTX_BRACE1" "$ntype" 3119 local len=$((${#str}-1)) 3120 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_BRACE}, 3121 len&&(_ble_syntax_attr[i]=${force_attr:-ctx},i+=len))) 3122 3123 return 0 3124 } 3125 3126 # 文脈 CTX_BRAX (brace expansion) 3127 _ble_syntax_context_proc[CTX_BRACE1]=ble/syntax:bash/ctx-brace-expansion 3128 _ble_syntax_context_proc[CTX_BRACE2]=ble/syntax:bash/ctx-brace-expansion 3129 _ble_syntax_context_end[CTX_BRACE1]=ble/syntax:bash/ctx-brace-expansion.end 3130 _ble_syntax_context_end[CTX_BRACE2]=ble/syntax:bash/ctx-brace-expansion.end 3131 function ble/syntax:bash/ctx-brace-expansion { 3132 if [[ $tail == '}'* ]] && ((ctx==CTX_BRACE2)); then 3133 local force_attr= 3134 local ntype; ble/syntax/parse/nest-type 3135 [[ $ntype == glob_attr=* ]] && force_attr=$ATTR_ERR # ※${ntype#*=} ではなくエラー 3136 3137 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_BRACE})) 3138 ble/syntax/parse/nest-pop 3139 return 0 3140 elif [[ $tail == ','* ]]; then 3141 local force_attr= 3142 local ntype; ble/syntax/parse/nest-type 3143 [[ $ntype == glob_attr=* ]] && force_attr=${ntype#*=} 3144 3145 ((_ble_syntax_attr[i++]=${force_attr:-ATTR_BRACE})) 3146 ((ctx=CTX_BRACE2)) 3147 return 0 3148 fi 3149 3150 local chars=",${_ble_syntax_bash_chars[CTX_ARGI]//'~:'}" 3151 ((ctx==CTX_BRACE2)) && chars="}$chars" 3152 ble/syntax:bash/cclass/update/reorder chars 3153 if ble/syntax:bash/check-plain-with-escape "[^$chars]+"; then 3154 return 0 3155 elif ble/syntax:bash/check-process-subst; then 3156 return 0 3157 elif ble/syntax:bash/check-quotes; then 3158 return 0 3159 elif ble/syntax:bash/check-dollar; then 3160 return 0 3161 elif ble/syntax:bash/check-glob; then 3162 return 0 3163 elif ble/syntax:bash/check-brace-expansion; then 3164 return 0 3165 elif ble/syntax:bash/starts-with-histchars; then 3166 ble/syntax:bash/check-history-expansion || 3167 ((_ble_syntax_attr[i++]=ctx)) 3168 return 0 3169 fi 3170 3171 return 1 3172 } 3173 function ble/syntax:bash/ctx-brace-expansion.end { 3174 if ((i==${#text})) || ble/syntax:bash/check-word-end/is-delimiter; then 3175 ble/syntax/parse/nest-pop 3176 ble/syntax/parse/check-end 3177 return "$?" 3178 fi 3179 3180 return 0 3181 } 3182 3183 #------------------------------------------------------------------------------ 3184 # チルダ展開 3185 3186 # ${_ble_syntax_bash_chars[CTX_ARGI]} により読み取りを行っている 3187 # ctx-command ctx-values ctx-conditions ctx-redirect から呼び出される事を想定している。 3188 3189 ## @fn ble/syntax:bash/check-tilde-expansion 3190 ## チルダ展開を検出して処理します。 3191 ## 単語の始めの ~、または変数代入形式の単語の途中の :~ または 3192 ## パラメータ展開中の word の始めの ~ の処理を行います。 3193 function ble/syntax:bash/check-tilde-expansion { 3194 [[ $tail == ['~:']* ]] || return 1 3195 3196 # @var rhs_enabled 3197 # 変数代入形式の文脈の右辺でチルダ展開が有効かどうか。set -o posix では限ら 3198 # れた文脈のみで有効になる。 3199 local rhs_enabled= 3200 { ((ctx==CTX_VRHS||ctx==CTX_ARGVR||ctx==CTX_VALR||ctx==CTX_ARGER)) || 3201 ! ble/base/is-POSIXLY_CORRECT; } && rhs_enabled=1 3202 3203 local tilde_enabled=$((i==wbegin||ctx==CTX_PWORD)) 3204 [[ $1 == rhs && $rhs_enabled ]] && tilde_enabled=1 # = の直後 3205 3206 if [[ $tail == ':'* ]]; then 3207 _ble_syntax_attr[i++]=$ctx 3208 3209 # 変数代入の右辺、または、その一つ下の角括弧式のときチルダ展開が有効。 3210 if [[ $rhs_enabled ]]; then 3211 if ! ((tilde_enabled=_ble_syntax_bash_command_IsAssign[ctx])); then 3212 if ((ctx==CTX_BRAX)); then 3213 local nctx; ble/syntax/parse/nest-ctx 3214 ((tilde_enabled=_ble_syntax_bash_command_IsAssign[nctx])) 3215 fi 3216 fi 3217 fi 3218 3219 local tail=${text:i} 3220 [[ $tail == '~'* ]] || return 0 3221 fi 3222 3223 if ((tilde_enabled)); then 3224 local chars="${_ble_syntax_bash_chars[CTX_ARGI]}/:" 3225 # Note: pword の時は delimiters も除外したいので 3226 # _ble_syntax_bash_chars[CTX_PWORD] ではなく 3227 # _ble_syntax_bash_chars[CTX_ARGI] を修正して使う。 3228 ((ctx==CTX_PWORD)) && chars=${chars/'{'/'{}'} 3229 3230 ble/syntax:bash/cclass/update/reorder chars 3231 local delimiters="$_ble_term_IFS;|&)<>" 3232 local rex='^(~\+|~[^'$chars']*)([^'$delimiters'/:]?)'; [[ $tail =~ $rex ]] 3233 local str=${BASH_REMATCH[1]} 3234 3235 local path attr=$ctx 3236 builtin eval "path=$str" 3237 if [[ ! ${BASH_REMATCH[2]} && $path != "$str" ]]; then 3238 ((attr=ATTR_TILDE)) 3239 3240 if ((ctx==CTX_BRAX)); then 3241 # CTX_BRAX は単語先頭には来ないので、 3242 # ここに来るのは [[ $tail == ':~'* ]] だった時のみのはず。 3243 # このとき、各括弧式は : の直後でキャンセルする。 3244 ble/util/assert 'ble/util/unlocal tail; [[ $tail == ":~"* ]]' 3245 ble/syntax/parse/nest-pop 3246 fi 3247 else 3248 # ~+ で始まってかつ有効なチルダ展開ではない時 ~ まで後退 (#D1424) 3249 if [[ $str == '~+' ]]; then 3250 ble/syntax/parse/set-lookahead 3 3251 str='~' 3252 fi 3253 fi 3254 ((_ble_syntax_attr[i]=attr,i+=${#str})) 3255 else 3256 ((_ble_syntax_attr[i]=ctx,i++)) # skip tilde 3257 local chars=${_ble_syntax_bash_chars[CTX_ARGI]} 3258 ble/syntax:bash/check-plain-with-escape "[^$chars]+" # 追加(失敗してもOK) 3259 fi 3260 3261 return 0 3262 } 3263 3264 #------------------------------------------------------------------------------ 3265 # 変数代入の形式の単語 3266 # 3267 # 実は通常の引数であっても変数代入の形式をしているものは微妙に扱いが異なる。 3268 # 変数代入の形式の引数の右辺ではチルダ展開が有効である。 3269 # 3270 3271 # 変数代入形式の時に文脈を切り替える文脈値。実際に変数代入でなくても変数代入形 3272 # 式によるチルダ展開が有効である時には区別する必要がある。 3273 _ble_syntax_bash_command_CtxAssign[CTX_CMDI]=$CTX_VRHS 3274 _ble_syntax_bash_command_CtxAssign[CTX_COARGI]=$CTX_VRHS 3275 _ble_syntax_bash_command_CtxAssign[CTX_ARGVI]=$CTX_ARGVR 3276 _ble_syntax_bash_command_CtxAssign[CTX_ARGEI]=$CTX_ARGER 3277 _ble_syntax_bash_command_CtxAssign[CTX_ARGI]=$CTX_ARGQ 3278 _ble_syntax_bash_command_CtxAssign[CTX_FARGI3]=$CTX_FARGQ3 3279 _ble_syntax_bash_command_CtxAssign[CTX_CARGI1]=$CTX_CARGQ1 3280 _ble_syntax_bash_command_CtxAssign[CTX_CPATI]=$CTX_CPATQ 3281 _ble_syntax_bash_command_CtxAssign[CTX_VALI]=$CTX_VALQ 3282 _ble_syntax_bash_command_CtxAssign[CTX_CONDI]=$CTX_CONDQ 3283 3284 # 以下の配列はチルダ展開が有効な文脈を無効な文脈に切り替えるのに使っている。 3285 _ble_syntax_bash_command_IsAssign[CTX_VRHS]=$CTX_CMDI 3286 _ble_syntax_bash_command_IsAssign[CTX_ARGVR]=$CTX_ARGVI 3287 _ble_syntax_bash_command_IsAssign[CTX_ARGER]=$CTX_ARGEI 3288 _ble_syntax_bash_command_IsAssign[CTX_ARGQ]=$CTX_ARGI 3289 _ble_syntax_bash_command_IsAssign[CTX_FARGQ3]=$CTX_FARGI3 3290 _ble_syntax_bash_command_IsAssign[CTX_CARGQ1]=$CTX_CARGI1 3291 _ble_syntax_bash_command_IsAssign[CTX_CPATQ]=$CTX_CPATI 3292 _ble_syntax_bash_command_IsAssign[CTX_VALR]=$CTX_VALI 3293 _ble_syntax_bash_command_IsAssign[CTX_VALQ]=$CTX_VALI 3294 _ble_syntax_bash_command_IsAssign[CTX_CONDQ]=$CTX_CONDI 3295 3296 ## @fn ble/syntax:bash/check-variable-assignment 3297 ## @var[in] tail 3298 function ble/syntax:bash/check-variable-assignment { 3299 ((wbegin==i)) || return 1 3300 3301 # 値リストにおける [0]=value の形式の単語は特別に扱う。 3302 if ((ctx==CTX_VALI)) && [[ $tail == '['* ]]; then 3303 ((ctx=CTX_VALR)) 3304 ble/syntax/parse/nest-push "$CTX_EXPR" 'd[' 3305 # → ble/syntax:bash/ctx-expr/.count-bracket で抜ける 3306 ((_ble_syntax_attr[i++]=ctx)) 3307 return 0 3308 fi 3309 3310 [[ ${_ble_syntax_bash_command_CtxAssign[ctx]} ]] || return 1 3311 3312 # パターン一致 (var= var+= arr[ のどれか) 3313 local suffix='[=[]' 3314 ((_ble_bash>=30100)) && suffix=$suffix'|\+=?' 3315 local rex_assign="^([_a-zA-Z][_a-zA-Z0-9]*)($suffix)" 3316 [[ $tail =~ $rex_assign ]] || return 1 3317 local rematch=$BASH_REMATCH 3318 local rematch1=${BASH_REMATCH[1]} # for bash-3.1 ${#arr[n]} bug 3319 local rematch2=${BASH_REMATCH[2]} # for bash-3.1 ${#arr[n]} bug 3320 if [[ $rematch2 == '+' ]]; then 3321 # var+... 曖昧状態 3322 3323 # Note: + の次の文字が = でない時に此処に来るので、 3324 # + の次の文字まで先読みしたことになる。 3325 ble/syntax/parse/set-lookahead "$((${#rematch}+1))" 3326 3327 return 1 3328 fi 3329 3330 local variable_assign= 3331 if ((ctx==CTX_CMDI||ctx==CTX_ARGVI||ctx==CTX_ARGEI&&${#rematch2})); then 3332 # 変数代入のときは ctx は先に CTX_VRHS, CTX_ARGVR に変換する 3333 local ret; ble/syntax/highlight/vartype "$rematch1" global 3334 ((wtype=ATTR_VAR, 3335 _ble_syntax_attr[i]=ret, 3336 i+=${#rematch}, 3337 ${#rematch2}&&(_ble_syntax_attr[i-${#rematch2}]=CTX_EXPR), 3338 variable_assign=1, 3339 ctx=_ble_syntax_bash_command_CtxAssign[ctx])) 3340 else 3341 # 変数代入以外のときは = が現れて初めて CTX_ARGQ などに変換する 3342 ((_ble_syntax_attr[i]=ctx, 3343 i+=${#rematch})) 3344 fi 3345 3346 if [[ $rematch2 == '[' ]]; then 3347 # arr[ 3348 if [[ $variable_assign ]]; then 3349 i=$((i-1)) ble/syntax/parse/nest-push "$CTX_EXPR" 'a[' 3350 # → ble/syntax:bash/ctx-expr/.count-bracket で抜ける 3351 else 3352 ((i--)) 3353 tail=${text:i} ble/syntax:bash/check-glob assign 3354 # → ble/syntax:bash/check-glob 内で nest-push "$CTX_BRAX" 'a[' し、 3355 # → ble/syntax:bash/ctx-bracket-expression で抜けた後で = があれば文脈値設定 3356 fi 3357 elif [[ $rematch2 == *'=' ]]; then 3358 if [[ $variable_assign && ${text:i} == '('* ]]; then 3359 # var=( var+=( 3360 # * nest-pop した直後は未だ CTX_VRHS, CTX_ARGVR の続きになっている。 3361 # 例: a=(1 2)b=1 は a='(1 2)b=1' と解釈される。 3362 # 従って ctx (nest-pop 時の文脈) はそのまま (CTX_VRHS, CTX_ARGVR) にする。 3363 3364 ble/syntax:bash/ctx-values/enter 3365 ((_ble_syntax_attr[i++]=ATTR_DEL)) 3366 else 3367 # var=... var+=... 3368 [[ $variable_assign ]] || ((ctx=_ble_syntax_bash_command_CtxAssign[ctx])) 3369 if local tail=${text:i}; [[ $tail == '~'* ]]; then 3370 ble/syntax:bash/check-tilde-expansion rhs 3371 fi 3372 fi 3373 fi 3374 3375 return 0 3376 } 3377 3378 #------------------------------------------------------------------------------ 3379 # 文脈: コマンドライン 3380 3381 _ble_syntax_context_proc[CTX_ARGX]=ble/syntax:bash/ctx-command 3382 _ble_syntax_context_proc[CTX_ARGX0]=ble/syntax:bash/ctx-command 3383 _ble_syntax_context_proc[CTX_CMDX]=ble/syntax:bash/ctx-command 3384 _ble_syntax_context_proc[CTX_CMDX0]=ble/syntax:bash/ctx-command 3385 _ble_syntax_context_proc[CTX_CMDX1]=ble/syntax:bash/ctx-command 3386 _ble_syntax_context_proc[CTX_CMDXT]=ble/syntax:bash/ctx-command 3387 _ble_syntax_context_proc[CTX_CMDXC]=ble/syntax:bash/ctx-command 3388 _ble_syntax_context_proc[CTX_CMDXE]=ble/syntax:bash/ctx-command 3389 _ble_syntax_context_proc[CTX_CMDXD]=ble/syntax:bash/ctx-command 3390 _ble_syntax_context_proc[CTX_CMDXD0]=ble/syntax:bash/ctx-command 3391 _ble_syntax_context_proc[CTX_CMDXV]=ble/syntax:bash/ctx-command 3392 _ble_syntax_context_proc[CTX_ARGI]=ble/syntax:bash/ctx-command 3393 _ble_syntax_context_proc[CTX_ARGQ]=ble/syntax:bash/ctx-command 3394 _ble_syntax_context_proc[CTX_CMDI]=ble/syntax:bash/ctx-command 3395 _ble_syntax_context_proc[CTX_VRHS]=ble/syntax:bash/ctx-command 3396 _ble_syntax_context_proc[CTX_ARGVR]=ble/syntax:bash/ctx-command 3397 _ble_syntax_context_proc[CTX_ARGER]=ble/syntax:bash/ctx-command 3398 _ble_syntax_context_end[CTX_CMDI]=ble/syntax:bash/ctx-command/check-word-end 3399 _ble_syntax_context_end[CTX_ARGI]=ble/syntax:bash/ctx-command/check-word-end 3400 _ble_syntax_context_end[CTX_ARGQ]=ble/syntax:bash/ctx-command/check-word-end 3401 _ble_syntax_context_end[CTX_VRHS]=ble/syntax:bash/ctx-command/check-word-end 3402 _ble_syntax_context_end[CTX_ARGVR]=ble/syntax:bash/ctx-command/check-word-end 3403 _ble_syntax_context_end[CTX_ARGER]=ble/syntax:bash/ctx-command/check-word-end 3404 3405 # declare var=value / eval var=value 3406 _ble_syntax_context_proc[CTX_ARGVX]=ble/syntax:bash/ctx-command 3407 _ble_syntax_context_proc[CTX_ARGVI]=ble/syntax:bash/ctx-command 3408 _ble_syntax_context_end[CTX_ARGVI]=ble/syntax:bash/ctx-command/check-word-end 3409 _ble_syntax_context_proc[CTX_ARGEX]=ble/syntax:bash/ctx-command 3410 _ble_syntax_context_proc[CTX_ARGEI]=ble/syntax:bash/ctx-command 3411 _ble_syntax_context_end[CTX_ARGEI]=ble/syntax:bash/ctx-command/check-word-end 3412 3413 # for var in ... / case arg in 3414 _ble_syntax_context_proc[CTX_SARGX1]=ble/syntax:bash/ctx-command-compound-expect 3415 _ble_syntax_context_proc[CTX_FARGX1]=ble/syntax:bash/ctx-command-compound-expect 3416 _ble_syntax_context_proc[CTX_FARGX2]=ble/syntax:bash/ctx-command-compound-expect 3417 _ble_syntax_context_proc[CTX_FARGX3]=ble/syntax:bash/ctx-command 3418 _ble_syntax_context_proc[CTX_FARGI1]=ble/syntax:bash/ctx-command 3419 _ble_syntax_context_proc[CTX_FARGI2]=ble/syntax:bash/ctx-command 3420 _ble_syntax_context_proc[CTX_FARGI3]=ble/syntax:bash/ctx-command 3421 _ble_syntax_context_proc[CTX_FARGQ3]=ble/syntax:bash/ctx-command 3422 _ble_syntax_context_end[CTX_FARGI1]=ble/syntax:bash/ctx-command/check-word-end 3423 _ble_syntax_context_end[CTX_FARGI2]=ble/syntax:bash/ctx-command/check-word-end 3424 _ble_syntax_context_end[CTX_FARGI3]=ble/syntax:bash/ctx-command/check-word-end 3425 _ble_syntax_context_end[CTX_FARGQ3]=ble/syntax:bash/ctx-command/check-word-end 3426 _ble_syntax_context_proc[CTX_CARGX1]=ble/syntax:bash/ctx-command-compound-expect 3427 _ble_syntax_context_proc[CTX_CARGX2]=ble/syntax:bash/ctx-command-compound-expect 3428 _ble_syntax_context_proc[CTX_CPATX]=ble/syntax:bash/ctx-command-case-pattern-expect 3429 _ble_syntax_context_proc[CTX_CPATX0]=ble/syntax:bash/ctx-command-case-pattern-expect 3430 _ble_syntax_context_proc[CTX_CARGI1]=ble/syntax:bash/ctx-command 3431 _ble_syntax_context_proc[CTX_CARGQ1]=ble/syntax:bash/ctx-command 3432 _ble_syntax_context_proc[CTX_CARGI2]=ble/syntax:bash/ctx-command 3433 _ble_syntax_context_proc[CTX_CPATI]=ble/syntax:bash/ctx-command 3434 _ble_syntax_context_proc[CTX_CPATQ]=ble/syntax:bash/ctx-command 3435 _ble_syntax_context_end[CTX_CARGI1]=ble/syntax:bash/ctx-command/check-word-end 3436 _ble_syntax_context_end[CTX_CARGQ1]=ble/syntax:bash/ctx-command/check-word-end 3437 _ble_syntax_context_end[CTX_CARGI2]=ble/syntax:bash/ctx-command/check-word-end 3438 _ble_syntax_context_end[CTX_CPATI]=ble/syntax:bash/ctx-command/check-word-end 3439 _ble_syntax_context_end[CTX_CPATQ]=ble/syntax:bash/ctx-command/check-word-end 3440 _ble_syntax_context_proc[CTX_TARGX1]=ble/syntax:bash/ctx-command-time-expect 3441 _ble_syntax_context_proc[CTX_TARGX2]=ble/syntax:bash/ctx-command-time-expect 3442 _ble_syntax_context_proc[CTX_TARGI1]=ble/syntax:bash/ctx-command 3443 _ble_syntax_context_proc[CTX_TARGI2]=ble/syntax:bash/ctx-command 3444 _ble_syntax_context_end[CTX_TARGI1]=ble/syntax:bash/ctx-command/check-word-end 3445 _ble_syntax_context_end[CTX_TARGI2]=ble/syntax:bash/ctx-command/check-word-end 3446 _ble_syntax_context_proc[CTX_COARGX]=ble/syntax:bash/ctx-command-compound-expect 3447 _ble_syntax_context_end[CTX_COARGI]=ble/syntax:bash/ctx-coproc/check-word-end 3448 3449 ## @fn ble/syntax:bash/starts-with-delimiter-or-redirect 3450 ## 3451 ## 空白類、コマンド区切り文字、またはリダイレクトかどうかを判定する。 3452 ## 単語開始における 1>2 や {fd}>2 もリダイレクトと判定する。 3453 ## 3454 ## Note: ここで "1>2" や "{fd}>" に一致しなかったとしても、通常の文脈で 3455 ## "{fd}" や "1" 等の列が一気に読み取られる限り先読みの問題は発生しないはず。 3456 ## ブレース展開の解析は "{fd}" が一気に読み取られる様に注意深く実装する。 3457 ## 3458 function ble/syntax:bash/starts-with-delimiter-or-redirect { 3459 local delimiters=$_ble_syntax_bash_RexDelimiter 3460 local redirect=$_ble_syntax_bash_RexRedirect 3461 [[ ( $tail =~ ^$delimiters || $wbegin -lt 0 && $tail =~ ^$redirect || $wbegin -lt 0 && $tail == $'\\\n'* ) && $tail != ['<>']'('* ]] 3462 } 3463 function ble/syntax:bash/starts-with-delimiter { 3464 [[ $tail == ["$_ble_term_IFS;|&<>()"]* && $tail != ['<>']'('* ]] 3465 } 3466 function ble/syntax:bash/check-word-end/is-delimiter { 3467 local tail=${text:i} 3468 if [[ $tail == [!"$_ble_term_IFS;|&<>()"]* ]]; then 3469 return 1 3470 elif [[ $tail == ['<>']* ]]; then 3471 ble/syntax/parse/set-lookahead 2 3472 [[ $tail == ['<>']'('* ]] && return 1 3473 fi 3474 return 0 3475 } 3476 3477 ## @fn ble/syntax:bash/check-here-document-from spaces 3478 ## @param[in] spaces 3479 function ble/syntax:bash/check-here-document-from { 3480 local spaces=$1 3481 [[ $nparam && $spaces == *$'\n'* ]] || return 1 3482 local rex="$_ble_term_FS@([RI][QH][^$_ble_term_FS]*)(.*$)" && [[ $nparam =~ $rex ]] || return 1 3483 3484 # ヒアドキュメントの開始 3485 local rematch1=${BASH_REMATCH[1]} 3486 local rematch2=${BASH_REMATCH[2]} 3487 local padding=${spaces%%$'\n'*} 3488 ((_ble_syntax_attr[i]=ctx,i+=${#padding})) 3489 nparam=${nparam::${#nparam}-${#BASH_REMATCH}}${nparam:${#nparam}-${#rematch2}} 3490 ble/syntax/parse/nest-push "$CTX_HERE0" 3491 ((i++)) 3492 nparam=$rematch1 3493 return 0 3494 } 3495 3496 function ble/syntax:bash/ctx-coproc/.is-next-compound { 3497 # @var ahead 3498 # 現在位置 p の次の文字を参照したかどうか 3499 local p=$i ahead=1 tail=${text:i} 3500 3501 # 空白類は無視 3502 if local rex=$'^[ \t]+'; [[ $tail =~ $rex ]]; then 3503 ((p+=${#BASH_REMATCH})) 3504 ahead=1 tail=${text:p} 3505 fi 3506 3507 local is_compound= 3508 if [[ $tail == '('* ]]; then 3509 is_compound=1 3510 elif rex='^[a-z]+|^\[\[?|^[{}!]'; [[ $tail =~ $rex ]]; then 3511 local rematch=$BASH_REMATCH 3512 3513 ((p+=${#rematch})) 3514 [[ $rematch == ['{}!'] || $rematch == '[[' ]]; ahead=$? 3515 3516 rex='^(\[\[|for|select|case|if|while|until|fi|done|esac|then|elif|else|do|[{}!]|coproc|function)$' 3517 if [[ $rematch =~ $rex ]]; then 3518 if rex='^[;|&()'$_ble_term_IFS']|^$|^[<>]\(?' ahead=1; [[ ${text:p} =~ $rex ]]; then 3519 local rematch=$BASH_REMATCH 3520 ((p+=${#rematch})) 3521 [[ $rematch && $rematch != ['<>'] ]]; ahead=$? 3522 [[ $rematch != ['<>']'(' ]] && is_compound=1 3523 fi 3524 fi 3525 fi 3526 3527 # 先読みの設定 3528 ble/syntax/parse/set-lookahead "$((p+ahead-i))" 3529 [[ $is_compound ]] 3530 } 3531 function ble/syntax:bash/ctx-coproc/check-word-end { 3532 ble/util/assert '((ctx==CTX_COARGI))' 3533 3534 # 単語の中にいない時は抜ける 3535 ((wbegin<0)) && return 1 3536 3537 # 未だ続きがある場合は抜ける 3538 ble/syntax:bash/check-word-end/is-delimiter || return 1 3539 3540 local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i 3541 local word=${text:wbegin:wlen} 3542 local wt=$wtype 3543 3544 if local rex='^[_a-zA-Z][_a-zA-Z0-9]*$'; [[ $word =~ $rex ]]; then 3545 if ble/syntax:bash/ctx-coproc/.is-next-compound; then 3546 # 構文: 変数名 複合コマンド 3547 local attr=$ATTR_VAR 3548 3549 # alias だった場合は解釈が変わり得る。alias が厳密に変数名に展開された時 3550 # にのみ変数名と判定する。複合コマンド開始に展開された場合は通常処理にフォー 3551 # ルバック。それ以外はエラー。 3552 if ble/alias#active "$word"; then 3553 attr= 3554 local ret; ble/alias#expand "$word" 3555 case $word in 3556 # 通常処理にフォールバックする 3557 ('if'|'while'|'until'|'for'|'select'|'case'|'{'|'[[') ;; 3558 # 通常処理(構文エラー) 3559 ('fi'|'done'|'esac'|'then'|'elif'|'else'|'do'|'}'|'!'|'coproc'|'function'|'in') ;; 3560 (*) 3561 if ble/string#match "$word" '^[_a-zA-Z][_a-zA-Z0-9]*$'; then 3562 # 変数名に展開される場合はOK 3563 attr=$ATTR_CMD_ALIAS 3564 else 3565 attr=$ATTR_ERR 3566 fi 3567 esac 3568 fi 3569 3570 if [[ $attr ]]; then 3571 # Note: [_a-zA-Z0-9]+ は一回の読み取りの筈なので、 3572 # 此処で遡って代入しても問題ない筈。 3573 _ble_syntax_attr[wbegin]=$attr 3574 ((ctx=CTX_CMDXC,wtype=CTX_ARGVI)) 3575 ble/syntax/parse/word-pop 3576 return 0 3577 fi 3578 fi 3579 fi 3580 3581 ((ctx=CTX_CMDI,wtype=CTX_CMDX)) 3582 ble/syntax:bash/ctx-command/check-word-end 3583 } 3584 3585 ## @arr _ble_syntax_bash_command_EndCtx 3586 ## 単語が終了した後の次の文脈値を設定する。 3587 ## check-word-end で用いる。 3588 ## 3589 ## Note #1: time -p -- cmd は bash-4.2 以降 3590 ## bash-4.2 未満では -p の直後にすぐコマンドが来なければならない。 3591 ## 3592 _ble_syntax_bash_command_EndCtx=() 3593 _ble_syntax_bash_command_EndCtx[CTX_ARGI]=$CTX_ARGX 3594 _ble_syntax_bash_command_EndCtx[CTX_ARGQ]=$CTX_ARGX 3595 _ble_syntax_bash_command_EndCtx[CTX_ARGVI]=$CTX_ARGVX 3596 _ble_syntax_bash_command_EndCtx[CTX_ARGVR]=$CTX_ARGVX 3597 _ble_syntax_bash_command_EndCtx[CTX_ARGEI]=$CTX_ARGEX 3598 _ble_syntax_bash_command_EndCtx[CTX_ARGER]=$CTX_ARGEX 3599 _ble_syntax_bash_command_EndCtx[CTX_VRHS]=$CTX_CMDXV 3600 _ble_syntax_bash_command_EndCtx[CTX_FARGI1]=$CTX_FARGX2 3601 _ble_syntax_bash_command_EndCtx[CTX_FARGI2]=$CTX_FARGX3 3602 _ble_syntax_bash_command_EndCtx[CTX_FARGI3]=$CTX_FARGX3 3603 _ble_syntax_bash_command_EndCtx[CTX_FARGQ3]=$CTX_FARGX3 3604 _ble_syntax_bash_command_EndCtx[CTX_CARGI1]=$CTX_CARGX2 3605 _ble_syntax_bash_command_EndCtx[CTX_CARGQ1]=$CTX_CARGX2 3606 _ble_syntax_bash_command_EndCtx[CTX_CARGI2]=$CTX_CASE 3607 _ble_syntax_bash_command_EndCtx[CTX_CPATI]=$CTX_CPATX0 3608 _ble_syntax_bash_command_EndCtx[CTX_CPATQ]=$CTX_CPATX0 3609 _ble_syntax_bash_command_EndCtx[CTX_TARGI1]=$((_ble_bash>=40200?CTX_TARGX2:CTX_CMDXT)) #1 3610 _ble_syntax_bash_command_EndCtx[CTX_TARGI2]=$CTX_CMDXT 3611 3612 ## @arr _ble_syntax_bash_command_EndWtype[wtype] 3613 ## 実際に tree 登録する wtype を指定します。 3614 ## ※解析中の wtype には解析開始時の wtype が入っていることに注意する。 3615 _ble_syntax_bash_command_EndWtype[CTX_ARGX]=$CTX_ARGI 3616 _ble_syntax_bash_command_EndWtype[CTX_ARGX0]=$CTX_ARGI 3617 _ble_syntax_bash_command_EndWtype[CTX_ARGVX]=$CTX_ARGVI 3618 _ble_syntax_bash_command_EndWtype[CTX_ARGEX]=$CTX_ARGEI 3619 _ble_syntax_bash_command_EndWtype[CTX_CMDX]=$CTX_CMDI 3620 _ble_syntax_bash_command_EndWtype[CTX_CMDX0]=$CTX_CMDX0 3621 _ble_syntax_bash_command_EndWtype[CTX_CMDX1]=$CTX_CMDI 3622 _ble_syntax_bash_command_EndWtype[CTX_CMDXT]=$CTX_CMDI 3623 _ble_syntax_bash_command_EndWtype[CTX_CMDXC]=$CTX_CMDI 3624 _ble_syntax_bash_command_EndWtype[CTX_CMDXE]=$CTX_CMDI 3625 _ble_syntax_bash_command_EndWtype[CTX_CMDXD]=$CTX_CMDI 3626 _ble_syntax_bash_command_EndWtype[CTX_CMDXD0]=$CTX_CMDI 3627 _ble_syntax_bash_command_EndWtype[CTX_CMDXV]=$CTX_CMDI 3628 _ble_syntax_bash_command_EndWtype[CTX_FARGX1]=$CTX_FARGI1 # 変数名 3629 _ble_syntax_bash_command_EndWtype[CTX_SARGX1]=$CTX_ARGI 3630 _ble_syntax_bash_command_EndWtype[CTX_FARGX2]=$CTX_FARGI2 # in 3631 _ble_syntax_bash_command_EndWtype[CTX_FARGX3]=$CTX_ARGI # in 3632 _ble_syntax_bash_command_EndWtype[CTX_CARGX1]=$CTX_ARGI 3633 _ble_syntax_bash_command_EndWtype[CTX_CARGX2]=$CTX_CARGI2 # in 3634 _ble_syntax_bash_command_EndWtype[CTX_CPATX]=$CTX_CPATI 3635 _ble_syntax_bash_command_EndWtype[CTX_CPATX0]=$CTX_CPATI 3636 _ble_syntax_bash_command_EndWtype[CTX_TARGX1]=$CTX_ARGI # -p 3637 _ble_syntax_bash_command_EndWtype[CTX_TARGX2]=$CTX_ARGI # -- 3638 3639 ## @arr _ble_syntax_bash_command_Expect 3640 ## 3641 ## 許容するコマンドの種類を表す正規表現を設定する。 3642 ## check-word-end で用いる。 3643 ## 配列 _ble_syntax_bash_command_bwtype の設定と対応している必要がある。 3644 ## 3645 ## * 前提: 予約語のみに一致する 3646 ## この配列が設定されている文脈値については、 3647 ## 既定でコマンドの属性は ATTR_ERR にする。 3648 ## 許容するコマンドは何れも予約語なので、 3649 ## 許容された暁には自動的に ATTR_KEYWORD で上書きされるのでOK 3650 ## 3651 ## 予約語以外に一致する時には、 3652 ## 明示的に属性値 ATTR_ERR をキャンセルする必要がある。 3653 ## 3654 _ble_syntax_bash_command_Expect=() 3655 _ble_syntax_bash_command_Expect[CTX_CMDXC]='^(\(|\{|\(\(|\[\[|for|select|case|if|while|until)$' 3656 _ble_syntax_bash_command_Expect[CTX_CMDXE]='^(\}|fi|done|esac|then|elif|else|do)$' 3657 _ble_syntax_bash_command_Expect[CTX_CMDXD]='^(\{|do)$' 3658 _ble_syntax_bash_command_Expect[CTX_CMDXD0]='^(\{|do)$' 3659 3660 ## @fn ble/syntax:bash/ctx-command/check-word-end 3661 ## @var[in,out] ctx 3662 ## @var[in,out] wbegin 3663 ## @var[in,out] 他 3664 function ble/syntax:bash/ctx-command/check-word-end { 3665 # 単語の中にいない時は抜ける 3666 ((wbegin<0)) && return 1 3667 3668 # 未だ続きがある場合は抜ける 3669 ble/syntax:bash/check-word-end/is-delimiter || return 1 3670 3671 local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i 3672 local word=${text:wbegin:wlen} 3673 local stat_wt=$wtype # 単語解析中の wtype 3674 3675 [[ ${_ble_syntax_bash_command_EndWtype[stat_wt]} ]] && 3676 wtype=${_ble_syntax_bash_command_EndWtype[stat_wt]} 3677 local rex_expect_command=${_ble_syntax_bash_command_Expect[stat_wt]} 3678 if [[ $rex_expect_command ]]; then 3679 # 特定のコマンドのみを受け付ける文脈 3680 [[ $word =~ $rex_expect_command ]] || ((wtype=CTX_CMDX0)) 3681 elif ((stat_wt==CTX_ARGX0||stat_wt==CTX_CPATX0)); then 3682 ((wtype=ATTR_ERR)) 3683 elif ((stat_wt==CTX_CMDX1)); then 3684 local rex='^(then|elif|else|do|\}|done|fi|esac)$' 3685 [[ $word =~ $rex ]] && ((wtype=CTX_CMDX0)) 3686 fi 3687 local tree_wt=$wtype # 実際に単語として登録された wtype 3688 ble/syntax/parse/word-pop 3689 3690 if ((ctx==CTX_CMDI)); then 3691 local ret 3692 ble/alias#expand "$word"; local word_expanded=$ret 3693 3694 # キーワードの処理 3695 if ((tree_wt==CTX_CMDX0)); then 3696 ((_ble_syntax_attr[wbeg]=ATTR_ERR,ctx=CTX_ARGX)) 3697 return 0 3698 elif ((stat_wt!=CTX_CMDXV)); then # Note: 変数代入の直後はキーワードは処理しない 3699 local processed= 3700 case $word_expanded in 3701 ('[[') 3702 # 条件コマンド開始 3703 ble/syntax/parse/touch-updated-attr "$wbeg" 3704 ((_ble_syntax_attr[wbeg]=ATTR_DEL, 3705 ctx=_ble_bash>=50200?CTX_CMDXE:CTX_ARGX0)) 3706 3707 ble/syntax/parse/word-cancel # 単語 "[[" (とその内部のノード全て) を削除 3708 if [[ $word == '[[' ]]; then 3709 # "[[" は一度角括弧式として読み取られるので、その情報を削除する。 3710 _ble_syntax_attr[wbeg+1]= # 角括弧式として着色されているのを消去 3711 fi 3712 3713 i=$wbeg ble/syntax/parse/nest-push "$CTX_CONDX" 3714 3715 # workaround: word "[[" を nest 内部に設置し直す 3716 i=$wbeg ble/syntax/parse/word-push "$CTX_CMDI" "$wbeg" 3717 ble/syntax/parse/word-pop 3718 return 0 ;; 3719 ('time') ((ctx=CTX_TARGX1)); processed=keyword ;; 3720 ('!') ((ctx=CTX_CMDXT)) ; processed=keyword ;; 3721 ('if'|'while'|'until') ((ctx=CTX_CMDX1)) ; processed=begin ;; 3722 ('for') ((ctx=CTX_FARGX1)); processed=begin ;; 3723 ('select') ((ctx=CTX_SARGX1)); processed=begin ;; 3724 ('case') ((ctx=CTX_CARGX1)); processed=begin ;; 3725 ('{') 3726 # 単語属性の再設定 3727 ble/syntax/parse/touch-updated-attr "$wbeg" 3728 if ((stat_wt==CTX_CMDXD||stat_wt==CTX_CMDXD0)); then 3729 attr=$ATTR_KEYWORD_MID # "for ...; {" などの時 3730 else 3731 attr=$ATTR_KEYWORD_BEGIN 3732 fi 3733 ((_ble_syntax_attr[wbeg]=attr)) 3734 3735 # 単語削除&入れ子&単語再設置 3736 ble/syntax/parse/word-cancel 3737 ((ctx=CTX_CMDXE)) 3738 i=$wbeg ble/syntax/parse/nest-push "$CTX_CMDX1" 'cmd_brace' 3739 i=$wbeg ble/syntax/parse/word-push "$CTX_CMDI" "$wbeg" 3740 ble/syntax/parse/word-pop 3741 return 0 ;; 3742 ('then'|'elif'|'else'|'do') ((ctx=CTX_CMDX1)); processed=middle ;; 3743 ('done'|'fi'|'esac') ((ctx=CTX_CMDXE)); processed=end ;; 3744 ('}') 3745 if local ntype; ble/syntax/parse/nest-type; [[ $ntype == 'cmd_brace' ]]; then 3746 ble/syntax/parse/touch-updated-attr "$wbeg" 3747 ((_ble_syntax_attr[wbeg]=ATTR_KEYWORD_END)) 3748 ble/syntax/parse/nest-pop 3749 else 3750 ble/syntax/parse/touch-updated-attr "$wbeg" 3751 ((_ble_syntax_attr[wbeg]=ATTR_ERR)) 3752 ((ctx=CTX_CMDXE)) 3753 fi 3754 return 0 ;; 3755 ('coproc') 3756 if ((_ble_bash>=40000)); then 3757 if ble/syntax:bash/ctx-coproc/.is-next-compound; then 3758 ((ctx=CTX_CMDXC)) 3759 else 3760 ((ctx=CTX_COARGX)) 3761 fi 3762 processed=keyword 3763 fi ;; 3764 ('function') 3765 ((ctx=CTX_ARGX)) 3766 local isfuncsymx=$'\t\n'' "$&'\''();<>\`|' rex_space=$'[ \t]' rex 3767 if rex="^$rex_space+" && [[ ${text:i} =~ $rex ]]; then 3768 ((_ble_syntax_attr[i]=CTX_ARGX,i+=${#BASH_REMATCH},ctx=CTX_ARGX)) 3769 if rex="^([^#$isfuncsymx][^$isfuncsymx]*)($rex_space*)(\(\(|\($rex_space*\)?)?" && [[ ${text:i} =~ $rex ]]; then 3770 local rematch1=${BASH_REMATCH[1]} 3771 local rematch2=${BASH_REMATCH[2]} 3772 local rematch3=${BASH_REMATCH[3]} 3773 ((_ble_syntax_attr[i]=ATTR_FUNCDEF,i+=${#rematch1}, 3774 ${#rematch2}&&(_ble_syntax_attr[i]=CTX_CMDX1,i+=${#rematch2}))) 3775 3776 if [[ $rematch3 == '('*')' ]]; then 3777 ((_ble_syntax_attr[i]=ATTR_DEL,i+=${#rematch3},ctx=CTX_CMDXC)) 3778 elif ((_ble_bash>=40200)) && [[ $rematch3 == '((' ]]; then 3779 ble/syntax/parse/set-lookahead 2 3780 ((ctx=CTX_CMDXC)) 3781 elif [[ $rematch3 == '('* ]]; then 3782 ((_ble_syntax_attr[i]=ATTR_ERR,ctx=CTX_ARGX0)) 3783 ble/syntax/parse/nest-push "$CTX_CMDX1" '(' 3784 ((${#rematch3}>=2&&(_ble_syntax_attr[i+1]=CTX_CMDX1),i+=${#rematch3})) 3785 else 3786 ((ctx=CTX_CMDXC)) 3787 fi 3788 processed=keyword 3789 fi 3790 fi 3791 [[ $processed ]] || ((_ble_syntax_attr[i-1]=ATTR_ERR)) ;; 3792 esac 3793 3794 if [[ $processed ]]; then 3795 local attr= 3796 case $processed in 3797 (keyword) attr=$ATTR_KEYWORD ;; 3798 (begin) attr=$ATTR_KEYWORD_BEGIN ;; 3799 (end) attr=$ATTR_KEYWORD_END ;; 3800 (middle) attr=$ATTR_KEYWORD_MID ;; 3801 esac 3802 if [[ $attr ]]; then 3803 ble/syntax/parse/touch-updated-attr "$wbeg" 3804 ((_ble_syntax_attr[wbeg]=attr)) 3805 fi 3806 3807 return 0 3808 fi 3809 fi 3810 3811 # 関数定義である可能性を考え stat を置かず読み取る 3812 ((ctx=CTX_ARGX)) 3813 if local rex='^([ ]*)(\([ ]*\)?)?'; [[ ${text:i} =~ $rex && $BASH_REMATCH ]]; then 3814 3815 # for bash-3.1 ${#arr[n]} bug 3816 local rematch1=${BASH_REMATCH[1]} 3817 local rematch2=${BASH_REMATCH[2]} 3818 3819 if [[ $rematch2 == '('*')' ]]; then 3820 # case: /hoge ( *)/ 関数定義 (単語の種類 wtype を変更) 3821 # 上方の ble/syntax/parse/word-pop で設定した値を書き換え。 3822 # Note: 単語の種類が CTX_CMDX0 の時はそのままにする。 3823 ((tree_wt==CTX_CMDX0)) || 3824 _ble_syntax_tree[i-1]="$ATTR_FUNCDEF ${_ble_syntax_tree[i-1]#* }" 3825 3826 ((_ble_syntax_attr[i]=CTX_CMDX1,i+=${#rematch1}, 3827 _ble_syntax_attr[i]=ATTR_DEL,i+=${#rematch2}, 3828 ctx=CTX_CMDXC)) 3829 elif [[ $rematch2 == '('* ]]; then 3830 # case: /hoge \( */ 括弧が閉じていない場合: 3831 # 仕方がないので extglob 括弧と思って取り敢えず解析する 3832 ((_ble_syntax_attr[i]=CTX_ARGX0,i+=${#rematch1}, 3833 _ble_syntax_attr[i]=ATTR_ERR, 3834 ctx=CTX_ARGX0)) 3835 ble/syntax/parse/nest-push "$CTX_PATN" 3836 ((${#rematch2}>=2&&(_ble_syntax_attr[i+1]=CTX_CMDXC), 3837 i+=${#rematch2})) 3838 else 3839 # case: /hoge */ 恐らくコマンド 3840 ((_ble_syntax_attr[i]=CTX_ARGX,i+=${#rematch1})) 3841 fi 3842 fi 3843 3844 # 引数の取り扱いが特別な builtin 3845 case $word_expanded in 3846 ('declare'|'readonly'|'typeset'|'local'|'export'|'alias') 3847 ((ctx=CTX_ARGVX)) ;; 3848 ('eval') 3849 ((ctx=CTX_ARGEX)) ;; 3850 esac 3851 3852 return 0 3853 fi 3854 3855 if ((ctx==CTX_FARGI2)); then 3856 # for name do ...; done 3857 if [[ $word == do ]]; then 3858 ((ctx=CTX_CMDX1)) 3859 return 0 3860 fi 3861 fi 3862 3863 if ((ctx==CTX_FARGI2||ctx==CTX_CARGI2)); then 3864 # for name in ... / case value in 3865 if [[ $word != in ]]; then 3866 ble/syntax/parse/touch-updated-attr "$wbeg" 3867 ((_ble_syntax_attr[wbeg]=ATTR_ERR)) 3868 fi 3869 fi 3870 3871 if ((_ble_syntax_bash_command_EndCtx[ctx])); then 3872 ((ctx=_ble_syntax_bash_command_EndCtx[ctx])) 3873 fi 3874 3875 return 0 3876 } 3877 3878 ## @arr _ble_syntax_bash_command_Opt 3879 ## その場でコマンドが終わっても良いかどうかを設定する。 3880 ## .check-delimiter-or-redirect で用いる。 3881 _ble_syntax_bash_command_Opt=() 3882 _ble_syntax_bash_command_Opt[CTX_ARGX]=1 3883 _ble_syntax_bash_command_Opt[CTX_ARGX0]=1 3884 _ble_syntax_bash_command_Opt[CTX_ARGVX]=1 3885 _ble_syntax_bash_command_Opt[CTX_ARGEX]=1 3886 _ble_syntax_bash_command_Opt[CTX_CMDX0]=1 3887 _ble_syntax_bash_command_Opt[CTX_CMDXV]=1 3888 _ble_syntax_bash_command_Opt[CTX_CMDXE]=1 3889 _ble_syntax_bash_command_Opt[CTX_CMDXD0]=1 3890 3891 _ble_syntax_bash_is_command_form_for= 3892 3893 function ble/syntax:bash/ctx-command/.check-delimiter-or-redirect { 3894 if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs || $wbegin -lt 0 && $tail == $'\\\n'* ]]; then 3895 # 空白 or \ + 改行 3896 3897 local spaces=$BASH_REMATCH 3898 if [[ $tail == $'\\\n'* ]]; then 3899 # \ + 改行は単純に無視 3900 spaces=$'\\\n' 3901 elif [[ $spaces == *$'\n'* ]]; then 3902 # 改行がある場合: ヒアドキュメントの確認 / 改行による文脈更新 3903 ble/syntax:bash/check-here-document-from "$spaces" && return 0 3904 if ((ctx==CTX_ARGX||ctx==CTX_ARGX0||ctx==CTX_ARGVX||ctx==CTX_ARGEX||ctx==CTX_CMDX0||ctx==CTX_CMDXV||ctx==CTX_CMDXT||ctx==CTX_CMDXE)); then 3905 ((ctx=CTX_CMDX)) 3906 elif ((ctx==CTX_FARGX2||ctx==CTX_FARGX3||ctx==CTX_CMDXD0)); then 3907 ((ctx=CTX_CMDXD)) 3908 fi 3909 fi 3910 3911 # ctx はそのままで素通り 3912 ((_ble_syntax_attr[i]=ctx,i+=${#spaces})) 3913 return 0 3914 3915 elif [[ $tail =~ ^$_ble_syntax_bash_RexRedirect ]]; then 3916 # リダイレクト (& 単体の解釈より優先する) 3917 3918 # for bash-3.1 ${#arr[n]} bug ... 一旦 rematch1 に入れてから ${#rematch1} で文字数を得る。 3919 local len=${#BASH_REMATCH} 3920 local rematch1=${BASH_REMATCH[1]} 3921 local rematch3=${BASH_REMATCH[3]} 3922 ((_ble_syntax_attr[i]=ATTR_DEL, 3923 ${#rematch1}<len&&(_ble_syntax_attr[i+${#rematch1}]=CTX_ARGX))) 3924 if ((ctx==CTX_CMDX||ctx==CTX_CMDX1||ctx==CTX_CMDXT)); then 3925 ((ctx=CTX_CMDXV)) 3926 elif ((ctx==CTX_CMDXC||ctx==CTX_CMDXD||ctx==CTX_CMDXD0)); then 3927 ((ctx=CTX_CMDXV, 3928 _ble_syntax_attr[i]=ATTR_ERR)) 3929 elif ((ctx==CTX_CMDXE)); then 3930 ((ctx=CTX_CMDX0)) 3931 elif ((ctx==CTX_FARGX3)); then 3932 ((_ble_syntax_attr[i]=ATTR_ERR)) 3933 fi 3934 3935 if [[ ${text:i+len} != [!$'\n|&()']* ]]; then 3936 # リダイレクトがその場で終わるときはそもそも nest-push せずエラー。 3937 # Note: 上の判定の文字集合は _ble_syntax_bash_RexDelimiter の部分集合。 3938 # 但し、空白類および <> はリダイレクトに含まれ得るので許容する。 3939 ((_ble_syntax_attr[i+len-1]=ATTR_ERR)) 3940 else 3941 if [[ $rematch3 == '>&' ]]; then 3942 ble/syntax/parse/nest-push "$CTX_RDRD2" "$rematch3" 3943 elif [[ $rematch1 == *'&' ]]; then 3944 ble/syntax/parse/nest-push "$CTX_RDRD" "$rematch3" 3945 elif [[ $rematch1 == *'<<<' ]]; then 3946 ble/syntax/parse/nest-push "$CTX_RDRS" "$rematch3" 3947 elif [[ $rematch1 == *\<\< ]]; then 3948 # Note: emacs bug workaround 3949 # '<<' と書くと何故か Emacs がヒアドキュメントと 3950 # 勘違いする様になったので仕方なく \<\< とする。 3951 ble/syntax/parse/nest-push "$CTX_RDRH" "$rematch3" 3952 elif [[ $rematch1 == *\<\<- ]]; then 3953 ble/syntax/parse/nest-push "$CTX_RDRI" "$rematch3" 3954 else 3955 ble/syntax/parse/nest-push "$CTX_RDRF" "$rematch3" 3956 fi 3957 fi 3958 ((i+=len)) 3959 return 0 3960 elif local rex='^(&&|\|[|&]?)|^;(;&?|&)|^[;&]' 3961 ((_ble_bash<40000)) && rex='^(&&|\|\|?)|^;(;)|^[;&]' 3962 [[ $tail =~ $rex ]] 3963 then 3964 # 制御演算子 && || | & ; |& ;; ;;& ;& 3965 3966 if [[ $BASH_REMATCH == ';' ]]; then 3967 if ((ctx==CTX_FARGX2||ctx==CTX_FARGX3||ctx==CTX_CMDXD0)); then 3968 ((_ble_syntax_attr[i++]=ATTR_DEL,ctx=CTX_CMDXD)) 3969 return 0 3970 elif ((ctx==CTX_CMDXT)); then 3971 # Note #D0592: time ; 及び ! ; に限っては、エラーにならずに直後に CTX_CMDXE になる 3972 # Note #D1477: Bash 4.4 で振る舞いが変わる。 3973 ((_ble_syntax_attr[i++]=ATTR_DEL,ctx=_ble_bash>=40400?CTX_CMDX:CTX_CMDXE)) 3974 return 0 3975 fi 3976 fi 3977 3978 # for bash-3.1 ${#arr[n]} bug 3979 local rematch1=${BASH_REMATCH[1]} rematch2=${BASH_REMATCH[2]} 3980 ((_ble_syntax_attr[i]=ATTR_DEL, 3981 (_ble_syntax_bash_command_Opt[ctx]||ctx==CTX_CMDX&&${#rematch2})|| 3982 (_ble_syntax_attr[i]=ATTR_ERR))) 3983 3984 ((ctx=${#rematch1}?CTX_CMDX1:( 3985 ${#rematch2}?CTX_CASE: 3986 CTX_CMDX))) 3987 ((i+=${#BASH_REMATCH})) 3988 return 0 3989 elif local rex='^\(\(?' && [[ $tail =~ $rex ]]; then 3990 # サブシェル (, 算術コマンド (( 3991 local m=${BASH_REMATCH[0]} 3992 if ((ctx==CTX_CMDX||ctx==CTX_CMDX1||ctx==CTX_CMDXT||ctx==CTX_CMDXC)); then 3993 ((_ble_syntax_attr[i]=ATTR_DEL)) 3994 ((ctx=_ble_bash>=50200||${#m}==1?CTX_CMDXE:CTX_ARGX0)) 3995 [[ $_ble_syntax_bash_is_command_form_for && $tail == '(('* ]] && ((ctx=CTX_CMDXD0)) 3996 ble/syntax/parse/nest-push "$((${#m}==1?CTX_CMDX1:CTX_EXPR))" "$m" 3997 ((i+=${#m})) 3998 else 3999 ble/syntax/parse/nest-push "$CTX_PATN" 4000 ((_ble_syntax_attr[i++]=ATTR_ERR)) 4001 fi 4002 return 0 4003 elif [[ $tail == ')'* ]]; then 4004 local ntype 4005 ble/syntax/parse/nest-type 4006 local attr= 4007 if [[ $ntype == '(' || $ntype == '$(' || $ntype == '((' || $ntype == '$((' ]]; then 4008 # 1 $ntype == '(' 4009 # ( sub shell ) 4010 # <( process substitution ) 4011 # func ( invalid ) 4012 # 2 $ntype== '$(' 4013 # $(command substitution) 4014 # 3 $ntype == '((', '$((' 4015 # ((echo) >/dev/null) / $((echo) >/dev/null) 4016 # ※これは当初は算術式だと思っていたら実はサブシェルだったというパターン 4017 ((attr=_ble_syntax_attr[inest])) 4018 fi 4019 4020 if [[ $attr ]]; then 4021 ((_ble_syntax_attr[i]=(ctx==CTX_CMDX||ctx==CTX_CMDX0||ctx==CTX_CMDXV||ctx==CTX_CMDXE||ctx==CTX_ARGX||ctx==CTX_ARGX0||ctx==CTX_ARGVX||ctx==CTX_ARGEX)?attr:ATTR_ERR, 4022 i+=1)) 4023 ble/syntax/parse/nest-pop 4024 return 0 4025 fi 4026 fi 4027 4028 return 1 4029 } 4030 4031 ## @fn ble/syntax:bash/ctx-command/.check-word-begin 4032 ## 単語が未開始の場合に開始します。 4033 ## @var[in,out] i,ctx,wtype,wbegin 4034 ## @return 引数が来てはならない所に引数が来た時に 1 を返します。 4035 _ble_syntax_bash_command_BeginCtx=() 4036 _ble_syntax_bash_command_BeginCtx[CTX_ARGX]=$CTX_ARGI 4037 _ble_syntax_bash_command_BeginCtx[CTX_ARGX0]=$CTX_ARGI 4038 _ble_syntax_bash_command_BeginCtx[CTX_ARGVX]=$CTX_ARGVI 4039 _ble_syntax_bash_command_BeginCtx[CTX_ARGEX]=$CTX_ARGEI 4040 _ble_syntax_bash_command_BeginCtx[CTX_CMDX]=$CTX_CMDI 4041 _ble_syntax_bash_command_BeginCtx[CTX_CMDX0]=$CTX_CMDI 4042 _ble_syntax_bash_command_BeginCtx[CTX_CMDX1]=$CTX_CMDI 4043 _ble_syntax_bash_command_BeginCtx[CTX_CMDXT]=$CTX_CMDI 4044 _ble_syntax_bash_command_BeginCtx[CTX_CMDXC]=$CTX_CMDI 4045 _ble_syntax_bash_command_BeginCtx[CTX_CMDXE]=$CTX_CMDI 4046 _ble_syntax_bash_command_BeginCtx[CTX_CMDXD]=$CTX_CMDI 4047 _ble_syntax_bash_command_BeginCtx[CTX_CMDXD0]=$CTX_CMDI 4048 _ble_syntax_bash_command_BeginCtx[CTX_CMDXV]=$CTX_CMDI 4049 _ble_syntax_bash_command_BeginCtx[CTX_FARGX1]=$CTX_FARGI1 4050 _ble_syntax_bash_command_BeginCtx[CTX_SARGX1]=$CTX_FARGI1 4051 _ble_syntax_bash_command_BeginCtx[CTX_FARGX2]=$CTX_FARGI2 4052 _ble_syntax_bash_command_BeginCtx[CTX_FARGX3]=$CTX_FARGI3 4053 _ble_syntax_bash_command_BeginCtx[CTX_CARGX1]=$CTX_CARGI1 4054 _ble_syntax_bash_command_BeginCtx[CTX_CARGX2]=$CTX_CARGI2 4055 _ble_syntax_bash_command_BeginCtx[CTX_CPATX]=$CTX_CPATI 4056 _ble_syntax_bash_command_BeginCtx[CTX_CPATX0]=$CTX_CPATI 4057 _ble_syntax_bash_command_BeginCtx[CTX_TARGX1]=$CTX_TARGI1 4058 _ble_syntax_bash_command_BeginCtx[CTX_TARGX2]=$CTX_TARGI2 4059 _ble_syntax_bash_command_BeginCtx[CTX_COARGX]=$CTX_COARGI 4060 4061 #%if !release 4062 ## @arr _ble_syntax_bash_command_isARGI[ctx] 4063 ## 4064 ## assert 用の配列。シェル単語の解析中に現れても良い文脈値を管理する。この配 4065 ## 列要素が非空文字列のとき、その文脈はシェル単語の解析中に現れても良い。 4066 ## 4067 _ble_syntax_bash_command_isARGI[CTX_CMDI]=1 4068 _ble_syntax_bash_command_isARGI[CTX_VRHS]=1 4069 _ble_syntax_bash_command_isARGI[CTX_ARGI]=1 4070 _ble_syntax_bash_command_isARGI[CTX_ARGQ]=1 4071 _ble_syntax_bash_command_isARGI[CTX_ARGVI]=1 4072 _ble_syntax_bash_command_isARGI[CTX_ARGVR]=1 4073 _ble_syntax_bash_command_isARGI[CTX_ARGEI]=1 4074 _ble_syntax_bash_command_isARGI[CTX_ARGER]=1 4075 _ble_syntax_bash_command_isARGI[CTX_FARGI1]=1 # var 4076 _ble_syntax_bash_command_isARGI[CTX_FARGI2]=1 # in 4077 _ble_syntax_bash_command_isARGI[CTX_FARGI3]=1 # args... 4078 _ble_syntax_bash_command_isARGI[CTX_FARGQ3]=1 # args... (= の後) 4079 _ble_syntax_bash_command_isARGI[CTX_CARGI1]=1 # value 4080 _ble_syntax_bash_command_isARGI[CTX_CARGQ1]=1 # value (= の後) 4081 _ble_syntax_bash_command_isARGI[CTX_CARGI2]=1 # in 4082 _ble_syntax_bash_command_isARGI[CTX_CPATI]=1 # pattern 4083 _ble_syntax_bash_command_isARGI[CTX_CPATQ]=1 # pattern 4084 _ble_syntax_bash_command_isARGI[CTX_TARGI1]=1 # -p 4085 _ble_syntax_bash_command_isARGI[CTX_TARGI2]=1 # -- 4086 _ble_syntax_bash_command_isARGI[CTX_COARGI]=1 # var (coproc の後) 4087 #%end 4088 # Detect the end of ${ list; } 4089 function ble/syntax:bash/ctx-command/.check-funsub-end { 4090 ((_ble_bash>=50300)) || return 1 4091 4092 # 新しいコマンド名が始まる文脈 4093 ((wbegin<0&&_ble_syntax_bash_command_BeginCtx[ctx]==CTX_CMDI)) || return 1 4094 4095 [[ $tail == '}'* ]] || return 1 4096 4097 local ntype 4098 ble/syntax/parse/nest-type 4099 [[ $ntype == 'cmdsub_nofork' ]] || return 1 4100 4101 ((_ble_syntax_attr[i++]=_ble_syntax_attr[inest])) 4102 ble/syntax/parse/nest-pop 4103 return 0 4104 } 4105 function ble/syntax:bash/ctx-command/.check-word-begin { 4106 if ((wbegin<0)); then 4107 local octx 4108 ((octx=ctx, 4109 wtype=octx, 4110 ctx=_ble_syntax_bash_command_BeginCtx[ctx])) 4111 #%if !release 4112 ble/util/assert '((ctx!=0))' "invalid ctx=$octx at the beginning of words" || 4113 ((ctx=wtype=CTX_ARGI)) 4114 #%end 4115 4116 # Note: ここで設定される wtype は最終的に ctx-command/check-word-end で 4117 # 配列 _ble_syntax_bash_command_EndWtype により変換されてから tree に登録される。 4118 ble/syntax/parse/word-push "$wtype" "$i" 4119 4120 ((octx!=CTX_ARGX0&&octx!=CTX_CPATX0)); return "$?" # return unexpectedWbegin 4121 fi 4122 4123 #%if !release 4124 ble/util/assert '((_ble_syntax_bash_command_isARGI[ctx]))' "invalid ctx=$ctx in words" 4125 #%end 4126 return 0 4127 } 4128 4129 # コマンド・引数部分 4130 function ble/syntax:bash/ctx-command { 4131 #%if !release 4132 if ble/syntax:bash/starts-with-delimiter-or-redirect; then 4133 ble/util/assert ' 4134 ((ctx==CTX_ARGX||ctx==CTX_ARGX0||ctx==CTX_ARGVX||ctx==CTX_ARGEX|| 4135 ctx==CTX_FARGX2||ctx==CTX_FARGX3||ctx==CTX_COARGX|| 4136 ctx==CTX_CMDX||ctx==CTX_CMDX0||ctx==CTX_CMDX1||ctx==CTX_CMDXT||ctx==CTX_CMDXC|| 4137 ctx==CTX_CMDXE||ctx==CTX_CMDXD||ctx==CTX_CMDXD0||ctx==CTX_CMDXV))' "invalid ctx=$ctx @ i=$i" 4138 ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char." 4139 ble/syntax:bash/ctx-command/.check-delimiter-or-redirect; return "$?" 4140 fi 4141 #%else 4142 ble/syntax:bash/ctx-command/.check-delimiter-or-redirect && return 0 4143 #%end 4144 4145 ble/syntax:bash/check-comment && return 0 4146 4147 ble/syntax:bash/ctx-command/.check-funsub-end && return 0 4148 4149 local unexpectedWbegin=-1 4150 ble/syntax:bash/ctx-command/.check-word-begin || ((unexpectedWbegin=i)) 4151 4152 local wtype0=$wtype i0=$i 4153 4154 local flagConsume=0 4155 if ble/syntax:bash/check-variable-assignment; then 4156 flagConsume=1 4157 elif local rex='^([^'${_ble_syntax_bash_chars[CTX_ARGI]}']+|\\.)'; [[ $tail =~ $rex ]]; then 4158 local rematch=$BASH_REMATCH 4159 local attr=$ctx 4160 [[ $BASH_REMATCH == '\'? ]] && attr=$ATTR_QESC 4161 ((_ble_syntax_attr[i]=attr,i+=${#rematch})) 4162 flagConsume=1 4163 elif ble/syntax:bash/check-process-subst; then 4164 flagConsume=1 4165 elif ble/syntax:bash/check-quotes; then 4166 flagConsume=1 4167 elif ble/syntax:bash/check-dollar; then 4168 flagConsume=1 4169 elif ble/syntax:bash/check-glob; then 4170 flagConsume=1 4171 elif ble/syntax:bash/check-brace-expansion; then 4172 flagConsume=1 4173 elif ble/syntax:bash/check-tilde-expansion; then 4174 flagConsume=1 4175 elif ble/syntax:bash/starts-with-histchars; then 4176 ble/syntax:bash/check-history-expansion || 4177 ((_ble_syntax_attr[i]=ctx,i++)) 4178 flagConsume=1 4179 fi 4180 4181 if ((flagConsume)); then 4182 ble/util/assert '((wtype0>=0))' 4183 4184 if ((ctx==CTX_FARGI1)); then 4185 # for var in ... の var の部分は変数名をチェックして着色 4186 local rex='^[_a-zA-Z][_a-zA-Z0-9]*$' attr=$ATTR_ERR 4187 if ((i0==wbegin)) && [[ ${text:i0:i-i0} =~ $rex ]]; then 4188 local ret; ble/syntax/highlight/vartype "$BASH_REMATCH" global; attr=$ret 4189 fi 4190 ((_ble_syntax_attr[i0]=attr)) 4191 fi 4192 4193 [[ ${_ble_syntax_bash_command_Expect[wtype0]} ]] && 4194 ((_ble_syntax_attr[i0]=ATTR_ERR)) 4195 if ((unexpectedWbegin>=0)); then 4196 ble/syntax/parse/touch-updated-attr "$unexpectedWbegin" 4197 ((_ble_syntax_attr[unexpectedWbegin]=ATTR_ERR)) 4198 fi 4199 return 0 4200 else 4201 return 1 4202 fi 4203 } 4204 4205 function ble/syntax:bash/ctx-command-compound-expect { 4206 ble/util/assert '((ctx==CTX_FARGX1||ctx==CTX_SARGX1||ctx==CTX_CARGX1||ctx==CTX_FARGX2||ctx==CTX_CARGX2||ctx==CTX_COARGX))' 4207 local _ble_syntax_bash_is_command_form_for= 4208 if ble/syntax:bash/starts-with-delimiter-or-redirect; then 4209 # "for var in ... / case arg in" を処理している途中で delimiter が来た場合。 4210 if ((ctx==CTX_FARGX2)) && [[ $tail == [$';\n']* ]]; then 4211 # for var in ... の in 以降が省略された形である。 4212 # ble/syntax:bash/ctx-command で FARGX3 と同様に処理する。 4213 ble/syntax:bash/ctx-command 4214 return "$?" 4215 elif ((ctx==CTX_FARGX1)) && [[ $tail == '(('* ]]; then 4216 # for ((...)) の場合 4217 # ここで return せずに以降の CTX_CMDX1 用の処理に任せる 4218 ((ctx=CTX_CMDX1,_ble_syntax_bash_is_command_form_for=1)) 4219 elif [[ $tail == $'\n'* ]]; then 4220 if ((ctx==CTX_CARGX2)); then 4221 ((_ble_syntax_attr[i++]=CTX_ARGX)) 4222 else 4223 ((_ble_syntax_attr[i++]=ATTR_ERR,ctx=CTX_ARGX)) 4224 fi 4225 return 0 4226 elif [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then 4227 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 4228 return 0 4229 elif ((ctx!=CTX_COARGX)); then 4230 local i0=$i 4231 ((ctx=CTX_ARGX)) 4232 ble/syntax:bash/ctx-command/.check-delimiter-or-redirect || ((i++)) 4233 ((_ble_syntax_attr[i0]=ATTR_ERR)) 4234 return 0 4235 fi 4236 fi 4237 4238 # コメント禁止 4239 local i0=$i 4240 if ble/syntax:bash/check-comment; then 4241 if ((ctx==CTX_FARGX1||ctx==CTX_SARGX1||ctx==CTX_CARGX1||ctx==CTX_COARGX)); then 4242 # "for var / select var / case arg / coproc" を処理している途中でコメントが来た場合 4243 ((_ble_syntax_attr[i0]=ATTR_ERR)) 4244 fi 4245 return 0 4246 fi 4247 4248 # 他は同じ 4249 ble/syntax:bash/ctx-command 4250 } 4251 4252 ## @fn ble/syntax:bash/ctx-command-expect/.match-word word 4253 ## 現在位置から始まる単語が指定した単語に一致するかどうかを検査します。 4254 ## @param[in] word 4255 ## @var[in] tail i 4256 function ble/syntax:bash/ctx-command-expect/.match-word { 4257 local word=$1 len=${#1} 4258 if [[ $tail == "$word"* ]]; then 4259 ble/syntax/parse/set-lookahead "$((len+1))" 4260 if ((${#tail}==len)) || i=$((i+len)) ble/syntax:bash/check-word-end/is-delimiter; then 4261 return 0 4262 fi 4263 fi 4264 return 1 4265 } 4266 4267 function ble/syntax:bash/ctx-command-time-expect { 4268 ble/util/assert '((ctx==CTX_TARGX1||ctx==CTX_TARGX2))' 4269 4270 if ble/syntax:bash/starts-with-delimiter-or-redirect; then 4271 ble/util/assert '((wbegin<0&&wtype<0))' 4272 if [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then 4273 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 4274 return 0 4275 else 4276 ((ctx=CTX_CMDXT)) 4277 ble/syntax:bash/ctx-command/.check-delimiter-or-redirect; return "$?" 4278 fi 4279 fi 4280 4281 # 期待する単語でない時は CTX_CMDXT に decay 4282 if ((ctx==CTX_TARGX1)); then 4283 # Note: bash-5.1 では "--" が来てもOKなので 4284 # ctx=CTX_TARGX2 として次の if で処理させる。 4285 ble/syntax:bash/ctx-command-expect/.match-word '-p' || 4286 ((ctx=_ble_bash>=50100?CTX_TARGX2:CTX_CMDXT)) 4287 fi 4288 if ((ctx==CTX_TARGX2)); then 4289 ble/syntax:bash/ctx-command-expect/.match-word '--' || 4290 ((ctx=CTX_CMDXT)) 4291 fi 4292 4293 # 他は同じ 4294 ble/syntax:bash/ctx-command 4295 } 4296 4297 function ble/syntax:bash/ctx-command-case-pattern-expect { 4298 ble/util/assert '((ctx==CTX_CPATX||ctx==CTX_CPATX0))' 4299 4300 if ble/syntax:bash/starts-with-delimiter-or-redirect; then 4301 local delimiter=$BASH_REMATCH 4302 if [[ $tail =~ ^$_ble_syntax_bash_RexSpaces ]]; then 4303 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 4304 elif [[ $tail == '|'* ]]; then 4305 ((_ble_syntax_attr[i++]=ctx==CTX_CPATX?ATTR_ERR:ATTR_GLOB,ctx=CTX_CPATX)) 4306 elif [[ $tail == ')'* ]]; then 4307 ((_ble_syntax_attr[i++]=ctx==CTX_CPATX?ATTR_ERR:ATTR_GLOB,ctx=CTX_CMDX)) 4308 elif [[ $tail == '('* ]]; then 4309 # ctx-command と同様にエラーにして @() を始める。 4310 ble/syntax:bash/ctx-command/.check-delimiter-or-redirect 4311 else 4312 # 改行、リダイレクト、; & はエラー 4313 ((_ble_syntax_attr[i]=ATTR_ERR,i+=${#delimiter})) 4314 fi 4315 return "$?" 4316 fi 4317 4318 # コメント禁止 4319 local i0=$i 4320 if ble/syntax:bash/check-comment; then 4321 ((_ble_syntax_attr[i0]=ATTR_ERR)) 4322 return 0 4323 fi 4324 4325 # 他は同じ 4326 ble/syntax:bash/ctx-command 4327 } 4328 4329 #------------------------------------------------------------------------------ 4330 # 文脈: 配列値リスト 4331 # 4332 4333 _ble_syntax_context_proc[CTX_VALX]=ble/syntax:bash/ctx-values 4334 _ble_syntax_context_proc[CTX_VALI]=ble/syntax:bash/ctx-values 4335 _ble_syntax_context_end[CTX_VALI]=ble/syntax:bash/ctx-values/check-word-end 4336 _ble_syntax_context_proc[CTX_VALR]=ble/syntax:bash/ctx-values 4337 _ble_syntax_context_end[CTX_VALR]=ble/syntax:bash/ctx-values/check-word-end 4338 _ble_syntax_context_proc[CTX_VALQ]=ble/syntax:bash/ctx-values 4339 _ble_syntax_context_end[CTX_VALQ]=ble/syntax:bash/ctx-values/check-word-end 4340 4341 ## 文脈値 ctx-values 4342 ## 4343 ## arr=() arr+=() から抜けた時にまた元の文脈値に復帰する必要があるので 4344 ## nest-push, nest-pop で入れ子構造を一段作成する事にする。 4345 ## 4346 ## 但し、外側で設定されたヒアドキュメントを処理する為に工夫が必要である。 4347 ## 外側の nparam をそのまま利用しまた抜ける場合には外側の nparam に変更結果を適用する。 4348 ## ble/syntax:bash/ctx-values/enter, leave はこの nparam の持ち越しに使用する。 4349 ## 4350 4351 ## @fn ble/syntax:bash/ctx-values/enter 4352 ## @remarks この関数は ble/syntax:bash/check-variable-assignment から呼ばれる。 4353 function ble/syntax:bash/ctx-values/enter { 4354 local outer_nparam=$nparam 4355 ble/syntax/parse/nest-push "$CTX_VALX" 4356 nparam=$outer_nparam 4357 } 4358 ## @fn ble/syntax:bash/ctx-values/leave 4359 function ble/syntax:bash/ctx-values/leave { 4360 local inner_nparam=$nparam 4361 ble/syntax/parse/nest-pop 4362 nparam=$inner_nparam 4363 } 4364 4365 ## @fn ble/syntax:bash/ctx-values/check-word-end 4366 function ble/syntax:bash/ctx-values/check-word-end { 4367 # 単語の中にいない時は抜ける 4368 ((wbegin<0)) && return 1 4369 4370 # 未だ続きがある場合は抜ける 4371 [[ ${text:i:1} == [!"$_ble_term_IFS;|&<>()"] ]] && return 1 4372 4373 local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i 4374 local word=${text:wbegin:wlen} 4375 4376 ble/syntax/parse/word-pop 4377 4378 ble/util/assert '((ctx==CTX_VALI||ctx==CTX_VALR||ctx==CTX_VALQ))' 'invalid context' 4379 ((ctx=CTX_VALX)) 4380 4381 return 0 4382 } 4383 4384 function ble/syntax:bash/ctx-values { 4385 # コマンド・引数部分 4386 if ble/syntax:bash/starts-with-delimiter; then 4387 #%if !release 4388 ble/util/assert '((ctx==CTX_VALX))' "invalid ctx=$ctx @ i=$i" 4389 ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char." 4390 #%end 4391 4392 if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then 4393 local spaces=$BASH_REMATCH 4394 ble/syntax:bash/check-here-document-from "$spaces" && return 0 4395 4396 # 空白 (ctx はそのままで素通り) 4397 ((_ble_syntax_attr[i]=ctx,i+=${#spaces})) 4398 return 0 4399 elif [[ $tail == ')'* ]]; then 4400 # 配列定義の終了 4401 ((_ble_syntax_attr[i++]=ATTR_DEL)) 4402 ble/syntax:bash/ctx-values/leave 4403 return 0 4404 elif [[ $type == ';'* ]]; then 4405 ((_ble_syntax_attr[i++]=ATTR_ERR)) 4406 return 0 4407 else 4408 ((_ble_syntax_attr[i++]=ATTR_ERR)) 4409 return 0 4410 fi 4411 fi 4412 4413 if ble/syntax:bash/check-comment; then 4414 return 0 4415 fi 4416 4417 if ((wbegin<0)); then 4418 ((ctx=CTX_VALI)) 4419 ble/syntax/parse/word-push "$ctx" "$i" 4420 fi 4421 4422 #%if !release 4423 ble/util/assert '((ctx==CTX_VALI||ctx==CTX_VALR||ctx==CTX_VALQ))' "invalid context ctx=$ctx" 4424 #%end 4425 4426 if ble/syntax:bash/check-variable-assignment; then 4427 return 0 4428 elif ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[CTX_ARGI]}]+"; then 4429 return 0 4430 elif ble/syntax:bash/check-process-subst; then 4431 return 0 4432 elif ble/syntax:bash/check-quotes; then 4433 return 0 4434 elif ble/syntax:bash/check-dollar; then 4435 return 0 4436 elif ble/syntax:bash/check-glob; then 4437 return 0 4438 elif ble/syntax:bash/check-brace-expansion; then 4439 return 0 4440 elif ble/syntax:bash/check-tilde-expansion; then 4441 return 0 4442 elif ble/syntax:bash/starts-with-histchars; then 4443 ble/syntax:bash/check-history-expansion || 4444 ((_ble_syntax_attr[i]=ctx,i++)) 4445 return 0 4446 fi 4447 4448 return 1 4449 } 4450 4451 #------------------------------------------------------------------------------ 4452 # 文脈: [[ 条件式 ]] 4453 4454 _ble_syntax_context_proc[CTX_CONDX]=ble/syntax:bash/ctx-conditions 4455 _ble_syntax_context_proc[CTX_CONDI]=ble/syntax:bash/ctx-conditions 4456 _ble_syntax_context_end[CTX_CONDI]=ble/syntax:bash/ctx-conditions/check-word-end 4457 _ble_syntax_context_proc[CTX_CONDQ]=ble/syntax:bash/ctx-conditions 4458 _ble_syntax_context_end[CTX_CONDQ]=ble/syntax:bash/ctx-conditions/check-word-end 4459 4460 ## @fn ble/syntax:bash/ctx-conditions/check-word-end 4461 function ble/syntax:bash/ctx-conditions/check-word-end { 4462 # 単語の中にいない時は抜ける 4463 ((wbegin<0)) && return 1 4464 4465 # 未だ続きがある場合は抜ける 4466 [[ ${text:i:1} == [!"$_ble_term_IFS;|&<>()"] ]] && return 1 4467 4468 local wbeg=$wbegin wlen=$((i-wbegin)) wend=$i 4469 local word=${text:wbegin:wlen} 4470 4471 ble/syntax/parse/word-pop 4472 4473 ble/util/assert '((ctx==CTX_CONDI||ctx==CTX_CONDQ))' 'invalid context' 4474 if [[ $word == ']]' ]]; then 4475 ble/syntax/parse/touch-updated-attr "$wbeg" 4476 ((_ble_syntax_attr[wbeg]=ATTR_DEL)) 4477 ble/syntax/parse/nest-pop 4478 else 4479 ((ctx=CTX_CONDX)) 4480 fi 4481 return 0 4482 } 4483 4484 function ble/syntax:bash/ctx-conditions { 4485 # コマンド・引数部分 4486 if ble/syntax:bash/starts-with-delimiter; then 4487 #%if !release 4488 ble/util/assert '((ctx==CTX_CONDX))' "invalid ctx=$ctx @ i=$i" 4489 ble/util/assert '((wbegin<0&&wtype<0))' "invalid word-context (wtype=$wtype wbegin=$wbegin) on non-word char." 4490 #%end 4491 4492 if [[ $tail =~ ^$_ble_syntax_bash_RexIFSs ]]; then 4493 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 4494 return 0 4495 else 4496 # [(<>;|&] など 4497 ((_ble_syntax_attr[i++]=CTX_CONDI)) 4498 return 0 4499 fi 4500 fi 4501 4502 ble/syntax:bash/check-comment && return 0 4503 4504 if ((wbegin<0)); then 4505 ((ctx=CTX_CONDI)) 4506 ble/syntax/parse/word-push "$ctx" "$i" 4507 fi 4508 4509 #%if !release 4510 ble/util/assert '((ctx==CTX_CONDI||ctx==CTX_CONDQ))' "invalid context ctx=$ctx" 4511 #%end 4512 4513 if ble/syntax:bash/check-variable-assignment; then 4514 return 0 4515 elif ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[CTX_ARGI]}]+"; then 4516 return 0 4517 elif ble/syntax:bash/check-process-subst; then 4518 return 0 4519 elif ble/syntax:bash/check-quotes; then 4520 return 0 4521 elif ble/syntax:bash/check-dollar; then 4522 return 0 4523 elif ble/syntax:bash/check-glob; then 4524 return 0 4525 elif ble/syntax:bash/check-brace-expansion; then 4526 return 0 4527 elif ble/syntax:bash/check-tilde-expansion; then 4528 return 0 4529 elif ble/syntax:bash/starts-with-histchars; then 4530 ble/syntax:bash/check-history-expansion || 4531 ((_ble_syntax_attr[i++]=ctx)) 4532 return 0 4533 else 4534 # 条件コマンドの時は $ や ) 等を許す。。 4535 ((_ble_syntax_attr[i++]=ctx)) 4536 return 0 4537 fi 4538 4539 return 1 4540 } 4541 4542 4543 #------------------------------------------------------------------------------ 4544 # 文脈: リダイレクト 4545 4546 _ble_syntax_context_proc[CTX_RDRF]=ble/syntax:bash/ctx-redirect 4547 _ble_syntax_context_proc[CTX_RDRD]=ble/syntax:bash/ctx-redirect 4548 _ble_syntax_context_proc[CTX_RDRD2]=ble/syntax:bash/ctx-redirect 4549 _ble_syntax_context_proc[CTX_RDRS]=ble/syntax:bash/ctx-redirect 4550 _ble_syntax_context_end[CTX_RDRF]=ble/syntax:bash/ctx-redirect/check-word-end 4551 _ble_syntax_context_end[CTX_RDRD]=ble/syntax:bash/ctx-redirect/check-word-end 4552 _ble_syntax_context_end[CTX_RDRD2]=ble/syntax:bash/ctx-redirect/check-word-end 4553 _ble_syntax_context_end[CTX_RDRS]=ble/syntax:bash/ctx-redirect/check-word-end 4554 function ble/syntax:bash/ctx-redirect/check-word-begin { 4555 if ((wbegin<0)); then 4556 # ※解析の段階では CTX_RDRF/CTX_RDRD/CTX_RDRD2/CTX_RDRS の間に区別はない。 4557 # 但し、↓の行で解析に用いられた ctx が保存される。 4558 # この情報は後で補完候補を生成するのに用いられる。 4559 ble/syntax/parse/word-push "$ctx" "$i" 4560 ble/syntax/parse/touch-updated-word "$i" #■これは不要では? 4561 fi 4562 } 4563 function ble/syntax:bash/ctx-redirect/check-word-end { 4564 # 単語の中にいない時は抜ける 4565 ((wbegin<0)) && return 1 4566 4567 # 未だ続きがある場合は抜ける 4568 ble/syntax:bash/check-word-end/is-delimiter || return 1 4569 4570 # 単語の登録 4571 ble/syntax/parse/word-pop 4572 4573 # pop 4574 ble/syntax/parse/nest-pop 4575 #%if !release 4576 # ここで終端の必要のある ctx (CMDI や ARGI などの単語中の文脈) になる事は無い。 4577 # 何故なら push した時は CMDX か ARGX の文脈にいたはずだから。 4578 ble/util/assert '((!_ble_syntax_bash_command_isARGI[ctx]))' "invalid ctx=$ctx in words" 4579 #%end 4580 return 0 4581 } 4582 function ble/syntax:bash/ctx-redirect { 4583 # redirect の直後にコマンド終了や別の redirect があってはならない 4584 if ble/syntax:bash/starts-with-delimiter-or-redirect; then 4585 ((_ble_syntax_attr[i++]=ATTR_ERR)) 4586 [[ ${tail:1} =~ ^$_ble_syntax_bash_RexSpaces ]] && 4587 ((_ble_syntax_attr[i]=ctx,i+=${#BASH_REMATCH})) 4588 return 0 4589 fi 4590 4591 if local i0=$i; ble/syntax:bash/check-comment; then 4592 ((_ble_syntax_attr[i0]=ATTR_ERR)) 4593 return 0 4594 fi 4595 4596 # 単語開始の設置 4597 ble/syntax:bash/ctx-redirect/check-word-begin 4598 4599 if ble/syntax:bash/check-plain-with-escape "[^${_ble_syntax_bash_chars[CTX_ARGI]}]+"; then 4600 return 0 4601 elif ble/syntax:bash/check-process-subst; then 4602 return 0 4603 elif ble/syntax:bash/check-quotes; then 4604 return 0 4605 elif ble/syntax:bash/check-dollar; then 4606 return 0 4607 elif ble/syntax:bash/check-glob; then 4608 return 0 4609 elif ble/syntax:bash/check-brace-expansion; then 4610 return 0 4611 elif ble/syntax:bash/check-tilde-expansion; then 4612 return 0 4613 elif ble/syntax:bash/starts-with-histchars; then 4614 ble/syntax:bash/check-history-expansion || 4615 ((_ble_syntax_attr[i]=ctx,i++)) 4616 return 0 4617 fi 4618 4619 return 1 4620 } 4621 4622 #------------------------------------------------------------------------------ 4623 # 文脈: ヒアドキュメント 4624 # 4625 # | <<[-] word 4626 # | contents 4627 # | delimiter 4628 # 4629 # ctx-heredoc-word (word の解析) は ctx-redirect を参考にして作成する。 4630 # 4631 4632 _ble_syntax_bash_heredoc_EscSP='\040' 4633 _ble_syntax_bash_heredoc_EscHT='\011' 4634 _ble_syntax_bash_heredoc_EscLF='\012' 4635 _ble_syntax_bash_heredoc_EscFS='\034' 4636 function ble/syntax:bash/ctx-heredoc-word/initialize { 4637 local ret 4638 ble/util/s2c ' ' 4639 ble/util/sprintf _ble_syntax_bash_heredoc_EscSP '\\%03o' "$ret" 4640 ble/util/s2c $'\t' 4641 ble/util/sprintf _ble_syntax_bash_heredoc_EscHT '\\%03o' "$ret" 4642 ble/util/s2c $'\n' 4643 ble/util/sprintf _ble_syntax_bash_heredoc_EscLF '\\%03o' "$ret" 4644 ble/util/s2c "$_ble_term_FS" 4645 ble/util/sprintf _ble_syntax_bash_heredoc_EscFS '\\%03o' "$ret" 4646 } 4647 ble/syntax:bash/ctx-heredoc-word/initialize 4648 4649 ## @fn ble/syntax:bash/ctx-heredoc-word/remove-quotes word 4650 ## @var[out] delimiter 4651 function ble/syntax:bash/ctx-heredoc-word/remove-quotes { 4652 local text=$1 result= 4653 4654 local rex='^[^\$"'\'']+|^\$?["'\'']|^\\.?|^.' 4655 while [[ $text && $text =~ $rex ]]; do 4656 local rematch=$BASH_REMATCH 4657 if [[ $rematch == \" || $rematch == \$\" ]]; then 4658 if rex='^\$?"(([^\"]|\\.)*)(\\?$|")'; [[ $text =~ $rex ]]; then 4659 local str=${BASH_REMATCH[1]} 4660 local a b 4661 b='\`' a='`'; str=${str//"$b"/"$a"} 4662 b='\"' a='"'; str=${str//"$b"/"$a"} # WA #D1751 checked 4663 b='\$' a='$'; str=${str//"$b"/"$a"} 4664 b='\\' a='\'; str=${str//"$b"/"$a"} 4665 result=$result$str 4666 text=${text:${#BASH_REMATCH}} 4667 continue 4668 fi 4669 elif [[ $rematch == \' ]]; then 4670 if rex="^('[^']*)'?"; [[ $text =~ $rex ]]; then 4671 builtin eval "result=\$result${BASH_REMATCH[1]}'" 4672 text=${text:${#BASH_REMATCH}} 4673 continue 4674 fi 4675 elif [[ $rematch == \$\' ]]; then 4676 if rex='^(\$'\''([^\'\'']|\\.)*)('\''|\\?$)'; [[ $text =~ $rex ]]; then 4677 builtin eval "result=\$result${BASH_REMATCH[1]}'" 4678 text=${text:${#BASH_REMATCH}} 4679 continue 4680 fi 4681 elif [[ $rematch == \\* ]]; then 4682 result=$result${rematch:1} 4683 text=${text:${#rematch}} 4684 continue 4685 fi 4686 4687 result=$result$rematch 4688 text=${text:${#rematch}} 4689 done 4690 4691 delimiter=$result$text 4692 } 4693 4694 ## @fn ble/syntax:bash/ctx-heredoc-word/remove-quotes delimiter 4695 ## @var[out] escaped 4696 function ble/syntax:bash/ctx-heredoc-word/escape-delimiter { 4697 local ret=$1 4698 if [[ $ret == *[\\\'$_ble_term_IFS$_ble_term_FS]* ]]; then 4699 local a b fs=$_ble_term_FS 4700 a=\\ ; b='\'$a; ret=${ret//"$a"/"$b"} 4701 a=\' ; b='\'$a; ret=${ret//"$a"/"$b"} 4702 a=' ' ; b=$_ble_syntax_bash_heredoc_EscSP; ret=${ret//"$a"/"$b"} 4703 a=$'\t'; b=$_ble_syntax_bash_heredoc_EscHT; ret=${ret//"$a"/"$b"} 4704 a=$'\n'; b=$_ble_syntax_bash_heredoc_EscLF; ret=${ret//"$a"/"$b"} 4705 a=$fs ; b=$_ble_syntax_bash_heredoc_EscFS; ret=${ret//"$a"/"$b"} 4706 fi 4707 escaped=$ret 4708 } 4709 function ble/syntax:bash/ctx-heredoc-word/unescape-delimiter { 4710 builtin eval "delimiter=\$'$1'" 4711 } 4712 4713 ## 文脈値 CTX_RDRH 4714 ## 4715 ## @remarks 4716 ## redirect と同様に nest-push と同時にこの文脈に入る事を想定する。 4717 ## 4718 _ble_syntax_context_proc[CTX_RDRH]=ble/syntax:bash/ctx-heredoc-word 4719 _ble_syntax_context_end[CTX_RDRH]=ble/syntax:bash/ctx-heredoc-word/check-word-end 4720 _ble_syntax_context_proc[CTX_RDRI]=ble/syntax:bash/ctx-heredoc-word 4721 _ble_syntax_context_end[CTX_RDRI]=ble/syntax:bash/ctx-heredoc-word/check-word-end 4722 function ble/syntax:bash/ctx-heredoc-word/check-word-end { 4723 ((wbegin<0)) && return 1 4724 4725 # 未だ続きがある場合は抜ける 4726 ble/syntax:bash/check-word-end/is-delimiter || return 1 4727 4728 # word = "EOF" 等の終端文字列 4729 local octx=$ctx word=${text:wbegin:i-wbegin} 4730 4731 # 終了処理 4732 ble/syntax/parse/word-pop 4733 ble/syntax/parse/nest-pop 4734 4735 local I 4736 if ((octx==CTX_RDRI)); then I=I; else I=R; fi 4737 4738 local Q delimiter 4739 if [[ $word == *[\'\"\\]* ]]; then 4740 Q=Q; ble/syntax:bash/ctx-heredoc-word/remove-quotes "$word" 4741 else 4742 Q=H; delimiter=$word 4743 fi 4744 4745 local escaped; ble/syntax:bash/ctx-heredoc-word/escape-delimiter "$delimiter" 4746 nparam=$nparam$_ble_term_FS@$I$Q$escaped 4747 return 0 4748 } 4749 function ble/syntax:bash/ctx-heredoc-word { 4750 ble/syntax:bash/ctx-redirect 4751 } 4752 4753 ## 文脈値 CTX_HERE0, CTX_HERE1 4754 ## 4755 ## nest-push された環境で評価される。 4756 ## nparam =~ [RI][QH]delimiter の形式を持つ。 4757 ## 4758 _ble_syntax_context_proc[CTX_HERE0]=ble/syntax:bash/ctx-heredoc-content 4759 _ble_syntax_context_proc[CTX_HERE1]=ble/syntax:bash/ctx-heredoc-content 4760 function ble/syntax:bash/ctx-heredoc-content { 4761 local indented= quoted= delimiter= 4762 ble/syntax:bash/ctx-heredoc-word/unescape-delimiter "${nparam:2}" 4763 [[ ${nparam::1} == I ]] && indented=1 4764 [[ ${nparam:1:1} == Q ]] && quoted=1 4765 4766 local rex ht=$'\t' lf=$'\n' 4767 if ((ctx==CTX_HERE0)); then 4768 rex="^${indented:+$ht*}"$'([^\n]+\n?|\n)' 4769 [[ $tail =~ $rex ]] || return 1 4770 4771 # ヒアドキュメント終了判定 4772 # ※前後の空白も含めて行が delimiter と一致していなければならない。 4773 local line=${BASH_REMATCH%"$lf"} 4774 local rematch1=${BASH_REMATCH[1]} 4775 if [[ ${rematch1%"$lf"} == "$delimiter" ]]; then 4776 local indent 4777 ((indent=${#BASH_REMATCH}-${#rematch1}, 4778 _ble_syntax_attr[i]=CTX_HERE0, 4779 _ble_syntax_attr[i+indent]=CTX_RDRH, 4780 i+=${#line})) 4781 ble/syntax/parse/nest-pop 4782 return 0 4783 fi 4784 fi 4785 4786 if [[ $quoted ]]; then 4787 ble/util/assert '((ctx==CTX_HERE0))' 4788 ((_ble_syntax_attr[i]=CTX_HERE0,i+=${#BASH_REMATCH})) 4789 return 0 4790 else 4791 ((ctx=CTX_HERE1)) 4792 4793 # \? 及び $? ${} $(()) $[] $() `` 4794 if rex='^(\\[\$`'$lf'])|^([^'${_ble_syntax_bash_chars[CTX_HERE1]}']|\\[^\$`'$lf'])+'$lf'?|^'$lf && [[ $tail =~ $rex ]]; then 4795 if [[ ${BASH_REMATCH[1]} ]]; then 4796 ((_ble_syntax_attr[i]=ATTR_QESC)) 4797 else 4798 ((_ble_syntax_attr[i]=CTX_HERE0)) 4799 [[ $BASH_REMATCH == *"$lf" ]] && ((ctx=CTX_HERE0)) 4800 fi 4801 ((i+=${#BASH_REMATCH})) 4802 return 0 4803 fi 4804 4805 if ble/syntax:bash/check-dollar; then 4806 return 0 4807 elif [[ $tail == '`'* ]] && ble/syntax:bash/check-quotes; then 4808 return 0 4809 elif ble/syntax:bash/starts-with-histchars; then 4810 ble/syntax:bash/check-history-expansion || 4811 ((_ble_syntax_attr[i]=CTX_HERE0,i++)) 4812 return 0 4813 else 4814 # 単独の $ や終端の \ など? 4815 ((_ble_syntax_attr[i]=CTX_HERE0,i++)) 4816 return 0 4817 fi 4818 fi 4819 } 4820 4821 #------------------------------------------------------------------------------ 4822 # Utilities based on syntactic strutures 4823 4824 function ble/syntax:bash/is-complete { 4825 local iN=${#_ble_syntax_text} 4826 4827 # (1) 最後の点にエラーが設定されていた時 4828 # - 閉じていない single quotation などは此処。 4829 # - 入れ子が閉じていない時もここで引っかかる。 4830 # - 実はヒアドキュメントが閉じていない時もここでかかる。 4831 ((iN>0)) && ((_ble_syntax_attr[iN-1]==ATTR_ERR)) && return 1 4832 4833 local stat=${_ble_syntax_stat[iN]} 4834 if [[ $stat ]]; then 4835 ble/string#split-words stat "$stat" 4836 4837 # (2) 入れ子が閉じていない時 4838 local nlen=${stat[3]}; ((nlen>=0)) && return 1 4839 4840 # (3) ヒアドキュメントの待ちがある時 4841 local nparam=${stat[6]}; [[ $nparam == none ]] && nparam= 4842 local rex="$_ble_term_FS@([RI][QH][^$_ble_term_FS]*)(.*$)" 4843 [[ $nparam =~ $rex ]] && return 1 4844 4845 # (4) 完結している文脈値の時以外 4846 local ctx=${stat[0]} 4847 ((ctx==CTX_ARGX||ctx==CTX_ARGX0||ctx==CTX_ARGVX||ctx==CTX_ARGEX|| 4848 ctx==CTX_CMDX||ctx==CTX_CMDX0||ctx==CTX_CMDXT||ctx==CTX_CMDXE||ctx==CTX_CMDXV|| 4849 ctx==CTX_TARGX1||ctx==CTX_TARGX2)) || return 1 4850 fi 4851 4852 # 構文 if..fi, etc が閉じているか? 4853 local attrs ret 4854 IFS= builtin eval 'attrs="::${_ble_syntax_attr[*]/%/::}"' # WA #D1570 checked 4855 ble/string#count-string "$attrs" ":$ATTR_KEYWORD_BEGIN:"; local nbeg=$ret 4856 ble/string#count-string "$attrs" ":$ATTR_KEYWORD_END:"; local nend=$ret 4857 ((nbeg>nend)) && return 1 4858 4859 return 0 4860 } 4861 4862 ## @fn ble/syntax:bash/find-end-of-array-index beg end 4863 ## "配列添字の綴じ括弧 ] の直前の位置" を求めます。 4864 ## @param[in] beg 4865 ## 配列要素の添字指定の開始位置 ("[" の位置) を指定します。 4866 ## @param[in] end 4867 ## 探索の終端位置を指定します。 4868 ## @var[out] ret 4869 ## beg に対応する "]" の位置を返します。 4870 ## 対応する終端がない場合は空文字列を返します。 4871 function ble/syntax:bash/find-end-of-array-index { 4872 local beg=$1 end=$2 4873 ret= 4874 4875 local inest0=$beg nest0 4876 [[ ${_ble_syntax_nest[inest0]} ]] || return 1 4877 4878 local q stat1 nlen1 inest1 r= 4879 for ((q=inest0+1;q<end;q++)); do 4880 local stat1=${_ble_syntax_stat[q]} 4881 [[ $stat1 ]] || continue 4882 ble/string#split-words stat1 "$stat1" 4883 ((nlen1=stat1[3])) # (workaround Bash-4.2 segfault) 4884 ((inest1=nlen1<0?nlen1:q-nlen1)) 4885 ((inest1<inest0)) && break 4886 ((r=q)) 4887 done 4888 4889 [[ ${_ble_syntax_text:r:end-r} == ']'* ]] && ret=$r 4890 [[ $ret ]] 4891 } 4892 4893 ## ble/syntax:bash/find-rhs wtype wbeg wlen opts 4894 ## 変数代入の形式の右辺の開始位置を取得します。 4895 ## @param[in] wtype wbeg wlen 4896 ## @param[in] opts 4897 ## element-assignment 4898 ## 配列要素の場合にも変数代入の形式を許します。 4899 ## long-option 4900 ## --long-option= の形式にも強制的に対応します。 4901 ## @var[out] ret 4902 ## 右辺の開始位置を返します。 4903 ## 変数代入の形式でない時には単語の開始位置を返します。 4904 ## @exit 4905 ## 単語が変数代入の形式を持つ時に成功します。 4906 ## それ以外の場合に失敗します。 4907 function ble/syntax:bash/find-rhs { 4908 local wtype=$1 wbeg=$2 wlen=$3 opts=$4 4909 4910 local text=$_ble_syntax_text 4911 local word=${text:wbeg:wlen} wend=$((wbeg+wlen)) 4912 4913 local rex= 4914 if ((wtype==ATTR_VAR)); then 4915 rex='^[_a-zA-Z0-9]+(\+?=|\[)' 4916 elif ((wtype==CTX_VALI)); then 4917 if [[ :$opts: == *:element-assignment:* ]]; then 4918 # 配列要素に対しても変数代入の形式を許す 4919 rex='^[_a-zA-Z0-9]+(\+?=|\[)|^(\[)' 4920 else 4921 rex='^(\[)' 4922 fi 4923 fi 4924 if [[ :$opts: == *:long-option:* ]]; then 4925 rex=${rex:+$rex'|'}'^--[-_a-zA-Z0-9]+=' 4926 fi 4927 4928 if [[ $rex && $word =~ $rex ]]; then 4929 local last_char=${BASH_REMATCH:${#BASH_REMATCH}-1} 4930 if [[ $last_char == '[' ]]; then 4931 # wtype==ATTR_VAR: arr[0]=x@ arr[1]+=x@ 4932 # wtype==ATTR_VAR: declare arr[0]=x@ arr[1]+=x@ 4933 # wtype==CTX_VALI: arr=([0]=x@ [1]+=x@) 4934 local p1=$((wbeg+${#BASH_REMATCH}-1)) 4935 if ble/syntax:bash/find-end-of-array-index "$p1" "$wend"; then 4936 local p2=$ret 4937 case ${text:p2:wend-p2} in 4938 (']='*) ((ret=p2+2)); return 0 ;; 4939 (']+='*) ((ret=p2+3)); return 0 ;; 4940 esac 4941 fi 4942 else 4943 # wtype==ATTR_VAR: var=x@ var+=x@ 4944 # wtype==ATTR_VAR: declare var=x@ var+=x@ 4945 ((ret=wbeg+${#BASH_REMATCH})) 4946 return 0 4947 fi 4948 fi 4949 4950 ret=$wbeg 4951 return 1 4952 } 4953 4954 #============================================================================== 4955 # 解析部 4956 4957 _ble_syntax_vanishing_word_umin=-1 4958 _ble_syntax_vanishing_word_umax=-1 4959 function ble/syntax/vanishing-word/register { 4960 local tree_array=$1 tofs=$2 4961 local beg=$3 end=$4 lbeg=$5 lend=$6 4962 (((beg<=0)&&(beg=1))) 4963 4964 local node i nofs 4965 for ((i=end;i>=beg;i--)); do 4966 builtin eval "node=(\${$tree_array[tofs+i-1]})" 4967 ((${#node[@]})) || continue 4968 for ((nofs=0;nofs<${#node[@]};nofs+=_ble_syntax_TREE_WIDTH)); do 4969 local wtype=${node[nofs]} wlen=${node[nofs+1]} 4970 local wbeg=$((wlen<0?wlen:i-wlen)) wend=$i 4971 4972 ((wbeg<lbeg&&(wbeg=lbeg), 4973 wend>lend&&(wend=lend))) 4974 ble/syntax/urange#update _ble_syntax_vanishing_word_ "$wbeg" "$wend" 4975 done 4976 done 4977 } 4978 4979 #---------------------------------------------------------- 4980 # shift 4981 4982 ## @var[in] shift2_j 4983 ## @var[in] beg,end,end0,shift 4984 function ble/syntax/parse/shift.stat { 4985 if [[ ${_ble_syntax_stat[shift2_j]} ]]; then 4986 local -a stat; ble/string#split-words stat "${_ble_syntax_stat[shift2_j]}" 4987 4988 local k klen kbeg 4989 for k in 1 3 4 5; do # wlen nlen tclen tplen 4990 (((klen=stat[k])<0)) && continue 4991 ((kbeg=shift2_j-klen)) 4992 if ((kbeg<beg)); then 4993 ((stat[k]+=shift)) 4994 elif ((kbeg<end0)); then 4995 ((stat[k]-=end0-kbeg)) 4996 fi 4997 done 4998 4999 _ble_syntax_stat[shift2_j]="${stat[*]}" 5000 fi 5001 } 5002 5003 ## @var[in] node,shift2_j,nofs 5004 ## @var[in] beg,end,end0,shift 5005 function ble/syntax/parse/shift.tree/1 { 5006 local k klen kbeg 5007 for k in 1 2 3; do # wlen/nlen tclen tplen 5008 ((klen=node[nofs+k])) 5009 ((klen<0||(kbeg=shift2_j-klen)>end0)) && continue 5010 # 長さが変化した時 (k==1)、または構文木の距離変化があった時 (k==2, k==3) にここへ来る。 5011 5012 # (1) 単語の中身が変化した事を記録 5013 # node の中身が書き換わった時 (wbegin < end0 の時): 5014 # dirty 拡大の代わりに _ble_syntax_word_umax に登録するに留める。 5015 if [[ $k == 1 && ${node[nofs]} =~ ^[0-9]$ ]]; then 5016 ble/syntax/parse/touch-updated-word "$shift2_j" 5017 5018 # 着色情報を clear 5019 node[nofs+4]='-' 5020 fi 5021 5022 # (1) 長さ・相対位置の補正 5023 if ((kbeg<beg)); then 5024 ((node[nofs+k]+=shift)) 5025 elif ((kbeg<end0)); then 5026 ((node[nofs+k]-=end0-kbeg)) 5027 fi 5028 done 5029 } 5030 5031 ## @var[in] shift2_j 5032 ## @var[in] beg,end,end0,shift 5033 function ble/syntax/parse/shift.tree { 5034 [[ ${_ble_syntax_tree[shift2_j-1]} ]] || return 1 5035 local -a node 5036 ble/string#split-words node "${_ble_syntax_tree[shift2_j-1]}" 5037 5038 local nofs 5039 if [[ $1 ]]; then 5040 nofs=$1 ble/syntax/parse/shift.tree/1 5041 else 5042 for ((nofs=0;nofs<${#node[@]};nofs+=_ble_syntax_TREE_WIDTH)); do 5043 ble/syntax/parse/shift.tree/1 5044 done 5045 fi 5046 5047 _ble_syntax_tree[shift2_j-1]="${node[*]}" 5048 } 5049 5050 ## @var[in] shift2_j 5051 ## @var[in] beg,end,end0,shift 5052 function ble/syntax/parse/shift.nest { 5053 # stat の先頭以外でも nest-push している 5054 # @ ctx-command/check-word-begin の "関数名 ( " にて。 5055 if [[ ${_ble_syntax_nest[shift2_j]} ]]; then 5056 local -a nest 5057 ble/string#split-words nest "${_ble_syntax_nest[shift2_j]}" 5058 5059 local k klen kbeg 5060 for k in 1 3 4 5; do 5061 (((klen=nest[k]))) 5062 ((klen<0||(kbeg=shift2_j-klen)<0)) && continue 5063 if ((kbeg<beg)); then 5064 ((nest[k]+=shift)) 5065 elif ((kbeg<end0)); then 5066 ((nest[k]-=end0-kbeg)) 5067 fi 5068 done 5069 5070 _ble_syntax_nest[shift2_j]="${nest[*]}" 5071 fi 5072 } 5073 5074 function ble/syntax/parse/shift.impl2/.shift-until { 5075 local limit=$1 5076 while ((shift2_j>=limit)); do 5077 #%if !release 5078 [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift[shift2_j+shift]=1 5079 #%end 5080 ble/syntax/parse/shift.stat 5081 ble/syntax/parse/shift.nest 5082 ((shift2_j--)) 5083 done 5084 } 5085 5086 ## @fn ble/syntax/parse/shift.impl2/.proc1 5087 ## 5088 ## @var[in] TE_i 5089 ## tree-enumerate によって設定される変数です。 5090 ## 現在処理している単語の終端境界を表します。 5091 ## 単語の情報は _ble_syntax_tree[TE_i-1] に格納されています。 5092 ## 5093 ## @var[in,out] shift2_j 何処まで処理したかを格納します。 5094 ## 5095 ## @var[in] i1,i2,j2,iN 5096 ## @var[in] beg,end,end0,shift 5097 ## これらの変数は更に子関数で使用されます。 5098 ## 5099 function ble/syntax/parse/shift.impl2/.proc1 { 5100 if ((TE_i<j2)); then 5101 ((tprev=-1)) # 中断 5102 return 0 5103 fi 5104 5105 ble/syntax/parse/shift.impl2/.shift-until "$((TE_i+1))" 5106 ble/syntax/parse/shift.tree "$TE_nofs" 5107 5108 if ((tprev>end0&&wbegin>end0)) && [[ ${wtype//[0-9]} ]]; then 5109 # skip 可能 5110 # 単語 (wtype=整数) の時は、nlen が外部に参照を持つ可能性がある。 5111 # tprev<=end0 の場合、stat の中の tplen が shift 対象の可能性がある事に注意する。 5112 #%if !release 5113 [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift[shift2_j+shift]=1 5114 #%end 5115 ble/syntax/parse/shift.stat 5116 ble/syntax/parse/shift.nest 5117 ((shift2_j=wbegin)) # skip 5118 elif ((tchild>=0)); then 5119 ble/syntax/tree-enumerate-children ble/syntax/parse/shift.impl2/.proc1 5120 fi 5121 } 5122 5123 function ble/syntax/parse/shift.method1 { 5124 # shift (shift は毎回やり切る。途中状態で抜けたりはしない) 5125 local i j 5126 for ((i=i2,j=j2;i<=iN;i++,j++)); do 5127 # 注意: データの範囲 5128 # stat[i] は i in [0,iN] 5129 # attr[i] は i in [0,iN) 5130 # tree[i-1] は i in (0,iN] 5131 local shift2_j=$j 5132 ble/syntax/parse/shift.stat 5133 ((j>0)) && ble/syntax/parse/shift.tree 5134 ((i<iN)) && ble/syntax/parse/shift.nest 5135 done 5136 } 5137 5138 function ble/syntax/parse/shift.method2 { 5139 #%if !release 5140 [[ $bleopt_syntax_debug ]] && _ble_syntax_stat_shift=() 5141 #%end 5142 5143 local iN=${#_ble_syntax_text} # tree-enumerate 起点は (古い text の長さ) である 5144 local shift2_j=$iN # proc1 に渡す変数 5145 ble/syntax/tree-enumerate ble/syntax/parse/shift.impl2/.proc1 5146 ble/syntax/parse/shift.impl2/.shift-until "$j2" # 未処理部分 5147 } 5148 5149 ## @var[in] i1,i2,j2,iN 5150 ## @var[in] beg,end,end0,shift 5151 function ble/syntax/parse/shift { 5152 # ※shift==0 でも更新で消滅した部分を縮める必要があるので 5153 # shift 実行する必要がある。 5154 5155 # ble/syntax/parse/shift.method1 # 直接探索 5156 ble/syntax/parse/shift.method2 # tree-enumerate による skip 5157 5158 if ((shift!=0)); then 5159 # 更新範囲の shift 5160 ble/syntax/urange#shift _ble_syntax_attr_ 5161 ble/syntax/wrange#shift _ble_syntax_word_ 5162 ble/syntax/wrange#shift _ble_syntax_word_defer_ 5163 ble/syntax/urange#shift _ble_syntax_vanishing_word_ 5164 fi 5165 } 5166 5167 #---------------------------------------------------------- 5168 # parse 5169 5170 _ble_syntax_dbeg=-1 _ble_syntax_dend=-1 5171 5172 ## @fn ble/syntax/parse/determine-parse-range 5173 ## 5174 ## @var[out] i1 i2 j2 5175 ## 5176 ## @var[in] beg end end0 5177 ## @var[in] _ble_syntax_dbeg 5178 ## @var[in] _ble_syntax_dend 5179 ## 文字列の変更範囲と、前回の解析でやり残した範囲を指定します。 5180 ## 5181 function ble/syntax/parse/determine-parse-range { 5182 local flagSeekStat=0 5183 ((i1=_ble_syntax_dbeg,i1>=end0&&(i1+=shift), 5184 i2=_ble_syntax_dend,i2>=end0&&(i2+=shift), 5185 (i1<0||beg<i1)&&(i1=beg,flagSeekStat=1), 5186 (i2<0||i2<end)&&(i2=end), 5187 (i2>iN)&&(i2=iN), 5188 j2=i2-shift)) 5189 5190 if ((flagSeekStat)); then 5191 # beg より前の最後の stat の位置まで戻る 5192 local lookahead='stat[7]' 5193 local -a stat 5194 while ((i1>0)); do 5195 if [[ ${_ble_syntax_stat[--i1]} ]]; then 5196 ble/string#split-words stat "${_ble_syntax_stat[i1]}" 5197 ((i1+lookahead<=beg)) && break 5198 fi 5199 done 5200 fi 5201 5202 #%if !release 5203 ble/util/assert '((0<=i1&&i1<=beg&&end<=i2&&i2<=iN))' "X2 0 <= $i1 <= $beg <= $end <= $i2 <= $iN" 5204 #%end 5205 } 5206 5207 function ble/syntax/parse/check-end { 5208 [[ ${_ble_syntax_context_end[ctx]} ]] && "${_ble_syntax_context_end[ctx]}" 5209 } 5210 5211 ## @fn ble/syntax/parse text opts [beg end end0] 5212 ## 5213 ## @param[in] text 5214 ## 解析対象の文字列を指定します。 5215 ## 5216 ## @param[in] opts 5217 ## 細かい動作を制御するオプションを指定します。 5218 ## 5219 ## @param[in] beg text変更範囲 開始点 (既定値 = text先頭) 5220 ## @param[in] end text変更範囲 終了点 (既定値 = text末端) 5221 ## @param[in] end0 長さが変わった時用 (既定値 = end) 5222 ## これらの引数はtextに変更があった場合にその範囲を伝達するのに用います。 5223 ## 5224 ## @var [in,out] _ble_syntax_dbeg 解析予定範囲 開始点 (初期値 -1 = 解析予定無し) 5225 ## @var [in,out] _ble_syntax_dend 解析予定範囲 終了点 (初期値 -1 = 解析予定無し) 5226 ## これらの変数はどの部分を解析する必要があるかを記録します。 5227 ## beg end beg2 end2 を用いてtextの変更範囲を指定しても、 5228 ## その変更範囲に対する解析を即座に完了させる訳ではなく逐次更新します。 5229 ## ここには前回の parse 呼出でやり残した解析範囲の情報が格納されます。 5230 ## 5231 ## @var [in,out] _ble_syntax_stat[] (内部使用) 解析途中状態を記録 5232 ## @var [in,out] _ble_syntax_nest[] (内部使用) 入れ子の構造を記録 5233 ## @var [in,out] _ble_syntax_attr[] 各文字の属性 5234 ## @var [in,out] _ble_syntax_tree[] シェル単語の情報を記録 5235 ## これらの変数には解析結果が格納されます。 5236 ## 5237 ## @var [in,out] _ble_syntax_attr_umin 5238 ## @var [in,out] _ble_syntax_attr_umax 5239 ## @var [in,out] _ble_syntax_word_umin 5240 ## @var [in,out] _ble_syntax_word_umax 5241 ## 今回の呼出によって文法的な解釈の変更が行われた範囲を更新します。 5242 ## 5243 function ble/syntax/parse { 5244 local text=$1 iN=${#1} 5245 local opts=$2 5246 local beg=${3:-0} end=${4:-$iN} end0=${5:-0} 5247 ((end==beg&&end0==beg&&_ble_syntax_dbeg<0)) && return 0 5248 5249 local IFS=$_ble_term_IFS 5250 5251 local shift=$((end-end0)) 5252 #%if !release 5253 ble/util/assert \ 5254 '((0<=beg&&beg<=end&&end<=iN&&beg<=end0))' \ 5255 "X1 0 <= beg:$beg <= end:$end <= iN:$iN, beg:$beg <= end0:$end0 (shift=$shift text=$text)" || 5256 ((beg=0,end=iN)) 5257 #%else 5258 ((0<=beg&&beg<=end&&end<=iN&&beg<=end0)) || ((beg=0,end=iN)) 5259 #%end 5260 5261 # 解析予定範囲の更新 5262 # @var i1 解析範囲開始 5263 # @var i2 解析必要範囲終端 (此処以降で文脈が一致した時に解析終了) 5264 # @var j2 シフト前の解析終端 5265 local i1 i2 j2 5266 ble/syntax/parse/determine-parse-range 5267 5268 ble/syntax/vanishing-word/register _ble_syntax_tree 0 "$i1" "$j2" 0 "$i2" 5269 5270 ble/syntax/parse/shift 5271 5272 # 解析途中状態の復元 5273 local ctx wbegin wtype inest tchild tprev nparam ilook 5274 if ((i1>0)) && [[ ${_ble_syntax_stat[i1]} ]]; then 5275 local -a stat 5276 ble/string#split-words stat "${_ble_syntax_stat[i1]}" 5277 local wlen=${stat[1]} nlen=${stat[3]} tclen=${stat[4]} tplen=${stat[5]} 5278 ctx=${stat[0]} 5279 wbegin=$((wlen<0?wlen:i1-wlen)) 5280 wtype=${stat[2]} 5281 inest=$((nlen<0?nlen:i1-nlen)) 5282 tchild=$((tclen<0?tclen:i1-tclen)) 5283 tprev=$((tplen<0?tplen:i1-tplen)) 5284 nparam=${stat[6]}; [[ $nparam == none ]] && nparam= 5285 ilook=$((i1+${stat[7]:-1})) 5286 else 5287 # 初期値 5288 ctx=$CTX_UNSPECIFIED ##!< 現在の解析の文脈 5289 ble/syntax:"$_ble_syntax_lang"/initialize-ctx # ctx 初期化 5290 wbegin=-1 ##!< シェル単語内にいる時、シェル単語の開始位置 5291 wtype=-1 ##!< シェル単語内にいる時、シェル単語の種類 5292 inest=-1 ##!< 入れ子の時、親の開始位置 5293 tchild=-1 5294 tprev=-1 5295 nparam= 5296 ilook=1 5297 fi 5298 5299 # 前回までに解析が終わっている部分 [0,i1), [i2,iN) 5300 local -a tail_syntax_stat tail_syntax_tree tail_syntax_nest tail_syntax_attr 5301 tail_syntax_stat=("${_ble_syntax_stat[@]:j2:iN-i2+1}") 5302 tail_syntax_tree=("${_ble_syntax_tree[@]:j2:iN-i2}") 5303 tail_syntax_nest=("${_ble_syntax_nest[@]:j2:iN-i2}") 5304 tail_syntax_attr=("${_ble_syntax_attr[@]:j2:iN-i2}") 5305 ble/array#reserve-prototype "$iN" 5306 _ble_syntax_stat=("${_ble_syntax_stat[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 再開用データ 5307 _ble_syntax_tree=("${_ble_syntax_tree[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 単語 5308 _ble_syntax_nest=("${_ble_syntax_nest[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 入れ子の親 5309 _ble_syntax_attr=("${_ble_syntax_attr[@]::i1}" "${_ble_array_prototype[@]:i1:iN-i1}") # 文脈・色とか 5310 5311 ble/syntax:"$_ble_syntax_lang"/initialize-vars 5312 5313 # 解析 5314 _ble_syntax_text=$text 5315 local i sstat tail 5316 #%if !release 5317 local debug_p1 5318 #%end 5319 for ((i=i1;i<iN;)); do 5320 ble/syntax/parse/serialize-stat 5321 if ((i>=i2)) && [[ ${tail_syntax_stat[i-i2]} == "$sstat" ]]; then 5322 if ble/syntax/parse/nest-equals "$inest"; then 5323 # 前回の解析と同じ状態になった時 → 残りは前回の結果と同じ 5324 _ble_syntax_stat=("${_ble_syntax_stat[@]::i}" "${tail_syntax_stat[@]:i-i2}") 5325 _ble_syntax_tree=("${_ble_syntax_tree[@]::i}" "${tail_syntax_tree[@]:i-i2}") 5326 _ble_syntax_nest=("${_ble_syntax_nest[@]::i}" "${tail_syntax_nest[@]:i-i2}") 5327 _ble_syntax_attr=("${_ble_syntax_attr[@]::i}" "${tail_syntax_attr[@]:i-i2}") 5328 break 5329 fi 5330 fi 5331 _ble_syntax_stat[i]=$sstat 5332 5333 tail=${text:i} 5334 #%if !release 5335 debug_p1=$i 5336 #%end 5337 # 処理 5338 "${_ble_syntax_context_proc[ctx]}" || ((_ble_syntax_attr[i]=ATTR_ERR,i++)) 5339 5340 # nest-pop で CMDI/ARGI になる事もあるし、 5341 # また単語終端な文字でも FCTX が失敗する事もある (unrecognized な場合) ので、 5342 # (FCTX の中や直後ではなく) ここで単語終端をチェック 5343 ble/syntax/parse/check-end 5344 done 5345 #%if !release 5346 builtin unset -v debug_p1 5347 #%end 5348 5349 ble/syntax/vanishing-word/register tail_syntax_tree "$((-i2))" "$((i2+1))" "$i" 0 "$i" 5350 5351 ble/syntax/urange#update _ble_syntax_attr_ "$i1" "$i" 5352 5353 (((i>=i2)?( 5354 _ble_syntax_dbeg=_ble_syntax_dend=-1 5355 ):( 5356 _ble_syntax_dbeg=i,_ble_syntax_dend=i2))) 5357 5358 # 終端の状態の記録 5359 if ((i>=iN)); then 5360 ((i=iN)) 5361 ble/syntax/parse/serialize-stat 5362 _ble_syntax_stat[i]=$sstat 5363 5364 # ネスト開始点のエラー表示は +syntax 内で。 5365 # ここで設定すると部分更新の際に取り消しできないから。 5366 if ((inest>0)); then 5367 ((_ble_syntax_attr[iN-1]=ATTR_ERR)) 5368 while ((inest>=0)); do 5369 ((i=inest)) 5370 ble/syntax/parse/nest-pop 5371 ((inest>=i&&(inest=i-1))) 5372 done 5373 fi 5374 fi 5375 5376 #%if !release 5377 ble/util/assert \ 5378 '((${#_ble_syntax_stat[@]}==iN+1))' \ 5379 "unexpected array length #arr=${#_ble_syntax_stat[@]} (expected to be $iN), #proto=${#_ble_array_prototype[@]} should be >= $iN" 5380 #%end 5381 } 5382 5383 ## @fn ble/syntax/highlight text [lang] 5384 ## @param[in] text 5385 ## @param[in] lang 5386 ## @var[out] ret 5387 function ble/syntax/highlight { 5388 local text=$1 lang=${2:-bash} cache_prefix=$3 5389 5390 local -a _ble_highlight_layer_list=(plain syntax) 5391 local -a vars=() 5392 ble/array#push vars "${_ble_syntax_VARNAMES[@]}" 5393 ble/array#push vars "${_ble_highlight_layer_plain_VARNAMES[@]}" 5394 ble/array#push vars "${_ble_highlight_layer_syntax_VARNAMES[@]}" 5395 5396 local "${vars[@]/%/=}" # WA #D1570 checked 5397 if [[ $cache_prefix ]] && ((${cache_prefix}_INITIALIZED++)); then 5398 ble/util/restore-vars "$cache_prefix" "${vars[@]}" 5399 5400 ble/string#common-prefix "$_ble_syntax_text" "$text" 5401 local beg=${#ret} 5402 ble/string#common-suffix "${_ble_syntax_text:beg}" "${text:beg}" 5403 local end=$((${#text}-${#ret})) end0=$((${#_ble_syntax_text}-${#ret})) 5404 else 5405 ble/syntax/initialize-vars 5406 ble/highlight/layer:plain/initialize-vars 5407 ble/highlight/layer:syntax/initialize-vars 5408 _ble_syntax_lang=$lang 5409 local beg=0 end=${#text} end0=0 5410 fi 5411 5412 ble/syntax/parse "$text" '' "$beg" "$end" "$end0" 5413 5414 local HIGHLIGHT_BUFF HIGHLIGHT_UMIN HIGHLIGHT_UMAX 5415 ble/highlight/layer/update "$text" '' "$beg" "$end" "$end0" 5416 IFS= builtin eval "ret=\"\${$HIGHLIGHT_BUFF[*]}\"" 5417 5418 [[ $cache_prefix ]] && 5419 ble/util/save-vars "$cache_prefix" "${vars[@]}" 5420 return 0 5421 } 5422 5423 #============================================================================== 5424 # 5425 # syntax-complete 5426 # 5427 #============================================================================== 5428 5429 # ## @fn ble/syntax/getattr index 5430 # function ble/syntax/getattr { 5431 # local i 5432 # attr= 5433 # for ((i=$1;i>=0;i--)); do 5434 # if [[ ${_ble_syntax_attr[i]} ]]; then 5435 # ((attr=_ble_syntax_attr[i])) 5436 # return 0 5437 # fi 5438 # done 5439 # return 1 5440 # } 5441 5442 # ## @fn ble/syntax/getstat index 5443 # function ble/syntax/getstat { 5444 # local i 5445 # for ((i=$1;i>=0;i--)); do 5446 # if [[ ${_ble_syntax_stat[i]} ]]; then 5447 # ble/string#split-words stat "${_ble_syntax_stat[i]}" 5448 # return 0 5449 # fi 5450 # done 5451 # return 1 5452 # } 5453 5454 function ble/syntax/completion-context/.add { 5455 local source=$1 5456 local comp1=$2 5457 ble/util/assert '[[ $source && comp1 -ge 0 ]]' 5458 sources[${#sources[*]}]="$source $comp1" 5459 } 5460 5461 ## @fn ble/syntax/completion-context/.check/parameter-expansion 5462 ## @var[in] text istat index ctx 5463 function ble/syntax/completion-context/.check/parameter-expansion { 5464 local rex_paramx='^(\$(\{[!#]?)?)([_a-zA-Z][_a-zA-Z0-9]*)?$' 5465 if [[ ${text:istat:index-istat} =~ $rex_paramx ]]; then 5466 local rematch1=${BASH_REMATCH[1]} 5467 local source=variable 5468 if [[ $rematch1 == '${'* ]]; then 5469 source=variable:b # suffix } 5470 elif ((ctx==CTX_BRACE1||ctx==CTX_BRACE2)); then 5471 source=variable:n # no suffix 5472 fi 5473 ble/syntax/completion-context/.add "$source" "$((istat+${#rematch1}))" 5474 fi 5475 } 5476 5477 ## @fn ble/syntax/completion-context/.check-prefix/ctx:* 5478 ## 5479 ## @var[in] text index 5480 ## 補完対象のコマンドラインと現在のカーソルの位置を指定します。 5481 ## 5482 ## @var[in] istat stat 5483 ## 直前の解析再開点の位置と記録されている情報を指定します。 5484 ## 5485 ## @var[in] ctx wbeg wlen 5486 ## 直前の解析再開点の文脈・単語開始点・ 5487 ## 直前の解析再開点に於ける単語の長さを指定します。 5488 ## 5489 ## @var[in] rex_param 5490 ## 5491 5492 ## @fn ble/syntax/completion-context/.check-prefix/ctx:inside-command 5493 ## CMDI 系統 (コマンドの続き) の文脈に対する補完文脈の生成 5494 _ble_syntax_bash_complete_check_prefix[CTX_CMDI]=inside-command 5495 function ble/syntax/completion-context/.check-prefix/ctx:inside-command { 5496 if ((wlen>=0)); then 5497 ble/syntax/completion-context/.add command "$wbeg" 5498 if [[ ${text:wbeg:index-wbeg} =~ $rex_param ]]; then 5499 ble/syntax/completion-context/.add variable:= "$wbeg" 5500 fi 5501 fi 5502 ble/syntax/completion-context/.check/parameter-expansion 5503 } 5504 ## @fn ble/syntax/completion-context/.check-prefix/ctx:inside-argument source 5505 ## ARGI 系統 (引数の続き) の文脈に対する補完文脈の生成 5506 ## @param[in] source 5507 _ble_syntax_bash_complete_check_prefix[CTX_ARGI]='inside-argument argument' 5508 _ble_syntax_bash_complete_check_prefix[CTX_ARGQ]='inside-argument argument' 5509 _ble_syntax_bash_complete_check_prefix[CTX_FARGI1]='inside-argument variable:w' 5510 _ble_syntax_bash_complete_check_prefix[CTX_FARGI3]='inside-argument argument' 5511 _ble_syntax_bash_complete_check_prefix[CTX_FARGQ3]='inside-argument argument' 5512 _ble_syntax_bash_complete_check_prefix[CTX_CARGI1]='inside-argument argument' 5513 _ble_syntax_bash_complete_check_prefix[CTX_CARGQ1]='inside-argument argument' 5514 _ble_syntax_bash_complete_check_prefix[CTX_CPATI]='inside-argument argument' 5515 _ble_syntax_bash_complete_check_prefix[CTX_CPATQ]='inside-argument argument' 5516 _ble_syntax_bash_complete_check_prefix[CTX_COARGI]='inside-argument variable command' 5517 _ble_syntax_bash_complete_check_prefix[CTX_VALI]='inside-argument sabbrev file' 5518 _ble_syntax_bash_complete_check_prefix[CTX_VALQ]='inside-argument sabbrev file' 5519 _ble_syntax_bash_complete_check_prefix[CTX_CONDI]='inside-argument sabbrev file option' 5520 _ble_syntax_bash_complete_check_prefix[CTX_CONDQ]='inside-argument sabbrev file' 5521 _ble_syntax_bash_complete_check_prefix[CTX_ARGVI]='inside-argument sabbrev variable:=' 5522 _ble_syntax_bash_complete_check_prefix[CTX_ARGEI]='inside-argument command:D variable:= file' 5523 function ble/syntax/completion-context/.check-prefix/ctx:inside-argument { 5524 if ((wlen>=0)); then 5525 local source 5526 for source; do 5527 ble/syntax/completion-context/.add "$source" "$wbeg" 5528 if [[ $source != argument ]]; then 5529 local sub=${text:wbeg:index-wbeg} 5530 if [[ $sub == *[=:]* ]]; then 5531 sub=${sub##*[=:]} 5532 ble/syntax/completion-context/.add "$source" "$((index-${#sub}))" 5533 fi 5534 fi 5535 done 5536 fi 5537 ble/syntax/completion-context/.check/parameter-expansion 5538 } 5539 5540 ## @fn ble/syntax/completion-context/.check-prefix/ctx:next-command 5541 ## CMDX 系統の文脈に対する補完文脈の生成 5542 _ble_syntax_bash_complete_check_prefix[CTX_CMDX]=next-command 5543 _ble_syntax_bash_complete_check_prefix[CTX_CMDX1]=next-command 5544 _ble_syntax_bash_complete_check_prefix[CTX_CMDXT]=next-command 5545 _ble_syntax_bash_complete_check_prefix[CTX_CMDXV]=next-command 5546 function ble/syntax/completion-context/.check-prefix/.test-redirection { 5547 ## @var[in] index 5548 local word=$1 5549 [[ $word =~ ^$_ble_syntax_bash_RexRedirect$ ]] || return 1 5550 # 文法的に元々リダイレクトは許されない 5551 ((ctx==CTX_CMDXC||ctx==CTX_CMDXD||ctx==CTX_CMDXD0||ctx==CTX_FARGX3)) && return 0 5552 5553 local rematch3=${BASH_REMATCH[3]} 5554 case $rematch3 in 5555 ('>&') 5556 ble/syntax/completion-context/.add fd "$index" 5557 ble/syntax/completion-context/.add file:no-fd "$index" ;; 5558 (*'&') 5559 ble/syntax/completion-context/.add fd "$index" ;; 5560 ('<<'|'<<-') 5561 ble/syntax/completion-context/.add wordlist:EOF:END:HERE "$index" ;; 5562 ('<<<'|*) 5563 ble/syntax/completion-context/.add file "$index" ;; 5564 esac 5565 return 0 5566 } 5567 function ble/syntax/completion-context/.check-prefix/ctx:next-command { 5568 # 直前の再開点が CMDX だった場合、 5569 # 現在地との間にコマンド名があればそれはコマンドである。 5570 # スペースや ;&| 等のコマンド以外の物がある可能性もある事に注意する。 5571 local word=${text:istat:index-istat} 5572 5573 # コマンドのチェック 5574 if ble/syntax:bash/simple-word/is-simple-or-open-simple "$word"; then 5575 # 単語が istat から開始している場合 5576 ble/syntax/completion-context/.add command "$istat" 5577 5578 # 変数・代入のチェック 5579 if local rex='^[_a-zA-Z][_a-zA-Z0-9]*(\+?=)?$' && [[ $word =~ $rex ]]; then 5580 if [[ $word == *= ]]; then 5581 if ((_ble_bash>=30100)) || [[ $word != *+= ]]; then 5582 # VAR=<argument>: 現在位置から argument 候補を生成する 5583 ble/syntax/completion-context/.add argument "$index" 5584 fi 5585 else 5586 # VAR<+variable>: 単語を変数名の一部と思って変数名を生成する 5587 ble/syntax/completion-context/.add variable:= "$istat" 5588 fi 5589 fi 5590 elif ble/syntax/completion-context/.check-prefix/.test-redirection; then 5591 true 5592 elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then 5593 # 単語が未だ開始していない時 (空白) 5594 shopt -q no_empty_cmd_completion || 5595 ble/syntax/completion-context/.add command "$index" 5596 fi 5597 5598 ble/syntax/completion-context/.check/parameter-expansion 5599 } 5600 ## @fn ble/syntax/completion-context/.check-prefix/ctx:next-argument 5601 ## ARGX 系統の文脈に対する補完文脈の生成 5602 _ble_syntax_bash_complete_check_prefix[CTX_ARGX]=next-argument 5603 _ble_syntax_bash_complete_check_prefix[CTX_CARGX1]=next-argument 5604 _ble_syntax_bash_complete_check_prefix[CTX_CPATX]=next-argument 5605 _ble_syntax_bash_complete_check_prefix[CTX_FARGX3]=next-argument 5606 _ble_syntax_bash_complete_check_prefix[CTX_COARGX]=next-argument 5607 _ble_syntax_bash_complete_check_prefix[CTX_ARGVX]=next-argument 5608 _ble_syntax_bash_complete_check_prefix[CTX_ARGEX]=next-argument 5609 _ble_syntax_bash_complete_check_prefix[CTX_VALX]=next-argument 5610 _ble_syntax_bash_complete_check_prefix[CTX_CONDX]=next-argument 5611 _ble_syntax_bash_complete_check_prefix[CTX_RDRS]=next-argument 5612 function ble/syntax/completion-context/.check-prefix/ctx:next-argument { 5613 local source 5614 if ((ctx==CTX_ARGX||ctx==CTX_CARGX1||ctx==CTX_FARGX3)); then 5615 source=(argument) 5616 elif ((ctx==CTX_COARGX)); then 5617 # Note: variable:w でも variable:= でもなく variable にしているのは、 5618 # coproc の後は変数名が来ても "変数代入 " か "coproc 配列名" 5619 # か分からないので、取り敢えず何も挿入しない様にする為。 5620 source=(command variable) 5621 elif ((ctx==CTX_ARGVX)); then 5622 source=(sabbrev variable:= option) 5623 elif ((ctx==CTX_ARGEX)); then 5624 source=(command:D variable:= file) 5625 elif ((ctx==CTX_CONDX)); then 5626 source=(sabbrev file option) 5627 else 5628 source=(sabbrev file) 5629 fi 5630 5631 local word=${text:istat:index-istat} 5632 if ble/syntax:bash/simple-word/is-simple-or-open-simple "$word"; then 5633 # 単語が istat から開始している場合 5634 local src 5635 for src in "${source[@]}"; do 5636 ble/syntax/completion-context/.add "$src" "$istat" 5637 done 5638 5639 if [[ ${source[0]} != argument ]]; then 5640 # 引数の途中に unquoted '=' がある場合 5641 local rex="^([^='\"\$\\{}]|\\.)*=" 5642 if [[ $word =~ $rex ]]; then 5643 word=${word:${#BASH_REMATCH}} 5644 ble/syntax/completion-context/.add rhs "$((index-${#word}))" 5645 fi 5646 fi 5647 elif ble/syntax/completion-context/.check-prefix/.test-redirection "$word"; then 5648 true 5649 elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then 5650 # 単語が未だ開始していない時 (空白) 5651 local src 5652 for src in "${source[@]}"; do 5653 ble/syntax/completion-context/.add "$src" "$index" 5654 done 5655 fi 5656 ble/syntax/completion-context/.check/parameter-expansion 5657 } 5658 ## @fn ble/syntax/completion-context/.check-prefix/ctx:next-compound 5659 ## 複合コマンドを補完します。 5660 _ble_syntax_bash_complete_check_prefix[CTX_CMDXC]=next-compound 5661 function ble/syntax/completion-context/.check-prefix/ctx:next-compound { 5662 local rex word=${text:istat:index-istat} 5663 if [[ ${text:istat:index-istat} =~ $rex_param ]]; then 5664 ble/syntax/completion-context/.add wordlist:-r:'for:select:case:if:while:until' "$istat" 5665 elif rex='^[[({]+$'; [[ $word =~ $rex ]]; then 5666 ble/syntax/completion-context/.add wordlist:-r:'(:{:((:[[' "$istat" 5667 fi 5668 } 5669 ## @fn ble/syntax/completion-context/.check-prefix/ctx:next-identifier source 5670 ## エスケープやクォートのない単純な単語に補完する文脈。 5671 ## @param[in] source 5672 _ble_syntax_bash_complete_check_prefix[CTX_FARGX1]="next-identifier variable:w" # CTX_FARGX1 → (( でなければ 変数名 5673 _ble_syntax_bash_complete_check_prefix[CTX_SARGX1]="next-identifier variable:w" 5674 function ble/syntax/completion-context/.check-prefix/ctx:next-identifier { 5675 local source=$1 word=${text:istat:index-istat} 5676 if [[ $word =~ $rex_param ]]; then 5677 ble/syntax/completion-context/.add "$source" "$istat" 5678 elif [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then 5679 # 単語が未だ開始していない時は現在位置から補完開始 5680 ble/syntax/completion-context/.add "$source" "$index" 5681 else 5682 ble/syntax/completion-context/.add none "$istat" 5683 fi 5684 } 5685 _ble_syntax_bash_complete_check_prefix[CTX_ARGX0]="next-word sabbrev" 5686 _ble_syntax_bash_complete_check_prefix[CTX_CMDX0]="next-word sabbrev" 5687 _ble_syntax_bash_complete_check_prefix[CTX_CPATX0]="next-word sabbrev" 5688 _ble_syntax_bash_complete_check_prefix[CTX_CMDXD0]="next-word wordlist:-rs:';:{:do'" 5689 _ble_syntax_bash_complete_check_prefix[CTX_CMDXD]="next-word wordlist:-rs:'{:do'" 5690 _ble_syntax_bash_complete_check_prefix[CTX_CMDXE]="next-word wordlist:-rs:'}:fi:done:esac:then:elif:else:do'" 5691 _ble_syntax_bash_complete_check_prefix[CTX_CARGX2]="next-word wordlist:-rs:'in'" 5692 _ble_syntax_bash_complete_check_prefix[CTX_CARGI2]="next-word wordlist:-rs:'in'" 5693 _ble_syntax_bash_complete_check_prefix[CTX_FARGX2]="next-word wordlist:-rs:'in:do'" 5694 _ble_syntax_bash_complete_check_prefix[CTX_FARGI2]="next-word wordlist:-rs:'in:do'" 5695 function ble/syntax/completion-context/.check-prefix/ctx:next-word { 5696 local source=$1 word=${text:istat:index-istat} rex=$'^[^ \t]*$' 5697 if [[ $word =~ ^$_ble_syntax_bash_RexSpaces$ ]]; then 5698 ble/syntax/completion-context/.add "$source" "$index" 5699 else 5700 ble/syntax/completion-context/.add "$source" "$istat" 5701 fi 5702 } 5703 ## @fn ble/syntax/completion-context/.check-prefix/ctx:time-argument { 5704 _ble_syntax_bash_complete_check_prefix[CTX_TARGX1]=time-argument 5705 _ble_syntax_bash_complete_check_prefix[CTX_TARGI1]=time-argument 5706 _ble_syntax_bash_complete_check_prefix[CTX_TARGX2]=time-argument 5707 _ble_syntax_bash_complete_check_prefix[CTX_TARGI2]=time-argument 5708 function ble/syntax/completion-context/.check-prefix/ctx:time-argument { 5709 ble/syntax/completion-context/.check/parameter-expansion 5710 ble/syntax/completion-context/.add command "$istat" 5711 if ((ctx==CTX_TARGX1)); then 5712 local rex='^-p?$' words='-p' 5713 ((_ble_bash>=50100)) && 5714 rex='^-[-p]?$' words='-p':'--' 5715 [[ ${text:istat:index-istat} =~ $rex ]] && 5716 ble/syntax/completion-context/.add wordlist:--:"$words" "$istat" 5717 elif ((ctx==CTX_TARGX2)); then 5718 local rex='^--?$' 5719 [[ ${text:istat:index-istat} =~ $rex ]] && 5720 ble/syntax/completion-context/.add wordlist:--:'--' "$istat" 5721 fi 5722 } 5723 ## @fn ble/syntax/completion-context/.check-prefix/ctx:quote 5724 _ble_syntax_bash_complete_check_prefix[CTX_QUOT]=quote 5725 function ble/syntax/completion-context/.check-prefix/ctx:quote { 5726 ble/syntax/completion-context/.check/parameter-expansion 5727 ble/syntax/completion-context/.check-prefix/ctx:quote/.check-container-word 5728 } 5729 function ble/syntax/completion-context/.check-prefix/ctx:quote/.check-container-word { 5730 # Note: CTX_QUOTE は nest の中にあるので、一旦外側に出て単語を探す。 5731 5732 local nlen=${stat[3]}; ((nlen>=0)) || return 1 5733 local inest=$((nlen<0?nlen:istat-nlen)) 5734 5735 local nest; ble/string#split-words nest "${_ble_syntax_nest[inest]}" 5736 [[ ${nest[0]} ]] || return 1 5737 5738 local wlen2=${nest[1]}; ((wlen2>=0)) || return 1 5739 local wbeg2=$((wlen2<0?wlen2:inest-wlen2)) 5740 if ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:wbeg2:index-wbeg2}"; then 5741 local wt=${nest[2]} 5742 [[ ${_ble_syntax_bash_command_EndWtype[wt]} ]] && 5743 wt=${_ble_syntax_bash_command_EndWtype[wt]} 5744 if ((wt==CTX_CMDI)); then 5745 ble/syntax/completion-context/.add command "$wbeg2" 5746 elif ((wt==CTX_ARGI||wt==CTX_ARGVI||wt==CTX_ARGEI||wt==CTX_FARGI2||wt==CTX_CARGI2)); then 5747 ble/syntax/completion-context/.add argument "$wbeg2" 5748 elif ((wt==CTX_CPATI)); then # case pattern の内部 5749 #ble/syntax/completion-context/.add file "$wbeg2" 5750 return 0 5751 fi 5752 fi 5753 } 5754 5755 ## @fn ble/syntax/completion-context/.check-prefix/ctx:redirection 5756 ## redirect の filename 部分を補完する文脈 5757 _ble_syntax_bash_complete_check_prefix[CTX_RDRF]=redirection 5758 _ble_syntax_bash_complete_check_prefix[CTX_RDRD2]=redirection 5759 _ble_syntax_bash_complete_check_prefix[CTX_RDRD]=redirection 5760 function ble/syntax/completion-context/.check-prefix/ctx:redirection { 5761 ble/syntax/completion-context/.check/parameter-expansion 5762 local p=$((wlen>=0?wbeg:istat)) 5763 if ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:p:index-p}"; then 5764 if ((ctx==CTX_RDRF)); then 5765 ble/syntax/completion-context/.add file "$p" 5766 elif ((ctx==CTX_RDRD)); then 5767 ble/syntax/completion-context/.add fd "$p" 5768 elif ((ctx==CTX_RDRD2)); then 5769 ble/syntax/completion-context/.add fd "$p" 5770 ble/syntax/completion-context/.add file:no-fd "$p" 5771 fi 5772 fi 5773 } 5774 _ble_syntax_bash_complete_check_prefix[CTX_RDRH]=here 5775 _ble_syntax_bash_complete_check_prefix[CTX_RDRI]=here 5776 function ble/syntax/completion-context/.check-prefix/ctx:here { 5777 local p=$((wlen>=0?wbeg:istat)) 5778 ble/syntax/completion-context/.add wordlist:EOF:END:HERE "$p" 5779 } 5780 5781 5782 ## @fn ble/syntax/completion-context/.check-prefix/ctx:rhs 5783 ## VAR=value の value 部分を補完する文脈 5784 _ble_syntax_bash_complete_check_prefix[CTX_VRHS]=rhs 5785 _ble_syntax_bash_complete_check_prefix[CTX_ARGVR]=rhs 5786 _ble_syntax_bash_complete_check_prefix[CTX_ARGER]=rhs 5787 _ble_syntax_bash_complete_check_prefix[CTX_VALR]=rhs 5788 function ble/syntax/completion-context/.check-prefix/ctx:rhs { 5789 ble/syntax/completion-context/.check/parameter-expansion 5790 if ((wlen>=0)); then 5791 local p=$wbeg 5792 local rex='^[_a-zA-Z0-9]+(\+?=|\[)' 5793 ((ctx==CTX_VALR)) && rex='^(\[)' 5794 if [[ ${text:p:index-p} =~ $rex ]]; then 5795 if [[ ${BASH_REMATCH[1]} == '[' ]]; then 5796 # CTX_VRHS: arr[0]=x@ arr[1]+=x@ 5797 # CTX_ARGVR: declare arr[0]=x@ arr[1]+=x@ 5798 # CTX_VALR: arr=([0]=x@ [1]+=x@) 5799 local p1=$((wbeg+${#BASH_REMATCH}-1)) 5800 if local ret; ble/syntax:bash/find-end-of-array-index "$p1" "$index"; then 5801 local p2=$ret 5802 case ${_ble_syntax_text:p2:index-p2} in 5803 (']='*) ((p=p2+2)) ;; 5804 (']+='*) ((p=p2+3)) ;; 5805 (']+') 5806 ble/syntax/completion-context/.add wordlist:-rW:'+=' "$((p2+1))" 5807 p= ;; 5808 esac 5809 fi 5810 else 5811 # CTX_VRHS: var=x@ var+=x@ 5812 # CTX_ARGVR: declare var=x@ var+=x@ 5813 ((p+=${#BASH_REMATCH})) 5814 fi 5815 fi 5816 else 5817 local p=$istat 5818 fi 5819 5820 if [[ $p ]] && ble/syntax:bash/simple-word/is-simple-or-open-simple "${text:p:index-p}"; then 5821 ble/syntax/completion-context/.add rhs "$p" 5822 fi 5823 } 5824 5825 _ble_syntax_bash_complete_check_prefix[CTX_PARAM]=param 5826 function ble/syntax/completion-context/.check-prefix/ctx:param { 5827 local tail=${text:istat:index-istat} 5828 if [[ $tail == : ]]; then 5829 return 0 5830 elif [[ $tail == '}'* ]]; then 5831 local nlen=${stat[3]} 5832 local inest=$((nlen<0?nlen:istat-nlen)) 5833 ((0<=inest&&inest<istat)) && 5834 ble/syntax/completion-context/.check-prefix "$inest" 5835 return "$?" 5836 else 5837 return 1 5838 fi 5839 } 5840 5841 ## @fn ble/syntax/completion-context/.check-prefix/ctx:expr 5842 ## 数式中の変数名を補完する文脈 5843 _ble_syntax_bash_complete_check_prefix[CTX_EXPR]=expr 5844 function ble/syntax/completion-context/.check-prefix/ctx:expr { 5845 local tail=${text:istat:index-istat} rex='[_a-zA-Z]+$' 5846 if [[ $tail =~ $rex ]]; then 5847 local p=$((index-${#BASH_REMATCH})) 5848 ble/syntax/completion-context/.add variable:a "$p" 5849 return 0 5850 elif [[ $tail == ']'* ]]; then 5851 local inest=... ntype 5852 local nlen=${stat[3]}; ((nlen>=0)) || return 1 5853 local inest=$((istat-nlen)) 5854 ble/syntax/parse/nest-type # ([in] inest; [out] ntype) 5855 5856 if [[ $ntype == [ad]'[' ]]; then 5857 # arr[...]=@ or arr=([...]=@) 5858 if [[ $tail == ']' ]]; then 5859 ble/syntax/completion-context/.add wordlist:-rW:'=' "$((istat+1))" 5860 elif ((_ble_bash>=30100)) && [[ $tail == ']+' ]]; then 5861 ble/syntax/completion-context/.add wordlist:-rW:'+=' "$((istat+1))" 5862 elif [[ $tail == ']=' || _ble_bash -ge 30100 && $tail == ']+=' ]]; then 5863 ble/syntax/completion-context/.add rhs "$index" 5864 fi 5865 fi 5866 fi 5867 } 5868 5869 ## @fn ble/syntax/completion-context/.check-prefix/ctx:expr 5870 ## ブレース展開の中での補完 5871 _ble_syntax_bash_complete_check_prefix[CTX_BRACE1]=brace 5872 _ble_syntax_bash_complete_check_prefix[CTX_BRACE2]=brace 5873 function ble/syntax/completion-context/.check-prefix/ctx:brace { 5874 # (1) CTX_BRACE{1,2} 以外になるまで nest を出る 5875 local ctx1=$ctx istat1=$istat nlen1=${stat[3]} 5876 ((nlen1>=0)) || return 1 5877 local inest1=$((istat1-nlen1)) 5878 while :; do 5879 local nest=${_ble_syntax_nest[inest1]} 5880 [[ $nest ]] || return 1 5881 ble/string#split-words nest "$nest" 5882 ctx1=${nest[0]} 5883 ((ctx1==CTX_BRACE1||ctx1==CTX_BRACE2)) || break 5884 inest1=${nest[3]} 5885 ((inest1>=0)) || return 1 5886 done 5887 5888 # (2) 直前の stat 5889 for ((istat1=inest1;1;istat1--)); do 5890 ((istat1>=0)) || return 1 5891 [[ ${_ble_syntax_stat[istat1]} ]] && break 5892 done 5893 5894 # (3) 単語の開始点 5895 local stat1 5896 ble/string#split-words stat1 "${_ble_syntax_stat[istat1]}" 5897 local wlen=${stat1[1]} 5898 local wbeg=$((wlen>=0?istat1-wlen:istat1)) 5899 5900 ble/syntax/completion-context/.check/parameter-expansion 5901 ble/syntax/completion-context/.add argument "$wbeg" 5902 } 5903 5904 5905 ## @fn ble/syntax/completion-context/.search-last-istat index 5906 ## @param[in] index 5907 ## @var[out] ret 5908 function ble/syntax/completion-context/.search-last-istat { 5909 local index=$1 istat 5910 for ((istat=index;istat>=0;istat--)); do 5911 if [[ ${_ble_syntax_stat[istat]} ]]; then 5912 ret=$istat 5913 return 0 5914 fi 5915 done 5916 ret= 5917 return 1 5918 } 5919 5920 ## @fn ble/syntax/completion-context/.check-prefix from 5921 ## @param[in,opt] from 5922 ## @var[in] text 5923 ## @var[in] index 5924 ## @var[out] sources 5925 function ble/syntax/completion-context/.check-prefix { 5926 local rex_param='^[_a-zA-Z][_a-zA-Z0-9]*$' 5927 local from=${1:-$((index-1))} 5928 5929 local ret 5930 ble/syntax/completion-context/.search-last-istat "$from" || return 1 5931 local istat=$ret stat 5932 ble/string#split-words stat "${_ble_syntax_stat[istat]}" 5933 [[ ${stat[0]} ]] || return 1 5934 5935 local ctx=${stat[0]} wlen=${stat[1]} 5936 local wbeg=$((wlen<0?wlen:istat-wlen)) 5937 local name=${_ble_syntax_bash_complete_check_prefix[ctx]} 5938 if [[ $name ]]; then 5939 builtin eval "ble/syntax/completion-context/.check-prefix/ctx:$name" 5940 fi 5941 } 5942 5943 ## @fn ble/syntax/completion-context/.check-here 5944 ## 現在地点を開始点とする補完の可能性を列挙します 5945 ## @var[in] text 5946 ## @var[in] index 5947 ## @var[out] sources 5948 function ble/syntax/completion-context/.check-here { 5949 ((${#sources[*]})) && return 0 5950 local -a stat 5951 ble/string#split-words stat "${_ble_syntax_stat[index]}" 5952 if [[ ${stat[0]} ]]; then 5953 # Note: ここで CTX_CMDI や CTX_ARGI は処理しない。既に check-prefix で引っ 5954 # かかっている筈だから。 5955 # 5956 # Note (#D1690): 文句が出たので 当初引数類の補完はその場で開始しない事にし 5957 # たが、すると空文字列から補完を開始できなくなってしまった。やはり、ここ 5958 # で引数の補完を開始しなければならない。 5959 # 5960 # そもそも .check-prefix で補完文脈を生成できる時は、入力済みの内容に拘ら 5961 # ず補完文脈を生成するべきである。それにより、.check-here に到達するのは 5962 # 補完文脈が他に生成しようがない状況に限定される。この時、実は 5963 # .check-here の側は修正は必要なかった。 5964 local ctx=${stat[0]} 5965 if ((ctx==CTX_CMDX||ctx==CTX_CMDXV||ctx==CTX_CMDX1||ctx==CTX_CMDXT)); then 5966 if ! shopt -q no_empty_cmd_completion; then 5967 ble/syntax/completion-context/.add command "$index" 5968 ble/syntax/completion-context/.add variable:= "$index" 5969 fi 5970 elif ((ctx==CTX_CMDXC)); then 5971 ble/syntax/completion-context/.add wordlist:-rs:'(:{:((:[[:for:select:case:if:while:until' "$index" 5972 elif ((ctx==CTX_CMDXE)); then 5973 ble/syntax/completion-context/.add wordlist:-rs:'}:fi:done:esac:then:elif:else:do' "$index" 5974 elif ((ctx==CTX_CMDXD0)); then 5975 ble/syntax/completion-context/.add wordlist:-rs:';:{:do' "$index" 5976 elif ((ctx==CTX_CMDXD)); then 5977 ble/syntax/completion-context/.add wordlist:-rs:'{:do' "$index" 5978 elif ((ctx==CTX_ARGX0||ctx==CTX_CPATX0||ctx==CTX_CMDX0)); then 5979 ble/syntax/completion-context/.add sabbrev "$index" 5980 elif ((ctx==CTX_ARGX||ctx==CTX_CARGX1||ctx==CTX_FARGX3)); then 5981 ble/syntax/completion-context/.add argument "$index" 5982 elif ((ctx==CTX_FARGX1||ctx==CTX_SARGX1)); then 5983 ble/syntax/completion-context/.add variable:w "$index" 5984 ble/syntax/completion-context/.add sabbrev "$index" 5985 elif ((ctx==CTX_ARGVX)); then 5986 # declare @ 5987 ble/syntax/completion-context/.add variable:= "$index" 5988 ble/syntax/completion-context/.add option "$index" 5989 ble/syntax/completion-context/.add sabbrev "$index" 5990 elif ((ctx==CTX_ARGEX)); then 5991 # eval @, eval echo @ 5992 ble/syntax/completion-context/.add variable:= "$index" 5993 ble/syntax/completion-context/.add command:D "$index" 5994 ble/syntax/completion-context/.add file "$index" 5995 elif ((ctx==CTX_CARGX2)); then 5996 # case a @ 5997 ble/syntax/completion-context/.add wordlist:-rs:'in' "$index" 5998 elif ((ctx==CTX_FARGX2)); then 5999 # for a @ 6000 ble/syntax/completion-context/.add wordlist:-rs:'in:do' "$index" 6001 elif ((ctx==CTX_TARGX1)); then 6002 # time @ 6003 local words='-p' 6004 ((_ble_bash>=50100)) && words='-p':'--' 6005 ble/syntax/completion-context/.add command "$index" 6006 ble/syntax/completion-context/.add wordlist:--:"$words" "$index" 6007 elif ((ctx==CTX_TARGX2)); then 6008 # time -p @ 6009 ble/syntax/completion-context/.add command "$index" 6010 ble/syntax/completion-context/.add wordlist:--:'--' "$index" 6011 elif ((ctx==CTX_COARGX)); then 6012 # coproc @ 6013 ble/syntax/completion-context/.add variable:w "$index" 6014 ble/syntax/completion-context/.add command "$index" 6015 elif ((ctx==CTX_CONDX)); then 6016 # [[ @ 6017 ble/syntax/completion-context/.add sabbrev "$index" 6018 ble/syntax/completion-context/.add option "$index" 6019 ble/syntax/completion-context/.add file "$index" 6020 elif ((ctx==CTX_CPATI||ctx==CTX_RDRF||ctx==CTX_RDRS)); then 6021 ble/syntax/completion-context/.add file "$index" 6022 elif ((ctx==CTX_RDRD)); then 6023 ble/syntax/completion-context/.add fd "$index" 6024 elif ((ctx==CTX_RDRD2)); then 6025 ble/syntax/completion-context/.add fd "$index" 6026 ble/syntax/completion-context/.add file:no-fd "$index" 6027 elif ((ctx==CTX_RDRH||ctx==CTX_RDRI)); then 6028 ble/syntax/completion-context/.add wordlist:EOF:END:HERE "$index" 6029 elif ((ctx==CTX_VRHS||ctx==CTX_ARGVR||ctx==CTX_ARGER||ctx==CTX_VALR)); then 6030 ble/syntax/completion-context/.add rhs "$index" 6031 fi 6032 fi 6033 } 6034 6035 ## @fn ble/syntax/completion-context/generate 6036 ## @var[out] sources[] 6037 function ble/syntax/completion-context/generate { 6038 local text=$1 index=$2 6039 sources=() 6040 ((index<0&&(index=0))) 6041 6042 ble/syntax/completion-context/.check-prefix 6043 ble/syntax/completion-context/.check-here 6044 } 6045 6046 #------------------------------------------------------------------------------ 6047 # extract-command 6048 6049 ## @fn ble/syntax:bash/extract-command/.register-word 6050 ## @var[in,out] comp_words, comp_line, comp_point, comp_cword 6051 ## @var[in] TE_i TE_nofs wbegin wlen 6052 function ble/syntax:bash/extract-command/.register-word { 6053 local wtxt=${_ble_syntax_text:wbegin:wlen} 6054 if [[ ! $comp_cword ]] && ((wbegin<=EC_pos)); then 6055 if ((EC_pos<=wbegin+wlen)); then 6056 comp_cword=${#comp_words[@]} 6057 comp_point=$((${#comp_line}+wbegin+wlen-EC_pos)) 6058 comp_line="$wtxt$comp_line" 6059 ble/array#push comp_words "$wtxt" 6060 else 6061 comp_cword=${#comp_words[@]} 6062 comp_point=${#comp_line} 6063 comp_line="$wtxt $comp_line" 6064 ble/array#push comp_words "" "$wtxt" 6065 fi 6066 else 6067 comp_line="$wtxt$comp_line" 6068 ble/array#push comp_words "$wtxt" 6069 fi 6070 [[ $EC_opts == *:treeinfo:* ]] && 6071 ble/array#push tree_words "$TE_i:$TE_nofs" 6072 } 6073 6074 function ble/syntax:bash/extract-command/.construct-proc { 6075 if [[ $wtype =~ ^[0-9]+$ ]]; then 6076 if ((wtype==CTX_CMDI||wtype==CTX_CMDX0)); then 6077 if ((EC_pos<wbegin)); then 6078 comp_line= comp_point= comp_cword= comp_words=() 6079 else 6080 ble/syntax:bash/extract-command/.register-word 6081 ble/syntax/tree-enumerate-break 6082 EC_found=1 6083 return 0 6084 fi 6085 elif ((wtype==CTX_ARGI||wtype==CTX_ARGVI||wtype==CTX_ARGEI||wtype==ATTR_VAR)); then 6086 ble/syntax:bash/extract-command/.register-word 6087 comp_line=" $comp_line" 6088 fi 6089 fi 6090 } 6091 6092 function ble/syntax:bash/extract-command/.construct { 6093 comp_line= comp_point= comp_cword= comp_words=() 6094 6095 if [[ $1 == nested ]]; then 6096 ble/syntax/tree-enumerate-children \ 6097 ble/syntax:bash/extract-command/.construct-proc 6098 else 6099 ble/syntax/tree-enumerate \ 6100 ble/syntax:bash/extract-command/.construct-proc 6101 fi 6102 6103 ble/array#reverse comp_words 6104 ((comp_cword=${#comp_words[@]}-1-comp_cword, 6105 comp_point=${#comp_line}-comp_point)) 6106 [[ $EC_opts == *:treeinfo:* ]] && 6107 ble/array#reverse tree_words 6108 } 6109 6110 ## (tree-enumerate-proc) ble/syntax:bash/extract-command/.scan 6111 function ble/syntax:bash/extract-command/.scan { 6112 ((EC_pos<wbegin)) && return 0 6113 6114 if ((wbegin+wlen<EC_pos)); then 6115 ble/syntax/tree-enumerate-break 6116 else 6117 local EC_has_word= 6118 ble/syntax/tree-enumerate-children \ 6119 ble/syntax:bash/extract-command/.scan 6120 local has_word=$EC_has_word 6121 ble/util/unlocal EC_has_word 6122 6123 if [[ $has_word && ! $EC_found ]]; then 6124 ble/syntax:bash/extract-command/.construct nested 6125 ble/syntax/tree-enumerate-break 6126 fi 6127 fi 6128 6129 if [[ $wtype =~ ^[0-9]+$ && ! $EC_has_word ]]; then 6130 EC_has_word=$wtype 6131 return 0 6132 fi 6133 } 6134 6135 ## @fn ble/syntax:bash/extract-command index [opts] 6136 ## @param[in] index 6137 ## @param[in] opts 6138 ## treeinfo 6139 ## コマンドを構成する各単語の構文木の情報を取得します。 6140 ## 以下の配列に結果を格納します。 6141 ## @arr[out] tree_words 6142 ## 6143 ## @var[out] comp_cword comp_words comp_line comp_point 6144 function ble/syntax:bash/extract-command { 6145 local EC_pos=$1 EC_opts=:$2: 6146 local EC_found= 6147 6148 local EC_has_word= 6149 ble/syntax/tree-enumerate \ 6150 ble/syntax:bash/extract-command/.scan 6151 if [[ ! $EC_found && $EC_has_word ]]; then 6152 ble/syntax:bash/extract-command/.construct 6153 fi 6154 [[ $EC_found ]] 6155 } 6156 6157 #------------------------------------------------------------------------------ 6158 # extract-command-by-noderef 6159 6160 ## @fn ble/syntax/tree#previous-sibling i[:nofs] [opts] 6161 ## @fn ble/syntax/tree#next-sibling i[:nofs] [opts] 6162 ## 指定した位置の単語について、前または次の兄弟ノードを取得します。 6163 ## 6164 ## @param[in] i:nofs 6165 ## 6166 ## @param[in] opts 6167 ## コロン区切りのオプションです。 6168 ## 6169 ## wvars が指定されている時、以下の変数に見つかった単語の情報を格納します。 6170 ## @var[out] wtype wlen wbeg wend wattr 6171 ## 6172 ## @var[out] ret=i:nofs 6173 ## 6174 function ble/syntax/tree#previous-sibling { 6175 local i0=${1%%:*} nofs0=0 opts=:$2: 6176 [[ $1 == *:* ]] && nofs0=${1#*:} 6177 6178 local node 6179 ble/string#split-words node "${_ble_syntax_tree[i0-1]}" 6180 ble-assert '((${#node[@]}>nofs0))' "Broken AST: tree-node info missing at $((i0-1))[$nofs0]" || return 1 6181 local tplen=${node[nofs0+3]} 6182 ((tplen>=0)) || return 1 6183 6184 local i=$((i0-tplen)) nofs=0 6185 ret=$i:$nofs 6186 if [[ $opts == *:wvars:* ]]; then 6187 ble/string#split-words node "${_ble_syntax_tree[i-1]}" 6188 ble-assert '((${#node[@]}>nofs))' "Broken AST: tree-node info missing at $((i-1))[$nofs]" || return 1 6189 wtype=${node[nofs]} 6190 wlen=${node[nofs+1]} 6191 ((wbeg=i-wlen,wend=i)) 6192 wattr=${node[nofs+4]} 6193 fi 6194 return 0 6195 } 6196 function ble/syntax/tree#next-sibling { 6197 local i0=${1%%:*} nofs0=0 opts=:$2: 6198 [[ $1 == *:* ]] && nofs0=${1#*:} 6199 6200 # 自分が末尾にいるので弟はない 6201 ((nofs0)) && return 1 6202 6203 local iN=${#_ble_syntax_text} i nofs node 6204 for ((i=i0+1;i<=iN;i++)); do 6205 [[ ${_ble_syntax_tree[i-1]} ]] || continue 6206 ble/string#split-words node "${_ble_syntax_tree[i-1]}" 6207 nofs=${#node[@]} 6208 while (((nofs-=_ble_syntax_TREE_WIDTH)>=0)); do 6209 # node = (wtype wlen tclen tplen wattr)+ 6210 if ((i0==i-node[nofs+2])); then 6211 # 親が見つかったので弟はいない 6212 return 1 6213 elif ((i0==i-node[nofs+3])); then 6214 # これが弟 6215 ret=$i:$nofs 6216 if [[ $opts == *:wvars:* ]]; then 6217 wtype=${node[nofs]} 6218 wlen=${node[nofs+1]} 6219 ((wbeg=i-wlen,wend=i)) 6220 wattr=${node[nofs+4]} 6221 fi 6222 return 0 6223 fi 6224 done 6225 done 6226 return 1 6227 } 6228 6229 ## @fn ble/syntax:bash/extract-command-by-noderef i[:nofs] [opts] 6230 ## @param[in] i:nofs 6231 ## 単語を指定します。 6232 ## 6233 ## @param[in] opts 6234 ## コロン区切りのオプションです。 6235 ## 6236 ## treeinfo が指定された時は以下の配列に各単語の位置を 6237 ## "i:nofs" の形式で格納します。 6238 ## @var[out] tree_words 6239 ## 6240 ## @var[out] comp_words comp_line comp_cword comp_point 6241 ## 再構築したコマンド情報を格納します。 6242 ## カーソル位置は指定した単語の末尾にあると仮定します。 6243 ## 6244 function ble/syntax:bash/extract-command-by-noderef { 6245 local i=${1%%:*} nofs=0 opts=:$2: 6246 [[ $1 == *:* ]] && nofs=${1#*:} 6247 6248 # initialize output 6249 comp_words=() 6250 tree_words=() 6251 comp_line= 6252 comp_cword=0 6253 comp_point=0 6254 6255 local ExprIsArgument='wtype==CTX_ARGI||wtype==CTX_ARGVI||wtype==CTX_ARGEI||wtype==ATTR_VAR' 6256 6257 # 自ノードの追加 6258 local ret node wtype wlen wbeg wend wattr 6259 ble/string#split-words node "${_ble_syntax_tree[i-1]}" 6260 wtype=${node[nofs]} wlen=${node[nofs+1]} 6261 [[ ! ${wtype//[0-9]} ]] && ((wtype==CTX_CMDI||ExprIsArgument)) || return 1 6262 ble/array#push comp_words "${_ble_syntax_text:i-wlen:wlen}" 6263 [[ $opts == *:treeinfo:* ]] && 6264 ble/array#push tree_words "$i:$nofs" 6265 6266 # 兄ノードの追加 6267 ret=$i:$nofs 6268 while 6269 { [[ ${wtype//[0-9]} ]] || ((wtype!=CTX_CMDI)); } && 6270 ble/syntax/tree#previous-sibling "$ret" wvars 6271 do 6272 [[ ! ${wtype//[0-9]} ]] || continue 6273 if ((wtype==CTX_CMDI||ExprIsArgument)); then 6274 ble/array#push comp_words "${_ble_syntax_text:wbeg:wlen}" 6275 [[ $opts == *:treeinfo:* ]] && 6276 ble/array#push tree_words "$ret" 6277 fi 6278 done 6279 ble/array#reverse comp_words 6280 [[ $opts == *:treeinfo:* ]] && 6281 ble/array#reverse tree_words 6282 6283 # 現在位置 (comp_cword, comp_point) 6284 ((comp_cword=${#comp_words[@]}-1)) 6285 6286 # 弟ノードの探索 6287 ret=$i:$nofs 6288 while ble/syntax/tree#next-sibling "$ret" wvars; do 6289 [[ ! ${wtype//[0-9]} ]] || continue 6290 ((wtype==CTX_CMDI)) && break 6291 if ((ExprIsArgument)); then 6292 ble/array#push comp_words "${_ble_syntax_text:wbeg:wlen}" 6293 [[ $opts == *:treeinfo:* ]] && 6294 ble/array#push tree_words "$ret" 6295 fi 6296 done 6297 6298 local IFS=$_ble_term_IFS 6299 comp_line="${comp_words[*]}" 6300 local tmp="${comp_words[*]::comp_cword+1}" 6301 comp_point=${#tmp} 6302 } 6303 6304 #============================================================================== 6305 # 6306 # syntax-highlight 6307 # 6308 #============================================================================== 6309 6310 # 遅延初期化対象 6311 _ble_syntax_attr2iface=() 6312 6313 # 遅延初期化子 6314 function ble/syntax/attr2iface/color_defface.onload { 6315 function ble/syntax/attr2iface/.define { 6316 ((_ble_syntax_attr2iface[$1]=_ble_faces__$2)) 6317 } 6318 6319 ble/syntax/attr2iface/.define CTX_ARGX syntax_default 6320 ble/syntax/attr2iface/.define CTX_ARGX0 syntax_default 6321 ble/syntax/attr2iface/.define CTX_ARGI syntax_default 6322 ble/syntax/attr2iface/.define CTX_ARGQ syntax_default 6323 ble/syntax/attr2iface/.define CTX_ARGVX syntax_default 6324 ble/syntax/attr2iface/.define CTX_ARGVI syntax_default 6325 ble/syntax/attr2iface/.define CTX_ARGVR syntax_default 6326 ble/syntax/attr2iface/.define CTX_ARGEX syntax_default 6327 ble/syntax/attr2iface/.define CTX_ARGEI syntax_default 6328 ble/syntax/attr2iface/.define CTX_ARGER syntax_default 6329 ble/syntax/attr2iface/.define CTX_CMDX syntax_default 6330 ble/syntax/attr2iface/.define CTX_CMDX0 syntax_default 6331 ble/syntax/attr2iface/.define CTX_CMDX1 syntax_default 6332 ble/syntax/attr2iface/.define CTX_CMDXT syntax_default 6333 ble/syntax/attr2iface/.define CTX_CMDXC syntax_default 6334 ble/syntax/attr2iface/.define CTX_CMDXE syntax_default 6335 ble/syntax/attr2iface/.define CTX_CMDXD syntax_default 6336 ble/syntax/attr2iface/.define CTX_CMDXD0 syntax_default 6337 ble/syntax/attr2iface/.define CTX_CMDXV syntax_default 6338 ble/syntax/attr2iface/.define CTX_CMDI syntax_command 6339 ble/syntax/attr2iface/.define CTX_VRHS syntax_default 6340 ble/syntax/attr2iface/.define CTX_QUOT syntax_quoted 6341 ble/syntax/attr2iface/.define CTX_EXPR syntax_expr 6342 ble/syntax/attr2iface/.define ATTR_ERR syntax_error 6343 ble/syntax/attr2iface/.define ATTR_VAR syntax_varname 6344 ble/syntax/attr2iface/.define ATTR_QDEL syntax_quotation 6345 ble/syntax/attr2iface/.define ATTR_QESC syntax_escape 6346 ble/syntax/attr2iface/.define ATTR_DEF syntax_default 6347 ble/syntax/attr2iface/.define ATTR_DEL syntax_delimiter 6348 ble/syntax/attr2iface/.define CTX_PARAM syntax_param_expansion 6349 ble/syntax/attr2iface/.define CTX_PWORD syntax_default 6350 ble/syntax/attr2iface/.define CTX_PWORDE syntax_error 6351 ble/syntax/attr2iface/.define CTX_PWORDR syntax_default 6352 ble/syntax/attr2iface/.define ATTR_HISTX syntax_history_expansion 6353 ble/syntax/attr2iface/.define ATTR_FUNCDEF syntax_function_name 6354 ble/syntax/attr2iface/.define CTX_VALX syntax_default 6355 ble/syntax/attr2iface/.define CTX_VALI syntax_default 6356 ble/syntax/attr2iface/.define CTX_VALR syntax_default 6357 ble/syntax/attr2iface/.define CTX_VALQ syntax_default 6358 ble/syntax/attr2iface/.define CTX_CONDX syntax_default 6359 ble/syntax/attr2iface/.define CTX_CONDI syntax_default 6360 ble/syntax/attr2iface/.define CTX_CONDQ syntax_default 6361 ble/syntax/attr2iface/.define ATTR_COMMENT syntax_comment 6362 ble/syntax/attr2iface/.define CTX_CASE syntax_default 6363 ble/syntax/attr2iface/.define CTX_PATN syntax_default 6364 ble/syntax/attr2iface/.define ATTR_GLOB syntax_glob 6365 ble/syntax/attr2iface/.define CTX_BRAX syntax_default 6366 ble/syntax/attr2iface/.define ATTR_BRACE syntax_brace 6367 ble/syntax/attr2iface/.define CTX_BRACE1 syntax_default 6368 ble/syntax/attr2iface/.define CTX_BRACE2 syntax_default 6369 ble/syntax/attr2iface/.define ATTR_TILDE syntax_tilde 6370 6371 # for var in ... / case arg in / time -p -- 6372 ble/syntax/attr2iface/.define CTX_SARGX1 syntax_default 6373 ble/syntax/attr2iface/.define CTX_FARGX1 syntax_default 6374 ble/syntax/attr2iface/.define CTX_FARGX2 syntax_default 6375 ble/syntax/attr2iface/.define CTX_FARGX3 syntax_default 6376 ble/syntax/attr2iface/.define CTX_FARGI1 syntax_varname 6377 ble/syntax/attr2iface/.define CTX_FARGI2 command_keyword 6378 ble/syntax/attr2iface/.define CTX_FARGI3 syntax_default 6379 ble/syntax/attr2iface/.define CTX_FARGQ3 syntax_default 6380 6381 ble/syntax/attr2iface/.define CTX_CARGX1 syntax_default 6382 ble/syntax/attr2iface/.define CTX_CARGX2 syntax_default 6383 ble/syntax/attr2iface/.define CTX_CARGI1 syntax_default 6384 ble/syntax/attr2iface/.define CTX_CARGQ1 syntax_default 6385 ble/syntax/attr2iface/.define CTX_CARGI2 command_keyword 6386 ble/syntax/attr2iface/.define CTX_CPATX syntax_default 6387 ble/syntax/attr2iface/.define CTX_CPATI syntax_default 6388 ble/syntax/attr2iface/.define CTX_CPATQ syntax_default 6389 ble/syntax/attr2iface/.define CTX_CPATX0 syntax_default 6390 6391 ble/syntax/attr2iface/.define CTX_TARGX1 syntax_default 6392 ble/syntax/attr2iface/.define CTX_TARGX2 syntax_default 6393 ble/syntax/attr2iface/.define CTX_TARGI1 syntax_default 6394 ble/syntax/attr2iface/.define CTX_TARGI2 syntax_default 6395 6396 ble/syntax/attr2iface/.define CTX_COARGX syntax_default 6397 ble/syntax/attr2iface/.define CTX_COARGI syntax_command 6398 6399 # redirection words 6400 ble/syntax/attr2iface/.define CTX_RDRF syntax_default 6401 ble/syntax/attr2iface/.define CTX_RDRD syntax_default 6402 ble/syntax/attr2iface/.define CTX_RDRD2 syntax_default 6403 ble/syntax/attr2iface/.define CTX_RDRS syntax_default 6404 6405 # here documents 6406 ble/syntax/attr2iface/.define CTX_RDRH syntax_document_begin 6407 ble/syntax/attr2iface/.define CTX_RDRI syntax_document_begin 6408 ble/syntax/attr2iface/.define CTX_HERE0 syntax_document 6409 ble/syntax/attr2iface/.define CTX_HERE1 syntax_document 6410 6411 ble/syntax/attr2iface/.define ATTR_CMD_BOLD command_builtin_dot 6412 ble/syntax/attr2iface/.define ATTR_CMD_BUILTIN command_builtin 6413 ble/syntax/attr2iface/.define ATTR_CMD_ALIAS command_alias 6414 ble/syntax/attr2iface/.define ATTR_CMD_FUNCTION command_function 6415 ble/syntax/attr2iface/.define ATTR_CMD_FILE command_file 6416 ble/syntax/attr2iface/.define ATTR_CMD_JOBS command_jobs 6417 ble/syntax/attr2iface/.define ATTR_CMD_DIR command_directory 6418 ble/syntax/attr2iface/.define ATTR_KEYWORD command_keyword 6419 ble/syntax/attr2iface/.define ATTR_KEYWORD_BEGIN command_keyword 6420 ble/syntax/attr2iface/.define ATTR_KEYWORD_END command_keyword 6421 ble/syntax/attr2iface/.define ATTR_KEYWORD_MID command_keyword 6422 ble/syntax/attr2iface/.define ATTR_FILE_DIR filename_directory 6423 ble/syntax/attr2iface/.define ATTR_FILE_STICKY filename_directory_sticky 6424 ble/syntax/attr2iface/.define ATTR_FILE_LINK filename_link 6425 ble/syntax/attr2iface/.define ATTR_FILE_ORPHAN filename_orphan 6426 ble/syntax/attr2iface/.define ATTR_FILE_FILE filename_other 6427 ble/syntax/attr2iface/.define ATTR_FILE_SETUID filename_setuid 6428 ble/syntax/attr2iface/.define ATTR_FILE_SETGID filename_setgid 6429 ble/syntax/attr2iface/.define ATTR_FILE_EXEC filename_executable 6430 ble/syntax/attr2iface/.define ATTR_FILE_WARN filename_warning 6431 ble/syntax/attr2iface/.define ATTR_FILE_FIFO filename_pipe 6432 ble/syntax/attr2iface/.define ATTR_FILE_SOCK filename_socket 6433 ble/syntax/attr2iface/.define ATTR_FILE_BLK filename_block 6434 ble/syntax/attr2iface/.define ATTR_FILE_CHR filename_character 6435 ble/syntax/attr2iface/.define ATTR_FILE_URL filename_url 6436 ble/syntax/attr2iface/.define ATTR_VAR_UNSET varname_unset 6437 ble/syntax/attr2iface/.define ATTR_VAR_EMPTY varname_empty 6438 ble/syntax/attr2iface/.define ATTR_VAR_NUMBER varname_number 6439 ble/syntax/attr2iface/.define ATTR_VAR_EXPR varname_expr 6440 ble/syntax/attr2iface/.define ATTR_VAR_ARRAY varname_array 6441 ble/syntax/attr2iface/.define ATTR_VAR_HASH varname_hash 6442 ble/syntax/attr2iface/.define ATTR_VAR_READONLY varname_readonly 6443 ble/syntax/attr2iface/.define ATTR_VAR_TRANSFORM varname_transform 6444 ble/syntax/attr2iface/.define ATTR_VAR_EXPORT varname_export 6445 } 6446 blehook/eval-after-load color_defface ble/syntax/attr2iface/color_defface.onload 6447 6448 #------------------------------------------------------------------------------ 6449 # ble/syntax/highlight/cmdtype 6450 6451 6452 ## @fn ble/syntax/highlight/cmdtype1 command_type command 6453 ## 指定したコマンドに対する属性を決定します。 6454 ## @param[in] command_type 6455 ## builtin type -t command の結果を指定します。 6456 ## @param[in] command 6457 ## コマンド名です。 6458 ## @var[out] type 6459 function ble/syntax/highlight/cmdtype1 { 6460 type=$1 6461 local cmd=$2 6462 case $type:$cmd in 6463 (builtin::|builtin:.) 6464 # 見にくいので太字にする 6465 ((type=ATTR_CMD_BOLD)) ;; 6466 (builtin:*) 6467 ((type=ATTR_CMD_BUILTIN)) ;; 6468 (alias:*) 6469 ((type=ATTR_CMD_ALIAS)) ;; 6470 (function:*) 6471 ((type=ATTR_CMD_FUNCTION)) ;; 6472 (file:*) 6473 ((type=ATTR_CMD_FILE)) ;; 6474 (keyword:*) 6475 ((type=ATTR_KEYWORD)) ;; 6476 (*:%*) 6477 # jobs 6478 ble/util/joblist.check 6479 if jobs -- "$cmd" &>/dev/null; then 6480 ((type=ATTR_CMD_JOBS)) 6481 else 6482 ((type=ATTR_ERR)) 6483 fi ;; 6484 (*) 6485 if [[ -d $cmd ]] && shopt -q autocd &>/dev/null; then 6486 ((type=ATTR_CMD_DIR)) 6487 else 6488 ((type=ATTR_ERR)) 6489 fi ;; 6490 esac 6491 } 6492 6493 # #D1341 #D1355 #D1440 locale 対策 6494 function ble/syntax/highlight/cmdtype/.jobs { local LC_ALL=C; jobs; } 6495 ble/function#suppress-stderr ble/syntax/highlight/cmdtype/.jobs 6496 function ble/syntax/highlight/cmdtype/.is-job-name { 6497 ble/util/joblist.check 6498 6499 local value=$1 word=$2 6500 if [[ $value == '%'* ]] && jobs -- "$value" &>/dev/null; then 6501 return 0 6502 fi 6503 6504 local quote=\'\"\\\` 6505 if [[ ${auto_resume+set} && $word != *["$quote"]* ]]; then 6506 if [[ $auto_resume == exact ]]; then 6507 local jobs job ret 6508 ble/util/assign-array jobs 'ble/syntax/highlight/cmdtype/.jobs' 6509 for job in "${jobs[@]}"; do 6510 ble/string#trim "${job#*' '}" 6511 ble/string#trim "${ret#*' '}" 6512 [[ $value == "$ret" ]] && return 0 6513 done 6514 return 1 6515 elif [[ $auto_resume == substring ]]; then 6516 jobs -- "%?$value" &>/dev/null; return "$?" 6517 else 6518 jobs -- "%$value" &>/dev/null; return "$?" 6519 fi 6520 fi 6521 6522 return 1 6523 } 6524 function ble/syntax/highlight/cmdtype/.impl { 6525 local cmd=$1 word=$2 6526 local cmd_type; ble/util/type cmd_type "$cmd" 6527 ble/syntax/highlight/cmdtype1 "$cmd_type" "$cmd" 6528 6529 if [[ $type == "$ATTR_CMD_ALIAS" && $cmd != "$word" ]]; then 6530 # alias を \ で無効化している場合は 6531 # unalias して再度 check (2fork) 6532 type=$( 6533 builtin unalias "$cmd" 6534 ble/util/type cmd_type "$cmd" 6535 ble/syntax/highlight/cmdtype1 "$cmd_type" "$cmd" 6536 printf %s "$type") 6537 elif ble/syntax/highlight/cmdtype/.is-job-name "$cmd" "$word"; then 6538 # %() { :; } として 関数を定義できるが jobs の方が優先される。 6539 # (% という名の関数を呼び出す方法はない?) 6540 # でも % で始まる物が keyword になる事はそもそも無いような。 6541 ((type=ATTR_CMD_JOBS)) 6542 elif [[ $type == "$ATTR_KEYWORD" ]]; then 6543 # Note: 予約語 (keyword) の時は構文解析の時点で着色しているのでコマンドとしての着色は行わない。 6544 # 関数 ble/syntax/highlight/cmdtype が呼び出されたとすれば、コマンドとしての文脈である。 6545 # 予約語がコマンドとして取り扱われるのは、クォートされていたか変数代入やリダイレクトの後だった時。 6546 # この時 type -a -t の二番目の候補を用いて種類を決定する #D1406 6547 ble/syntax/highlight/cmdtype1 "${cmd_type[1]}" "$cmd" 6548 fi 6549 } 6550 6551 ## @fn ble/syntax/highlight/cmdtype cmd word 6552 ## @param[in] cmd 6553 ## シェル展開・クォート除去を実行した後の文字列を指定します。 6554 ## @param[in] word 6555 ## シェル展開・クォート除去を実行する前の文字列を指定します。 6556 ## @var[out] type 6557 6558 # Note: 連想配列 _ble_syntax_highlight_filetype は core-syntax-def.sh で先に定義される。 6559 _ble_syntax_highlight_filetype_version=-1 6560 function ble/syntax/highlight/cmdtype { 6561 local cmd=$1 word=$2 6562 6563 # check cache 6564 if ((_ble_syntax_highlight_filetype_version!=_ble_edit_LINENO)); then 6565 ble/gdict#clear _ble_syntax_highlight_filetype 6566 ((_ble_syntax_highlight_filetype_version=_ble_edit_LINENO)) 6567 fi 6568 6569 if local ret; ble/gdict#get _ble_syntax_highlight_filetype "$word"; then 6570 type=$ret 6571 return 0 6572 fi 6573 6574 ble/syntax/highlight/cmdtype/.impl "$cmd" "$word" 6575 ble/gdict#set _ble_syntax_highlight_filetype "$word" "$type" 6576 } 6577 6578 #------------------------------------------------------------------------------ 6579 # ble/syntax/highlight/filetype 6580 6581 ## @fn ble/syntax/highlight/filetype filename 6582 ## @var[out] type 6583 function ble/syntax/highlight/filetype { 6584 type= 6585 local file=$1 6586 6587 # Note: #D1168 Cygwin では // で始まるパスはとても遅い 6588 if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $file == //* ]]; then 6589 [[ $file == // ]] && ((type=ATTR_FILE_DIR)) 6590 [[ $type ]]; return "$?" 6591 fi 6592 6593 if [[ -h $file ]]; then 6594 if [[ -e $file ]]; then 6595 ((type=ATTR_FILE_LINK)) 6596 else 6597 ((type=ATTR_FILE_ORPHAN)) 6598 fi 6599 elif [[ -e $file ]]; then 6600 if [[ -d $file ]]; then 6601 if [[ -k $file ]]; then 6602 ((type=ATTR_FILE_STICKY)) 6603 elif [[ -h ${file%/} ]]; then 6604 ((type=ATTR_FILE_LINK)) 6605 else 6606 ((type=ATTR_FILE_DIR)) 6607 fi 6608 elif [[ -f $file ]]; then 6609 if [[ -u $file ]]; then 6610 ((type=ATTR_FILE_SETUID)) 6611 elif [[ -g $file ]]; then 6612 ((type=ATTR_FILE_SETGID)) 6613 elif [[ -x $file ]]; then 6614 ((type=ATTR_FILE_EXEC)) 6615 else 6616 ((type=ATTR_FILE_FILE)) 6617 fi 6618 elif [[ -c $file ]]; then 6619 ((type=ATTR_FILE_CHR)) 6620 elif [[ -p $file ]]; then 6621 ((type=ATTR_FILE_FIFO)) 6622 elif [[ -S $file ]]; then 6623 ((type=ATTR_FILE_SOCK)) 6624 elif [[ -b $file ]]; then 6625 ((type=ATTR_FILE_BLK)) 6626 fi 6627 elif local rex='^https?://[^ ^`"<>\{|}]+$'; [[ $file =~ $rex ]]; then 6628 ((type=ATTR_FILE_URL)) 6629 fi 6630 [[ $type ]] 6631 } 6632 6633 #------------------------------------------------------------------------------ 6634 # ble/syntax/highlight/ls_colors 6635 6636 function ble/syntax/highlight/ls_colors/.clear { 6637 _ble_syntax_highlight_lscolors=() 6638 ble/gdict#clear _ble_syntax_highlight_lscolors_ext 6639 } 6640 6641 ## @fn ble/syntax/highlight/ls_colors/.register-extension key value 6642 ## @param[in] key value 6643 ## @fn ble/syntax/highlight/ls_colors/.read-extension key 6644 ## @param[in] key 6645 ## @var[out] ret 6646 6647 # Note: _ble_syntax_highlight_lscolors_ext は core-syntax-def.sh で先に定義 6648 function ble/syntax/highlight/ls_colors/.register-extension { 6649 local key=$1 value=$2 6650 ble/gdict#set _ble_syntax_highlight_lscolors_ext "$key" "$value" 6651 } 6652 function ble/syntax/highlight/ls_colors/.read-extension { 6653 ble/gdict#get _ble_syntax_highlight_lscolors_ext "$1" 6654 } 6655 6656 function ble/syntax/highlight/ls_colors/.parse { 6657 ble/syntax/highlight/ls_colors/.clear 6658 6659 local fields field 6660 ble/string#split fields : "$1" 6661 for field in "${fields[@]}"; do 6662 [[ $field == *=* ]] || continue 6663 local lhs=${field%%=*} 6664 local ret; ble/color/sgrspec2g "${field#*=}"; local rhs=$ret 6665 case $lhs in 6666 ('di') _ble_syntax_highlight_lscolors[ATTR_FILE_DIR]=$rhs ;; 6667 ('st') _ble_syntax_highlight_lscolors[ATTR_FILE_STICKY]=$rhs ;; 6668 ('ln') _ble_syntax_highlight_lscolors[ATTR_FILE_LINK]=$rhs ;; 6669 ('or') _ble_syntax_highlight_lscolors[ATTR_FILE_ORPHAN]=$rhs ;; 6670 ('fi') _ble_syntax_highlight_lscolors[ATTR_FILE_FILE]=$rhs ;; 6671 ('su') _ble_syntax_highlight_lscolors[ATTR_FILE_SETUID]=$rhs ;; 6672 ('sg') _ble_syntax_highlight_lscolors[ATTR_FILE_SETGID]=$rhs ;; 6673 ('ex') _ble_syntax_highlight_lscolors[ATTR_FILE_EXEC]=$rhs ;; 6674 ('cd') _ble_syntax_highlight_lscolors[ATTR_FILE_CHR]=$rhs ;; 6675 ('pi') _ble_syntax_highlight_lscolors[ATTR_FILE_FIFO]=$rhs ;; 6676 ('so') _ble_syntax_highlight_lscolors[ATTR_FILE_SOCK]=$rhs ;; 6677 ('bd') _ble_syntax_highlight_lscolors[ATTR_FILE_BLK]=$rhs ;; 6678 (\*.*) 6679 ble/syntax/highlight/ls_colors/.register-extension "${lhs:2}" "$rhs" ;; 6680 esac 6681 done 6682 } 6683 6684 ## @fn ble/syntax/highlight/ls_colors filename 6685 ## 対応する LS_COLORS 設定が見つかった時に g を上書きし成功します。 6686 ## それ以外の場合は g は変更せず失敗します。 6687 ## @param[in] filename 6688 ## @var[in] type 6689 ## @var[in,out] g 6690 function ble/syntax/highlight/ls_colors { 6691 local file=$1 6692 if ((type==ATTR_FILE_FILE)); then 6693 local ext=${file##*/} ret= 6694 while [[ $ext == *.* ]]; do 6695 ext=${ext#*.} 6696 [[ $ext ]] || break 6697 if ble/syntax/highlight/ls_colors/.read-extension "$ext"; then 6698 local g1=$ret 6699 ble/color/face2g filename_ls_colors; g=$ret 6700 ble/color/g#append g "$g1" 6701 return 0 6702 fi 6703 done 6704 fi 6705 6706 local g1=${_ble_syntax_highlight_lscolors[type]} 6707 if [[ $g1 ]]; then 6708 ble/color/face2g filename_ls_colors; g=$ret 6709 ble/color/g#append g "$g1" 6710 return 0 6711 fi 6712 6713 return 1 6714 } 6715 6716 function ble/syntax/highlight/getg-from-filename { 6717 local filename=$1 type= 6718 ble/syntax/highlight/filetype "$filename" 6719 if [[ $bleopt_filename_ls_colors ]]; then 6720 if ble/syntax/highlight/ls_colors "$filename"; then 6721 return 0 6722 fi 6723 fi 6724 6725 if [[ $type ]]; then 6726 ble/syntax/attr2g "$type" 6727 else 6728 g= 6729 fi 6730 } 6731 6732 function bleopt/check:filename_ls_colors { 6733 ble/syntax/highlight/ls_colors/.parse "$value" 6734 } 6735 bleopt -I filename_ls_colors 6736 6737 #------------------------------------------------------------------------------ 6738 # ble/progcolor 6739 6740 _ble_syntax_progcolor_vars=( 6741 node TE_i TE_nofs wtype wlen wbeg wend wattr) 6742 _ble_syntax_progcolor_wattr_vars=( 6743 wattr_buff wattr_pos wattr_g) 6744 6745 ## @fn ble/progcolor/load-word-data i:nofs 6746 ## @var[out] TE_i TE_nofs node 6747 ## @var[out] wtype wlen wbeg wend wattr 6748 function ble/progcolor/load-word-data { 6749 # TE_i TE_nofs 6750 TE_i=${1%%:*} TE_nofs=${1#*:} 6751 [[ $1 != *:* ]] && TE_nofs=0 6752 6753 # node 6754 ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}" 6755 6756 # wvars 6757 wtype=${node[TE_nofs]} 6758 wlen=${node[TE_nofs+1]} 6759 wattr=${node[TE_nofs+4]} 6760 wbeg=$((TE_i-wlen)) 6761 wend=$TE_i 6762 } 6763 6764 ## @fn ble/progcolor/set-wattr value 6765 ## @var[in] TE_i TE_nofs node 6766 function ble/progcolor/set-wattr { 6767 ble/syntax/urange#update color_ "$wbeg" "$wend" 6768 ble/syntax/wrange#update _ble_syntax_word_ "$TE_i" 6769 node[TE_nofs+4]=$1 6770 local IFS=$_ble_term_IFS 6771 _ble_syntax_tree[TE_i-1]="${node[*]}" 6772 } 6773 6774 ## @fn ble/progcolor/eval-word [iword] [opts] 6775 ## 現在のコマンドの iword 番目の単語を評価した値を返します。 6776 ## iword を省略した場合には現在着色中の単語が使われます。 6777 ## 単語が評価できない場合にはコマンドは失敗します。 6778 ## 6779 ## @param[in,opt] iword 6780 ## @param[in,opt] opts 6781 ## ble/syntax:bash/simple-word/eval に対する opts を指定します。 6782 ## 6783 ## @var[in] progcolor_iword 6784 ## 現在処理中の単語の番号を指定します。 6785 ## iword を省略した時に使われます。 6786 ## @var[in,out] progcolor_wvals 6787 ## @var[in,out] progcolor_stats 6788 ## 単語の評価値のキャッシュです。 6789 ## @var[out] ret 6790 ## 6791 function ble/progcolor/eval-word { 6792 local iword=${1:-progcolor_iword} opts=$2 6793 if [[ ${progcolor_stats[iword]+set} ]]; then 6794 ret=${progcolor_wvals[iword]} 6795 return "${progcolor_stats[iword]}" 6796 fi 6797 6798 local wtxt=${comp_words[iword]} 6799 local simple_flags simple_ibrace 6800 if ! ble/syntax:bash/simple-word/reconstruct-incomplete-word "$wtxt"; then 6801 # not simple word 6802 ret= 6803 progcolor_stats[iword]=2 6804 progcolor_wvals[iword]= 6805 return 2 6806 fi 6807 6808 ble/syntax:bash/simple-word/eval "$ret" "$opts"; local ext=$? 6809 ((ext==148)) && return 148 6810 if ((ext!=0)); then 6811 # fail glob 6812 ret= 6813 progcolor_stats[iword]=1 6814 progcolor_wvals[iword]= 6815 return 1 6816 fi 6817 6818 progcolor_stats[iword]=0 6819 progcolor_wvals[iword]=$ret 6820 return 0 6821 } 6822 6823 ## @fn ble/progcolor/load-cmdspec-opts 6824 ## @var[out] cmdspec_opts 6825 function ble/progcolor/load-cmdspec-opts { 6826 if [[ $progcolor_cmdspec_opts ]]; then 6827 cmdspec_opts=$progcolor_cmdspec_opts 6828 else 6829 ble/cmdspec/opts#load "${comp_words[0]}" 6830 progcolor_cmdspec_opts=${cmdspec_opts:-:} 6831 fi 6832 } 6833 6834 ## @fn ble/progcolor/stop-option#init cmdspec_opts 6835 ## @var[out] rexrej rexreq stopat 6836 function ble/progcolor/stop-option#init { 6837 rexrej='^--$' rexreq= stopat= 6838 local cmdspec_opts=$1 6839 if [[ $cmdspec_opts ]]; then 6840 # copied from ble/complete/source:option/.stops-option 6841 if [[ :$cmdspec_opts: == *:no-options:* ]]; then 6842 stopat=0 6843 return 1 6844 elif ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-at && [[ $ret ]]; then 6845 ((stopat=ret)) 6846 return 1 6847 fi 6848 6849 local ret 6850 if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-on && [[ $ret ]]; then 6851 rexrej=$ret 6852 elif [[ :$cmdspec_opts: == *:disable-double-hyphen:* ]]; then 6853 rexrej= 6854 fi 6855 if ble/opts#extract-first-optarg "$cmdspec_opts" stop-options-unless && [[ $ret ]]; then 6856 rexreq=$ret 6857 elif [[ :$cmdspec_opts: == *:stop-options-postarg:* ]]; then 6858 rexreq='^-.+' 6859 ble/opts#has "$cmdspec_opts" plus-options && rexreq='^[-+].+' 6860 fi 6861 fi 6862 } 6863 ## @fn ble/progcolor/stop-option#test value 6864 ## @var[in] rexrej rexreq 6865 function ble/progcolor/stop-option#test { 6866 [[ $rexrej && $1 =~ $rexrej || $rexreq && ! ( $1 =~ $rexreq ) ]] 6867 } 6868 6869 ## @fn ble/progcolor/is-option-context 6870 ## 現在の単語位置がオプションが解釈される文脈かどうかを判定します。 6871 ## 6872 ## @var progcolor_iword 6873 ## 現在の単語の位置。 6874 ## 6875 ## @var progcolor_optctx[0] 6876 ## 空の時には未だオプションを初期化していない事を示します。 6877 ## 現在のコマンドについて何処までオプション停止条件を検査済みかを記録します。 6878 ## 6879 ## @var progcolor_optctx[1] 6880 ## 負の時は常にオプションが有効である事を示す。 6881 ## 0の時は常にオプションが無効である事を示す。 6882 ## 正の時はその位置以前の引数でオプションが有効である事を示す。 6883 ## 6884 ## @var progcolor_optctx[2] 6885 ## @var progcolor_optctx[3] 6886 ## @var progcolor_optctx[4] 6887 ## それぞれ抽出済みの rexrej rexreq stopat の値を記録します。 6888 ## ble/progcolor/stop-option#init によって初期化された値のキャッシュです。 6889 ## 6890 function ble/progcolor/is-option-context { 6891 # 既にオプション停止位置が計算済みの場合 6892 if [[ ${progcolor_optctx[1]} ]]; then 6893 # Note: 等号は停止を引き起こした引数 -- 自体の時 (オプションとして有効) 6894 ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1]))) 6895 return "$?" 6896 fi 6897 6898 local rexrej rexreq stopat 6899 if [[ ! ${progcolor_optctx[0]} ]]; then 6900 progcolor_optctx[0]=1 6901 local cmdspec_opts 6902 ble/progcolor/load-cmdspec-opts 6903 ble/progcolor/stop-option#init "$cmdspec_opts" 6904 if [[ ! $rexrej$rexreq ]]; then 6905 progcolor_optctx[1]=${stopat:--1} 6906 ((progcolor_optctx[1]<0?1:(progcolor_iword<=progcolor_optctx[1]))) 6907 return "$?" 6908 fi 6909 progcolor_optctx[2]=$rexrej 6910 progcolor_optctx[3]=$rexreq 6911 progcolor_optctx[4]=$stopat 6912 else 6913 rexrej=${progcolor_optctx[2]} 6914 rexreq=${progcolor_optctx[3]} 6915 stopat=${progcolor_optctx[4]} 6916 fi 6917 [[ $stopat ]] && ((progcolor_iword>stopat)) && return 1 6918 6919 local iword 6920 for ((iword=progcolor_optctx[0];iword<progcolor_iword;iword++)); do 6921 ble/progcolor/eval-word "$iword" "$highlight_eval_opts" 6922 if ble/progcolor/stop-option#test "$ret"; then 6923 progcolor_optctx[1]=$iword 6924 return 1 6925 fi 6926 done 6927 progcolor_optctx[0]=$iword 6928 return 0 6929 } 6930 6931 ## @fn ble/progcolor/wattr#initialize 6932 ## @var[out] wattr_buff 6933 ## @var[out] wattr_pos 6934 ## @var[out] wattr_g 6935 function ble/progcolor/wattr#initialize { 6936 wattr_buff=() 6937 wattr_pos=$wbeg 6938 wattr_g=d 6939 } 6940 ## @fn ble/progcolor/wattr#setg pos g 6941 ## @param[in] pos 6942 ## @param[in] g 6943 ## @var[in,out] wattr_buff 6944 ## @var[in,out] wattr_pos 6945 ## @var[in,out] wattr_g 6946 function ble/progcolor/wattr#setg { 6947 local pos=$1 g=$2 6948 local len=$((pos-wattr_pos)) 6949 ((len>0)) && ble/array#push wattr_buff "$len:$wattr_g" 6950 wattr_pos=$pos 6951 wattr_g=$g 6952 } 6953 function ble/progcolor/wattr#setattr { 6954 local pos=$1 attr=$2 g 6955 ble/syntax/attr2g "$attr" 6956 ble/progcolor/wattr#setg "$pos" "$g" 6957 } 6958 ## @fn ble/progcolor/wattr#finalize 6959 ## @var[in,out] wattr_buff 6960 ## @var[in,out] wattr_pos 6961 ## @var[in,out] wattr_g 6962 function ble/progcolor/wattr#finalize { 6963 local wattr 6964 if ((${#wattr_buff[@]})); then 6965 local len=$((wend-wattr_pos)) 6966 ((len>0)) && ble/array#push wattr_buff \$:"$wattr_g" 6967 wattr_pos=$wend 6968 wattr_g=d 6969 IFS=, builtin eval 'wattr="m${wattr_buff[*]}"' 6970 else 6971 wattr=$wattr_g 6972 fi 6973 ble/progcolor/set-wattr "$wattr" 6974 } 6975 6976 6977 ## @fn ble/progcolor/highlight-filename/.detect-separated-path word 6978 ## @param[in] word 6979 ## @var[in] wtype p0 6980 ## @var[in] _ble_syntax_attr 6981 ## @var[out] ret 6982 ## 有効な区切り文字の集合を返します。 6983 function ble/progcolor/highlight-filename/.detect-separated-path { 6984 local word=$1 6985 ((wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_VALI||wtype==ATTR_VAR||wtype==CTX_RDRS)) || return 1 6986 6987 local detect_opts=url:$highlight_eval_opts 6988 ((wtype==CTX_RDRS)) && detect_opts=$detect_opts:noglob 6989 [[ $word == '~'* ]] && ((_ble_syntax_attr[p0]!=ATTR_TILDE)) && detect_opts=$detect_opts:notilde 6990 ble/syntax:bash/simple-word/detect-separated-path "$word" :, "$detect_opts" 6991 } 6992 6993 ## @fn ble/progcolor/highlight-filename/.pathspec.wattr g [opts] 6994 ## @param[in] g 6995 ## @param[in,opt] opts 6996 ## @var[in] wtype p0 p1 6997 ## @var[in] path spec 6998 function ble/progcolor/highlight-filename/.pathspec.wattr { 6999 local p=$p0 opts=$2 7000 7001 if [[ :$opts: != *:no-path-color:* ]]; then 7002 local ipath npath=${#path[@]} 7003 for ((ipath=0;ipath<npath-1;ipath++)); do 7004 local epath=${path[ipath]} espec=${spec[ipath]} 7005 local g=d 7006 7007 if ble/syntax/util/is-directory "$epath"; then 7008 local type 7009 if ble/syntax/highlight/filetype "$epath"; then 7010 ble/syntax/highlight/ls_colors || 7011 ble/syntax/attr2g "$type" 7012 fi 7013 elif ((wtype==CTX_CMDI)); then 7014 # コマンド名の時はディレクトリが存在する必要 #D1419 7015 ble/syntax/attr2g "$ATTR_ERR" 7016 fi 7017 7018 # コマンド名の時は下線は引かない様にする 7019 ((wtype==CTX_CMDI&&(g&=~_ble_color_gflags_Underline))) 7020 7021 ble/progcolor/wattr#setg "$p" "$g" 7022 ((p=p0+${#espec})) 7023 done 7024 fi 7025 7026 ble/progcolor/wattr#setg "$p" "$1" 7027 [[ $1 != d ]] && 7028 ble/progcolor/wattr#setg "$p1" d 7029 return 0 7030 } 7031 7032 ## @fn ble/progcolor/highlight-filename/.pathspec-with-attr.wattr attr 7033 ## @param[in] attr 7034 ## @var[in] wtype p0 p1 7035 ## @var[in] path spec 7036 function ble/progcolor/highlight-filename/.pathspec-with-attr.wattr { 7037 local g; ble/syntax/attr2g "$1" 7038 ble/progcolor/highlight-filename/.pathspec.wattr "$g" 7039 return 0 7040 } 7041 ## @fn ble/progcolor/highlight-filename/.pathspec-by-name.wattr value 7042 ## @param[in] value 7043 ## @var[in] wtype p0 p1 7044 ## @var[in] path spec 7045 function ble/progcolor/highlight-filename/.pathspec-by-name.wattr { 7046 local value=$1 7047 7048 local highlight_opts= 7049 local type=; ble/syntax/highlight/filetype "$value" 7050 ((type==ATTR_FILE_URL)) && highlight_opts=no-path-color 7051 7052 # check values 7053 if ((wtype==CTX_RDRF||wtype==CTX_RDRD2)); then 7054 if ((type==ATTR_FILE_DIR)); then 7055 # ディレクトリにリダイレクトはできない 7056 type=$ATTR_ERR 7057 elif ((_ble_syntax_TREE_WIDTH<=TE_nofs)); then 7058 # noclobber の時は既存ファイルを > または <> で上書きできない 7059 # 7060 # 仮定: _ble_syntax_word に於いてリダイレクトとファイルは同じ位置で終了すると想定する。 7061 # この時、リダイレクトの情報の次にファイル名の情報が格納されている筈で、 7062 # リダイレクトの情報は node[TE_nofs-_ble_syntax_TREE_WIDTH] に入っていると考えられる。 7063 # 7064 local redirect_ntype=${node[TE_nofs-_ble_syntax_TREE_WIDTH]:1} 7065 if [[ ( $redirect_ntype == *'>' || $redirect_ntype == '>'[\|\&] ) ]]; then 7066 if [[ -e $value || -h $value ]]; then 7067 if [[ -d $value || ! -w $value ]]; then 7068 # ディレクトリまたは書き込み権限がない 7069 type=$ATTR_ERR 7070 elif [[ ( $redirect_ntype == [\<\&]'>' || $redirect_ntype == '>' || $redirect_ntype == '>&' ) && -f $value ]]; then 7071 if [[ -o noclobber ]]; then 7072 # 上書き禁止 7073 type=$ATTR_ERR 7074 else 7075 # 上書き注意 7076 type=$ATTR_FILE_WARN 7077 fi 7078 fi 7079 elif [[ $value == */* && ! -w ${value%/*}/ || $value != */* && ! -w ./ ]]; then 7080 # ディレクトリに書き込み権限がない 7081 type=$ATTR_ERR 7082 fi 7083 elif [[ $redirect_ntype == '<' && ! -r $value ]]; then 7084 # ファイルがないまたは読み取り権限がない 7085 type=$ATTR_ERR 7086 fi 7087 fi 7088 fi 7089 7090 local g= 7091 if [[ $bleopt_filename_ls_colors ]]; then 7092 ble/syntax/highlight/ls_colors "$value" 7093 fi 7094 [[ $type && ! $g ]] && ble/syntax/attr2g "$type" 7095 7096 ble/progcolor/highlight-filename/.pathspec.wattr "${g:-d}" "$highlight_opts" 7097 return 0 7098 } 7099 7100 ## @fn ble/progcolor/highlight-filename/.single.wattr p0:p1 7101 ## @param[in] p0 p1 7102 ## ファイル名の (コマンドライン内部における) 範囲を指定します。 7103 ## @param[in] wtype 7104 function ble/progcolor/highlight-filename/.single.wattr { 7105 local p0=${1%%:*} p1=${1#*:} 7106 local wtxt=${text:p0:p1-p0} 7107 7108 # alias はシェル展開等を行う前に判定するべき 7109 if ((wtype==CTX_CMDI)) && ble/alias#active "$wtxt"; then 7110 # Note: [*?] 等の文字が含まれている alias 名の場合 failglob するかもしれ 7111 # ないが、実際は展開されるので問題ない。 7112 ble/progcolor/wattr#setattr "$p0" "$ATTR_CMD_ALIAS" 7113 return 0 7114 fi 7115 7116 local path_opts=after-sep:$highlight_eval_opts 7117 # チルダ展開の文脈でない時には抑制 7118 [[ $wtxt == '~'* ]] && ((_ble_syntax_attr[p0]!=ATTR_TILDE)) && path_opts=$path_opts:notilde 7119 ((wtype==CTX_RDRS||wtype==ATTR_VAR||wtype==CTX_VALI&&wbeg<p0)) && path_opts=$path_opts:noglob 7120 7121 local ret path spec ext value count 7122 ble/syntax:bash/simple-word/evaluate-path-spec "$wtxt" / "count:$path_opts"; ext=$? value=("${ret[@]}") 7123 ((ext==148)) && return 148 7124 if ((ext==142)); then 7125 if [[ $ble_textarea_render_defer_running ]]; then 7126 # background で timeout した時はこのファイル名の着色は諦める 7127 ble/progcolor/wattr#setg "$p0" d 7128 else 7129 # foreground で timeout した時は後で background で着色する為に取り敢えず抜ける 7130 return 148 7131 fi 7132 elif ((ext&&(wtype==CTX_CMDI||wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_RDRF||wtype==CTX_RDRS||wtype==CTX_RDRD||wtype==CTX_RDRD2||wtype==CTX_VALI))); then 7133 # failglob 等の理由で展開に失敗した場合 7134 ble/progcolor/highlight-filename/.pathspec-with-attr.wattr "$ATTR_ERR" 7135 elif (((wtype==CTX_RDRF||wtype==CTX_RDRD||wtype==CTX_RDRD2)&&count>=2)); then 7136 # 複数語に展開されたら駄目 7137 ble/progcolor/wattr#setattr "$p0" "$ATTR_ERR" 7138 elif ((wtype==CTX_CMDI)); then 7139 local attr=${_ble_syntax_attr[wbeg]} 7140 if ((attr!=ATTR_KEYWORD&&attr!=ATTR_KEYWORD_BEGIN&&attr!=ATTR_KEYWORD_END&&attr!=ATTR_KEYWORD_MID&&attr!=ATTR_DEL)); then 7141 local type=; ble/syntax/highlight/cmdtype "$value" "$wtxt" 7142 if ((type==ATTR_CMD_FILE||type==ATTR_CMD_FILE||type==ATTR_ERR)); then 7143 ble/progcolor/highlight-filename/.pathspec-with-attr.wattr "$type" 7144 elif [[ $type ]]; then 7145 ble/progcolor/wattr#setattr "$p0" "$type" 7146 fi 7147 fi 7148 elif ((wtype==CTX_RDRD||wtype==CTX_RDRD2)); then 7149 if local rex='^[0-9]+-?$|^-$'; [[ $value =~ $rex ]]; then 7150 ble/progcolor/wattr#setattr "$p0" "$ATTR_DEL" 7151 elif ((wtype==CTX_RDRD2)); then 7152 ble/progcolor/highlight-filename/.pathspec-by-name.wattr "$value" 7153 else 7154 ble/progcolor/wattr#setattr "$p0" "$ATTR_ERR" 7155 fi 7156 elif ((wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_VALI||wtype==ATTR_VAR||wtype==CTX_RDRS||wtype==CTX_RDRF)); then 7157 ble/progcolor/highlight-filename/.pathspec-by-name.wattr "$value" 7158 fi 7159 } 7160 7161 function ble/progcolor/highlight-filename.wattr { 7162 local p0=$1 p1=$2 7163 if ((p0<p1)) && [[ $bleopt_highlight_filename ]]; then 7164 local wtxt=${text:p0:p1-p0} 7165 local ret; ble/progcolor/highlight-filename/.detect-separated-path "$wtxt"; local ext=$? 7166 ((ext==148)) && return 148 7167 if ((ext==0)); then 7168 local sep=$ret ranges i 7169 ble/syntax:bash/simple-word/locate-filename "$wtxt" "$sep" "url:$highlight_eval_opts" 7170 (($?==148)) && return 148; ranges=("${ret[@]}") 7171 for ((i=0;i<${#ranges[@]};i+=2)); do 7172 ble/progcolor/highlight-filename/.single.wattr "$((p0+ranges[i])):$((p0+ranges[i+1]))" 7173 (($?==148)) && return 148 7174 done 7175 elif ble/syntax:bash/simple-word/is-simple "$wtxt"; then 7176 ble/progcolor/highlight-filename/.single.wattr "$p0":"$p1" 7177 (($?==148)) && return 148 7178 fi 7179 fi 7180 } 7181 7182 function ble/progcolor/@wattr { 7183 [[ $wtype =~ ^[0-9]+$ ]] || return 1 7184 [[ $wattr == - ]] || return 1 7185 local "${_ble_syntax_progcolor_wattr_vars[@]/%/=}" # WA #D1570 checked 7186 ble/progcolor/wattr#initialize 7187 7188 "$@"; local ext=$? 7189 7190 if ((ext==148)); then 7191 _ble_textarea_render_defer=1 7192 ble/syntax/wrange#update _ble_syntax_word_defer_ "$wend" 7193 else 7194 ble/progcolor/wattr#finalize 7195 fi 7196 return "$ext" 7197 } 7198 7199 ## @fn ble/progcolor/word:default/.is-option wtxt 7200 ## @var[in] wtype 7201 ## @var[in] progcolor_* 7202 function ble/progcolor/word:default/.is-option { 7203 # Note: オプションとして許される文字に関しては_ble_complete_option_chars と同期を取る。 7204 ((wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_ARGVI)) && 7205 ble/string#match "$1" '^(-[-_a-zA-Z0-9!#$%&:;.,^~|\?/*@]*)=?' && # 高速な判定を先に済ませる 7206 ble/progcolor/is-option-context && 7207 ble/string#match "$1" '^(-[-_a-zA-Z0-9!#$%&:;.,^~|\?/*@]*)=?' # 再実行 for BASH_REMATCH 7208 } 7209 7210 ## @fn ble/progcolor/word:default 7211 ## @var[in] node TE_i TE_nofs 7212 ## @var[in] wtype wlen wbeg wend wattr 7213 ## @var[in] ${_ble_syntax_progcolor_wattr_vars[@]} 7214 function ble/progcolor/word:default/impl.wattr { 7215 if ((wtype==CTX_RDRH||wtype==CTX_RDRI||wtype==ATTR_FUNCDEF||wtype==ATTR_ERR)); then 7216 # ヒアドキュメントのキーワード指定部分は、 7217 # 展開・コマンド置換などに従った解析が行われるが、 7218 # 実行は一切起こらないので一色で塗りつぶす。 7219 ble/progcolor/wattr#setattr "$wbeg" "$wtype" 7220 7221 else 7222 # @var p0 p1 7223 # 文字列を切り出す範囲。 7224 local p0=$wbeg p1=$wend wtxt=${text:wbeg:wlen} 7225 7226 # 変数代入 7227 if ((wtype==ATTR_VAR||wtype==CTX_VALI)); then 7228 # 変数代入の場合は右辺だけ切り出す。 7229 # Note: arr=(a=a*b a[1]=a*b) などはパス名展開の対象ではない。 7230 # これは変数代入の形式として認識されているからである。 7231 # 以下では element-assignment を指定することで、 7232 # 配列要素についても変数代入の形式を抽出する様にしている。 7233 local ret 7234 ble/syntax:bash/find-rhs "$wtype" "$wbeg" "$wlen" element-assignment && p0=$ret 7235 elif ((wtype==CTX_ARGI||wtype==CTX_ARGEI||wtype==CTX_VALI)) && { local rex='^[_a-zA-Z][_a-zA-Z0-9]*='; [[ $wtxt =~ $rex ]]; }; then 7236 # 変数代入形式の通常引数 7237 ((p0+=${#BASH_REMATCH})) 7238 elif ble/progcolor/word:default/.is-option "$wtxt"; then 7239 # --prefix= 等のオプション引数 7240 local rematch=$BASH_REMATCH rematch1=${BASH_REMATCH[1]} 7241 local ret; ble/color/face2g argument_option 7242 ble/progcolor/wattr#setg "$p0" "$ret" 7243 ble/progcolor/wattr#setg "$((p0+${#rematch1}))" d 7244 ((p0+=${#rematch})) 7245 fi 7246 7247 ble/progcolor/highlight-filename.wattr "$p0" "$p1" 7248 (($?==148)) && return 148 7249 fi 7250 7251 return 0 7252 } 7253 7254 function ble/progcolor/word:default { 7255 ble/progcolor/@wattr ble/progcolor/word:default/impl.wattr 7256 } 7257 7258 ## @fn ble/progcolor/default 7259 ## @var[in] comp_words comp_cword comp_line comp_point 7260 ## @var[in] tree_words 7261 ## @var[in,out] color_umin color_umax 7262 function ble/progcolor/default { 7263 local i "${_ble_syntax_progcolor_vars[@]/%/=}" # WA #D1570 checked 7264 for ((i=1;i<${#comp_words[@]};i++)); do 7265 local ref=${tree_words[i]} 7266 [[ $ref ]] || continue 7267 local progcolor_iword=$i 7268 ble/progcolor/load-word-data "$ref" 7269 ble/progcolor/word:default 7270 done 7271 } 7272 7273 ## @fn ble/progcolor/.compline-rewrite-command command [args...] 7274 ## @var[in,out] comp_words comp_cword comp_line comp_point 7275 function ble/progcolor/.compline-rewrite-command { 7276 local ocmd=${comp_words[0]} 7277 [[ $1 != "$ocmd" ]] || (($#>=2)) || return 1 7278 local IFS=$_ble_term_IFS 7279 local ins="$*" 7280 comp_line=$ins${comp_line:${#ocmd}} 7281 ((comp_point-=${#ocmd},comp_point<0&&(comp_point=0),comp_point+=${#ins})) 7282 comp_words=("$@" "${comp_words[@]:1}") 7283 ((comp_cword&&(comp_cword+=$#-1))) 7284 7285 # update tree_words 7286 ble/array#reserve-prototype $# 7287 tree_words=("${tree_words[0]}" "${_ble_array_prototype[@]::$#-1}" "${tree_words[@]:1}") 7288 } 7289 ## @fn ble/progcolor cmd opts 7290 ## @var[in] comp_words comp_cword comp_line comp_point 7291 ## @var[in] tree_words 7292 ## @var[in,out] color_umin color_umax 7293 function ble/progcolor { 7294 local cmd=$1 opts=$2 7295 7296 # cache used by "eval-word" 7297 local -a progcolor_stats=() 7298 local -a progcolor_wvals=() 7299 7300 # cache used by "load-cmdspec-opts" 7301 local progcolor_cmdspec_opts= 7302 7303 # cache used by "is-option-context" 7304 local -a progcolor_optctx=() 7305 7306 local -a alias_args=() 7307 local checked=" " processed= 7308 while :; do 7309 if ble/is-function ble/cmdinfo/cmd:"$cmd"/chroma; then 7310 ble/progcolor/.compline-rewrite-command "$cmd" "${alias_args[@]}" 7311 ble/cmdinfo/cmd:"$cmd"/chroma "$opts" 7312 processed=1 7313 break 7314 elif [[ $cmd == */?* ]] && ble/is-function ble/cmdinfo/cmd:"${cmd##*/}"/chroma; then 7315 ble/progcolor/.compline-rewrite-command "${cmd##*/}" "${alias_args[@]}" 7316 ble/cmdinfo/cmd:"${cmd##*/}"/chroma "$opts" 7317 processed=1 7318 break 7319 fi 7320 checked="$checked$cmd " 7321 7322 local ret 7323 ble/alias#expand "$cmd" 7324 ble/string#split-words ret "$ret" 7325 [[ $checked == *" $ret "* ]] && break 7326 cmd=$ret 7327 ((${#ret[@]}>=2)) && 7328 alias_args=("${ret[@]:1}" "${alias_args[@]}") 7329 done 7330 [[ $processed ]] || 7331 ble/progcolor/default 7332 7333 # コマンド名に対しては既定の着色を実行 7334 if [[ ${tree_words[0]} ]]; then 7335 local "${_ble_syntax_progcolor_vars[@]/%/=}" # WA #D1570 checked 7336 ble/progcolor/load-word-data "${tree_words[0]}" 7337 [[ $wattr == - ]] && ble/progcolor/word:default 7338 fi 7339 } 7340 7341 #------------------------------------------------------------------------------ 7342 # ble/highlight/layer:syntax 7343 7344 # adapter に頼らず直接実装したい 7345 function ble/highlight/layer:syntax/touch-range { 7346 ble/syntax/urange#update '' "$@" 7347 } 7348 function ble/highlight/layer:syntax/fill { 7349 local _ble_local_script=' 7350 local iARR=0 i1ARR=$2 i2ARR=$3 7351 for ((iARR=i1ARR;iARR<i2ARR;iARR++)); do 7352 ARR[iARR]=$4 7353 done 7354 '; builtin eval -- "${_ble_local_script//ARR/$1}" 7355 } 7356 7357 _ble_highlight_layer_syntax_VARNAMES=( 7358 _ble_highlight_layer_syntax_buff 7359 _ble_highlight_layer_syntax_active 7360 _ble_highlight_layer_syntax1_table 7361 _ble_highlight_layer_syntax2_table 7362 _ble_highlight_layer_syntax3_list 7363 _ble_highlight_layer_syntax3_table) 7364 function ble/highlight/layer:syntax/initialize-vars { 7365 # Note (#D2000): core-syntax は遅延ロードされるので layter/update/shift 対象 7366 # の配列は全て必要な要素数を備えている必要がある。 7367 local prev_iN=${#_ble_highlight_layer_plain_buff[*]} 7368 ble/array#reserve-prototype "$prev_iN" 7369 _ble_highlight_layer_syntax_buff=("${_ble_array_prototype[@]::prev_iN}") 7370 _ble_highlight_layer_syntax1_table=("${_ble_array_prototype[@]::prev_iN}") 7371 _ble_highlight_layer_syntax2_table=("${_ble_array_prototype[@]::prev_iN}") 7372 _ble_highlight_layer_syntax3_table=("${_ble_array_prototype[@]::prev_iN}") # errors 7373 _ble_highlight_layer_syntax3_list=() 7374 } 7375 ble/highlight/layer:syntax/initialize-vars 7376 7377 function ble/highlight/layer:syntax/update-attribute-table { 7378 ble/highlight/layer/update/shift _ble_highlight_layer_syntax1_table 7379 if ((_ble_syntax_attr_umin>=0)); then 7380 ble/highlight/layer:syntax/touch-range _ble_syntax_attr_umin _ble_syntax_attr_umax 7381 7382 local i g=0 7383 ((_ble_syntax_attr_umin>0)) && 7384 ((g=_ble_highlight_layer_syntax1_table[_ble_syntax_attr_umin-1])) 7385 7386 for ((i=_ble_syntax_attr_umin;i<_ble_syntax_attr_umax;i++)); do 7387 if ((${_ble_syntax_attr[i]})); then 7388 ble/syntax/attr2g "${_ble_syntax_attr[i]}" 7389 fi 7390 _ble_highlight_layer_syntax1_table[i]=$g 7391 done 7392 7393 _ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1 7394 fi 7395 } 7396 7397 function ble/highlight/layer:syntax/word/.update-attributes/.proc { 7398 [[ $wtype =~ ^[0-9]+$ ]] || return 1 7399 [[ ${node[TE_nofs+4]} == - ]] || return 1 7400 7401 if ((wtype==CTX_CMDI||wtype==CTX_ARGI||wtype==CTX_ARGVI||wtype==CTX_ARGEI)); then 7402 local comp_line comp_point comp_words comp_cword tree_words 7403 if ble/syntax:bash/extract-command-by-noderef "$TE_i:$TE_nofs" treeinfo; then 7404 local cmd=${comp_words[0]} 7405 ble/progcolor "$cmd" 7406 return 0 7407 fi 7408 fi 7409 7410 # コマンドラインを復元できなければ単一単語の着色 7411 ble/progcolor/word:default 7412 } 7413 7414 ## @fn ble/highlight/layer:syntax/word/.update-attributes 7415 ## @var[in] _ble_syntax_word_umin,_ble_syntax_word_umax 7416 ## @var[in,out] color_umin color_umax 7417 function ble/highlight/layer:syntax/word/.update-attributes { 7418 ((_ble_syntax_word_umin>=0)) || return 1 7419 7420 # Timeout setting for simple-word/eval 7421 if [[ ! $ble_textarea_render_defer_running ]]; then 7422 local _ble_syntax_bash_simple_eval_timeout=$bleopt_highlight_timeout_sync 7423 local _ble_syntax_bash_simple_eval_timeout_carry= 7424 local highlight_eval_opts=cached:single:stopcheck:timeout-carry 7425 else 7426 local _ble_syntax_bash_simple_eval_timeout=$bleopt_highlight_timeout_async 7427 local highlight_eval_opts=cached:single:stopcheck 7428 fi 7429 7430 ble/syntax/tree-enumerate-in-range "$_ble_syntax_word_umin" "$_ble_syntax_word_umax" \ 7431 ble/highlight/layer:syntax/word/.update-attributes/.proc 7432 } 7433 7434 ## @fn ble/highlight/layer:syntax/word/.apply-attribute wbeg wend wattr 7435 ## @param[in] wbeg wend wattr 7436 function ble/highlight/layer:syntax/word/.apply-attribute { 7437 local wbeg=$1 wend=$2 wattr=$3 7438 ((wbeg<color_umin&&(wbeg=color_umin), 7439 wend>color_umax&&(wend=color_umax), 7440 wbeg<wend)) || return 1 7441 7442 if [[ $wattr =~ ^[0-9]+$ ]]; then 7443 ble/array#fill-range _ble_highlight_layer_syntax2_table "$wbeg" "$wend" "$wattr" 7444 elif [[ $wattr == m* ]]; then 7445 local ranges; ble/string#split ranges , "${wattr:1}" 7446 local i=$wbeg j range 7447 for range in "${ranges[@]}"; do 7448 local len=${range%%:*} sub_wattr=${range#*:} 7449 if [[ $len == '$' ]]; then 7450 j=$wend 7451 else 7452 ((j=i+len,j>wend&&(j=wend))) 7453 fi 7454 ble/highlight/layer:syntax/word/.apply-attribute "$i" "$j" "$sub_wattr" 7455 (((i=j)<wend)) || break 7456 done 7457 elif [[ $wattr == d ]]; then 7458 ble/array#fill-range _ble_highlight_layer_syntax2_table "$wbeg" "$wend" '' 7459 fi 7460 } 7461 7462 function ble/highlight/layer:syntax/word/.proc-childnode { 7463 if [[ $wtype =~ ^[0-9]+$ ]]; then 7464 local wbeg=$wbegin wend=$TE_i 7465 ble/highlight/layer:syntax/word/.apply-attribute "$wbeg" "$wend" "$attr" 7466 fi 7467 7468 ((tchild>=0)) && ble/syntax/tree-enumerate-children "$proc_children" 7469 } 7470 7471 ## @var[in,out] _ble_syntax_word_umin _ble_syntax_word_umax 7472 function ble/highlight/layer:syntax/update-word-table { 7473 # update table2 (単語の削除に関しては後で考える) 7474 # (1) 単語色の計算 7475 local color_umin=-1 color_umax=-1 iN=${#_ble_syntax_text} 7476 ble/highlight/layer:syntax/word/.update-attributes 7477 7478 # (2) 色配列 shift 7479 ble/highlight/layer/update/shift _ble_highlight_layer_syntax2_table 7480 7481 # 2015-08-16 暫定 (本当は入れ子構造を考慮に入れたい) 7482 ble/syntax/wrange#update _ble_syntax_word_ "$_ble_syntax_vanishing_word_umin" "$_ble_syntax_vanishing_word_umax" 7483 ble/syntax/wrange#update color_ "$_ble_syntax_vanishing_word_umin" "$_ble_syntax_vanishing_word_umax" 7484 _ble_syntax_vanishing_word_umin=-1 _ble_syntax_vanishing_word_umax=-1 7485 7486 # (3) 色配列に登録 7487 ble/highlight/layer:syntax/word/.apply-attribute 0 "$iN" d # clear word color 7488 local TE_i 7489 for ((TE_i=_ble_syntax_word_umax;TE_i>=_ble_syntax_word_umin;)); do 7490 if ((TE_i>0)) && [[ ${_ble_syntax_tree[TE_i-1]} ]]; then 7491 local -a node 7492 ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}" 7493 7494 local wlen=${node[1]} 7495 local wbeg=$((TE_i-wlen)) wend=$TE_i 7496 7497 if [[ ${node[0]} =~ ^[0-9]+$ ]]; then 7498 local attr=${node[4]} 7499 ble/highlight/layer:syntax/word/.apply-attribute "$wbeg" "$wend" "$attr" 7500 fi 7501 7502 local tclen=${node[2]} 7503 if ((tclen>=0)); then 7504 local tchild=$((TE_i-tclen)) 7505 local tree= TE_nofs=0 proc_children=ble/highlight/layer:syntax/word/.proc-childnode 7506 ble/syntax/tree-enumerate-children "$proc_children" 7507 fi 7508 7509 ((TE_i=wbeg)) 7510 else 7511 ((TE_i--)) 7512 fi 7513 done 7514 ((color_umin>=0)) && ble/highlight/layer:syntax/touch-range "$color_umin" "$color_umax" 7515 7516 _ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1 7517 } 7518 7519 function ble/highlight/layer:syntax/update-error-table/set { 7520 local i1=$1 i2=$2 g=$3 7521 if ((i1<i2)); then 7522 ble/highlight/layer:syntax/touch-range "$i1" "$i2" 7523 ble/highlight/layer:syntax/fill _ble_highlight_layer_syntax3_table "$i1" "$i2" "$g" 7524 _ble_highlight_layer_syntax3_list[${#_ble_highlight_layer_syntax3_list[@]}]="$i1 $i2" 7525 fi 7526 } 7527 function ble/highlight/layer:syntax/update-error-table { 7528 ble/highlight/layer/update/shift _ble_highlight_layer_syntax3_table 7529 7530 # clear old errors 7531 # shift の前の方が簡単に更新できるが、 7532 # umin umax を更新する為に shift の後で処理する。 7533 local j=0 jN=${#_ble_highlight_layer_syntax3_list[*]} 7534 if ((jN)); then 7535 for ((j=0;j<jN;j++)); do 7536 local -a range 7537 ble/string#split-words range "${_ble_highlight_layer_syntax3_list[j]}" 7538 7539 local a=${range[0]} b=${range[1]} 7540 ((a>=DMAX0?(a+=DMAX-DMAX0):(a>=DMIN&&(a=DMIN)), 7541 b>=DMAX0?(b+=DMAX-DMAX0):(b>=DMIN&&(b=DMIN)))) 7542 if ((a<b)); then 7543 ble/highlight/layer:syntax/fill _ble_highlight_layer_syntax3_table "$a" "$b" '' 7544 ble/highlight/layer:syntax/touch-range "$a" "$b" 7545 fi 7546 done 7547 _ble_highlight_layer_syntax3_list=() 7548 fi 7549 7550 # この実装では毎回全てのエラーを設定するので 7551 # 実は下の様にすれば良いだけ… 7552 #_ble_highlight_layer_syntax3_table=() 7553 7554 # set errors 7555 if ((iN>0)) && [[ ${_ble_syntax_stat[iN]} ]]; then 7556 # iN==0 の時は実行しない。face 遅延初期化のため(最初は iN==0)。 7557 local ret; ble/color/face2g syntax_error; local g=$ret 7558 7559 # 入れ子が閉じていないエラー 7560 local -a stat 7561 ble/string#split-words stat "${_ble_syntax_stat[iN]}" 7562 local ctx=${stat[0]} nlen=${stat[3]} nparam=${stat[6]} 7563 [[ $nparam == none ]] && nparam= 7564 local i inest 7565 if ((nlen>0)) || [[ $nparam ]]; then 7566 # 終端点の着色 7567 ble/highlight/layer:syntax/update-error-table/set "$((iN-1))" "$iN" "$g" 7568 7569 if ((nlen>0)); then 7570 ((inest=iN-nlen)) 7571 while ((inest>=0)); do 7572 # 開始字句の着色 7573 local inest2 7574 for ((inest2=inest+1;inest2<iN;inest2++)); do 7575 [[ ${_ble_syntax_attr[inest2]} ]] && break 7576 done 7577 ble/highlight/layer:syntax/update-error-table/set "$inest" "$inest2" "$g" 7578 7579 ((i=inest)) 7580 local wtype wbegin tchild tprev 7581 ble/syntax/parse/nest-pop 7582 ((inest>=i&&(inest=i-1))) 7583 done 7584 fi 7585 fi 7586 7587 # コマンド欠落・引数の欠落 7588 if ((ctx==CTX_CMDX1||ctx==CTX_CMDXC||ctx==CTX_FARGX1||ctx==CTX_SARGX1||ctx==CTX_FARGX2||ctx==CTX_CARGX1||ctx==CTX_CARGX2||ctx==CTX_COARGX)); then 7589 # 終端点の着色 7590 ble/highlight/layer:syntax/update-error-table/set "$((iN-1))" "$iN" "$g" 7591 fi 7592 fi 7593 } 7594 7595 function ble/highlight/layer:syntax/update { 7596 local text=$1 player=$2 7597 local i iN=${#text} 7598 7599 local umin=-1 umax=-1 7600 # 少なくともこの範囲は文字が変わっているので再描画する必要がある 7601 ((DMIN>=0)) && umin=$DMIN umax=$DMAX 7602 7603 # 今回 layer:syntax が無効の時 7604 if [[ ! $bleopt_highlight_syntax ]]; then 7605 if [[ $_ble_highlight_layer_syntax_active ]]; then 7606 _ble_highlight_layer_syntax_active= 7607 PREV_UMIN=0 PREV_UMAX=${#1} 7608 fi 7609 return 0 7610 fi 7611 7612 # 前回 layer:syntax が無効だった時 7613 if [[ ! $_ble_highlight_layer_syntax_active ]]; then 7614 # Request full update 7615 _ble_highlight_layer_syntax_active=1 7616 umin=0 umax=${#text} 7617 fi 7618 7619 #-------------------------------------------------------- 7620 7621 #%if !release 7622 if [[ $bleopt_syntax_debug ]]; then 7623 local debug_attr_umin=$_ble_syntax_attr_umin 7624 local debug_attr_uend=$_ble_syntax_attr_umax 7625 fi 7626 #%end 7627 7628 ble/cmdspec/initialize # load chroma 7629 ble/highlight/layer:syntax/update-attribute-table 7630 ble/highlight/layer:syntax/update-word-table 7631 ble/highlight/layer:syntax/update-error-table 7632 7633 # shift&sgr 設定 7634 if ((DMIN>=0)); then 7635 ble/highlight/layer/update/shift _ble_highlight_layer_syntax_buff 7636 if ((DMAX>0)); then 7637 local g sgr ch ret 7638 ble/highlight/layer:syntax/getg "$DMAX" 7639 ble/color/g2sgr "$g"; sgr=$ret 7640 ch=${_ble_highlight_layer_plain_buff[DMAX]} 7641 _ble_highlight_layer_syntax_buff[DMAX]=$sgr$ch 7642 fi 7643 fi 7644 7645 local i j g gprev=0 7646 if ((umin>0)); then 7647 ble/highlight/layer:syntax/getg "$((umin-1))" 7648 gprev=$g 7649 fi 7650 7651 if ((umin>=0)); then 7652 local ret 7653 for ((i=umin;i<=umax;i++)); do 7654 local ch=${_ble_highlight_layer_plain_buff[i]} 7655 ble/highlight/layer:syntax/getg "$i" 7656 [[ $g ]] || ble/highlight/layer/update/getg "$i" 7657 if ((gprev!=g)); then 7658 ble/color/g2sgr "$g" 7659 ch=$ret$ch 7660 ((gprev=g)) 7661 fi 7662 _ble_highlight_layer_syntax_buff[i]=$ch 7663 done 7664 fi 7665 7666 PREV_UMIN=$umin PREV_UMAX=$umax 7667 PREV_BUFF=_ble_highlight_layer_syntax_buff 7668 7669 #%if !release 7670 if [[ $bleopt_syntax_debug ]]; then 7671 local status buff= nl=$'\n' 7672 _ble_syntax_attr_umin=$debug_attr_umin _ble_syntax_attr_umax=$debug_attr_uend ble/syntax/print-status -v status 7673 7674 local -a DRAW_BUFF=() 7675 ble/syntax/print-layer-buffer.draw plain 7676 ble/syntax/print-layer-buffer.draw syntax 7677 ble/syntax/print-layer-buffer.draw disabled 7678 ble/syntax/print-layer-buffer.draw region 7679 ble/syntax/print-layer-buffer.draw overwrite 7680 local ret; ble/canvas/sflush.draw 7681 status=$status$ret 7682 #ble/util/assign buff 'declare -p _ble_textarea_bufferName $_ble_textarea_bufferName | cat -A'; status="$status$buff" 7683 ble/edit/info/show ansi "$status" 7684 fi 7685 #%end 7686 7687 # # 以下は単語の分割のデバグ用 7688 # local -a words=() word 7689 # for ((i=1;i<=iN;i++)); do 7690 # if [[ ${_ble_syntax_tree[i-1]} ]]; then 7691 # ble/string#split-words word "${_ble_syntax_tree[i-1]}" 7692 # local wtxt="${text:i-word[1]:word[1]}" value 7693 # if ble/syntax:bash/simple-word/is-simple "$wtxt"; then 7694 # local ret; ble/syntax:bash/simple-word/eval "$wtxt" noglob; value=$ret 7695 # else 7696 # value="? ($wtxt)" 7697 # fi 7698 # ble/array#push words "[$value ${word[*]}]" 7699 # fi 7700 # done 7701 # ble/edit/info/show text "${words[*]}" 7702 } 7703 7704 function ble/highlight/layer:syntax/getg { 7705 [[ $bleopt_highlight_syntax ]] || return 1 7706 local i=$1 7707 if [[ ${_ble_highlight_layer_syntax3_table[i]} ]]; then 7708 g=${_ble_highlight_layer_syntax3_table[i]} 7709 elif [[ ${_ble_highlight_layer_syntax2_table[i]} ]]; then 7710 g=${_ble_highlight_layer_syntax2_table[i]} 7711 elif [[ ${_ble_highlight_layer_syntax1_table[i]} ]]; then 7712 g=${_ble_highlight_layer_syntax1_table[i]} 7713 fi 7714 } 7715 7716 function ble/highlight/layer:syntax/textarea_render_defer.hook { 7717 ble/syntax/wrange#update _ble_syntax_word_ "$_ble_syntax_word_defer_umin" "$_ble_syntax_word_defer_umax" 7718 _ble_syntax_word_defer_umin=-1 7719 _ble_syntax_word_defer_umax=-1 7720 } 7721 blehook textarea_render_defer!=ble/highlight/layer:syntax/textarea_render_defer.hook 7722 7723 #%#---------------------------------------------------------------------------- 7724 #%# old test samples 7725 #%#---------------------------------------------------------------------------- 7726 #%begin 7727 # mytest 'echo hello world' 7728 # mytest 'echo "hello world"' 7729 # mytest 'echo a"hed"a "aa"b b"aa" aa' 7730 # mytest 'echo a"$"a a"\$\",$*,$var,$12"a $*,$var,$12' 7731 # mytest 'echo a"---$((1+a[12]*3))---$(echo hello)---"a' 7732 # mytest 'a=1 b[x[y]]=1234 echo <( world ) >> hello; ( sub shell); ((1+2*3));' 7733 # mytest 'a=${#hello} b=${world[10]:1:(5+2)*3} c=${arr[*]%%"test"$(cmd).cpp} d+=12' 7734 # mytest 'for ((i=0;i<10;i++)); do echo hello; done; { : '"'worlds'\\'' record'"'; }' 7735 # mytest '[[ echo == echo ]]; echo hello' 7736 7737 # 関数名に使える文字? 7738 # 7739 # 全く使えない文字 |&;<>()!$\'"` 7740 # 7741 # name() の形式だと 7742 # { } をコマンドとして定義できない。function の形式なら可能 7743 # 7744 # set -H だと 7745 # ! を履歴展開の構文で含む関数は定義できない。 7746 # set +H にしておけば定義する事ができる。 7747 # name() の形式では ^ で始まる関数は定義できない。 7748 # 7749 # extglob on だと 7750 # ? * @ + ! は name() の形式で定義できない。 7751 # 一応 name () と間に空白を挟めば定義できる。 7752 # function ?() *() などとすると "?()" という名前で関数が作られる。 7753 # 7754 #%end 7755 #%#---------------------------------------------------------------------------- 7756 #%) 7757 #%m main main.r/\<ATTR_/_ble_attr_/ 7758 #%m main main.r/\<CTX_/_ble_ctx_/ 7759 #%x main 7760 7761 function ble/syntax/import { :; } 7762 7763 blehook/invoke syntax_load 7764 ble/function#try ble/textarea#invalidate str 7765 7766 return 0