Tải bản đầy đủ (.doc) (15 trang)

NGÔN NGỮ PHI NGỮ CẢNH

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (151.06 KB, 15 trang )

Chương 5
NGÔN NGỮ PHI NGỮ CẢNH
Trong những chương trước, chúng ta đã nhận thấy rằng không phải tất cả ngôn
ngữ đều là chính qui. Chỉ vì các ngôn ngữ chính qui có hiệu quả trong việc mô tả
những trường hợp đơn giản, nên người ta không quan tâm đến những ví dụ về ngôn
ngữ không chính qui. Tuy nhiên trong ngôn ngữ lập trình, nó đòi hỏi phải mở rộng họ
ngôn ngữ chính qui. Điều này dẫn đến ngôn ngữ và văn phạm phi ngữ cảnh .

Chúng ta bắt đầu chương này với đònh nghóa văn phạm và ngôn ngữ phi ngữ
cảnh, bằng những ví dụ đơn giản. Tiếp đến, chúng ta tìm hiểu các vấn đề có liên
quan. Cụ thể chúng ta hỏi rằng nếu cho trước một chuỗi, thì chuỗi đó được sinh ra từ
văn phạm nào đã biết. Giải thích một câu mà thông qua một văn phạm thì rất quen
thuộc với chúng ta. Trong ngôn ngữ tự nhiên, ta gọi việc đó là phân tích cú pháp
(Parsing). Parsing là phương pháp mô tả cấu trúc một câu. Phân tích cú pháp là điều
tối cần trong nhiều lãnh vực của máy tính như : dòch từ một ngôn ngữ này sang một
ngôn ngữ khác, xây dựng trình biên dòch, trình thông dòch và những chương trình dòch
khác.
Chủ đề của ngôn ngữ phi ngữ cảnh là chủ đề quan trọng nhất của lý thuyết
ngôn ngữ hình thức, vì nó áp dụng cho ngôn ngữ lập trình. Ngôn ngữ lập trình có
nhiều chức năng và liên kết với những dạng của ngôn ngữ phi ngữ cảnh. Lý thuyết
ngôn ngữ hình thức cho ta những áp dụng quan trọng trong việc thiết kế ngôn ngữ lập
trình cũng như viết những trình biên dòch .
5.1 VĂN PHẠM PHI NGỮ CẢNH
Các luật sinh trong văn phạm chính quy bò hạn chế cả vế trái và vế phải. Để
tạo ra những văn phạm có nhiều tính năng hơn, chúng ta phải lược bỏ một số những
hạn chế này. Bằng các giữ lại hạn chế bên vế trái, nhưng cho phép tự do bên vế
phải. Khi đó, chúng ta được văn phạm phi ngữ cảnh.
65
Đònh nghóa 5.1

Một văn phạm G = (V, T, S, P) được gọi là phi ngữ cảnh nếu tất cả luật sinh


trong P có dạng
A-> x
Với A

V và x

(V U T)
*
.
Một ngôn ngữ L được gọi là phi ngữ cảnh nếu và chỉ nếu có một văn phạm
phi ngữ cảnh G sao cho L = L (G).

Mọi văn phạm chính qui là phi ngữ cảnh, do vậy một ngôn ngữ chính qui cũng
là phi ngữ cảnh. Nhưng như chúng ta đã biết {a
n
b
n
} là môt ngôn ngữ không chính qui.
Ngôn ngữ này được sinh ra từ một văn phạm phi ngữ cảnh, vì thế ta thấy rằng họ của
những ngôn ngữ chính qui là một tập con đúng của họ ngôn ngữ phi ngữ cảnh, nghóa
là văn phạm chính qui thụộc về ngôn ngữ phi ngữ cảnh.

Các ví dụ của ngôn ngữ phi ngữ cảnh
Ví dụ 5.1
Cho ngôn ngữ G = ({S},{a,b},S, P ), với các luật sinh
S -> aSa
S -> bSb
S -> λ,
Là phi ngữ cảnh . Một dẫn xuất cho văn phạm trên là
S => aSa => aaSaa => aabSbaa => aabbaa.

