Knowledge Network

Một phần của tài liệu xây dựng bộ phân tích cú pháp chương trình (Trang 44 - 65)

i. Xóa bỏ tất cả các biến frontal, chuỗi wave liên quan tới track node này Xóa bỏ track node

3.5.2Knowledge Network

Knowledge Network trong WI là tập hợp của các Node và các Link nối các Node lại với nhau.

Để tiện cho việc truy xuất trực tiếp tới các Node một cách nhanh chóng qua tên của Node, trong KN có cài đặt thêm bảng NAT (Node Address Translation).

Node trong KN chứa các thông tin sau:

• NAME: tên của node hiện tại trong KN

• LINK TABLE: là một bảng chưa dánh sách các link được nối với node • COLOR TABLE: bảng chứa cách biến Nodal Variable

• NAT INDEX: Vị trí của node trong bảng NAT Link trong KN bao gồm:

• NAME: tên của link

• SIGN: hướng của link và có 3 loại là “+”, “-” và ko có hướng Wave Queue

Wave String Wave Environment

• DESTINATION: địa chỉ của node đích

Link trong KN được chia làm 2 phần khi liên kết giữa 2 node, mỗi node sẽ chứa một phần link đó trong bảng LINK TABLE. Ngoài ra địa chỉ của trường DESTINATION trong link là tập hợp của 2 địa chỉ: địa chỉ của máy chứa node mà link nối tới và địa chỉ của node đó trong bảng NAT nằm trên máy tính đó.

Hình 3-25: Knowledge Network

Như trên hình vẽ ta có: node a có LINK TABLE gồm 2 phần tử là +p và r, trong khi đó node c có LINK TABLE chứa 1 phần tử là -p.

3.5.3 Track Forest

Track Forest được quản lý bởi Track Processor, được xem là toàn bộ các Track

a c b Máy 2 Máy 1 p r

Hệ thống Track được sinh ra trong quá trình xử lý các Rule. Thành phần chính của Track Forest bao gồm các Track Node (TN) được chứa trong một bảng gọi là Track Table, Track Table chính là ánh xạ trực tiếp từ địa chỉ vào vùng nhớ chứa TN. Các TN liên kết với nhau tạo thành một cấu trúc dạng cây. Ngoài ra Track Forest còn một thành phần cấp phát địa chỉ cho TN để đảm bảo khi TN được sinh ra thì nó là duy nhất trong mạng máy tính. Địa chỉ của TN thực chất bao gồm 2 thành phần đó là: địa chỉ của máy cục bộ đang chạy WI và thành phần địa chỉ của TN trên máy cục bộ (thành phần này được cấp phát một cách tự động)

Cấu trúc dữ liệu của Track Node được mô tả như sau:

• TYPE: Kiểu của track dùng để phân biệt phân biệt các nhánh của track là được sinh ra từ lệnh hop hay từ các sector trong Wave. Kiểu này có thể là PROP (cho lệnh hop) hoặc là SECT (cho các sector)

• ADDRESS: địa chỉ của track node khi được cấp pháp bởi Track Forest • HOST: địa chỉ của máy tính nơi Track Node được sinh ra.

• PARENT: Trỏ đến TN cha.

• CHILDREN: là một danh sách các TN con.

• RULE: luật được áp dụng cho Track để xác định các thức xử lý khi track nhận được echo từ các track con.

• ACTIVE BRANCH: vị trí của nhánh (track con) đang được phát triển. • SUSPENDED BRANCH: là một hàng đợi các nhánh chờ được xử lý.

• ECHO: giá trị tổng hợp echo khi nhận được từ các nhánh con. Echo bao gồm 4 loại là: TRUE, FALSE, DONE, ABORT.

• REMAINING ECHO: số lượng echo còn lại chờ nhận được từ các nhánh con. • ENCLOSED WAVE: chuỗi wave chứa trong Rule

• SUSPENDED TAIL: chỗi wave còn lại khi loại bỏ toàn bộ Rule • WAVE ENVIRONMENT: Giá trị của các biến mỗi trường wave

