LỜI CẢM ƠN
Trước hết, em xin chân thành cảm ơn các thầy cô giáo trong khoa Công nghệ thông tin Trường ĐH Kỹ thuật – Hậu cần CAND đã trang bị những kiến thức cơ bản, cần thiết và quý báu để em thực hiện chuyên đề của mình.
Đặc biệt, em xin bày tỏ lòng kính trọng và biết ơn sâu sắc tới thầy Nghiêm Văn Hưng, giáo viên giảng dạy, và thầy Cao Xuân Trường, người đã tận tình hướng dẫn, chỉ bảo và tạo mọi điều kiện thuận lợi giúp em trong quá trình thực hiện chuyên đề.
Mặc dù đã rất cố gắng cùng nhận
77 trang |
Chia sẻ: huong20 | Ngày: 08/01/2022 | Lượt xem: 412 | Lượt tải: 0
Tóm tắt tài liệu Chuyên đề Nghiên cứu Ngôn ngữ hình thức, văn phạm phi ngữ cảnh và Automata đẩy xuống, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
được sự giúp đỡ tận tâm của thầy giáo hướng dẫn, xong do trình độ còn hạn chế, tài liệu chưa được phong phú, và nội dung này khá khó đối với em nên không tránh khỏi những thiếu sót trong quá trình tiếp nhận kiến thức. Em rất mong nhận được sự quan tâm giúp đỡ, chỉ dẫn của thầy cô và sự góp ý từ bạn bè để trong thời gian tới em có thể tiếp tục tìm hiểu và xây dựng chuyên đề một cách hoàn thiện nhất.
Em xin chân thành cảm ơn!
GIỚI THIỆU TỔNG QUAN VỀ CHUYÊN ĐỀ
Tên chuyên đề: Nghiên cứu Ngôn ngữ hình thức, Văn phạm phi ngữ cảnh và Automata đẩy xuống
Sinh viên thực hiện: Hoàng Văn Thao
Lớp: B3-D2B
Giáo viên hướng dẫn: Thiếu úy Cao Xuân Trường
Tính cấp thiết của chuyên đề:
Lý thuyết ngôn ngữ hình thức và Automata đóng một vai trò rất quan trọng trong các cơ sở toán học của tin học. Ngôn ngữ hình thức được sử dụng trong việc xây dựng các ngôn ngữ lập trình, lý thuyết về các chương trình dịch. Các ngôn ngữ hình thức tạo thành một công cụ mô tả đối với các mô hình tính toán cả cho dạng thông tin vào - ra lẫn kiểu thao tác. Lý thuyết ngôn ngữ hình thức, chính vì thực chất của nó là một lĩnh vực khoa học liên ngành; nhu cầu mô tả hình thức văn phạm được phát sinh trong nhiều ngành khoa học khác nhau từ ngôn ngữ học đến sinh vật học. Do đó những khía cạnh thích hợp của lý thuyết ngôn ngữ hình thức sẽ có tầm quan trọng quyết định trong các giáo trình về Lý thuyết ngôn ngữ hình thức và Automata.
Lĩnh vực mà lý thuyết ngôn ngữ hình thức nghiên cứu là những mẫu hình (pattern) có cấu trúc bên trong ngôn ngữ hình thức, và đó là những khía cạnh hoàn toàn mang tính chất có cú pháp. Ngôn ngữ hình thức không còn đơn giản chỉ là để định nghĩa ngôn ngữ tự nhiên, mà nó vượt ra ngoài khỏi phạm vi đó và nó cũng là một cách để thể hiện được những quy tắc có cú pháp của ngôn ngữ tự nhiên.
Mục tiêu của chuyên đề: Nghiên cứu tổng quan về văn phạm hình thức và các Automata, là những công cụ sinh ngôn ngữ, đồng thời đề cập đến các tính chất của ngôn ngữ chính quy, ngôn ngữ phi ngữ cảnh. Ngoài ra, cũng giới thiệu sơ lược về Trình biên dịch, một phần quan trọng của học phần Chương trình dịch gắn bó chặt chẽ với Lý thuyết ngôn ngữ hình thức và Automata, trong đó Văn phạm phi ngữ cảnh là cơ sở lý thuyết để xây dựng Bộ phân tích cú pháp, là thành phần quan trọng nhất trong một Trình biên dịch.
Đối tượng nghiên cứu: Ngôn ngữ hình thức và lý thuyết Automata.
Phạm vi nghiên cứu:
Ngôn ngữ phi ngữ cảnh cùng hai phương tiện để xác định chúng là Văn phạm phi ngữ cảnh;
Automata đẩy xuống.
Phương pháp nghiên cứu:
Phương pháp nghiên cứu tài liệu;
Phương pháp chuyên gia;
Phương pháp thực nghiệm.
Nội dung nghiên cứu:
Lý thuyết về Ngôn ngữ hình thức, Văn phạm phi ngữ cảnh và Automata đẩy xuống;
Các tính chất của Ngôn ngữ hình thức, Văn phạm phi ngữ cảnh và Automata đẩy xuống;
Ứng dụng của Ngôn ngữ hình thức và Automata với trình biên dịch.
Chuyên đề gồm 5 chương:
Chương I: Nhập môn về văn phạm và ngôn ngữ hình thức
1.1 Khái niệm ngôn ngữ
1.2 Văn phạm và ngôn ngữ sinh bởi văn phạm
1.3 Một số tính chất của ngôn ngữ
Chương II: Văn phạm phi ngữ cảnh
2.1 Suy dẫn phi ngữ cảnh
2.2 Biến đổi các Văn phạm phi ngữ cảnh
Chương III: Automata đẩy xuống
3.1 Automata đẩy xuống không tiền định
3.2 Automata đẩy xuống và Văn phạm phi ngữ cảnh
Chương IV: Tổng quan về trình biên dịch
4.1 Ngôn ngữ lập trình
4.2 Trình biên dịch
4.3 Ứng dụng của Văn phạm phi ngữ cảnh và Automata đẩy xuống với trình biên dịch
Chương V: Demo một bài toán
5.1 Bài toán và cơ sở lý thuyết
5.2 Demo ví dụ về sự tương đương giữa BTCQ và NFAε
Sản phẩm:
Báo cáo chuyên đề;
Chương trình demo cơ bản.
MỤC LỤC
MỤC LỤC HÌNH
Hình 1.1 Cây dẫn xuất cho ví dụ 12
Hình 2.1 Một cây suy dẫn 21
Hình 2.2 Một A-cây 21
Hình 2.3 Một A-cây có một đỉnh trong 22
Hình 2.4 Một A-cây và các cây con của nó 23
Hình 2.5 Một cây suy dẫn trong G0 24
Hình 2.6 Một cây suy dẫn khác của G0 25
Hình 2.7 Một cây suy dẫn của G1 26
Hình 2.8 Một cây suy dẫn của G1 26
Hình 4.1 Sơ đồ trình biên dịch 50
Hình 4.2 Sơ đồ trình thông dịch 50
Hình 4.3 Cây cú pháp A 51
Hình 4.4 Cây cú pháp B 56
Hình 4.5 Sơ đồ hoạt động loader 61
Hình 5.1 Giao diện làm việc của Demo 65
Hình 5.2 Nhập BTCQ cần chuyển 66
Hình 5.3 Kết quả là bảng biểu diễn một NFAε 67
Hình 5.4 Một cách biểu diễn khác của NFAε 68
MỤC LỤC BẢNG
Bảng IV.1 Bảng danh biểu 1 54
Bảng IV.2 Bảng danh biểu 2 60
LỜI NÓI ĐẦU
Ngôn ngữ là phương tiện để giao tiếp, sự giao tiếp có thể hiểu là giao tiếp giữa con người với nhau, giao tiếp giữa người với máy, hay giao tiếp giữa máy với máy. Ngôn ngữ để con người có thể giao tiếp với nhau được gọi là ngôn ngữ tự nhiên, chẳng hạn như tiếng Anh, tiếng Nga, tiếng Việt là các ngôn ngữ tự nhiên. Các quy tắc cú pháp của ngôn ngữ tự nhiên nói chung rất phức tạp nhưng các yêu cầu nghiêm ngặt về ngữ nghĩa thì lại thiếu chặt chẽ, chẳng hạn cùng một từ hay cùng một câu ta có thể hiểu chúng theo những nghĩa khác nhau tùy theo từng ngữ cảnh cụ thể. Con người muốn giao tiếp với máy tính tất nhiên cũng thông qua ngôn ngữ. Để có sự giao tiếp giữa người với máy hay giữa máy với nhau, cần phải có một ngôn ngữ với các quy tắc cú pháp chặt chẽ hơn so với các ngôn ngữ tự nhiên, nói cách khác, với một từ hay một câu thì ngữ nghĩa của chúng phải là duy nhất mà không phụ thuộc vào ngữ cảnh. Những ngôn ngữ như thế được gọi là ngôn ngữ hình thức. Con người muốn máy tính thực hiện công việc, phải viết các yêu cầu đưa cho máy bằng ngôn ngữ máy hiểu được. Việc viết các yêu cầu như thế gọi là lập trình. Ngôn ngữ dùng để lập trình được gọi là ngôn ngữ lập trình. Các ngôn ngữ lập trình đều là các ngôn ngữ hình thức.
Cả ngôn ngữ hình thức lẫn ngôn ngữ tự nhiên đều có thể xem như những tập các từ, tức là các xâu hữu hạn các phần tử của một bộ chữ cái cơ sở nào đó. Về mặt truyền thống, lý thuyết ngôn ngữ hình thức liên quan đến các đặc tả cú pháp của ngôn ngữ nhiều hơn là đến những vấn đề ngữ nghĩa. Một đặc tả về cú pháp của một ngôn ngữ có hữu hạn từ, ít nhất về nguyên tắc, có thể được cho bằng cách liệt kê các từ. Điều đó không thể áp dụng đối với các ngôn ngữ có vô hạn từ. Nhiệm vụ chính của lý thuyết ngôn ngữ hình thức là nghiên cứu các cách đặc tả hữu hạn của các ngôn ngữ vô hạn.
Lý thuyết ngôn ngữ hình thức và ôtômat đóng một vai trò rất quan trọng trong các cơ sở toán học của tin học. Ngôn ngữ hình thức được sử dụng trong việc xây dựng các ngôn ngữ lập trình, lý thuyết về các chương trình dịch. Các ngôn ngữ hình thức tạo thành một công cụ mô tả đối với các mô hình tính toán cả cho dạng thông tin vào-ra lẫn kiểu thao tác. Lý thuyết ngôn ngữ hình thức, chính vì thực chất của nó là một lĩnh vực khoa học liên ngành; nhu cầu mô tả hình thức văn phạm được phát sinh trong nhiều ngành khoa học khác nhau từ ngôn ngữ học đến sinh vật học.
Báo cáo này nhằm trình bày về văn phạm hình thức và ôtômat đẩy xuống, là những công cụ sinh ngôn ngữ, đồng thời đề cập đến các tính chất của ngôn ngữ chính quy, ngôn ngữ phi ngữ cảnh, ngôn ngữ đệ quy và ngôn ngữ đệ quy đếm được. Ngoài ra cũng giới thiệu sơ lược về trình biên dịch, một phần quan trọng của học phần Chương trình dịch/.
Chuyên đề gồm 8 phần chính:
Lời mở đầu: Giới thiệu về chuyên đề
Chương I: Nhập môn về văn phạm và ngôn ngữ hình thức
Chương II: Văn phạm phi ngữ cảnh
Chương III: Automata đẩy xuống
Chương IV: Tổng quan về trình biên dịch
Chương V: Giới thiệu về chương trình Demo
Kết luận: Đưa ra một số đánh giá tổng quan về kết quả nghiên cứu, nêu ra hướng phát triển trong thời gian tới.
Tài liệu tham khảo: Đưa ra các tài liệu đã được trích dẫn, tham khảo khi thực hiện chuyên đề.
NHẬP MÔN VỀ VĂN PHẠM VÀ NGÔN NGỮ HÌNH THỨC
Khái niệm ngôn ngữ
Các khái niệm cơ bản
Bảng chữ cái
Theo tài liệu [3], tác giả: Phan Đình Diệu, có viết “Một dãy hữu hạn hay vô hạn các phần tử, kí hiệu S được gọi là một bảng chữ cái trong đó mỗi phần tử a Î S được gọi là một kí hiệu (một chữ cái).”.
Từ đó ta có Định nghĩa I.1.
Định nghĩa I.1
Tập S khác rỗng gồm hữu hạn hay vô hạn các ký hiệu được gọi là bảng chữ cái. Mỗi phần tử aÎ S được gọi là một chữ cái hay một ký hiệu.
Thí dụ 1.1:
Dưới đây là các bảng chữ cái:
å = {a, b, c, , x, y, z},
Δ = {a, b, g, d, e, h, j, k, m, c, n, p, q, r, s, t, w,x, y},
Г = {0, 1},
W = {if, then, else, a, b, c, d, e, f, +, -, *, /, =, ¹}.
Từ
Định nghĩa I.2
Giả sử có bảng chữ cái S = {a1, a2, , am}, một dãy các chữ cái α = ai1 ai2 ait, với aij Î S (1 ≤ j ≤ t) được gọi là một từ hay một xâu trên bảng chữ cái S.
Tổng số vị trí của các ký hiệu xuất hiện trong xâu α được gọi là độ dài của từ α và ký hiệu là | α |. Như vậy, một từ trên bảng chữ cái S là một xâu hữu hạn gồm một số lớn hơn hay bằng không các chữ cái của S, trong đó một chữ cái có thể xuất hiện nhiều lần.
Xâu không có chữ cái nào được gọi là từ rỗng và được ký hiệu là e. Rõ ràng từ rỗng là từ thuộc mọi bảng chữ cái. Hai từ a = a1a2an và b = b1b2bm được gọi là bằng nhau, và được ký hiệu là a = b, nếu n = m và ai = bi với mọi i = 1, 2, , n.
Nếu α là một từ trên bảng chữ cái S, và S Í Δ thì α cũng là từ trên bảng chữ cái Δ. Tập mọi từ trên bảng chữ cái S được ký hiệu là S*, còn tập mọi từ khác rỗng trên bảng chữ cái S được ký hiệu là S+. Như vậy S+ = S* \ {e} và S* = S+ È {e}. Dễ thấy rằng các tập S* và S+ là vô hạn.
Về cấu trúc đại số thì S* là một vị nhóm tự do sinh bởi S với đơn vị là từ rỗng e, còn S+ là một nửa nhóm tự do sinh bởi S. Có thể chứng minh được rằng các tập S* và S+ là vô hạn đếm được.
Thí dụ 1.2:
Ta có e , 0, 01, 101, 1010, 110011 là các từ trên bảng chữ cái Г = {0,1}. Các xâu e, beautiful, happy, holiday là các từ trên bảng chữ cái S = {a, b, c, , z}.
Ngôn ngữ
Định nghĩa I.3
Cho bảng chữ cái S, mỗt tập con L Í S* được gọi là một ngôn ngữ hình thức (hay ngôn ngữ) trên bảng chữ cái S.
Tập rỗng, ký hiệu Æ, là một ngôn ngữ không gồm một từ nào và được gọi là ngôn ngữ rỗng. Vậy ngôn ngữ rỗng là ngôn ngữ trên mọi bảng chữ cái.
Chú ý rằng ngôn ngữ rỗng: L = Æ là khác với ngôn ngữ chỉ gồm một từ rỗng: L = {e}.
Thí dụ 1.3:
· S* là ngôn ngữ gồm tất cả các từ trên S còn S+ là ngôn ngữ gồm tất cả các từ khác từ trống trên S.
· L = { e, 0, 1, 01, 10, 00, 11, 011,100} là một ngôn ngữ trên bảng chữ cái Г = {0, 1}.
· L = {a, b, c, aa, ab, ac, abc} là ngôn ngữ trên bảng chữ cái S = {a, b, c}.
· L1 = {e, a, b, abb, aab, aaa, bbb, abab}, L2 = {anbn | nÎ N} là hai ngôn ngữ trên bảng chữ S = {a, b}, L1 là ngôn ngữ hữu hạn trong khi L2 là ngôn ngữ vô hạn. Mỗi từ thuộc ngôn ngữ L2 có số chữ cái a bằng số chữ cái b với a và b không xen kẽ, a nằm ở phía trái và b ở phía phải của từ.
Các phép toán trên các từ
Phép nhân ghép
Theo tài liệu [5], tác giả: Nguyễn Văn Định, có viết “Tích ghép (hay nhân ghép) của hai từ α = a1a2am và từ b = b1b2bn trên bảng chữ cái S, là từ g = a1a2amb1b2bn trên bảng chữ cái S.
Kí hiệu phép nhân ghép là g = α.b (hay g = αb).”
Từ đó ta có Định nghĩa I.4.
Định nghĩa I.4
Tích ghép (hay nhân ghép) của hai từ α = a1a2am và từ b = b1b2bn trên bảng chữ cái S, là từ g = a1a2amb1b2bn trên bảng chữ cái S.
Kí hiệu phép nhân ghép là g = α.b (hay g = αb).
Thí dụ 1.4:
Trên bảng chữ cái W = {if, then, else, a, b, c, d, e, f, +, -, *, /, =, ¹}, ta có các từ a = if a+b=c then c*d=e và b = else c/d=f, còn αb là từ: if a+b=c then c*d=e else c/d=f.
Cho S = {a, b, c}, khi đó: Từ w = abcbcb chứa 2 vị trí của bcb, đó là a*bcb*cb và abc*bcb*, φ = bcb là một từ con của w. Từ w chứa một vị trí của ký hiệu a, đó là *a*bcbcb.
Từ w = 010111001 trên bảng chữ cái {0, 1} có độ dài 9, trong đó 0101 là tiền tố và 11001 là hậu tố của w.
Phép lấy từ ngược
Theo tài liệu [5], tác giả: Nguyễn Văn Định, có viết “Giả sử có từ khác rỗng w = a1a2 am trên bảng chữ cái S, khi đó từ am am-1 a2a1 được gọi là từ ngược (hay từ soi gương) của từ w, và được ký hiệu là wR, hay w^.
Khi w = e ta quy ước eR = e.”
Từ đó ta có Định nghĩa I.5.
Định nghĩa I.5
Giả sử có từ khác rỗng w = a1a2 am trên bảng chữ cái S, khi đó từ am am-1 a2 a1 được gọi là từ ngược (hay từ soi gương) của từ w, và được ký hiệu là wR, hay w^.
Khi w = e ta quy ước eR = e.
Thí dụ 1.5:
Cho các từ α = 100110 và b = aabb trên bảng chữ cái {0,1,a,b}, theo định nghĩa ta có:
αR = 011001 và (αR)R = (011001)R = 100110 = α.
bR = bbaa và (bR)R = (bbaa)R = aabb = b.
Cho các từ happy và oto trên bảng chữ cái å = {a, b, c, x, y, z}, khi đó ta có: (happy)R = yppah và (oto)R = oto. Ngoài ra ta có: | (happy)R | = | yppah| = | happy | = 3.
Phép chia từ
Là phép toán ngắt bỏ phần đầu hay phần cuối của một từ. Ta có các định nghĩa sau:
Phép chia trái của từ α cho từ b (hay thương bên trái của α và b) cho kết quả là phần còn lại của từ α sau khi ngắt bỏ phần đầu b trong từ α, và được ký hiệu là b\
Phép chia phải của từ α cho từ g (hay thương bên phải của α và g) cho kếtg
quả là phần còn lại của từ α sau khi ngắt bỏ phần cuối g trong từ α, và được ký hiệu là α/
Các phép toán trên ngôn ngữ.
Các họ ngôn ngữ cụ thể thường được đặc trưng một cách tiện lợi qua các phép toán xác định trên ngôn ngữ, họ đó gồm các ngôn ngữ nhận được bằng việc tổ hợp từ một số ngôn ngữ cho trước bởi một số phép toán nào đó. Vì mỗi ngôn ngữ là một tập hợp nên ta có các phép toán đại số tập hợp như là phép giao, phép hợp, phép hiệu, phép lấy bù trên các ngôn ngữ. Chẳng hạn, với L1 và L2 là hai ngôn ngữ trên bảng chữ cái S thì ta cũng có các ngôn ngữ mới sau đây trên bảng chữ cái S: L1 È L2, L1 Ç L2, L1.L2, S* \ L1.
Phép hợp
Theo tài liệu [9], tác giả: Nguyễn Quốc Thắng – Nguyễn Lâm Tùng, có viết “Tập các từ {x | x Î L1 hoặc x Î L2 } được gọi là hợp của hai ngôn ngữ L1 và L2, ký hiệu L1È L2.”.
Từ đó ta có Định nghĩa I.6.
Định nghĩa I.6
Hợp của hai ngôn ngữ L1 và L2 trên bảng chữ cái å, ký hiệu L1È L2, là một ngôn ngữ trên bảng chũ cái å, đó là tập từ:
L = {w Î S* | w Î L1 hoặc w Î L2 }
Định nghĩa phép hợp có thể mở rộng cho một số hữu hạn các ngôn ngữ, tức là hợp của các ngôn ngữ L1, L2, , Ln trên bảng chữ cái S, là tập từ:
và
Phép giao
Định nghĩa I.7
Giao của hai ngôn ngữ L1 và L2 trên bảng chữ cái å, ký hiệu L1∩ L2, là một ngôn ngữ trên bảng chữ cái å, đó là tập từ:
L = {w Î S* | w Î L1 và w Î L2 }
Định nghĩa phép giao có thể mở rộng cho một số hữu hạn các ngôn ngữ, tức là giao của các ngôn ngữ L1, L2, , Ln trên bảng chữ cái S, là tập từ:
{w Î S* | w Î Li, với mọi i, 1 ≤ i ≤ n }
Phép lấy phần bù
Định nghĩa I.8
Ngôn ngữ phần bù của ngôn ngữ L trên bảng chữ cái S, ký hiệu CSL (hay đơn giản là CL, nếu không gây nhầm lẫn), là một ngôn ngữ trên bảng chữ cái å, đó là tập từ: CSL = {w Î S* | w Ï L }.
Thí dụ 1.6:
Cho ngôn ngữ L1 = {e, 0, 01}, L2 = {e, 01, 10} trên bảng chữ cái S = {0, 1}, khi đó ta có: L1È L2 = {e, 0, 01, 10}, L1 ∩ L2 = {e, 01}.
Cho ngôn ngữ L = {w Î å*, với | w | là một số chẵn }, khi đó ta có: CSL = {w Î å+, với | w | là một số lẻ}.
Phép nhân ghép
Định nghĩa I.9
Cho hai ngôn ngữ L1 trên bảng chữ S1 và L2 trên bảng chữ S2. Nhân ghép hay tích của hai ngôn ngữ L1 và L2 là một ngôn ngữ trên bảng chữ S1 È S2, ký hiệu L1L2, đuợc xác định bởi: L1L2 = {ab | aÎL1 và bÎL2}.
Thí dụ 1.7:
Đây là một phản ví dụ để chỉ ra rằng phép nhân ghép không có tính phân phối đối với phép giao. Phép hợp, phép giao không có tính phân phối đối với phép nhân ghép. Xét các ngôn ngữ L1 = {0, 01}, L2 = {01, 10}, L3 = {0} trên bảng chữ cái S = {0, 1}.
Có thể kiểm tra được rằng phép nhân ghép không có tính phân phối đối với phép giao: Ta có: L2 Ç L3 = Æ, do đó: L1(L2 Ç L3) = Æ,
Mặt khác, ta có L1L2 = {001, 010, 0101, 0110} và L1L3 = {00, 010}, do đó: (L1L2) Ç (L1L3) = {010}. Vậy L1(L2 Ç L3) ¹ (L1L2) Ç (L1L3), tức là phép nhân ghép không có tính phân phối đối với phép giao.
Kiểm tra tính phân phối của phép hợp, phép giao đối với phép nhân ghép: Ta có: L2L3 = {010, 100}, do đó: L1 È (L2L3) = {0, 01, 010, 100},
Mặt khác ta cũng có L1 È L2 = {0, 01, 10} và L1 È L3 = {0, 01}, do đó: (L1 È L2)(L1 È L3) = {00, 001, 010, 0101, 100, 1001}. Vậy L1 È (L2L3) ¹ (L1 È L2)(L1 È L3), tức là phép hợp không có tính phân phối đối với phép nhân ghép. Tương tự, đối với phép giao, ta có: L2L3 = {010, 100}, do đó: L1 Ç (L2L3) = Æ.
Mặt khác L1 Ç L2 = {01}, L1 Ç L3 = {0}, do đó: (L1 Ç L2)(L1 Ç L3) = {010}. Vậy L1 Ç (L2L3) ¹ (L1 Ç L2)(L1 Ç L3). Tức là phép giao không có tính phân phối đối với phép nhân ghép. Vì phép ghép ngôn ngữ có tính kết hợp nên ký hiệu Ln được dùng với mọi ngôn ngữ L và số tự nhiên n theo nghĩa quen thuộc sau:
Phép lặp
Định nghĩa I.10
Cho ngôn ngữ L trên bảng chữ cái S, khi đó:
Tập từ được gọi là ngôn ngữ lặp cắt của ngôn ngữ L, ký hiệu là L*. Vậy ngôn ngữ lặp của L là tập hợp lũy thừa của L:
L*=
Tập từ được gọi là ngôn ngữ lặp cắt của ngôn ngữ L, ký hiệu là L+, Vậy ngôn ngữ lặp cắt của L là hợp của mọi lũy thừa dương của L: L+=
Thí dụ 1.8:
+ Xét ngôn ngữ L = {0, 1} trên bảng chữ S = {0, 1}. Ta có:
L2 = {00, 01, 10, 11}, tập hợp các xâu nhị phân độ dài 2;
L3 = {000, 001, 010, 011, 100, 101, 110, 111}, tập hợp các xâu nhị phân độ dài 3. Tương tự, Ln là tập hợp các xâu nhị phân độ dài n. Vì vậy, L* là tập hợp tất cả các xâu nhị phân.
+ Xét hai ngôn ngữ trên bảng chữ S = {a}:
L1 = {a2n | n ³ 1},
L2 = {a5n+3 | n ³ 0}.
Khi đó, ta có L1 = {a2}+, L2 = {a5}*{a3}.
Phép lấy ngôn ngữ ngược
Định nghĩa I.11
Cho ngôn ngữ L trên bảng chữ cái S, khi đó ngôn ngữ ngược của L là một ngôn ngữ trên bảng chữ cái å, được ký hiệu là LR hay L^, là tập từ:
LR = {w Î S* / wR Î L}
Thí dụ 1.9:
Cho L = {e, ab, abc, cbaa} là một ngôn ngữ trên bảng chữ cái S = {a, b, c}, khi đó LR = {e, ba, cba, aabc} là ngôn ngữ ngược của L.
Phép chia ngôn ngữ
Định nghĩa I.12
Cho ngôn ngữ X và Y trên bảng chữ cái S, khi đó thương bên trái của ngôn ngữ X cho ngôn ngữ Y là một ngôn ngữ trên å, được ký hiệu là Y \ X, là tập từ:
Y \ X = {z Î S* / x Î X, y Î Y mà x = yz}
Cho ngôn ngữ X và Y trên bảng chữ cái S, khi đó thương bên phải của ngôn ngữ X cho ngôn ngữ Y là một ngôn ngữ trên å, được ký hiệu là X /Y, là tập từ:
X / Y = {z Î S* / x Î X, y Î Y mà x = zy}
Văn phạm và ngôn ngữ sinh bởi văn phạm
Ta có thể hình dung một văn phạm như một “thiết bị tự động” mà nó có khả năng sinh ra một tập hợp các từ trên một bảng chữ cái cho trước. Mỗi từ được sinh ra sau một số hữu hạn bước thực hiện các quy tắc của văn phạm.
Việc xác định một ngôn ngữ trên bảng chữ cái cho trước có thể được thực hiện bằng một trong các cách thức sau:
Cách 1. Đối với mỗi từ thuộc ngôn ngữ đã cho, ta có thể chọn một quy cách hoạt động của “thiết bị tự động” để sau một số hữu hạn bước làm việc nó dừng và sinh ra chính từ đó.
Cách 2. “Thiết bị tự động” có khả năng lần lượt sinh ra tất cả các từ trong ngôn ngữ đã cho.
Cách 3. Với mỗi từ w cho trước, “thiết bị tự động” có thể cho biết từ đó có thuộc ngôn ngữ đã cho hay không.
Trong lý thuyết văn phạm, người ta đã chứng minh được rằng ba cách thức trên là tương đương nhau hay văn phạm làm việc theo các cách trên là tương đương nhau. Vì vậy, ở đây ta quan tâm đến cách thứ nhất, tức là ta xét văn phạm như là một “thiết bị tự động” sinh ra các từ. Vì lẽ đó mà người ta còn gọi các “thiết bị tự động” đó là văn phạm sinh.
Định nghĩa văn phạm
Theo tài liệu [2], tác giả: Trần Văn Lộc, có viết: “Văn phạm G là 1 bộ sắp thứ tự gồm 4 thành phần: G = ”
Từ đó ta có Định nghĩa I.13.
Định nghĩa I.13
Văn phạm G là 1 bộ sắp thứ tự gồm 4 thành phần: G = , trong đó:
S là một bảng chữ cái, gọi là bảng chữ cái cơ bản (hay bảng chữ cái kết thúc), mỗi phần tử của nó được gọi là một ký hiệu kết thúc hay ký hiệu cơ bản;
là một bảng chữ cái, Ç S = Æ, gọi là bảng ký hiệu phụ (hay báng chữ cái không kết thúc), mỗi phần tử của nó được gọi là một ký hiệu không kết thúc hay ký hiệu phụ.
S Î được gọi là ký hiệu xuất phát hay tiên đề;
P là tập hợp các quy tắc sinh có dạng a®b, a được gọi là vế trái và b được gọi là vế phải của quy tắc này, với a, b Î (S È )* và trong a chứa ít nhất một ký hiệu không kết thúc.
P = {a®b | a = α’Aα’’, với A Î Δ, α’, α’’, b Î (S È )* }
Chẳng hạn, với S = {0,1}, = {S, A, B} thì các quy tắc S ® 0S1A, 0AB ® 1A1B, A ® e, là các quy tắc hợp lệ vì vế trái luôn chứa ít nhất 1 ký hiệu phụ thuộc . Nhưng các quy tắc dạng 0 ® A, 01 ® 0B, là các quy tắc không hợp lệ.
Thí dụ 1.10:
Các bộ bốn sau là các văn phạm:
· G1 = ,
· G2 = ,
· G3 =
Chú ý: Nếu các quy tắc có vế trái giống nhau có thể viết gọn lại: hai quy tắc a® b, a® g có thể được viết là a® b | g. Chẳng hạn, như trong văn phạm G1 ở thí dụ 1.10, ta có thể viết hai quy tắc của nó dưới dạng S®0S1 | e.
Ngôn ngữ sinh bởi văn phạm
Định nghĩa I.14
Cho văn phạm G = và h, wÎ(S È )*. Ta nói w được suy dẫn trực tiếp từ h trong G, ký hiệu h├G w hay ngắn gọn là h├ w (nếu không sợ nhầm lẫn), nếu tồn tại quy tắc a®bÎP và g, dÎ(S È )* sao cho h = gad, w = gbd.
Điều này có nghĩa là nếu h nhận vế trái a của quy tắc a®b như là từ con thì ta thay a bằng b để được từ mới w.
Cho văn phạm G = và h, wÎ(S È )*. Ta nói w được suy dẫn từ h trong G, ký hiệu h╞G w hay ngắn gọn là h╞ w (nếu không sợ nhầm lẫn), nếu h = w hoặc tồn tại một dãy D = w0, w1,, wkÎ(S È )* sao cho w0 = h, w k = w và wi-1├ wi, với i = 1, 2,..., k.
Dãy D = w0, w1, , wk được gọi là một dẫn xuất của w từ h trong G và số k được gọi là độ dài của dẫn xuất này. Nếu w0 = S và wk Î S* thì dãy D gọi là dẫn xuất đầy đủ.
Nếu wi được suy dẫn trực tiếp từ wi-1 bằng việc áp dụng một quy tắc p nào đó trong G thì ta nói quy tắc p được áp dụng ở bước thứ i.
Cho văn phạm G = . Từ wÎS* được gọi là sinh bởi văn phạm G nếu tồn tại suy dẫn S╞ w. Ngôn ngữ sinh bởi văn phạm G, ký hiệu L(G), là tập hợp tất cả các từ sinh bởi văn phạm G:
L(G) = {wÎS* | S ╞G w}.
Hai văn phạm G1 = và G2 = được gọi là tương đương nếu L(G1) = L(G2).
Thí dụ 1.11:
Xét văn phạm G1 trong thí dụ 1.11 Từ w = 00001111 được suy dẫn từ S bằng dãy dẫn xuất độ dài 5: S├ 0S1├ 00S11├ 000S111├ 0000S1111 ├ 00001111 (có thể viết ngắn gọn là w = 0414). Bằng việc sử dụng n lần (n ³ 0) quy tắc 1 rồi quy tắc 2, ta có: S╞ 0n1n. Do đó L(G1) = {0n1n | n ³ 0}.
Xét văn phạm G2 trong thí dụ 1.10 Sử dụng quy tắc 1, rồi n lần (n ³ 0) quy tắc 2, sau đó quy tắc 3 để kết thúc, ta có: S├ Ab╞ anAbnb├ anbn+1.
Do đó L(G2) = {anbn+1 | n ³ 0}.
Dễ dàng thấy rằng: L(G4) = {tôi ăn cơm, anh ăn cơm, chị ăn cơm, tôi ăn phở, anh ăn phở, chị ăn phở, tôi uống sữa, anh uống sữa, chị uống sữa, tôi uống café, anh uống café, chị uống café}.
Ta có thể biểu diễn việc dẫn xuất từ đến một từ trong L(G4), chẳng hạn “tôi ăn cơm” bằng một cây gọi là cây dẫn xuất hay cây phân tích cú pháp như dưới đây. Tất nhiên, theo quan điểm phân tích cú pháp thực tế, việc xem xét các quy tắc theo hướng ngược lại là từ phải qua trái. Điều đó có nghĩa là cây dưới đây được xử lý từ dưới lên trên chứ không phải là từ trên xuống dưới. (Hình 1.1).
Hình 1.1 Cây dẫn xuất cho ví dụ
Phân loại văn phạm theo Chomsky
Theo tài liệu [8], tác giả: Trần Văn Ban, có viết “Trong định nghĩa của văn phạm G = (VN, å, P, S); VN là tập các biến, å là tập chữ cái và S ÎVN. Để tiện lợi cho việc nghiên cứu và khảo sát các ngôn ngữ được sinh ra bởi văn phạm, chúng ta tìm cách phân loại chúng. Muốn phân loại được các ngôn ngữ, chúng ta phải dựa vào các dạng khác nhau của các qui tắc dẫn xuất. Chomsky đã chia các qui tắc dẫn xuất của văn phạm G thành bốn loại ”
Dựa vào đặc điểm của tập quy tắc mà người ta chia các văn phạm thành các nhóm khác nhau. Noam Chomsky (Institute Professor, Massachusetts Institute of Technology. Born December 7, 1928 Philadelphia, Pennsylvania, USA) đã phân loại văn phạm thành bốn nhóm:
Nhóm 0: Văn phạm không hạn chế (hay văn phạm ngữ cấu, văn phạm tổng quát),
Nhóm 1: Văn phạm cảm ngữ cảnh,
Nhóm 2: Văn phạm phi ngữ cảnh,
Nhóm 3: Văn phạm chính quy.
Dưới đây là các định nghĩa cho các nhóm văn phạm nói trên.
Định nghĩa I.15
Văn phạm G = mà không có một ràng buộc nào đối với các quy tắc của nó được gọi là văn phạm tổng quát hay văn phạm không hạn chế.
Như vậy, các quy tắc trong văn phạm nhóm 0 có dạng: a®b, với a = α’Aα’’, A Î Δ, α’, α’’, b Î (S È )*.Các quy tắc của văn phạm nhóm 0 được gọi là quy tắc không hạn chế. Ngôn ngữ do văn phạm nhóm 0 sinh ra được gọi là ngôn ngữ tổng quát.
Văn phạm G = mà các quy tắc của nó đều có dạng: a®b, với a = α’Aα’’, A Î Δ, α’, α’’, b Î (S È )*, và | a | ≤ | b |, được gọi là văn phạm nhóm 1hay văn phạm cảm ngữ cảnh.
Các quy tắc trong văn phạm nhóm 1 được gọi là quy tắc cảm ngữ cảnh. Ngôn ngữ do văn phạm cảm ngữ cảnh sinh ra được gọi là ngôn ngữ cảm ngữ cảnh.
Các văn phạm mà các quy tắc của chúng có dạng trên, đồng thời chứa thêm quy tắc rỗng S®e, cũng được xếp vào lớp văn phạm nhóm 1.
Thí dụ 1.12:
Cho văn phạm: G1 = , với P1 = {S®e, S®1A, A®1B, B®1A, A®1}. Khi đó, G1 là văn phạm chính quy và L(G1) = {12n | n ³ 0}. Thật vậy, sử dụng quy tắc 1, ta có S├ 12n, (e = 12n, với n = 0), sử dụng quy tắc 2, rồi n-1 lần (n ³ 1) liên tiếp cặp quy tắc 3 và 4, cuối cùng là quy tắc 5, ta có:
S├ 1A ├ 11B ├ 111A ├ ╞ 1(12n-2)A ├ 1(12n-2)1 = 12n.
Một số tính chất của ngôn ngữ
Một số tính chất của văn phạm và dẫn xuất
Định lý I.1
Với mọi văn phạm G = , luôn tồn tại một văn phạm G’ = tương đương với văn phạm G, tức là L(G) = L(G’).
Chứng minh:
Giả sử có văn phạm G = , ta xây dựng văn phạm G’ = , trong đó:
· S’ = S, và với mỗi a Î S, ta bổ xung một ký hiệu Ï S È và gọi là đối ngẫu của a, đặt Г = { | a Î S}
· ’ = È Г
· S’ = S
P’ = P1 È P2, với P1 = { ® a | "a Î S}, P2 = {a ® b | "a®b Î P}, a và b là các xâu a và b đã được thay các ký hiệu thuộc S bằng các ký hiệu đối ngẫu của nó. Dễ thấy rằng L(G) = L(G’), thật vậy ta sẽ chứng minh hai bao hàm thức:
a./ Chứng minh L(G) Í L(G’): Lấy bất kỳ w Î L(G), khi đó ta có S╞Gw, tức là ta có một dãy suy dẫn trực tiếp trong G: S = w0├G w1├G ├G wk = w, với dãy suy dẫn này, ta thay mọi quy tắc trong các suy dẫn wi ├G wi+1, ( 0 ≤ i ≤ k-1), bởi các quy tắc tương ứng trong P1 và P2, ta nhận được dãy các suy dẫn trong G’: S = w’0├G’ w’1 ├G’ ├G’ w’m = w, do đó ta có S╞G’w , tức là w Î L(G’). Vậy L(G) Í L(G’).
b./ Chứng minh L(G’) Í L(G): Lấy bất kỳ w Î L(G’), khi đó ta có S╞G’w, tức là ta có một dãy suy dẫn trong G’: S = w’0├G’ w’1 ├G’ ├G’ w’k = w, trong các suy dẫn wi├G’ wi+1, ( 0 ≤ i ≤ k-1), ta thay mọi kí hiệu a Î Г bởi các ký hiệu tương ứng a Î S1, khi đó mọi quy tắc đều thuộc P, ta nhận được dãy các suy dẫn trưc tiếp trong G: S = w0├G w1├G ├G wk = w, ta có S╞Gw, tức là w Î L(G). Vậy L(G’) Í L(G).
Định lý I.2 (Hợp thành các suy dẫn)
Cho hệ viết lai W=(V, P) và cho u1,, un, v1,, vn là các xâu trong V*. Bây giờ nếu u1⟹*v1,, un⟹*vn, thì ta có u1un ⟹*v1vn.
Ta thừa nhận định lý trên (không cần chứng minh).
Tính đóng của lớp ngôn ngữ sinh bởi văn phạm
Định lý I.3
Lớp ngôn ngữ sinh bởi văn phạm là đóng đối với phép hợp (È), phép giao (∩) và phép nhân ghép ngôn ngữ (.)
Chứng minh:
Trước hết, ta sẽ chứng minh lớp ngôn ngữ sinh bởi văn phạm là đóng đối với phép hợp, việc chứng minh tính đóng của lớp ngôn ngữ sinh bởi văn phạm đối với các phép giao và phép nhân ngôn ngữ là hoàn toàn tương tự.
Giả sử L1, L2 là các ngôn ngữ được sinh bởi văn phạm G1= , G2 = , tức là L1 = L(G1), L2 = L(G2). Ta chứng minh tồn tại văn phạm G sao cho L(G) = L1È L2.
Xây dựng văn phạm G sinh ra ngôn ngữ L1È L2 như sau: G = , với:
S = S1È S2
Δ = Δ1È2 È{S}
P = P1È P2 È {S®S1, S®S2}
Ta sẽ chứng minh L(G) = L1È L2 bằng cách chứng minh hai bao hàm thức:
a./ Chứng minh L(G) Í L1È L2: Giả sử w Î L(G), khi đó tồn tại một suy dẫn trong văn phạm G: S ╞G w, trong đó w Î S* = (S1È S2)*. Do cách xây dựng tập quy tắc P, nên trong suy dẫn S╞ w, có hai khả năng:
· hoặc S├G S1╞G1 w, vậy w là kết quả của suy dẫn S1╞ w trong G1, do đó w Î L(G1). (a)
· hoặc S├G S2╞G2 w, vậy w là kết quả của suy dẫn S2╞ w trong G2, do đó w Î L(G2). (b)
Từ (a) và (b), ta thấy w Î L1È L2, hay L(G) Í L1È L2
b./ Chứng minh L1ÈL2 Í L(G): Giả sử wÎ L1ÈL2, khi đó ta cũng có hai khả năng: w Î L1 hoặc w Î L2 :
· Nếu w Î L1 = L(G1), khi đó ta có suy dẫn S1╞G1 w trong G1, do đó ta cũng có suy dẫn S ├G S1 ╞G1 w là một suy dẫn trong G (vì theo cách xây dựng G, mọi quy tắc và mọi ký hiệu trong G1 cũng đều thuộc G), như vậy w Î L(G).
· Nếu w Î L2 = L(G2), khi đó ta có suy dẫn S2╞G2 w trong G2, do đó ta cũng có suy dẫn S ├G S2 ╞G2 w là một suy dẫn trong G (vì theo cách xây dựng G, mọi quy tắc và mọi ký hiệu trong G2 cũng đều thuộc G), như vậy w Î L(G).
Vậy ta luôn luôn có w Î L(G), do đó: L1ÈL2 Í L(G). Tức là ta đã chứng minh được rằng L(G) = L1È L2.
Tương tự, để chứng minh tính đóng của lớp ngôn ngữ sinh bởi văn phạm đối với phép nhân ghép ngôn ngữ, ta xây dựng văn phạm G = sao cho L(G) = L(G1). L(G2) như sau:
S = S1È S2
Δ = Δ1È2 È{S}
P = P1È P2 È{S®S1S2}. Khi đó L(G) = L(G1).L(G2)
Để chứng minh tính đóng của lớp ngôn ngữ sinh bởi văn phạm đối với phép giao, ta xây dựng văn phạm G = sao cho L(G) = L(G1) Ç L(G2) như sau:
S = S1 Ç S2
Δ = Δ1È2 È Г1 È Г2 È{S}
Trong đó: Г1 = { | a Î S1} là tập các ký hiệu đối ngẫu của các ký hiệu trong , còn Г2= { | b } là tập các ký hiệu đối ngẫu của .
P = { S }
Trong đó là tập hợp các quy tắc nhận được từ , mà mọi ký hiệu a đều được thay đổi bởi ký hiệu đối ngẫu tương ứng của nó
VĂN PHẠM PHI NGỮ CẢNH
Suy dẫn phi ngữ cảnh
Các thí dụ về văn phạm phi ngữ cảnh
Theo tài liệu [4], tác giả: Nguyễn Thị Trúc Viên, có viết:
“Một văn phạm G = (V, T, S, P) được gọi là phi ngữ cảnh (context free) nếu mọi luật sinh trong P có dạng
A → x,
trong đó A ∈ V còn x ∈ (V ∪T)*.”
Từ đó ta có Định nghĩa II.1.
Định nghĩa II.1
Văn phạm phi ngữ cảnh là một văn phạm G = (∑, ∆, P, S) mà mọi sản xuất (hay quy tắc) của nó đều có dạng:
A→α, trong đó A∈∆ và α∈(∑∪∆)*
Trước hết ta hãy xem một số thí dụ về văn phạm phi ngữ cảnh.
Thí dụ 2.1:
Trong các sách văn phạm tiếng Việt ta gặp một số các quy tắc thành lập câu như sau:
Một câu (có thể) có dạng chủ ngữ vị ngữ
Một chủ ngữ (có thể) là một danh từ
Một chủ ngữ (có thể) là một đại từ
Một vị ngữ (có thể) là một động từ
Một danh từ (có thể) là mèo
Một danh từ (có thể) là bò
Một đại từ (có thể) là tôi
Một đại từ (có thể) là nó
Một động từ (có thể) là ăn
Một động từ (có thể) là nằm
Một động từ (có thể) là uống
Từ các quy tắc còn rất ít ỏi trên, ta có thể đã thành lập một số câu như sau:
Tôi nằm
Nó ăn
Bò uống
Mèo nằm
Trong các quy luật trên thì các từ gạch dưới đơ...1γqv (3-4)
Đối chiếu (3-1), (3-3) và (3-4) ta có:
β=β1γ= ξβ’1γ, vậy ξ là một tiền tố của β, nghĩa là β= ξβ’
trong đó β’= β’1γ. Từ đó (3-4) trở thành:
α’pw⟹i+1β’qv
Như thế tính chất được nghiệm đúng với i+1, và từ đó nó luôn đúng.
Hệ quả III.2
Cho một quá trình dịch chuyển αpw⟹*βqv.
Nếu α = ξα’ trong đó |α’|≥1 và suốt quá trình dịch chuyển, trừ trong hình trạng cuối cùng, độ dài của stack vẫn luôn lớn hơn | ξ |, thì:
β = ξβ’ với |β’|≥0
α’pw ⟹*β’pv
Định lý III.3 (phân rã các quá trình dịch chuyển)
Cho ξ, α∈Γ* và w∈∑*
Nếu ξαpw ⟹*q thì tồn tại hai xâu x,y ∈∑*, hai số nguyên k, l và một trạng thái r sao cho:
w=xy, i = k+l, αpx ⟹kr và ξry ⟹lq.
Chứng minh
Bởi định nghĩa các ôt ô mát đẩy xuống, độ dài stack nhiều nhất là giảm một trong các bước dịch chuyển. Như thế, trong quá trình dịch chuyển đã cho, độ dài stack ít nhất một lần đạt giá trị |ξ|. Giả sử γry là hình trạng đầu tiên đã gặp mà |γ|=|ξ| và giả sử k là độ dài của suy dẫn kể từ tình trạng đầu tiên ξαpw đến hình trạng γry đó. Đặt l=j-k. Vậy thì suy dẫn đã cho ξαpw⟹iq có thể viết thành:
ξαpw ⟹k γry⟹lq
Nếu k=0 thì ξαpw=γry, từ đó ta dễ dễ dàng suy ra: ξ=γ, α=0, p=r, w=y và như vậy tính chất trên được thỏa mãn một cách tầm thường.
Nếu k≠0, bởi Định lý III.2 áp dụng cho phần đầu của suy dẫn, ta có:
- ξ là một tiền tố của γ. Nhưng |γ|=|ξ, vì vậy γ|=ξ
- αpw ⟹k ry
Theo Định lý III.1 y là một hậu tố của w, nghĩa là ∃x: w=xy, và αpx⟹k ry. Phần thứ hai của suy dẫn có thể viết khác đi là: ξry⟹i q.
Vậy định lý đã được chứng minh.
Hệ quả III.3
Cho một số nguyên không âm n và α1, α2,, αn∈Γ*
Nếu αn αn-1 α1pw⟹i thì tồn tại các xâu trong ∑*, x1, x2,, xn các số nguyên k1, k2,, kn và các trạng thia q1, q2,, qn+1 sao cho
w = x1, x2,, xn,
i = k1, k2,, kn,
q = q1, q2,, qn+1
và với I từ 1 đến n ta có: αiqixi⟹k q+1.
Chứng minh
Kết quả trực tiếp của Định lý III.3
Tương đương giữa hai loại ô tô mát đẩy xuống thừa nhận theo stack rỗng và thừa nhận theo trạng thái cuối
Theo tài liệu [13], ta có Định lý III.4.
Định lý III.4
Cho L=L(M) trong đó M là một ô tô mát đẩy xuống. Tồn tại một ô tô mát đẩy xuống M’ sao cho L=N(M’).
Chứng minh
Giả sử M=(∑, Q, Γ,γ, q0, Z0, F).
Ô tô mát M’ có thêm hai trạng thái mới q’0và qe, cũng như một ký hiệu đáy stack mới X0, nghĩa là
M’=(∑, Q ∪ {q’0, qe},γ’,q’0, X0, ∅)
Ô tô mát M’ ban đầu đặt ký hiệu đáy stack Z0 của M lên trên ký hiệu đáy stack của nó X0, đồng thời chuyển sang trạng thái ddaaud q0 của M:
(3-5) γ’(X0, q0, ε)={(X0Z0q0)}
Sau đó ô tô mát M’ rập theo sự hoạt động của ô tô mát M cho đến khi gặp một trạng thái cuối:
(3-6) Với mọi q∈Q-F, a∈∑∪{ ε } và Z∈ Γ: δ’(Z,q,a)= δ(Z,q,a)
Lưu ý rằng ta dùng ký hiệu X0 làm đáy stack cho M’ (thay vì Z0) để đề phòng một trường hợp tế nhị là khi M đọc xong một xâu và dừng lại ở một trạng thái không thừa nhận, nhưng lại với stack rỗng. Như thế thì khi M’ rập theo quá trình ấy nó sẽ dừng lại với stack còn có X0 và vì stack không rỗng, nó sẽ không thừa nhận xâu đó đúng như M đã không thừa nhận xâu đó.
Khi ô tô mát M gặp một trạng thái cuối, ô tô mát M’ có hai lựa chọn là tiếp tục bắt chước M hoặc chuyển sang trạng thái qe, để từ đó bắt đầu các thao tác dốc sạch stack:
(3-7) Với mọi q∈F, α∈∑ và Z∈Γ:
δ’(Z,q,a)= δ(Z,q,a)
δ’(Z,q,ε)= δ(Z,q, ε)∪{(ε,qe)}
(3-8) Với mọi q∈F:
δ'(X0,q, ε)={(ε,qe)}
Khi ô tô mát M’ ở trạng thái qe, nó dỡ hết mọi ký hiệu còn lại trong stack:
(3-9)Với mọi Z∈Γ∪{X0}:
δ'(Z,q, ε)={(ε,qe)}
Các quy tắc (3-5), (3-6), (3-7), (3-8) và (3-9) đinh nghĩa một cách đầy đủ hàm dịch chuyển δ'.
Giả sử x∈L(M), thế thì Z0q0x⟹*M γq với q∈F và γ∈ Γ*.
Ô tô mát M’ làm việc trên xâu x như sau:
Bởi quy tắc (3-5): X0q’0x⟹*M’X0Z0q0x
Bởi quy tắc (3-6), M’ bắt chước hoạt động của M và cho:
X0q’0x⟹*M’X0Z0γq
Bởi các quy tắc (3-7) và (3-8) còn tiếp tục làm việc và cho:
X0γq⟹*M’qe
Vậy thì X0q’0x⟹*M’qe nghĩa là x∈N(M').
Ngược lại, cũng bằng cách tương tự, ta có thể chứng minh rằng nếu x ∈N(M') thì:
x∈L(M).
Định lý III.5
Cho L=N(M) trong đó M là ô tô mát đẩy xuống. Tồn tại một ô tô mát đẩy xuống M’ sao cho L=L(M’).
Chứng minh
Giả sử M=(∑, Q, Γ,δ, q0, Z0, ∅)
Ô tô mát M’ có thêm hai trạng thái mới là q’0 và qf, cũng như một ký hiệu đáy stack mới X0.
M’=(∑, Q ∪ {q’0, qf},Γ∪{X0}, δ', q’0, X0, {qf})
Ô tô mát M’ đầu tiên đặt ký hiệu đáy stack Z0 của M lên trên ký hiệu đáy stack X0 của nó, đồng thời chuyển qua trạng thái q0:
(3-10) δ'(X0, q’0,ε)={(X0, Z0,q0)}
Tiếp đó ô tô mát M’ rập theo hoạt động của M:
(3-11) Với mọi q∈Q: δ'(X0, q’0,ε)={(ε,qf)}
Các quy tắc (3-10), (3-11) đã định nghĩa đầy đủ δ'.
Việc chứng minh L(M’)=N(M) được thực hiện tương tự như ở Định lý III.4
Tương đương giữa ô tô mát đẩy xuống và văn phạm phi ngữ cảnh
Chúng ta sẽ chứng minh rằng lớp các ngôn ngữ phi ngữ cảnh là trùng với lớp các ngôn ngữ được các ô tô mát đẩy xuống thừa nhận.
Từ văn phạm phi ngữ cảnh đến ô tô mát đẩy xuống
Theo tài liệu [7], tác giả Phan Huy Khánh, có viết “Một ngôn ngữ là phi ngữ cảnh nếu và chỉ nếu ngôn ngữ đó đước thừa nhận bởi một ôtômat đẩy xuống.”.
Từ đó ta có Định lý III.6.
Định lý III.6
Nếu L là một ngôn ngữ phi ngữ cảnh, thì tồn tại một ô tô mát đẩy xuống M mà L=N(M)
Chứng minh
Giả sử G=(∑, ∆, S, P) là căn phạm phi ngữ cảnh sản sinh L. Ô tô mát M mà L=N(M) chỉ có một trạng thái q:
M=(∑,p, ∑∪∆, δ, q, S, ∅)
Vậy M bắt đầu quá trình đoán nhận với duy nhất một ký hiệu đầu S của văn phạm ở trong stack.
Nếu đỉnh là một ký hiệu không thể kết thúc A, ô tô mát M mô phỏng sự viết lại bởi một quy tắc A→α của văn phạm G, bằng cách này thay A bởi α nhưng theo trật tự đảo ngược (αR) để cho ký hiệu đầu tiên của α trở thành đỉnh stack mới:
(3-12) Với mọi A∈∆: δA,q,ε={(αR, q) | A→α∈P}
Nếu đỉnh stack là một ký hiệu kết thúc a, thì ô tô mát đọ nó với ký hiệu đọc được ở xâu vào, và nếu bằng nhau thì dỡ a khỏi stack:
(3-13) Với mọi a∈∑: δa,q,a= {(ε,q)}
Các quy tắc (3-12) và (3-13) đã định nghĩa đầy đủ ở hàm dịch chuyển δ.
Các quy tắc đó cho phép ô tô mát Mmoo phỏng một suy dẫn trái S⟹*x đối với xâu vào x. Trong một suy dẫn như vậy thì mỗi dạng câu ω (tức là một xâu trung gian trong suy dẫn) luôn có dạng ω=ưγ với u∈∑*, γ∈∆. (∑∪∆)*∪{ε} và u là một tiền tố của xâu x, nghĩa là ∃y:x=uy. Giả sử γ=Aγ’, A sẽ được thay thế bởi nhờ áp dụng một quy tắc A→α. Bây giờ các ký hiệu kết thúc đứng đầu xâu αγ’ chắc chắn phải là các ký hiệu đứng đầu y. Lưu ý rằng ô tô mát M ghi nhớ phần γ của dạng câu ω trong stack của nó, nhưng theo chiều ngược, bởi vì stack (trong xâu hình trạng) luôn luôn hướng sang phải, trong khi xâu γ tiến theo chiều LIFO lại từ bên trái
1) Hãy chứng minh L⊆N(M)
Trước hết ta chứng minh bằng quy nạp I trên mệnh đề sau đây:
Nếu S⟹i Guγ với u∈∑* và γ∈∆∪∑∪∆*∪{ε}, thì Squ⟹*MγRq
Với i=0, mệnh đề đúng (với u=ε,γ=S).
Giả thiết là mệnh đề đã nghiệm đúng cho đến i và xét suy dẫn có độ dài i+1:
S⟹i GuAβ⟹G uvαβ,
Trong đó A→vα∈P và u,v∈∑*, β∈(∑∪∆)*, α∈(∑∪∆)*∪{ε}.
Từ giả thiết quy nạp ta có:
Squ⟹*MβRAq
Bởi tính chất hợp thành các suy dẫn ta có:
Squv⟹*MβRAqv
Vì rằng A→vα∈P, cho nên (αRvR,q)∈δ(A,q,ε).
Từ đó: Squv⟹*MβRαRvRqv⟹*MβRαRq,
Là điều cho phép kết thúc quy nạp.
Áp dụng mệnh đề vừa chứng minh vào trường hợp riêng ta có:
Nếu S⟹*Gx với x∈∑*, thì Sqx⟹*Mq
Vậy L(G)⊆N(M).
2) Bây giờ ta chứng minh N(M)⊆L
Trước hết ta chứng minh quy nạp trên i mệnh đề sau:
Nếu Squ⟹iMγRq với u∈∑* và γ∈(∆∪ε)* thì S⟹*G uγ
Với i=0, mệnh đề là đúng (u=ε, γ=S).
Giả thiết mệnh đề đã nghiệm đúng đối với mọi quá trình dịch chuyển của M có chiều dài cho đến i.
Giả sử Squ⟹iMγRq là một quá trình dịch chuyển có độ dài là i+1.
Hai trường hợp có thể là:
Trường hợp một: Dịch chuyển cuối cùng thuộc loại (3-13):
Squ⟹iMγRaqa⟹MγRq, trong đó a∈∑
Bởi định lý III.1 ta có:
u=u’a và
Squ’⟹iMγRaq
Do giả thiết quy nạp, bấy giờ ta có giả thiết trong G:
S⟹*G u'aγ=uγ
- Trường hợp hai: Dịch chuyển cuối cùng thuộc loại (3-12):
Squ⟹iMγRAq⟹MγRαRq = γRq, trong đó γ=αβ và A→α∈P.
Do giả thiết quy nạp, ta có trong G:
S⟹*GuAβ.
Sử dụng tiếp quy tắc A→α, ta thu được:
S⟹*GuAβ⟹uAβ⟹uγ,
là điều cho phép kết thúc quy nạp.
Áp dụng mệnh đề vừa chứng minh vào trường hợp riêng, ta có:
Nếu Sqx⟹*Mq thì S⟹*G x
Vậy N(M)⊆L(G).
Từ ô tô mát đẩy xuống đến văn phạm phi ngữ cảnh
Theo tài liệu [11], ta có Định lý III.7.
Định lý III.7
Cho một L=N(M) với M là một ô tô mát đẩy xuống, thế thì L là một ngôn ngữ phi ngữ cảnh.
Chứng minh
Giả sử M=(∑,Q,Γ, δ,q0,Z0,∅) là ô tô mát đẩy xuống thừa nhận L theo stack rỗng.
Ta lập một văn phạm phi ngữ cảnh G=(∑,∆,P,S) sao cho các suy dẫn trái của nó mô phỏng sự hoạt động của M.
Đặt E={[Z,p,q]|Z∈Γ và p,q∈Q} và ∆=E∪{S} trong đó S∉E.
Tập các quy tắc P được thành lập như sau:
(3-14) Với mọi q∈Q, S→[Z0, q0, q] là một quy tắc,
(3-15) Nếu (XkX2X1,p)∈δ(Z,q,a) trong đó k≥1 và X1,X2,,Xk∈Γ thì với mọi s1, s2,,sk∈Q
[Z,q,sk] →a[X1,p,s1] [X2,s1,s2][ Xk, sk-1,sk] là một quy tắc.
(3-16) Nếu (ε,r)∈δ(Z,q,a) thì [Z,q,a] →a là một quy tắc
Văn phạm G được thành lập như trên là nhằm tới tính chất sau:
Mỗi suy dẫn trái sinh ra một xâux∈∑* trong G mô phỏng một quá trình dịch chuyển của ô tô mát M thừa nhận xâu x. Mỗi dạng câu trong suy dẫn đó có dạng uγ, trong đó u∈∑*, γ∈∆*; dạng câu đó phản ánh một tình huống của ô tô mát M: u là phần đã đọc của xâu x và xâu γ R là nội dung stack.
Để chứng minh rằng L(G)⊆N(M), trước hết ta chứng minh bằng quy nạp trên I mệnh đề sau đây:
(a) Nếu [Z,q,r] ⟹iG w∈∑* thì Zqw⟹*M r
Với i=0 mệnh đề đúng, vì [Z,q,r]→w là một quy tắc (với w∑∈∪{ε}), từ đó bởi các thành phần lập (3-16), (ε,r)∈δ(Z,q,w), vậy Zqw⟹*M r
Giả sử mệnh đề đã nghiệm đúng với mọi suy dẫn có độ dài cho đến I, ta chứng minh nó cũng đúng với một suy dẫn có độ dài i+1, trong đó i≥1.
Dựa vào định nghĩa của các quy tắc, thì một số suy dẫn có độ dài i+1 với (i≥1) có thể được viết thành như sau:
[Z,q,r] ⟹Ga[X1,s0,s1] [X2,s1,s2][ Xk, sk-1,sk] ⟹iGw
Trong đó a∈∑∪{ε}, k≥1, sk=r và (XkX2X1,s0)∈δ(Z,q,a)
Theo tính chất phân rã các suy dẫn phi ngữ cảnh (Định lý III.1) tồn tại các số nguyên n1,,nk và các xâu w1,,wk sao cho w=aw1wn, i=n1++nk và với j từ 1 tới k:
[xj,sj-1,sj] ⟹nj Gwj.
Vì nj≤i, ta có thể vận dụng giả thiết quy nạp cho các suy dẫn cho các suy dẫn có độ dài nj và bởi thế:
(b) Với j từ 1 tới k: Xjsj-1wj⟹*Msj
Lại vì có (XkX2X1,s0)∈δ(Z,q,a) ta có:
(c) Zqaw1wk⟹MXkX2X1s0w1w2wk
Tổ hợp (b) và (c), nhờ cào tính chất hợp thành các suy dẫn trong hệ viết lại, ta thu được Zqw⟹*Mr
Vậy tính chất (a) là đúng với mọi số nguyên i.
Cho w∈L(G). Bởi định nghĩa của các quy tắc trong G, tồn tại một trạng thái q và một số nguyên i sao cho: S⟹G[Z0, q0, q] ⟹iw
Bởi tính chất (a) ta có: Z0q0w⟹*Mq
Vậy w∈N(M).
N(M)⊆L(G): Chứng minh xin được chuyển thành bài tập. Để thực hiện chứng minh rằng, ta cần sử dụng tính chất phân rã các quá trình dịch chuyển.
Thí dụ 3.2:
Cho M=({a,b},{q0, q1},{a,Z},δ,q0,Z,∅). Hàm dịch chuyển δ cho như sau:
δ(Z,q0,a)={(Za,q0)}
δ(a,q1,b)={(ε,q1)}
δ(a,q0,a)={(aa,q0)}
δ(a,q1,ε)={(ε,q1)}
δ(a,q0,b)={(ε,q1)}
δ(Z,q1,ε)={(ε,q1)}
Dễ thấy rằng N(M)={aibj| 1≤j≤i}
Ta thành lập văn phạm tương đương với ô tô mát theo phương pháp đưa ra trong định lý III.7.
Ta chỉ cần lập các quy tắc cho các ký hiệu là đến được từ S.
S→[Z,q0,q0] | [Z,q0,q1]
Từ (Za, q0)∈δ(Z,q0,a) ta thu được:
[Z,q0,q0]→ a[a, q0, q0] [Z,q0,q0] | a[a, q0, q1] [Z,q1,q0]
[Z,q0,q1]→ a[a, q0, q0] [Z,q0,q1] | a[a, q0, q1] [Z,q1,q1]
Từ (aa,q0)∈δ(a,q0,a) ta thu được:
[a,q0,q0]→ a[a, q0, q0] [a,q0,q0] | a[a, q0, q1] [a,q1,q0]
[a,q0,q1]→ a[a, q0, q0] [a,q0,q1] | a[a, q0, q1] [a,q1,q1]
Ta còn nhận được:
[a,q0,q1]→ b Vì (ε,q1)∈δ(a,q0,b)
[a,q1,q1]→ b|ε Vì (ε,q1)∈δ(a,q1,b)∩δ(a,q1,ε)
[Z,q1,q1]→ ε Vì (ε,q1)∈δ(Z,q1,ε)
Nếu loại bỏ các ký hiệu vô sinh ta được văn phạm:
S→[Z,q0,q1]
[Z,q0,q1]→ a[a, q0, q1] [Z,q1,q1]
[a,q0,q1]→ a[a, q0, q1] [a,q1,q1]
[a,q0,q1]→ b
[a,q1,q1]→ b|ε
[Z,q1,q1]→ ε
TỔNG QUAN VỀ TRÌNH BIÊN DỊCH
Ngôn ngữ lập trình
Mở đầu
Từ ngàn xưa con người muốn giao tiếp với nhau phải dùng ngôn ngữ. Vậy người giao tiếp với máy tính tất nhiên cũng thông qua ngôn ngữ. Con người muốn máy tính thực hiện công việc, phải viết các yêu cầu đưa cho máy bằng ngôn ngữ máy hiểu được. Việc viết các yêu cầu, ta gọi là lập trình (programming). Ngôn ngữ dùng để lập trình được gọi là ngôn ngữ lập trình (programming language).
Viết chương trình để giải quyết vấn đề sẽ dễ dàng và tự nhiên hơn nếu ngôn ngữ lập trình gần với vấn đề cần giải quyết. Có nghĩa là ngôn ngữ phải chứa đựng các cấu trúc thuật ngữ, phần tử dùng để miêu tả vấn đề và không phụ thuộc vào máy tính cụ thể.
Các ngôn ngữ lập trình có tính chất như trên được gọi là ngôn ngữ cấp cao. Nhưng máy tính chỉ hiểu, chỉ chấp nhận ngôn ngữ cấp thấp riêng của mình, đó là chuỗi các số 0 và 1, chuỗi số đó lại không gần gũi chút nào đối với con người.
Việc phân cấp ngôn ngữ lập trình được dựa trên cơ sở của tính không phụ thuộc với máy tính ngày càng cao của các ngôn ngữ.
Phân loại
Ngôn ngữ máy (machine language),
Hợp ngữ (assembly language),
Ngôn ngữ cấp cao (higher-level language).
Bởi vì máy tính chỉ có thể hiểu ngôn ngữ máy cho nên một chương trình viết trong ngôn ngữ cấp cao cuối cùng rồi cũng được dịch sang ngôn ngữ máy. Công cụ thực hiện việc dịch đó được gọi là chương trình dịch (translator).
Chương trình dịch được chia làm hai loại: trình biên dịch (compiler) và trình thông dịch (interpreter).
Trình biên dịch: chuyển một chương trình viết trong ngôn ngữ cấp cao chương trình nguồn sang chương trình trong ngôn ngữ cấp cao khác hoặc ngôn ngữ máy - chương trình đích.
Thời gian chuyển một chương trình nguồn sang chương trình đích được gọi là thời gian dịch (compile time).
Thời gian mà chương trình đích được thực thi được gọi là thời gian thực thi (run time).
Hình 4.1 Sơ đồ trình biên dịch
Như vậy, đối với trình biên dịch, chương trình nguồn và dữ liệu được xử lý trong thời gian khác nhau, đó là thời gian dịch và thời gian thực thi.
Trình thông dịch: quá trình xử lý dạng bên trong của chương trình nguồn và dữ liệu cùng một thời gian.
Hình 4.2 Sơ đồ trình thông dịch
Một số trình thông dịch làm việc như sau: phân tích từng phát biểu và thực thi luôn.
Hiện nay trình thông dịch đa phần áp dụng kỹ thuật của trình biên dịch là biên dịch chương trình nguồn sang dạng mã trung gian. Từ mã trung gian sẽ được thực thi bằng trình thông dịch.
Cú pháp và ngữ nghĩa
Để tiện lợi hơn trong đặc tả và hiện thực sự biên dịch, ta coi sự biên dịch bao gồm hai phép chiếu đơn giản hơn.
Thứ nhất là phép ánh xạ cú pháp (syntactic mapping), nó ánh xạ một chương trình viết trong ngôn ngữ nguồn sang cấu trúc là đối số của phép ánh xạ tiếp theo, đó là phép ánh xạ ngữ nghĩa (semantic mapping). Cấu trúc của phép ánh xạ cú pháp là cây cú pháp (syntactic tree). Sau đây là thí dụ cây cú pháp được xây dựng như thế nào trên chuỗi nhập vào là một câu tiếng Anh. Mỗi câu tiếng Anh được bẻ ra thành những ký hiệu cú pháp nhờ vào các luật văn phạm.
Thí dụ 4.1:
Chuỗi ký tự a + b * c có cây cú pháp A sau:
b
Hình 4.3 Cây cú pháp A
Qua thí dụ trên ta thấy rằng với mỗi câu của ngôn ngữ đêu tồn tại cây cú pháp của nó. Quá trình tìm ra cây cú pháp của một câu, được gọi là quá trình phân tích cú pháp câu đó (syntactic analysis parsing). Quá trình phân tích cú pháp được thực hiện dựa trên cơ sở các luật cú pháp của ngôn ngữ (đó chính là các quy tắc hay luật sinh). Cú pháp của ngôn ngữ là tập luật sinh, nó cung cấp cho ta mối quan hệ giữa mỗi câu của ngôn ngữ với cấu trúc cú pháp.
Trình biên dịch
Định nghĩa
Theo tài liệu [6], tác giả: Nguyễn Gia Định, có viết “Trình biên dịch là chương trình dùng để đọc một chương trình được viết trong một ngôn ngữ lập trình được gọi là ngôn ngữ nguồn (source language) và dịch chương trình đó sang chương trình tương ứng trong ngôn ngữ khác, ngôn ngữ đích ”
Từ đó ta có Định nghĩa IV.1.
Định nghĩa IV.1
Trình biên dịch là chương trình dùng để đọc một chương trình được viết trong một ngôn ngữ lập trình được gọi là ngôn ngữ nguồn (source language) và dịch chương trình đó sang chương trình tương ứng trong ngôn ngữ khác, ngôn ngữ đích (target language).
Như vậy ta sẽ có rất nhiều trình biên dịch, vì ta có hàng ngàn các ngôn ngữ nguồn từ những ngôn ngữ lập trình họ cổ điển (Fortran, Pascal) đến các ngôn ngữ đặc biệt đã xuất hiện rất nhiều trong lĩnh vực ứng dụng máy tính.
Các phần của trình biên dịch
Chương trình nguồn trong ngôn ngữ lập trình không gì khác là chuỗi các ký tự. Trình biên dịch có nhiệm vụ chuyển chuỗi ký tự này sang chuỗi ký tự khác - đó là mã đối tượng. Quá trình này bao gồm các quá trình nhỏ hơn và được đặt tên như sau:
Phân tích từ vựng.
Bảng danh biểu và thông báo lỗi.
Phân tích cú pháp.
Phân tích ngữ nghĩa.
Sinh mã trung gian.
Tối ưu mã trung gian.
Sinh mã đối tượng.
Đối với một trình biên dịch tồn tại trong thực tế, thứ tự các quá trình nhỏ có thể hơi khác so với thứ tự ở trên. Có thể một số quá trình nhỏ kết hợp lại với nhau thành một quá trình duy nhất. Trình biên dịch phải có khả năng nhận biết chuỗi nhập vào có phải là một chương trình hợp lệ cú pháp không. Nếu không, trình biên dịch phải thông báo lỗi.
Phân tích từ vựng (lexical analysis)
Giai đoạn phân tích từ vựng là giai đoạn đầu của quá trình biên dịch. Dòng nhập vào trình biên dịch là chuỗi các ký tự cho phép của một ngôn ngữ lập trình, cũng là chuỗi nhập vào bộ phân tích từ vựng.
Chẳng hạn, đối với ngôn ngữ Pascal, các ký tự alphabet là các ký tự sau: AZ, az, $ @ 0 1 29 dấu trống - + = := / * ( ), & >=
Trong chương trình nguồn, sự kết hợp một số ký tự alphabet sẽ tạo nên một thực thể của ngôn ngữ. Chẳng hạn, BEGIN là sự kết hợp 5 ký tự B, E, G, I, N tạo nên thực thể là từ khoá BEGIN.
Các thực thể:
Ký hiệu trống, dấu tab, dấu xuống hàng,
Từ khoá: begin, end, goto, while, do, integer,
Chuỗi các ký tự số tượng trưng cho hằng số,
Danh biểu, dùng để đặt tên cho các biến, hàm thủ tục, nhãn,
Các ký hiệu đặc biệt: +, -, /, *, :=, ;, =, >, >=, <, <=, được gọi là các token.
Nhiệm vụ của bộ phân tích từ vựng là khi tiếp nhận chuỗi ký tự nhập phải biết nhóm các ký tự thành các thực thể cú pháp token. Token là ký hiệu kết thúc, mỗi token sẽ có một cấu trúc từ vựng. Cấu trúc từ vựng này là một cặp (loại token, dữ liệu), gồm hai thành phần:
Thành phần thứ nhất là phạm trù cú pháp: hằng, biến.
Thành phần thứ hai là con trỏ, chỉ đến thông tin của token, được cất giữ trong bảng, được gọi là bảng danh biểu (symbol table).
Với ngôn ngữ lập trình cho trước, số lượng loại token là hữu hạn. Tóm lại bộ phân tích từ vựng là bộ dịch (translator) mà đầu nhập của nó là chuỗi các ký tự, tượng trưng cho chương trình nguồn, đầu ra là các token. Dạng đầu ra này là đầu nhập của bộ phân tích cú pháp.
Thí dụ 4.2:
Chương trình nguồn là phát biểu gán trong ngôn ngữ Pascal:
COST:=(PRICE+TAX)*65
Bộ phân tích từ vựng có nhiệm vụ nhận biết: COST, PRICE, TAX là nhóm token thuộc loại danh biểu, 65 là token thuộc loại hằng. Các ký tự :=, (, ), +, * tự bản thân là token.
Giả sử tất cả các hằng, danh biểu là các token có loại và . Thành phần thứ hai là dữ liệu, ở đây chính là con trỏ chỉ đến vị trí của các token đó trong bảng danh biểu, chứa đựng trị từ vựng (lexeme) của token và các thuộc tính khác của token. Thành phần thứ nhất của token sẽ được dùng trong giai đoạn phân tích cú pháp. Thành phần thứ hai của token được dùng trong giai đoạn xử lý ngữ nghĩa và sinh mã đối tượng.
Bảng danh biểu và thông báo lỗi
Các token được bộ phân tích từ vựng nhận biết và các thông tin của từng token sẽ được lưu chứa trong bảng danh biểu. Xét phát biểu trong Thí dụ 4.2. Sau hi phát biểu được đi qua bộ phân tích từ vựng, bảng danh biểu sẽ chứa các thông tin sau:
Bảng IV.1 Bảng danh biểu 1
Chỉ số
Token
Lexeme
Các thông tin khác
1
id
COST
Biến thực
2
id
PRICE
Biến thực
3
id
TAX
Biến thực
4
num
65
Hằng số nguyên
Nếu bộ phân tích từ vựng nhận tiếp các chuỗi ký tự của chương trình nhập, để nhận dạng token, thì bảng danh biểu cũng thường xuyên được truy xuất. Hành vi truy xuất nhằm hai mục đích: nếu danh biểu vừa được nhận dạng đã được lưu chứa trong bảng danh biểu thì phần thứ hai của token là dữ liệu sẽ được cập nhật bằng chỉ số của danh biểu đó trong bảng danh biểu.
Thí dụ 4.3:
COST có chỉ số là 1 trong bảng danh biểu, COST lại xuất hiện trong chuỗi nhập sau:
y:=COST*2.0
Chuỗi xuất ra của bộ phân tích từ vựng là:
id5:=id1*num6 Û(id, 5):=(id, 1)*(num, 6)
Trong trường hợp này COST không cất vào bảng danh biểu nữa, nhưng bộ phân tích từ vựng sẽ đẩy ra token (id, 1), 1 là vị trí COST đã được cất trong bảng danh biểu trước đó.
Bảng danh biểu thường xuyên được truy xuất để thêm hoặc truy xuất các token, do đó phải thoả mãn hai điều kiện:
Thực hiện nhanh các thao tác thêm token, hoặc các thông tin của token.
Có khả năng truy xuất nhanh các thông tin của một token.
Phát hiện và thông báo lỗi
Ở mỗi giai đoạn của quá trình biên dịch một chương trình nguồn đều có thể có lỗi. Như vậy sau khi phát hiện một lỗi, trình biên dịch xem xét lỗi đó xem có tiếp tục quá trình dịch hay không. Tất nhiên, nếu một trình biên dịch mà ngay khi phát hiện lỗi đầu tiên đã dừng chương trình thì không hữu hiệu.
Trong giai đoạn phân tích từ vựng và cú pháp thường xuất hiện nhiều lỗi do trình biên dịch phát hiện. Trong lúc phân tích từ vựng, lỗi được phát hiện khi phần còn lại trên băng nhập không thể tạo nên token. Lỗi xảy ra khi bộ phân tích cú pháp không thể xây dựng cấu trúc cú pháp cho chuỗi token cho trước. Lỗi cũng có thể được phát hiện trong quá trình phân tích ngữ nghĩa, khi trình biên dịch kiểm tra kiểu dữ liệu của hai toán hạng thuộc một phép toán không phù hợp. Chẳng hạn, một toán hạng thuộc kiểu dãy, cộng với một toán hạng là tên của chương trình con.
Phân tích cú pháp (Syntactic analysis parsing)
Chuỗi xuất ra từ bộ phân tích từ vựng là các token có dạng (loại token, dữ liệu), sẽ là chuỗi nhập vào bộ phân tích cú pháp. Bộ phân tích cú pháp chỉ xét thành phần thứ nhất của token là loại token.
Sự phân tích cú pháp là một quá trình, trong quá trình này chuỗi các token sẽ được kiểm tra xem có thể được biểu diễn bằng cấu trúc cú pháp của ngôn ngữ lập trình cho trước hay không, Nếu tồn tại một cấu trúc cú pháp cho chuỗi nhập thì cấu trúc được sinh ra đó chính là kết quả của quá trình phân tích cú pháp. Ở giai đoạn sinh mã, cấu trúc cú pháp sẽ được xem xét để từ đó sinh ra mã cho chuỗi ký tự của chương trình nguồn.
Phân tích ngữ nghĩa
Sau giai đoạn phân tích cú pháp, cấu trúc cú pháp của chuỗi nhập sẽ được bộ phân tích ngữ nghĩa xử lý. Bộ phân tích ngữ nghĩa sẽ kiểm tra lỗi về ngữ nghĩa.. Một nhiệm vụ quan trọng mà bộ phân tích ngữ nghĩa thực hiện là kiểm tra loại dữ liệu. Dựa trên cây cú pháp, bộ phân tích ngữ nghĩa sẽ xử lý từng phép toán. Với mỗi phép toán, nó sẽ xét các toán hạng xem loại dữ liệu của chúng có cho phép để tham gia vào phép tính đó không (nói cách khác loại dữ liệu của các toán hạng trong phép toán cụ thể, có được ngôn ngữ lập trình định nghĩa không).
Thí dụ 4.4:
a + 1 với a là biến thuộc loại dữ liệu số thực, 1 là thuộc loại luận lý. Vậy phép cộng không thể thực hiện với hai toán hạng loại số thực và loại luận lý.
Hoặc: a + n với a là số thực và n là số nguyên
Khi kiểm tra thấy hai toán hạng của phép cộng một có trị thực, một có trị nguyên thì hầu hết các trình biên dịch sẽ chuyển trị của toán hạng n sang biểu thức số thực, cụ thể nếu n có trị là 10 thì trị 10 sẽ được chuyển sang trị thuộc loại thực 10.0 để cộng với trị của a.
Hình 4.4 Cây cú pháp B
Trị 65 sẽ được chuyển sang số thực. Cây cú pháp khi xử lý ngữ nghĩa sẽ có dạng như trên.
Sinh mã trung gian
Sau giai đoạn phân tích cú pháp và ngữ nghĩa, một số trình biên dịch đã tạo ra sự biểu diễn trung gian của chương trình nguồn. Sự biểu diễn trung gian của chương trình nguồn được hiểu như là chương trình của máy tính trừu tượng (abstract machine).
Ngôn ngữ được dùng cho máy trừu tượng là mã trung gian. Mã trung gian có hai đặc điểm quan trọng: dễ được sinh ra và dễ chuyển sang mã đối tượng của chương trình đích. Với Thí dụ 4.4, kết quả của giai đoạn sinh mã trung gian có dạng:
temp p1 := intoreal (trị số bằng 65)
temp p2 := id2 + id3 (4-1)
temp p3 := temp p2 * temp p1
id1 := temp p3
Tối ưu mã trung gian
Giai đoạn này sẽ thu giảm một số bước trong mã trung gian nhằm làm cho khi sinh ra mã đối tượng thì thời gian thực thi mã đối tượng sẽ ngắn hơn.
Bước sinh mã sẽ dùng cây cú pháp đã được xử lý ngữ nghĩa (đã qua bước phân tích ngữ nghĩa) để sinh mã trung gian.
Cách làm thông thường như sau:cứ ứng với nút là toán tử sẽ sinh ra mã trung gian như ở (4-1). Tuy vậy, có cách tốt hơn là với (4-1) chỉ cần hai mã trung gian mà thôi.
temp p1 := id2 + id3 (4-2)
id1 := temp p1 + 65.0
Việc thu giảm như trên sẽ được thực hiện ở bước tối ưu mã. Bước chuyển số nguyên sang số thực sẽ được thực hiện ngay trong thời gian dịch, do đó phép toán intoreal sẽ được bỏ đi. Xem lại (4-1), ta thấy ở mã thứ tư id1 := temp p3, với temp p3 chỉ dùng có một lần là gán trị vào id1, do đó có thể ghép mã thứ 3 và thứ 4 thành mã thứ 2 của (4-2).
Còn rất nhiều trường hợp khác mà trình biên dịch thực hiện tối ưu mã. Ở đây một vấn đề nảy sinh là thực hiện tối ưu mã trong thời gian biên dịch sẽ làm thời gian dịch tăng lên trong giai đoạn này. Tuy nhiên một số trường hợp tối ưu mã cho phép nếu thời gian thực thi của chương trình đích được rút ngắn mà không làm sự biên dịch quá lâu.
Sinh mã đối tượng
Giai đoạn cuối của trình biên dịch là sinh mã đối tượng. Mã đối tượng có thể là mã máy định vị lại địa chỉ hoặc mã hợp ngữ.
Thí dụ 4.5:
Ta sử dụng hai thanh ghi 1 và 2, để dịch mã trung gian (4-2) sang mã hợp ngữ:
movF id2, R1 movF id3, R2
addF R2, R1 (4-3)
mulF # 65.0, R1
movF R1, id1
Lưu ý rằng movF, addF, mulF là phép tính với số thực. Chỉ thị đầu thực hiện chuyển trị từ vị trí nhớ có tên PRICE, thuộc loại token id2 vào thanh ghi R1. Chỉ thị thứ hai thực hiện chuyển trị ở vị trí nhớ có tên TAX thuộc loại token id3 vào thanh ghi R2. Chỉ thị thứ ba thực hiện phép cộng nội dung hai thanh ghi R1 và R2, kết quả phép toán được cất trong R1. Chỉ thị thứ tư thực hiện phép nhân hằng có trị số thực 65.0 với trị nằm trong thanh ghi R1. Chỉ thị cuối cùng chuyển nội dung trong thanh ghi R1 vào vị trí nhớ có tên COST thuộc loại token id1.
Ứng dụng của ngôn ngữ hình thức và ôtômat với trình biên dịch
Các phần trước của chương này ta có nói chuỗi ký tự nhập vào trình biên dịch là văn bản của chương trình nguồn. Đúng vậy, song văn bản đó lại có thể là sản phẩm đầu ra của một hoặc nhiều bộ tiền xử lý (preprocessor) và sản phẩm đầu ra của trình biên dịch có thể lại tiếp tục được xử lý trước khi trở thành mã máy của máy tính thật. Trong phần này ta sẽ nói tới các mối liên quan đó.
Bộ tiền xử lý
Bộ tiền xử lý sẽ tạo ra chuỗi nhập vào trình biên dịch. Bộ tiền xử lý thực hiện các chức năng sau:
Xử lý macro (macro processing). Bộ tiền xử lý có thể cho phép người sử dụng định nghĩa các macro. Macro được hiểu là cách viết ngắn gọn cho cấu trúc dài hơn.
Chêm tập tin (file inclusion). Bộ tiền xử lý có thể “nhét” các tập tin vào chương trình văn bản. Chẳng hạn, tiền xử lý ngôn ngữ C sẽ “nhét” nội dung của tập tin vào thay thế cho phát biểu # include khi nó xử lý một tập tin có chứa phát biểu trên.
Bộ xử lý hoà hợp (Rational processor). Bộ tiền xử lý loại này sẽ tạo nên sự hoà hợp giữa ngôn ngữ cổ điển với những cấu trúc điều khiển, cấu trúc dữ liệu hiện đại hơn.. Chẳng hạn, bộ tiền xử lý giúp cho người sư dụng có thể dùng các phát biểu có cấu trúc như while, if trong ngôn ngữ lập trình, mà tự bản thân ngôn ngữ đó không có các phát biểu trên. Thực tế các phát biểu while, if chính là các macro, khi người sử dụng viết một chương trình trong ngôn ngữ cổ điển có dùng tới hai loại phát biểu có cấu trúc trên và cần biên dịch ra ngôn ngữ máy thì bộ tiền xử lý sẽ làm việc trước. Tất cả nơi nào có hai phát biểu while, if sẽ được thay thế bởi chuỗi các phát biểu mà ngôn ngữ lập trình cổ điển có.
Mở rộng ngôn ngữ (language extension). Bộ tiền xử lý tăng khả năng cho ngôn ngữ bằng một số các macro nội tại của nó. Thí dụ ngôn ngữ Equel là ngôn ngữ hỏi đáp với cơ sở dữ liệu được nhúng vào ngôn ngữ C. Các phát biểu được bắt đầu bằng hai dấu # # ở trong C được bộ tiền xử lý, xử lý, là các phát biểu truy xuất cơ sở dữ liệu, không liên quan đến C, được dịch thành các phát biểu gọi thủ tục, sẽ gọi các trình con đặc nhiệm trong mã máy để thực hiện việc truy xuất cơ sở dữ liệu.
Bây giờ ta sẽ nói kỹ hơn về bộ xử lý macro. Bộ xử lý này thường làm việc với hai loại phát biểu: định nghĩa macro và sử dụng macro.
Định nghĩa macro bao gồm: từ khoá define hoặc macro, tiếp theo là tên macro. Theo sau là thân (body) của macro. Chẳng hạn, \define {}.
Thông thường bộ xử lý macro cho phép các thông số hình thức (formal parameter) trong định nghĩa, chúng là các ký hiệu sẽ bị thay thế bởi các trị (chuỗi các ký tự) sau này khi macro được dùng.
Phát biểu dùng macro bao gồm: tên macro và các thông số thực (actual parameter), là trị của các thông số hình thức trong thân của macro.
Thí dụ 4.5:
Hệ thóng đánh máy typesetting có phương tiện macro với phát biểu định nghĩa macro như sau:
\define {}
: tên macro
: danh sách thông số hình thức
: thân macro
Macro định nghĩa ve sự trích dẫn của tạp chí ACM như sau:
\define\JACM #1; #2; #3
{{\S1 J.ACM}{\bf #1}: #2, pp. #3}
Tên macro là \JACM. Các thông số hình thức là #1, #2, #3 được ngăn cách nhau bởi dấu ‘;’ và được kết thúc bằng dấu ‘.’.
Khi dùng macro, người sử dụng sẽ viết như sau: \JACM 17; 4; 715 – 728 sẽ được hiểu như sau: J.ACM 17: 4, pp. 715 – 728.
Trình biên dịch hợp ngữ
Một số trình biên dịch cho sản phẩm ở đầu ra là mã hợp ngữ, chuỗi mã hợp ngữ này sẽ được đưa sang trình biên dịch hợp ngữ xử lý tiếp. Một số trình biên dịch khác thực hiện luôn công việc của assembler, nghĩa là nó dịch ra luôn mã máy khả định vị (relocatable machine code), mã máy sẽ được chuyển trực tiếp đến bộ phận “loader/link editor.
Mã hợp ngữ là phiên bản gợi nhớ của mã máy, trong đó các tên sẽ được dùng thay thế cho các mã nhị phân của các tác vụ và tên cũng được đại diện cho các địa chỉ của vị trí nhớ. Chẳng hạn, chuỗi chỉ thị trong mã hợp ngữ của phát biểu gán b := a+2.
mov a, R1
add #2, R1 (4-4)
mov R1, b
Ba c
Các file đính kèm theo tài liệu này:
- chuyen_de_nghien_cuu_ngon_ngu_hinh_thuc_van_pham_phi_ngu_can.docx