You are on page 1of 42

XÂY DỰNG NỀN TẢNG WEBSERVER

Thực hiện:
Lê Nguyên Dũng
Email:
dungcoivb@gmail.com

TP. Hồ Chí Minh – 2011


Lời nói đầu
Trong những năm gần đây, sự bùng nổ của ngành Công nghệ thông tin nói chung và mạng
Internet nói riêng kéo theo nhu cầu xây dựng các website doanh nghiệp lẫn cá nhân. Để xây dựng
một website cho các doanh nghiệp thường có một chi phí không nhỏ, chi phí này bao gồm cả
phần cứng và phần mềm để duy trì hoạt động của một website. Với các website cá nhân chi phí
thường thấp thậm chí có là miễn phí, tuy nhiên tính tồn tại cũng như tự chủ của các cá nhân với
website của mình thường còn hạn chế.
Để đơn giản hóa vấn đề này, tôi đã tiến hành xây dựng một nền tảng web server nhỏ gọn
nhưng đủ đáp ứng đủ những yêu cầu tối thiểu như bảo mật, dễ dùng để xây dựng và duy trì các
web site nhỏ.
Nền tảng web server này rất gọn nhẹ, chiếm rất ít tài nguyên hệ thống, điều này giúp việc
triển khai một website với chi phí về phần cứng giảm đi rất nhiều so với các nền tảng thông
thường. Đề tài không mang tham vọng trở thành một nền tảng mạnh mẽ thay thế các nền tảng
hiện nay, nhưng nó có lẽ cũng là một sự lựa chọn đáng để thử do tính tinh gọn cũng như khả
năng mềm dẻo dễ dàng mở rộng của nó.

Dù đã có nhiều cố gắng, nhưng đề tài này không tránh được nhiều thiếu xót, tôi mong nhận
được sự phê bình và góp ý của quý thầy cô.

Trang 1
Mục lục
Chương 1: Kiến trúc hệ thống web server ............................................................................................ 3
1.1 Các thành phần......................................................................................................................... 3
1.2 Tương tác giữa PedaServer và hệ thống thực ............................................................................ 3
1.3 Tương tác giữa nội bộ giữa các thành phần ............................................................................... 4
Chương 2: Server Core - Quản lý tương tác kết nối............................................................................. 7
2.1 Kết nối client-server ................................................................................................................. 7
2.2 Giao thức HTTP....................................................................................................................... 7
2.3 Tùy chọn cổng làm việc của server ........................................................................................... 9
2.4 Bảng ánh xạ URL..................................................................................................................... 9
2.5 Bảng phân quyền.................................................................................................................... 10
Chương 3: Minimum Scripting - Ngôn ngữ kịch bản xử lý tại server ............................................... 12
3.1 Các thao tác xây dựng ngôn ngữ lập trình ............................................................................... 12
3.2 Đặc tả cú pháp ngôn ngữ lập trình .......................................................................................... 13
3.3 Phân tích cấu trúc ngôn ngữ ................................................................................................... 15
3.4 Kiến trúc máy ảo .................................................................................................................... 22
Chương 4: Thành phần plugin ............................................................................................................ 28
4.1 Giới thiệu............................................................................................................................... 28
4.2 Quản lý plugin ....................................................................................................................... 28
Chương 5: Ứng dụng minh họa........................................................................................................... 30
5.1 Giới thiệu............................................................................................................................... 30
5.2 Mô hình ................................................................................................................................. 30
5.3 Minh họa................................................................................................................................ 32
Chương 6: Kết luận ............................................................................................................................. 34
6.1 Ưu điểm................................................................................................................................. 34
6.2 Khuyết điểm .......................................................................................................................... 34
6.3 Hướng phát triển .................................................................................................................... 34
Chương 7: Phụ lục ............................................................................................................................... 35
7.1 Các thuật ngữ sử dụng ............................................................................................................ 35
7.2 Đặc tả cấu trúc ngôn ngữ Minimum Scripting......................................................................... 35
7.3 Các opcode của máy ảo .......................................................................................................... 39
Tài liệu tham khảo ............................................................................................................................... 41

Trang 2
Chương 1: Kiến trúc hệ thống web server
1.1 Các thành phần
- Web server được xây dựng từ các thành phần chính như sau:
o Server Core: Phần tương tác kết nối, quản lý các kết nối, truyền nhận giữa server và
các client.
o Minimum Scripting Engine: Phần xử lý ngôn ngữ kịch bản tại server. Đây là một
ngôn ngữ lập trình mới được xây dựng cho PedaServer, cú pháp của Minimum
Scripting dễ học, dễ viết, tuy nhiên chỉ nằm ở mức cơ bản.
o SQLite: Phần xử lý tương tác với cơ sở dữ liệu. SQLite là một hệ quản trị cơ sở dữ
liệu tương tự MySQL hay SQL Server hay Oracle… tuy nhiên nhỏ gọn và chức năng
hạn chế hơn nhiều. Tuy nhiên với nhu cầu của một web server nhỏ, không cần quá
nhiều xử lý về mặt cơ sở dữ liệu thì SQLite là đủ đáp ứng nhu cầu.
o Plugin: Nền tảng bổ sung chức năng. Thành phần này là mã thực thi thuần độc lập
với nền tảng PedaServer hiện tại, thành phần này không thuộc server core mà chỉ là
một module ở ngoài độc lập hoàn toàn. Server core chỉ gọi lên khi khởi động và gọi
hàm nhất định khi request tới có chứa cấu trúc là module này yêu cầu. Server core có
quyền quyết định thành phần này có làm được gọi hay không, còn sau khi đã được
gọi thì thành phần này làm việc với quyền ngang bằng server core.

1.2 Tương tác giữa PedaServer và hệ thống thực


- Các thành phần trên muốn tương tác trực tiếp với dữ liệu trong server phải dựa trên cơ
chế phân quyền của server core, điều này đảm bảo hệ thống hoạt động nhất quán và an
toàn.

- Bảng quyền tương tác của các thành phần:


Tác động trực tiếp
Khởi tạo
Thành phần với dữ liệu trên
bởi
server
Scripting Engine Server Core Không
SQLite Server Core Không1
Plugin Server Core Có
1: Khởi tạo ban đầu để tương tác vẫn phải thông qua Server Core

Trang 3
Server Files/Folder

PedaServer

Server core

Plugin Scripting SQLite


Engine

Hình: Tương tác với hệ thống

1.3 Tương tác giữa nội bộ giữa các thành phần


- Phần Server Core quản lý tương tác với client. Khi nhận một request từ client, Server
Core sẽ phân tích xem yêu cầu thuộc loại nào, có 3 loại yêu cầu:
o Yêu cầu xem tập tin/thư mục: Server Core sẽ tự xử lý.
o Yêu cầu một tập tin dạng scripting: Server Core sẽ chuyển yêu cầu này đến
Scripting Engine xử lý. Server Core ghi nhận kết quả trả về và trả về client. Trong
quá trình này có diễn ra bắt lỗi của parser và runtime của máy ảo.

Trang 4
o Yêu cầu một chức năng nhúng: Server Core sẽ chuyển yêu cầu này đến plugin xử
lý với module tương ứng. Server Core ghi nhận kết quả trả về và trả về client.
- Tương tác giữa SQLite và Scripting Engine: Với mỗi thao tác tương tác từ Scripting
Engine gọi hàm của SQLite sẽ phải thông qua việc kiểm duyệt của bảng phân quyền
thuộc Server Core. Trong thực thế xây dựng thì Scripting Engine sẽ không gọi trực tiếp
hàm tương tác mà thay vào đó nó sẽ gọi hàm thuộc một lớp mang tên PedaSQL, lớp này
kiểm tra tương tác đó đến từ scripting thuộc thư mục nào và đối chiếu với bảng phân
quyền để xem scripting đó có quyền tương tác với SQLite hay không, nếu có thì lớp này
sẽ tiến hành truy vấn tới SQLite.

- Bảng quyền tương tác của các thành phần:


