Sau khi có file khả thi thí dụ tên là pascalscan.exe, bạn có thể dùng nó với cú pháp sau đây: pascalscan mypro.pas để đọc file mã nguồn Pascal rồi hiển thị các token tương ứng lên màn h
Trang 1Hướng dẫn tạo bộ phân tích từ vựng cho ngôn ngữ Pascal
Xin hướng dẫn tạo bộ phân tích từ vựng cho ngôn ngữ Pascal, tức là chương trình đọc tệp pas và in kết quả phân tích từ vựng ra màn hình Cách dễ dàng và nhanh chóng nhất để xây dựng bộ phân tích từ vựng cho 1 ngôn ngữ nào đó là dùng ngôn ngữ Lex (Lexical Analyzer Generator) File Lex chứa các biểu thức chính quy, mỗi biểu thức chính quy miêu tả 1 token cụ thể của ngôn ngữ, định dạng tổng quát của file Lex như sau:
%{
//các lệnh định nghĩa viết bằng C hay C++
%}
%%
//các biểu thức chính quy nhận dạng các token
%%
//các đoạn code C hay C++ miêu tả ứng dụng
Sau đây là file Lex đọc file mã nguồn pascal rồi xuất ra danh sách các token tương ứng:
%{
#include <stdlib.h>
//định nghĩa các biến cần dùng
unsigned char ch;
unsigned yline = 1;
char buff[2048];
//hàm dò chuỗi chú thích
void scan_comment() {
unsigned char ch;
while ((ch=input())>0 && ch != '}')
if (ch == '\n') yline++;
if (ch == '}') return;
fprintf (stderr,"EOF duoc tim thay trong luc do chu thich (hang %d)\n",yline);
exit(1);
}
//hàm dò hằng chuỗi
void scan_string() {
unsigned char ch, *pret;
int i= 0;
while ((ch=input()) >0 && ch != '\'') {
if (ch == '\n') yline++;
if (i >= 2047) {
fprintf (stderr,"Chuoi qua dai (%d)\n",yline);
exit(1);
}
buff[i++] = ch;
Trang 2}
buff[i++] = 0;
if (ch == '\'') return;
fprintf (stderr,"EOF duoc tim thay trong luc do chuoi (hang %d)\n",yline); exit(1);
}
%}
%%
"{" { scan_comment(); }
"'" { scan_string();
printf("[string,'%s'] ",buff); }
"+" { printf ("[addop] "); }
"-" { printf ("[subop] "); }
"*" { printf ("[mulop] "); }
"/" { printf ("[divop] "); }
"<" { printf ("[lessop] "); }
">" { printf ("[thanop] "); }
"(" { printf ("[lparent] "); }
")" { printf ("[rparent] "); }
"," { printf ("[comma] "); }
";" { printf ("[pcomma] "); }
":" { printf ("[toodot] "); }
":=" { printf ("[assign] "); }
"=" { printf ("[equal] "); }
"." { printf ("[dot] "); }
[-][0-9]+ |
[0-9]+ { printf ("[iconst,%s] ", yytext); }
[0-9]*[.][0-9]* |
[-][0-9]*[.][0-9]* |
[0-9]*[.][0-9]*[eE][-]*[0-9]+ |
[-][0-9]*[.][0-9]*[eE][-]*[0-9]+ { printf ("[rconst,%s] ", yytext); }
[pP][rR][oO][gG][rR][aA][mM] { printf ("[program] "); }
[uU][sS][eE][sS] { printf ("[uses] "); }
[vV][aA][rR] { printf ("[var] "); }
[tT][yY][pP][eE] { printf ("[type] "); }
[bB][eE][gG][iI][nN] { printf ("[begin] "); }
[eE][nN][dD] { printf ("[end] "); }
[fF][uU][nN][cC][tT][iI][oO][nN] { printf ("[function] "); }
[pP][rR][oO][cC][eE][dD][uU][rR][eE] { printf ("[procedure] "); }
[iI][fF] { printf ("[if] "); }
[tT][hH][eE][nN] { printf ("[then] "); }
[eE][lL][sS][eE] { printf ("[else] "); }
[dD][oO] { printf ("[do] "); }
[rR][eE][pP][eE][aA][tT] { printf ("[repeat] "); }
[uU][nN][tT][iI][lL] { printf ("[until] "); }
[a-zA-Z][a-zA-Z0-9\_]* { printf ("[ident,%s] ", yytext);}
Trang 3[ \t\r]* { }
[\n] { printf("\n"); yline++; }
{ fprintf(stderr,"Syntax error: symbol '%c'(%d) o hang %d\n",yytext[0],yytext[0], yline); } %%
//điểm nhập của chương trình
void main(int argc,char *argv[]) {
//mở file mã nguồn
if ((yyin = fopen(argv[1],"r")) == 0) {
fprintf(stderr,"Khong the mo file %s\n",argv[1]);
exit(1);
}
//khởi động yylex
yylex_init();
//gọi yylex phân tích từ vựng
yylex();
}
Lưu ý rằng chúng tôi chỉ đặc tả một số token Pascal thường dùng, bạn dựa vào các lệnh đặc tả
để đặc tả thêm các token còn lại cho đủ
Sau khi đã viết xong file Lex, bạn có thể dùng tool Lex dịch nó ra file *.c tương ứng Thí dụ dưới đây là hàng lệnh gọi tiện ích FLEX dịch file Lex ra *.c:
flex -tl pascalscan.l >pascalscan.c
Sau khi đã có file *.c, bạn có thể dùng chương trình dịch C hay C++ dịch nó ra file khả thi Sau khi có file khả thi (thí dụ tên là pascalscan.exe), bạn có thể dùng nó với cú pháp sau đây:
pascalscan mypro.pas để đọc file mã nguồn Pascal rồi hiển thị các token tương ứng lên màn hình Hoặc dùng hàng lệnh sau:
pascalscan mypro.pas >output để đọc file mã nguồn Pascal rồi xuất các token tương ứng lên file
"output" để tham khảo sau đó
Lưu ý rằng chuỗi token nhận dạng được bởi bộ phân tích từ vựng thường được gửi tới bộ phân tích cú pháp chứ ít ai hiển thị hay xuất ra file như thí dụ trên
Bạn có thể tải tiện ích FLEX trên Internet (đây là ứng dụng mã nguồn mở)
Tôi muốn viết một ứng dụng đọc và hiển thị file TXT dạng Unicode trên Windows, nhưng tôi không thể hiển thị đúng các ký tự Unicode mặc dù đã sử dụng kiểu widestring và widechar Mặc dù mã Unicode đã được chuẩn hóa và tổng quát để miêu tả đồng thời nhiều ký tự của nhiều ngôn ngữ, nhưng hiện nay việc hiện thực xử lý mã Unicode không hoàn hảo, tùy vào môi trường lập trình và ngôn ngữ lập trình được dùng mà mức độ hỗ trợ mã Unicode rất khác nhau Thí dụ nếu bạn dùng môi trường Net (VC#, VJ#, VB Net) thì mức độ hỗ trợ mã Unicode là rất tốt, hầu như trong suốt hoàn toàn với code mà bạn viết Tuy nhiên nếu bạn dùng VB hay tệ hơn là VC++ thì mức độ hỗ trợ mã Unicode còn khá thấp và chưa được trong suốt cho người lập trình Thí dụ
Trang 4các đối tượng giao diện có sẵn của môi trường VB 6.0 trở xuống không thể hiển thị đúng được chuỗi Unicode, bạn phải dùng các đối tượng tương ứng trong thư viện Form2 kèm theo VB Còn trong VC++, nếu bạn dịch ứng dụng ở chế độ mặc định (ANSI) thì ứng dụng sẽ không xử lý được chuỗi Unicode Điều kiện tiên quyết để viết ứng dụng xử lý tốt chuỗi Unicode trong VC++
là phải dịch ứng dụng ở chế độ Unicode (dùng macro dịch là -D "Unicode") Về mặt lập trình VC++, nếu bạn muốn xử lý chuỗi Unicode cấp thấp hay muốn gọi các hàm API Windows để xử
lý chuỗi Unicode, bạn sẽ dùng kiểu dữ liệu widechar và widestring để định nghĩa các biến chứa
ký tự hay chuỗi Unicode Lưu ý rằng Windows chia tập các hàm có thông số chuỗi ra thành 2 loại: loại chỉ xử lý chuỗi ANSI và loại chỉ xử lý chuỗi Unicode Thí dụ hàm TextOut() chỉ hiển thị chuỗi ANSI, còn hàm TextOutW() chỉ hiển thị chuỗi Unicode Việc chuyển chuỗi ANSI về
mã Unicode luôn thành công, nhưng ngược lại, việc chuyển chuỗi Unicode về ANSI có thể làm mất thông tin
Sau đây là đoạn code VC++ thực hiện việc thiết lập font hỗ trợ Unicode cho Form, gọi hàm API TextOutW() để xuất chuỗi Unicode "Nguyễn Văn Hiệp" ra vị trí (10,100), xuất chuỗi "Nguyễn Văn Hiệp" ra TextBox được nhận dạng bởi biến m_edit
//xây dựng record miêu tả font cần dùng
LOGFONT lgcursfont; // font structure
lgcursfont.lfHeight = 24;
lgcursfont.lfWidth = 10;
lgcursfont.lfEscapement = 0;
lgcursfont.lfOrientation = 0;
lgcursfont.lfWeight = FW_NORMAL;
lgcursfont.lfItalic = FALSE;
lgcursfont.lfUnderline = FALSE;
lgcursfont.lfStrikeOut = FALSE;
lgcursfont.lfCharSet = ANSI_CHARSET;
lgcursfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
lgcursfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lgcursfont.lfQuality = DEFAULT_QUALITY;
lgcursfont.lfPitchAndFamily = FF_DONTCARE;
wcscpy(lgcursfont.lfFaceName, _T("Times"));
//thiết lập font cho Form
HDC hDC = this->GetDC()->m_hDC;
HFONT hFont = ::CreateFontIndirect (&lgcursfont);
::SelectObject(hDC,hFont);
//xây dựng chuỗi Unicode "Nguyễn Văn Hiệp"
wchar_t buf[50];
buf[0] = 'N'; buf[1] = 'g';
buf[2] = 'u'; buf[3] = 'y';
buf[4] = 0x1ec5; buf[5] = 'n';
buf[6] = ' '; buf[7] = 'V';
buf[8] = 0x103; buf[9] = 'n';
buf[10] = ' '; buf[11] = 'H';
buf[12] = 'i'; buf[13] = 0x1ec7;
Trang 5buf[14] = 'p'; buf[15] = 0;
//xuất chuỗi Unicode "Nguyễn Văn Hiệp" ra tọa độ (10,100) TextOutW(hDC,10,100,buf, wcslen(buf));
//xuất chuỗi Unicode "Nguyễn Văn Hiệp" ra TextBox UpdateData(TRUE);
m_edit = buf;
UpdateData(FALSE);