Thiết kế trình biên dịch: Phân tích ngữ nghĩa

Chúng ta đã tìm hiểu cách trình phân tích cú pháp xây dựng cây phân tích cú pháp trong giai đoạn phân tích cú pháp.

Cây phân tích cú pháp đơn giản được xây dựng trong giai đoạn đó thường không sử dụng cho trình biên dịch, vì nó không mang bất kỳ thông tin nào về cách đánh giá cây.

Việc tạo ra ngữ pháp không có ngữ cảnh, tạo ra các quy tắc của ngôn ngữ, không phù hợp với cách diễn giải chúng.

Ví dụ:

E → E + T

Quá trình xử lý CFG ở trên không có quy tắc ngữ nghĩa nào đi kèm với nó và nó không thể giúp ích cho việc tạo ra bất kỳ ý nghĩa nào cho quá trình xử lý.

Nếu bỏ lỡ hướng dẫn này thì bạn có thể xem tại đây:

Thiết kế trình biên dịch: Phân tích cụ pháp
Trong hướng dẫn này, bạn sẽ tìm hiểu chi tiết về phân tích cu pháp và trình phân tích cú pháp trong thiết kế trình biên dịch.

Ngữ nghĩa

Ngữ nghĩa của một ngôn ngữ cung cấp ý nghĩa cho các cấu trúc của nó, như mã thông báo và cấu trúc cú pháp. Ngữ nghĩa học giúp giải thích các ký hiệu, kiểu của chúng và mối quan hệ của chúng với nhau. Phân tích ngữ nghĩa đánh giá liệu cấu trúc cú pháp được xây dựng trong chương trình nguồn có phát sinh bất kỳ ý nghĩa nào hay không.

CFG + semantic rules = Syntax Directed Definitions

Ví dụ:

int a = “value”;

không nên đưa ra lỗi trong giai đoạn phân tích từ vựng và cú pháp, vì nó đúng về mặt từ vựng và cấu trúc, nhưng nó sẽ tạo ra lỗi ngữ nghĩa vì kiểu dữ liệu của phép gán khác nhau.

Các quy tắc này được thiết lập bởi ngữ pháp của ngôn ngữ và được đánh giá trong phân tích ngữ nghĩa.

Các tác vụ sau đây cần được thực hiện trong phân tích ngữ nghĩa:

  • Phân giải phạm vi
  • Kiểm tra kiểu dữ liệu.
  • Kiểm tra giới hạn mảng.

Lỗi ngữ nghĩa

Chúng tôi đã đề cập đến một số lỗi ngữ nghĩa mà trình phân tích ngữ nghĩa sẽ nhận ra:

  • Kiểu dữ liệu không phù hợp.
  • Biến không được khai báo.
  • Sử dụng sai định danh dành riêng..
  • Khai báo trùng biến trong một phạm vi.
  • Truy cập một biến ngoài phạm vi.
  • Thông số thực tế và thông số chính thức không khớp.

Ngữ pháp thuộc tính

Ngữ pháp thuộc tính là một dạng ngữ pháp đặc biệt không có ngữ cảnh, trong đó một số thông tin bổ sung (thuộc tính) được nối vào một hoặc nhiều đầu cuối không phải của nó để cung cấp thông tin nhạy cảm theo ngữ cảnh.

Mỗi thuộc tính có miền giá trị được xác định rõ ràng, chẳng hạn như số nguyên, số thập phân, ký tự, chuỗi và biểu thức.

Ngữ pháp thuộc tính là một phương tiện để cung cấp ngữ nghĩa cho ngữ pháp không có ngữ cảnh và nó có thể giúp xác định cú pháp và ngữ nghĩa của một ngôn ngữ lập trình.

Ngữ pháp thuộc tính (khi được xem như một cây phân tích cú pháp) có thể chuyển các giá trị hoặc thông tin giữa các nút của cây.

Ví dụ:

E → E + T { E.value = E.value + T.value }

Phần bên phải của CFG chứa các quy tắc ngữ nghĩa chỉ định cách diễn giải ngữ pháp. Ở đây, các giá trị của không phải đầu cuối E và T được cộng lại với nhau và kết quả được sao chép vào đầu cuối E.

Các thuộc tính ngữ nghĩa có thể được gán cho các giá trị của chúng từ miền của chúng tại thời điểm phân tích cú pháp và đánh giá tại thời điểm gán hoặc điều kiện.

Dựa trên cách các thuộc tính nhận được giá trị của chúng, chúng có thể được chia thành hai loại: thuộc tính tổng hợp và thuộc tính kế thừa.

Thuộc tính tổng hợp

Các thuộc tính này nhận giá trị từ các giá trị thuộc tính của các nút con của chúng. Để minh họa, hãy giả sử sản xuất sau:

S → ABC

Nếu S đang nhận các giá trị từ các nút con của nó (A, B, C), thì nó được cho là một thuộc tính tổng hợp, vì các giá trị của ABC được tổng hợp thành S.

Như trong ví dụ trước của chúng ta (E → E + T), nút cha E nhận giá trị từ nút con của nó. Các thuộc tính tổng hợp không bao giờ lấy giá trị từ các nút cha của chúng hoặc bất kỳ nút anh em nào.