Hình 3-26: Track Forest PROP ADDR2 OS Parent Children TRUE … PROP ADDR1 AP Parent Children TRUE … SECT ADDR3 NM Parent Children TRUE … SECT ADDR4 NM Parent Children FALSE …

3.5.4 Parsing Unit

Trong bất cứ trình biên dịch, hay thông dịch nào, thành phần parser bao giờ cũng là quan trọng nhất. Thành phần parser chịu trách nhiệm phân tích các đoạn mã wave rồi từ đó đưa ra các hành động tương ứng với đoạn mã phân tích được. Vì tính chất quan trọng của nó nên thành phần này sẽ được mô tả chi tiết dưới đây.

1. Giới thiệu về JavaCC a. JavaCC là gì? (adsbygoogle = window.adsbygoogle || []).push({});

JavaCC – Java Compiler Compiler là bộ sinh parser hiện đang được dùng phổ biến trong các ứng dụng Java. Bộ sinh parser này sẽ đọc các đặc tả cú pháp của ngôn ngữ nào đó và chuyển nó thành chương trình Java – chương trình có thể nhận dạng ra chính xác cấu trúc của ngôn ngữ đó. Bên cạnh đó, công cụ

JavaCC còn cung cấp các khả năng khác trong quá trình đọc cú pháp như việc xây dựng cây, gỡ lỗi…

Để sinh ra bộ mã Parser cho ngôn ngữ wave ta sử dụng 2 công cụ trong gói JavaCC bao gồm:

- JJTree: đọc đặc tả file .jjt (jjt bao gồm các đặc tả về ngôn ngữ wave bao gồm các token và các luật triển khai, cây cú pháp của wave) và đưa ra đặc tả về Token (các files AST: abstract synctax tree) và file .jj dùng cho việc sinh ra mã nguồn parser

- JavaCC: đọc đặc tả .jj và sinh ra bộ mã bao gồm parser và các mã để kiểm soát lỗi của ngôn ngữ wave

b. Quá trình xử lý của JavaCC

Quá trình phân tích từ vựng và cú pháp diễn ra song song với nhau. Bộ phân tích từ vựng sẽ đọc từng ký tự (ký tự trong các đoạn mã của wave) sau đó sẽ sinh ra các đối tượng Token. Cách tách các ký tự thành token tùy thuộc vào người lập trình – đó là các định nghĩa nằm trong biểu thức chính quy. JavaCC cho phép sử dụng luật sinh EBNF để định nghĩa.

Mỗi token trong quá trình phân tích tự vựng sẽ là đầu vào cho quá trình phân tích cú pháp để thực thi phân tích cấu trúc của ngôn ngữ và sinh ra cây cú pháp.

c. Các ngôn ngữ có thể xây dựng bởi JavaCC

Rất nhiều loại ngôn ngữ khác nhau có thể được sinh bởi JavaCC như: Visual Basic, Python, Rational Rose mdl files, XML, XML DTDs, HTML, C, C++, Java, JavaScript, Oberon, SQL, VHDL, VRML, ASN1,.. và rất nhiều loại ngôn ngữ khác nữa: WAVE...

2. File cú pháp của JavaCC

Để javaCC có thể biên dịch một đặc tả cú pháp sang dạng mã java, cần có một quy ước về cách viết file cú pháp để từ đó có thể sinh ra các đoạn mã java (phục vụ cho quá trình parser hay quản lý token) một cách hiệu quả.

Cú pháp file đặc tả JavaCC bao gồm các thành phần sau: javacc_input ::=javacc_options "PARSER_BEGIN" "(" <IDENTIFIER> ")" java_compilation_unit "PARSER_END" "(" <IDENTIFIER> ")" ( production )* <EOF>

Một file cú pháp bắt đầu với một danh sách các tùy chọn. Theo sau là thành phần biên dịch java được bao trong hai từ khóa "PARSER_BEGIN(parser_name)" và "PARSER_END(parser_name)". Tiếp đến là danh sách các cú pháp luật sinh. Các tùy chọn và luật sinh sẽ được trình bày dưới đây.

