1. Trang chủ
  2. » Luận Văn - Báo Cáo

Nghiên cứu phát triển kỹ thuật debugger cho các bộ phát sinh tự động phân tích từ vựng và phân tích cú pháp

70 22 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 70
Dung lượng 2,05 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Khi đó bộ sinh sẽ dựa vào phần đặc tả này sinh mã cho bộ phân tích từ vựng và cú pháp.Tuy nhiên nhược điểm chính của các bộ sinh là không có hoặc hỗ trợ rất hạn chế quá trình tìm lỗi phá

Trang 1

TRƯỜNG ĐẠI HỌC BÁCH KHOA

Trang 2

CÔNG TRÌNH ĐƯỢC HOÀN THÀNH TẠI TRƯỜNG ĐẠI HỌC BÁCH KHOA ĐẠI HỌC QUỐC GIA TP HỒ CHÍ MINH

Cán bộ hướng dẫn khoa học: TS Nguyễn Hứa Phùng

Trang 3

TRƯỜNG ĐẠI HỌC BÁCH KHOA CỘNG HÒA XÃ HỘI CHỦ NGHĨA VIỆT NAM KHOA: Công Nghệ Thông Tin Độc Lập - Tự Do - Hạnh Phúc

- -oOo -

Tp HCM, ngày 1 tháng 7 năm 2010

NHIỆM VỤ LUẬN VĂN THẠC SĨ

Họ và tên học viên: Phạm Hùng Tiến Phái: Nam

Ngày, tháng, năm sinh: 07/11/1983 Nơi sinh: TPHCM

Chuyên ngành: Khoa Học Máy Tính

MSHV: 00708210

1- TÊN ĐỀ TÀI: Nghiên cứu phát triển kỹ thuật debugger cho các bộ phát sinh tự

động phân tích từ vựng và phân tích cú pháp

2- NHIỆM VỤ LUẬN VĂN

 Tìm hiểu các các bộ phát sinh tự động phân tích từ vựng và phân tích cú pháp

 Đánh giá các giải pháp hiện có về debugger cho các bộ phát sinh tự động phân tích từ vựng và phân tích cú pháp

 Nghiên cứu và phát triển debugger cho bộ phát sinh tự động phân tích từ vựng và phân tích cú pháp

3- NGÀY GIAO NHIỆM VỤ: 25/1/2010

4- NGÀY HOÀN THÀNH NHIỆM VỤ: 2/7/2010

5- HỌ VÀ TÊN CÁN BỘ HƯỚNG DẪN: TS Nguyễn Hứa Phùng

Nội dung và đề cương Luận văn thạc sĩ đã được Hội Đồng Chuyên Ngành thông qua

CÁN BỘ HƯỚNG DẪN CHỦ NHIỆM BỘ MÔN KHOA QL CHUYÊN NGÀNH

(Họ tên và chữ ký) QUẢN LÝ CHUYÊN NGÀNH (Họ tên và chữ ký)

(Họ tên và chữ ký)

Trang 4

LỜI CẢM ƠN

Trước tiên, tôi xin chân thành cảm ơn thầy TS Nguyễn Hứa Phùng đã tận tình hướng dẫn, truyền đạt kiến thức, kinh nghiệm cho tôi trong suốt quá trình thực hiện luận văn tốt nghiệp này

Xin gửi lời cảm ơn đến quý thầy cô Trường Đại học Bách Khoa TP.HCM, những người đã truyền đạt kiến thức quý báu cho tôi trong thời gian học cao học vừa qua

Sau cùng, lời tri ân sâu sắc xin được dành cho bố mẹ, những người đã nuôi dạy con khôn lớn và hết lòng quan tâm, động viên để con hoàn thành luận văn tốt nghiệp này

Trang 5

Tóm tắt luận văn

Sự ra đời của các bộ sinh mã trình biên dịch đã làm giảm đi rất nhiều công sức phát triển một trình biên dịch Người phát triển chỉ cần đặc tả văn phạm cho bộ phân tích từ vựng và cú pháp Khi đó bộ sinh sẽ dựa vào phần đặc tả này sinh mã cho bộ phân tích từ vựng và cú pháp.Tuy nhiên nhược điểm chính của các bộ sinh là không có hoặc hỗ trợ rất hạn chế quá trình tìm lỗi phát sinh trong quá trình đặc tả văn phạm

Mục đích chính của luận văn là nghiên cứu và đưa ra giải pháp phát triển một debugger cho phép debug ở mức cao – mức đặc tả văn phạm

Trang 6

MỤC LỤC

CHƯƠNG 1 MỞ ĐẦU 1

1.1 Giới thiệu 1

1.2 Đóng góp của luận văn 4

1.3 Cấu trúc luận văn 5

CHƯƠNG 2 BỘ SINH MÃ TRÌNH BIÊN DỊCH 6

2.1 Bộ phân tích từ vựng 7

2.1.1 Nguyên lý hoạt động 7

