《编译原理-龙书》练习第4章

本文深入探讨上下文无关文法表示方法,详细解析文法构建与转换过程,涉及正则表达式转文法、文法无用符号识别、自顶向下与自底向上语法分析等关键概念。同时,介绍LR语法分析技术,包括SLR(1)与LL(1)文法特点及构造LR(0)自动机方法,以及更强大的LR语法分析器的理解与应用。文章旨在提供全面的文法与语法分析技术指南。

4.2 上下文无关文法

**4.2.7节中L={a^nb^n|n>=1}怎么用文法表示?
S -> aAb
A -> ab|ε

4.2.1

1) E -> EE* 

-> EE+E*

-> aa+a*

左到右依次a

2) 与1)一样,只是最后一步右到左依次a

3)       E

      E       E  *

 E E +

id id        id

4)无二义性,但是怎么证明呢?

5)+*组成的后缀表达式

4.2.3 如果是正则表达式,可以采用4.2.7的方法转成文法

1) (0*1+)* 根据DNF推出:

S -> 0A

S -> 1S

S -> ε

A -> 1S

2)

S -> ABA

A -> BB

B -> 0|1|ε

4.2.4

对于A->X[Y]Z,可以表示为

A->XBZ

B->Y|ε

对于A->X{YZ},可以表示为

A->XB

B->CC

C->YZ|ε

4.2.5 stmt -> if expr then stmt [ else stmt ] | begin stmt{; stmt} end

4.2.6 基本的正则表达式还剩*,表示为A->Z*,可以改写为:

A->BB

B->Z|ε

4.2.7 1)

建立一个集合表示所有非“无用符号”集合T,开始置为所有终结符号

集合A表示所有推导式,默认不打标记

for(所有没有打标记的A中推导式)

{

 if( 如果右边只有T中符号集合)

  {

    将这个表达式打标记

    表达式左边符号加入集合T

  }

 if(一个循环结束没有新的表达式被打标记)

   循环退出

}

将开始符号加入T

2)非“无用符号”集合T会依次加入{0, B, S},所以A是无用符号

4.2.8 读懂这道题费了好大劲,不知到是因为最后一道题了还是比较晚了(2012-12-20 22:49,离世界末日不远了)

1)前2行不变,后面改为:

option -> A1|A2|...|An

A1 -> a1|b1

...

An -> an|bn

2)又花了一段时间读懂了这个题目,生成的串固定长度n,不考虑<n的串

n!*n的产生式是这样的,1)中的第一行改为:

option->Aa1|Aa2...|Aan

其中a1有n种选择(A1-An),a2有剩下的n-1种.....an为剩下的一种。

这些产生式一共包括n!个长度为n的产生式,即O(n!*n)


如果有一个O(n*2^n)的产生式序列,那么可能是n个长度为2^n的产生式,具体构造方法没想出来


4.3 设计文法

4.3.1 1)没有左公因子

2)有左递归,不能自顶向下语法分析

3)

rexpr -> rterm | E1

E1 -> +rterm | ε

rterm -> rfactor | F1

F1 -> rfactor | ε

rfactor -> rprimary | P1

P1 -> * | ε

rprimary -> a|b

4)目前没有左递归、左公因子,可以自顶向下语法分析

4.3.3 考虑 if E1 then if E2 else if E3 then MATCHED1 else MATCHED2

if E3 then MATCHED1 else MATCHED2不能确定跟前面两个if中的哪个匹配

4.4 自顶向下的语法分析

4.4.1 1)S -> 0S1 | 01
S -> 0T
T -> S1|1
FIRST(S) = {0}
FIRST(T) = {0,1}
FOLLOW(S) = {1,$}
FOLLOW(T) = {1,$}
 01$
SS->0T  
TT->S1T->1 


4.4.2 S -> SS+ | SS* | a
提取左公因子
S -> SST|a
T -> +|*
消除左递归

S -> aX

X -> STX |ε 

T -> +|*

FIRST(S) = {a}
FIRST(X) = {a}
FOLLOW(S) = {+,*,$}
FOLLOW(X) = {+,*,$}
提取左公因子
 a+*$
SaX   
XSTXεεε
T +* 

4.4.5 感觉可以识别aaaaaa,为啥不行呢?

