Giới thiệu : Kỹ thuật phân tích từ vựng, ngữ nghĩa, cú pháp thuộc về mảng đầu tiên của trình biên dịch Mọi sự phân tích cú pháp đều dựa vào CFG hoặc BNF Bộ phân tích cú pháp được c
Trang 1Chương 4 :TỪ VỰNG VÀ PHÂN TÍCH CÚ PHÁP
Nội dung :
• Giới thiệu
• Phân tích từ vựng
• Phân tích cú pháp
• Phân tích cú pháp từ trên xuống
• Phân tích cú pháp từ dưới lên
I. Giới thiệu :
Kỹ thuật phân tích từ vựng, ngữ nghĩa, cú pháp thuộc về mảng đầu tiên của trình biên dịch
Mọi sự phân tích cú pháp đều dựa vào CFG hoặc BNF
Bộ phân tích cú pháp được chia làm 2 phần :
o Phần mức thấp gọi là bộ phân tích từ vựng(Scaner) là một automatch hữu hạn dựa vào văn phạm chính qui
o Phần mức cao cao gọi là bộ phân tích cú pháp là một automatch đẩy xuống dựa vào ngôn ngữ phi ngữ cảnh hoặc BNF
Lý do chia bộ phân tích cú pháp thành 2 phần :
o Đơn giản – phân tích từ vựng đơn giản hơn =>việc xây dựng bộ phân tích từ vựng đon giản hơn
o Hiệu quả :việc phân chia thành 2 khối giúp tối ưu hóa
o Tính khả chuyển :bộ phân tích từ vựng không khả chuyễn nhưng bô phân tích cú pháp có thể khả chuyễn(đẩu ra của bộ phân tích từ vựng là các token có thể đưa vào các máy phân tích cú pháp khác nhau )
II. Phân tích từ vựng
Phân tích từ vựng là so sách với các mẫu(thường được mô tả bởi biểu thức chính quy)
o Ví dụ
Tên biến có dạng (a| |Z)(0| |9|a |z)*
Số nguyện có dạng :0+(1| |9)(0| |9)*
Xác định chuỗi con có thuộc một nhóm(Lexemes)
o Ví dụ a1 :thuộc tên biến,98 thuộc số nguyên
Với các chuổi con thuộc mẩu giống nhau sẽ được vào một token(phân loại)
Trang 2Chương trình nguồn Scanner(PTTV) Parser(PTCP)
Bộ phân tích từ vựng giống như 1 hàm sẽ được gọi bởi bộ phân tích cú pháp khi cần token kế tiếp
Quá trình phân tích từ vựng:
o Bỏ qua các ghi chú, các tab, xuống dòng, và khoảng trống
o Lưu lại các thông tin về tập tin,dòng, cột để thông báo lỗi
o Phát hiện và báo cáo các lỗi từ vựng trong token cho người sử dụng
III. Ba cách xây dựng bộ phân tích từ vựng(Scanner)
o Dùng phần mềm pháp sinh tự động(đầu vào là một văn phạm chính quy)
o Xây dựng sơ đồ trạng thái DFA
o Xây dựng bảng thực thi dòng, cột
Luật tìm lexeme dài nhất
Trang 3o Scanner chỉ trả về token khi đọc đến ký tự kết thúc token( thường là khoàng trắng)
o Trong một số trường hợp Scanner phải đọc nhiều hơn một ký tự để nhận biết đã kết thúc token
o Ví dụ :trong pascal khi ta đọc được số 3 và sau đó là dấu
Ta tiếp tục sử lý với huy vọng là số 3.1
Hay dừng với 3 5
Trong một số trường hợp ta phải đọc cả chuổi nhập mới xác định được ý nghĩa của chuổi
o Ví dụ trong Fortran
DO 5 I = 1,25 //VÒNG lăp
DO 5 I = 1.25 //biến=1.25
Ta phải lưu lại tất cả các thông tin nếu không sẽ gặp các khó khăn về sau
IV. Thiết kế sơ đồ truyền
Xây dựng bộ phân tích từ vựng để nhận dạng tên trong chương trình như tên
biến,chương trình con, từ dành riêng, chuổi số nguyên
Một đường truyền đơn giản là từ một trạng thái ở bước I sẽ có đường truyền đến tất cả các trạng thái ở bước i+1 =>sơ đồ rất lớn
Phải tích hợp các trạng thái để đơn gian sơ đồ truyền
o Các ký tự hoa, thường :lớp ký tự
o Các số nguyên :lớp số
o Từ dành riêng : nhận dạng chung với lớp ký tự
Từ trạng thái đầu chỉ có hai đường truyền đến lớp ký tự và lớp số
Các hàm con được dùng để xử lý token
o getChar :lấy ký tự kế tiếp trong dữ liệu nhập đưa vào biến toàn cục nextChar
o addChar :lấy ký tự từ biến nextChar đưa vào biến toàn cục là lexeme đề tạo
thành token
o lookup :xác định từ trong lexeme là từ dành riêng
Trang 5V. Phân tích cú pháp
Trang 6 Nhiệm vụ
oTìm tất cả các lỗi cú pháp , đưa ra các thông báo lỗi
oXây dựng cây phân tích cú pháp(lưu vết ) cho chương trình
Có hai kỹ thuật phân tích
oTop-down :xây dựng cây phân tích cú pháp bắt đầu từ gốc
oBottom up :xây dựng cây phân tích cú pháp bắt đầu từ lá
Bô phân tích cú pháp thường nhìn trước một token trong chuổi các token trong đầu ra của scanner
Các quy ước
oKý hiệu kết thúc :dùng các chữ thường đầu tiên trong bảng aphab (a,b,c…),
oKý hiệu không kết thúc :dùng chữ hoa đầu tiên(A,B,C…)
oChuổi ký tự kết thúc :các ký tự thường cuối cùng trong bảng aphab(x,y,z)
oChuỗi chứa ký tự không kết thúc (α,β )
1. Phân tích Top-down
o Cho dạng câu xAα, chọn luật sinh có vế trái là A(là ký tự không kết thúc trái nhất) để tạo dẫn xuất kế tiếp
o Thuật toán được dùng
o–Recursive descent :Đệ quy xuống
o–LL parsers :bảng điều khiển thực thi
2. Phân tích Buttom-up
o Cho trước một dạng câu phải α,xác định chuổi con nào của α là vế phải của một luật sinh nào đó->thay thế chuỗi con bằng vế phải của luật sinh
o Ví dụ αβ£ và luật sinh A->β thì chuổi sẽ trở thành αA£
o Thuật toán được dùng là LR(đọc từ trái qua phải)
Ví dụ : cho bộ luật sinh sau:
id_list id id_list_tail id_list_tail , id id_list_tail id_list_tail ;
Trang 7o Ví dụ : cho bộ luật sinh
(1)E E+T
(2)E T
(3)TT*F
(4)T F
(5)F(E)
(6)F ID
(7)F N
o Cho biểu thức 1+(2+3)*4+5
o Dùng dẫn xuất phải
(1) E E+T->(4) E E+F->(7) EE+5->(1) EE+T+5->(3) EE+T*F+5->(7)
EE+T*4+5->(4) EE+F*4+5->(5) EE+(E)*4+5->(1) EE+(E+T)*4+5->(4) EE+ (E+F)*4+5->(7) EE+(E+3)*4+5->(2) EE+(T+3)*4+5->(4) EE+(F+3)*4+5 ->(7) EE+(2+3)*4+5 ->(2) ET+(2+3)*4+5 ->(4) EF+(2+3)*4+5 ->(7)
E1+(2+3)*4+5
o Vẽ lại luật sinh lùi lại ta được phân tích cú pháp từ dưới lên
3. Phân tích cú pháp đệ quy xuống :
Trang 8o Là một chương trình con duyệt qua tất cả các ký tự không kết thúc trong ngôn ngữ, thay thế ký tự không kết thúc= ký tự kết thúc bắt đầu từ ký hiệu bắt đầu
o BNF là lý tưởng cho là cơ sở cho một phân tích cú pháp đệ quy gốc, bởi vì EBNF giảm thiểu số lượng ký tự không kết thúc
o Cho một ngôn ngữ với các luật sinh sau :
<expr> <term> {(+ | –) <term>}
<term> <factor> {(* | /) <factor>}
<factor> id | ( <expr> )
Có ba chương trình con expr,term,factor.chương trình con expr sẽ thực hiện đầu tiên
o Giả sử ta đang phân tích từ vựng có tên là lex, token kế tiếp có tên là NextToken, các xử lý
Duyệt tất cả các ký tự kết thúc trong vế phải của luật sinh, nếu tìm thấy NextToken thì tiếp tục, nếu không báo lỗi
Duyệt tất cả các ký tự không kết thúc trong vế phải, gọi chương trình con liên quan đến ký hiệu không kết thúc này
o Ví dụ hàm expr() phân tích chuỗi trong ngôn ngữ có luật sinh
<expr> <term> {(+ | -) <term>}
void expr() {
/* Parse the first term */
term();
/* As long as the next token is + or -,
call lex() to get the next token, and
parse the next term */
while (nextToken == PLUS_CODE ||
nextToken == MINUS_CODE) {
lex();
term();
}
}
o Một ký tự không kết thúc có nhiều hơn một một vế phải đòi hỏi một quá trình phân tích để xác định vế phải được chọn
-việc lựa chọn vế phải dựa trên cơ sở là so sánh ký tự tiếp theo của Token kế tiếp
Ví dụ
A->aBC|CD
C->bEF
Nếu NextToken=a sẽ chọn luật sinh A->aBC
Nếu NextToken=b sẽ chọn luật sinh A->CD và C->bEF
NextToken=d sẽ báo lỗi
Ví dụ :
Function factor()
/* Parses strings in the language generated
by the rule:
<factor> -> id | (<expr>) */
void factor() {
Trang 9/* Determine which RHS */
if (nextToken == ID_CODE)
/* For the RHS id, just call lex */
lex();
/* If the RHS is (<expr>) – call lex() to pass
over the left parenthesis, call expr(), and
check for the right parenthesis */
else
if (nextToken == LEFT_PAREN_CODE) {
lex();
expr();
if (nextToken == RIGHT_PAREN_CODE)
lex();
else
error();
}
else error(); /* Neither RHS matches */
4. Văn phạm (LL Grammand):
o Nếu một ngữ pháp là đệ quy trái , nó không thể là cơ sở cho một phân tích cú pháp từ trên xuống
Có thể sửa đổi để loại bỏ đệ quy trái
Ví dụ cho luật sinh A->A+B
Một chương trình con cho nonterminal A ngay lập tức cuộc gọi riêng của mình để phân tích các biểu tượng đầu tiên trong RHS của nó(đệ quy vô hạn)
o Các cặp tách rời
FIRST(α)={a|α=>*aβ}: FIRST(α):là ký tự kết thúc đầu tiên trong chuỗi α
Trong một tập luật sinh :
• A->α
• A->β
• Chỉ đúng khi FIRST(α)∩FIRST(β)=θ
Ví dụ
• Ví dụ 1: A aB | aAb
o FIRST(aB)=a
o FIRST(aAb)=a
o FIRST(aB) ∩FIRST(aAb)=a# θ =>luật sinh sai
• Ví dụ 2 :A aB | bAb | c với FIRST(aB)=a,FIRST(bAb)=b,FIRST(c)=c =>luật sinh đúng
5. Thừa tố trái :dùng để giải quyết cặp tách rời
o ví dụ cho luật sinh sau:
<variable> identifier | identifier [<expression>]
có thể được viết thành
<variable> identifier <new>
<new> ε| [<expression>]
Hoặc
Trang 10<variable> identifier [[<expression>]]([]:ký hiệu mở rộng
6. Phân tích từ dưới lên
o Quá trình phân tích từ dưới lên :đảo ngược của quá trình dẫn xuất phải
o Bắt đầu từ câu đi ngược lại về gốc
o Trong mỗi bước sẽ tìm luật sinh có vế phải thuộc câu, thay thế vế phải bằng vế trái
o Ví dụ cho bộ luật sinh
EE + T | T
T T * F | F
F (E) | id
o Cho chuỗi :E + T * id ta phân tích như sau :
F->id =>id thay bằng F ta có E+T*F
T->T*F : thay T*F=T ta co E+T
E->E+T :=>E
Nếu chọn thay thế E+T bằng E :E*id không về đầu được :sai
7. Các Định Nghĩa
o β là một handle của một dạng câu phải ¥ nếu và chỉ nếu :
S =>rm* αAw =>rm*αβw (=¥)(có một luật sinh mà A được thay thế = β trong một bước)
o β là một phrase của một dạng câu phải ¥ nếu và chỉ nếu :
S =>* αAw =>+αβw (=¥)(dẫn xuất qua nhiều bước)
o β là một simple phrase của một dạng câu phải ¥ nếu và chỉ nếu :
S =>* αAw =>αβw (=¥)(dẫn xuất một bước, một trong các simple phrase là handle)
o Ví dụ cho bộ luật sinh
EE + T | T
T T * F | F
F (E) | id
Biểu thức E+T*id được phân tích thành:
Các phrase của biểu thức E+T*id là :E+T*id,T*id,id(E+T không phải là một phrase vì không được dẫn ra từ một ký hiệu không kết thúc)
Chỉ có một simple phrase là id
Handle của một dạng câu phải là simple phrase trái nhất
o Ví dụ 2 :
o Ví dụ cho bộ luật sinh
EE + T | T
T T * F | F
F (E) | id
Biểu thức E+T*id được phân tích thành(dùng dẫn xuất phải):
Trang 11 Bước 1 :Ta có ba simple phase là id1,id2,id3(từ trái qua phải) id1 là handle
và thay thế id1= F Ta có F+id*id
Bước 2 :ta có 3 simple phase là F,id1,id2, thay F bằng T ta có T+id*id
Bước 3:ta có 3 simple phase là T,id1,id2, thay T bằng E, ta có E+id*id
Bước 4 :ta có id2,id3 là simple phase, thay id2= F :E+F.ID, Thay F=T, E+T*id,id3 thay th bằng F,T*F thay T,E+T thay bằng E là gốc
8. Thuật toán rút gọn,chuyển dời(Shift-Reduce Algorithms)
Rút gọn :là thao tác thay đổi handle trên đỉnh của ngăn xếp bằng 1 vế trái phù hơp
• Ví dụ
Shift : là thao tác di chuyển token vào đầu ngăn xếp
9. Ưu điểm LR parsers
Làm việc với hầu hết mọi văn phạm, dùng để mô tả các ngôn ngữ lập trình
Có thể phát hiện lỗi nhanh chóng
Các văn phạm được nhận dạng bởi LR thì bao gồm cả những văn phạm được nhận dạng bởi LL
10. Cấu trúc bộ phân tích LR
Trang 12Cấu hình LR Parser :
Dùng một Stack để lưu trữ ký hiệu trạng thái Si và ký hiệu văn phạm Xi
S0X1S1…XmSm<-đỉnh của Stack
Bảng phân tích (parsing Table) gồm 2 phần :
• Action :là phần có Ký hiệu trạng thái như nhãn của dòng và ký hiệu kết thúc như nhãn của cột
•