2.1.1.1 Tách token sử dụng NFA 9

2.1.1.2 Tách token sử dụng DFA 11

2.1.2 Bộ sinh mã phân tích từ vựng 14

2.2 Bộ phân tích cú pháp 15

2.2.1 LR (k) 15

2.2.2 LL(k) 18

2.2.2.1 Recursive Decent Parser (RDP) 18

2.2.2.2 Non-recursive Predictive Parser (NPP) 20

CHƯƠNG 3 DEBUGGER 24

3.1 Giới thiệu debugger 24

3.2 Phân loại debugger 24

3.3 Kiến trúc tổng quát debugger 24

3.3.1 Tầng giao diện (User Interface) 25

3.3.1.1 Source View 25

3.3.1.2 Stack View 26

3.3.1.3 Breakpoint View 27

3.3.1.4 Control, Disassembly và Hardware Register View (CPU View) 27

3.3.1.5 Inspect View 28

3.3.1.6 Variable View 28

3.3.2 Debugger Kernel 29

3.3.3 Operating System Debugger Interface (OSDI) 29

Trang 7

3.4 Nguyên lý hoạt động debugger 29

CHƯƠNG 4 CÁC NGHIÊN CỨU LIÊN QUAN 31

4.1 Antlrworks 31

4.2 AntlrIDE 33

4.3 Antlr Studio 35

4.4 Nooza 36

4.5 Zyacc 36

CHƯƠNG 5 DEBUGGER CHO BỘ SINH MÃ TRÌNH BIÊN DỊCH 37

5.1 Chức năng debugger cho bộ sinh 39

5.2 Kỹ thuật hiện thực debugger cho bộ sinh mã 39

5.2.1 Kiến trúc debugger cho bộ sinh 39

5.2.2 Ánh xạ giữa văn phạm và mã sinh ra 40

5.2.3 Nguyên lý hoạt động của debugger cho bộ sinh 42

5.2.4 Các chức năng chính của debugger cho bộ sinh 45

5.2.4.1 Breakpoint 45

5.2.4.2 Chạy từng bước (stepping) 46

5.2.4.3 Phát hiện lỗi 48

5.3 HLDCC (High Level Debugger for Compiler Compiler) 48

5.3.1 Kiến trúc tổng quan 49

5.3.2 Nguyên lý hoạt động của HLDCC 50

5.3.3 Hiện thực 51

5.3.3.1 Language API 51

5.3.3.2 Debugger API 52

5.3.4 Debugger cho ngôn ngữ lập trình (Native Debugger) 59

CHƯƠNG 6 KẾT LUẬN VÀ HƯỚNG PHÁT TRIỂN 61

6.1 Kết luận 61

6.2 Hướng phát triển 61

TÀI LIỆU THAM KHẢO 62

Trang 8

CHƯƠNG 1 MỞ ĐẦU

1.1 Giới thiệu

Ra đời từ thập niên 50, lý thuyết trình biên dịch đã có khoảng thời gian phát triển khá dài Các lý thuyết này được ứng dụng rộng rãi từ việc phát triển trình biên dịch cho các ngôn ngữ lập trình hiện đại đến các ngôn ngữ scripting và ngay cả ứng dụng đơn giản như đọc các tập tin cấu hình Quá trình hiện thực trình biên dịch trải qua nhiều bước Hình 1-1 minh họa sơ đồ tổng quan của các bước thực thi của trình biên dịch

Hình 1-1: Các bước của quá trình biên dịch

Phân tích từ vựng sẽ tiến hành phân tích dữ liệu đầu vào và tách thành các ký hiệu kết thúc (token) Các token này sẽ được sử dụng cho quá trình phân tích cú pháp Kết quả của phân tích cú pháp là cây cú pháp Kế tiếp, quá trình xử lý ngữ nghĩa sẽ được thực thi dựa vào cây phân tích cú pháp này Nhằm đảm bảo tính khả

Trang 9

chuyển, quá trình sinh mã trung gian được thực thi Ứng với các hệ máy khác nhau trình biên dịch sẽ tiến hành tối ưu mã và cuối cùng sinh ra mã thực thi Trong quá trình biên dịch, trình biên dịch lưu trữ các thông tin trung gian (biến, tên thủ tục…) trong bảng danh biểu nhằm hỗ trợ cho quá trình phân tích Ngoài ra trình biên dịch còn cung cấp chức năng xử lý lỗi( lỗi cú pháp hoặc ngữ nghĩa…) Tuy nhiên không phải lúc nào các bước trên được thực thi một cách đầy đủ Tùy theo từng nhu cầu cụ thể mà một số bước ta có thể bỏ qua

Với sự ra đời của biểu thức chính quy (regular expression), Cú pháp cho các

từ vựng có thể được định nghĩa một cách hình thức Hình 1-2 minh họa cách thức đặc tả từ vựng dựa vào biểu thức chính quy, trong đó ID là biểu thức các định danh, INT là biểu thức số nguyên:

ID  ('a' 'z'|'A' 'Z')+ ; INT  '0' '9'+ ;

Hình 1-2: Biểu thức chính quy cho phân tích từ vựng

Tương tự như vậy, cú pháp của chương trình cũng có thể đặc tả một cách hình thức dựa vào văn phạm phi ngữ cảnh (context-free grammar) Văn phạm phi ngữ cảnh bao gồm tập hợp các luật sinh Quá trình phân tích cú pháp sẽ được vào các luật sinh này để kiểm tra xem chương trình nhập đúng cú pháp hay không Hình 1-3 minh họa văn phạm phi ngữ cảnh cho biểu thức tính toán:

expr  multExpr (('+'|'-') multExpr)*;

multExpr  atom (('*'|’/’) atom)*;

atom  INT | ID | '(' expr ')';

Hình 1-3: Văn phạm phi ngữ cảnh cho cú pháp tính giá trị biểu thức

Ngoài việc đặc tả một cách hình thức, các luật từ vựng và cú pháp có thể được kết hợp với các hành vi ngữ nghĩa Hành vi ngữ nghĩa được đặc tả sử dụng chính ngôn ngữ lập trình phát triển cho bộ phân tích từ vựng hoặc cú pháp Ví dụ

Trang 10

Hình 1-4 minh họa luật sinh kết hợp với hành vi ngữ nghĩa Luật sinh expr và multExpr nhận giá trị trả về là biến value Các hành vi ngữ nghĩa được đặc tả giữa

“{“ và “}” Đặc tả ngữ nghĩa có thể sử dụng các ký hiệu văn phạm ở vế phải (hoặc

đặt lại tên cho ký hiệu văn phạm ở vế phải) như là các biến

expr returns [int value]

 e=multExpr {$value = $e.value;}

( '+' e=multExpr {$value += $e.value;}

| '-' e=multExpr {$value -= $e.value;}

)* ;

multExpr returns [int value]

 e=atom {$value = $e.value;} ('*' e=atom {$value *= $e.value;})* ;

Hình 1-4: Luật sinh được kết hợp với hành vi ngữ nghĩa

Từ các đặc tả hình thức từ vựng, ta có thể hiện thực bộ phân tích từ vựng dựa vào lý thuyết về automat và cũng từ các đặc tả cú pháp ta có thể sử dụng phương pháp từ dưới lên (bottom-up – LR(k)) hoặc từ trên xuống (top-down – LL(k)) để kiếm tra tính đúng đắn của chương trình nhập

Trước đây việc hiện thực các lý thuyết trên tốn nhiều công sức Vì vậy sự ra đời của các bộ sinh mã cho trình biên dịch (compiler generator hay compiler compiler) đã giảm bớt đi rất nhiều gánh nặng cho người phát triển Mục đích bộ sinh mã là dựa vào các đặc tả hình thức các luật sinh kết hợp với hành vi ngữ nghĩa như trên sẽ sinh ra mã ngôn ngữ lập trình mong muốn cho quá trình phân tích từ vựng và cú pháp

Đến nay rất nhiều bộ sinh mã đã ra đời nhưng các công cụ hỗ trợ phát triển còn rất hạn chế nên việc ứng dụng bộ sinh mã gặp nhiều giới hạn như:

 Làm thế nào để phát hiện lỗi trong quá trình đặc tả văn phạm?

 Làm thế nào để biết chắc rằng văn phạm đặc tả đúng yêu cầu?

Trang 11

 Khi bộ phân tích (từ vựng hoặc cú pháp) được thực thi, trong trường hơp phát sinh lỗi thì nguyên nhân lỗi này do đâu?

Bản thân chính các bộ sinh mã hiện tại không cung cấp hoặc nếu có thì rất hạn chế các công cụ gỡ rối (debugger) Người đặc tả chủ yếu dựa vào kinh nghiệm

là chính Một số bộ sinh cũng hỗ trợ nhưng đòi hỏi người đặc tả phải có các kiến thức về trình biên dịch (quá trình shift/reduce…) và cấu trúc mã sinh ra (trong trường hợp sử dụng debugger có sẵn của ngôn ngữ lập trình – source level debugger) Do đó nhu cầu phát triển debugger ở mức cấp cao (high level debugger) cho bộ sinh là cần thiết

Mục đích chính của debugger ở mức cao bao gồm:

 Ghi nhận được quá trình áp dụng luật sinh cho đến vị trí phát sinh lỗi hoặc điểm dừng (breakpoint)

 Cho phép chạy từng bước (stepping) qua các luật sinh cũng như hành vi ngữ nghĩa

 Cho phép xem xét trạng thái chương trình (giá trị biến…) tại điểm dừng

1.2 Đóng góp của luận văn

Luận văn trình bày về HLDCC (High level debugger for compiler compiler)