Thành phần Nhận chỉ thị từ
Scripting Engine Server Core
SQLite Scripting Engine2
Plugin Server Core
2: Scripting Engine tương tác với SQLite tuy nhiên chịu sự quản lý của bảng phân quyền
thuộc Server Core.

Trang 5
Client

Server Response Request

Server core

SecurityTable

Parameter
Result Result

Plugin Scripting SQLite


Engine
Result
Query

Hình: Tương tác nội bộ PedaServer

Trang 6
Chương 2: Server Core - Quản lý tương tác kết nối
2.1 Kết nối client-server
- Khi khởi động, phần mềm web server trên máy server sẽ là một tiến trình. Tiến trình này
lắng nghe tại một cổng, khi có yêu cầu kết nối (Từ client), tiến trình web server sẽ quyết
định có kết nối hay không, nếu có thì tiến hành tạo một thread mới với một socket tương
ứng với kết nối.
- Một server quản lý cùng lúc nhiều kết nối với các máy client. Mọi quá trình làm việc như
nhận, gửi dữ liệu giữa server với máy trạm sẽ sử dụng socket tương ứng đang kết nối
giữa 2 máy.

Process

Thread 1
Thread 2
Thread 3
Thread 4

Client Server

Hình: Kết nối client-server

2.2 Giao thức HTTP


- HTTP (HyperText Transfer Protocol - Giao thức truyền tải siêu văn bản) là một trong
năm giao thức chuẩn về mạng Internet, được dùng để liên hệ thông tin giữa máy cung cấp
dịch vụ (Web server) và máy sử dụng dịch vụ (client) là giao thức Client/Server dùng cho
World Wide Web-WWW, HTTP là một giao thức ứng dụng của bộ giao thức TCP/IP
(Các giao thức nền tảng cho Internet).

Trang 7
Process
Request Thread i

Response

Client Server

Hình: Truyền nhận dữ liệu giữa client-server theo giao thức HTTP

2.2.1 HTTP Request


- Client gửi yêu cầu đến Server bằng một yêu cầu.
- Một thông báo yêu cầu bao gồm một số dòng văn bản.
- Dòng đầu tiên được gọi là đầu yêu cầu (request header) chứa ba thông số:
o Phương thức yêu cầu (request method): GET/ POST.
o URL.
o Phiên bản HTTP được sử dụng.
- Các dòng tiếp theo chứa thông tin về các kiểu tệp, tập ký tự được chấp nhận, phiên bản
trình duyệt, hệ điều hành sử dụng trên client …

- Ví dụ:
Gói tin Request GET khi truy cập vào địa chỉ http://fit.hcmup.edu.vn bằng FireFox
GET / HTTP/1.1
Host: fit.hcmup.edu.vn
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US;
rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=
0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
If-Modified-Since: Mon, 06 Sep 2010 03:23:50 GMT
If-None-Match: "774c7-8f-48f8ecf20bd80"-gzip
Cache-Control: max-age=0

2.2.2 HTTP Response


- Dữ liệu do server gửi về cho client được định dạng bởi HTTP Response.
- Một HTTP Response bao gồm:
o Dòng trạng thái (status line): Giao thức được dùng, số trạng thái và giá trị trạng thái.
o Phần đầu gói tin trả lời (response header): Chứa chuỗi các cặp tên/giá trị.

Trang 8
o Dữ liệu thực sự: Trang HTML.

- Ví dụ:
Gói tin tin Response khi truy cập vào địa chỉ http://fit.hcmup.edu.vn bằng FireFox
HTTP/1.1 200 OK
Date: Wed, 02 Mar 2011 11:42:36 GMT
Server: Apache/2.2.13 (Win32) mod_ssl/2.2.13 OpenSSL/0.9.8k
P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"
Set-Cookie: lang=deleted; expires=Tue, 02-Mar-2010 11:42:35
GMT; path=/
Set-Cookie: jfcookie=deleted; expires=Tue, 02-Mar-2010
11:42:35 GMT; path=/
Set-Cookie: jfcookie[lang]=deleted; expires=Tue, 02-Mar-2010
11:42:35 GMT; path=/
Set-Cookie: portal_site=61; expires=Wed, 02-Mar-2011 21:42:36
GMT
Content-Encoding: gzip
X-Content-Encoded-By: Joomla! 1.5
Expires: Mon, 1 Jan 2001 00:00:00 GMT
Last-Modified: Wed, 02 Mar 2011 11:42:36 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-
check=0, pre-check=0
Pragma: no-cache
Keep-Alive: timeout=15, max=97
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
<title>Khoa Công Nghệ Thông Tin-Trường ĐHSP TP.HCM</title>
<frameset>
<frame src="http://portal.hcmup.edu.vn/?site=61">
</frameset>

2.3 Tùy chọn cổng làm việc của server


- Mỗi máy tính bất kỳ đều có rất nhiều cổng (Port) để có thể kết nối với máy tính hay thiết
bị khác. Với một địa chỉ IP, chúng ta chỉ có thể xác định được một máy tính duy nhất trên
mạng, tuy nhiên khi một máy tính chạy nhiều dịch vụ khác nhau thì chúng phải được
phân biệt bởi khái niệm cổng.
- Để cấu hình PedaServer làm việc trên cổng nào của server thì chúng ta cấu hình trong tập
tin config.xml.

- Phẩn code cấu hình mẫu :


<system>
<port>80</port>
</system>

2.4 Bảng ánh xạ URL

Trang 9
- Ánh xạ URL là việc khi server nhận được request một URL nào đó và nếu nó thuộc
nguồn của một ánh xạ thì sẽ được chuyển đến (Redirect) tới một URL đích.
- Nền tảng PedaServer hỗ trợ chức năng ánh xạ URL qua một địa chỉ khác cùng tên miền
mà PedaServer đang sử dụng. Để cấu hình chức năng này người dùng cần cấu hình trong
tập tin config.xml.

- Phần code cấu hình mẫu :


<mapping_table>
<mapping url="/add">
/sample/site/index.min
</mapping>
<mapping url="/sql">
/sample/sql.min
</mapping>
</mapping_table>

/add /sample/site/index.min

/sql /sample/sql.min

Tập nguồn Tập đích


l l
Hình: Ánh xạ URL

2.5 Bảng phân quyền


- Nền tảng PedaServer cho phép thiết lập bảng phân quyền theo từng thư mục, đây là yếu
tố then chốt để đảm bảo sự bảo mật cho hệ thống.
- Bảng phân quyền được cấu hình tại tập tin config.xml. Bảng phân quyền có 4 quyền như
sau.
Tên phân quyền Ý nghĩa
access Quyền truy cập vào thư mục
directory Quyền truy cập duyệt theo thư mục
scripting Quyền thực thi scripting tại thư mục
sql Quyền được kết nối SQLite từ scripting thuộc thư mục đó hay không

- Phần code cấu hình mẫu:


<permission path="/WebResource"> <!— URL thư mục -->
<access>true</access>
<directory>true</directory>
<scripting>false</scripting>
<sql>false</sql>
</permission>

Trang 10
Hình: Phân quyền được duyệt thư mục

Hình: Phân quyền không được duyệt thư mục

Trang 11
Chương 3: Minimum Scripting - Ngôn ngữ kịch bản
xử lý tại server
3.1 Các thao tác xây dựng ngôn ngữ lập trình
- Phân tích từ vựng của ngôn ngữ.
- Phân tích cú pháp: Từ các bộ từ vựng ở bước trên và cú pháp ta khai báo bằng ngôn ngữ
đặc tả chúng ta sẽ phân tích được ngữ nghĩa của đoạn mã đầu vào. Kết quả trả về sẽ là
một cây ngữ nghĩa, với mỗi nút tương ứng với một cú pháp nào đó mà chúng ta đã xây
dựng.
- Phân tích ngữ nghĩa: Từ cây cú pháp đã có, chúng ta tiến hành kiểm tra tính logic của
cây.
- Biên/Thông dịch: Dựa vào cái cây ở trên mà biên dịch hoặc thông dịch ra dạng mã trung
gian.
- Trình nạp: Đưa lên dữ liệu sau khi biên/thông dịch lên bộ nhớ thực theo cấu trúc quy ước
từ trước của máy ảo.
- Thực thi: Bước thực thi vùng mã sau khi đã tiến hành bước bộ nạp.