Điều này dễ thấy rằng
L(G) = {ww
R
, w

(a,b)
*
}.
Ngôn ngữ này là phi ngữ cảnh, nhưng không chính qui
Ví dụ 5.2
Cho văn phạm G, với các luật sinh
S -> abB
A -> aaBb
B -> bbAa
A -> λ
là phi ngữ cảnh.
66
Hãy chỉ ra rằng L(G) = {ab(bbaa)
n
bba(ba)
n
: n

0} (coi như bài tập)
Cả hai ví dụ ở trên đều bao gồm các văn phạm mà không chỉ là phi ngữ
cảnh, mà còn tuyến tính, những văn phạm chính qui và tuyến tính thì rõ ràng là
phi ngữ cảnh, nhưng văn phạm phi ngữ cảnh không nhất thiết phải tuyến tính.
Ví dụ 5.3
Ngôn ngữ L = {a
n

b
m
: m

n }
Là phi ngữ cảnh.
Để thấy được vấn đề này, chúng ta tìm một văn phạm phi ngữ cảnh cho
ngôn ngữ đó. Trườâng hợp m = n đã được giải quyết trong ví dụ 1.5 và chúng ta
có thể xây dựng lời giải trên cơ sở đó.
Trường hợp m < n , chúng ta tạo ra một chuỗi với số lương kí tự a và kí tự
b bằng nhau, sau đó thêm vào các kí tự a ở vế trái như sau.
S

AS
1
S
1


aS
1
b | λ
A

aA |a.
Một cách tương tự như trên, trường hợp n < m, ta có câu trả lời
S

AS
1

| S
1
B
S
1
-> aS
1
b| λ,
A -> aA| a,
B -> bB| b.
Kết quả là văn phạm phi ngữ cảnh, do đó L là ngôn ngữ phi ngữ cảnh. Tuy nhiên
văn phạm thì không tuyến tính .
Ví dụ 5.4
Xem xét văn phạm với luật sinh
S

aSb | SS | λ.
Đây là văn phạm phi ngữ cảnh, nhưng không tuyến tính. Một vài chuỗi trong
L(G) là abaabb, aababb, và ababab. Không khó để ta phỏng đoán và chứng
minh rằng
L = {w

{a,b}
*
: n
a
(w) = n
b
(w) và n
a

(v) ≥ n
b
(v)}
(với v là tiền tố của w bất kỳ ).
Chúng ta dễ thấy mối liên kết với các ngôn ngữ lập trình, nếu chúng ta
thay thế a và b bởi các dấu mở ngoặc và đóng ngoặc tương ứng. Ngôn ngữ L
67
1 2
2
3 4 5
1
4
2
5
3
bao gồm chuỗi như (( )) và ( )( )( ) và thật ra là việc lồng dấu ngoặc trong
ngôn ngữ lập trình.
Dẫn xuất trái nhất và dẫn xuất phải nhất
Trong văn phạm phi ngữ cảnh không tuyến tính, một dẫn xuất có thể bao
gồm những hình thức câu có nhiều hơn một biến. Trong những trường hợp như
vậy, chúng ta có thể chọn cách để thay thế các biến.
Lấy ví dụ văn phạm G = ({A, B, S},{a,b},S,P ) với các luật sinh
1. S

AB,
2. A

aaA
3. A


λ.
4. B

Bb
5. B

λ.
Dễ thấy rằng văn phạm này sinh ra ngôn ngữ L(G) ={ a
2n
b
m
: n ≥ 0, m ≥ 0 }
Bây giờ ta xem hai dẫn xuất
S

AB

aaAB

aaB

aaBb

aab

S

AB

ABb


aaABb

aaAb

aab.
Để chỉ ra dẫn xuất nào được sử dụng, chúng ta đánh số các luật sinh và viết
số tương ứng lên trên kí hiệu ⇒. Từ đây chúng ta thấy rằng hai dẫn xuất không
chỉ là cho kết quả cùng một câu mà còn sử dụng những luật sinh như nhau. Sự
khác nhau chỉ là trong thứ tự luật sinh được áp dụng, Để loại bỏ đi những nhân tố
không thích hợp này, chúng ta thường yêu cầu các biến được thay thế theo một
thứ tự cụ thể.
Đònh nghóa 5.2