do tác giả phát triển HLDCC là real-time debugger cho bộ sinh mã trình biên dịch HLDCC có cách tiếp cận khác với các debugger cho bộ sinh hiện có: hỗ trợ nhiều

bộ sinh và ngôn ngữ lập trình khác nhau Chức năng chính của HLDCC bao gồm:

 Hỗ trợ các bộ sinh mã RDP

 Môi trường phát triển tích hợp (IDE)

 Hỗ trợ đặc tả văn phạm cho nhiều bộ sinh

 Hỗ trợ debug nhiều ngôn ngữ lập trình khác nhau

 Real-time debugger: cho phép debug các hành vi ngữ nghĩa

 Hỗ trợ hầu hết các chức năng của debugger: step (in, out, over), breakpoint (có điều kiện và không điều kiện)

Trang 12

1.3 Cấu trúc luận văn

Chương 2 sẽ giới thiệu nguyên lý hoạt động của bộ sinh mã trình biên dịch Chuơng 3 giới thiệu về debugger Kế tiếp, chương 4 sẽ trình bày các nghiên cứu liên quan trong việc phát triển debugger cho bộ sinh mã trình biên dịch Chương 5 tập trung chính vào phát triển debugger cho bộ sinh mã cho trình biên dịch và chương 6 đưa ra kết luận và hướng phát triển của đề tài

Trang 13

CHƯƠNG 2 BỘ SINH MÃ TRÌNH BIÊN DỊCH

Chương này trình bày tổng quan về nguyên lý hoạt động chung của các bộ sinh mã trình biên dịch, phương pháp sinh mã cho bộ phân tích từ vựng và hai phương pháp sinh mã cho bộ phân tích cú pháp: phương pháp LR (từ dưới lên) và

LL (từ trên xuống)

Các bộ sinh mã trình biên dịch cung cấp ngôn ngữ cho phép đặc tả hình thức phân tích từ vựng (lexer) và phân tích cú pháp (parser) Từ đặc tả này, bộ sinh mã sẽ tiến hành xử lý và sinh ra mã tương ứng cho bộ phân tích từ vựng và cú pháp Hình 2-1 minh họa quá trình này

Hình 2-1: Nguyên tắc hoạt động của bộ sinh mã trình biên dịch

Trong quá trình hoạt động phân tích cú pháp, bộ phân tích cú pháp sẽ sử dụng bộ phân tích từ vựng để lấy các token phục vụ cho quá trình phân tích Mối quan hệ giữa 2 bộ phân tích này được minh họa trong Hình 2-2

Hình 2-2: Giao tiếp giữa bộ phân tích từ vựng và cú pháp

Phần tiếp theo sẽ trình bày chi tiết về nguyên lý hoạt động của bộ phân tích

và bộ sinh mã từ vựng (mục 2.1) và cú pháp (mục 2.2)

Trang 14

2.1 Bộ phân tích từ vựng

2.1.1 Nguyên lý hoạt động

Nhiệm vụ chính của bộ phân tích từ vựng là tách ra các token phục vụ cho quá trình phân tích cú pháp Từ vựng có thể được đặc tả một cách hình thức dựa vào biểu thức chính quy và văn phạm chính quy Từ đặc tả này, sơ đồ truyền (transition diagram) sẽ được tạo ra Sơ đồ truyền biểu diễn luồng đi của các ký tự nhập trong quá trình quét (scan) các ký tự đầu vào Kết quả thực thi của sơ đồ truyền sẽ đưa ra token

Sơ đồ truyền bao gồm tập các trạng thái (state) và các đường truyền (transition) giữa các trạng thái Mỗi đường truyền được quy định điều kiện dựa trên

ký tự nhập Điều kiện này thỏa thì đường truyền mới được thực thi

Để minh họa ta xét văn phạm chính quy đặc tả chữ số như sau (Hình 2-3):

number  digit+ ('.' digit+ ‘E’)? ('+' | ‘-‘)? digit+ ; digit  ‘0’ ’9’ ;

Hình 2-3: Văn phạm chính quy nhận dạng chữ số

Khi đó sơ đồ truyền cho luật sinh number như Hình 2-4

Hình 2-4: Sơ đồ truyền cho quá trình nhận dạng chữ số (luật sinh number)

Trang 15

Trạng thái được biểu diễn dưới dạng các vòng tròn và các đường truyền là các mũi tên nối giữa các vòng tròn Trên các mũi tên quy định điều kiện ký tự nhập

để từ trạng thái này có thể chuyển sang trạng thái khác Ví dụ ta đang ở trạng thái xuất phát(trạng thái 1 - vòng tròn in đậm) thì khi gặp ký tự số ta chuyển sang trạng thái 2 Tại trạng thái 2 ta có ba đường đi Nếu ký tự nhập tiếp theo là số thì trạng thái 2 vẫn giữ nguyên Trong trường hợp là ký tự ‘.’ ta chuyển sang trạng thái 3 và cuối cùng ta chuyển sang trạng thái 5 nếu ký tự nhập là ‘E’