Mã đầu vào

Phân tích
ngữ pháp Phân tích từ vựng

Phân tích cú pháp

Phân tích ngữ nghĩa

Thông dịch

Máy ảo
Bộ nạp lên bộ nhớ

Thực thi máy ảo

Kết quả trả về

Hình: Kiến trúc xây dựng một trình thông dịch

Trang 12
- Để đơn giản hóa quá trình xây dựng ngôn ngữ lập trình, tôi sử dụng một số công cụ trong
một số công đoạn xây dựng ngôn ngữ lập trình:
o flex: Lexical Analysis + Tokens.
o Bison: Syntactic Analysis

3.2 Đặc tả cú pháp ngôn ngữ lập trình


- Để xây dựng cú pháp của một ngôn ngữ lập trình, trước hết chúng ta phải xây dựng một
mô hình giúp mô tả đẩy đủ nhất các loại cú pháp mà ta muốn sử dụng. Để làm được việc
này, người ta sử dụng một dạng gọi là Ngữ pháp phi ngữ cảnh (Context-free grammar).
Ngữ pháp phi ngữ cảnh là một ngữ pháp tự nhiên tạo thành một ngôn ngữ hình thức. Ngữ
pháp phi ngữ cảnh có thể được biểu thị bằng ký pháp Dạng chuẩn Backus (Backus
Normal Form).

3.2.1 Đặc tả định nghĩa các kiểu dữ liệu


- Đây là các đặc tả cơ bản mô tả các kiểu dữ liệu của một bộ phân tích ngữ nghĩa ngôn ngữ
sử dụng. Đây là các dữ liệu đầu tiên để tiến hành xây dựng các luật sau nó.
Cấu trúc viết:
Khai báo một Kiểu dữ liệu:
Tên kiểu Biểu thức Dòng mới

Khai báo một Biểu thức:

Ký tự

Bộ chữ *

Bộ tên +

( Biểu thức ) ?

- Bảng quy tắc viết đặc tả :


Quy tắc Ý nghĩa Ví dụ Giá trị
[…] Giá trị thuộc cụm khai báo … a b c* ab, abc, abcc, abccc, abcccc,
...
{…} Giá trị tương ứng đối tượng khai {LETTER}+ cat, dog, pig, ...
báo trong …
(…) Chứa các đối tượng khác (a|b|c) a, b, c
| Toán tử hoặc a|b|c a, b, c
* Thêm 0 hoặc nhiều ký tự trước nó a[12]*b ab, a1b, a2b, a12b, a21b,
a22b, a111b, ..
+ Thêm 1 hay nhiều ký tự trước nó '*'+ '*'+
? Tùy chọn, 0 hoặc 1 ký tự a b? c abc, ac

Trang 13
“…” Đưa vào một giá trị chuỗi “string” string

- Sau đây là các kiểu dữ liệu mà đề tài này khai báo :


Tên khai báo Khai báo Kiểu dữ liệu Ví dụ
LETTER [a-zA-Z_] Ký tự B
DIGIT [0-9] Số 8
IDENT {LETTER}({LETTER}|{DIGIT})* Tên biến b8
STR \"[^\"]*\" Chuỗi “I’m string”
INTEGER {DIGIT}{DIGIT}* Số nguyên 123
Hoặc : {DIGIT}+
FLOAT {DIGIT}+\.{DIGIT}* Số thực 123.45
WSPACE [ \t]+ Khoảng cách

3.2.2 Đặc tả định nghĩa các luật


- Đây là các đặc tả mô tả cấu trúc của các cú pháp, cấu trúc của ngôn ngữ.
- Quy tắc viết một luật :

Tên kiểu
Tên luật Dòng mới : Dòng mới
Tên luật
;

| Dòng mới
Dòng mới

- Cú pháp ngôn ngữ được viết bằng nhiều luật khác nhau. Có thể coi cú pháp của một ngôn
ngữ là một cây, trên cây đó luôn có một nút đỉnh, mỗi luật hay Kiểu dữ liệu là một nút
trên cây.
- Mỗi luật có thể có nhiều cách thể hiện khác nhau, tuy nhiên phải đảm bảo không trùng
lặp và sai logic trong cây.
- Mỗi luật có thể cấu thành từ nhiều luật khác, thậm chí là chính nó (Đệ qui).
- Ví dụ :
o Luật để thực hiện các phép toán cơ bản với số nguyên:
add_expression
: add_expression “+” mul_expression
| add_expression “-“ mul_expression
| mul_expression
;

mul_expression
: mul_expression “*” INTEGER
| mul_expression “/” INTEGER

Trang 14
| mul_expression “%” INTEGER
| INTEGER
;

o Đặc tả khai báo phép gán:


assign_expression
: identifier “=” assign_expression
| add_expression
;

o Đặc ta biểu thức:


equal_expression
: expression “==” assign_expression
| expression “<” assign_expression
| expression “>” assign_expression
| expression “<=” assign_expression
| expression “>=” assign_expression
| expression “!=” assign_expression
| assign_expression
;

o Đặc tả IF then
optional_else_statement
: “else” statement
|
;
if_statement
: “if” “(“ expression “)” statement optional_else_statement
;

o Đặc tả FOR
for_statement
: “for” “(“ expression “;” expression “;” expression “)” statement
;

3.3 Phân tích cấu trúc ngôn ngữ


- Phần này sẽ đi sâu vào giải thích cách thức phân tích cú pháp từ một mã nguồn đầu vào.
3.3.1 Phân tích từ vựng
- Đây là phần định nghĩa các kiểu dữ liệu, các từ vựng có nghĩa ra thành các phần độc lập.
Việc tách từ vựng này hoàn toàn tương đồng với với việc tách một câu văn bản thành các
thành phần độc lập có nghĩa riêng trong câu văn thông thường.
- Ví dụ:

Trang 15
print “print” Đây là ví dụ

Sẽ được phân tích: Sẽ được phân tích:


print “print” Đây là ví dụ

Hàm Chuỗi Động từ Trạng từ Danh từ

- Việc bóc tách dữ liệu gồm 2 phần :


o Bóc tách một các kiểu dữ liệu tĩnh như : Số nguyên, số thực, chuỗi …
o Bóc các chuỗi khai báo được dùng như : “if”, “for”, “while” …
3.3.1.1 Bóc tách một số kiểu dữ liệu
- Việc bóc tách một số dạng dữ liệu sử dụng chúng ta sử dụng Đặc tả kiểu dữ liệu. Với mỗi
kiểu dữ liệu, chúng ta sẽ thể hiện kiểu dữ liệu theo cách chung nhất bằng các đặc tả.
- Việc viết cụ thể đặc tả từng loại dữ liệu sẽ giúp bộ phân tích xác định được dữ liệu đầu
vào thuộc loại dữ liệu nào đã quy định hay không.
- Sau đây là các kiểu dữ liệu mà đề tài này khai báo :
Tên khai báo Khai báo Kiểu dữ liệu Ví dụ
LETTER [a-zA-Z_] Ký tự B
DIGIT [0-9] Số 8
IDENT {LETTER}({LETTER}|{DIGIT})* Tên biến b8
STR \"[^\"]*\" Chuỗi “I’m string”
INTEGER {DIGIT}{DIGIT}* Số nguyên 123
Hoặc : {DIGIT}+
FLOAT {DIGIT}+\.{DIGIT}* Số thực 123.45
WSPACE [ \t]+ Khoảng cách

3.3.1.2 Bóc tách một số chuỗi khai báo được dùng


- Là các chuỗi chưa thuộc các Kiểu dữ liệu tĩnh, được khai báo để sử dụng trong các cú
pháp lệnh, hàm.
- Một số chuỗi khai báo trong đề tài :”if”, “for”, “while”, “print”, “=”, “==” …