Tham số name trong cặp từ khóa PARSER_BEGIN và PARSER_END là tên của bộ phân tích cú pháp được sinh ra. Đối với đề tài name có tên là WaveParser thì JavaCC tự động chèn các tên này vào sau các file tùy chọn được sinh ra như WaveParserConstants.java, WaveParserTokenManager.java … PARSER_BEGIN(parser_name) . . . class parser_name . . . { . . . } . . . PARSER_END(parser_name)

a. Các từ khóa và tùy chọn của JavaCC

JavaCC cung cấp một tập các từ khóa như bảng sau:

EOF IGNORE_CASE JAVACODE LOOKAHEAD

Ngoài ra JavaCC còn cung cấp một số từ khóa thuộc loại tùy chọn trong một file đặc tả cú pháp của JavaCC.

LOOKAHEAD: Số token được nhìn trước trước khi quyết định lựa chọn một điểm trong quá trình phân tích. Mặc định có giá trị 1. Số nhỏ hơn, quá trình phân tích nhanh hơn. Số này có thể định nghĩa lại cho các luật sinh cụ thể trong phạm vi ngữ pháp để định rõ tính chất giai đoạn sau.

CHOICE_AMBIGUITY_CHECK: Đây là một tùy chọn số nguyên có giá trị mặc định là 2. Đây là số token có được trong việc kiểm tra sự lựa chọn của hình thức "A | B | ..." cho tình trạng có nhiều nghĩa.

OTHER_AMBIGUITY_CHECK: Đây là một tùy chọn số nguyên có giá trị mặc định là 1. Đây là số token có được trong việc kiểm tra tất cả các hình thức khác nhau của sự lựa chọn (ví dụ của hình thức "(A)*", "(A)+", and "(A)?") cho tình trạng có nhiều nghĩa. Việc này mất nhiều thời gian hơn sự kiểm tra lựa chọn, do đó giá trị mặc định được thiết lập là 1 hơn là 2.

STATIC: Đây là một tùy chọn kiểu boolean có giá trị mặc định là true. Nếu true tất cả các phương thức và biến của lớp theo lý thuyết đều là static trong sự phát sinh cú pháp và quản lý token.

DEBUG_PARSER: Đây là một tùy chọn kiểu boolean có giá trị mặc định là false. Tùy chọn này được sử dụng để gỡ lỗi từ việc phát sinh cú pháp. Thiết lập tùy chọn này là true là nguyên nhân quá trình phân tích cú pháp phát sinh dấu hiệu của hành động.

DEBUG_LOOKAHEAD: Đây là tùy chọn kiểu boolean có giá trị mặc định là false. Thiết lập tùy chọn này là true là nguyên nhân quá trình phân tích cú pháp phát sinh tất cả dấu hiệu thông tin nó thực hiện khi tùy chọn DEBUG_PARSER là true, đó cũng là nguyên nhân để nó phát sinh ra dấu hiệu hành động đã thi hành trong thao tác lookahead.

DEBUG_TOKEN_MANAGER: Tùy chọn boolean có giá trị mặc định là false. Tùy chọn này được sử dụng để gỡ lỗi thông tin từ các token đã phát sinh. Thiết đặt tùy chọn này là true là nguyên nhân để quản lý token phát sinh dấu hiệu của hành động. Dấu hiệu này khá rộng và chỉ nên sử dụng nó khi có một lỗi từ vựng, những lỗi thông báo đến bạn và bạn không biết tại sao. Điển hình trong trường hợp này, bạn có thể giải quyết vấn đề bằng cách chú ý đến vài dòng trước đó của dấu hiệu.

ERROR_REPORTING: Tùy chọn boolean có giá trị mặc định là true. Thiết lập tùy chọn này là false là nguyên nhân các lỗi mắc phải từ các lỗi phân tích cú pháp có báo cáo thiếu chi tiết. Lý do để thiết lập tùy chọn này là false là để cải tiến sự thực thi.

JAVA_UNICODE_ESCAPE: Đây là tùy chọn boolean có giá trị mặc định là false. Khi thiết lập true, sự phân tích cú pháp đã phát sinh sử dụng một luồng

vào xử lý bởi Java Unicode escapes(\u...) trước khi gửi ký tự tới token manager. Mặc định Java Unicode escapes không xử lý.