Các lý thuyết về automat hữu hạn (finite automata) được áp dụng để xây dựng sơ đồ truyền từ đặc tả văn phạm Có hai loại automat hữu hạn:

 Xác định (deterministic finite automata – DFA) là automat mà ở đó mỗi trạng thái chỉ có duy nhất 1 đường truyền đi ra trên cùng ký hiệu nhập Hình 2-2 minh họa một DFA

 Không xác định (nondeterministic finite automata – DFA) là automat mà

ở đó trạng thái hiện tại nào đó có nhiều đường truyền đi ra trên cùng ký hiệu nhập

Chi tiết quá trình xây dựng DFA hoặc NFA từ đặc tả có thể tham khảo trong [1] Các DFA hoặc NFA được lưu trữ sử dụng bảng truyền (transition table) Bảng truyền có dạng như Hình 2-6

Hình 2-6: Bảng truyền cho

sơ đồ Hình 2-5

Hình 2-5: Sơ đồ truyền (NFA) cho biểu

thức chính quy (a|b)*abb

Trang 16

Như ta đã biết đặc tả cho phân tích từ vựng bao gồm một tập các văn phạm chính quy Vậy làm thế nào để bộ phân tích từ vựng có thể tách được các token này

từ các đặc tả này? Phần tiếp theo sẽ trình bày hai cách tách dữ liệu nhập thành các token dựa vào NFA và DFA

Hình 2-8: NFA cho luật sinh ID1

Hình 2-9: NFA cho luật sinh ID2

Hình 2-10: NFA cho luật sinh ID3

Trang 17

Từ 3 sơ đồ trên ta xây dựng sơ đồ NFA (Hình 2-11) tổng hợp bằng cách thêm trạng thái 0 nối với các trạng thái bắt đầu của 3 sơ đồ này với điều kiện chuyển là (ký tự đọc là rỗng) Từ đó ta áp dụng giải thuật nhận dạng để tiến hành tách token Tùy vào trạng thái kết thúc đạt được ta sẽ phân loại được các token Nếu trạng thái 2 thì token sẽ là ID1, trạng thái 6 token sẽ là ID2 và trạng thái 8 token là ID3

Hình 2-11: NFA tổng hợp cho 3 luật sinh ID1, ID2 và ID3

Từ trạng thái 0, giải thuật nhận dạng sẽ thực thi bước nhìn trước (look-ahead)

ký hiệu nhập để xác định trạng thái kế tiếp là 1, 3 hoặc 7 (thực tế là bất cứ khi nào gặp các đường truyền có điều kiện là thì bước nhìn trước được thực thi) Hình 2-12 minh họa thủ tục nhìn trước cho NFA Hình 2-11

la = look-ahead(input); //nhìn trước 1 ký tự nhập

if (la == ‘a’) state = 1 else if (la == ‘c’) state = 3 else if (la == ‘b’ || la == ‘d’) state = 7 else error();

//tiếp thục thực thi giải thuật nhận dạng

Hình 2-12: Thủ tục thực thi bước nhìn trước

Trang 18

Thủ tục nhìn trước kết hợp với thủ tục nhận dạng token dựa trên NFA cho ra thủ tục tách token như sau:

while a ≠ null do begin

s0 = trạng thái kết quả của quá trình nhìn trước

Trong đó

 F: tập trạng thái kết thúc của NFA

 move(S,a): tập trạng thái có thể đạt đến từ trạng thái trong tập S với ký hiệu nhập là a

 -closure(T): tập trạng thái có thể đạt đến từ trạng thái s ∈T trên ký hiệu nhập rỗng( ) Chi

tiết giải thuật tính tham khảo [1]

Hình 2-13: Thủ tục nhận dạng dựa trên NFA

2.1.1.2 Tách token sử dụng DFA

Do có sự tương đồng giữa NFA và DFA, ta có thể áp dụng giải thuật để chuyển từ NFA sang DFA [1] Sau đó áp dụng giải thuật nhận dạng dựa trên DFA này cho quá trình tách token Xét lại ví dụ Hình 2-11, sau khi áp dụng giải thuật chuyển từ NFA sang DFA ta sẽ được bảng truyền DFA như Hình 2-14

Trang 19

Trạng thái Ký hiệu nhập Kết quả nhận

- ID3 ID2

Hình 2-14: Bảng trạng thái chuyển từ NFA sang DFA

Việc xây dựng thủ tục nhận dạng dựa trên sơ truyền tương đối đơn giản Một vòng lặp sẽ được thực thi chuyển trạng thái cho đến khi nhận dạng được token hoặc phát sinh lỗi Hình 2-15 minh họa chi tiết giải thuật này

Trang 20

else error();

break;

case 5:

if(c==’+’ || c==’-‘) state = 6 else if(isdigit(c)) state = 7;

end

}

Hình 2-15: Thủ tục nhận dạng dựa trên DFA