3.3.2 Xây dựng cây cú pháp


3.3.2.1 Phân tích cú pháp
- Giai đoạn phân tích cú pháp thực hiện công việc nhóm các thẻ từ của chương trình nguồn
thành các ngữ đoạn văn phạm (grammatical phrase), mà sau đó sẽ được trình biên dịch
tổng hợp ra kết quả. Thông thường, các ngữ đoạn văn phạm này được biểu diễn bằng
dạng cây phân tích cú pháp (parse tree) với :
o Ngôn ngữ được đặc tả bởi các luật sinh.
o Phân tích cú pháp dựa vào luật sinh để xây dựng cây phân tích cú pháp.

3.3.2.2 Cây cú pháp


- Từ bước Phân tích cú pháp, chúng ta sẽ xây dựng được một cây cú pháp, trên đó sẽ thể
hiện toàn cấu bộ cấu trúc mã nguồn đầu vào được thể hiện dưới dạng một cây. Mỗi luật
được thể hiện bằng một nhánh trong cây.
- Đây là bước hoàn tất để máy tính có thể “hiểu” được mã nguồn nhập vào mang ý nghĩa
gì.

Trang 16
- Ví dụ:
o Đặc tả:
Kiểu dữ liệu:
LETTER [a-zA-Z_]
INTEGER {DIGIT}+

Luật:
add_exp
: add_exp “+” mul_exp
| add_exp “-“ mul_exp
| mul_exp
;

mul_exp
: mul_exp “*” INTEGER
| mul_exp “/” INTEGER
| mul_exp “%” INTEGER
| INTEGER
;
o Đoạn mã:
3+4*5

add_exp

+
add_exp mul_exp

mul_exp mul_exp * INTEGER

INTEGER INTEGER 5

3 4

3.3.2.3 Phân tích ngữ nghĩa


- Giai đoạn phân tích ngữ nghĩa sẽ thực hiện việc kiểm tra xem chương trình nguồn có
chứa lỗi về ngữ nghĩa hay không và tập hợp thông tin về kiểu cho giai đoạn sinh mã về
sau. Một phần quan trọng trong giai đoạn phân tích ngữ nghĩa là kiểm tra kiểu và ép
chuyển đổi kiểu dữ liệu.
- Ví dụ:
o Đặc tả tính toán biểu thức toán:
Kiểu dữ liệu:
LETTER [a-zA-Z_]
INTEGER {DIGIT}+

Trang 17
STRING \"[^\"]*\"

Luật:
add_exp
: add_exp “+” mul_exp
| add_exp “-“ mul_exp
| mul_exp
;

mul_exp
: mul_exp “*” val_exp
| mul_exp “/” val_exp
| mul_exp “%” val_exp
| val_exp
;

val_exp
: STRING
| INTEGER
;
o Đoạn mã:
3+4*”wrong”
o Phân tích ngữ nghĩa:
Cú pháp không có gì sai tuy nhiên về mặt ngữ nghĩa, một chuỗi
(STRING) không thể thực hiện phép tính (mul_exp hay add_exp)
với một số nguyên (INTEGER).
Chúng ta có thể quan sát cây ngữ cú pháp để nhận ra điều trên.

add_exp

+
add_exp mul_exp

mul_exp mul_exp * val_exp

val_exp val_exp STRING

INTEGER INTEGER “wrong”

3 4

Trang 18
3.3.3 Quản lý bảng ký hiệu
- Phần này sử dụng từ định danh để nói đến các tên biến.
- Một nhiệm vụ quan trọng của trình biên dịch là ghi lại các định danh được sử dụng trong
chương trình nguồn và thu thập các thông tin về các thuộc tính khác nhau của mỗi định
danh. Những thuộc tính này có thể cung cấp thông tin về vị trí lưu trữ được cấp phát cho
một định danh, kiểu và tầm vực của định danh, và nếu định danh là tên của một thủ tục
thì thuộc tính là các thông tin về số lượng và kiểu của các đối số, phương pháp truyền đối
số và kiểu trả về của thủ tục nếu có.
- Bảng ký hiệu (Symbol table) là một cấu trúc dữ liệu mà mỗi phần tử là một mẩu tin dùng
để lưu trữ một định danh, bao gồm các trường lưu giữ ký hiệu và các thuộc tính của nó.
Cấu trúc này cho phép tìm kiếm, truy xuất tên định danh một cách nhanh chóng.
- Trong quá trình phân tích từ vựng, danh biểu được tìm thấy và nó được đưa vào bảng ký
hiệu nhưng nói chung các thuộc tính của nó có thể chưa xác định được trong giai đoạn
này.

- Ví dụ:
o Đoạn mã:
strTest = "1234";
intTest = 1234;