Thuộc tính kế thừa

Ngược lại với các thuộc tính tổng hợp, các thuộc tính kế thừa có thể lấy giá trị từ các nút cha và / hoặc anh em. Như trong xử lý sau,

S → ABC

A có thể nhận giá trị từ S, B và C. B có thể nhận giá trị từ S, A và C. Tương tự như vậy, C có thể nhận giá trị từ S, A và B.

Mở rộng: Khi một non-terminal được mở rộng thành các đầu cuối (terminal) theo quy tắc ngữ pháp:

Thuộc tính kế thừa

Giảm: Khi một đầu cuối (terminal) được giảm thành non-terminal tương ứng của nó theo các quy tắc ngữ pháp. Cây cú pháp được phân tích cú pháp từ trên xuống và từ trái sang phải. Bất cứ khi nào việc giảm xảy ra, chúng tôi áp dụng các quy tắc ngữ nghĩa tương ứng (hành động) của nó.

Phân tích ngữ nghĩa sử dụng bản dịch theo hướng cú pháp để thực hiện các nhiệm vụ trên.

Trình phân tích ngữ nghĩa nhận AST (Cây cú pháp trừu tượng) từ giai đoạn trước của nó (phân tích cú pháp).

Trình phân tích ngữ nghĩa đính kèm thông tin thuộc tính với AST, được gọi là AST thuộc tính.

Các thuộc tính là cặp giá trị, <tên thuộc tính, giá trị thuộc tính>

Ví dụ:

int value = 5;
<type, “integer”>
<presentvalue, “5”>

Đối với mỗi xử lý, chúng tôi đính kèm một quy tắc ngữ nghĩa.

SDT được phân bổ theo S

Nếu một SDT chỉ sử dụng các thuộc tính tổng hợp, nó được gọi là SDT được phân bổ theo S. Các thuộc tính này được đánh giá bằng cách sử dụng các SDT do S phân bổ có các hành động ngữ nghĩa của chúng được viết sau khi xử lý (phía bên phải).

SDT được phân bổ theo S

Như được mô tả ở trên, các thuộc tính trong SDT do S phân bổ được đánh giá trong phân tích cú pháp từ dưới lên, vì giá trị của các nút cha phụ thuộc vào giá trị của các nút con.

SDT được phân bổ theo L

Dạng SDT này sử dụng cả thuộc tính tổng hợp và thuộc tính kế thừa với hạn chế là không lấy giá trị từ các anh em bên phải.

Trong SDT được phân bổ theo L, một đầu cuối (terminal) không phải là non-terminal có thể nhận các giá trị từ các nút cha, con và anh em của nó. Như trong xử lý sau

S → ABC

S có thể nhận các giá trị từ A, B và C (tổng hợp). A chỉ có thể nhận các giá trị từ S. B có thể nhận các giá trị từ S và A. C có thể nhận các giá trị từ S, A và B. Không có đầu cuối nào có thể nhận các giá trị từ phần tử anh em ở bên phải của nó.

Các thuộc tính trong SDT do L phân bổ được đánh giá theo cách phân tích cú pháp theo chiều sâu từ trái sang phải.

SDT được phân bổ theo L

Chúng ta có thể kết luận rằng nếu một định nghĩa được quy cho S, thì nó cũng được quy cho L vì định nghĩa được quy cho L bao gồm các định nghĩa được quy cho S.

Trong hướng dẫn tiếp theo, bạn sẽ tìm hiểu vì sao cần phải tạo mã trung gian? Cách tạo mã trung gian trong thiết kế trình biên dịch.

Thiết kế trình biên dịch: Tạo mã trung gian
Trong hướng dẫn này, bạn sẽ tìm hiểu vì sao cần phải tạo mã trung gian? Cách tạo mã trung gian trong thiết kế trình biên dịch.
Trình Biên Dịch
Bài Viết Liên Quan:
Thiết kế trình biên dịch: Môi trường thực thi
Trung Nguyen 07/04/2021
Thiết kế trình biên dịch: Môi trường thực thi

Trong hướng dẫn này, bạn sẽ tìm hiểu chi tiết về môi trường thực thi, nơi chương trình sau khi biên dịch sẽ được thực thi.

Thiết kế trình biên dịch: Bảng ký hiệu
Trung Nguyen 07/04/2021
Thiết kế trình biên dịch: Bảng ký hiệu

Trong hướng dẫn này, bạn sẽ tìm hiểu bảng ký hiệu là gì? Cách trình biên dịch sử dụng bảng ký hiệu trong thiết kế trình biên dịch.

Thiết kế trình biên dịch: Tối ưu hóa mã
Trung Nguyen 07/04/2021
Thiết kế trình biên dịch: Tối ưu hóa mã

Trong hướng dẫn này, bạn sẽ tìm hiểu chi tiết các phương pháp để tối ưu mã trong thiết kế trình biên dịch.

Thiết kế trình biên dịch: Tạo mã
Trung Nguyen 07/04/2021
Thiết kế trình biên dịch: Tạo mã

Trong hướng dẫn này, bạn sẽ tìm hiểu tạo mã và trình tạo mã trong thiết kế trình biên dịch.