aaaaaa aSa

  aaaaa  Sa

  aaaaa  aSaa 如果可以向前看4个输入符号,则可以识别aaaaa,否则,会继续采用S -> aSa

    aaaa  Saa    aSaaa

      aaa  aSaaaa

        aa  Saaaa    aSaaaaa

          a  Saaaaa  需要回溯,回溯到一定程度就可以识别

4.4.6 1)如果产生式左边只能生成ε,则将其在文法中所有出现的地方删除即可

否则,就是类似T -> X | ε,这种情况下,将T在其他推导中出现的地方用这两个替换

这样有可能粗先左递归或左公因子。

2)

S -> aSbS  => S -> aaSbSbaSbS | abSaSbbSaS | aSbS

S -> bSaS  => S -> baSbSaaSbS | bbSaSabSaS | bSaS

S -> ε

综合得到S -> aaSbSbaSbS | abSaSbbSaS | aSbS | baSbSaaSbS | bbSaSabSaS | bSaS

4.4.7 1)如果有A=》B,则将B进行替换,替换为B可推导得到的表达式

2)E -> E+T | T   T -> T*F | F   F->(E) | id 进行替换如下:

T -> T*F | (E) | id

E -> E+T | T*F | (E) | id

3)一步推导得到的环可以直接删除,如果是多步,不失一般性,假设为2步,由于文法中不存在ε,所以必然存在这样的推导:

A->B B->A,我们可以爱用上面方法将B去掉,并去掉一步环。

4.4.8 根据4.4.7不难得到,但是得到的结果有左递归

4.4.9 如果n*n的表能够构造成功直到j-i=n-1,说明串在这个语言中

4.5 自底向上的语法分析

4.5.1  S -> 0S1 | 01

1)000111 最右推导:S -> 0S1 -> 00S11 -> 000111

最中间的01

2)00S11   最右推导: S -> 0S1 ->00S11

中间的0S1

4.5.2  S -> SS+ |SS* | a

1)SS+A*+      SS+

2)SS+a*a+   SS+

3)aaa*a++    a

4.5.3

1)

$            000111$

$0001            11$

$00S              11$

$00S1              1$

$0S                   1$

$0S1                   $

$S                       $

2)

aaa*a++

Saa*a++

SSa*a++

SSS*a++

SSa++

SSS++

SS+

S

4.6 LR语法分析技术介绍:简单LR技术

4.6.1

1) 0,0S,00S....

2)

4.6.2 S -> SS+| SS* | a

采用图4-33中算法处理 

(1) S`->S

(2) S -> SS+

(3) S -> SS*

(4) S -> a

01 0S2 0a3 1S4 3+5 3*

S`->.S

S->.SS+

S->.SS*

S->.a

S`->S.

S->S.S+

S->S.S*

S->.a

1$ = accept

S->a.

S->SS.+

S->SS.*

S->SS+.S->SS*.

采用算法4.46

FOLLOW(S) = {+, *,a $}    FOLLOW(+)={+, *, a}    FOLLOW(*)={+, *, a}   FOLLOW(a)={+, *, a}

 a+*$S
0s2   1
1s2  acc3
2r4r4r4  
3 s4s5  
4r2r2r2  
5r3r3r3  

没有发现哪个ACTION既有归约又有移入操作,应该是SLR文法

4.6.3

符号输入动作
0 aa*a+$移入
02aa*a+$归约S->a
01Sa*a+$移入
012Sa*a+$归约S->a
013SS*a+$移入
0135SS*a+$归约S->SS*
01Sa+$移入
012Sa+$归约S->a
013SS+$移入
0134SS+$归约S->SS+
01S$接收
    


对于下面两题

LL(1)文法特点:需要满足4.4.3中的三个条件

SLR(1)文法特点:在输入某个表示串以后,有移入/规约冲突或规约/规约冲突

LL文法是LR文法的一个真子集,而不是SLR的

4.6.5

S`->S

S -> AaAb | BbBa

A ->ε

B ->ε

构造LR(0)自动机

0

S`->.S

S->.AaAb

S->.BbBa

A->.ε

B->.ε

1 0S

S`->S

没法继续构造下去

4.6.6

FIRST(SA)与FIRST(A)都包含{a}。所以不是LL(1)的

4.6.7 1)

S -> Aibi          n个

Ai -> ajAi          n^2-n个

Ai -> aj             n^2-n个 

