Hướng dẫn tạo bộ phân tích từ vựng cho ngôn ngữ Pascal

Chia sẻ: Lang Huyen | Ngày: | Loại File: PDF | Số trang:5

0
196
lượt xem
68
download

Hướng dẫn tạo bộ phân tích từ vựng cho ngôn ngữ Pascal

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

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ã...

Chủ đề:
Lưu

Nội dung Text: Hướng dẫn tạo bộ phân tích từ vựng cho ngôn ngữ Pascal

  1. Hướ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 //đị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;
  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 ("[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);}
  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ụ
  4. cá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;
  5. buf[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);  
Đồng bộ tài khoản