Một dẫn xuất được gọi là trái nhất ( leftmost derivation) nếu trong mỗi
bước biến bên trái nhất trong dạng câu được thay thế. Nếu biến bên phải
nhất được thay thế, chúng ta gọi là dẫn xuất phải nhất (rightmost
derivation).

68
Ví dụ 5.5 Xét văn phạm với các luật sinh
S -> aAB
A -> bBb
B -> A | λ
Thì
S

aAB

abBbB


abAbB

abbBbbB

abbbbB

abbbb
là một dẫn xuất trái nhất của chuỗi abbbb. Một dẫn xuất phải nhất của
cùng một chuỗi này là :
S

aAB

aA

abBb

abAb

abbBbb

abbbb
Cây dẫn xuất
Một cách thứ hai để trình bày các dẫn xuất, độc lập với thứ tự các luật sinh được
áp dụng, là cây dẫn xuất. Một cây dẫn xuất là cây có thứ tự, trong đó mỗi nút được
gán nhãn là vế trái của luật sinh, các con của các nút đó biểu diễn vế phải tương
ứng. Ví dụ, hình 5.1 trình bày một phần của cây dẫn xuất biểu diễn luật sinh.
A -> abABc .
A



a b A B c

Trong cây dẫn xuất, nút được gán nhãn với biến bên trái của luật sinh có con là
những kí tự bên phải của luật sinh. Bắt đầu, gốc được gán nhãn với kí hiệu khởi đầu
và lá là kí hiệu kết thúc, cây dẫn xuất chỉ ra cách mà mỗi biến được thay thế trong
dẫn xuất.
Đònh nghóa 5.3

Cho G = ( V, T, S, P) là một văn phạm phi ngữ cảnh. Mộât cây có thứ tự là cây
dẫn xuất cho G nếu và chỉ nếu nó có các tính chất sau
1. Gốc được gán nhãn là S.
2. Mỗi lá có một nhãn lấy từ T

{λ}
3. Mỗi nút bên trong (không phải là nút lá) có một nhãn lấy từ V.
4. Nếu mỗi nút có nhãn là A

V, và các con của nó được gán nhãn (từ trái
sang phải) a
1
, a
2
, . . . , a
n
, thì P phải chứa một luật sinh có dạng
A -> a
1
a

2
. . . a
n
.
5. Một lá không có anh em được gán nhãn là λ, nghóa là nút có một con được
69
Hình 5.1
gán nhãn là λ khi không có con nào khác.
Một cây mà có các tính chất 3, 4 và 5, nhưng trong đó không nhất thiết có
tính chất 1 và 2 được thay thế bằng:
2a. Mỗi lá có nhãn lấy từ V

T

{λ}
thì được gọi là cây dẫn xuất riêng phần.
Chuỗi kí hiệu nhận bằng cách đọc các nút lá của cây từ trái sang phải,
bỏ qua bất kỳ λ nào, là kết quả của cây.

Ví dụ 5.6 Xét văn phạm G, với các luật sinh
S -> aAB,
A -> bBb,
B -> A |λ,
Cây trong hình 5.2 là cây dẫn xuất riêng phần cho G, trong khi cây
trong hình 5.3 là một cây dẫn xuất. Kết quả của cây thứ nhất là chuỗi
abBbB chính là một dạng câu của G. Kết quả của cây thứ hai, abbbb là
một câu của L(G).

Hình 5.2


Hình 5.3
70
a
A B
S
b
B b
Quan hệ giữa dạng câu và cây dẫn xuất
Cây dẫn xuất dùng mô tả một cách tường minh những dẫn xuất. Tuy nhiên,
chúng ta phải thiết lập sự kết nối giữa dẫn xuất và cây dẫn xuất.
Đònh Lý 5.1

