Luận văn Xây dựng trình biên dịch cho ngôn ngữ wave

Chúng ta muốn tạo lưới và hiển thị trên chương trình hiển thị, cần lựa chọn tệp tin tạo lưới trong thưmục Simulation. Tùy thuộc vào nội dung mà chúng ta tạo lưới sẽ được tạo khác nhau.

pdf93 trang | Chia sẻ: lylyngoc | Ngày: 26/10/2013 | Lượt xem: 1463 | Lượt tải: 0download
Bạn đang xem nội dung tài liệu Luận văn Xây dựng trình biên dịch cho ngôn ngữ wave, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
ao 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 MORE options PARSER_BEGIN PARSER_END SKIP SPECIAL_TOKEN TOKEN TOKEN_MGR_DECLS 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. - 47 - 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. 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. - 48 - 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ử dụng để định nghĩa ngữ pháp của các Token, trình quản lý Token sử dụng 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; - 49 - 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 )* - 50 - 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. 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 = { jjtThis.name = t.image; } } • regular_expr_production regular_expr_production ::= [ lexical_state_list ] regexpr_kind [ "[" "IGNORE_CASE" "]" ] ":" "{" regexpr_spec ( "|" regexpr_spec )* "}" - 51 - 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 khai báo và câu lệnh java(Java Block). 3. 5. 5 Excution Processor Excution Processor (EP) là thành phần đảm nhiệm chức năng xử lý các phép toán và các lệnh trong wave. EP sẽ được gọi đến để xử lý sau quá trình phân tích để lấy xác định phép toán cần được xử lý trong Wave Head. Các phép toán trong Wave được xử lý ở mức đơn giản là chỉ gồm một toán tử và hai toán hạng. Cấu trúc của EP bao gồm 3 thành phần chính đó là: Execution Control Unit (ECU), Operand Fetch Unit (OFU), Data Processing Unit (DPU) và Matching Unit (MU). • Execution Control Unit sẽ lấy ra chỉ lệnh và các tên của các toán hạng bên trái, bên phải. ECU chịu trách nhiệm điều khiển quá trình xử lý. • Operand Fetch Unit sẽ lấy ra giá trị của các biến bên trái, bên phải của toán hạng và điều khiển quá trình lưu trữ dữ liệu. • Data Processing Unit là thành phần xử lý sau khi đã xác định được toán tử và dữ liệu sẵn sàng. • Matching Unit đảm nhận công việc xử lý tìm kiếm trong KN. - 52 - Hình 3-11: Excution Processor 3. 5. 6 TrackProcessor Track Processor (TP) quản lý Track Forest của WI. TP xử lý tất cả các echo và suspended tail thông qua track. Track Processor nhận thông điệp điều khiển từ các thành phần khác thông qua Track Queue (TQ). Các thông điệp đi vào Track Queue gồm có: • CREATE: Thông điệp yêu cầu tạo track. • EXPANDH, EXPANDS: Tạo các nhánh con (Track con) của track hiện tại. EXPANDH dùng trong trường hợp các nhánh được sinh ra bởi lệnh hop, còn EXPANDS là trường hợp các nhánh sinh ra bởi các sector của wave head. • ACTIVATE: Thông điệp này sẽ được gửi ngay sau khi gửi hết thông điệp EXPAND để kích hoạt TN hiện tại. • ECHO: Thông điệp echo từ các nhánh con được gửi lên TN cha. • TAIL: Thông điệp gửi chuỗi wave tail đến các nhánh con để tiếp tục phát triển. Dưới đây là một quá trình nhận và xử lý các thông điệp của Track Processor - 53 - PROP Parent / Children / NONE 0 Wave Env #.C==b b c a PROP Parent Children / NONE 0 Wave Env b c a PROP Parent Children / NONE 0 Wave Env PROP Parent Children NONE 2 Wave Env C==b C==b TRR2 TRR3 TRR1 Hình 3-12: Sau khi nhận và xử lý CREATE Hình 3-13: Sau khi nhận và xử lý EXPANDH - 54 - PROP Parent Children / NONE 0 Wave Env b c a PROP Parent Children / NONE 0 Wave Env PROP Parent / Children NONE 2 Wave Env C==b C==b TRR2 TRR3 TRR1 Hình 3-14: Sau khi nhận và xử lý ACTIVATE - 55 - PROP Parent Children / NONE 0 Wave Env b c a PROP Parent Children / NONE 0 Wave Env PROP Parent / Children NONE 0 Wave Env C==b C==b TRR2 TRR3 TRR1 -1 2x Hình 3-15: Sau khi nhận ECHO từ các nhánh con - 56 - 3. 5. 7 Communication Processor Communication Processor đóng vai trò liên lạc giữa các WI trên các máy tính trong mạng. Khi chuỗi wave di chuyển trong KN bằng các lệnh hop, chúng có thể lan truyền từ WI trong máy tính này sang WI trên máy tính khác. Quá trình di chuyển của các chuỗi wave đó đều phải thông qua CP. Để đảm bảo dữ liệu giữa các máy tính là an toàn nên CP xử dụng giao thức TCP/IP của tầng giao vận để truyền và nhận các thông điệp, kết nối giữa các máy tính sẽ được giữ nguyên từ lúc các WI được thiết lập cho đến khi WI rời khỏi mạng. CP quản lý Communication Queue, đây là một hàng đợi chứa các thông điệp sẽ được gửi đi. PROP Parent Children / NONE 0 Wave Env b c a PROP Parent / Children NONE 0 Wave Env C==b TRR2 TRR1 -1 2x Hình 3-16: Sau khi xử lý ECHO nhận được - 57 - Cấu trúc chung của các thông điệp: • Type: kiểu của thông điệp bao gồm : o WAVE: thông điệp gửi wave đi o CREATE TRACK: thông điệp yêu cầu tạo track trên máy khác, o CREATE NODE: thông điệp yêu cầu tạo một node trong KN, o CREATE LINK: thông điệp yêu cầu tạo link giữa 2 node trong KN, o ECHO: thông điệp gửi echo đi, TAIL: thông điệp gửi wave tail đi) • Destination Address: địa chỉ của máy nhận thông điệp. • Source Address: địa chỉ của máy gửi thông điệp. • Data: Thành phần dữ liệu của thông điệp. 3. 6 Quan hệ giữa các thành phần trong Wave Interpreter Tổ chức các thành phần trong WI và quan hệ giữa chúng được thể hiện trong Hình 3-18: Quan hệ giữa các thành phần trong Wave Interpreter. Các thông điệp đến WI đều thông qua Communication Processor với 3 kiểu thông điệp chính là Wave, Echo và Tail. Type Destination Address Source Address Data Communication Queue Communication Message Hình 3-17: Communication Processor - 58 - Wave nhận được từ CP sẽ được gửi vào WQ để phân tích và xử lý, ngoài ra wave có thể được chèn vào WQ một cách trực tiếp từ WI, hoặc từ TP khi kích hoạt các nhánh để gửi các suspended wave. Thành phần EP cũng có thể gửi các wave vào WQ khi xử lý các sector, các toán tử code injection của ngôn ngữ Wave. Sự lan truyền của wave phụ thuộc vào TP và EP. Nếu trạng thái track của wave được kích hoạt, sự lan truyền của wave phụ thuộc gián tiếp vào EP, đầu tiên EP sẽ gửi thông điệp điều khiển vào TQ, thông điệp sẽ được TP xử lý và chuỗi wave được lan truyền khi track tương ứng được kích hoạt. Thông điệp ECHO và TAIL được CP và TP xử lý. Mỗi khi chuỗi wave kết thúc trong chế độ track EP sẽ tạo ra thông điệp ECHO và gửi vào TQ. Các suspended tail sẽ được TP gửi đi sau khi xử lý các ECHO mà điều kiện của rule được thỏa mãn để kích hoạt track. Các thành phần xử lý trong WI độc lập với nhau, mỗi thành phần sẽ xử lý dữ liệu trong các hàng đợi riêng. Tuy nhiên, EP có thể coi là thành phần xử lý chính, khi EP chịu trách nhiệm điều khiển toàn bộ quá trình xử lý wave và khởi tạo các thông điệp điều khiển tới các thành phần xử lý khác. PARSING UNIT (PU) TRACK PROCESSOR (TP) EXECUTION PROCESSOR (EP) COMMUNICATION PROCESSOR (CP) trackless sectors, code injection, hop, action track operations TQ resumed tails, activated sectors propagations, echoes, tails CQ wave, create KN echoes, tails ™ ƒÆ½Ä! waves  echoes  tails  WQ KN TN Hình 3-18: Quan hệ giữa các thành phần trong Wave Interpreter - 59 - WI là một hệ thống mở hoàn toàn khi 3 thông điệp WAVE, ECHO và TAIL có thể gửi trực tiếp từ bên ngoài vào trong hệ thống tới các node và link hoặc giữa các track. Người dùng có thể tương tác với WI bằng cách gửi các thông điệp tới CP hoặc trực tiếp gửi vào WQ thông qua các giao diện được cung cấp trên máy có cài đặt WI. Tương ứng với 3 loại thông điệp, WI có thể được tổ chức thành 3 luồng điều khiển chính cho từng thông điệp và thực thi một cách hoàn toàn độc lập. Các luồng điều khiền đó chính là đầu vào WI từ bên ngoài hệ thống, không những thế chúng còn tương tác qua lại lẫn nhau trong WI tạo thành chu trình xử lý các đoạn mã wave trên các node trong KN. Các hoạt động cơ bản của WI và tương tác giữa chúng được mô tả như trong Hình 3-19.Trước tiên, chúng tôi sẽ đưa ra cái nhìn tổng quát về các luồng chính này, sau đó sẽ mô tả chi tiết chúng trong phần tiếp theo. • Luồng xử lý Wave: Khi nhận được một wave từ bên ngoài, chuỗi wave thông thường được xử lý theo các bước: phân tích và xử lý wave head (parse, process_head), tạo ra các nhánh (spread_branches), sao chép wave tail cho các nhánh (replicate_tail) và lan tỏa chuỗi wave thu được đến các node đích (propagate). Quá trình này sẽ được tiếp tục cho đến khi chuỗi wave kết thúc hoặc được gửi ra ngoài hệ thống. • Luồng xử lý Echo: mỗi echo nhận được sẽ được gửi cho TP xử lý. Quá trình xử lý echo trải qua các bước: kích hoạt echo (activate_echo), gửi echo lên track cha (propagate_echo), và kết hợp echo nhận được với echo đã có ở track cha. Nếu tất cả các nhánh con của track cha đều đã gửi echo lên, thì quá trình lại được tiếp tục đối với track cha. Vòng lặp cứ tiếp diễn cho đến khi echo được gửi ra bên ngoài hệ thống. • Luồng xử lý Tail: tail từ các rule sẽ được gửi ngược lại thông qua track xuống các track con. Một wave tail từ bên ngoài đến sẽ được gửi tới track tương ứng và gửi xuống các track con thông qua hệ thống track hiện tại. Quá trình gửi wave tail sẽ được lặp lại cho đến khi tới các node đích hoặc wave tail được gửi đến các track con ở bên ngoài hệ thống. - 60 - Hình 3-19: Luồng xử lý giữa các thành phần trong Wave Interpreter 3. 6. 1 Luồng xử lý Wave Các wave nằm trong WQ độc lập với nhau, do đó sẽ được xử lý đồng thời. Đầu tiên, khi có wave nằm trong WQ, WI sẽ lần lượt lấy ra các wave, tách thành phần wave string và wave envrironment. Chuỗi wave thu được sẽ trải qua 2 giai đoạn: phân tích bởi PU và xử lý bởi EU. Ở giai đoạn phân tích, chuỗi wave được tách ra thành wave head và wave tail. Trong giai đoạn xử lý, wave head sẽ được xử lý ở node hiện tại và phụ thuộc vào kết quả xử lý mà wave tail sẽ được lan tỏa đến các node đích để tiếp tục quá trình thông dịch. Quá trình xử lý wave head được chia ra làm 7 trường hợp xử lý bao gồm: hop, action, sectors, control_rule, echo_dot_halt, code_injection, và external_call. Chi tiết về các trường hợp này được mô tả dưới đây: - 61 - • Hop Lệnh hop giúp cho wave di chuyển từ node này sang node khác cũng như từ WI này sang WI khác trong mạng. Quá trình xử lý hop như sau: 9 parser: wave string được phân tích thành wave head và wave tail. Wave head được phân tích thành 2 toán hạng và toán tử. 9 process_head: Toán tử xác định được từ bước trên dùng để xác định cấu trúc điều khiển trong 7 trường hợp xử lý. Trong trường hợp này là hop được xác định bằng toán tử ‘#’ 9 hop: xử lý lệnh hop. Hai toán hạng được xác định giá trị ở vị trí hiện tại trong KN. 9 match_KN: hai toán hạng sẽ được khớp với link và node của KN nối với node hiện tại. Nếu wave ở trong chế độ CREATE thì các link và node mới được tạo ra. Kết quả của bước này là danh sách các node thỏa mãn điều kiện. 9 spread_branch: Với mỗi node đích, một wave mới được tạo ra cùng với các biến frontal và biến environment ở node hiện tại. 9 replicate_tail: wave tail được gán làm wave string cho mỗi wave mới tạo ra. 9 activate_branch: kích hoạt xử lý các wave. 9 propagate: các wave tạo ra được gửi đến các node đích. Nếu node đích nằm trên cùng KN thì quá trình lan tỏa kết thúc và wave tail được gửi đến các node đích để tiếp tục được xử lý. Ngược lại wave sẽ được gửi ra ngoài mạng để tới node đích trên KN của máy khác. Độ dài của chuỗi wave giảm dần do wave head bị mất đi sau mỗi chu trình. Chu trình liên tục được diễn ra cho đến khi chuỗi wave di chuyển sang WI khác hoặc wave tail rỗng. Chuỗi wave rỗng nghĩa là đã được xử lý xong và có thể echo sẽ được gửi đi thông báo trạng thái kết thúc của wave • Action Các action được thực hiện ngay trên node hiện tại, bao gồm các toán tử gán và các toán tử so sánh. Các action được thực hiện dựa trên hai toán hạng bên trái và bên phải. Trong phép gán, kết quả sẽ được lưu trữ vào toán hạng bên trái. Trong các phép so sánh, trạng thái thực hiện của phép toán sẽ được ước lượng và quá trình xử lý chuỗi wave sẽ dừng lại nếu phép so sanh trả về kết quả là ‘fail’. Luồng xử lý các action sẽ bao gồm các bước sau: 9 parse: wave head và wave tail được tách ra từ chuỗi wave. Trong trường hợp này toán tử được xác định là các phép gán hoặc các phép so sánh. 9 process_head: quá trình xử lý chuyển sang xử lý các action 9 action_sequence: xử lý toán tử thu được. Việc xử lý các phép gán và các phép so sánh là khác nhau nên được chuyển thành 2 bước xử lý nhỏ hơn: − process_assign: Đây là bước xử lý cho các phép gán. Kết quả của phép gán sẽ được lưu lại vào toán hạng bên trái. - 62 - − process_filter: Các phép so sánh được xử lý. Nếu điều kiện so sánh được thỏa mãn thì quá trình xử lý sẽ tiếp tục, ngược lại việc xử lý sẽ ngừng lại và một echo thể hiện trạng thái thất bại được gửi đi (start_echo) − resume_tail: Khi action được xử lý xong, quá trình xử lý wave sẽ được tiếp tục lặp lại ở node hiện tại. • Sectors Nếu trong wave head chứa nhiều sector (mỗi sector phân cách nhau bởi dấu phẩy) thì chuỗi wave sẽ được phân chia ở node hiện tại thành các nhánh khác nhau. Mỗi nhánh nhận một wave string khác nhau, mà mỗi wave string là một sector được nối với wave tail. Việc xử lý sector cũng gần giống với hop nhưng khác một chút đó là các wave do quá trình xử lý sector sinh ra vẫn tiếp tục được xử lý ở node hiện tại. Quá trình xử lý các sector sẽ như sau: 9 parse: Wave head và wave tail được chia ra từ chuỗi wave, trong wave head chứa cú pháp của sector (chứa dấu phẩy phân cách). Trong trường hợp này, thay vì xác định các toán từ thì chuỗi wave giữa các sector sẽ được tách ra và trả lại cho bước xử lý tiếp theo. 9 process_head: quá trình xử lý chuyển sang xử lý các sector. 9 sectors: các nhánh ứng với mỗi sector bắt đầu được tạo ra. 9 spread_branches: mỗi nhánh ứng với một sectors được tạo ra trên cùng node hiện tại. Các biến frontal, environment được sao chép cho mỗi nhánh. 9 replicate_tail: wave tail sẽ được sao chép cho mỗi nhánh. Khác với việc sao chép wave tail trong hop, ở đây wave tail được nỗi vào chuỗi wave nằm trong từng nhánh được tách ra từ các sector trước đó. Các dấu ngoặc thừa bị xóa bỏ. 9 activate_branch: kích hoạt nhánh để xử lý (khác với hop, việc xử lý các sector không có bước propagation do các nhánh vẫn nằm trên node hiện tại) 9 resume_tail: wave string thu được sẽ tiếp tục được xử lý từ bước đầu tiên • Code injection Ngôn ngữ wave cho phép chèn vào các đoạn chương trình ngay trong quá trình xử lý từ các biến đi cùng wave trong quá trình đi chuyển hay từ các biến nằm trong node mà chuỗi wave đi qua. Đoạn chương trình được đưa vào sẽ nối vào đầu của wave tail và tiếp tục được xử lý trên cùng node với chuỗi wave trước đó. 9 parser: wave head và wave tail được tách ra. Wave head là toán tử ‘^’ đứng trước một biến báo hiệu đoạn mã được chèn thêm vào chính là giá trị của biến đó. 9 process_head: quá trình xử lý chuyển sang xử lý lệnh code injection 9 code_injection: Giá trị của biến được lấy ra và chuyển sang dạng xâu (nếu cần thiết). String thu được sẽ nối vào đầu của wave tail, quá trình xử lý với chuỗi wave tổng hợp lại tiếp tục từ đầu trên cùng node hiện tại. • External call - 63 - Toán tử external call là cách giao tiếp của WI với các hệ thống khác thông qua hệ điều hành. Một lời gọi external call sẽ tạm dừng nhánh hiện tại và tiếp tục quay lại xử lý sau khi lời gọi kết thúc. 9 parser: Quá trình phân tích gặp toán tử external call (cú pháp của ‘?’) 9 process_head: quá trình xử lý chuyển sang xử lý external call 9 external_call: hai toán tử được lấy giá trị và kết hợp thành lệnh để gọi chương trình. Lệnh này sẽ được gửi đi, nhánh hiện tại sẽ tạm treo cho đến khi chương trình được gọi thực hiện xong và trả về kết quả. 9 resume_tail: chuỗi wave sẽ tiếp tục quay lại quá trình xử lý • Control rule Chuỗi wave có 2 trạng thái lan tỏa, một là trong trạng thái không có track và một là trong trạng thái track. Khi di chuyển trong trạng thái track, sau mỗi lệnh hop, hoặc xử lý các sector thì sẽ tạo ra track mới nối với track hiện tại. Một control rule sẽ thiết lập một điểm điều khiển vào node hiện tại, việc điều khiển này được gắn với chuỗi wave nằm trong cặp mở đóng ngoặc của cú pháp rule. Chuỗi wave này gọi là enclosed wave. Quá trình xử lý được chia làm 2 phần: 9 Các biến frontal và environment của nhánh hiện tại sẽ được nhân bản cho enclosed wave, và một track mới được tạo ra. 9 Rule được gán cho track vừa tạo ra để xử lý các echo và wave tail sẽ được treo tại track mới này. Các luật tuần tự (SQ, OQ, AQ) đảm bảo các nhánh được thực hiện một cách lần lượt. Sau khi kích hoạt các nhánh thì chỉ duy nhất một nhánh được thực hiện và các nhánh khác sẽ tạm treo lại. Các bước xử lý như sau: 9 parser: Wave head và wave tail được tách ra. Wave head chứa một trong số các Rule của ngôn ngữ Wave và chuỗi wave nằm trong cặp mở đóng ngoặc. 9 process_head: Quá trình xử lý chuyển sang xử lý rule 9 control_rule: Xứ lý rule trong wave head 9 extract_head: tách wave string nằm trong cặp mở đóng ngoặc của rule, và tách các biến frontal, environment khỏi wave. 9 create_track: tạo ra track mới và track hiện tại (nếu có) sẽ là track cha của track vừa tạo 9 assign_rule: track mới tạo ra sẽ được gán với rule để xử lý các echo về sau. 9 suspend_tail: wave tail sẽ được treo ở track vừa tạo ra. • Echo dot halt Echo dot halt là các toán từ đặc biệt được xử dụng để kết thúc chuỗi wave đang thực thi trên node hiện tại và gửi lại các echo cho track. Sau đó, wave tail được treo tại track có thể được tiếp tục lan tỏa trong node hiện tại. Các bước xử lý như sau: - 64 - 9 parse: wave head và wave tail được tách ra, wave head chứa toán tử echo dot halt (cú pháp của ‘!’). 9 process_head: chuyển quá trình xủ lý sang echo dot halt 9 echo_dot_halt: giá trị của echo được xác định 9 start_echo: echo được gán cho track hiện tại và tiếp tục được lan tỏa lên trên theo track. 9 cancel_tail: wave tail sẽ được xóa bỏ sau khi gửi echo. Không chỉ có lệnh echo dot halt gửi echo sau khi xử lý mà lệnh hop và các lệnh so sánh cũng gửi echo. Lệnh hop sẽ gửi echo nếu việc khớp các toán hạng với KN thất bại. Các lệnh so sánh cũng gửi echo nếu điều kiện so sánh không được thỏa mãn. 3. 6. 2 Luồng xử lý các echo và điều khiển các rule Các echo sẽ lan truyền ngược lên gốc của hệ thống track và kết hợp với nhau tại các TN. Các echo sẽ được đồng bộ với nhau bằng cách track cha sẽ chờ các track con sau khi kết thúc gửi echo lên. Sau khi tất cả các echo đã được nhận đủ, track cha lại tiếp tục kích hoạt việc gửi echo tổng hợp lên trên. Quá trình trên sẽ tiếp tục cho đến khi gặp track có kèm theo rule hoặc echo được gửi tới gốc của hệ thống track. Sau khi xử lý xong chuỗi wave lan tỏa trong chế độ track, chương trình sẽ gửi thông điệp xóa track, để xóa toàn bộ dữ liệu lưu trữ trong track và giải phóng bộ nhớ phục vụ cho những đoạn mã wave tiếp theo. Trong track có thể gắn thêm rule hoặc không. Nếu track có gắn thêm rule thì việc kết hợp các echo sẽ không giống với khác track thông thường mà chịu sự qui định khác nhau của mỗi rule. Thêm vào đó, đối với các rule tuần tự có thể kích hoạt các track bị treo sau khi nhận được echo từ track con. Các rule còn thay đổi hành động của track sau khi nhận được các echo từ track con. Trong trường hợp này, thay vì gửi echo lên trên, chuỗi wave tail bị treo trong track sẽ được gửi đến các track con nếu điều kiện của rule được thỏa mãn. WT tail 2 1 3 7 4 5 6 Node đích Hình 3-20: Lan truyền echo lên trên - 65 - Các bước xử lý echo như trong ta thấy, ban đầu khi các track đã phát triển tới được node đích, quá trình echo sẽ được thực hiện từ các node đích ngược trở lên trên về track cha. Khi echo lên tới track có gán rule thì dựa vào echo tổng hợp được và rule thì wave tail sẽ được gửi trở lại các track lá. Các bước xử lý echo được thể hiện trong luồng thứ 2 của Hình 3-19: Luồng xử lý giữa các thành phần trong Wave Interpreter. Các echo sẽ bắt đầu lan tỏa từ các node đích khi chuỗi wave kết thúc hoặc bị hủy bỏ bởi các điều kiện không thỏa mãn trong phép so sánh hay khi xử lý lệnh echo dot halt. Echo được kích hoạt tại các track lá, sau đó lan truyền và đồng bộ trong hệ thống track. Luồng xử lý echo có thể chia làm 3 phần đó là: gửi các echo đi, xóa track và tổng hợp echo tại track chứa rule. • Gửi các echo Đây là phần chính trong luồng xử lý echo, bắt đầu khi chuỗi wave kết thúc xử lý. Các echo có thể được nhận từ ngoài mạng hoặc từ trong cùng WI, khi nhận được echo các bước xử lý bắt đầu sau bước start_echo: 9 activate_echo: echo được kích hoạt trong track và bắt đầu lan tỏa. 9 propagate_echo: echo được gửi lên track cha. Nếu track cha nằm trên máy khác thì echo sẽ được gửi ra bên ngoài mạng. 9 merge_echo: bước này do track cha xử lý khi nhận được echo, echo sẽ được tổng hợp lại với echo đang lưu trữ trong track. Nếu track cha nhận đủ echo từ các track con, quá trình này lại được tiếp tục đối với track cha. • Xóa track Cũng gần giống với việc lan tỏa các echo trong quá trình xóa track cũng có một thông báo được gửi đi, nhưng thay vì chỉ gửi echo lên trên, công việc xóa track lại bắt đầu từ track cha.Thông điệp xóa track sẽ được gửi đi sau khi chuỗi wave thực hiện xong, hoặc chuỗi wave chuyển từ chế độ track sang chế độ thông thường. Các bước xóa track diễn ra như sau: 9 activate_echo: quá trình xóa track được kích hoạt. tail 2 1 3 7 4 5 6 Node đích Hình 3-21: Gửi tail cho các track con - 66 - 9 delete_track: track được xóa và tất cả các dữ liệu lưu trữ trong track được giải phóng. 9 propagate_echo: thông điệp xóa track được gửi xuống các track con 9 merge_echo: nếu track con nằm trên cùng WI với track cha, quá trình xóa track được lặp lại. Nếu không cùng WI thì thông điệp xóa track sẽ được gửi ra ngoài mạng. • Tổng hợp echo tại Track có rule Nếu rule được gán vào track thì quá trình xử lý echo sẽ thay đổi tùy thuộc vào ý nghĩa của từng rule khác nhau. Mỗi rule định nghĩa những điều kiện logic làm thay đổi việc tổng hợp các echo. Ví dụ như đối với luật AS nếu gặp ít nhất một echo mang giá trị FALSE thì giá trị của echo tại track là FALSE. Các rule tuần tự còn bao gồm các nhánh bị treo lại, và các nhánh sẽ lần lượt được kích hoạt sau khi nhận được echo và điều kiện của rule được thỏa mãn. Thông thường đối với các rule, các wave tail bị treo lại sẽ được gửi đến tất cả các track con sau khi track nhận đủ echo và điều kiện của rule được thỏa mãn. Sau khi gửi tail đi thì rule cũng sẽ bị xóa khỏi track và tính chất của rule cũng sẽ mất đi. Các rule trong track sẽ được kích hoạt mỗi khi nhận được echo từ một trong số các track con. 9 activate_echo: echo được kích hoạt trong track. Trong trường hợp này track được kích hoạt là một trong số các track con của track có gắn rule. 9 propagate_echo: echo được gửi lên track cha có gắn rule. Tại đây có 3 trường hợp có thể xảy ra. − activate_branch: nếu track hiện tại được gán rule tuần tự (SQ, OS, AS) thì nhánh tiếp theo sẽ được kích hoạt và xử lý. − delete_rule: nếu điều kiện của rule được thỏa mãn, rule được xóa khỏi track và wave tail được gửi đến các track con. − forward_tail: wave tail bắt đầu được truyền đi từ track hiện tại • Luồng xử lý Tail Quá trình gửi tail thông qua hệ thống track là luồng xử lý cuối cùng của WI. Quá trình này được kích hoạt bởi các rule hoặc có thông điệp tail đến từ mạng. Trên mỗi track nhận được tail, tail sẽ tiếp tục được được gửi đến các track con của nó cho đến khi track hiện tại là các track lá hoặc track được gửi nằm bên ngoài hệ thống. Các bước xử lý tail diễn ra như sau: 9 forward_tail: tail được nhân ra và gửi đến tất cả các track con, lặp lại cho đến khi tới được các track cuối cùng. 9 delete_track: bước này chỉ sử dụng khi track gửi tail đi không có track cha và chuỗi wave sau quá trình này sẽ chuyển sang trạng thái lan tỏa không có track. Tại bước này track sẽ bị xóa đi sau khi gửi tail đến các track con và giải phóng toàn bộ vùng nhớ mà track chiếm dụng. - 67 - 9 resume_tail: Khi các track cuối cùng nhận được tail. Tại các track này wave tail cùng với các biến frontal, environment được lưu trữ kết hợp tạo thành wave và tiếp tục quá trình xử lý. 3. 6. 3 Xây dựng trình biên dịch Wave trên ngôn ngữ Java Phần dưới đây tôi xin giới thiệu sơ qua về các lớp chính và quan hệ giữa chúng khi được viết trên ngôn ngữ Java. • Hệ thống Knowledge Network • Hệ thống Track Forest • Wave và waveEnvironment - 68 - • Các thành phần xử lý - 69 - CHƯƠNG 4. THỰC HIỆN VÀ KẾT QUẢ Ở các chương trước chúng ta đã tìm hiểu về công nghệ Wave và từng bước xây dựng trình biên dịch cho ngôn ngữ này. Trong chương này chúng ta sẽ sử dụng chính công nghệ Wave để mô phỏng một số vấn đề, bài toán như: tạo lưới đơn hướng (có thể tạo ở trên máy khác), đa hướng, di chuyển để tránh hoặc vòng quanh chứng ngại vật, 3D và thực tại ảo… 4. 1 Cài đặt 4. 1. 1 Các yêu cầu về phần cứng Muốn cài đặt và chạy các chương trình mô phỏng và hiển thị tệp tin VRML, chúng ta cần phải có những máy tính với phần cứng tối thiểu như sau: - CPU Pentium 4 - Bộ nhớ màn hình 64MB - RAM 128MB ( phụ thuộc vào yêu cầu tối thiểu của hệ điều hành được cài đặt) - Màn hình, chuột, bàn phím - Ổ đĩa cứng trống tối thiểu 30MB Để chạy được các chương trình trên nhiều máy tính, chúng ta cần phải có từ hai máy tính trở lên với cấu hình mỗi máy tối thiểu theo yêu cầu trên và mỗi máy phải có một card mạng và kết nối mạng với nhau. 4. 1. 2 Các yêu cầu về phần mềm Chương trình mô phỏng và hiển thị tệp tin VRML đòi hỏi từng phần mềm khác nhau được cài đặt sẵn, để chạy được tất cả các chương trình, cần phải có tối thiểu các phần mềm sau: - Hệ điều hành Windows, Linux, Unix, … (Bắt buộc phải sử dụng Windows để chạy chương trình GnuPlot và hiển thị tệp tin VRML tại các phiên bản hiện tại) - JRE phiên bản 6 - Java 3D phiên bản 1.5.2 - Netbeans 6.1 (Dùng để chạy chương trình dễ dàng hơn) - Bộ thư viện X3D - GnuPlot phiên bản 4.2.5 - 70 - 4. 2 Thử nghiệm 4. 2. 1 Sử dụng chương trình Chương trình WAVE có thể được đóng gói để chạy như một ứng dụng riêng biệt, tuy nhiên, vì trong chương trình chúng ta sử dụng các thư viện của WAVE để chạy nên chúng ta sẽ gộp chung chương trình hiển thị và WAVE vào chung một project của Netbeans cho thuận tiện hơn khi chạy. Để chạy chương trình mô phỏng các bài toán ở dạng 2D, chúng ta cần phải chạy chương trình là lớp Sim trong gói (package) sim của project WAVE. Khi mới được chạy, chương trình sẽ xuất hiện cửa sổ chưa có nội dung. Hình 4-1. Chương trình hiển thị khi mới được chạy Sau khi chương trình hiển thị đã chạy, chúng ta chạy cả project WAVE (RUN project) để chạy chương trình WAVE. Lúc này, chương trình WAVE cũng xuất hiện một cửa sổ. - 71 - Hình 4-2. Chương trình WAVE khi bắt đầu chạy Sau đó, để thực hiện mô phỏng cho một bài toán cụ thể, chúng ta chọn nút Browse… của cửa sổ chương trình WAVE sau đó chọn các tệp tin tương ứng trong thư mục Simulation để chạy tiếp chương trình. 4. 2. 2 Tạo lưới thực địa Chúng ta muốn tạo lưới và hiển thị trên chương trình hiển thị, cần lựa chọn tệp tin tạo lưới trong thư mục Simulation. Tùy thuộc vào nội dung mà chúng ta tạo lưới sẽ được tạo khác nhau. Giả sử, ở đây, chúng ta tạo ra một lưới 5x5, chương trình hiển thị sẽ hiển thị lưới ra màn hình: Hình 4-3. Lưới 5x5 - 72 - Chương trình WAVE cũng đưa ra thông báo, chúng ta có thể xem được tại cửa sổ Output của Netbeans: Hình 4-4. Cửa sổ output của Netbeans 4. 3 Di chuyển tự do Bài toán di chuyển tự do của một vật từ tọa độ (1-1) đến (5-5) được mô phỏng trong chương trình có kết quả chạy như sau: Hình 4-5. Vị trí đầu tiên 1-1 - 73 - Hình 4-6. Chạy ngẫu nhiên tới vị trí tiếp theo - 74 - Hình 4-7. Các bước chạy ngẫu nhiên tiếp theo - 75 - Hình 4-9. Dừng khi chạy tới đích 4. 3. 1 Di chuyển tránh chướng ngại vật Đối tượng di chuyển từ nút (1-1) đến (5-5) mà không đi qua chướng ngại vật Hình 4-8. Tiếp tục chạy ngẫu nhiên - 76 - Hình 4-10. Di chuyển qua chướng ngại vật - 77 - 4. 3. 2 Di chuyển vòng quanh chướng ngại vật Đối tượng đi vòng quanh chướng ngại vật mà không đi đến các điểm nằm cách xa chướng ngại vật hoặc đi xuyên qua chướng ngại vật: Hình 4-11. Vượt qua chướng ngại vật và về đến đích - 78 - Hình 4-12. Di chuyển vòng quanh chướng ngại vật - 79 - Hình 4-13. Vòng quanh chướng ngại vật 1 vòng thì dừng - 80 - 4. 4 Di chuyển cùng nhau kiểu tịnh tiến Hai đối tượng sẽ cùng nhau di chuyển đến đích: 4. 4. 1 Hiển thị hình ảnh 3D động bằng GnuPlot Bằng cách sử dụng GnuPlot, chúng ta có thể hiển thị các hình ảnh 3D động trên nhiều máy, mỗi máy hiển thị một phần hình ảnh động. Hình ảnh hiển thị 3D trên máy thứ nhất với tọa độ x trong khoảng từ 1 đến 5: Hình 4-14. Di chuyển tịnh tiến cùng nhau - 81 - Hình 4-15. Hình ảnh 3D trên máy thứ nhất sử dụng GnuPlot Máy thứ hai hiển thị phần hình ảnh 3D tiếp theo với x nằm trong khoảng từ 6 đến 10: Hình 4-16. Hình ảnh 3D trên máy thứ hai sử dụng GnuPlot 4. 4. 2 Hiển thị hình ảnh 3D của tệp tin VRML Từ KN của WAVE, WAVE sẽ tạo ra một tệp tin VRML và chương trình sẽ hiển thị tệp tin VRML này dưới dạng 3D: - 82 - Hình 4-17. Tệp tin VRML được hiển thị sau khi được tạo bởi KN 4. 4. 3 Hiển thị hình ảnh 3D với các góc nhìn khác nhau Các vật thể được mô tả trong tệp tin VRML được mô tả giống như các vật thể trong tệp tin VRML được hiển thị trong Hình 4-17. Tệp tin VRML được hiển thị sau khi được tạo bởi KN, sau khi thay đổi Transform, nó lại được hiển thị theo một cách khác: - 83 - Hình 4-18. Các đối tượng hiển thị theo cách khác thi thay đổi Transform Hình 4-19. Một cách nhìn khác thi thay đổi Transform 4. 4. 4 Hiển thị hình ảnh 3D VRML trên nhiều máy Các đối tượng khác nhau được đặt trên các máy khác nhau, mỗi một đối tượng được hiển thị trên một máy. Từ một tệp tin VRML được hiển thị như bây giờ Hình - 84 - 4-20. Hiển thị đối tượng đầu tiên trên máy 1, mỗi máy sẽ nhận nhiệm vụ đưa ra từng đối tượng: Trên máy thứ nhất hiển thị đối tượng đầu tiên là hình trụ: Hình 4-20. Hiển thị đối tượng đầu tiên trên máy 1 Máy 2 hiển thị hình nón: - 85 - Hình 4-21. Hiển thị đối tượng thứ hai trên máy 2 - 86 - CHƯƠNG 5. PHỤ LỤC 5. 1 JJTree 5. 1. 1 Giới thiệu JJTree là một bộ tiền xử lý của JavaCC, quá trình biên dịch JJTree kết hợp việc phân tích từ loại xây dựng nên cây hành động tương ứng với những đoạn mã riêng biệt trong JavaCC. Việc thực thi JJTree thông qua JavaCC sẽ tạo ra các từ loại. JJTree phát sinh mã để tạo nên cấu trúc cây cú pháp với nhiều nút, mỗi nút có các lá là các ký hiệu (biến, từ khóa…) trong ngôn ngữ lập trình. JJTree định nghĩa một nút giao diện Java mà tất cả việc phân tích các nút của cây phải được tiến hành. Nút giao diện này cung cấp các phương thức thực thi như việc tạo dựng các nút cha, thêm vào các nút con và việc gọi lại chính nó. JJTree thao tác một trong hai nút, simple và multi. Nút simple, mỗi bộ phân tích cú pháp nút cây cụ thể là một loại SimpleNode. Còn nút mutil, kiểu của bộ phân tích cú pháp nút cây này được lấy từ tên của nút. JavaCC phân tích cú pháp từ trên xuống còn JJTree lại xây dựng bộ phân tích cú pháp từ dưới lên. Để làm điều này JJTree sử dụng một ngăn xếp để đưa vào các nút đã được tạo ra. Khi mà các nút con này tìm thấy các nút cha thì nó lấy các nút con thêm vào các nút cha. Nó tiếp tục lấy các nút cha vừa mới được tạo ra để đưa vào ngăn xếp, nút cha này lại được xem như các nút con và quá trình này lại được tiếp diễn cho tới khi kết thúc. 5. 1. 2 Các kiểu cấu trúc cây - Khai báo với số lượng nút cụ thể void Assignment() #Assignment(2) : {} { Id() "=" Expression() } - Khai báo với số lượng nút con chưa xác định rõ o Có giới hạn void Assignment() #Assignment(>2) : o Không có giới hạn void Assignment()#Assignment - 87 - 5. 2 Thực thi trên ngôn ngữ simpleLang Trong phần trên, chúng tôi đã giới thiệu qua về JavaCC, file cú pháp của JavaCC, các tùy chọn khi biên dịch file cú pháp để tạo ra bộ parser thỏa mãn yêu cầu của người dùng. Trong phần này, dựa vào những khái niệm và tính chất của file cú pháp, ta sẽ tìm hiểu cách tạo ra bộ parser cho ngôn ngữ simpleLang – một ngôn ngữ rất đơn giản định nghĩa toán tử cộng. Thành phần chính dùng để xây dựng lên bộ parser cho ngôn ngữ simpleLang là file cú pháp jjt (jjtree) bao gồm các các từ tố và đặc tả cú pháp của ngôn ngữ simpleLang. Cú pháp định nghĩa ngôn ngữ simpleLang trong JavaCC có dạng như sau : void simpleLang() : {} { addExpr() } void addExpr() : {} { integerLiteral() ( "+" integerLiteral() )? } void integerLiteral() : {} { } SKIP : { " " | "\t" | "\n" | "\r" } TOKEN : { } Ý nghĩa : - Định nghĩa ngôn ngữ simpleLang gồm phép toán duy nhất là toán tử cộng (addExpr()) - Toán tử cộng được định nghĩa bởi 2 hay nhiều integerLiteral cách nhau bới dấu + - Một integerLiteral được định nghĩa là một từ tố là một số nguyên Việc xây dựng bộ parser dựa trên file cú pháp .jj sẽ phải tích hợp rất nhiều đoạn code java vào trong file .jj và không tận dụng được những lợi ích mà Java IDE mang lại trong quá trình phát triển chương trình. Như đã nói ở trên, file jjtree được tạo ra làm bộ tiền xử lý cho quá trình tạo ra file .jj, jjtree sẽ định nghĩa được các hành động, thuộc tính trên từng node của cây cú pháp, do vậy sẽ có khả năng tùy biến cao hơn cho quá trình lập trình(thiết lập, thay đổi giá trị, sự kiện… tại mỗi node). Cú pháp của file jjtree tương ứng với cú pháp của file .jj như sau : SimpleNode simpleLang() : #Root {} { addExpr() { return jjtThis; }} void addExpr() : {} { integerLiteral() ( "+" integerLiteral() #Add(2) )? } void integerLiteral() : #IntLiteral {} { } SKIP : { " " | "\t" | "\n" | "\r" } TOKEN : { } - 88 - Ý nghĩa : Tương tự như file .jj ở phần trên ta đã xem xét, file jjtree định nghĩa cú pháp và từ tố theo cùng cách thức, điểm khác biệt đó là jjtree tạo ra các class java (các AST) tại từng node của cây cú pháp. Trong ngôn ngữ simpleLang có 3 AST được tạo ra (sau dấu #) là : ASTRoot, ASTAdd và ASTIntLiteral thừa kế từ lớp SimpleNode – mang những thuộc tính chung nhất của một node trong cây cú pháp. Ví dụ 1: Cây cú pháp cho 1 biểu thức đơn Ví dụ 2: Cây cú pháp với phép toán cộng Làm việc với cây cú pháp Phần trên ta đã tạo được ra cây cú pháp, việc sử dụng cây cú pháp như thế nào tùy thuộc vào mục đích của người lập trình. Mỗi node trong cây cú pháp được gọi là một SimpleNode bao gồm một số thuộc tính và phương thức như : node cha, danh sách các node con, giá trị tại node, duyệt cây từ node hiện thời đổ xuống… SimpleParser parser = new SimpleParser(new StringReader( expression )); SimpleNode rootNode = parser.simpleLang(); rootNode.dump(); sẽ cho kết quả: Root Add IntLiteral IntLiteral Một phương thức quan trọng trong SimpleNode đó là lấy ra giá trị của các node con của một node, câu lệnh như sau: SimpleNode lhs = addNode.jjtGetChild( 0 ); - 89 - SimpleNode rhs = addNode.jjtGetChild( 1 ); sẽ lấy ra 2 node con của node addNode. Thiết lập và lấy giá trị tại node Quá trình phân tích cú pháp ở trên chưa cho phép lấy ra giá trị IntLiteral tại các node mang giá trị là số nguyên. Để làm được điều này ta cần thêm các phương thức set và get để thiết lập và lấy giá trị tại node: public class SimpleNode extends Node { String m_text; public void setText( String text ) { m_text = text; } public String getText() { return m_text; } ... } Và trong file jjt ta cần sửa mã: void integerLiteral() : #IntLiteral {} } thành: void integerLiteral() : #IntLiteral { Token t; } { t= { jjtThis.setText( t.image );} } Khi đó cây cú pháp của phép toán 42+1 sẽ có dạng như sau: Root Add IntLiteral[42] IntLiteral[1] 5. 3 Xây dựng bộ parser cho ngôn ngữ Wave Wave cũng giống như các ngôn ngữ khác, cũng bao gồm các đặc tả về Token, đặc tả cú pháp của ngôn ngữ. Dựa vào những phần trình bày bên trên, công việc chính cần làm đó là tạo ra file đặc tả cú pháp (jjt) của Wave. Một phần file jjt của Wave: options { STATIC = false; - 90 - MULTI = true; VISITOR = true; } PARSER_BEGIN(WAVEParser) package ast; public class WAVEParser { public static void main(String args[]) { System.err.println("Reading a \"WAVEProgram\" from standard input..."); WAVEParser p = new WAVEParser(System.in); try { SimpleNode wave = p.Wave(); wave.dump(" "); System.err.println("Thank you."); } catch (Exception e) { System.out.println("Oops."); System.out.println(e.getMessage()); e.printStackTrace(); } } final public String WaveTail() { return m_waveTail; } private String m_waveTail; } PARSER_END(WAVEParser) TOKEN: { )+> | <STRING: "`" (~["`","\n","\r"])*"'" | (["a"-"z","_"] ( | )*)> | | | } Ví dụ quá trình parse chuỗi Wave “Fa=1.Fa+1.T=Fa “ sẽ cho kết quả như sau: - 91 - WAVEProgram : Fa=1.Fa+1.T=Fa Zone : Fa=1 Sector : Fa=1 Assign : Fa=1 FrontalVar : Fa IntConst : 1 Zone : Fa+1 Sector : Fa+1 Sum : Fa+1 FrontalVar : Fa IntConst : 1 Zone : T=Fa Sector : T=Fa TerminalOutput : T=Fa FrontalVar : Fa - 92 - CHƯƠNG 6. TÀI LIỆU THAM KHẢO TÀI LIỆU TIẾNG ANH [1] Peter Sapaty, Mobile processing in Distributed and Open environments, 1998 [2] Bell, G., A. Parisi, and M. Pesce, The Virtual Reality Modelling Language: Version 1.0 Specification, November 9, 1995. [3] Bruno, J., and S. M. Altman, “A Theory of Asychronous Control Networks,” IEEE Trans. Comput., Vol. C-20, No. 6, June 1971. [4] Sapaty, P. S,. “Active Information Field as a Model for the Structural Solution of Task on Graphs and Networds,” Proc. USSR Academy of Sciences: Technical Cybernetics, No. 5, 1984 (in Russian). [5] Varbanov, S., and P. S. Sapaty, “An Information System Based on the Wave Navigation Techniques,” Abstr. International Conference, AIMSA’86, Varna, Bulgaria, 1986. [6] Sapaty, P.S., “The Wave-0 Language as a Framework of Navigational Structures for Knowledge Bases Using Semantic Networks,” Proc. USSR Academy of Sciences: Technical Cybernetics, No. 5, 1986 (in Russian). [7] Borst, P., The First Implementation of the WAVE System for UNIX and TCP/IP Computer Networks, TR 18/92, Faculty of Informatics, University of Karlsrule. Karlsruhe, Germany, December 1992. [8] Borst, P.M., H.-T Goetz, P. S. Sapaty, and W. Xorn, “Paralled Knowledge Processing in Open Networks,” Proc. International Conference and Exhibition “Hight- Perfor-mance Computing in Networks” (HPCN Europe ‘94), Munich, Germany, April 1994. [9] Corbin M. J., and P. S. Sapaty, “Distributed Object-Based Simulation in Wave,” J. Simul. Pract. Theory, Vol. 3, No.3, pp. 157-181, 1995. [10] Merchant F., L. F. Bic, P. M. Borst, M. J. Corbin, M. Dillencourt, M. Fukuda, and P. S. Sapaty, “Simulating Autonomous Objects in a S patial Database Using WAVE,” Proc. 9th European Simulation Multiconference, Prague, Czechoslovakia, June 1995. [11] Livatharas, C., “Integration of Heterogeneous Databases Using WAVE,” M.Sc. Project Report, Department of Electrical Engineering, University of Surrey. Surrey, England, August 1995. [12] Vuong, S., and I. Ivanov, “Mobile Intelligent Agent Systems: WAVE vs. JAVA,” Proc, etaCOM’96, Portland, Oreg., May 1996. [13] Vuong, S., and L. Mathy, “Simulating the Mobile-IP Protocol Using Wave,” Proc, etaCOM’96, Porland, Oreg., May 1996. [14] Darling, J. C. C., P.S Sapaty, and M. J. Underhill, “Distributed Virtual Reality: A Fully Dynamic Approach,” Proc. 15th Workshop on Standards for the - 93 - Interoperability of Distributed Simulations, Institute for Simulation and Training, University of Central Florida, Orlando, Fla., September 1995. [15] Tan, H. K. V,. “Distributed Dynamic 3D Virtual Reality,” M.Sc Telematics Diploma Project (base on WAVE), Department of Electrical Engineering, University of Surrey, Surrey, England, 1997. WEBSITE THAM KHẢO [16] [17] https://javacc.dev.java.net/ [18] [19] [20] [21] [22]

Các file đính kèm theo tài liệu này:

  • pdfLuận văn- Xây dựng trình biên dịch cho ngôn ngữ wave.pdf