Mỗi lần lặp một ký tự sẽ được đọc vào Các case trong lệnh switch chính là

các trạng thái hiện tại Dựa vào trạng thái hiện tại và ký tự đọc vào thủ tục sẽ tiến hành ra quyết định xem trạng thái kế tiếp là trạng thái nào

Một cách khác là ta có thể sử dụng bảng truyền để thực thi quá trình nhận dạng Thủ tục minh Hình 2-16 họa giải thuật nhận dạng token dựa trên bảng truyền thủ tục move(s,c) sẽ thực hiện tra trong bảng truyền trạng thái kế tiếp trong trường hợp hợp trạng thái hiện tại là s và ký tự nhập là c hoặc phát sinh lỗi trong trường hợp không tìm thấy trạng thái kế tiếp

Trang 21

if(s == null) error();

else if(s == trạng thái kết thúc)

Bộ phân tích từ vựng

Đặc tả từ vựng Bộ sinh

Bảng truyền (transition table)

Giải thuật nhận dạng automat

Hình 2-17: Bộ sinh mã phân tích từ vựng

Trang 22

2.2 Bộ phân tích cú pháp

Hiện nay có hai giải thuật được sử dụng rộng rãi là phân tích LR(k) và

LL(k) Phần tiếp theo sẽ trình bày chi tiết về hai giải thuật này

2.2.1 LR (k)

Bộ phân tích LR hay còn gọi là bộ phân tích từ dưới lên (bottom-up) đọc dữ liệu đầu vào từ trái sang (left-to-right) và xây dựng các dẫn xuất phải nhất (rightmost), k là số lượng ký hiệu (token) nhìn trước để thực hiện quyết định phân tích Các bộ sinh mã thuộc họ LR bao gồm: Flex/Bison [2], Lex/Yacc [3], GoldParser [4]…

Bộ sinh mã LR hoạt động bằng cách phân tích văn phạm đặc tả và sinh ra bảng action/goto Có 3 phương pháp xây dựng bảng action/goto: SLR, Canonical LR, LALR [1] Dựa vào bảng này và các token đọc vào, bộ phân tích thực hiện quá trình shift/reduce cho đến khi kết thúc hoặc có lỗi xảy ra Hình 2-18 minh họa mô hình hoạt động của bộ phân tích LR

Hình 2-18: Mô hình hoạt động của bộ phân tích LR

Trang 23

Thủ tục Hình 2-19 minh họa giải thuật shift/reduce của bộ phân tích LR dựa vào bảng action/goto cho quá trình phân tích cú pháp:

1 set ip to point to the first symbol of w$

2 repeat forever begin

3 let s be the state on top of the stack and

4 a the symbol pointed to by ip;

5 if action[s,a] = shift s’ then begin

6 push a then s’ on top of the stack

7 advance ip to the next input symbol

8 end

9 else if action[s,a] = reduce A b then begin

10 pop 2*|b| symbols off the stack;

11 let s’ be the state now on top of the stack;

12 push A then goto[s’,A] on top of the stack;

13 output the production A  b

Hình 2-19: Thủ tục phân tích cú pháp dựa trên bảng action/goto

Ví dụ dưới (Hình 2-20) minh họa văn phạm và bảng action/goto sinh ra theo giải thuật SLR cho văn phạm này:

Trang 24

Hình 2-20: Bảng action/goto của văn phạm sử dụng phương pháp SLR

Để minh họa, ta sẽ tiến hành chạy từng bước giải thuật trên với dữ liệu input:

shift 5 reduce by F→id reduce by T→T*F reduce by E→T shift 6

shift 5 reduce by F→id reduce by T→F reduce by E→E+T

accept

F→id T→F

F→id T→T*F E→T

F→id T→F E→E+T

Hình 2-21: Quá trình chạy từng bước giải thuật phân tích LR

Trang 25

Bộ phân tích LL được phân ra thành hai loại: Recursive Decent Parser và Non-recursive Predictive Parser

2.2.2.1 Recursive Decent Parser (RDP)

RDP thực thi quá trình phân tích bằng cách gọi đệ quy các thủ tục Các thủ tục này tương ứng với luật sinh Quy tắc sinh mã cho bộ sinh RDP như sau:

 Mỗi ký hiệu không kết thúc tương ứng với một chương trình con

 Mỗi ký hiệu không kết thúc ở vế phải của luật sinh tương ứng với lời gọi chương trình con

 Mỗi ký hiệu kết thúc ở vế phải tương ứng với lời gọi hàm match kiểm tra

tính hợp lệ của ký hiệu kết thúc này

 Các hành vi ngữ nghĩa được sao chép nguyên bản theo thứ tự tương ứng trong luật sinh

 Đối với ký hiệu không kết thúc có nhiều lựa chọn (alternative) ở vế phải