2)考虑其中某个i和j

index项集

贡献数量

 
0

S`->.S 

S->.Aibi 

Ai->.ajAi 

Ai->.aj

1S`->.S
S->.A1b1 ...
S->.Anbn
A1->.a2A1...
A1->.anA1...
...
An->.a1An...
An->.an-1An
1
0->S
S`->S.1S`->S.
2
0->Ai
S->Ai.binn个S->Ai.bi
3
0->aj

Ai->aj.Ai

Ai->aj.

Ai->.ajAi

Ai->.aj

n对于a1
A2->a1.A2...
An->a1.An
A2->.ajA2
A2->.aj
...an
4
2->bi
S->Aibi.nn个S->Aibi.
5
3->Ai, 6Ai
Ai->ajAi.n*(n-1)对于a1
输入A2...An
6
3->aj,6->aj
Ai->aj.Ai
Ai->.ajAi
Ai->.aj
n*n对于a1
输入a1,a3...an
A2->aj.A2
A2->aj.
    

这样算下来是2n*n+2n+2

结果应该是2^n+n^2+n,看来我算错了,求高手指点

如果势2^n那说明状态数量太多了,手工构造太困难了,做4.6练习过程感觉中间太容易出错了(手动计算的情况)

4.6.8

4.6.9

index项集 
0S'->.S
S->.AS
S->.b
A->.SA
A->.a
 
1
0->S
S'->S.
A->S.A
A->.SA
A->.a
->$
accept
2
0->A
S->A.S
S->.AS
S->.b
A->.SA
A->.a
 
3
0->a
1->a
2->a
5->a
7->a
A->a. 
4
0->b
S->b. 
5
1->S
5->S
7->S
A->S.A
A->.SA
A->.a
 
6
1->A
5->A
7->A
A->SA. 
7
2->S
S->AS.
A->S.A
A->.SA
A->.a
 
8
2->A
8->A
S->A.S
S->.AS
S->.b
 
9
8->S
A->AS. 

FIRST(A)、FIRST(S)、FOLLOW(A)、FOLLOW(S)都是{a,b},7可能有冲突,因为当前状态输入a的情况下,不能确定按S->AS.规约还是移入a。

4.7 更强大的LR语法分析器

学习编译原理真是个痛并快乐的过程,网上说龙书翻译的还行,个人感觉也是如此,只是学习的时候如果希望从头读一遍就理解是不可能的,通常要看3-4次才能理解,而且每次阅读的时候都会有新的收获。

学习4.7.5的时候怎么都想不明白例4.64怎么得出的自发向前看符号,再回头看一遍4.7,并且手动构造下LR项集族,马上明白了。

学习这一节最重要的是理解4.7.2开头对于算法4.53的解释,明白了以后这一节基本也就没问题了

为理解4.7.5中算法执行过程,构造例4.6的规范LR项集族

index项集GOTO
0S`->.S    ,$
S->.L=R ,$
S->.R     ,$
L->.*R    ,=
L->.id     ,=
R->L      ,$
 
1  
2  
3  
4  
5  
6  
7  
8  
8  

4.7.1 FIRST(S) = {a}

S'->S

S->SS+

S->SS*

S->a

构造LR项集族如下


index项集GOTO
0S`->.S     , $
S->.SS+  ,a
S->.SS*   ,a
S->.a        ,a
 
