-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfeed.xml
More file actions
9634 lines (7511 loc) · 789 KB
/
feed.xml
File metadata and controls
9634 lines (7511 loc) · 789 KB
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
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>LvKouKou Blog</title>
<description>这里是 @LvKouKou 的个人博客,与你一起发现更大的世界 | 要做一个想环游世界的程序员</description>
<link>https://jaredddddd.github.io/</link>
<atom:link href="https://jaredddddd.github.io/feed.xml" rel="self" type="application/rss+xml" />
<pubDate>Sun, 06 Jul 2025 07:18:12 +0000</pubDate>
<lastBuildDate>Sun, 06 Jul 2025 07:18:12 +0000</lastBuildDate>
<generator>Jekyll v3.10.0</generator>
<item>
<title>语法分析</title>
<description><h1 id="语法分析关键知识点小结">语法分析关键知识点小结</h1>
<h1 id="ll">LL</h1>
<p>自顶向下解析 可以细分为 两种: 递归下降解析 和( ) ,其中递归下降解析又有两种实现方式,回溯法和 预测解析 ,其中预测解析通过预读字符来实现,其预读的个数也作为一个环境变量。</p>
<p>预读k个字符的预测解析 法 被称为:</p>
<p>LL(k)文法, 其中第一个L表示从左向右扫描输入字符串 ;第二个L表示LeftMost(最左推导)</p>
<p>k表示预读 k个字符。</p>
<h2 id="如何判断文法是不是有二义性">如何判断文法是不是有二义性</h2>
<blockquote>
<p>==尝试的时候就看,没有确定优先级和结合性一般就会导致二义性了==</p>
</blockquote>
<p>定义 10 用二义性甩. 如果对于一个文法,存在一个句子,对这个句子可以构造两棵不同的分析树,那么我们称这个文法为二义的</p>
<p>所以找到两个不同分析树即可,例如:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240409223311707.png" alt="image-20240409223311707" /></p>
<h3 id="优先级和结合性">优先级和结合性:</h3>
<blockquote>
<p>参考:<a href="https://blog.csdn.net/sandalphon4869/article/details/103423292">编译原理(三)语法分析:3.二义性与二义性的消除</a></p>
</blockquote>
<p>若文法G对同一句子产生不止一棵分析树,则称G是二义的。</p>
<blockquote>
<p>例3.7 句子id+id*id和id+id+id可能的分析树
<code class="language-plaintext highlighter-rouge">E→E+E | E*E |(E)| -E | id</code></p>
</blockquote>
<p>深度越深,越远离开始节点,<a href="https://so.csdn.net/so/search?q=优先级&amp;spm=1001.2101.3001.7020">优先级</a>越高。
非终结符在终结符(如+)的左边是左结合,右边是右结合。
<img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/20191206145651402.png" alt="在这里插入图片描述" /></p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/24326825-0aa2e31475b1f08a.png" alt="img" /></p>
<h2 id="如何判断是不是ll文法">如何判断是不是LL文法</h2>
<p>Top-down + predict = LL(K)</p>
<p>如果有以下两项则不是LL(K)文法(lec_7,P35)</p>
<ol>
<li>有共同前缀</li>
<li>有左递归</li>
</ol>
<p>幸运的是有办法可以消除这两个。</p>
<h2 id="如何变为ll文法">如何变为LL文法</h2>
<p>==建议先消除共同前缀,然后再消除左递归,例如hw2的1.5==</p>
<h3 id="如何消除左递归">如何消除左递归</h3>
<p>更一般情况下的直接左递归</p>
<p>考虑如下文法:</p>
<table>
<tbody>
<tr>
<td>A→Aα1</td>
<td>Aα2</td>
<td>…</td>
<td>Aαm</td>
<td>β1</td>
<td>β2</td>
<td>…</td>
<td>βn</td>
</tr>
</tbody>
</table>
<p>产生的句子为:</p>
<p>βiαj1αj2…</p>
<p>所以,消除递归后文法为:</p>
<p>A→(β1|β2|…|βn)A’ 用来产生以βi开头的句型/句子
A’→(α1|α2|…|αm)A’|ε
\(\begin{align*}
A &amp;\rightarrow A\alpha \\
&amp;\rightarrow A\alpha \alpha \\
&amp;\rightarrow A\alpha \alpha \alpha
\end{align*}\)
<img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240409224714958.png" alt="image-20240409224714958" style="zoom: 50%;" /></p>
<hr />
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240409224655651.png" alt="image-20240409224655651" style="zoom: 50%;" /></p>
<h4 id="易错点例子">易错点例子</h4>
<p>==β为多个时==</p>
<blockquote>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701170939385.png" alt="image-20240701170939385" /></p>
</blockquote>
<p>==没有β时==</p>
<blockquote>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701170947186.png" alt="image-20240701170947186" /></p>
</blockquote>
<h3 id="如何消除共同前缀">如何消除共同前缀</h3>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240409225649592.png" alt="image-20240409225649592" style="zoom: 50%;" /></p>
<h2 id="first集follow集和select集">First集、Follow集和Select集</h2>
<p>定义 15</p>
<ul>
<li>
<p>FIRST(α)为从α中推导出来的字符串<strong>第一个终端符号</strong>的集合</p>
</li>
<li>
<p>FOLLOW(A)集为可以出现在<strong>A右侧</strong>的<strong>终端符号</strong>的集合</p>
<blockquote>
<p>FOLLOW(A)是所有该文法开始符推导的句型中出现在<strong>紧跟A之后</strong>的终结符或 “#”。
\(follow(A)=\{a|S\to...Aa...,a\in V_t\}\)
其中S表示文法的开始符号,非终结符A后面跟着的<strong>终结符</strong>或<strong>‘#</strong>’集合,且是紧挨着A的</p>
</blockquote>
</li>
</ul>
<hr />
<h3 id="如何求first集">如何求First集</h3>
<p>求$First(X)$</p>
<ol>
<li>
<p>如果$X$是<strong>终结符</strong>,则$First(X)={X}$</p>
</li>
<li>
<p>如果$X$是<strong>非终结符</strong>且存在$X\to \varepsilon $,则将$\varepsilon$加入到$First(X)$中</p>
</li>
<li>
<p>若$X→Y…$,且$Y$属于<strong>非终结符</strong>且$Y$不能推出$\varepsilon$,则将$FIRST(Y)/{\varepsilon}$加入到$FIRST(X)$中。</p>
<blockquote>
<p>也就是第一个<strong>非终结符</strong>的$First$集就不包含$\varepsilon$(换句话说第一个<strong>非终结符</strong>不能推出$\varepsilon$)</p>
</blockquote>
</li>
<li>
<p>如果$X$是非终结符且$X \to Y_1Y_2Y_3…Y_k$,则</p>
<ol>
<li>
<p>若$Y_1Y_2Y_3…Y_k$都是<strong>非终结符</strong>,且$Y_1Y_2Y_3…Y_{i-1}$的$FIRST$集合中均包含$\varepsilon$,则将$FIRST(Y_j),(j=1,2…i)$的所有非$\varepsilon$元素加入到$FIRST(X)$中,</p>
<blockquote>
<p>==也就是找到第一个$First$集里不包含$\varepsilon$的的$Y_i$==,然后将$Y_0到Y_i$的$First$集合都加入到$First(X)$中。</p>
<blockquote>
<p>这里很容易忘记是要找到第一个不包含$\varepsilon$的symbol</p>
</blockquote>
<p><strong>例如</strong>:如果X是非终结符,有产生式形如$X\to ABCdEF…$(A,B,C为<strong>非终结符</strong>且包含ε,d为终结符,也就是说First(d)={d}不包含$\varepsilon$),则需要把<strong>FIRST(A), FIRST(B),FIRST(C),First(d)={d}</strong>入到FIRST(X)中</p>
</blockquote>
</li>
<li>
<p>==特别地,若$Y_1Y_2Y_3…Y_k$均有$\varepsilon$产生式,则将$\varepsilon$加到$FIRST(X)$中。==</p>
</li>
</ol>
</li>
</ol>
<p>所以可以总结为直到找到后面不能推出空的,然后累加前面的所有的First集</p>
<hr />
<p><strong>技巧:</strong></p>
<p>==First一般从下往上找。==</p>
<p>如果要找A的First,我们要找A的定义式,即A在<strong>左边</strong>的式子,看着他的右边来找。</p>
<hr />
<h3 id="如何求follow集">如何求Follow集</h3>
<p>如何求$Follow集$(==非终结符才有==),所以求follow集的时候先找非终结符</p>
<ol>
<li>
<p>如果是文法的开始符号S,把$$$加入到$Follow(S)$</p>
</li>
<li>
<p>如果有$A\toαBβ$,则把$First(β)/{ε}$加入到$Follow(B)$中.<strong>(first(β)/{ε}表示把first(β)中去掉ε)</strong></p>
<blockquote>
<p>也就是这里B后面第一个不为空,所以可以直接将后一个的First集加入到$Follow(B)$中</p>
</blockquote>
</li>
<li>
<p>如果有$A\to αB$或$A\to αBβ$,<strong>且$β\toε$</strong>(这个条件应该是First(β)包含ε),则把$Follow(A)$加入到$Follow(B)$中。</p>
<blockquote>
<p><strong>注意,</strong>A-&gt;αBβ,α可以是终结符也可以为非终结符也可以为空,没影响,因为Follow看的是B后面可以是什么符号,β可以为终结符或者非终结符,但是不能为空且B后面要有符号;</p>
</blockquote>
</li>
</ol>
<p>==注意这里要按顺序走完,满足2的同样有机会满足3,(有$β\toε$)==</p>
<table>
<tbody>
<tr>
<td>如S-&gt;(L)</td>
<td>aL</td>
<td>LC</td>
</tr>
</tbody>
</table>
<p>找Follow的三种情况:</p>
<p>先在候选式(右边)中找到该<strong>非终结符</strong>,如L(注意例中只有一个定义,但找Follow要看到所有右边出现该非终结符的)</p>
<ul>
<li>如果L的右边是<strong>终结符</strong>, 那么这个终结符加入L的$Follow$(其实这一个是第二个情况的一部分,因为如果L右边是终结符,也就是$β$是终结符,那么$First(β)={β}$,所以相当于是把这个终结符加入$Follow集$)</li>
<li>如果L的右边是<strong>非终结符</strong>, 那么把这个<strong>非终结符</strong>的$First$除去空加到L的Follow中</li>
<li>如果L处在最末尾(右边没有东西了),那么,$\to$左手边符号的$Follow$集加入到L的$Follow$集。</li>
</ul>
<hr />
<p><strong>小结:</strong>
FOLLOW集对于<strong>非终结符</strong>而言,是非终结符的全部后跟符号的集合,</p>
<p>如果后跟终结符则加入,</p>
<p>如果后跟非终结符,则加入该非终结符的不含空符号串的FIRST集,</p>
<p>特别地,文法的<strong>开始符号</strong>的FOLLOW集需要额外的加入‘#’。</p>
<p>1</p>
<p><strong>技巧</strong></p>
<p>==Follow一般从上往下找。==</p>
<p>如果要找L的Follow,要从式子的<strong>右边</strong>找到<strong>非终结符</strong>L,然后来找L的Follow,这与First是不同的。</p>
<h3 id="如何求select集">如何求Select集</h3>
<p><strong>定义</strong>:给定上下文无关文法的产生式$A→α, A∈VN,α∈V*$</p>
<p>如何求:(α是一个整体,例如<img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701205715627.png" alt="image-20240701205715627" />,其中第一个A可以推出空,所以还需要求B的first,所以First(AB)={a,b})</p>
<ol>
<li>若α不能推导出ε则:SELECT(A→α)=FIRST(α) </li>
<li>如果α能推导出ε则:SELECT(A→α)=(FIRST(α) –{ε})∪FOLLOW(A)</li>
</ol>
<blockquote>
<p>也可以</p>
<ol>
<li>
<p>先只考虑first集</p>
</li>
<li>
<p>然后考虑如果 ε 在 FIRST(α) 中,或者 α=ε,则 a 在 FOLLOW(A) 中(ε-产生式)</p>
<blockquote>
<p>如果 ε 在 FIRST(α) 中并且 $ 在 FOLLOW(A) 中,则将 A 与 α 一起添加到 M[A, $]</p>
</blockquote>
</li>
</ol>
<p>例如:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701000422788.png" alt="image-20240701000422788" /></p>
</blockquote>
<p>易错点的例题( 来自HW2-3(1) ):</p>
<blockquote>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701170440626.png" alt="image-20240701170440626" /></p>
</blockquote>
<p>需要注意的是,SELECT集是针对<strong>产生式</strong>而言的。</p>
<p><strong>意义</strong>:给定输入的表达式字符串,需要根据表达式的字符串中的字符用来寻找正确的产生式,即<strong>输入的表达式字串中的字符</strong>作为key,<strong>产生式</strong>作为value,那么这个select集就是构造的map</p>
<p>例子:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="n">G</span><span class="p">(</span><span class="n">E</span><span class="p">)</span><span class="o">:</span>
<span class="n">E</span><span class="o">-&gt;</span><span class="n">TE</span><span class="err">'</span>
<span class="n">E</span><span class="err">'</span><span class="o">-&gt;+</span><span class="n">TE</span><span class="err">'</span> <span class="o">|</span> <span class="err">ε</span>
<span class="n">T</span><span class="o">-&gt;</span><span class="n">FT</span><span class="err">'</span>
<span class="n">T</span><span class="err">'</span><span class="o">-&gt;*</span><span class="n">FT</span><span class="err">'</span> <span class="o">|</span> <span class="err">ε</span>
<span class="n">F</span><span class="o">-&gt;</span><span class="p">(</span><span class="n">E</span><span class="p">)</span> <span class="o">|</span> <span class="n">i</span>
<span class="err">对应的</span><span class="n">first</span><span class="err">集合:</span>
<span class="n">first</span><span class="p">(</span><span class="n">E</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="p">(}</span>
<span class="n">first</span><span class="p">(</span><span class="n">T</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="p">(}</span>
<span class="n">first</span><span class="p">(</span><span class="n">E</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">+</span><span class="p">,</span> <span class="err">ε</span><span class="p">}</span>
<span class="n">first</span><span class="p">(</span><span class="n">T</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">*</span><span class="p">,</span> <span class="err">ε</span><span class="p">}</span>
<span class="n">first</span><span class="p">(</span><span class="n">F</span><span class="p">)</span> <span class="o">=</span> <span class="p">{(,</span> <span class="n">i</span><span class="p">}</span>
<span class="err">对应的</span><span class="n">follow</span> <span class="err">集合:</span>
<span class="n">follow</span><span class="p">(</span><span class="n">E</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// E作为开始符,且在F-&gt;(E) | i 产生式中,E后存在 ) 符号</span>
<span class="n">follow</span><span class="p">(</span><span class="n">E</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// 根据规则 A-&gt;αB,则E-&gt;TE'将follow(E)加入到follow(E')</span>
<span class="n">follow</span><span class="p">(</span><span class="n">T</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">+</span><span class="p">,</span> <span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// 有E-&gt;TE',根据A-&gt;αBβ规则,有α为空,E'-&gt;ε,此时将follow(E)加入follow(T),根据E'-&gt;+TE' | ε将first(+)\{ε}加入follow(T)</span>
<span class="n">follow</span><span class="p">(</span><span class="n">T</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">+</span><span class="p">,</span> <span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// T-&gt;FT'将follow(T)加入</span>
<span class="n">follow</span><span class="p">(</span><span class="n">F</span><span class="p">)</span> <span class="o">=</span> <span class="p">{(</span><span class="o">*</span><span class="p">,</span><span class="o">+</span><span class="p">,</span><span class="err">#</span><span class="p">,)}</span> <span class="c1">// T-&gt;FT',根据A-&gt;αBβ,α为空,T'-&gt;ε,将follow(T)加入;T'-&gt;*FT' | ε,根据A-&gt;αBβ,将first(T') / {ε} 加入</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>那么计算的Select集是</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="n">select</span><span class="p">(</span><span class="n">E</span><span class="o">-&gt;</span><span class="n">TE</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">,</span> <span class="p">(}</span> <span class="c1">// T无法推出ε,所以取first(T)</span>
<span class="n">select</span><span class="p">(</span><span class="n">E</span><span class="err">'</span><span class="o">-&gt;+</span><span class="n">TE</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">+</span><span class="p">}</span> <span class="c1">// 直接取first(+)</span>
<span class="n">select</span><span class="p">(</span><span class="n">E</span><span class="err">'</span><span class="o">-&gt;</span><span class="err">ε</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// 注意候选式要分开计算,因为是ε所以取follow(E')</span>
<span class="n">select</span><span class="p">(</span><span class="n">T</span><span class="p">)</span> <span class="o">=</span> <span class="p">{(,</span><span class="n">i</span><span class="p">}</span> <span class="c1">// first(F)</span>
<span class="n">select</span><span class="p">(</span><span class="n">T</span><span class="err">'</span><span class="o">-&gt;*</span><span class="n">FT</span><span class="err">'</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="o">*</span><span class="p">}</span> <span class="c1">// first(*)</span>
<span class="n">select</span><span class="p">(</span><span class="n">T</span><span class="err">'</span><span class="o">-&gt;</span><span class="err">ε)</span> <span class="o">=</span> <span class="p">{</span><span class="o">+</span><span class="p">,</span> <span class="err">#</span><span class="p">,</span> <span class="p">)}</span> <span class="c1">// follow(T')</span>
<span class="n">select</span><span class="p">(</span><span class="n">F</span><span class="o">-&gt;</span><span class="p">(</span><span class="n">E</span><span class="p">))</span> <span class="o">=</span> <span class="p">{(}</span> <span class="c1">// first(()</span>
<span class="n">select</span><span class="p">(</span><span class="n">F</span><span class="o">-&gt;</span><span class="n">i</span><span class="p">)</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">}</span> <span class="c1">// first(i)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>参考:<a href="https://blog.csdn.net/wzy1120433847/article/details/121866976">First集 Follow集 Select集 知识点+例题</a></p>
<h3 id="利用select集构建parse-table">利用select集构建Parse Table</h3>
<p>根据select表决定放在哪一列</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240630235526319.png" alt="image-20240630235526319" /></p>
<h2 id="如何判断是否符合ll1文法">如何判断是否符合LL(1)文法</h2>
<p>确定文法是否为 LL(1) 的两种方法:</p>
<ol>
<li>
<p>构造LL(1)表,检查如果每个格子中只有一条产生式,那么则是LL(1)</p>
</li>
<li>
<p>观察所有Select集,若<strong>左部相同的产生式</strong>的Select集的交集为空,则符合LL(1)文法,否则不满足。</p>
</li>
</ol>
<h2 id="如何构造ll1的预测语法表">如何构造LL(1)的预测语法表</h2>
<ol>
<li>变为适合LL文法</li>
<li>计算First集和Follow集</li>
<li>根据First集和Follow集计算Select表</li>
<li>根据Select表画出预测语法表</li>
</ol>
<p>用Select表将每个产生式放到产生式<strong>左部非终结符</strong>所对应的行,列表中各个<strong>终结符所对应的列</strong></p>
<p>例如:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240410223835753.png" alt="image-20240410223835753" style="zoom: 43%;" /></p>
<h2 id="如何使用ll1的预测语法表">如何使用LL(1)的预测语法表、</h2>
<p>X为栈顶symbol,a为目前input buffer中处理的token</p>
<ol>
<li>
<p>比较(Match):<strong>栈顶X</strong>为<strong>终结符</strong>出栈查表<strong>对比</strong></p>
<ol>
<li>$X==a==$$,那么返回<strong>success</strong></li>
<li>$X==a!=$$,那么属于中间过程,将X和a弹出即可</li>
<li>$X!=a$$,paser halt and input is <strong>rejected</strong></li>
</ol>
</li>
<li>
<p>展开(Expand):<strong>栈顶X</strong>为<strong>非终结符</strong>出栈<strong>展开</strong></p>
<ol>
<li>
<p>如果MA[X,a] == ‘X-&gt;RHS’,那么X出栈,RHS入栈,input buffer中不变</p>
<blockquote>
<p>入栈的时候是逆序入栈,例如X-&gt;BcD
其实也就是最左推导,最左边符号需要先被展开/比较,所以需要在靠近栈顶的位置。</p>
<table>
<thead>
<tr>
<th>之前的栈</th>
<th>之后的栈</th>
</tr>
</thead>
<tbody>
<tr>
<td> </td>
<td>B</td>
</tr>
<tr>
<td> </td>
<td>c</td>
</tr>
<tr>
<td>X</td>
<td>D</td>
</tr>
<tr>
<td>…</td>
<td>…</td>
</tr>
<tr>
<td>$</td>
<td>$</td>
</tr>
</tbody>
</table>
</blockquote>
</li>
<li>
<p>如果MA[X,a] == empty,paser halt and input is <strong>rejected</strong></p>
</li>
</ol>
</li>
</ol>
<h1 id="lr">LR</h1>
<p>LR 文法,<strong>自底向上</strong>的语法分析</p>
<hr />
<p>首先介绍 <strong>规约</strong> 的概念 ,自底向上的分析 和 自顶向下是反过来的,每次操作都是将 语法符号序列 转为非终结符 。(这与 自顶向下<strong>非终结符</strong>展开为<strong>语法符号序列</strong>恰好相反)</p>
<blockquote>
<p>也就是从右往左替换,例如A-&gt;B,LL中是A被B替换,而LR中是用A替换B</p>
</blockquote>
<p><strong>自底向上</strong> 对应 <strong>反向最右推导</strong>(<strong>自顶向下</strong> 对应 <strong>最左推导</strong>)</p>
<hr />
<p>例如:下面举个例子:
\(id*id=&gt;dF*id=&gt;T*id=&gt;T*T=&gt;T=&gt;E\)
如果反过来:
\(E=&gt;T=&gt;T*F=&gt;T*id=&gt;F*id=&gt;id*id\)
发现正好是一个最右推导</p>
<p>例子(lec-10):</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504160449367.png" alt="image-20240504160449367" style="zoom:50%;" /></p>
<hr />
<p><strong>先科普下LR语法分析器的优势:</strong></p>
<ol>
<li>更通用:对于几乎所有程序设计语言,只要能写出上下文无关文法,就能够造出识别该语言的LR语法分析器。确实存在非LR的上下文无关文法,但常见程序设计语言都可以避免这种文法。</li>
<li>已知的最通用的 <strong>无回溯</strong> 移入-规约分析技术,其实现十分高效。</li>
<li>能尽早的发现语法错误,可以解决左递归问题</li>
<li>LR的语言是LL语言的 真超集(也就是$LL \subseteq LR$)。LR文法的要求比LL文法要求宽松,且与LL一样高效</li>
</ol>
<p>缺点:手工构造LR分析器的工作量非常大,但是可以利用LR语法分析器生成工具。比如Yacc等。</p>
<hr />
<p><strong>分界点用 # 标记</strong>(#号左边即为已经处理过的,右边为还没处理的)</p>
<ul>
<li># 仅作为标示,不是字符串的一部分</li>
<li>最初,所有输入都是未经检查的,例如: $ # x1 x2 . . .xn$[输入尚未有任何解析]</li>
</ul>
<hr />
<p>自下而上的解析也称为 Shift-Reduce 解析</p>
<ul>
<li>
<p>涉及两种类型的操作:Shift和Reduce</p>
<blockquote>
<p>回顾:LL(自顶向下)中也是只有两种动作:expend和compare</p>
</blockquote>
</li>
<li>
<p>Shift[移入]:向右移动 # 一个位置[向左侧移入终结符,推进解析]</p>
</li>
<li>
<p>ABC<strong>#x</strong>yz ⇒ ABC<strong>x#</strong>yz</p>
</li>
<li>
<p>Reduce[归约]:逆向使用产生式[左侧串的右端进行归约](#号左边的句柄规约)</p>
<ul>
<li>如果 E → Cx 是产生式,那么:AB<strong>Cx</strong>#yz ⇒ AB<strong>E</strong>#yz</li>
</ul>
</li>
</ul>
<hr />
<p>我们用栈来实现,</p>
<p>#号左边字符串存储到堆栈中,堆栈的顶部是 #[#号左右串的分界]</p>
<ul>
<li>
<p>Shift就是将终结符放到栈中</p>
</li>
<li>
<p>Reduce 执行以下操作(例如右产生式LHS -&gt; RHS):</p>
<ol>
<li>从堆栈中弹出零个或多个符号[pop出了产生式RHS]</li>
<li>在堆栈上推进非终结符[push进了产生式LHS]</li>
</ol>
<blockquote>
<p>其实就是反向使用产生式LHS &lt;- RHS</p>
</blockquote>
</li>
</ul>
<hr />
<p>下面介绍 <strong>移入-规约 语法分析技术 ,实际上这是自底向上分析的基本算法框架</strong>,</p>
<p>其包含两个组件:1个栈 , 1个输入缓冲区 ;</p>
<p>主要由4 个操作组成:</p>
<p><strong>1)移入</strong>:将下一个输入 符号 移入 栈顶</p>
<p><strong>2)规约</strong> :规约是对栈中的语法符号串进行规约,被规约的串右侧位于栈顶,语法分析器确定串左端</p>
<p><strong>3)接收</strong> :宣布语法分析过程成功完成</p>
<p><strong>4)报错</strong> :发现语法错误,执行错误恢复程序。</p>
<p>移入-规约 技术的关键在于 决定 何时进行 规约, 何时进行移入操作,这将在后文的具体实现中介绍方法。</p>
<h2 id="句柄">句柄</h2>
<p>一个关键的问题是何时进行Shift何时进行Reduce?</p>
<p>只有<strong>栈顶</strong>是<strong>句柄</strong>的时候才Reduce,其他时候都是Shift</p>
<p>实际上句柄就是产生式的替换 ,</p>
<p>如果有$ S=&gt;^{rm*}\alpha A\omega =&gt;^{rm}\alpha \beta \omega$</p>
<p>则我们称$ A-&gt;\beta $是 $\alpha\beta\omega $的一个句柄,为了简化起见,有时直接把 $\beta $称为句柄,其中 $\omega $只包含终结符号(最右推导,所以右边这里其实是已经处理完的,也即最右推导中已被完全展开 ,左边的尚未被完全展开 )。</p>
<blockquote>
<p>回顾: $\omega $只包含终结符号,也就是<strong>句子</strong>,<strong>句型</strong>既包含终结符也包含非终结符</p>
</blockquote>
<p>所以自顶向上分析的技术可以看成不断的替换<strong>句柄</strong>为<strong>非终结符</strong> 的过程。</p>
<hr />
<p>如何找到句柄?</p>
<ul>
<li>最右句型:最右句型是指在语法分析过程中,通过逐步应用产生式规则,从初始符号开始逐步推导出的一系列符号串中的<strong>最后一个</strong>。在自底向上的语法分析(如移进-归约分析)中,最右句型是分析<strong>栈顶部的符号串</strong></li>
<li>短语:一个句型的语法树中<strong>任一子树</strong>的叶结点所组成的符号串都是该句型的短语</li>
<li>直接短语:只用一步就可以推导出</li>
</ul>
<p><strong>句柄是最左直接短语:</strong></p>
<p>例子1:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504162935876.png" alt="image-20240504162935876" /></p>
<p>例子2:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504163108393.png" alt="image-20240504163108393" /></p>
<hr />
<p>==句柄一定只出现在栈顶==</p>
<h2 id="一些概念">一些概念</h2>
<blockquote>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701180526280.png" alt="image-20240701180526280" /></p>
</blockquote>
<p>例子:</p>
<blockquote>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240701180548844.png" alt="image-20240701180548844" /></p>
</blockquote>
<h2 id="活前缀可动前缀">活前缀/可动前缀</h2>
<p>定义:一个可行前缀是一个最右句型的前缀,并且它没有越过该最右句型的最右句柄的右端</p>
<p>举例:S =&gt; bBa =&gt; b<strong>bA</strong>a,这里句柄是 bA,因此可行前缀包括 bA 的所有前缀:b, bb,</p>
<p>bbA),但不能是 bbAa(因为越过了句柄)</p>
<h2 id="二义文法">二义文法</h2>
<p>有多个可能的动作,会导致冲突</p>
<p>例如:既可以像左边一样规约,也可以向右边一样移进</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504163623225.png" alt="image-20240504163623225" /></p>
<p>Can have conflicts[冲突可能发生]</p>
<ul>
<li>如果Shift或Reduce是合法的,则存在<strong>Shift-Reduce conflict[移入-归约冲突]</strong></li>
<li>如果有两个合法的Reduce,则有一个<strong>Reduce-Reduce conflict[归约-归约冲突]</strong></li>
<li><strong>没有Shift-Shift冲突这种东西</strong></li>
</ul>
<hr />
<p>自底向上有很多种实现方式,但是我们只学LR类解析器,例如:LR(0), LR(1), SLR, LALR</p>
<p>LR(k):LR 解析器系列的成员</p>
<ul>
<li>L:从左到右扫描输入</li>
<li>R:反向构造最右边的推导</li>
<li>k:向前看k个
<ul>
<li>k = 0 或 1 特别有意义,默认为 1</li>
</ul>
</li>
</ul>
<p>LR比LL更加powerful且一样高效</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504174545143.png" alt="image-20240504174545143" /></p>
<p>LR 解析器使用两个表:action表和goto表,这两个表通常合并在一起</p>
<ul>
<li>
<p>action表指定终结符的条目(遇到终结符看action表)</p>
</li>
<li>
<p>goto表指定非终结符的条目(遇到非终结符看goto表)</p>
<blockquote>
<p>其实是reduce之后看goto表,reduce得出的结果是非终结符,所以也可以说遇到非终结符看goto表</p>
</blockquote>
</li>
</ul>
<p><strong>Action table[动作表]</strong>(也就是用s+a确定下一个动作,a是终结符)</p>
<ul>
<li>Action[s, a] 告诉解析器当堆栈是 S,终结符 A 是下一个输入token
<ul>
<li>可能的操作:shift、reduce、accept、error</li>
</ul>
</li>
</ul>
<p><strong>Goto Table[跳转表]</strong>(也就是查看action表后发现是Reduce操作,Reduce发生后用S+x确定下一个状态,x是非终结符)</p>
<ul>
<li>Goto[s, X] 表示要放置在堆栈顶部的新状态,在非终结符X 的Reduce之后,状态 s 位于
堆栈</li>
</ul>
<h2 id="如何使用解析表">如何使用解析表</h2>
<p>PPT:lec11</p>
<p>表条目的意思:</p>
<ul>
<li>s<strong>i</strong>:及先执行shift,然后转移到 state i</li>
<li>r<strong>j</strong>:用j号产生式来归约,规约之后要去goto表找对应状态加到stack中</li>
<li>ACC:接受</li>
<li>空白:错误</li>
</ul>
<p>最终目标是栈中只有$</p>
<h2 id="如何构建解析表">如何构建解析表</h2>
<ol>
<li>求出item(根据增广文法求)</li>
<li>由S’-&gt;S求出I0(这里需要用到求closure的方法,每一个$I_{k}$都是先根据$I_{k-1}$中的item,找到转移symbol可得到的下一个状态的item,然后看<strong>点</strong>后面的符号,在全部的production中找到可以的符号再加入到$I_{k}$中)</li>
<li>然后一步步求出全部的I得到DFA</li>
<li>根据DFA构建ACTION表和GOTO表</li>
</ol>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504175519565.png" alt="image-20240504175519565" /></p>
<h2 id="增广文法">增广文法</h2>
<table>
<tbody>
<tr>
<td>首先引入,<strong>增广文法</strong>的概念,这个比较简单。G的增广文法G’是 G加上一个新的 开始符S’,并且加入产生式 :$ S’-&gt;S$ (S为G的开始符) 得到的文法。其目的是告诉语法分析器合适结束(当要运行规则$ S’-&gt;S $时结束),==同时还要把同时又多个可能的拆开,例如$S \to AB</td>
<td>a$ 拆为 $S \to AB, S \to a$==</td>
</tr>
</tbody>
</table>
<blockquote>
<p>对文法进行增广的目的是为了区分开某些右部含有开始符号的文法,在归约过程中能分清是否已归约到文法的最初开始符,还是在文法右部出现的开始符号,增广文法的开始符号S′只在左部出现,这样确保了不会混淆。</p>
</blockquote>
<p>也就是添加了一个0号规则保证ACC的唯一性</p>
<h2 id="s0-initial-state">S0 initial state</h2>
<p>也就是加入了增广文法之后的item set</p>
<p>例子:
\(S \to AB | a\\
A \to a | ε \\
B \to b\)
那么initial stage $S_0$是:
\({ S’\to .S, S \to .AB, S \to .a, A \to .a, A \to . }\)</p>
<hr />
<hr />
<p>下面介绍具体实现,构造LR语法分析器的关键就在于 识别 何时该 移入,何时该规约。</p>
<p>LR语法分析器通过维护一些状态,来做出 移入-规约 决策。</p>
<p>那么这些状态是什么? 是 item 的集合。</p>
<h2 id="如何求文法的项目item">如何求文法的项目item</h2>
<p>那么 item 是什么?:一个文法G的LR(0)item(简称item)是G的一个<strong>产生式</strong>再加上位于其中的某<strong>点</strong>,比如,</p>
<ul>
<li>$A-&gt;XYZ $有4个item:</li>
</ul>
\[A-&gt;\dot \ XYZ\\ A-&gt;X\dot\ YZ\\ A-&gt;XY\dot\ Z \\ A-&gt;XYZ\dot \\\]
<ul>
<li>$A-&gt;\epsilon$ 只有一个item:</li>
</ul>
\[A-&gt; \dot\\]
<p>item的通俗理解是: 表明了在语法分析 过程中 ,我们已经<strong>看到了 一个产生式的哪些部分</strong></p>
<p>举例来说:</p>
<ol>
<li>$A-&gt;\dot\ XYZ $在接下来的输入中期望看到从$XYZ$推导而得到的串</li>
<li>$A-&gt;X\dot\ YZ $表明我们刚刚在输入中看到了一个可以由$X$推导得到的串,并且希望看到从$YZ$推导得到的串。</li>
<li>$A-&gt;XYZ \dot\ $表示已经看到了产生式体 $XYZ $可以,可以执行规约了。</li>
</ol>
<p>==如何求项目:如何求项目:就给每个规则加一个点以后然后挪位置,挪一个位置就得到一个项目,操作完了以后你就得到了一堆项目==</p>
<p>item的集合叫LR parser的<strong>configuration set[配置集]</strong></p>
<hr />
<p><strong>根据项集族,可以构建 DFA(确定有穷自动机)</strong> -&gt; 利用该自动机,就可以自动作出决策(移入 or 规约)</p>
<blockquote>
<p>明确来说,自动机的每个状态对应item set族中的一个item set,</p>
</blockquote>
<hr />
<p>下面来研究如何 构造LR(0)的项集族。</p>
<blockquote>
<ul>
<li><strong>项</strong>就是上文的<strong>item</strong>,已经有明确定义</li>
<li><strong>item set</strong>或者<strong>项集</strong>,表示 item的集合,</li>
<li><strong>item set族</strong>或者<strong>项集族</strong>,意思就是集合的集合 。</li>
</ul>
<p>DFA的每个状态对应 LR(0)项集族中的一个元素,也就是一个项集</p>
</blockquote>
<hr />
<hr />
<h2 id="项目集闭包closure-of-item-sets">项目集闭包(closure of item sets)</h2>
<blockquote>
<p>也是期待意义等价</p>
</blockquote>
<blockquote>
<p>求闭包是要先根据$I_{k-1}$得到$I_k$(对应到图里面,走横线上的才看上一个框),然后对$I_k$中的item求闭包(求闭包的时候(用点后面那个符号扩展继续推)要看<strong>所有的产生式</strong>。)</p>
</blockquote>
<p>下面引入 <strong>项集的闭包</strong>的概念</p>
<p>如果I是文法G的一个项集,那么$ CLOSURE(I) $通过以下算法生成:</p>
<ol>
<li>
<p>令其为$I$(也就是把$I$中每一项都会被加到$ CLOSURE(I) $中 ) ;</p>
</li>
<li>
<p>如果 $A\to\alpha \dot\ B\beta $位于$CLOSURE(I)$ 中,且 $B\to\gamma $是一个产生式,将$ B\to\dot\ \gamma $加入$CLOSURE(I)$(如果有了就不加)</p>
<blockquote>
<p>也就是点后面的那个符号还能推出什么(这一步要查看<strong>所有产生式</strong>),都加到$ CLOSURE(I) $中</p>
</blockquote>
</li>
<li>
<p>不断应用规则2,直到没有新项可以加入$CLOUSURE(I)$.</p>
</li>
</ol>
<blockquote>
<p>对应到图里面,走横线上的才看上一个框,求闭包的时候(用点后面那个符号扩展继续推)要看<strong>所有的产生式</strong>。</p>
</blockquote>
<p>例子1:已知文法G[E]如下:
(1) E -&gt; E+T
(2) E -&gt; T
(3) T -&gt;( E )
(4) T -&gt; d</p>
<p>可以直到它的拓广文法G’ [E’]为 :
(0) E’ -&gt; E
(1) E -&gt; E+T
(2) E -&gt; T
(3) T -&gt; ( E )
(4) T -&gt; d</p>
<p>令I0 = CLOSURE({E’-&gt;.E})</p>
<p>则I0 = {
E’ -&gt; • E,(这一步是根据{E’-&gt;• E}得到的)</p>
<blockquote>
<p>剩下这些是求闭包产生的,要看全部产生式</p>
</blockquote>
<p>E -&gt; • E+T,
E -&gt; • T,
T -&gt; •( E ),
T -&gt; • d
}</p>
<p>例子2:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504180616098.png" alt="image-20240504180616098" /></p>
<hr />
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504181307301.png" alt="image-20240504181307301" /></p>
<hr />
<hr />
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504181409872.png" alt="image-20240504181409872" /></p>
<p>表条目的意思:</p>
<ul>
<li>s<strong>i</strong>:及先执行shift,然后转移到 state i</li>
<li>r<strong>j</strong>:用j号产生式来归约</li>
<li>ACC:接受</li>
<li>空白:错误</li>
</ul>
<p>最终目标是栈中只有$</p>
<hr />
<p>在实际工程实践中,分两种项,</p>
<p>1)核心项: 包含初始项 $S’\to\dot\ S $以及<strong>点不在最左端</strong>的所有项</p>
<p>2)非核心项: 除了 $S’\to\dot\ S $之外的点在最左端的所有项</p>
<p>由于,我们感兴趣的(也就是实际中能出现)的每一个项集 都是某个<strong>内核项</strong>的闭包,则如果抛弃非内核项,就可以用很少的内存来表示项集</p>
<p>【注:以上的意思就是,实际工程出现的项集都是<strong>内核项集</strong>的闭包, 所以只需要用内核项集来表示 真实项集(节省内存),需要的时候执行一次闭包运算即可】</p>
<hr />
<h2 id="goto函数状态转移函数">Goto函数(状态转移函数)</h2>
<p>下面引入 <strong>GOTO函数</strong> 的概念</p>
<p>GOTO (I,X) = CLOSURE(J)</p>
<p>GOTO(I,X):返回可以通过推进X到达的状态。</p>
<p>GOTO(I,X) 输入I 为项集 ,X为语法符号。其取值为是一个项集,定义为:</p>
<p>I中所有形如 $[A\to\alpha\dot\ X\beta] $的项【这里括号只是强调是一个项】所对应的项$[A\to\alpha X\dot\ \beta] $的集合的闭包 。</p>
<blockquote>
<p>也就是先归约/识别之后再求闭包,表示对于一个状态项目集中的一个项目A -&gt; α• Xβ,在下一个输入字符是X的情况下,一定到另一个新状态 A -&gt; αX•β。</p>
<p>例如:</p>
<p>(0) E’ -&gt; E
(1) E -&gt; E+T
(2) E -&gt; T
(3) T -&gt; ( E )
(4) T -&gt; d</p>
<p>那么$GOTO(I_0,E)=CLOSURE{E’ -&gt; E \cdot ,E -&gt; E\cdot+T}$</p>
</blockquote>
<p>显然,GOTO定义了DFA的转换关系。自动机的状态对应于项目集,goto(I, X)指定输入 X 下 I 的状态转换</p>
<p>有了以上概念,我们可以来构造增广语法G’的 LR(0)项集族C了(实际就是构建DFA)</p>
<p>例子:</p>
<p><img src="/img/in-post/CompilationPrinciple/SyntaxAnalysis/image-20240504214809285.png" alt="image-20240504214809285" /></p>
<p>算法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre>void items(G') {
C={CLOSURE({[S'-&gt;.S]})};
repeat
for (C中的每个项集I)
for(每个文法符号X)
if(GOTO(I,X)非空 且不在 C中)
将GOTO(I,X)加入 C中;
until 在某一轮中没有新 项集 被加入 C
}
</pre></td></tr></tbody></table></code></pre></div></div>