α → β1 | β2 |…| βn |, mã sinh ra sẽ là lệnh switch/case (hoặc if/else) kiểm tra điều kiện để thực thi lựa chọn thích hợp (Hình 2-22)

Trang 26

case điều kiện n:

mã thực thi βn;

break;

}

Hình 2-22: Mã sinh ra ứng với ký hiệu không kết thúc có nhiều lựa chọn

 Đối với luật sinh có dạng α → β+, mã sinh ra có dạng sau (Hình 2-23)

mã thực thi β;

while(true){

if (điều kiện dừng) break;

else

mã thực thi β;

}

Hình 2-23: Mã sinh ra của luật sinh có dạng α → β+

 Đối với luật sinh có dạng α → β*, mã sinh ra sẽ có dạng sau (Hình 2-24):

while(true){

if (điều kiện dừng) break;

else

mã thực thi β;

} Hình 2-24: Mã sinh ra của luật sinh có dạng α → β*

 Đối với luật sinh có dạng α → β?, mã sinh ra sẽ có dạng như sau (Hình 2-25):

if (điều kiện thực thi β)

mã thực thi β;

Hình 2-25: Mã sinh ra của luật sinh có dạng α → β?

Để hiểu rõ hơn quy tắc sinh mã trên, xét ví dụ sau (Hình 2-26):

prog ::= ‘program’ body

Trang 27

Hình 2-26: Văn phạm và mã sinh ra tương ứng theo giải thuật RDP

Ta thấy rằng luật sinh prog sẽ tương ứng với thủ tục prog bên mã sinh Kế

tiếp do gặp ký hiệu kết thúc ‘program’ nên dòng mã tiếp theo sẽ thực thi lệnh match nhằm đảm bảo token hiện tại có giá trị ‘program’ Tiếp theo vế phải body sẽ tương ứng với lời gọi thủ tục body và cuối cùng hành vi ngữ nghĩa action_block_1 sẽ được

chép nguyên vẹn sang phần tiếp theo của mã sinh ra

2.2.2.2 Non-recursive Predictive Parser (NPP)

Khác với RPP, NPP không thực hiện gọi đệ quy các thủ tục mà thay vào đó

sử dụng stack và thực hiện các thao tác push và pop tùy thuộc vào token tiếp theo của dữ liệu nhập NPP có cơ chế hoạt động sử dụng bảng phân tích giống với bộ sinh LR Hình 2-27 minh họa mô hình hoạt động của NPP

Hình 2-27: Các thành phần của bộ sinh NPP

Trang 28

Thủ tục hoạt động của bộ sinh NPP như sau (Hình 2-28):

1 set ip to point to the first symbol of w$

9 if M[X,a] = XY1Y2…Yk then begin

10 pop X from the stack;

11 push Yk, Yk-1 ,…,Y1 onto the stack with Y on top

12 output the production X  Y1Y2…Yk

Hình 2-29: Văn phạm tính giá trị biểu thức

Do văn phạm chứa luật sinh đệ quy trái, do đó trước tiên ta phải khử đệ quy trái cho văn phạm này Hình 2-30 là luật sinh văn phạm sau khi khử đệ quy trái

Trang 29

Ứng với văn phạm này bảng phân tích sẽ như sau (Hình 2-31):

E’ 

T’ 

Hình 2-31: Bảng phân tích của văn phạm LL(1)

Thực hiện quá trình chạy từng bước giải thuật phân tích (Hình 2-32), ta ra được kết quả như Hình 2-32

T  FT’

F  id

Hình 2-32: Từng bước quá trình chạy giải thuật NPP

Do tiếp cận theo hướng từ trên xuống nên bộ phân tích LL có cấu trúc gần giống với ngôn ngữ tự nhiên và do đó giúp cho việc đặc tả văn phạm cũng như tìm lỗi dễ dàng hơn so với bộ phân tích LR

Trang 30

Một số bộ sinh hỗ trợ văn phạm thuộc tính (attributed grammar) cho phép đặc tả các hành vi ngữ nghĩa trực tiếp vào văn phạm hoặc cung cấp các phương thức

hỗ trợ đặc tả hành vi ngữ nghĩa thông qua các lớp (class) sinh ra Tùy từng bộ sinh khác nhau mà cách thức hỗ trợ nhúng hành vi ngữ nghĩa trực tiếp vào văn phạm khác nhau Hình 2-33 minh họa cách đặc tả ngữ nghĩa của văn phạm Antlr

expr returns [int value]

 e=multExpr {$value = $e.value;}

( '+' e=multExpr {$value += $e.value;}

| '-' e=multExpr {$value -= $e.value;}

)*;

multExpr returns [int value]  e=atom {$value = $e.value;}

('*' e=atom {$value *= $e.value;})*;

Hình 2-33: Hành vi ngữ nghĩa nhúng vào văn phạm

Như hình trên ta thấy, văn phạm Antlr cho phép đặc tả tham số (value) và giá