Cho G = ( V, T, S, P ) là một văn phạm phi ngữ cảnh. Thì với mỗi
w

L(G), tồn tại một cây dẫn xuất của G mà kết quả của nó là w. Ngược
lại, kết quả của một cây dẫn xuất bất kì là thuộc L(G). Cũng vậ, nếu t
G


cây dẫn xuất riêng phần bất kì của G mà gốc của nó được gán nhãn là S thì
kết quả của t
G
là một dạng câu của G.


BÀI TẬP
1. Tìm văn phạm phi ngữ cảnh cho ngôn ngữ ( n, m ≥ 0)
a) L = { a
n

b
m
: m + 3 ≥ n }
b) L = { a
n
b
m
: n = 2m }
2. Tìm cây dẫn xuất cho chuỗi aabbbb với văn phạm

S -> AB|λ
A -> aB
B -> Sb
Mô tả ngôn ngữ được sinh bởi văn phạm này
3. Cho văn phạm với luật sinh
S -> aaB
A -> bBb|λ
B -> Aa
Chỉ ra rằng chuỗi aabbabba không phải là ngôn ngữ được sinh bởi văn phạm này
71
5.2 PHÂN TÍCH CÚ PHÁP VÀ TÍNH NHẬP NHẰNG
Chúng ta đã tìm hiểu, có văn phạm G, từ G cho chúng ta những chuỗi được
sinh ra từ nó. Trong các ứng dụng thực tế, chúng ta cũng cần quan tâm đến vấn đề
phân tích văn phạm, nghóa là, cho một chuỗi w, chúng ta muốn biết w có được sinh ra
bởi L(G) hay không. Nếu được sinh ra bởi L(G), khi đó có thể tìm một dẫn xuất cho
w. Giải thuật để xác đònh w thuộc L(G) được gọi là giải thuật thành viên. Thuật ngữ
phân tích cú pháp (parsing) là tìm một dãy các luật sinh mà một chuỗi w

L(G)
được dẫn xuất ra.

Phân tích cú pháp và thành viên
Cho một chuỗi w thuộc L(G), chúng ta có thể phân tích cú pháp nó. Nghóa
là đi tìm một dẫn xuất nào đó có dẫn ra chuỗi w hay không, cách thực hiện là : Ở
lượt thứ nhất xem xét các các luật sinh có dạng
S

x
tìm tất cả những x mà có thể được dẫn xuất từ S trong luật này. Nếu không tìm
được, ta đi đến lượt tiếp theo, áp dụng các luật sinh có thể dẫn đến biến bên trái nhất
của mỗi x. Thao tác này sẽ cho ta một tập các dạng câu, một vài dạng câu trong đó
có khả năng dẫn tới w.
Trong mỗi lượt kế tiếp, chúng ta lại lấy tất cả các biến trái nhất và áp dụng tất
cả các luật sinh có thể. Có thể rằng một số trong các dạng câu này có thể bò từ chối
vì lý do rằng w không bao giờ có thể được dẫn xuất từ chúng. Sau lượt đầu tiên,
chúng ta có thể có các dạng câu mà có thể được dẫn xuất từ S bằng cách áp dụng
một luật sinh, sau lượt thứ hai chúng ta có các dạng câu mà có thể được dẫn xuất từ
S với hai luật sinh, tiếp tục như thế. Nếu w

L(G) thì nó phải có một dẫn xuất trái
nhất có độ dài hữu hạn. Vì vậy phương pháp này, cuối cùng sẽ tìm ra được một dẫn
xuất trái nhất của w.
Chúng ta gọi phương pháp này là phương pháp phân tích cú pháp vét cạn, nó
thuộc dạng phân tích cú pháp từ trên xuống.
Ví dụ 5.7
Xét văn phạm
S

SS | aSb | bSa| λ
và chuỗi w = aabb. Ở lượt thứ nhất cho ta
1. S => SS

2. S => aSb
3. S => bSa
4. S => λ
72