UNICODE_INPUT: Đây là tùy chọn kiểu boolean giá trị mặc định là false. Khi thiết lập true, sự phân tích cú pháp đã phát sinh sử dụng một luồng nhập để đọc file Unicode. Mặc định file ASCII được thừa nhận. (adsbygoogle = window.adsbygoogle || []).push({});

Tùy chọn này được bỏ qua nếu một trong hai tùy chọn USER_TOKEN_MANAGER, USER_CHAR_STREAM được thiết lập là true.

IGNORE_CASE: Tùy chọn kiểu boolean có giá trị mặc định là false. Thiết lập tùy chọn này là true là nguyên nhân phát sinh ra token manager để bác bỏ trong trường hợp token được chỉ định rõ và những hồ sơ đưa vào. Tùy chọn này hữu dụng trong việc viết ngữ pháp cho ngôn ngữ như HTML.

USER_TOKEN_MANAGER: Tùy chọn kiểu boolean, giá trị ngầm định là false. Hành động mặc định là phát sinh ra một token manager làm việc trên những token ngữ pháp đã chỉ rõ. Nếu nó được thiết lập true, bộ phân tích cú pháp được phát sinh để thừa nhận những token từ bất kỳ token quản lý nào thuộc kiểu "TokenManager"- giao diện được phát sinh vào trong thư mục bộ phân tích cú pháp.

b. Bộ quản lý Token

Phần đặc tả từ vựng của JavaCC được tổ chức thành tập các trạng thái từ vựng “lexical states”. Mỗi trạng thái từ vựng có một định danh. Trạng thái từ vựng chuẩn là DEFAULT. Trình quản lý token được sinh ra tại mỗi thời điểm sẽ có một trạng thái từ vựng trong tập các trạng thái từ vựng. Khi trình quản lý token khởi tạo nó sẽ có trạng thái từ vựng mặc định là DEFAULT. Trạng thái từ vựng bắt đầu có thể được xác định giống như là tham số khi xây dựng một đối tượng quản lý token.

Có 4 loại biểu thức chính qui: SKIP, MORE, TOKEN, và SPECIAL_TOKEN.

o SKIP: Bỏ qua chuỗi từ vựng khi gặp khai báo SKIP.

o MORE: Tiếp tục cho đến khi gặp trạng thái kế tiếp, đưa chuỗi từ vựng khi gặp khai báo MORE vào trạng thái chờ. Chuỗi này sẽ là tiền tố cho chuỗi kế tiếp.

o TOKEN: Tạo ra một token sử dụng chuỗi và gửi nó vào bộ phân tích cú pháp.

o SPECIAL_TOKEN: Tạo ra một token đặc biệt không tham gia vào quá trình phân tích.

c. Luật sinh

Có 4 loại luật sinh trong JavaCC gồm javacode_production , bnf_production, regular_expr_production, token_manager_decls.

javacode_production và bnf_production được sử dụng để định nghĩa ngữ pháp trực tiếp cho bộ phân tích cú pháp. regular_expr_production được sử

các ngữ pháp này để tạo ra Token manager, có thể hiểu đây các đặc tả token trong bộ phân tích cú pháp. token_manager_decls được sử dụng để giới thiệu các khai báo dùng để chèn vào bên trong trình quản lý token.

javacode_production

javacode_production là một cách để viết mã Java cho một số các luật sinh thay vì sử dụng EBNF. Điều này hữu ích khi mà việc định nghĩa một cú pháp bằng EBNF quá khó khăn. Ví dụ sau minh họa cho việc định nghĩa xác định luật sinh của một block.

JAVACODE void skip_to_matching_brace() { Token tok; int nesting = 1; while (true) { tok = getToken(1);

if (tok.kind == LBRACE) nesting++; if (tok.kind == RBRACE) { nesting--; if (nesting == 0) break; } tok = getNextToken(); } } • bnf_production

bnf_production ::= java_access_modifier java_return_type java_identifier "(" java_parameter_list ")" ":"

java_block

"{" expansion_choices "}"

bnf_production là luật sinh chuẩn được sử dụng trong JavaCC.