trị trả về cho luật sinh Các biến này có thể sử dụng để nhúng vào ngữ nghĩa bằng cách sử dụng ký hiệu $ Ngoài ra Antlr còn cho phép đặt tên cho các vế phải và sử dụng tên này như các biến

Trang 31

CHƯƠNG 3 DEBUGGER

3.1 Giới thiệu debugger

Debugger là chương trình phần mềm giúp xác định tìm ra nguyên nhân tại sao một chương trình (debugee) hoạt động chưa chính xác Với sự trợ giúp của debugger, lập trình viên có thể theo vết sự thực thi của chương trình, tạm dừng chương trình và kiểm tra trạng thái hiện tại từ đó tìm ra được các lỗi

Debugger thực sự là một chương trình rất phức tạp Bản chất hoạt động bên trong của debugger đòi hỏi sự hiểu biết thuật toán và cấu trúc dữ liệu Một số debugger thậm chí đòi hỏi sự hiểu biết sâu về hệ điều hành (kernel debugger) Quá trình phát triển debugger là một quãng thời gian khá dài, có rất nhiểu loại debugger

ra đời và ngày càng được cải tiến nhằm hỗ trợ tối đa quá trình tìm lỗi

3.2 Phân loại debugger

Các chương trình phần mềm rất đa dạng Mỗi loại đòi hỏi kỹ thuật và debugger riêng Dưới đây là một số cách thức phân loại debugger:

 Source-level (Symbolic) và Machine-level: mức source-level cho phép ta debug trên source code mà không cần quan tâm đến ngôn ngữ máy đã được biên dịch và ánh xạ bởi trình biên dịch

 OS-kernel và Application-level: Kernel debugging là phần quan trong trong quá trình viết driver cho các thiết bị ngoại vi Thường mức này được hỗ trợ bởi hệ điều hành thông qua các công cụ và API Trong khi đó application-level là các ứng dụng giao tiếp với người dùng thông thường

3.3 Kiến trúc tổng quát debugger

Kiến trúc tổng quan của debugger như Hình 3-1 [9] Kiến trúc này bao gồm nhiều tầng khác nhau Vòng ngoài cùng là phần giao diện người dùng tương tác trực tiếp với debugger còn các vòng trong là các thành phân được hệ điều hành cung cấp trợ giúp cho quá trình debug Phần tiếp theo sẽ trình bày ý nghĩa tầng UI, Kernel và OS Debug API

Trang 32

Hình 3-1: Kiến trúc điển hình của debugger

3.3.1 Tầng giao diện (User Interface)

3.3.1.1 Source View

Đây là view giữa vai trò quan trọng nhất của debugger Source View thể hiện trực quan quá trình debugger thực thi các lệnh của chương trình cũng như thiết lập các chức năng hỗ trợ cho quá trình debug (thiết lập điểm dừng…) Hình 3-2 ví dụ

về source view trong visual studio

Trang 33

Hình 3-2: Source View trong Visual Studio và các chức năng sử dụng debug 3.3.1.2 Stack View

Stack view thể hiện quá trình gọi các phương thức (thủ tục) trong quá trình thực thi chương trình Stack này bao gồm tập các stack frame – lưu trữ toàn bộ trạng thái hiện tại của thủ tục được gọi Ở trạng thái dừng, ta có thể chọn bất kỳ frame nào

để xem xét trạng thái Hình 3-3 ví dụ về stack view trong visual studio

Hình 3-3: Stack View trong Visual Studio

Trang 34

3.3.1.3 Breakpoint View

Breakpoint view có chức năng liệt kê danh sách các breakpoint đã được thiết lập Qua đó ta có thể thấy được tổng quan về các breakpoint hiện tại đã được thiết lập trong chương trình Hình 3-4 ví dụ về breakpoint view trong visual studio

Hình 3-4: Breakpoint View trong Visual Studio

3.3.1.4 Control, Disassembly và Hardware Register View (CPU View)

Source view ánh xạ giữa mã nguồn và mã assembly.Tuy nhiên trong một số trường hợp, để hiểu rõ thực sự hoạt động bên trong của một số loại chương trình thì

mã assembly vẫn cần thiết CPU View có nhiệm vụ hiển thị mã assembly, giá trị thanh ghi… Hình 3-5 ví dụ về CPU view trong visual studio

Trang 35

Hình 3-5: CPU View trong Visual Studio

3.3.1.5 Inspect View

Inspect view cho phép định nghĩa các biểu thức nhằm xem xét trạng thái, giá trị hiện tại các biến ứng với thời điểm tạm dừng hiện tại của chương trình (giá trị các biến tại stack frame nào đó…) Hình 3-6 ví dụ về inspect view trong visual studio

Hình 3-6: Inspect View trong Visual Studio

3.3.1.6 Variable View

Variable view hiển thị trực quan giá trị các biến cục bộ của stack frame hiện tại Hình 3-7 ví dụ về variable view trong visual studio

Ngày đăng: 04/04/2021, 00:33

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w