-
-
Notifications
You must be signed in to change notification settings - Fork 76
/
core-syntax.sh
7181 lines (6467 loc) · 253 KB
/
core-syntax.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/bin/bash
#%[release = 0]
#%m main (
function ble/syntax/util/is-directory {
local path=$1
# Note: #D1168 Cygwin では // で始まるパスの判定は遅い
if [[ ( $OSTYPE == cygwin || $OSTYPE == msys ) && $path == //* ]]; then
[[ $path == // ]]
else
[[ -d $path ]]
fi
}
## @fn ble/syntax/urange#update prefix p1 p2
## @fn ble/syntax/wrange#update prefix p1 [p2]
## @param[in] prefix
## @param[in] p1 p2
## @var[in,out] {prefix}umin {prefix}umax
##
## ble/syntax/urange#update に関しては、
## ble/urange#update --prefix=prefix p1 p2 に等価である。
## ble/syntax/wrange#update に対応するものはない。
##
function ble/syntax/urange#update {
local prefix=$1
local p1=$2 p2=${3:-$2}
((0<=p1&&p1<p2)) || return 1
(((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1),
(${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2)))
}
function ble/syntax/wrange#update {
local prefix=$1
local p1=$2 p2=${3:-$2}
((0<=p1&&p1<=p2)) || return 1
(((${prefix}umin<0||${prefix}umin>p1)&&(${prefix}umin=p1),
(${prefix}umax<0||${prefix}umax<p2)&&(${prefix}umax=p2)))
}
## @fn ble/syntax/urange#shift prefix
## @fn ble/syntax/wrange#shift prefix
## @param[in] prefix
## @var[in] beg end end0 shift
## @var[in,out] {prefix}umin {prefix}umax
##
## ble/syntax/urange#shift に関しては、
## ble/urange#shift --prefix=prefix "$beg" "$end" "$end0" "$shift" に等価である。
## ble/syntax/wrange#shift に対応するものはない。
##
function ble/syntax/urange#shift {
local prefix=$1
((${prefix}umin>=end0?(${prefix}umin+=shift):(
${prefix}umin>=beg&&(${prefix}umin=end)),
${prefix}umax>end0?(${prefix}umax+=shift):(
${prefix}umax>beg&&(${prefix}umax=beg)),
${prefix}umin>=${prefix}umax&&
(${prefix}umin=${prefix}umax=-1)))
}
function ble/syntax/wrange#shift {
local prefix=$1
# ※以下の不等号について (動作を見ながら)
# もう一度考え直した方が良いかも。
((${prefix}umin>=end0?(${prefix}umin+=shift):(
${prefix}umin>beg&&(${prefix}umin=end)),
${prefix}umax>=end0?(${prefix}umax+=shift):(
${prefix}umax>=beg&&(${prefix}umax=beg)),
${prefix}umin==0&&++${prefix}umin,
${prefix}umin>${prefix}umax&&
(${prefix}umin=${prefix}umax=-1)))
}
## @var _ble_syntax_text
## 解析対象の文字列を保持する。
## @var _ble_syntax_lang
## 解析対象の言語を保持する。
##
## @var _ble_syntax_stat[i]
## 文字 #i を解釈しようとする直前の状態を記録する。
## 各要素は "ctx wlen wtype nlen tclen tplen nparam lookahead" の形式をしている。
##
## @var ctx = int (stat[0])
## 現在の文脈。
## @var wlen = int (stat[1])
## 現在のシェル単語の継続している長さ。
## @var wtype = string (stat[2])
## 現在のシェル単語の種類。
## @var nlen = int (stat[3])
## 現在の入れ子状態が継続している長さ。
## @var tclen,tplen = int (stat[4], stat[5])
## tchild, tprev の負オフセット。
## @var nparam = string (stat[6])
## その入れ子レベルに特有のデータ一般を記録する文字列。
## ヒアドキュメントの開始情報を記録するのに使用する。
## 将来的に `{ .. }` や `do .. done` の対応を取るのにも使うかもしれない。
## 解析変数の nparam が空文字列のときは "none" という値を格納する。
## @var lookahead = int (stat[7])
## 先読みの文字数を指定します。通常は 1 です。
## この _ble_syntax_stat 要素の情報に影響を与えた、
## 対応する点以降の文字数を格納します。文字列末端も 1 文字と数えます。
##
## @var _ble_syntax_nest[inest]
## 入れ子の情報
## 各要素は "ctx wlen wtype inest tclen tplen nparam ntype" の形式をしている。
## ctx wbegin inest wtype nparam は入れ子を抜けた時の状態を表す。
## ntype は入れ子の種類を表す文字列。
## nparam は復帰時の入れ子レベルにおける nparam 値を保持する。
## nparam 値が空文字列の場合には代わりに none という文字列が格納される。
##
## @var _ble_syntax_tree[i-1]
## 境界 #i で終端した範囲 (単語・入れ子) についての情報を保持する。
## 同じ位置で複数の階層の範囲が終端した場合は、それらの情報が連結されて格納される。
## 各要素は "( wtype wlen tclen tplen wattr )*" の形式をしている。
## より外側の範囲の情報はより左側に格納される。
##
## tclen tplen を用いて他の _ble_syntax_tree 要素を参照する。
## 別の位置から或る位置を参照するとき、一番左側の範囲情報を参照する。
## 或る位置から自分自身を参照するとき、同じ要素の一つ右の範囲情報を参照する。
##
## wtype (ntype)
## 範囲の種類を保持する。範囲が単語のとき、文脈値を整数で保持する。
## 範囲が入れ子範囲のとき、整数以外の文字列になる。
##
## wlen (nlen)
## 範囲の長さを保持する。範囲の開始点は i-wlen である。
##
## tclen
## 0 以上の時、一つ内側の要素の終端位置までの offset を保持する。
## _ble_syntax_tree[i-1-tclen] に子要素の情報が格納されている。
## 子要素がないとき負の値。
##
## tplen
## 0 以上の時、一つ前の兄弟要素までの offset を保持する。
## _ble_syntax_tree[i-1-tplen] に兄要素の情報が格納されている。
## 兄要素が同じ位置で終端することはないので必ず正の値になるはず。
## 兄要素がないとき (自分が長男要素のとき) 負の値。
##
## attr (wattr) または - or --
## 単語の着色に関する情報を保持する。
## 以下の何れかの形式を持つ。
##
## "-"
## 単語の着色が未だ計算されていない事を表す。
## g
## 描画属性値を保持する。
## 'm' len ':' attr (',' len ':' attr)*
## 長さ len の部分列と属性の組。
## 長さ len として '$' を指定した場合は単語終端までを意味する。
## 'd'
## 描画属性を削除することを意味する。
##
## @var _ble_syntax_TREE_WIDTH
## _ble_syntax_tree に格納される一つの範囲情報のフィールドの数。
##
## @var _ble_syntax_attr[i]
## 文脈・属性の情報
_ble_syntax_text=
_ble_syntax_lang=bash
_ble_syntax_stat=()
_ble_syntax_nest=()
_ble_syntax_tree=()
_ble_syntax_attr=()
_ble_syntax_TREE_WIDTH=5
#--------------------------------------
# ble/syntax/tree-enumerate proc
# ble/syntax/tree-enumerate-children proc
# ble/syntax/tree-enumerate-in-range beg end proc
function ble/syntax/tree-enumerate/.add-root-element {
local wtype=$1 wlen=$2 tclen=$3 tplen=$4
# Note: wtype は単語に格納される時に EndWtype テーブルで変換される。
# ble/syntax:bash/ctx-command/check-word-end の実装を参照の事。
[[ ! ${wtype//[0-9]} && ${_ble_syntax_bash_command_EndWtype[wtype]} ]] &&
wtype=${_ble_syntax_bash_command_EndWtype[wtype]}
TE_root="$wtype $wlen $tclen $tplen -- $TE_root"
}
## @fn ble/syntax/tree-enumerate/.initialize
##
## @var[in] iN
## 文字列の長さ、つまり現在の解析終端位置を指定します。
##
## @var[out] TE_root
## ${_ble_syntax_tree[iN-1]} を調整して返します。
## 閉じていない範囲 (word, nest) を終端位置で閉じたときの値を計算します。
##
## @var[out] TE_i
## 一番最後の範囲の終端位置を返します。
## 解析情報がない場合は -1 を返します。
##
## @var[out] TE_nofs
## 0 に初期化します。
##
function ble/syntax/tree-enumerate/.initialize {
if [[ ! ${_ble_syntax_stat[iN]} ]]; then
TE_root= TE_i=-1 TE_nofs=0
return 0
fi
local -a stat nest
ble/string#split-words stat "${_ble_syntax_stat[iN]}"
local wtype=${stat[2]}
local wlen=${stat[1]}
local nlen=${stat[3]} inest
((inest=nlen<0?nlen:iN-nlen))
local tclen=${stat[4]}
local tplen=${stat[5]}
TE_root=
((iN>0)) && TE_root=${_ble_syntax_tree[iN-1]}
while
if ((wlen>=0)); then
# TE_root に単語ノードを追加
ble/syntax/tree-enumerate/.add-root-element "$wtype" "$wlen" "$tclen" "$tplen"
tclen=0
fi
((inest>=0))
do
ble/util/assert '[[ ${_ble_syntax_nest[inest]} ]]' "$FUNCNAME/FATAL1" || break
ble/string#split-words nest "${_ble_syntax_nest[inest]}"
local olen=$((iN-inest))
tplen=${nest[4]}
((tplen>=0&&(tplen+=olen)))
# TE_root にネストノードを追加
ble/syntax/tree-enumerate/.add-root-element "${nest[7]}" "$olen" "$tclen" "$tplen"
wtype=${nest[2]} wlen=${nest[1]} nlen=${nest[3]} tclen=0 tplen=${nest[5]}
((wlen>=0&&(wlen+=olen),
tplen>=0&&(tplen+=olen),
nlen>=0&&(nlen+=olen),
inest=nlen<0?nlen:iN-nlen))
ble/util/assert '((nlen<0||nlen>olen))' "$FUNCNAME/FATAL2" || break
done
if [[ $TE_root ]]; then
((TE_i=iN))
else
((TE_i=tclen>=0?iN-tclen:tclen))
fi
((TE_nofs=0))
}
## @fn ble/syntax/tree-enumerate/.impl command...
## @param[in] command...
## 各ノードについて呼び出すコマンドを指定します。
## @var[in] iN
## @var[in] TE_root,TE_i,TE_nofs
function ble/syntax/tree-enumerate/.impl {
local islast=1
while ((TE_i>0)); do
local -a node
if ((TE_i<iN)); then
ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
else
ble/string#split-words node "${TE_root:-${_ble_syntax_tree[iN-1]}}"
fi
ble/util/assert '((TE_nofs<${#node[@]}))' "$FUNCNAME(i=$TE_i,iN=$iN,TE_nofs=$TE_nofs,node=${node[*]},command=$@)/FATAL1" || break
local wtype=${node[TE_nofs]} wlen=${node[TE_nofs+1]} tclen=${node[TE_nofs+2]} tplen=${node[TE_nofs+3]} attr=${node[TE_nofs+4]}
local wbegin=$((wlen<0?wlen:TE_i-wlen))
local tchild=$((tclen<0?tclen:TE_i-tclen))
local tprev=$((tplen<0?tplen:TE_i-tplen))
"$@"
ble/util/assert '((tprev<TE_i))' "$FUNCNAME/FATAL2" || break
((TE_i=tprev,TE_nofs=0,islast=0))
done
}
## @var[in] iN
## @var[in] TE_root,TE_i,TE_nofs
## @var[in] tchild
function ble/syntax/tree-enumerate-children {
((0<tchild&&tchild<=TE_i)) || return 1
local TE_nofs=$((TE_i==tchild?TE_nofs+_ble_syntax_TREE_WIDTH:0))
local TE_i=$tchild
ble/syntax/tree-enumerate/.impl "$@"
}
function ble/syntax/tree-enumerate-break () ((tprev=-1))
## @fn ble/syntax/tree-enumerate command...
## 現在の解析状態 _ble_syntax_tree に基いて、
## 指定したコマンド command... を
## トップレベルの各ノードに対して末尾にあるノードから順に呼び出します。
##
## @param[in] command...
## 呼び出すコマンドを指定します。
##
## コマンドは以下のシェル変数を入力・出力とします。
## @var[in] TE_i TE_nofs
## @var[in] wtype wbegin wlen attr tchild
## @var[in,out] tprev
## 列挙を中断する時は ble/syntax/tree-enumerate-break
## を呼び出す事によって、tprev=-1 を設定します。
##
## 内部で ble/syntax/tree-enumerate-children を呼び出すと、
## 更に入れ子になった単語について処理を実行する事ができます。
##
## @var[in] iN
## 解析の起点を指定します。_ble_syntax_stat が設定されている必要があります。
## 指定を省略した場合は _ble_syntax_stat の末尾が使用されます。
function ble/syntax/tree-enumerate {
local TE_root TE_i TE_nofs
[[ ${iN:+set} ]] || local iN=${#_ble_syntax_text}
ble/syntax/tree-enumerate/.initialize
ble/syntax/tree-enumerate/.impl "$@"
}
## @fn ble/syntax/tree-enumerate-in-range beg end proc
## 入れ子構造に従わず或る範囲内に登録されている節を列挙します。
## @param[in] beg,end
## @param[in] proc
## 以下の変数を使用する関数を指定します。
## @var[in] wtype wlen wbeg wend wattr
## @var[in] node
## @var[in] TE_i TE_nofs
function ble/syntax/tree-enumerate-in-range {
local beg=$1 end=$2
local proc=$3
local -a node
local TE_i TE_nofs
for ((TE_i=end;TE_i>=beg;TE_i--)); do
((TE_i>0)) && [[ ${_ble_syntax_tree[TE_i-1]} ]] || continue
ble/string#split-words node "${_ble_syntax_tree[TE_i-1]}"
local flagUpdateNode=
for ((TE_nofs=0;TE_nofs<${#node[@]};TE_nofs+=_ble_syntax_TREE_WIDTH)); do
local wtype=${node[TE_nofs]} wlen=${node[TE_nofs+1]} wattr=${node[TE_nofs+4]}
local wbeg=$((wlen<0?wlen:TE_i-wlen)) wend=$TE_i
"${@:3}"
done
done
}
#--------------------------------------
# ble/syntax/print-status
function ble/syntax/print-status/.graph {
local char=$1
if ble/util/isprint+ "$char"; then
graph="'$char'"
return 0
else
local ret
ble/util/s2c "$char"
local code=$ret
if ((code<32)); then
ble/util/c2s $((code+64))
graph="$_ble_term_rev^$ret$_ble_term_sgr0"
elif ((code==127)); then
graph="$_ble_term_rev^?$_ble_term_sgr0"
elif ((128<=code&&code<160)); then
ble/util/c2s $((code-64))
graph="${_ble_term_rev}M-^$ret$_ble_term_sgr0"
else
graph="'$char' ($code)"
fi
fi
}
## @var[in,out] word
function ble/syntax/print-status/.tree-prepend {
local j=$1
local value=$2${tree[j]}
tree[j]=$value
((max_tree_width<${#value}&&(max_tree_width=${#value})))
}
function ble/syntax/print-status/.dump-arrays/.append-attr-char {
if (($?==0)); then
attr="${attr}$1"
else
attr="${attr} "
fi
}
## @fn ble/syntax/print-status/ctx#get-text ctx
## @var[out] ret
function ble/syntax/print-status/ctx#get-text {
local sgr
ble/syntax/ctx#get-name "$1"
ret=${ret#BLE_}
if [[ ! $ret ]]; then
ble/color/face2sgr syntax_error
ret="${ret}CTX$1$_ble_term_sgr0"
fi
}
## @fn ble/syntax/print-status/word.get-text index
## _ble_syntax_tree[index] の内容を文字列にします。
## @param[in] index
## @var[out] word
function ble/syntax/print-status/word.get-text {
local index=$1
ble/string#split-words word "${_ble_syntax_tree[index]}"
local out= ret
if [[ $word ]]; then
local nofs=$((${#word[@]}/_ble_syntax_TREE_WIDTH*_ble_syntax_TREE_WIDTH))
while (((nofs-=_ble_syntax_TREE_WIDTH)>=0)); do
local axis=$((index+1))
local wtype=${word[nofs]}
if [[ $wtype =~ ^[0-9]+$ ]]; then
ble/syntax/print-status/ctx#get-text "$wtype"; wtype=$ret
elif [[ $wtype =~ ^n* ]]; then
# Note: nest-pop 時の tree-append では prefix n を付けている。
wtype=$sgr_quoted\"${wtype:1}\"$_ble_term_sgr0
else
wtype=$sgr_error${wtype}$_ble_term_sgr0
fi
local b=$((axis-word[nofs+1])) e=$axis
local _prev=${word[nofs+3]} _child=${word[nofs+2]}
if ((_prev>=0)); then
_prev="@$((axis-_prev-1))>"
else
_prev=
fi
if ((_child>=0)); then
_child=">@$((axis-_child-1))"
else
_child=
fi
local wattr=${word[nofs+4]} _wattr=
if [[ $wattr != - ]]; then
wattr="/(wattr=$wattr)"
else
wattr=
fi
out=" word=$wtype:$_prev$b-$e$_child$wattr$out"
for ((;b<index;b++)); do
ble/syntax/print-status/.tree-prepend "$b" '|'
done
ble/syntax/print-status/.tree-prepend "$index" '+'
done
word=$out
fi
}
## @fn ble/syntax/print-status/nest.get-text index
## _ble_syntax_nest[index] の内容を文字列にします。
## @param[in] index
## @var[out] nest
function ble/syntax/print-status/nest.get-text {
local index=$1
ble/string#split-words nest "${_ble_syntax_nest[index]}"
if [[ $nest ]]; then
local ret
ble/syntax/print-status/ctx#get-text "${nest[0]}"; local nctx=$ret
local nword=-
if ((nest[1]>=0)); then
ble/syntax/print-status/ctx#get-text "${nest[2]}"; local swtype=$ret
local wbegin=$((index-nest[1]))
nword="$swtype:$wbegin-"
fi
local nnest=-
((nest[3]>=0)) && nnest="'${nest[7]}':$((index-nest[3]))-"
local nchild=-
if ((nest[4]>=0)); then
local tchild=$((index-nest[4]))
nchild='$'$tchild
if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then
nchild=$sgr_error$nchild$_ble_term_sgr0
fi
fi
local nprev=-
if ((nest[5]>=0)); then
local tprev=$((index-nest[5]))
nprev='$'$tprev
if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then
nprev=$sgr_error$nprev$_ble_term_sgr0
fi
fi
local nparam=${nest[6]}
if [[ $nparam == none ]]; then
nparam=
else
nparam=" nparam=${nparam//$_ble_term_FS/$'\e[7m^\\\e[m'}"
fi
nest=" nest=($nctx w=$nword n=$nnest t=$nchild:$nprev$nparam)"
fi
}
## @fn ble/syntax/print-status/stat.get-text index
## _ble_syntax_stat[index] の内容を文字列にします。
## @param[in] index
## @var[out] stat
function ble/syntax/print-status/stat.get-text {
local index=$1
ble/string#split-words stat "${_ble_syntax_stat[index]}"
if [[ $stat ]]; then
local ret
ble/syntax/print-status/ctx#get-text "${stat[0]}"; local stat_ctx=$ret
local stat_word=-
if ((stat[1]>=0)); then
ble/syntax/print-status/ctx#get-text "${stat[2]}"; local stat_wtype=$ret
stat_word="$stat_wtype:$((index-stat[1]))-"
fi
local stat_inest=-
if ((stat[3]>=0)); then
local inest=$((index-stat[3]))
stat_inest="@$inest"
if ((inest<0)) || [[ ! ${_ble_syntax_nest[inest]} ]]; then
stat_inest=$sgr_error$stat_inest$_ble_term_sgr0
fi
fi
local stat_child=-
if ((stat[4]>=0)); then
local tchild=$((index-stat[4]))
stat_child='$'$tchild
if ! ((0<tchild&&tchild<=index)) || [[ ! ${_ble_syntax_tree[tchild-1]} ]]; then
stat_child=$sgr_error$stat_child$_ble_term_sgr0
fi
fi
local stat_prev=-
if ((stat[5]>=0)); then
local tprev=$((index-stat[5]))
stat_prev='$'$tprev
if ! ((0<tprev&&tprev<=index)) || [[ ! ${_ble_syntax_tree[tprev-1]} ]]; then
stat_prev=$sgr_error$stat_prev$_ble_term_sgr0
fi
fi
local snparam=${stat[6]}
if [[ $snparam == none ]]; then
snparam=
else
snparam=" nparam=${snparam//"$_ble_term_FS"/$'\e[7m^\\\e[m'}"
fi
local stat_lookahead=
((stat[7]!=1)) && stat_lookahead=" >>${stat[7]}"
stat=" stat=($stat_ctx w=$stat_word n=$stat_inest t=$stat_child:$stat_prev$snparam$stat_lookahead)"
fi
}
## @var[out] resultA
## @var[in] iN
function ble/syntax/print-status/.dump-arrays {
local -a tree char line
tree=()
char=()
line=()
local ret
ble/color/face2sgr syntax_error; local sgr_error=$ret
ble/color/face2sgr syntax_quoted; local sgr_quoted=$ret
local i max_tree_width=0
for ((i=0;i<=iN;i++)); do
local attr=" ${_ble_syntax_attr[i]:-|}"
if ((_ble_syntax_attr_umin<=i&&i<_ble_syntax_attr_umax)); then
attr="${attr:${#attr}-2:2}*"
else
attr="${attr:${#attr}-2:2} "
fi
[[ ${_ble_highlight_layer_syntax1_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax1_table[i]}"
ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}a${_ble_term_sgr0}"
[[ ${_ble_highlight_layer_syntax2_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax2_table[i]}"
ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}w${_ble_term_sgr0}"
[[ ${_ble_highlight_layer_syntax3_table[i]} ]] && ble/color/g2sgr "${_ble_highlight_layer_syntax3_table[i]}"
ble/syntax/print-status/.dump-arrays/.append-attr-char "${ret}e${_ble_term_sgr0}"
[[ ${_ble_syntax_stat_shift[i]} ]]
ble/syntax/print-status/.dump-arrays/.append-attr-char s
local index=000$i
index=${index:${#index}-3:3}
local word nest stat
ble/syntax/print-status/word.get-text "$i"
ble/syntax/print-status/nest.get-text "$i"
ble/syntax/print-status/stat.get-text "$i"
local graph=
ble/syntax/print-status/.graph "${_ble_syntax_text:i:1}"
char[i]="$attr $index $graph"
line[i]=$word$nest$stat
done
resultA='_ble_syntax_attr/tree/nest/stat?'$'\n'
ble/string#reserve-prototype "$max_tree_width"
for ((i=0;i<=iN;i++)); do
local t=${tree[i]}${_ble_string_prototype::max_tree_width}
resultA="$resultA${char[i]} ${t::max_tree_width}${line[i]}"$'\n'
done
}
## @fn ble/syntax/print-status/.dump-tree/proc1
## @var[out] resultB
## @var[in] prefix
## @var[in] nl
function ble/syntax/print-status/.dump-tree/proc1 {
local tip="| "; tip=${tip:islast:1}
prefix="$prefix$tip " ble/syntax/tree-enumerate-children ble/syntax/print-status/.dump-tree/proc1
resultB="$prefix\_ '${_ble_syntax_text:wbegin:wlen}'$nl$resultB"
}
## @fn ble/syntax/print-status/.dump-tree
## @var[out] resultB
## @var[in] iN
function ble/syntax/print-status/.dump-tree {
resultB=
local nl=$_ble_term_nl
local prefix=
ble/syntax/tree-enumerate ble/syntax/print-status/.dump-tree/proc1
}
function ble/syntax/print-status {
local iN=${#_ble_syntax_text}
local resultA
ble/syntax/print-status/.dump-arrays
local resultB
ble/syntax/print-status/.dump-tree
local result=$resultA$resultB
if [[ $1 == -v && $2 ]]; then
local "${2%%\[*\]}" && ble/util/upvar "$2" "$result"
else
ble/util/print "$result"
fi
}
function ble/syntax/print-layer-buffer.draw {
local layer_name=$1
local -a keys vals
builtin eval "keys=(\"\${!_ble_highlight_layer_${layer_name}_buff[@]}\")"
builtin eval "vals=(\"\${_ble_highlight_layer_${layer_name}_buff[@]}\")"
local ret sgr0=$_ble_term_sgr0
ble/color/face2sgr command_builtin; local sgr1=$ret
ble/color/face2sgr syntax_varname; local sgr2=$ret
ble/color/face2sgr syntax_quoted; local sgr3=$ret
ble/canvas/put.draw "${sgr1}buffer${sgr0} ${sgr2}$layer_name${sgr0}=("
local i count=0
for ((i=0;i<${#keys[@]};i++)); do
local key=${keys[i]} val=${vals[i]}
while ((count++<key)); do
((count==1)) || ble/canvas/put.draw ' '
ble/canvas/put.draw $'\e[91munset\e[m'
done
((count==1)) || ble/canvas/put.draw ' '
ble/string#quote-word "$val" quote-empty:sgrq="$sgr3"
ble/canvas/put.draw "$ret"
done
ble/canvas/put.draw ")$_ble_term_nl"
}
#--------------------------------------
function ble/syntax/parse/generate-stat {
((ilook<=i&&(ilook=i+1)))
_stat="$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))"
}
## @fn ble/syntax/parse/set-lookahead count
##
## @param[in] count
## 現在位置の何文字先まで参照して動作を決定したかを指定します。
## @var[out] i
## @var[out] ilook
##
## 例えば "a@bcdx" の @ の位置に i があって、
## x の文字を見て c 直後までしか読み取らない事を決定したとき、
## set-lookahead 4 を実行して i を 2 進めるか、
## i を 2 進めてから set-lookahead 2 を実行します。
##
## 最終的に i の次の 1 文字までしか参照しない時、
## set-lookahead を呼び出す必要はありません。
##
function ble/syntax/parse/set-lookahead {
((i+$1>ilook&&(ilook=i+$1)))
}
# 構文木の管理 (_ble_syntax_tree)
## @fn ble/syntax/parse/tree-append
## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。
function ble/syntax/parse/tree-append {
#%if !release
[[ $debug_p1 ]] && ble/util/assert '((i-1>=debug_p1))' "Wrong call of tree-append: Condition violation (p1=$debug_p1 i=$i iN=$iN)."
#%end
local type=$1
local beg=$2 end=$i
local len=$((end-beg))
((len==0)) && return 0
local tchild=$3 tprev=$4
# 子情報・兄情報
local ochild=-1 oprev=-1
((tchild>=0&&(ochild=i-tchild)))
((tprev>=0&&(oprev=i-tprev)))
[[ $type =~ ^[0-9]+$ ]] && ble/syntax/parse/touch-updated-word "$i"
# 追加する要素の数は _ble_syntax_TREE_WIDTH と一致している必要がある。
_ble_syntax_tree[i-1]="$type $len $ochild $oprev - ${_ble_syntax_tree[i-1]}"
}
function ble/syntax/parse/word-push {
wtype=$1 wbegin=$2 tprev=$tchild tchild=-1
}
## @fn ble/syntax/parse/word-pop
## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。
# 仮定: 1つ上の level は nest-push による level か top level のどちらかである。
# この場合に限って ble/syntax/parse/nest-reset-tprev を用いて、tprev
# を適切な値に復元することができる。
function ble/syntax/parse/word-pop {
ble/syntax/parse/tree-append "$wtype" "$wbegin" "$tchild" "$tprev"
((wbegin=-1,wtype=-1,tchild=i))
ble/syntax/parse/nest-reset-tprev
}
## '[[' 専用の関数:
## word-push/word-pop と nest-push の順序を反転させる為に。
## 具体的にどう使われているかは使っている箇所を参照すると良い。
## ※本当は [[ が見付かった時点でコマンドとして読み取るのではなく、
## 特別扱いするべきな気もするが、面倒なので今の実装になっている。
## 仮定: 一番最後に設置されたのが単語である事。
## かつ、キャンセルされる単語は今回の解析ステップで設置された物である事。
function ble/syntax/parse/word-cancel {
local -a word
ble/string#split-words word "${_ble_syntax_tree[i-1]}"
local wlen=${word[1]} tplen=${word[3]}
local wbegin=$((i-wlen))
tchild=$((tplen<0?tplen:i-tplen))
ble/dense-array#fill-range _ble_syntax_tree "$wbegin" "$i" ''
}
# 入れ子構造の管理
## @fn ble/syntax/parse/nest-push newctx ntype
## @param[in] newctx 新しい ctx を指定します。
## @param[in,opt] ntype 文法要素の種類を指定します。
## @var [in] i 現在の位置を指定します。
## @var [in out] inest 親 nest の位置を指定します。新しい nest の位置 (i) を返します。
## @var [in,out] ctx 復帰時の ctx を指定します。新しい ctx (newctx) を返します。
## @var [in,out] wbegin 復帰時の wbegin を指定します。新しい wbegin (-1) を返します。
## @var [in,out] wtype 復帰時の wtype を指定します。新しい wtype (-1) を返します。
## @var [in,out] tchild 復帰時の tchild を指定します。新しい tchild (-1) を返します。
## @var [in,out] tprev 復帰時の tprev を指定します。新しい tprev (tchild) を返します。
## @var [in,out] nparam 復帰時の nparam を指定します。新しい nparam (空文字列) を返します。
function ble/syntax/parse/nest-push {
local wlen=$((wbegin<0?wbegin:i-wbegin))
local nlen=$((inest<0?inest:i-inest))
local tclen=$((tchild<0?tchild:i-tchild))
local tplen=$((tprev<0?tprev:i-tprev))
_ble_syntax_nest[i]="$ctx $wlen $wtype $nlen $tclen $tplen ${nparam:-none} ${2:-none}"
((ctx=$1,inest=i,wbegin=-1,wtype=-1,tprev=tchild,tchild=-1))
nparam=
}
## @fn ble/syntax/parse/nest-pop
## 要件 解析位置を進めてから呼び出す必要があります (要件: i>=p1+1)。
## 現在の入れ子を閉じます。現在の入れ子情報を記録して、一つ上の入れ子情報を復元します。
## @var[ out] ctx 上の入れ子階層の ctx を復元します。
## @var[ out] wbegin 上の入れ子階層の wbegin を復元します。
## @var[ out] wtype 上の入れ子階層の wtype を復元します。
## @var[in,out] inest 記録する入れ子情報を指定します。上の入れ子階層の inest を復元します。
## @var[in,out] tchild 記録する入れ子情報を指定します。上の入れ子階層の tchild を復元します。
## @var[in,out] tprev 記録する入れ子情報を指定します。上の入れ子階層の tprev を復元します。
## @var[ out] nparam 上の入れ子階層の nparam を復元します。
function ble/syntax/parse/nest-pop {
((inest<0)) && return 1
local -a parentNest
ble/string#split-words parentNest "${_ble_syntax_nest[inest]}"
local ntype=${parentNest[7]} nbeg=$inest
ble/syntax/parse/tree-append "n$ntype" "$nbeg" "$tchild" "$tprev"
local wlen=${parentNest[1]} nlen=${parentNest[3]} tplen=${parentNest[5]}
((ctx=parentNest[0]))
((wtype=parentNest[2]))
((wbegin=wlen<0?wlen:nbeg-wlen,
inest=nlen<0?nlen:nbeg-nlen,
tchild=i,
tprev=tplen<0?tplen:nbeg-tplen))
nparam=${parentNest[6]}
[[ $nparam == none ]] && nparam=
}
function ble/syntax/parse/nest-type {
local _var=ntype
[[ $1 == -v ]] && _var=$2
if ((inest<0)); then
builtin eval "$_var="
return 1
else
builtin eval "$_var=\"\${_ble_syntax_nest[inest]##* }\""
fi
}
## @fn ble/syntax/parse/nest-ctx
## @var[out] nctx
function ble/syntax/parse/nest-ctx {
nctx=
((inest>=0)) || return 1
nctx=${_ble_syntax_nest[inest]%% *}
}
function ble/syntax/parse/nest-reset-tprev {
if ((inest<0)); then
tprev=-1
else
local -a nest
ble/string#split-words nest "${_ble_syntax_nest[inest]}"
local tclen=${nest[4]}
((tprev=tclen<0?tclen:inest-tclen))
fi
}
## @fn ble/syntax/parse/nest-equals
## 現在のネスト状態と前回のネスト状態が一致するか判定します。
## @var i1 更新開始点
## @var i2 更新終了点
## @var _tail_syntax_stat[i-i2] i2 以降の更新前状態
## @var _ble_syntax_stat[i] 新しい状態
function ble/syntax/parse/nest-equals {
local parent_inest=$1
while :; do
((parent_inest<i1)) && return 0 # 変更していない範囲 または -1
((parent_inest<i2)) && return 1 # 変更によって消えた範囲
local _onest=${_tail_syntax_nest[parent_inest-i2]}
local _nnest=${_ble_syntax_nest[parent_inest]}
[[ $_onest != "$_nnest" ]] && return 1
local -a onest; ble/string#split-words onest "$_onest"
#%if !release
ble/util/assert \
'((onest[3]!=0&&onest[3]<=parent_inest))' \
"invalid nest onest[3]=${onest[3]} parent_inest=$parent_inest text=$text" || return 0
#%end
((parent_inest=onest[3]<0?onest[3]:(parent_inest-onest[3])))
done
}
# 属性値の変更範囲
## @var _ble_syntax_attr_umin, _ble_syntax_attr_umax は更新された文法属性の範囲を記録する。
## @var _ble_syntax_word_umin, _ble_syntax_word_umax は更新された単語の先頭位置の範囲を記録する。
## attr については [_ble_syntax_attr_umin, _ble_syntax_attr_umax) が範囲である。
## word については [_ble_syntax_word_umin, _ble_syntax_word_umax] が範囲である。
_ble_syntax_attr_umin=-1 _ble_syntax_attr_umax=-1
_ble_syntax_word_umin=-1 _ble_syntax_word_umax=-1
_ble_syntax_word_defer_umin=-1 _ble_syntax_word_defer_umax=-1
function ble/syntax/parse/touch-updated-attr {
ble/syntax/urange#update _ble_syntax_attr_ "$1" $(($1+1))
}
function ble/syntax/parse/touch-updated-word {
#%if !release
ble/util/assert "(($1>0))" "invalid word position $1"
#%end
ble/syntax/wrange#update _ble_syntax_word_ "$1"
}
#==============================================================================
#
# 文脈値
#
# 文脈値達 from lib/core-syntax-ctx.def
#%$ sed 's/[[:space:]]*#.*//;/^$/d' lib/core-syntax-ctx.def | awk '$2 ~ /^[0-9]+$/ {print $1 "=" $2;}'
# for debug
_ble_syntax_bash_ctx_names=(
#%$ sed 's/[[:space:]]*#.*//;/^$/d' lib/core-syntax-ctx.def | awk '$2 ~ /^[0-9]+$/ {print " [" $2 "]=" $1;}'
)
## @fn ble/syntax/ctx#get-name ctx
## @param[in] ctx
## @var[out] ret
function ble/syntax/ctx#get-name {
ret=${_ble_syntax_bash_ctx_names[$1]#_ble_ctx_}
}
# @var _BLE_SYNTAX_FCTX[]
# @var _BLE_SYNTAX_FEND[]
# 以上の二つの配列を通して文法要素は最終的に登録される。
# (逆に言えば上の二つの配列を弄れば別の文法の解析を実行する事もできる)
_BLE_SYNTAX_FCTX=()
_BLE_SYNTAX_FEND=()
#==============================================================================
#
# 空文法
#
#------------------------------------------------------------------------------
function ble/syntax:text/ctx-unspecified {
((i+=${#tail}))
return 0
}
_BLE_SYNTAX_FCTX[CTX_UNSPECIFIED]=ble/syntax:text/ctx-unspecified
function ble/syntax:text/initialize-ctx { ctx=$CTX_UNSPECIFIED; }
function ble/syntax:text/initialize-vars { :; }
#==============================================================================
#
# Bash Script 文法
#
#------------------------------------------------------------------------------
_ble_syntax_bash_IFS=$' \t\n'
_ble_syntax_bash_RexSpaces=$'[ \t]+'
_ble_syntax_bash_RexIFSs="[$_ble_syntax_bash_IFS]+"
_ble_syntax_bash_RexDelimiter="[$_ble_syntax_bash_IFS;|&<>()]"
_ble_syntax_bash_RexRedirect='((\{[a-zA-Z_][a-zA-Z_0-9]*\}|[0-9]+)?(&?>>?|>[|&]|<[>&]?|<<[-<]?))[ ]*'
## @var _ble_syntax_bash_chars[]
## 特定の役割を持つ文字の集合。Bracket expression [~] に入れて使う為の物。
## histchars に依存しているので変化があった時に更新する。
_ble_syntax_bash_chars=()
_ble_syntax_bashc_seed=
function ble/syntax:bash/cclass/update/reorder {
builtin eval "local a=\"\${$1}\""
# Bracket expression として安全な順に並び替える
[[ $a == *']'* ]] && a="]${a//]}"
[[ $a == *'-'* ]] && a="${a//-}-"
builtin eval "$1=\$a"
}
## @fn ble/syntax:bash/cclass/update
##
## @var[in] _ble_syntax_bash_histc12
## @var[in,out] _ble_syntax_bashc_seed
## @var[in,out] _ble_syntax_bash_chars[]
##
## @exit 更新があった時に正常終了します。
## 更新の必要がなかった時に 1 を返します。
##
function ble/syntax:bash/cclass/update {
local seed=$_ble_syntax_bash_histc12
shopt -q extglob && seed=${seed}x
[[ $seed == "$_ble_syntax_bashc_seed" ]] && return 1
_ble_syntax_bashc_seed=$seed
local key modified=
if [[ $_ble_syntax_bash_histc12 == '!^' ]]; then
for key in "${!_ble_syntax_bash_charsDef[@]}"; do
_ble_syntax_bash_chars[key]=${_ble_syntax_bash_charsDef[key]}
done
_ble_syntax_bashc_simple=$_ble_syntax_bash_chars_simpleDef
else
modified=1
local histc1=${_ble_syntax_bash_histc12:0:1}
local histc2=${_ble_syntax_bash_histc12:1:1}
for key in "${!_ble_syntax_bash_charsFmt[@]}"; do
local a=${_ble_syntax_bash_charsFmt[key]}
a=${a//@h/$histc1}
a=${a//@q/$histc2}
_ble_syntax_bash_chars[key]=$a
done
local a=$_ble_syntax_bash_chars_simpleFmt
a=${a//@h/$histc1}
a=${a//@q/$histc2}
_ble_syntax_bashc_simple=$a
fi
if [[ $seed == *x ]]; then
# extglob: ?() *() +() @() !()
local extglob='@+!' # *? は既に登録されている筈
_ble_syntax_bash_chars[CTX_ARGI]=${_ble_syntax_bash_chars[CTX_ARGI]}$extglob
_ble_syntax_bash_chars[CTX_PATN]=${_ble_syntax_bash_chars[CTX_PATN]}$extglob
_ble_syntax_bash_chars[CTX_PWORD]=${_ble_syntax_bash_chars[CTX_PWORD]}$extglob
_ble_syntax_bash_chars[CTX_PWORDE]=${_ble_syntax_bash_chars[CTX_PWORDE]}$extglob
_ble_syntax_bash_chars[CTX_PWORDR]=${_ble_syntax_bash_chars[CTX_PWORDR]}$extglob
fi
if [[ $modified ]]; then
for key in "${!_ble_syntax_bash_chars[@]}"; do
ble/syntax:bash/cclass/update/reorder _ble_syntax_bash_chars[key]