Hai trường hợp 3 và 4 có thể loại bỏ, vì không thể dẫn xuất ra được w.
Tiếp tục lượt thứ hai với trường hợp 1 và 2 ta có các dạng câu sau:
( thay những biến S bên trái của vế phải bằng những luật sinh )
S => SS => SSS
S => SS => aSbS
S => SS => bSaS
S => SS => S
Tương tự , ta loại bỏ những trường hợp mà dạng câu không thể khớp với w,
áp dụng những luật sinh có thể cho những trường hợp còn lại,
ta có những dạng câu có thể là:
S => aSb => aSSb
S => aSb => aaSbb
S => aSb => abSab
S => aSb => ab
Một lần nữa, ta thấy có thể loại bỏû một vài dạng câu theo như trên. Trên lượt
kế tiếp ta tìm thấy chuỗi.
S => aSb => aaSbb => aabb
Vì vậy, aabb thuộc ngôn ngữ được sinh ra bởi văn phạm đang xét.
Phương pháp phân tích cú pháp vét cạn có một số nhược điểm, kém hiệu
qủa. Điều khá quan trọng là, phương pháp này luôn phân tích ra được những chuỗi w

L(G), nếu chuỗi không thuộc L(G), phương pháp này có thể không dừng. Chẳng
hạn với w = abb phương pháp này sẽ đi đến việc sinh ra vô hạn các dạng câu mà
không dừng lại, trừ phi, chúng ta xây dựng thêm trong nó một cách nào đó để dừng
lại.

Vấn đề không kết thúc của phương pháp phân tích cú pháp vét cạn là tương
đối dễ khắc phục, nếu chúng ta giới hạn dạng mà văn phạm có thể có. Nếu chúng ta
khảo sát ví dụ 5.7 chúng ta thấy rằng khó để áp dụng được luật sinh S λ, luật sinh
này có thể sử dụng để làm giảm độ dài của các dạng câu kế tiếp. Vì vậy không thể
nói một cách dễ dàng, khi nào phương pháp này dừng lại, nếu chúng ta không có bất
kỳ luật sinh nào như vậy. Thực tế có hai luật sinh chúng ta muốn loại trừ: những luật
sinh dạng A λ cũng như các luật sinh có dạng A

B. Như chúng ta sẽ thấy trong
chương tiếp theo, sự giới hạn này (loại trừ các luật sinh nói trên) không ảnh hưởng
đến kết qủa của những văn phạm về bất kỳ khía cạnh nào.
73
Ví dụ 5.8
Cho văn phạm

S

SS | aSb | bSa | ab | ba
Thỏa mãn các yêu cầu đã nêu. Nó sinh ra ngôn ngữ trong ví dụ 5.7 không
có chuỗi rỗng .
Cho một chuỗi bất kỳ w

{a,b}
+
, phương pháp phân tích cú pháp vét cạn
sẽ luôn luôn kết thúc sau không quá |w| lượt . Điều này rõ ràng vì chiều dài
của dạng câu tăng ít nhất là một kí hiệu sau mỗi lượt.
Sau |w| lượt , ta có kết qủa w có thuộc L(G) hay không.
Ý tưởng trong ví dụ này có thể đưa ra đònh lý sau cho ngôn ngữ phi ngữ
cảnh.

Đònh lý 5.2

Giả sử rằng G = ( V, T, S, P ) là một văn phạm phi ngữ cảnh mà không có
bất kỳ luật sinh nào có dạng:
A  λ
hay A

B
Trong đó: A, B

V thì phương pháp phân tích cú pháp vét cạn có thể tạo
thành thành một giải thuật mà đối với bất kỳ w


*
hoặc tạo ra được sự phân
tích cú pháp của w hoặc cho ta biết không có sự phân tích cú pháp nào có
thểù.

Chứng minh
Với mỗi dạng câu, xét chiều dài và số kí hiệu kết thúc. Mỗi bước trong dẫn
xuất tăng ít nhất là ( hoặc chiều dài, hoặc số ký hiệu kết thúc). Vì cả chiều dài
lẫn số kí hiệu kết thúc đều không vượt quá |w| nên quá trình dẫn xuất sẽ không
nhiều hơn 2|w| lượt, tại thời điểm đó chúng ta hoặc phân tích cú pháp thành công
hoặc w không thể được sinh ra bởi văn phạm.
Sự nhập nhằng trong ngôn ngữ và văn phạm
Coi w

