25/05/2018, 10:16

Sự hình thành bảng ký hiệu

Một cấu trúc dữ liệu gọi là bảng ký hiệu (symbol table) thường được dùng để lưu giữ thông tin về các cấu trúc của ngôn ngữ nguồn. Các thông tin này được tập hợp từ các giai đoạn phân tích của trình biên dịch và được sử dụng bởi giai đoạn ...

Một cấu trúc dữ liệu gọi là bảng ký hiệu (symbol table) thường được dùng để lưu giữ thông tin về các cấu trúc của ngôn ngữ nguồn. Các thông tin này được tập hợp từ các giai đoạn phân tích của trình biên dịch và được sử dụng bởi giai đoạn tổng hợp để sinh mã đích. Ví dụ trong quá trình phân tích từ vựng, các chuỗi ký tự tạo ra một token (trị từ vựng của token) sẽ được lưu vào một mục ghi trong bảng danh biểu. Các giai đoạn sau đó có thể bổ sung thêm các thông tin về kiểu của danh biểu, cách sử dụng nó và vị trí lưu trữ. Giai đoạn sinh mã sẽ dùng thông tin này để tạo ra mã phù hợp, cho phép lưu trữ và truy xuất biến đó.

1. Giao diện của bảng ký hiệu

Các thủ tục trên bảng ký hiệu chủ yếu liên quan đến việc lưu trữ và truy xuất các trị từ vựng. Khi một trị từ vựng được lưu trữ thì token kết hợp với nó cũng được lưu. Hai thao tác sau được thực hiện trên bảng ký hiệu.

Insert (s, t): Trả về chỉ mục của một ô mới cho chuỗi s, token t.

Lookup (s): Trả về chỉ mục của ô cho chuỗi s hoặc 0 nếu chuỗi s không tồn tại.

Bộ phân tích từ vựng sử dụng thao tác tìm kiếm lookup để xác định xem một ô cho một trị từ vựng của một token nào đó đã tồn tại trong bảng ký hiệu hay chưa? Nếu chưa thì dùng thao tác xen vào insert để tạo ra một ô mới cho nó.

2. Xử lý từ khóa dành riêng

Ta cũng có thể sử dụng bảng ký hiệu nói trên để xử lý các từ khóa dành riêng (reserved keyword). Ví dụ với hai token div mod với hai trị từ vựng tương ứng là div và mod. Chúng ta có thể khởi tạo bảng ký hiệu bởi lời gọi:

insert (“div”, div);

insert (“mod”, mod);

Sau đó lời gọi lookup (“div”) sẽ trả về token div, do đó “div” không thể được dùng làm danh biểu.

Với phương pháp vừa trình bày thì tập các từ khóa được lưu trữ trong bảng ký hiệu trước khi việc phân tích từ vựng diễn ra. Ta cũng có thể lưu trữ các từ khóa bên ngoài bảng ký hiệu như là một danh sách có thứ tự của các từ khóa. Trong quá trình phân tích từ vựng, khi một trị từ vựng được xác định thì ta phải tìm (nhị phân) trong danh sách các từ khóa xem có trị từ vựng này không. Nếu có, thì trị từ vựng đó là một từ khóa, ngược lại, đó là một danh biểu và sẽ được đưa vào bảng ký hiệu.

3. Cài đặt bảng ký hiệu

Cấu trúc dữ liệu cụ thể dùng cài đặt cho một bảng ký hiệu được trình bày trong hình dưới đây. Chúng ta không muốn dùng một lượng không gian nhớ nhất định để lưu các trị từ vựng tạo ra một danh biểu bởi vì một lượng không gian cố định có thể không đủ lớn để lưu các danh biểu rất dài và cũng rất lãng phí khi gặp một danh biểu ngắn.

Thông thường, một bảng ký hiệu gồm hai mảng :

1. Mảng lexemes (trị từ vựng) dùng để lưu trữ các chuỗi ký tự tạo ra một danh biểu, các chuỗi này ngăn cách nhau bởi các ký tự EOS (end - of - string).

2. Mảng symtable với mỗi phần tử là một mẩu tin (record) bao gồm hai trường, trường con trỏ lexptr trỏ tới đầu trị từ vựng và trường token. Cũng có thể dùng thêm các trường khác để lưu trữ giá trị các thuộc tính.

Mục ghi thứ zero trong mảng symtable phải được để trống bởi vì giá trị trả về của hàm lookup trong trường hợp không tìm thấy ô tương ứng cho chuỗi ký hiệu.

Trong hình trên, ô thứ nhất và thứ hai trong bảng ký hiệu dành cho các từ khóa div mod. Ô thứ ba và thứ tư dành cho các danh biểu counti.

Ðoạn mã (ngôn ngữ giả) cho bộ phân tích từ vựng được dùng để xử lý các danh biểu như sau. Nó xử lý khoảng trắng và hằng số nguyên cũng giống như thủ tục đã nói ở phần trước. Khi bộ phân tích từ vựng đọc vào một chữ cái, nó bắt đầu lưu các chữ cái và chữ số vào trong vùng đệm lexbuf. Chuỗi được tập hợp trong lexbuf sau đó được tìm trong mảng symtable của bảng ký hiệu bằng cách dùng hàm lookup. Bởi vì bảng ký hiệu đã được khởi tạo với 2 ô cho div và mod (hình 2.14) nên nó sẽ tìm thấy trị từ vựng này nếu lexbuf có chứa div hay mod, ngược lại nếu không có ô cho chuỗi đang chứa trong lexbuf thì hàm lookup sẽ trả về 0 và do đó hàm insert được gọi để tạo ra một ô mới trong symtable và p là chỉ số của ô trong bảng ký hiệu của chuỗi trong lexbuf. Chỉ số này được truyền tới bộ phân tích cú pháp bằng cách đặt tokenval := p và token nằm trong trường token được trả về.

Kết quả mặc nhiên là trả về số nguyên mã hóa cho ký tự dùng làm token.

Function lexan: integer;

var lexbuf: array[0..100] of char;

c: char

be gin

loop begin

đọc một ký tự vào c;

if c là một ký tự trống blank hoặc ký tự tab then

không thực hiện điều gì ;

else if c là ký tự newline then

lineno = lineno + 1

else if c là một ký tự số then

begin

đặt tokenval là giá trị của ký số này và các ký số theo sau;

return NUM;

end

else if c là một chữ cái then

begin

đặt c và các ký tự, ký số theo sau vào lexbuf;

p := lookup (lexbuf);

if p = 0 then p := insert (lexbuf, id);

tokenval := p;

return trường token của ô có chỉ mục p;

end

else

begin /* token là một ký tự đơn */

đặt tokenval là NONE; /* không có thuộc tính */

return số nguyên mã hóa của ký tự c;

end;

end;

end;

0