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
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() <EOF> }
void addExpr() : {} { integerLiteral() ( "+" integerLiteral() )? }
void integerLiteral() : {} { <INT> } SKIP : { " " | "\t" | "\n" | "\r" }
TOKEN : { < INT : ( ["0" - "9"] )+ > } Ý 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ố <INT> 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() <EOF> { return jjtThis; }}
void addExpr() : {} { integerLiteral() ( "+" integerLiteral() #Add(2) )? }
void integerLiteral() : #IntLiteral {} { <INT> } SKIP : { " " | "\t" | "\n" | "\r" }
Ý 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 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 {} <INT> } thành:
void integerLiteral() : #IntLiteral { Token t; }
{ t=<INT> { 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]