1
0->S
S'->S.       ,$
S->S.S+   ,a/+
S->S.S*   ,a/*
S->.a        ,a/+/*
 
2
0->a
S->a.        ,a 
3
1->S
S->SS.+    ,a/+
S->SS.*     ,a/*
S->.A          ,a/+/*
 
4
1->a
S->a.          ,a/+/* 
5
3->+
S->SS+.     ,a/+ 
6
3->*
S->SS*.      ,a/* 
   

LALR项集族只需要将上面2/4合并即可

4.7.3 算法4.6.3前后看了至少5遍,稍微有了些理解

  INIT12
0S'->.S$$$
1S'->S.
S->S.S+
S->S.S*
aaa
2S->a.aaa
3S->SS.+
S->SS.*
 a/+/*a/+/*
4S->SS+.  a/+
5S->SS*.  a/*

4.7.4/4.7.5 类似例4.58



index项集GOTO
0S`->.S    ,$
S->.L=R ,$
S->.R     ,$
L->.*R    ,=
L->.id     ,=
R->L      ,$
 
1  
2  
3  
4  
5  
6  
7  
8  
8  
编译原理龙书答案 完整性高 第二 2.2 Exercises for Section 2.2 2.2.1 Consider the context-free grammar: S -> S S + | S S * | a Show how the string aa+a* can be generated by this grammar. Construct a parse tree for this string. What language does this grammar generate? Justify your answer. answer S -> S S * -> S S + S * -> a S + S * -> a a + S * -> a a + a * L = {Postfix expression consisting of digits, plus and multiple signs} 2.2.2 What language is generated by the following grammars? In each case justify your answer. S -> 0 S 1 | 0 1 S -> + S S | - S S | a S -> S ( S ) S | ε S -> a S b S | b S a S | ε ⧗ S -> a | S + S | S S | S * | ( S ) answer L = {0n1n | n>=1} L = {Prefix expression consisting of plus and minus signs} L = {Matched brackets of arbitrary arrangement and nesting, includes ε} L = {String has the same amount of a and b, includes ε} ? 2.2.3 Which of the grammars in Exercise 2.2.2 are ambiguous answer No No Yes Yes Yes 2.2.4 Construct unambiguous context-free grammars for each of the following languages. In each case show that your grammar is correct. Arithmetic expressions in postfix notation. Left-associative lists of identifiers separated by commas. Right-associative lists of identifiers separated by commas. Arithmetic expressions of integers and identifiers with the four binary operators +, - , *, /. answer 1. E -> E E op | num 2. list -> list , id | id 3. list -> id , list | id 4. expr -> expr + term | expr - term | term term -> term * factor | term / factor | factor factor -> id | num | (expr) 5. expr -> expr + term | expr - term | term term -> term * unary | term / unary | unary unary -> + factor | - factor factor - > id | num | (expr) 2.2.5 Show that all binary strings generated by the following grammar have values divisible by 3. Hint. Use induction on the number of nodes in a parse tree. num -> 11 | 1001 | num 0 | num num Does the grammar generate all binary strings with values divisible by 3? answer prove any string derived from the grammar can be considered to be a sequence consisting of 11, 1001 and 0, and not prefixed with 0. the sum of this string is: sum = Σn (21 + 20) * 2 n + Σm (23 + 20) * 2m = Σn 3 * 2 n + Σm 9 * 2m It is obviously can divisible by 3. No. Consider string "10101", it is divisible by 3, but cannot derived from the grammar. Question: any general prove? 2.2.6 Construct a context-free grammar for roman numerals. Note: we just consider a subset of roman numerals which is less than 4k. answer wikipedia: Roman_numerals via wikipedia, we can categorize the single noman numerals into 4 groups: I, II, III | I V | V, V I, V II, V III | I X then get the production: digit -> smallDigit | I V | V smallDigit | I X smallDigit -> I | II | III | ε and we can find a simple way to map roman to arabic numerals. For example: XII => X, II => 10 + 2 => 12 CXCIX => C, XC, IX => 100 + 90 + 9 => 199 MDCCCLXXX => M, DCCC, LXXX => 1000 + 800 + 80 => 1880 via the upper two rules, we can derive the production: romanNum -> thousand hundred ten digit thousand -> M | MM | MMM | ε hundred -> smallHundred | C D | D smallHundred | C M smallHundred -> C | CC | CCC | ε ten -> smallTen | X L | L smallTen | X C smallTen -> X | XX | XXX | ε digit -> smallDigit | I V | V smallDigit | I X smallDigit -> I | II | III | ε 2.3 Exercises for Section 2.3 2.3.1 Construct a syntax-directed translation scheme that trans­ lates arithmetic expressions from infix notation into prefix notation in which an operator appears before its operands; e.g. , -xy is the prefix notation for x - y . Give annotated parse trees for the inputs 9-5+2 and 9-5*2.。 answer productions: expr -> expr + term | expr - term | term term -> term * factor | term / factor | factor factor -> digit | (expr) translation schemes: expr -> {print("+")} expr + term | {print("-")} expr - term | term term -> {print("*")} term * factor | {print("/")} term / factor | factor factor -> digit {print(digit)} | (expr) 2.3.2 Construct a syntax-directed translation scheme that trans­ lates arithmetic expressions from postfix notation into infix notation. Give annotated parse trees for the inputs 95-2* and 952*-. answer productions: expr -> expr expr + | expr expr - | expr expr * | expr expr / | digit translation schemes: expr -> expr {print("+")} expr + | expr {print("-")} expr - | {print("(")} expr {print(")*(")} expr {print(")")} * | {print("(")} expr {print(")/(")} expr {print(")")} / | digit {print(digit)} Another reference answer E -> {print("(")} E {print(op)} E {print(")"}} op | digit {print(digit)} 2.3.3 Construct a syntax-directed translation scheme that trans­ lates integers into roman numerals answer assistant function: repeat(sign, times) // repeat('a',2) = 'aa' translation schemes: num -> thousand hundred ten digit { num.roman = thousand.roman || hundred.roman || ten.roman || digit.roman; print(num.roman)} thousand -> low {thousand.roman = repeat('M', low.v)} hundred -> low {hundred.roman = repeat('C', low.v)} | 4 {hundred.roman = 'CD'} | high {hundred.roman = 'D' || repeat('X', high.v - 5)} | 9 {hundred.roman = 'CM'} ten -> low {ten.roman = repeat('X', low.v)} | 4 {ten.roman = 'XL'} | high {ten.roman = 'L' || repeat('X', high.v - 5)} | 9 {ten.roman = 'XC'} digit -> low {digit.roman = repeat('I', low.v)} | 4 {digit.roman = 'IV'} | high {digit.roman = 'V' || repeat('I', high.v - 5)} | 9 {digit.roman = 'IX'} low -> 0 {low.v = 0} | 1 {low.v = 1} | 2 {low.v = 2} | 3 {low.v = 3} high -> 5 {high.v = 5} | 6 {high.v = 6} | 7 {high.v = 7} | 8 {high.v = 8} 2.3.4 Construct a syntax-directed translation scheme that trans­ lates roman numerals into integers. answer productions: romanNum -> thousand hundred ten digit thousand -> M | MM | MMM | ε hundred -> smallHundred | C D | D smallHundred | C M smallHundred -> C | CC | CCC | ε ten -> smallTen | X L | L smallTen | X C smallTen -> X | XX | XXX | ε digit -> smallDigit | I V | V smallDigit | I X smallDigit -> I | II | III | ε translation schemes: romanNum -> thousand hundred ten digit {romanNum.v = thousand.v || hundred.v || ten.v || digit.v; print(romanNun.v)} thousand -> M {thousand.v = 1} | MM {thousand.v = 2} | MMM {thousand.v = 3} | ε {thousand.v = 0} hundred -> smallHundred {hundred.v = smallHundred.v} | C D {hundred.v = smallHundred.v} | D smallHundred {hundred.v = 5 + smallHundred.v} | C M {hundred.v = 9} smallHundred -> C {smallHundred.v = 1} | CC {smallHundred.v = 2} | CCC {smallHundred.v = 3} | ε {hundred.v = 0} ten -> smallTen {ten.v = smallTen.v} | X L {ten.v = 4} | L smallTen {ten.v = 5 + smallTen.v} | X C {ten.v = 9} smallTen -> X {smallTen.v = 1} | XX {smallTen.v = 2} | XXX {smallTen.v = 3} | ε {smallTen.v = 0} digit -> smallDigit {digit.v = smallDigit.v} | I V {digit.v = 4} | V smallDigit {digit.v = 5 + smallDigit.v} | I X {digit.v = 9} smallDigit -> I {smallDigit.v = 1} | II {smallDigit.v = 2} | III {smallDigit.v = 3} | ε {smallDigit.v = 0} 2.3.5 Construct a syntax-directed translation scheme that trans­ lates postfix arithmetic expressions into equivalent prefix arithmetic expressions. answer production: expr -> expr expr op | digit translation scheme: expr -> {print(op)} expr expr op | digit {print(digit)} Exercises for Section 2.4 2.4.1 Construct recursive-descent parsers, starting with the follow­ ing grammars: S -> + S S | - S S | a S -> S ( S ) S | ε S -> 0 S 1 | 0 1 Answer 1) S -> + S S | - S S | a void S(){ switch(lookahead){ case "+": match("+"); S(); S(); break; case "-": match("-"); S(); S(); break; case "a": match("a"); break; default: throw new SyntaxException(); } } void match(Terminal t){ if(lookahead = t){ lookahead = nextTerminal(); }else{ throw new SyntaxException() } } 2) S -> S ( S ) S | ε void S(){ if(lookahead == "("){ S(); match("("); S(); match(")"); S(); } } 3) S -> 0 S 1 | 0 1 void S(){ switch(lookahead){ case "0": match("0"); S(); match("1"); break; case "1": // match(epsilon); break; default: throw new SyntaxException(); } } Exercises for Section 2.6 2.6.1 Extend the lexical analyzer in Section 2.6.5 to remove com­ ments, defined as follows: A comment begins with // and includes all characters until the end of that line. A comment begins with /* and includes all characters through the next occurrence of the character sequence */. 2.6.2 Extend the lexical analyzer in Section 2.6.5 to recognize the relational operators <, =, >. 2.6.3 Extend the lexical analyzer in Section 2.6.5 to recognize float­ ing point numbers such as 2., 3.14, and . 5. Answer Source code: commit 8dd1a9a Code snippet(src/lexer/Lexer.java): public Token scan() throws IOException, SyntaxException{ for(;;peek = (char)stream.read()){ if(peek == ' ' || peek == '\t'){ continue; }else if(peek == '\n'){ line = line + 1; }else{ break; } } // handle comment if(peek == '/'){ peek = (char) stream.read(); if(peek == '/'){ // single line comment for(;;peek = (char)stream.read()){ if(peek == '\n'){ break; } } }else if(peek == '*'){ // block comment char prevPeek = ' '; for(;;prevPeek = peek, peek = (char)stream.read()){ if(prevPeek == '*' && peek == '/'){ break; } } }else{ throw new SyntaxException(); } } // handle relation sign if("".indexOf(peek) > -1){ StringBuffer b = new StringBuffer(); b.append(peek); peek = (char)stream.read(); if(peek == '='){ b.append(peek); } return new Rel(b.toString()); } // handle number, no type sensitive if(Character.isDigit(peek) || peek == '.'){ Boolean isDotExist = false; StringBuffer b = new StringBuffer(); do{ if(peek == '.'){ isDotExist = true; } b.append(peek); peek = (char)stream.read(); }while(isDotExist == true ? Character.isDigit(peek) : Character.isDigit(peek) || peek == '.'); return new Num(new Float(b.toString())); } // handle word if(Character.isLetter(peek)){ StringBuffer b = new StringBuffer(); do{ b.append(peek); peek = (char)stream.read(); }while(Character.isLetterOrDigit(peek)); String s = b.toString(); Word w = words.get(s); if(w == null){ w = new Word(Tag.ID, s); words.put(s, w); } return w; } Token t = new Token(peek); peek = ' '; return t; } Exercises for Section 2.8 2.8.1 For-statements in C and Java have the form: for ( exprl ; expr2 ; expr3 ) stmt The first expression is executed before the loop; it is typically used for initializ­ ing the loop index. The second expression is a test made before each iteration of the loop; the loop is exited if the expression becomes O. The loop itself can be thought of as the statement {stmt expr3 ; }. The third expression is executed at the end of each iteration; it is typically used to increment the loop index. The meaning of the for-statement is similar to expr1 ; while ( expr2 ) {stmt expr3 ; } Define a class For for for-statements, similar to class If in Fig. 2.43. Answer class For extends Stmt{ Expr E1; Expr E2; Expr E3; Stmt S; public For(Expr expr1, Expr expr2, Expr expr3, Stmt stmt){ E1 = expr1; E2 = expr2; E3 = expr3; S = stmt; } public void gen(){ E1.gen(); Label start = new Lable(); Lalel end = new Lable(); emit("ifFalse " + E2.rvalue().toString() + " goto " + end); S.gen(); E3.gen(); emit("goto " + start); emit(end + ":") } } 2.8.2 The programming language C does not have a boolean type. Show how a C compiler might translate an if-statement into three-address code. Answer Replace emit("isFalse " + E.rvalue().toString() + " goto " + after); with emit("ifNotEqual " + E.rvalue().toString() + " 0 goto " + after); or emit("isNotEqualZero " + E.rvalue().toString() + " goto " + after);
目 录 译者序 前言 第1 概论 1 1.1 为什么要用编译器 2 1.2 与编译器相关的程序 3 1.3 翻译步骤 5 1.4 编译器中的主要数据结构 8 1.5 编译器结构中的其他问题 10 1.6 自举与移植 12 1.7 TINY样本语言与编译器 14 1.7.1 TINY语言 15 1.7.2 TINY编译器 15 1.7.3 TM机 17 1.8 C-Minus:编译器项目的一种语言 18 练习 19 注意与参考 20 第2 词法分析 21 2.1 扫描处理 21 2.2 正则表达式 23 2.2.1 正则表达式的定义 23 2.2.2 正则表达式的扩展 27 2.2.3 程序设计语言记号的正则表达式 29 2.3 有穷自动机 32 2.3.1 确定性有穷自动机的定义 32 2.3.2 先行、回溯和非确定性自动机 36 2.3.3 用代码实现有穷自动机 41 2.4 从正则表达式到DFA 45 2.4.1 从正则表达式到NFA 45 2.4.2 从NFA到DFA 48 2.4.3 利用子集构造模拟NFA 50 2.4.4 将DFA中的状态数最小化 51 2.5 TINY扫描程序的实现 52 2.5.1 为样本语言TINY实现一个扫描 程序 53 2.5.2 保留字与标识符 56 2.5.3 为标识符分配空间 57 2.6 利用Lex 自动生成扫描程序 57 2.6.1 正则表达式的Lex 约定 58 2.6.2 Lex输入文件的格式 59 2.6.3 使用Lex的TINY扫描程序 64 练习 65 编程练习 67 注意与参考 67 第3 上下文无关文法及分析 69 3.1 分析过程 69 3.2 上下文无关文法 70 3.2.1 与正则表达式比较 70 3.2.2 上下文无关文法规则的说明 71 3.2.3 推导及由文法定义的语言 72 3.3 分析树与抽象语法树 77 3.3.1 分析树 77 3.3.2 抽象语法树 79 3.4 二义性 83 3.4.1 二义性文法 83 3.4.2 优先权和结合性 85 3.4.3 悬挂else问题 87 3.4.4 无关紧要的二义性 89 3.5 扩展的表示法:EBNF和语法图 89 3.5.1 EBNF表示法 89 3.5.2 语法图 91 3.6 上下文无关语言的形式特性 93 3.6.1 上下文无关语言的形式定义 93 3.6.2 文法规则和等式 94 3.6.3 乔姆斯基层次和作为上下文无关 规则的语法局限 95 3.7 TINY语言的语法 97 3.7.1 TINY的上下文无关文法 97 3.7.2 TINY编译器的语法树结构 98 练习 101 注意与参考 1044 自顶向下的分析 105 4.1 使用递归下降分析算法进行自顶向下 的分析 105 4.1.1 递归下降分析的基本方法 105 4.1.2 重复和选择:使用EBNF 107 4.1.3 其他决定问题 112 4.2 LL(1)分析 113 4.2.1 LL(1)分析的基本方法 113 4.2.2 LL(1)分析与算法 114 4.2.3 消除左递归和提取左因子 117 4.2.4 在LL(1)分析中构造语法树 124 4.3 First集合和Follow集合 125 4.3.1 First 集合 125 4.3.2 Follow 集合 130 4.3.3 构造LL(1)分析表 134 4.3.4 再向前:LL(k)分析程序 135 4.4 TINY语言的递归下降分析程序 136 4.5 自顶向下分析程序中的错误校正 137 4.5.1 在递归下降分析程序中的错误 校正 138 4.5.2 在LL(1)分析程序中的错误校正 140 4.5.3 在TINY分析程序中的错误校正 141 练习 143 编程练习 146 注意与参考 148 第5 自底向上的分析 150 5.1 自底向上分析概览 151 5.2 LR(0)项的有穷自动机与LR(0)分析 153 5.2.1 LR(0)项 153 5.2.2 项目的有穷自动机 154 5.2.3 LR(0)分析算法 157 5.3 SLR(1)分析 160 5.3.1 SLR(1)分析算法 160 5.3.2 用于分析冲突的消除二义性 规则 163 5.3.3 SLR(1)分析能力的局限性 164 5.3.4 SLR(k)文法 165 5.4 一般的LR(1)和LALR(1)分析 166 5.4.1 LR(1)项的有穷自动机 166
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值