5 Thực nghiệm
2.24 Định nghĩa Menu
2.8.2 Cú pháp và ngữ nghĩa2.8.2.1 Biến 2.8.2.1 Biến
OCaml là ngôn ngữ hằng nên biến thực chất là giá trị được đặt tên và không thể
thay đổi trong một phạm vi nào đó trong suốt quá trình chạy [14]. Điều này có
nghĩa là khi lập trình viên gán giá trị cho biến thì sau đó không thể thay đổi giá trị cho biến đó. OCaml cung cấp kiểu tham chiếu (mutable references), nó giống như là các con trỏ. Các biến tham chiếu này có thể thay đổi giá trị mà nó trỏ tới.
2.8.2.2 Kiểu dữ liệu cơ bản
Bảng 2.1: Các kiểu dữ liệu cơ bản của OCaml
Kiểu dữ liệu Mô tả
int Kiểu nguyên dương 31-bit float Kiểu số thực
bool Kiểu logic bao gồm true hoặc false char Kiểu kí tự
string Chuỗi ký tự unit Kiểu đặc biệt
OCaml cung cấp các kiểu dữ liệu cơ bản được liệt kê trong Bảng 2.1. Kiểu int chỉ sử dụng 32 bits tại vì nó sử dụng 1 bit để phân biệt giữa số nguyên và con trỏ. Nếu lập trình viên muốn sử dụng số nguyên 32-bit thì có thể sử dụng Int32 hoặc sổ nguyên 64-bit thì có thể sử dụng Int64.
2.8.2.3 Hàm
Hàm là một đối tượng dữ liệu nhận vào gồm có các tham số và trả về một đối
tượng dữ liệu có kiểu [14]. Một hàm có thể nhận và trả về một kiểu dữ liệu
Chương 2.Kiến thức cơ bản SimpleExpression: AdditiveExpr Expression: OrExpr Hình 2.25: Định nghĩa biểu thức.
nghĩa bằng cách sử dụng từ khóa let, f un hoặc f unction. Tuy nhiên, hàm yêu
cầu có ít nhất một tham số.
Hình 2.28 định nghĩa một hàm đơn giản nhất. Hàm có tên làmy f uncnhận
tham số là một kiểu unit và trả về giá trị là một kiểu int. Hàm được kết thúc
bằng hai dấu chấm phẩy. Hình 2.29 mô tả cách gọi hàm trong OCaml. Hàm phải có duy nhất một kiểu trả về, mặc dù kiểu trả về có thể là kiểu đa hình. Kiểu mà hàm trả về sẽ được trình biên dịch tự động đưa ra. Trình biên dịch tìm kiểu thông dụng nhất mà hàm có thể trả về và sau đó sử dụng kiểu đó.
2.8.2.4 Hàm mức cao
OCaml hỗ trợ hàm mức cao [14]. Điều này có nghĩa là hàm có thể nhận một hàm
khác làm tham số. Sở dĩ OCaml làm được điều này vì OCaml xem hàm như là
các lớp dữ liệu cơ bản. Hình 2.30 là ví dụ minh họa hàmbiggernhận ba tham số
đầu vào. Tham số f đầu tiên là một hàm có hai tham số. Hai tham số cuối cùng
x,yđược xem như là tham số của tham số f.
2.8.2.5 Hàm nặc danh
Hàm nặc danh là hàm không có tên, vì vậy hàm không được gán bất kỳ một
giá trị nào [14]. Hàm nặc danh rất hữu ích trong OCaml. Nhiều hàm sử dụng
các hàm khác như là đối số và hàm nặc danh là cách dễ dàng khai báo và đưa vào làm tham số cho các hàm khác. Thông thường, khai báo hàm nặc danh bằng
cách sử dụng từ khóa f unhoặc f unction. Hàmcomparedùng để so sánh hai giá
trị trong việc sắp xếp một danh sách. Hàm nhận vào hai số nguyên và trả về giá trị là 1 nếu giá trị thứ hai bé hơn giá trị thứ nhất. Hàm trả về giá trị là 0 nếu giá trị thứ hai bằng giá trị thứ nhất và trả về giá trị là -1 nếu giá trị thứ hai lớn hơn giá trị thứ nhất. Hàmcomparecó thể được cài đặt riêng biệt rồi sử dụng nó. Tuy nhiên, với hàm nặc danh có thể giúp khai báo và sử dụng trực tiếp chính nó. Hình 2.31 mô tả một hàm nặc danh.
Chương 2.Kiến thức cơ bản OrExpr: AndExpr OrExpr||AndExpr AndExpr: BitOrExpr
AndExpr&&BitOrExpr BitOrExpr: BitExclOrExpr BitOrExpr|BitExclOrExpr BitExclOrExpr: BitAndExpr BitExclOrExpr∧BitAndExpr BitAndExpr: EqualityExpr
BitAndExpr&EqualityExpr EqualityExpr: RelationalExpr EqualityExpr==RelationalExpr EqualityExpr!==EqualityExpr RelationalExpr: ShiftExpr RelationalExpr<ShiftExpr RelationalExpr<=ShiftExpr RelationalExpr>ShiftExpr RelationalExpr>=ShiftExpr ShiftExpr: AdditiveExpr ShiftExpr»AdditiveExpr ShiftExpr«AdditiveExpr
Hình 2.26: Định nghĩa biểu thức (tiếp).
2.8.2.6 Hàm đệ quy
Hàm đệ quy là một phần cực kỳ quan trọng của OCaml [14]. Hiệu suất và Kích
thước mã nguồn của chương trình được cải tiến nhờ khả năng đệ quy đuôi trong OCaml. Hàm nặc danh không thể sử dụng đệ quy đuôi. Bởi vì hàm nặc danh không có tên nên không thể gọi hàm đó được. Hàm đệ quy được khai báo trong
OCaml bằng cách thêm từ khóa rec trước tên của hàm đó. Hàm đệ quy trong
OCaml là đệ quy đuôi nếu nó đảm bảo hai yêu cầu sau: hàm đệ quy không chứa try/with và giá trị trả về phải là giá trị không đổi. Ví dụ, hàm tính số Fibonacci không phải là hàm đệ quy đuôi [14]. Bởi vì, hàm tính số Fibonacci trả về giá trị là hàm yêu cầu tính giá trị Fibonacci tiếp theo. Hình 2.32 mô tả một hàm đệ quy tính số Fibonacci.
2.8.2.7 So sánh mẫu
OCaml cung cấp một chức năng so sánh mẫu tương tự với cấu trúcSW ITCH...
Chương 2.Kiến thức cơ bản AdditiveExpr: MultiplicativeExpr AdditiveExpr+MultiplicativeExpr AdditiveExpr-MultiplicativeExpr MultiplicativeExpr: UnaryExpr MultiplicativeExpr*UnaryExpr MultiplicativeExpr\UnaryExpr MultiplicativeExpr%UnaryExpr UnaryExpr: BaseExpr +BaseExpr -BaseExpr !BaseExpr BaseExpr: IntegerLiteral Variable ConstantIdent ’ActionLabel #SetIdent @(SetIdent , Expression) (Expression)
Hình 2.27: Định nghĩa biểu thức (tiếp).
# l e t myfunc ( ) = 1 + 1 ; ;
v a l myfunc : u n i t −> i n t = <fun>
Hình 2.28: Ví dụ khai báo hàm.
giá trị đầu vào. Cấu trúc lệnh của so sánh mẫu được mô tả ở Hình 2.33. Trình
biên dịch OCaml sẽ so sánh expression với các patternn để đưa ra quyết định
thực hiệnexpressionn tương ứng. Ví dụ so sánh mẫu được mô tả ở Hình 2.34.
2.8.2.8 Đối tượng
OCaml là ngôn ngữ lập trình hàm được phát triển dựa trên ngôn ngữ CAML và
có thêm chức năng hướng đối tượng [14]. OCaml cung cấp đầy đủ các nguyên
lý của hướng đối tượng như: lớp, đối tượng, thừa kế, đa hình.
# myfunc 2 ; ;
− : i n t = 4
Chương 2.Kiến thức cơ bản # l e t b i g g e r f x y = f x y ; ; v a l b i g g e r : ( 'a −> 'b −> 'c ) −> 'a −> 'b −> 'c = <fun> # b i g g e r ( > ) 10 2 0 ; ; − : bool = f a l s e # b i g g e r ( < ) 10 2 0 ; ; − : bool = t r u e Hình 2.30: Ví dụ hàm mức cao. # L i s t . s o r t (fun x y −> i f x = y then −1 e l s e i f x < y then 1 e l s e 0 ) [ 1 ; 4 ; 9 ; 3 ; 2 ; 1 ] ; ; − : i n t l i s t = [ 9 ; 4 ; 3 ; 2 ; 1 ; 1 ] Hình 2.31: Ví dụ hàm nặc danh. # l e t r e c f i b n = i f ( n < 2 ) then 1 e l s e ( f i b ( n − 1 ) ) + ( f i b ( n − 2 ) ) ; ; v a l f i b : i n t −> i n t = <fun> # f i b 6 ; ; − : i n t = 13 Hình 2.32: Ví dụ hàm đệ quy.
match expression with | pattern1-> expression1 | pattern2-> expression2
...
| patternn-> expressionn
Hình 2.33: Cấu trúc lệnh của so sánh mẫu.
# l e t f i = match i with
| o −> " Zero "
| 3 −> " Three "
| _ −> " Neith er zero nor t h r e e " ; ;
Chương 2.Kiến thức cơ bản
2.9 OCamllex và OCamlyacc
2.9.1 OCamllex
Ocamllex [15] là bộ sinh dùng để sinh ra chương trình phân tích đoán nhận các
mẫu từ vựng trong tập tin đầu vào. Các biểu thức chính quy và các luật được đặc tả trong tập tin từ vựng. Ocamllex sẽ sinh ra tập tin mã nguồn OCaml dựa vào tập tin đặc tả này. Tập tin mã nguồn được sinh ra sẽ định nghĩa các hàm xử lý từ vựng [15]. Tập tin mã nguồn được dịch và liên kết lại để sinh ra tập tin có khả năng thực thi. Tập tin thực thi này khi chạy sẽ xử lý đầu vào của nó dựa vào các biểu thức chính quy. Khi nó tìm thấy một từ vựng nó sẽ thực thi đoạn mã nguồn OCaml tương ứng. Tập tin mã nguồn đặc tả từ vựng có đuôi mở rộng là .mll. Để biên dịch tập tin này sử dụng dòng lệnh:ocamllex∗.mll. Tập tin đặc tả từ vựng gồm có bốn thành phần: header,các định nghĩa, các luậtvàtrailer. Trong đó, các thành phầnheadervàruleslà cần thiết còn hai thành phần còn lại làrules vàtrailerlà tùy chọn. Cấu trúc tập tin đặc tả từ vựng được mô tả như Hình 2.35. Hình 2.36 là ví dụ mô tả một tập tin đặc tả từ vựng.
(* thành phần header *) { header }
(* thành phần định nghĩa *) let ident = regexp
let ...
(* thành phần luật *)
rule entrypoint [arg1... argn] = parse | pattern { action }
| ...
| pattern { action }
and entrypoint [arg1... argn] = parse ... and ... (* thành phần trailer *) { trailer } Hình 2.35: Cấu trúc tập tin đặc tả từ vựng. 2.9.2 OCamlyacc
Ocamlyacc [16] là bộ sinh dùng để sinh ra chương trình phân tích cú pháp bằng
ngôn ngữ OCaml từ một tập tin đặc tả văn phạm phi ngữ cảnh. Chương trình OCaml này sẽ có chức năng phân tích cú pháp cho tập tin dữ liệu đầu vào tuân thủ theo văn phạm đó. Tập tin mã nguồn đặc tả cú pháp có đuôi mở rộng là .mly. Để biên dịch tập tin này sử dụng dòng lệnh:ocamlyacc∗.mly. Tập tin đặc
Chương 2.Kiến thức cơ bản { l e t num_lines = r e f 0 l e t num_chars = r e f 0 } r u l e count = parse
| '\n' { i n c r num_lines ; i n c r num_chars ; count ←-
l e x b u f } | _ { i n c r num_chars ; count l e x b u f } | e o f { ( ) } { l e t main ( ) = l e t l e x b u f = Lexing . from_channel s t d i n i n count l e x b u f ; P r i n t f . p r i n t f " # o f l i n e s = %d , # o f c h a r s = %d\n " !←- num_lines ! num_chars l e t _ = P r i n t e x c . p r i n t main ( ) } Hình 2.36: Ví dụ tập tin đặc tả từ vựng.
tả cú pháp gồm có bốn thành phần: header,khai báo Ocamlyacc, luật văn phạmvà trailer. Cấu trúc tập tin đặc tả cú pháp được mô tả như Hình 2.37.
%{
Header (mã nguồn OCaml) %}
Khai báo Ocamlyacc %%
Các luật văn phạm %%
trailer (có thể có mã nguồn OCaml)
Chương 3 AGTool
3.1 Giới thiệu AGTool
Phương pháp kiểm chứng mô hình còn gặp phải vấn đề "bùng nổ không gian trạng thái". Một giả thuyết được đặt ra là thay vì kiểm chứng trên toàn bộ hệ thống, chúng ta sẽ kiểm chứng từng thành phần của hệ thống đó. Với giả thuyết này thì phương pháp kiểm chứng giả định-đảm bảo có tiềm năng trong việc giải quyết bài toán của phương pháp kiểm chứng mô hình. Để sử dụng được phương pháp kiểm chứng đảm bảo giả định, chúng ta cần sinh ra cho hệ thống
một giả định (Assumption). Và AGTool [7] là một trong những công cụ dùng để
thực hiện công việc sinh ra giả định đó. Hình 3.1 mô tả tổng quan về AGTool.
Hình 3.1: Mô hình công cụ kiểm chứng AGTool.
Các thành phần M1, M2 và thuộc tính Pcủa hệ thống được đặc tả bởi các
Chương 3.AGTool
(a) LTS của các thành phần đầu vào.
(b) Tập tin đặc tả các thành phần đầu vào.
Hình 3.2: Thành phần đầu vào của AGTool.
dạng LTS. AGTool được cài đặt bằng ngôn ngữ lập trình hàm OCaml. Hiện tại, AGTool tương tác với người dùng bằng giao diện dòng lệnh. Các thành phần đầu vào được đưa vào từ tệp văn bản. Dưới đây là một ví dụ minh họa cho đầu vào và đầu ra của AGTool. Hình 3.2a mô tả các thành phần của hệ thống kênh truyền tin dưới dạng các biểu đồ trực quan. Đầu vào gồm có hai thành phần
M1, M2 của hệ thống và thuộc tính P. Các thành phần này được đặc tả bằng
dưới dạng biểu diễn liệt kê. Hình 3.2b minh họa tập tin chứa các đặc tả đầu vào. AGTool sinh ra giả định từ đặc tả ở Hình 3.2b. Hình 3.3 mô tả kết quả sinh ra bởi AGTool.
Chương 3.AGTool
3.2 Hạn chế của AGTool
Với các chức năng của AGTool hiện tại thì nó còn tồn tại một vài nhược điểm để có thể đưa vào ứng dụng trong thực tế. Kiểu dữ liệu đầu vào được biểu diễn dưới dạng liệt kê, công việc này yêu cầu người dùng phải chuẩn tập tin đầu vào một cách tỉ mỉ, mất nhiều thời gian và dễ gây lỗi. Không những vậy, với các biểu diễn đầu vào và đầu ra dưới dạng liệt kê thì AGTool không thể sử dụng lại các đặc tả của các công cụ kiểm chứng phần mềm khác như LTSA.
Chương 4
Chuyển đổi giữa các dạng biểu diễn của LTS
LTSA là một công cụ nổi tiếng về kiểm chứng phần mềm tương tranh [3]. AGTool
có thể giao tiếp được với LTSA có ý nghĩa quan trọng trong việc ứng dụng nó vào trong thực tế. Các thành phần trong dữ liệu đầu vào của công cụ LTSA được đặc tả bởi ngôn ngữ FSP. Trong khi đó, AGTool nhận dữ liệu đầu vào và sinh ra kết quả (giả định) ở LF. Để AGTool và LTSA có thể giao tiếp, sử dụng lại các kết quả của nhau thì đầu vào và đầu ra của hai công cụ này phải sử dụng chung một kiểu dữ liệu. Vì vậy, các thành phần đầu vào và kết quả của AGTool phải được đặc tả bởi ngôn ngữ FSP. Trong chương này, luận văn đưa ra phương pháp
giải quyết hai bài toán chính là: chuyển đổi FSP sang LF và chuyển đổi từ LF
sang FSP. Ý tưởng của phương pháp này là thiết kế hai thành phần có chức năng chuyển đổi qua lại giữa FSP và LF. Sau đó, các thành phần này được tích hợp vào công cụ AGTool. Hình 4.1 mô tả kiến trúc của GUI-AGTool. Trong đó, các thành phần mới sẽ được phát triển và tích hợp với AGTool Core.
Chương 4.Chuyển đổi giữa các dạng biểu diễn của LTS
4.1 Chuyển đổi FSP sang LF
4.1.1 Ý tưởng
Hệ thống sẽ cài đặt một thành phần riêng được gọi làFSP Compiler. Thành phần
này có chức năng chuyển đổi dữ liệu FSP sang LF. Bài toán chuyển đổi FSP sang LF được mô tả bởi Hình 4.2. Dữ liệu FSP ở đầu vào được xử lý bởi thành phần
Hình 4.2: Cấu trúc thành phần chuyển đổi từ FSP sang LF.
FSP Compiler. Các thành phần trong dữ liệu đầu vào sẽ được chuyển đổi từ danh sách các hàm chuyển trạng thái sang kiểu dữ liệu LF bởi Thuật toán 4.1.
Danh sách list_transitions gồm các phần tử có định dạng là (start_state, list_sub_trans). Trong đó, start_state là trạng thái ban đầu, list_sub_trans là danh sách các tiến trình con. Tiến trình con này được định nghĩa bởi một nhãn và một trạng thái kết thúc –stop_state. Tương ứng với mỗi tiến trình con, ta thu được một phần tử gồm ba thành phần: (start_state, label, stop_state). Phần tử này chính là một hàm chuyển trạng thái. Phần tử khởi tạo là trạng thái bắt đầu
của hàm dịch chuyển đầu tiên trong danh sách list_transitions. Danh sách các
trạng tháil_statesđược xác định tại các dòng 3, 4 và 8, 9 của thuật toán. Tại các bước này, một trạng thái xuất hiện sẽ được kiểm tra. Nếu trạng thái này là trạng
thái mới thì nó được thêm vào danh sách l_states. Các hàm chuyển trạng thái
được thêm vào danh sáchl_transtại dòng 7 trong Thuật toán 4.1. FSP M biểu diễn một kênh truyền tin được đặc tả như Hình 4.3.
S0 = (ack2,ack1→ S0 | snd1→ S1 | snd2→ S2), S1 = (snd2,snd1,out1,ack2→S0), S2 = (snd2,snd1,out2,ack1→S0). Hình 4.3: FSP M.
Dựa vào việc phân tích từ vựng và phân tích cú pháp của M ta thu được list_transitions chứa các phần tử có kiểu là (start_state, list_sub_trans).
Chương 4.Chuyển đổi giữa các dạng biểu diễn của LTS
Thuật toán 4.1Thuật toán chuyển đổi FSP sang LF.
Đầu vào: list_transitions là danh sách các tiến trình con.
Đầu ra: listing_form là kiểu dữ liệu LF.
1: Khởi tạo l_states = empty, l_trans = empty, init_state = null {l_ states là danh sách rỗng chứa các trạng thái, l_ trans là danh sách rỗng chứa các hàm chuyển trạng thái, init_state là trạng thái khởi tạo}
2: for all(start_state, list_sub_trans) in list_transitionsdo
3: ifl_states.exists(start_state) = falsethen
4: l_states.add(start_state) {thêm phần tử start_state vào danh sách l_states}