L(G), phương pháp phân tích cú pháp vét cạn sẽ sinh ra một cây
dẫn xuất cho w. Chúng ta nói “một” cây dẫn xuất cho w, bởi vì có thể có một số cây

dẫn xuất khác. Tình huống này đưa đến sự nhập nhằng.
74
Đònh nghóa 5.4.

Một văn phạm phi ngữ cảnh G được gọi là nhập nhằng nếu tồn tại w


L(G) mà có ít nhất hai cây dẫn xuất phân biệt cho nó . Một cách phát biểu
khác, sự nhập nhằng thì suy ra tồn tại hai hay nhiều dẫn xuất trái nhất hay
phải nhất.

Ví dụ 5.9
Xem một văn phạm phi ngữ cảnh có dạng
A → ax,
mà A ∈ V, a ∈ T, x ∈ V*, và có một hạn chế thêm nữa là hai luật sinh bất kỳ có
vế trái giống nhau thì vế phải bắt đầu bằng một ký hiệu kết thúc khác nhau. Khi
đó một chuỗi bất kỳ trong ngôn ngữ được sinh ra bởi văn phạm như thế có thể
được phân tích nhiều nhất là |w| bước.
Để thấy điều này, hãy xem phương pháp tìm kiếm vét cạn và chuỗi w=
a
1
a
2
…a
n
. Bởi vì có nhiều nhất một luật sinh mà S ở vế trái và a
1
bắt đầu ở vế
phải, ta phải có dẫn xuất bắt đầu S ⇒ a
1

A
1
A
2
… A
m
. Tiếp theo, thay thế cho biến
A
1
, nhưng chỉ có một cách chọn, nên ta phải có:

S a
1
a
2
B
1
B
2
… A
2
…A
m
Ta thấy rằng với một bước, ta có thêm một ký tự kết thúc, và tiến trình hòan tất
sẽ không hơn |w| bước.
Ví dụ này, không phải là một điều mà do ta tự tạo ra. Nó là lọai văn phạm được
gọi là Simple-grammar hay S-grammar. Như ta đã biết, nhiều đặc tính của ngôn
ngữ tựa pascal có thể mô tả với S-grammar.
Ví dụ 5.10
Văn phạm trong ví dụ 5.4, với luật sinh S


aSb | SS | λ , là nhập nhằng.
Câu aabb có hai cây dẫn xuất được chỉ ra trong hình 5.4

S
S
S S

a S b λ a S b

a S b a S b

λ
λ

Hình 5.4 hai cây dẫn xuất cho aabb
75

Tính nhập nhằng là một đặc tính chung của ngôn ngữ tự nhiên. Trong ngôn ngữ
lập trình mà chỉ có một sự thông dòch cho mỗi câu, tình nhập nhằng phải được loại bỏ
khi có thể. Thông thường thì chúng ta viết lại văn phạm đó thành một văn phạm
tương đương, ở dạng không nhập nhằng.
Ví dụ 5.11
Xét văn phạm sau G = ( V, T, E, P ) với
V = { E, I}.
T = { a, b, c, +, *, (, )}
Và các luật sinh
E

I

E

E + E
E

E * E
E

(E)
I

a| b| c
Các chuỗi (a + b) * c và a * b + c ở trong L(G). Dễ thấy rằng văn
phạm này sinh ra một tập con hữu hạn của những biểu thức số học cho
ngôn ngữ Fortran và các ngôn ngữ lập trình giống Pascal. Văn phạm này
là nhập nhằng. Ví dụ như chuỗi a + b * c có hai cây dẫn xuất, như trong
hình 5.5

( a) ( b )

