. 98 TỔNG QUAN VỀ BIÊN DỊCH
seq seq x+ Ínsir.d
2.4 PHÂN TÍCH CÚ PHÁP
Phân tích cú pháp là quá trình xác định xem một chuỗi £hé từ (token) cĩ thể được sinh ra từ một văn phạm hay khơng. Khi thảo luận về vấn để này, chúng ta xem như đang xây dựng một cây phân tích cú pháp mặc dù một trình biên dịch cĩ thể khơng xây dựng một cây như thế. Tuy nhiên (hế phản cú pháp (parser) phải cĩ khá năng xây dựng nĩ, nếu khơng thì việc phiên dịch khơng bảo đảm được tính đúng đắn.
Trong phần này chúng ta sẽ giới thiệu một phương pháp phân tích cú pháp được dùng để xây dựng các chương trình dịch dựa cú pháp (syntax-directed translator). Chương trình C hồn chỉnh cài đặt lược để dịch của Hình 2.13 sẽ được trình bày trong phần kế tiếp. Một khả năng khác là dùng một cơng cụ phẩn mềm để tạo trực tiếp chương trình dịch từ một lược để dịch. Phần 4.9 cĩ mơ tả về một cơng cụ như thế; nĩ cĩ thể cài đặt lược để dịch của Hình 2.13 mà khơng cần sửa đổi gì.
Thể phân cú pháp cĩ thể được xây đựng cho một văn phạm bất kỳ. Tuy nhiên các văn phạm được dùng trong thực tế thường cĩ dạng đặc biệt. Đối với một văn phạm phi ngữ cảnh chúng ta đều cĩ một thể phân cú pháp cần thời gian tối đa là O(z3) để phân tích cú pháp cho một chuỗi gồm n thẻ từ. Nhưng chỉ phí thời gian lập phương là quá cao. Cho trước một ngơn ngữ lập trình, nĩi chung chúng ta cĩ thể xây dựng một văn phạm sao cho cĩ thể phân tích nĩ một cách nhanh chĩng. Các thuật tốn tuyến tính là đủ để phân tích mọi ngơn ngữ gặp trong thực hành. Thể phán cú pháp cho các ngơn ngữ lập trình hầu như luơn quét nguyên liệu từ trái sang phải, mỗi lần một thẻ từ.
Phần lớn các phương pháp phân tích đều rơi vào một trong bai lớp, được gọi là phương pháp từ trên xuống và phương pháp từ dưới lên. Những thuật ngữ này muốn nĩi đến thứ tự xây dựng các nút trong cây phân tích cú pháp. Trong phương pháp đầu, quá trình xây dựng bắt đấu tại gốc, tiến hành hướng xuống các nút lá, cịn trong phương pháp sau thì tiến hành từ các nút lá hướng về gốc. Tính thơng dụng của các thể phân cú pháp từ trên xuống là đo cĩ thể xây dựng được chúng một cách hiệu quả theo lối thủ cơng. Tuy nhiên phân tích cú pháp từ dưới lên lại cĩ thể xử lý được một lớp văn phạm và lược đổ dịch phong phú hơn, vì thể các cơng cụ phần mềm giúp xây dựng các thể phân cú pháp một cách trực tiếp từ các văn phạm đều cĩ xu hướng sử dụng các phương pháp từ đưới lên.
PHẦN 2.4 PHÁN TÍCH CÚ PHÁP 47
Phân tích cú pháp từ trên xuống
Chúng ta sẽ giới thiệu kỹ thuật phán tích cú pháp từ trên xuống qua việc xem xét một văn phạm rất thích hợp cho lớp phương pháp này. Ở những đoạn sau chúng ta sẽ xét đến phương pháp xây dựng các thê phân cú pháp từ trên xuống nĩi chung. Văn phạm sau đây tạo ra một tập con các kiểu dữ liệu của Pascal. Chúng ta dùng thẻ từ dotdot thay cho “.." để nhấn mạnh rằng chuỗi ký tự này được xử lý như một đơn vị.
type -> simpie | Tiad
| array [ simple | oŸ type
sùimple -> integer (28!
| char
{ num dotdot num
(a) te
t) array —C simple 7 Nx type
—Z⁄Z7 éWwx-
(e) array simple pe
TS
nam dotdot num
—Z⁄Z7Ng-
(d) — mrray simple pe
num dotdot num simple
—“Z⁄Zý-
array l xữnple of type
(e) 4 | SS |
nuưm dotdot Tum simple
integer
48 MỘT TRÌNH BIÊN DỊCH MỘT LƯỢT ĐƠN GIẢN
Xây dựng cây phân tích cú pháp theo lối từ trên xuống được thực hiện từ gốc, với nhãn là chưa tận khởi đầu, rồi thực biện hai bước sau đây lập đi lập lại (xem thí dụ trong Hình 2.15).
1. Tại nút n cĩ nhãn là chưa tận A, chọn một trong những luật sinh cho Á và cho các ký hiệu ở vế phải của luật sinh này làm con của ,
2. Tìm nút kế tiếp để xây dựng một cây con tại đĩ.
Đối với một số văn phạm, các bước trên cĩ thể được cài đặt trong khi quét chuỗi nguyên liệu từ trái sang phải. Thẻ từ hiện đang được quét trong nguyên liệu thường được gọi là ký hiệu sải oới (lookahead symbol). Lúc ban đầu, ký hiệu sải với là thẻ từ đầu tiên, nghĩa là thê từ tận trái của chuỗi nguyên liệu. Hình 2.16 minh họa việc phân tích cú pháp chuỗi
array [Í num dotdot num } of integer
Ban đầu, thẻ từ array là ký hiệu sải với và phản đã biết của cây phân tích cú pháp bao gồm gốc, cĩ nhãn là chưa tận khởi đầu £ype trong Hình 2.16(a). Mục đích là xây dựng phần cịn lại của cây sao cho chuỗi được sinh ra bởi cây sẽ sơ khép được (đối sánh được) với chuỗi nguyên liệu.
Đề cĩ được một đối sánh, chưa tận /ype trong Hình 2.16(a) phải dẫn xuất ra một chuỗi bắt đầu bằng sải với array. Trong văn phạm (2.8), chí cĩ một luật sinh cho ¿ype cĩ thể dẫn xuất một chuỗi như thế nên chúng ta sẽ chọn luật sinh đĩ và xây dựng các con cho gốc cĩ nhãn là những ký hiệu ở vế phải của luật sinh.
Mỗi hình trong số ba hình của Hình 2.16 cĩ các mũi tên chỉ ra ký hiệu sải với trong nguyên liệu và nút đang được xét trong cây phân tích cú pháp. Khi xây dựng các con của một nút thì bước kế tiếp chúng ta sẽ xét con tận trái. Trong Hình 2.16(b), các con vừa được xây dựng tại gốc, và con tận trái với nhãn là array sẽ được xét.
Trong cây phân tích cú pháp, khi một tận và nút cho tận đĩ đối sánh được với ký hiệu sải với thì chúng ta dịch tới trước một bước, cả ở cây phân tích cú pháp và ở nguyên liệu. Thẻ từ kế tiếp trong nguyên liệu trở thành sải với mới và con kế tiếp trong cây sẽ được xét. Trong Hình 2.16(e), mũi tên trong cây đã được dịch tới con kế tiếp của gốc và mũi tên trong nguyên liệu đã được địch tới thẻ từ tiếp theo là [. Sau khi dịch tới trước, mũi tên trong cây sẽ chỉ đến con cĩ nhân là chưa tận sinpc. Khi một nút cĩ nhàn là một chưa tận được xét đến, chúng ta sẽ lập lại quá trình chọn một luật sinh cho chưa tận đĩ.
Nĩi chung, việc chọn một luật sinh cho một ký hiệu chưa tận cĩ thể được thực hiện theo kiêu £h nà sơi; nghĩa là chúng ta cĩ thể phải thử một luật sinh rồi phải quay lại để thử một luật sinh khác nếu luật sinh thứ nhất khơng phù hợp. Một luật sinh sẽ khơng phù hợp nếu sau khi dùng nĩ, chúng ta khơng thể tạo ra một cây so khớp được
PHÂN 2.4 PHÂN TÍCH CÚ PHÁP 40
với chuỗi nguyên liệu. Tuy nhiên cĩ một trường hợp đặc biệWcĩ tên là phân tích cú pháp dự đốn (predictive parsing) khơng cĩ tình trạng phải thử lại (trở lui).
PARSE type TREE (4)
Ixpur “TAY f nưn dotdot num 1 of integer
tp€
PARSE —⁄ >>
TREE array [ sửmple 1 of tne
(b)
array [ num dotloi num ] c9f integer INPUr D
tne
PARSE TREE array —““. ⁄ 1e f simple 1
ef type
()
array [ num dotdot nm ] of Ìnteger INPUT
Hình 2.16. Phân tích cú pháp từ trên xuống trong khi đọc nguyên liệu từ trái sang phải. Phân tích cú pháp đự đốn
Phân tích cú pháp đệ qui-tuống (recursive-descent parsing) là một phương pháp phân tích từ trên xuống, trong đĩ chúng ta cho thực thi một tập thủ tục đệ qui để xử lý chuỗi nguyên liệu. Mỗi chưa tận của văn phạm được kèm với một thủ tục. Ở đây chúng ta xét một hình thái đặc biệt của phân tích cú pháp đệ qui-xuống, đĩ là phán tích cú pháp dự đốn ípredictive parsing), trong đĩ sải với giúp xác định đúng thủ tục cần chọn cho mỗi chưa tận. Loạt thủ tục được gọi trong khi xử lý chuỗi nguyên liệu ngầm định aghia một cây phân tích cú pháp cho nguyên liệu.
Thể phân cú pháp dự đốn trong Hình 2.17 gồm cĩ các thủ tục cho chưa tận fype và sữmple của văn phạm (2.8) và một thủ tục bổ sung mạch. Chúng ta dùng mơïch nhằm đơn giản hĩa đoạn mã cho #ype và simple; nĩ địch tới thẻ từ tiếp theo nếu đối ¿
50 MỘT TRÌNH BIÊN DỊCH MỘT LƯỢT ĐƠN GIẢN
của nĩ so khớp được với sải với. Vì thế mafch làm thay đổi biến /ookahead, đĩ là thẻ từ hiện đang được quét trong nguyên liệu.
procedure mưich(: toben); begin Ìf lookahead = t then lookahead := nextioken else error end; procedure ype; begin
Íf lookahead thuộc {integer, char, num)} then simple
else if Íoobahead = '?°' then begin match(†?; matchäđ)
end
else if iookahead = array then begin
match(array); match((9; simple, matchC 3; match\(of); type end
else error end;
procedure simpile; begin
Íf lookahcad = integer then match(integer)
else if íookahead = char then maich(char}
else if /oobahead = num then begin
mafch(num); maich(dotdot); mafch(num) end
else error end;
Hình 2.17. Đoạn mã giả cho một thể phân cú pháp dự đốn.
Phân tích cú pháp bắt đầu bằng một lời gọi đến thủ tục cho chưa tận khởi đầu iype. Với cùng nguyên liệu như trong Hình 2.16, thoạt đầu /ookøhead là thẻ từ thứ nhất array. Thủ tực £ype thực thi đoạn mã
PHÂN 3.4 PHÂN TÍCH CÚ PHÁP ø=
mnaich(array); match({9; sinple, match('Ƒ); mateh(of); type (2.9) tương ứng với vế phải của luật sinh
type -> array [ simple | oŸ type