if (strTest == intTest)
{
print "Hello !”;
}
else
print “Oh my code :( !”;
o Bảng định danh sau bước phân tích cú pháp:
Bảng ký hiệu Kiểu
strTest Chưa rõ
intTest Chưa rõ
“1234” STRING
1234 INTEGER
“Hello !” STRING
“Oh my code :( !” STRING
- Rõ ràng thuộc tính kiểu của 2 biến strTest và intTest chưa thể xác định khi các định
danh được xác định và đưa vào bảng ký hiệu. Các giai đoạn sau đó như phân tích ngữ
nghĩa và thông dịch mới đưa thêm các thông tin này vào và sử dụng chúng. Nói
chung giai đoạn thông tin thường đưa các thông tin chi tiết về vị trí lưu trữ dành cho
định danh và sẽ sử dụng chúng khi cần thiết.
o 2 nhánh của cây cú pháp:

Trang 19
assign_exp

IDENT STR

STR
strTest “1234”

assign_exp

IDENT INTEGER

INTEGER
strTest 1234

- Từ bước phân tích ngữ nghĩa ta có thể nhận ra thuộc tính của định danh.
Bảng ký hiệu Kiểu
strTest STRING
intTest INTEGER
- Một số thuộc tính, giá trị chỉ xuất hiện trong quá trình thực thi mã.

3.3.4 Thông dịch


- Sau bước phân tích cú pháp ngôn ngữ chúng ta sẽ thu được một cây cấu trúc chương
trình. Để biến nó trở thành một chương trình có thể làm việc được ta phải tiến hành bước
gọi là Thông dịch để trở thành mã có thể chạy trên máy ảo (Hoặc biên dịch để có thể
chạy được trên máy thật).
- Quá trình thông dịch ra dạng mã máy trung gian dựa hoàn toàn trên các opcode quy định
của máy ảo. Viết dạng mã máy trung gian phải đặc biệt chú tâm các vấn đề như bảo toàn
ngăn xếp, hạn chế sử dụng bộ nhớ không cần thiết, giải phóng bộ nhớ sau sử dụng cẩn
thận…
- Quy tắc thông dịch là không phải mọi nút trên cây đều phải được thông dịch, thay vào đó
là chỉ dịch những nút cần thiết đủ để diễn tả trọn vẹn nghĩa của chương trình mà cây thể
hiện.
- Quá trình thông dịch, với các thông tin là định danh thì mọi các giá trị bảng ký hiệu được
quy về con trỏ ảo tương ứng trong bảng ký hiệu.
- Với một số opcode đặc biệt, thông tin giá trị lưu trữ không cần lưu trong Bảng ký hiệu.
Như OP_JUMP, nó sẽ lấy một giá trị lưu bên trong biến Opcode đó để nhảy (Do cấu trúc
máy ảo đề tài này xây dựng có chút khác biệt với máy thật).
- Ví dụ:
o Đoạn mã:

Trang 20
strTest = "1234";
intTest = 1234;

if (strTest == intTest)
{
print "Hello !”;
}
else
print “Oh my code :( !”;
o Cây ngữ pháp (Có giản lượt cho gọn):

program

= = if_statement

strTest “1234” intTest 1234 condition then_part else_part

== print print

Định danh strTest intTest “Hello !” “Oh my code :( !”

o Bảng ký hiệu:
Con trỏ Giá trị Kiểu
1. strTest
2. “1234” STRING
3. intTest
4. 1234 INTEGER
5. “Hello !” STRING
6. “Oh my code :( !” STRING
o Mã kết quả thông dịch:
IP Opcode Giá trị
1. OP_PUSH 2
2. OP_GETTOP 1
3. OP_PUSH 4
4. OP_GETTOP 3
5. OP_PUSH 1

Trang 21
6. OP_PUSH 3
7. OP_EQUAL
8. OP_JMPF
9. OP_PUSH 5
10. OP_PRINT
11. OP_JMP 14
12. OP_PUSH 6
13. OP_PRINT
14. OP_NOP
3.4 Kiến trúc máy ảo
3.4.2 Các thành phần của máy ảo

Ngăn Bảng ký Mã Thanh


Máy ảo
xếp hiệu thực thi ghi IP

3.4.2.1 Ngăn xếp


- Ngăn xếp: Là một cấu trúc danh sách sách giá trị liền kề, hoạt động theo nguyên lý "vào
sau ra trước" (Last In First Out - LIFO).
- Ở máy ảo này, ngăn xếp dùng để lưu trữ tạm trong các thao tác làm việc trên máy ảo.

Push Pop

Hình: Mô tả ngăn xếp

3.4.2.2 Bảng định danh


- Bảng định danh: Là một mảng lưu trữ các kiểu giá trị cụ thể hoặc biến được sử dụng.
- Nếu Bảng định danh sau khi phân tích cú pháp nó chỉ mang một số giá trị thông tin chưa
đầy đủ thì trong máy ảo, bảng định danh này tiếp tục được bổ xung thông tin và thay đổi
các giá trị đã có phụ thuộc vào thao tác làm việc của máy ảo.
- Mỗi vị trí trên bảng ký hiệu làm một cấu trúc gồm 2 thông tin :
o Kiểu giá trị.
o Giá trị.
o Sau đây là các kiểu giá trị được mô tả:
Kiểu Loại giá trị
INTEGER Kiểu số nguyên
FLOAT Kiểu số thực

Trang 22
STRING Kiểu chuỗi
ARRAY Kiểu mảng
SQL Kiểu biến SQL mang con trỏ kết nối hiện tại
SQL_RET Kiểu biến SQL trả về sau khi truy vấn
SQL_ARRAY Kiểu biến SQL lấy cụ thể mảng giá trị sau truy vấn

1 Giá trị 1

2 Giá trị 2 Kiểu giá trị


Giá trị
3 Giá trị 3

Con trỏ ảo

Hình: Bảng ký hiệu

- Các thao tác với giá trị đều thông qua các con trỏ ảo trong bảng ký hiệu.

3.4.2.3 Thanh ghi IP


- Thanh ghi IP: Địa chỉ lưu trữ vị trí hiện tại của con trỏ đang làm trên trên bộ nhớ.
- Thanh ghi này là tối quan trọng trong việc điều khiển cấu trúc máy ảo hoạt động, với các
opcode thường không làm thay đổi IP thì thanh ghi chỉ đơn thuần cộng 1 sau mỗi bước,
nhưng các opcode như nhảy (OP_JUMP) thì việc nhảy đó chính là thanh đổi giá trị thanh
ghi IP này.

3.4.3 Bộ nạp lên bộ nhớ


o Đây là phần tiền khởi tạo trước khi thực thi một đoạn mã sau khi đã thông dịch. Nó giúp
đưa những các dữ liệu khởi tạo ban đầu tới phần tương ứng trên bộ nhớ của nó.
o Bộ nạp sẽ lấy dữ liệu từ bảng ký hiệu và mã thực thi rồi đưa lên bộ nhớ của máy ảo, các
thành phần bộ nạp tương tác ở máy ảo trong đề tài này là:
o Bảng ký hiệu: Bộ nạp sẽ đưa lên bảng ký hiệu của máy ảo và đánh số (Con trỏ ảo)
từng vị trí ký hiệu.
o Mã thực thi: Ngoài việc đưa mã thực thi, tại một số mã thực thi có chứa ký hiệu thì bộ
nạp sẽ ghi giá trị con trỏ ảo để tương tác.

3.4.4Cấp phát bộ nhớ trên máy ảo


o Máy ảo quản lý bộ nhớ trên một bảng lưu trữ ký hiệu, danh sách này khai báo số ô nhớ là
tĩnh. Bộ nhớ này được độc lập với các thành phần còn lại của máy ảo, các thao tác khai
báo cần sử dụng thêm bộ nhớ sẽ được quản lý bởi máy ảo.
o Ngoài ra, trong bảng lưu trữ ký hiệu có cơ chế để lưu trữ mảng, mỗi phần tử của mảng
hoạt động như một bảng lưu trữ ký hiệu khác tuy nhiên thay vì con trỏ ảo thì sử dụng một
định danh khác.
o Quá trình tương tác với bộ nhớ trên máy ảo xảy ra trong các bước:
o Bộ nạp: Tác động lên thành phần bảng ký hiệu và mã thực thi.

Trang 23
o Quá trình thực thi mã: Tương tác với ngăn xếp và bảng ký hiệu
o Ví dụ:
o Đoạn mã:
intTest = 12+34;
arrStr["One"] = 56;

o Bảng ký hiệu sau phân tích cú pháp:


Con trỏ Giá trị Kiểu
1. intTest
2. 12 INTEGER
3. 34 INTEGER
4. arrStr ARRAY
5. “One” STRING
6. 56 INTEGER
o Bảng định danh khi máy ảo thực thi mã:
Con trỏ Giá trị Kiểu
1. intTest INTEGER
2. 12 INTEGER
3. 34 INTEGER
4. arrStr ARRAY
5. “One” STRING
6. 56 INTEGER

[Bảng định danh của biến arrStr]


Định danh Giá trị Kiểu
“One” 56 INTEGER

3.4.4.1 Mã thực thi


- Mã thực thi: Chứa các mã thực thi liên tục với nhau để thực hiện các chỉ thị lệnh cho máy
ảo thực hiện các thao tác.
- Mỗi vị trí mã thực thi sẽ lưu 2 giá trị: Một opcode và một giá trị đi kèm. Việc có một giá
trị đi kèm là dùng để thực hiện các thao tác ban đầu như PUSH, JUMP… mà không cần
dùng ngăn xếp. Tuy mô hình thật sự hoàn chỉnh của máy ảo là không có giá trị này mà sử
dũng ngăn xếp hoàn toàn, tuy nhiên do thời gian xây dựng ít nên ở tôi tôi tạm sử dụng
cách này để đảm bảo ngăn xếp được ổn định trong quá trình làm việc.
- Khi thực thi máy ảo sẽ thực thi từng opcode trong danh sách cho đến hết danh sách. Quá
trình thực thi có thể không tuần tự do có một số opcode mang chỉ thị nhảy.
- Trong quá trình thực hiện các thao tác làm việc, sẽ có một số opcode chứa các việc dùng
ngăn xếp và thanh ghi biến.

Trang 24
Opcode1

Opcode2 Opcode
Giá trị
Opcode3

Hình …: Danh sách mã thực thi

o Ví dụ 1: Mô tả thao tác cộng 2 số: 5 + 3


o Khởi tạo bộ nhớ ban đầu sau khi nạp

OP_PUSH 1
1 INT 3
OP_PUSH 2
2 INT 5
OP_ADD

Bảng ký hiệu Danh sách mã thực thi

o Thực thi:

IP Stack Giá trị biến tại Opcode Thao tác


stack
0 <rỗng> Khởi tạo

1 INT 3
1 OP_PUSH(1) Push(1)

1 INT 3
2 2 INT 5 OP_PUSH(2) Push(2)

i = Pop()
k INT 8 j = Pop()
3 OP_ADD val(k) = val(i) + val(j)
//Lấy giá trị biến từ con trỏ
Push(k)

o Ví dụ 2:
o Đoạn mã:
strTest = "1234";
intTest = 1234;

Trang 25
if (getnumber(strTest) == intTest)
{
print "Hello !”;
}
else
print “Oh my code :( !”;

o Bảng ký hiệu:
Con trỏ Giá trị Kiểu
1. strTest
2. “1234” STRING
3. intTest
4. 1234 INTEGER
5. “Hello !” STRING
6. “Oh my code :( !” STRING

o Mã kết quả thông dịch:


IP Opcode Giá trị
1. OP_PUSH 2
2. OP_GETTOP 1
3. OP_PUSH 4
4. OP_GETTOP 3
5. OP_PUSH 1
6. OP_GETNUM
7. OP_PUSH 3
8. OP_EQUAL
9. OP_JMPF
10. OP_PUSH 5
11. OP_PRINT
12. OP_JMP 15
13. OP_PUSH 6
14. OP_PRINT
15. OP_NOP

o Quá trình thực thi mã:


 Bảng ký hiệu chỉ mô tả lại các ký hiệu có giá trị bị thay đổi so với bảng ký hiệu
ban đầu hoặc thao tác trước đó.
 Hàm New(): Mô tả thao tác khai báo 1 ký hiệu mới lên bộ nhớ, trả về con trỏ ảo.
 Hàm val(i): Mô tả lấy giá trị tại con trỏ ảo i.
 Hàm number(giá trị): Mô tả việc lấy giá trị và trả về một số kiểu INTEGER.
 Hàm Print(giá trị): Mô tả việc in giá trị ra màn hình.

Trang 26
Bảng ký hiệu
Con
STT IP Stack Định Opcode
trỏ Kiểu Giá trị Thao tác
danh
ảo
1 1 OP_PUSH (2) Push(2)
OP_GETTOP (1) i = Pop()
2 2 2
val(1) = val(i)
3 3 1 strTest STRING “1234” OP_PUSH (4) Push(4)
i = Pop()
4 4 4 OP_GETTOP (3)
val(3) = val(i)
5 5 3 intTest INTEGER 1234 OP_PUSH (1) Push(1)
i = Pop()
n = New()
6 6 1 OP_GETNUM
val(n) = number(val(i))
Push(n)
7 7 7 7 const01 INTEGER 1234 OP_PUSH (3) Push (3)
i = Pop()
j = Pop()
3 if (val(i) == val(j)
8 8 OP_EQUAL
7 Push (1)
else
Push (-1)
9 9 1 OP_JMPF (12)
10 10 OP_PUSH (5) Push (5)
i = Pop()
11 11 5 OP_PRINT
Print val(i)
12 12 OP_JMP (14)
13 14 OP_NOP
o Kết quả xuất ra:
Hello !

Trang 27
Chương 4: Thành phần plugin
4.1 Giới thiệu
- Plugin là thành phần nhúng bổ sung chức năng cho ứng dụng ban đầu.
- Ở PedaServer, cho phép sử dụng các plugin để bổ sung thêm tính năng xử lý ở các URL
quy định từ phía người quản trị.
- Mỗi plugin là một thành phần hoàn toàn riêng biệt lẫn nhau và lẫn server core. Các plugin
chỉ liên kết với server core bằng các hàm gọi. Plugin được khởi động khi server khởi
động, kết thúc phiên làm việc khi server dừng làm việc.
- Một plugin của PedaServer là một Thư viện liên kết động (Các tập tin có đuôi .dll) gồm
có 3 hàm:
o Init: Khởi tạo dữ liệu ban đầu.
o RunIt: Với mỗi truy vấn tới, hàm này sẽ được gọi để xử lý sau đó trả về giá trị
tương ứng với truy vấn đó.
o Clean: Giải phóng bộ nhớ.

4.2 Quản lý plugin


- Plugin được cấu hình trong tập tin config.xml gồm đường dẫn tới plugin và URL mà
plugin sẽ sử dụng. Việc cấu hình này giúp việc quản lý các plugin dễ dàng quả lý và an
toàn hơn cơ chế tự nhận plugin ở một thư mục cố định rồi khởi động plugin.
Cấu hình bổ sung thêm module và url ánh xạ tới module
<plugin_table>
<plugin name="Plugin\PedaDict.dll">
/q=
</plugin>
</plugin_table>

- Server Core sẽ quản lý các plugin bằng một danh sách chứa con trỏ điều khiển các
plugin. Khi PedaServer khởi động, chương trình sẽ tìm kiếm tất cả các plugin trong bảng
cấu hình sau đó sẽ đưa lần lượt từng module này lên bộ nhớ và gọi hàm Init để khởi tạo
một phiên làm việc cho từng plugin.
- Khi một request từ tới server, server core sẽ kiểm tra xem trong danh sách các plugin có
đối tượng nào cần sử dụng URL request hay không, nếu có thì sẽ trả về plugin tương ứng.
Việc làm này tương tự với việc ánh xạ URL.

/add Add

/dic Dictionary
t
Tập URL Tập plugin
l l
Hình: Ánh xạ URL vào plugin

Trang 28
Client

Server Response Request

Server core Plugin table


Request

Result

Plugin Plugin Plugin

Hình: Tương tác với Plugin

Trang 29
Chương 5: Ứng dụng minh họa
5.1 Giới thiệu
- Ứng dụng từ điển online: PedaDict.
- Nền tảng sử dụng: Plugin – PedaServer.
- Chức năng: Từ điển Anh-Việt, Việt-Anh nhỏ gọn và linh hoạt.
- Cơ sở dữ liệu nền tảng: Là cơ sở dữ liệu từ điển được chia sẻ tại
http://www.tudientiengviet.net . Tuy nhiên cấu trúc có thay đổi để có thể sử dụng băm để
cải thiện tốc độ tra từ.
- Ứng dụng:
o Khai triển tại máy cá nhân: Do ứng dụng đòi hỏi cấu hình rất thấp nên có thể dễ dàng
cài đặt trên máy tính cá nhân.
o Dùng trong mạng nội bộ: Do thế mạnh là giao diện trên nền web nên các máy khác
cùng mạng có thể dễ dàng truy cập và sử dụng từ điển này khi có một máy bất kỳ
trong mạng có cài đặt ứng dụng.
o Từ điển online: Chỉ cần một máy chủ có kết nối Internet và cấu hình có thể khai triển
ứng dụng thành một từ điển online dễ dàng.

5.2 Mô hình
- Ứng dụng gồm 2 phần:
o Phần client: Giao diện tương tác và truy vấn lên server. Phần client sử dụng Ajax
trong các thao các tra từ.
o Phần server: Module Dict được viết thành một class với 3 hàm cơ bản để Server
Core gọi:
 Init: Khởi tạo module. Kiểm tra xem các tập tin dữ liệu từ điển có tồn tại,
đúng cấu trúc không. Nếu đúng sẽ mở cơ sở dữ liệu từ điển và đưa lên một
struct khai báo sẵn.
 RunIt: Gọi khi request tới. Xử lý request, tra từ trên struct đã lưu trữ trên
bộ nhớ. Nếu có sẽ trả về kết quả là nghĩa của từ.
 ~Dict: Hủy lớp. Giải phóng bộ nhớ các struct đã lưu trữ.

- Trong Server Core có viết thêm vài lệnh để khi truy vấn có dạng /q=… thì request sẽ gọi
hàm RunIt từ module Dict và truyền vào truy vấn. Sau đó Dict sẽ trả về kết quả cho
Server Core để response cho client.
- Trong tập tin config.xml có thêm một số dòng để cấu hình:
Cấu hình quyền truy cập vào thư mục chứa PedaDict
<permission path="/Dict">
<access>true</access>
<directory>true</directory>
<scripting>false</scripting>
<sql>false</sql>
</permission>

Cấu hình ánh xạ để rút ngắn tên khi truy cập


<mapping url="/dict">
/Dict/index.html

Trang 30
</mapping>

Cấu hình bổ sung thêm module và url ánh xạ tới module


<plugin_table>
<plugin name="Plugin\PedaDict.dll">
/q=
</plugin>
</plugin_table>

- Cấu trúc tập tin lưu trữ từ điển:


Mỗi loại từ điển (Anh-Việt, Việt-Anh) sẽ gồm 3 tập tin:
dic.hash: File chỉ mục vị trí xuất hiện chữ cái đầu tiên của từ
Ký tự Base64 của hash

dic.index: File chỉ mục các từ


Từ Base64 địa chỉ nghĩa Base64 độ dài nghĩa

dic.dict: File chưa nghĩa cụ thể của các từ


Nghĩa

Trang 31
5.3 Minh họa

Gợi ý

Nghĩa

Hình: Giao diện chính của từ điển

Trang 32
Hình: Tính năng Select&See

Trang 33
Chương 6: Kết luận
6.1 Ưu điểm
- Nhỏ gọn, hoạt động tiết kiệm tài nguyên hệ thống.
- Cơ chế phân quyền theo thư mục giúp đảm mức độ bảo mật cho hệ thống.
- Scripting Engine với máy ảo giúp cơ chế làm việc an toàn và hiệu quả.
- Plugin giúp dễ dàng phát triển ứng dụng web service với tốc độ xử lý nhanh và tối ưu bộ
nhớ mà không tốn nhiều tài nguyên hệ thống.
- Cấu hình dễ dàng với tập tin config.xml.
- Hoạt động ổn định (Trừ Scripting Engine đôi lúc còn lỗi).

6.2 Khuyết điểm


- Scripting Engine:
o Hiện chưa thể xử lý đa luồng (Do phần parser sử dụng bison không hỗ trợ đa
luồng).
o Đôi khi còn bị lỗi
o Leak bộ nhớ.
o Chưa hỗ trợ tốt Tiếng Việt.
o Ngôn ngữ và máy ảo còn nhiều khiếm khuyết.
- Cấu hình hiện tại chưa linh động, mỗi lần thay đổi cấu hình phải khởi động lại server.

6.3 Hướng phát triển


- Mặc dù có thể sử dụng các hệ Scripting Engine có sẵn hiện nay như PHP hay Python…
nhưng ở đây tôi cố gắng xây dựng một nền tảng ngôn ngữ lập trình riêng nhằm tạo ra một
nền đủ vững để phát triển sau này. Với cơ chế máy ảo chạy độc lập của Minimum
Scripting cho phép phát triển hệ thống trên cơ chế phân quyền chặt chẽ, và quan trọng
hơn cả là khả năng phát triển hệ thống hỗ trợ web dịch vụ (Web service) với một cấu hình
server yêu cầu không cần cao.
- Scripting Engine mở rộng cú pháp, hoàn thiện các cơ chế như session, cookie... cho phép
biên dịch mã nguồn thành tập tin thực thi.
- Thiết lập tùy chọn cấu hình các luật nhầm bảo vệ server trước hình thức tấn công DDos
và các hình thức tấn công khác.

Trang 34
Chương 7: Phụ lục
7.1 Các thuật ngữ sử dụng

Thuật ngữ Nghĩa


Client Máy trạm
Server Máy chủ
Thread Luồng
Process Tiến trình
Socket Là thành phần quản lý kết nối trong mô hình TCP/IP
Opcode Operation code – Mã điều khiển
Virtual Machine Máy ảo
SQL Structured Query Language - Ngôn ngữ truy vấn có cấu trúc
Lexical Analysis Bóc tách từ vựng
Tokens Các bộ từ vựng
Syntactic Analysis Phân tích ngữ pháp
Parse Tree Cây ngữ nghĩa
Compiler Biên dịch
Interpreter Thông dịch
Loader Chương trình nạp
Execute Thực thi
Formal language Ngôn ngữ hình thức
Context-free grammar Ngữ pháp phi ngữ cảnh
Backus Normal Form Dạng chuẩn Backus
Defining Terminal Định nghĩa nút dữ liệu
Defining Rules Định nghĩa các luật
Stack Ngăn xếp
Instruction Pointer Thanh ghi con trỏ hiện tại trên bộ nhớ
Jump Nhảy
Request Yêu cầu
Response Trả lời / Đáp ứng
Plugin Thành phần tương tác bổ sung
Embedded Nhúng
Module Thành phần bổ sung
Dynamic-link library Thư viện liên kết động

7.2 Đặc tả cấu trúc ngôn ngữ Minimum Scripting


7.2.1 Phân tích từ vựng
Đây là phần đặc tả đầu vào để Flex phân tích thực hiện phân tích từ vựng.

LETTER [a-zA-Z_]
DIGIT [0-9]
IDENT {LETTER}({LETTER}|{DIGIT})*
STR \"[^\"]*\"
INTEGER {DIGIT}{DIGIT}*

Trang 35
FLOAT {DIGIT}+\.{DIGIT}*
WSPACE [ \t]+

"int" {return TYPE_INTEGER;}


"float" {return TYPE_FLOAT;}
"string" {return TYPE_STRING;}

"rand" {return FUNC_RANDOM;}


"get" {return FUNC_GET;}
"getstring" {return FUNC_STRING;}
"getnumber" {return FUNC_NUMBER;}

"sql_connect" {return FUNC_SQL_CONNECT;}


"sql_create_db" {return FUNC_SQL_CREATE_DB;}
"sql_select_db" {return FUNC_SQL_SELECT_DB;}
"sql_query" {return FUNC_SQL_QUERY;}
"sql_fetch_array" {return FUNC_SQL_FETCH_ARRAY;}
"sql_close" {return FUNC_SQL_CLOSE;}

"isnull" {return IS_NULL;}

"if" {return IF;}


"else" {return ELSE;}
"while" {return WHILE;}
"for" {return FOR;}
"print" {return PRINT;}
"inra" {return INRA;}
"input" {return INPUT;}
"=" {return ASSIGN;}
"==" {return EQUAL;}
"!=" {return NOT_EQUAL;}
"<" {return L_OP;}
">" {return G_OP;}
"<=" {return LE_OP;}
">=" {return GE_OP;}

"+" {return ADD;}


"-" {return SUB;}
"*" {return MUL;}
"/" {return DIV;}
"%" {return MOD;}

";" {return END_STMT;}


"(" {return OPEN_PAR;}
")" {return CLOSE_PAR;}
"[" {return OPEN_ARRAY;}
"]" {return CLOSE_ARRAY;}
"{" {return BEGIN_CS;}
"}" {return END_CS;}

"," {return COMMA;}

{IDENT} {Identifier (); return ID;}

{INTEGER} {Identifier (); return CONST_INTEGER;}


{FLOAT} {Identifier (); return CONST_FLOAT;}

{STR} {StringConstant (); return STRING;}


"//" {EatComment();}
\n {lineno++;}
{WSPACE} {}

Trang 36
. {return ERROR_TOKEN;}

7.2.1 Phân tích cú pháp


Đây là phần đặc tả đầu vào để Bison phân tích thực hiện cú pháp đưa về cây cú pháp.

program
: statement_list {tree = $1;}
;

statement_list
: statement_list statement {$$ = new TreeNode (STMT_LIST, $1, $2);}
| /* empty */ {$$ = new TreeNode (EMPTY_STMT);}
;

statement
: END_STMT {$$ = new TreeNode (EMPTY_STMT);}
| expression END_STMT {$$ = new TreeNode (EXPR_STMT, $1);}
| PRINT expression END_STMT {$$ = new TreeNode (PRINT_STMT, $2);}
| INPUT identifier END_STMT
{$$ = new TreeNode (INPUT_STMT); $$->symbol = $2;}
| if_statement {$$ = $1;}
| while_statement {$$ = $1;}
| for_statement {$$ = $1;}
| procedure_expression {$$ = $1;}
| compound_statement {$$ = $1;}
| error END_STMT {$$ = new TreeNode (ERROR_STMT);}
;

if_statement
: IF OPEN_PAR expression CLOSE_PAR statement optional_else_statement
{
if ($6 != NULL)
$$ = new TreeNode (IFTHENELSE_STMT, $3, $5, $6);
else
$$ = new TreeNode (IFTHEN_STMT, $3, $5);
}
;

optional_else_statement
: ELSE statement {$$ = $2;}
| /* empty */ {$$ = NULL;}
;

while_statement
: WHILE OPEN_PAR expression CLOSE_PAR statement
{$$ = new TreeNode (WHILE_STMT, $3, $5);}
;

for_statement
: FOR OPEN_PAR expression END_STMT expression END_STMT expression CLOSE_PAR statement
{$$ = new TreeNode (FOR_STMT, $3, $5, $7, $9);}
;

compound_statement
: BEGIN_CS statement_list END_CS
{$$ = $2;}
;

expression
: equal_expression {$$ = $1;}

Trang 37
| declaration {$$ = $1;}
;

equal_expression
: expression EQUAL assign_expression
{$$ = new TreeNode (COND_EQUAL, $1, $3);}
| expression L_OP assign_expression
{$$ = new TreeNode (COND_L, $1, $3);}
| expression G_OP assign_expression
{$$ = new TreeNode (COND_G, $1, $3);}
| expression LE_OP assign_expression
{$$ = new TreeNode (COND_LE, $1, $3);}
| expression GE_OP assign_expression
{$$ = new TreeNode (COND_GE, $1, $3);}
| expression NOT_EQUAL assign_expression
{$$ = new TreeNode (COND_NOT_EQUAL, $1, $3);}
| assign_expression
{$$ = $1;}
;

assign_expression
: identifier ASSIGN assign_expression
{$$ = new TreeNode (ASSIGN_EXPR, $3); $$->symbol = $1;}
| identifier OPEN_ARRAY value_expression CLOSE_ARRAY ASSIGN assign_expression
{$$ = new TreeNode (ASSIGN_ARRAY, $3, $6); $$->symbol = $1;}
| add_expression
{$$ = $1;}
;

declaration
: TYPE_INTEGER identifier
{$$ = new TreeNode (DECL_TYPE_INTEGER); $$->symbol = $2;}
| TYPE_FLOAT identifier
{$$ = new TreeNode (DECL_TYPE_FLOAT); $$->symbol = $2;}
| TYPE_STRING identifier
{$$ = new TreeNode (DECL_TYPE_STRING); $$->symbol = $2;}
;

constant_expression
: integer
{$$ = new TreeNode (INT_EXPR); $$->symbol = $1;}
| float
{$$ = new TreeNode (FLOAT_EXPR); $$->symbol = $1;}
| string
{$$ = new TreeNode (STR_EXPR); $$->symbol = $1;}
;

add_expression
: add_expression ADD mul_expression
{$$ = new TreeNode (OPERATION_ADD, $1, $3);}
| add_expression SUB mul_expression
{$$ = new TreeNode (OPERATION_SUB, $1, $3);}
| mul_expression
{$$ = $1;}
;

mul_expression
: mul_expression MUL value_expression
{$$ = new TreeNode (OPERATION_MUL, $1, $3);}
| mul_expression DIV value_expression
{$$ = new TreeNode (OPERATION_DIV, $1, $3);}

Trang 38
| mul_expression MOD value_expression
{$$ = new TreeNode (OPERATION_MOD, $1, $3);}
| value_expression
{$$ = $1;}
;

value_expression
: identifier
{$$ = new TreeNode (IDENT_EXPR); $$->symbol = $1;}
| identifier OPEN_ARRAY value_expression CLOSE_ARRAY
{$$ = new TreeNode (ARRAY_EXPR, $3); $$->symbol = $1;}
| constant_expression
{$$ = new TreeNode (CONST_EXPR); $$ = $1;}
| OPEN_PAR add_expression CLOSE_PAR
{$$ = $2;}
| function_expression
{$$ = $1;}
;

function_expression
: FUNC_RANDOM OPEN_PAR CLOSE_PAR
{$$ = new TreeNode (FUNC_RANDOM_STMT);}
| FUNC_GET OPEN_PAR add_expression CLOSE_PAR
{$$ = new TreeNode (FUNC_GET_STMT, $3);}
| FUNC_STRING OPEN_PAR add_expression CLOSE_PAR
{$$ = new TreeNode (FUNC_STRING_STMT, $3);}
| FUNC_NUMBER OPEN_PAR add_expression CLOSE_PAR
{$$ = new TreeNode (FUNC_NUMBER_STMT, $3);}
| IS_NULL OPEN_PAR identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_IS_NULL_STMT); $$->symbol = $3;}
| FUNC_SQL_CONNECT OPEN_PAR add_expression COMMA add_expression CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_CONNECT_STMT, $3, $5);}
| FUNC_SQL_CREATE_DB OPEN_PAR add_expression COMMA identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_CREATE_DB_STMT, $3); $$->symbol = $5;}
| FUNC_SQL_FETCH_ARRAY OPEN_PAR identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_FETCH_ARRAY_STMT); $$->symbol = $3;}
| FUNC_SQL_QUERY OPEN_PAR add_expression COMMA identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_QUERY_STMT, $3); $$->symbol = $5;}
;