Hình 5.5a và 5.5b hai cây dẫn xuất cho a + b * c
Một cách để loại bỏ tính nhập nhằng, như trong ngôn ngữ lập trình, là đưa
ra thứ tự ưu tiên thực hiện các phép toán, phép * có độ ưu tiên cao hơn phép
toán + , như hình 5.5(a) cho thấy rằng b * c được lượng giá trước khi thực hiện
76
phép cộng. Tuy nhiên, sự giải quyết này hoàn toàn bên ngoài văn phạm. Điều
tốt hơn là viết lại văn phạm để chỉ có một phân tích cú pháp là có thể.
Ví dụ 5.12
Hãy đưa ra các biến mới, để viết lại văn phạm trong ví dụ 5.11, chọn
V = {E, T, F, I} và thay các luật sinh với

E

T
T

F
F

I
E

E + T
T

T * F
F

(E)
I

a| b |c
Một cây dẫn xuất của câu a + b * c được chỉ ra trong hình 5.6, không có
một cây dẫn xuất nào khác cho chuỗi này: Văn phạm này là không nhập
nhằng. Nó cũng tương đương với văn phạm trong ví dụ 5.11
E
E + T

T T * F

F F I

I I c
a b
hình 5.6
Trong các ví dụ trên sự nhập nhằng là do văn phạm, có thể loại bỏ được bằng
cách tìm một văn phạm không nhập nhằng tương đương. Tuy nhiên, trong một vài
trường hợp, điều này là không thể thực hiện được, bởi vì sự nhập nhằng xảy ra trong
chính ngôn ngữ tự nhiên.

BÀI TẬP
1. Tìm một văn phạm cho L(aaa* b + b)
2. Ch ỉ ra văn phạm sau là văn phạm nhập nhằng
S -> AB| aaB
A -> a| Aa
B -> b
77
5.3 VĂN PHẠM PHI NGỮ CẢNH VÀ NGÔN NGỮ LẬP TRÌNH.
Một trong những ứng dụng quan trọng của lý thuyết ngôn ngữ hình thức là
đònh nghóa ngôn ngữ lập trình cũng như việc xây dựng các trình thông dòch và
biên dòch cho nó. Vấn đề cơ bản ở đây là đònh nghóa ngôn ngữ lập trình một cách
chính xác và sử dụng đònh nghóa này như là một điểm khởi đầu cho việc viết các
chương trình dòch đạt hiệu quả và tin cậy. Ngôn ngữ chính quy và ngôn ngữ phi
ngữ cảnh đều quan trọng cho việc thực hiện điều này. Như ta đã biết, ngôn ngữ
chính quy được dùng để nhận dạng những mẫu đơn giản trong ngôn ngữ lập
trình, nhưng như phần giới thiệu của chương này, ngôn ngữ phi ngữ cảnh giúp ta
thực hiện những vấn đề phức tạp hơn.
Như hầu hết với các ngôn ngữ khác, ngôn ngữ lập trình được đònh nghóa
bằng một văn phạm. Một cách truyền thống khi viết văn phạm cho ngôn ngữ lập
trình, chúng ta sử dụng cách viết gọi là Backus – Naus form hay BNF. Cách viết
này, về cơ bản gần giống như các kí hiệu ta đã dùng. Trong BNF, các biến được
đóng bởi các cặp dấu ngoặc nhọn. Những kí hiệu kết thúc được viết mà không

có đánh dấu gì đặc biệt. BNF cũng sử dụng các kí hiệu phụ như “|”, giống như
cách chúng ta đã dùng. Do vậy, văn phạm trong ví dụ 5.12 được viết trong BNF
như sau:
<expression> ::= <term> | <expression> + <term>,
<term> ::= <factor> | <term> * <factor>

Kí hiệu + và * là những kí hiệu kết thúc. Kí hiệu “|” được sử dụng như một kí
hiệu cho ta chọn lựa vế phải của luật sinh , ký hiệu ::= được sử dụng thay thế
cho ký hiệu ->