Mỗi luật sinh BNF có vế trái là một đặc tả không kết thúc. Luật sinh BNF sẽ định nghĩa đặc tả không kết thúc này theo cách thức của BNF mở rộng hay EBNF. Điều này được thực hiện giống như các khai báo của các phương thức trong Java.

Có 2 phần chính ở vế phải của luật sinh BNF :

- Phần thứ nhất là tập các khai báo và mã của Java(Java block). Các mã java này sẽ được sinh ra ở phần đầu của các phương thức cho luật sinh BNF. Mỗi khi phương thức luật sinh này được thực thi trong quá

trình phân tích cú pháp thì những khai báo và mã java này sẽ được thực thi. Khai báo trong phần này sẽ có mặt ở tất cả các hành động trong EBNF. JavaCC không xử lý bất kỳ một khai báo hoặc đoạn mã java nào, ngoại trừ việc bỏ qua các dấu ngoặc đơn và thu thập tất cả mã lệnh để làm cơ sở trong các phương thức được sinh ra sau này. - Phần thứ hai của vế phải là các lựa chọn mở rộng expansion_choices

có cú pháp như sau:

expansion_choices ::= expansion ( "|" expansion )*

Các expansion_choices được viết giống như một danh sách của một hoặc nhiều các mở rộng (expansion) được ngăn cách nhau bởi các dấu hoặc | . expansion ::= ( expansion_unit )*

Một mở rộng(expansion) được viết thành một tập các đơn vị mở rộng(expansion units). Một expansion đúng khi tất cả các expansion units đều đúng. Ví dụ, mở rộng (expansion) "{" decls() "}" bao gồm 3 đơn vị mở rộng(expansion units) “{” , decls(), và “}”. Một cú pháp đúng cho mở rộng này là phải bắt đầu bằng một dấu “{” kết thúc bởi dấu “}” và thỏa mãn hàm decls() ở phần thân. (adsbygoogle = window.adsbygoogle || []).push({});

Một đơn vị mở rộng(expansion_unit) có thể là một lookahead, một tập các khai báo và mã java (java_block), Các lựa chọn mở rộng(expansion_choices), .. expansion_unit ::= local_lookahead | java_block | "(" expansion_choices ")" [ "+" | "*" | "?" ] | "[" expansion_choices "]" | [ java_assignment_lhs "=" ] regular_expression | [ java_assignment_lhs "=" ] java_identifier "(" java_expression_list ")"

Một đơn vị mở rộng có thể gồm các lựa chọn mở rộng có các tùy chọn “+”, “*”, “?”.

lookahead ::= "LOOKAHEAD" "(" [ java_integer_literal ] [ "," ] [ expansion_choices ] [ "," ] [ "{" java_expression "}" ] ")"

Số token được nhìn trước trước khi quyết định lựa chọn một điểm trong quá trình phân tích. Mặc định có giá trị 1. Số nhỏ hơn, quá trình phân tích nhanh hơn. Số này có thể định nghĩa lại cho các luật sinh cụ thể trong phạm vi ngữ pháp để định rõ tính chất giai đoạn sau.

Ví dụ của luật sinh BNF: void WriteStatement() : { Token t; }

{ "printf" t = <IDENTIFIER> { jjtThis.name = t.image; } } • regular_expr_production regular_expr_production ::= [ lexical_state_list ] regexpr_kind [ "[" "IGNORE_CASE" "]" ] ":" "{" regexpr_spec ( "|" regexpr_spec )* "}"

Một luật sinh biểu thức chính qui (regular_expr_production) được sử dụng để định nghĩa các thực thể từ vựng sẽ được phân tích bởi trình quản lý token.

Một luật sinh biểu thức chính qui (regular_expr_production) bắt đầu với một đặc tả của các tình trạng cho ứng dụng của nó. Tình trạng chuẩn là DEFAULT, đây là tình trạng mặc định cho các thực thể token.

token_manager_decls

token_manager_decls ::= "TOKEN_MGR_DECLS" ":" java_block

Phần khai báo của trình quản lý token ( token_manager_decls ) bắt đầu bằng từ khóa "TOKEN_MGR_DECLS" theo sau là dấu “:” và tập các

Một phần của tài liệu xây dựng bộ phân tích cú pháp chương trình (Trang 44 - 65)