procedure_expression
: FUNC_SQL_SELECT_DB OPEN_PAR add_expression COMMA identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_SELECT_DB_STMT, $3); $$->symbol = $5;}
| FUNC_SQL_CLOSE OPEN_PAR identifier CLOSE_PAR
{$$ = new TreeNode (FUNC_SQL_CLOSE_STMT); $$->symbol = $3;}
;

7.3 Các opcode của máy ảo

Opcode Thao tác


OP_NOP Không làm gì cả
OP_PUSH Đưa vị trí của giá trị (Hoặc biến) vào stack
OP_GETTOP Đưa giá trị vào biến
OP_DISCARD Xóa khỏi bộ nhớ 1 biến
OP_PRINT In giá trị ra
OP_JMP Nhảy tới một địa chỉ trên bộ nhớ
OP_JMPF Nhảy tới một địa chỉ trên bộ nhớ nếu đáy stack mang giá trị false

Trang 39
OP_COND_EQUAL, So sánh bằng
OP_COND_NOT_EQUAL So sánh không bằng
OP_COND_L So sánh nhỏ hơn
OP_COND_G So sánh lớn hơn
OP_COND_LE So sánh nhỏ hơn hoặc bằng
OP_COND_GE So sánh lớn hơn hoặc bằng
OP_ADD Thực hiện phép tính cộng
OP_SUB Thực hiện phép tính trừ
OP_MUL Thực hiện phép tính nhân
OP_DIV Thực hiện phép tính chia
OP_MOD Thực hiện phép tính lấy dư
OP_GET Lấy giá trị truyền vào qua URL (Thao tác Get)
OP_STR Đưa giá trị về kiểu chuỗi
OP_NUM Đưa giá trị về kiểu số
OP_GETTOP_ARR Đưa giá trị vào một phần tử trong bảng
OP_SQL_CONNECT Kết nối với SQL
OP_SQL_CREATE_DB Tạo một cơ sở dữ liệu mới
OP_SQL_SELECT_DB Chọn cơ sở dữ liệu để thao tác
OP_SQL_QUERY Thực hiện truy vấn
OP_SQL_FETCH_ARRAY Truy cập đến bộ phần tử trong mảng
OP_SQL_CLOSE Đóng kết nối SQL
OP_IS_NULL Kiểm tra biến có là rỗng không
OP_RAND Tạo giá trị ngẫu nhiên
JUMPTARGET Khai báo địa chỉ để nhảy tới (Thực ra chỉ là một OP_NOP)

Trang 40
Tài liệu tham khảo
1. Jan Niestadt. Implementing A Scripting Engine. 1999.
2. Sekio. SupraWWW. 2007.
3. D. Richard Hipp. SQLite. 2011.
4. Lee Thomason. TinyXML. 2010.
5. W3Schools. PHP MySQL Introduction. 2011.
6. Devin Cook. GOLD Meta-Language Overview. 2011
7. Wikipedia.com.
8. Thạc sĩ Lê Ngọc Sơn, Đại học Công nghiệp TP. Hồ Chí Minh. Giáo trình trình biên dịch.

Trang 41

You might also like