Ví dụ như câu lệnh trong Pascal If - then – else có thể được đònh nghóa như sau
<if _statement> ::= if <expression> <then_clause> <else_clause>.
đây từ khóa if là kí hiệu kết thúc. Còn lại là các biến mà cần được đònh
nghóa thêm . Nếu chúng ta so sánh điều này với ví dụ 5.9, chúng ta thấy rằng
điều này giống như một luật sinh của văn phạm S. Biến <if_statement> ở vế trái
là luôn liên kết với kí hiệu kết thúc if bên vế phải, đây là lý do cho việc phân
tích cú pháp được dễ dàng và hiệu quả. Tại đây, cho ta biết tại sao ta phải sử
dụng từ khóa trong các ngôn ngữ lập trình. Từ khóa không chỉ cho một cấu trúc
trực quan, giúp ta đọc được chương trình mà còn làm cho công việc viết trình
biên dòch dễ dàng hơn .
Nhưng, không phải tất cả các đặc tính của ngôn ngữ lập trình đều có thể
được biểu thò bằng văn phạm. Những qui luật cho <expression> ở trên là không
ở trong loại này, vì vậy việc phân tích cú pháp trở nên thiếu rõ ràng. Câu hỏi
được đưa ra, những luật văn phạm gì có thể cho phép phân tích cú pháp đạt hiệu
78
quả. Hiện nay đó là các văn phạm LL và LR, những văn phạm có khả năng biểu
diễn các đặc tính thiếu rõ ràng của ngôn ngữ lập trình, và còn cho phép chúng ta
phân tích với thời gian tuyến tính. Loại văn phạm này, đi sâu vào nó thì nằm ngoài
giáo trình này.
Lại nữa, vấn đề về tính mơ hồ chiếm một ý nghóa quan trọng. Ngôn ngữ lập

trình phải tránh được mơ hồ, nếu không một chương trình có thể tạo ra các kết quả
khác nhau khi được xử lý bằng những trình biên dòch khác nhau hay chạy trên những
hệ thống khác nhau. Như ví dụ 5.11, có thể dễ dàng tạo ra sự mơ hồ trong văn phạm.
Lấy một ví dụ như đối với Pascal, đònh nghóa BNF thông thường cho ta các cấu trúc
sau
var x, y : real;
x, z : integer;
hay
var x : integer;
x := 3.2
Những cấu trúc trên đều không được trình biên dòch Pascal chấp nhận, vì
chúng vi phạm, một biến nguyên không thể gán một giá trò thực.
Ví d ụ 5.9 Xem một loại văn phạm phi ngữ cảnh A → ax
Mà A ∈ V, a∈T, x ∈V* và có một hạn chế sâu hơn là đối với mọi cặp (A,a) chỉ xuất
hiện tối đa trên một luật sinh cho trước A, bên vế trái có thể không có nhiều hơn một
luật cho ký hiệu kết thúc a ở đầu của chuỗi bên vế phải. Nói khác đi, hai luật sinh bất
kỳ có vế trái giống nhau, thì vế phải bắt đầu bằng một ký hiệu kết thúc khác. Khi đó
một chuỗi bất kỳ trong ngôn ngữ được sinh ra bởi văn phạm như thế có thể được phân
tích nhiều nhất là |w| bước. Để thấy điều này hãy xem phương pháp tìm kiếm vét cạn
và chuỗi w= a
1
a
2
…a
n
. Bởi vì có nhiều nhất một luật sinh mà S ở vế trái và a
1
bắt đầu ở
vế phải, ta phải có dẫn xuất S ⇒ a
1

A
1
A
2
… A
m
, tiếp theo, thay thế cho biến A
1
, nhưng
vì một lần nữa chỉ có một cách chọn, ta phải có :
S ⇒… ⇒ a
1
a
2
B
1
B
2
… A
2
… A
m
.
Nhận thấy, với một bước ta có thêm một ký tự kết thúc, và tiến trình hoàn tất không
hơn |w| bước. Ví dụ này không phải là một điều tự tạo ra, đây là một loại văn phạm gọi là
Simple Grammar. Như ta đã biệt, nhiều đặc tính của ngôn ngữ tựa Pascal có thể được mô
tả với S-grammar
79

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×