Q trình phân tích từ vựng

Một phần của tài liệu (LUẬN văn THẠC sĩ) nghiên cứu tổng quan về chương trình dịch luận văn ths toán học 60 46 35 (Trang 35)

3.3. Bộ sinh trình phân tích cú pháp Bison

3.3.1. Cấu trúc

Tƣơng tự nhƣ Flex, Bison nhận đầu vào là một tệp bao gồm các đặc tả của một ngơn ngữ. Từ đó, Bison biên dịch ra bộ phân tích cú pháp bằng mã C để chạy cùng với chƣơng trình. Cấu trúc của tệp đặc tả ngơn ngữ gồm ba phần:

Phần khai báo % % Phần luật dịch %% Flex Compiler C Compiler flex.out File mơ tả nguồn flex.l flex.yy.c Chƣơng trình nguồn flex.yy.c flex.out chuỗi token

Các thủ tục

1. Phần khai báo: Gồm các khai báo C thông thƣờng (biến, hàm, cấu trúc,

định nghĩa...), đƣợc giới hạn trong %{ và %} nhƣ Flex, khai báo các token và các thuộc tính kết hợp với token (nếu có).

2. Phần luật dịch: Mỗi luật dịch là một luật sinh kết hợp với hành vi ngữ

nghĩa. Mỗi luật sinh có dạng:

<vế trái>  <alt1> | <alt2> | ....| <altn> đƣợc mô tả trong Bison nhƣ sau:

<vế trái> : <alt1> {hành vi ngữ nghĩa 1} | <alt2> {hành vi ngữ nghĩa 2}

...

| <altn> {hành vi ngữ nghĩa n} ;

Trong luật sinh, các ký tự nằm trong cặp dấu nháy đơn ' ' là một ký hiệu kết thúc, một chuỗi chữ cái và chữ số không nằm trong cặp dấu nháy đơn và không đƣợc khai báo là token sẽ là ký hiệu chƣa kết thúc.

Hành vi ngữ nghĩa của Bison là một chuỗi các lệnh đƣợc viết bằng mã C mã C với:

$$: giá trị thuộc tính kết hợp với ký hiệu chƣa kết thúc bên vế trái của luật sinh.

$n: giá trị thuộc tính kết hợp với ký hiệu văn phạm thứ n (ký hiệu kết thúc hoặc chƣa kết thúc) của vế phải.

3.3.2. Quy trình vận hành

Hình 3.2. Tạo một chƣơng trình dịch input / output với Bison

Một chƣơng trình dịch có thể đƣợc xây dựng nhờ Bison bằng phƣơng thức đƣợc minh họa trong hình 3.2 trên. Trƣớc tiên, cần chuẩn bị một tập tin, chẳng hạn là translate.y, chứa một đặc tả Bison của chƣơng trình dịch. Lệnh bison translate.y sẽ biến đổi tập tin translate.y thành một chƣơng trình C có tên là y.tab.C. Bằng cách dịch y.tab.C cùng với thƣ viện ly nhờ lệnh cc y.tab.C - ly chúng ta thu đƣợc một chƣơng trình đối tƣợng a.out thực hiện quá trình dịch đƣợc đặc tả bởi chƣơng trình Bison ban đầu. Nếu cần thêm các thủ tục khác, chúng có thể đƣợc biên dịch hoặc đƣợc tải vào y.tab.C giống nhƣ mọi chƣơng trình C khác.

a.out Bison Compiler a.out translate.y y.tab.c input y.tab.c c C Compiler output

Chƣơng 4 - XÂY DỰNG CHƢƠNG TRÌNH DỊCH CHO NGƠN NGỮ MINIPAS

Trong chƣơng này, luận văn sẽ định nghĩa một ngơn ngữ tựa Pascal có tên gọi là Minipas và sau đó sử dụng các cơng cụ đã đƣợc giới thiệu trong chƣơng ba để xây dựng bộ phân tích từ vựng và bộ phân tích cú pháp cho ngơn ngữ

Minipas.

4.1. Yêu cầu

Minipas đƣợc xây dựng dựa trên ngơn ngữ Pascal. Mặc dù cịn đơn giản hơn Pascal rất nhiều nhƣng Minipas đáp ứng đƣợc các yêu cầu sau:

- Đơn giản, gọn nhẹ, dễ học.

- Đủ dùng để diễn tả một số thuật toán đơn giản cho ngƣời mới làm quen với ngơn ngữ lập trình.

4.2. Ngơn ngữ Minipas 4.2.1. Giới thiệu sơ lƣợc 4.2.1. Giới thiệu sơ lƣợc

Ngôn ngữ xây dựng đƣợc đặt tên là Minipas có một số đặc điểm:

- Có năm kiểu dữ liệu đơn giản: nguyên (integer), thực (double), logic (bool), kí tự (char), chuỗi ký tự (string). Hỗ trợ các phép toán số học - logic cơ bản trên ba kiểu đầu tiên. Kiểu char chỉ hỗ trợ phép gán và so sánh.

- Có kiểu cấu trúc là kiểu mảng một chiều với chỉ số nguyên, kiểu phần tử là kiểu đơn giản.

- Có ba cấu trúc điều khiển: tuần tự, rẽ nhánh (if… then… else) và vịng lặp (for…to…do, while.... do).

- Có kiểu chƣơng trình con là hàm. Chƣơng trình con có thể có tối đa là ba tham số.

- Có hai lệnh nhập dữ liệu từ bàn phím và xuất dữ liệu ra màn hình. - Cho phép hai dạng chú thích: chú thích khối và chú thích trên dịng. - Ngơn ngữ Minipas có phân biệt chữ hoa và chữ thƣờng.

Ngôn ngữ Minipas nhƣ vậy là tƣơng đối đủ cho ngƣời mới bắt đầu làm quen với việc học lập trình. Một số chi tiết phức tạp của ngơn ngữ Pascal (con

trỏ, kiểu dữ liệu cấu trúc, unit, đồ hoạ, tham biến...) đã đƣợc lƣợc bỏ giúp

Minipas đơn giản hơn.

4.2.2. Hƣớng dẫn sử dụng ngôn ngữ Minipas 4.2.2.1. Bảng chữ cái

Bảng chữ cái của Minipas là bảng kí tự ASCII.

4.2.2.2. Từ khố

Từ khóa là các từ dành riêng của ngôn ngữ Minipas, mỗi từ khóa có một cơng dụng riêng biệt. Minipas khơng cho phép đặt tên biến, tên hằng, tên chƣơng trình con… trùng với tên của từ khóa. Minipas có các từ khoá sau:

var const function endfunction begin

if then else endif for

do endfor to return end

while endwhile write read wait

Bảng 4.1. Bảng các token của ngôn ngữ Minipas 4.2.2.3. Tên 4.2.2.3. Tên

Tên trong Minipas đƣợc đặt theo quy tắc sau: tên bắt đầu bằng một chữ cái hoặc dấu gạch dƣới, sau đó có thể là chữ cái, chữ số và dấu gạch dƣới. Tên phân

biệt chữ hoa, chữ thƣờng. Độ dài tối đa cho tên là mƣời kí tự (nếu dài hơn sẽ bị cắt bỏ phần cuối).

4.2.2.4. Giá trị

Giá trị trong Minipas có năm loại:

- Giá trị nguyên: đƣợc viết dƣới dạng thập phân, cho phép có dấu hoặc khơng dấu, tối đa chín chữ số.

- Giá trị thực: đƣợc viết dƣới dạng thập phân, cho phép có dấu hoặc khơng dấu, tối đa chín chữ số.

- Giá trị logic: có hai giá trị là true (đúng) và false (sai).

- Giá trị kí tự: một ký tự đƣợc viết giống nhƣ trong Pascal, bắt dầu và kết thúc bằng cặp dấu „‟, ở bên trong là một kí tự.

- Giá trị xâu: xâu đƣợc viết giống nhƣ trong Pascal, một xâu đƣợc viết bên trong cặp dấu “ “, độ dài xâu có hơn một kí tự.

4.2.2.5. Hằng

Hằng là đại lƣợng có giá trị khơng đổi. Trong Minipas có hỗ trợ năm kiểu hằng ứng với năm kiểu giá trị ở trên.

Khai báo hằng trong Minipas nhƣ sau:

const

kiểu dữ liệu tên hằng := giá trị;

4.2.2.6. Khai báo biến

Biến là đại lƣợng có giá trị thay đổi. Trong Minipas, có bao nhiêu kiểu dữ liệu thì có bấy nhiêu kiểu biến, biến đƣợc khai báo nhƣ sau:

var

Khai báo biến kiểu mảng nhƣ sau:

var

array kiểu dữ liệu tên mảng[số phần tử];

4.2.2.7. Biểu thức

Biểu thức trong Minipas dùng để thể hiện một cơng thức tốn học. Biểu thức gồm tốn hạng và các phép tốn. Tốn hạng có thể là biến, hằng, hàm. Các phép tốn gồm:

- Các phép toán số học: +, - , *, /, % (chia lấy kết quả là phần dư) - Các phép toán quan hệ: <, > , =, != (khác), >=, <=

- Các phép toán logic: & (và), | (hoặc), ! (phủ định).

4.2.2.8. Cấu trúc chƣơng trình

<các khai báo> begin

<các câu lệnh>

end.

Trong phần <các khai báo> thì thứ tự của các khai báo nhƣ sau: 1. Khai báo biến

2. Khai báo hằng số

3. Khai báo chƣơng trình con

4.2.2.9. Các câu lệnh

Câu lệnh trong Minipas bắt buộc kết thúc bằng dấu „ ; „, kể cả câu lệnh đứng trƣớc từ khóa end kết thúc chƣơng trình. Khơng cho phép câu lệnh rỗng. Có các loại câu lệnh sau trong Minipas:

 Câu lệnh gán

<biến> := <biểu thức>;

<biến mảng> [<chỉ số>] := <biểu thức>;

 Câu lệnh nhập giá trị cho biến

read <biến 1>,…,<biến n>;

 Câu lệnh in ra giá trị của biểu thức

write <biểu thức>;  Câu lệnh rẽ nhánh Dạng 1: if <biểu thức> then <câu lệnh> endif; Dạng 2:

if <biểu thức> then <câu lệnh 1>

else <câu lệnh 2>

endif;

 Câu lệnh lặp với số bƣớc lặp xác định

for <biến>:=<giá trị đầu> to <giá trị cuối> do

<câu lệnh>

 Câu lệnh lặp với số bƣớc lặp không xác định

while <biểu thức> do

<câu lệnh>

endwhile;

4.2.2.10. Chƣơng trình con

Chƣơng trình con trong Minipas chỉ bao gồm hàm. Hàm có thể có tối đa ba tham số hình thức.

Khai báo hàm nhƣ sau:

function Tên_hàm(danh sách tham số hình thức);

<các khai báo cục bộ>

begin

<các câu lệnh>

endfunction;

Khi gọi hàm, phải viết Tên_hàm kèm theo danh sách tham số thực sự. Các tham số thực sự phải giống về số lƣợng và tƣơng ứng về kiểu dữ liệu với các tham số hình thức.

4.2.2.11. Chú thích

Minipas cho phép chú thích gồm hai cách:

 Đoạn văn bản chú thích đƣợc đặt sau dấu //, chú thích trên một dịng.

 Đoạn văn bản chú thích đƣợc đặt giữa cặp /* và */, đặt trên số dòng tuỳ ý.

4.3. Xây dựng chƣơng trình dịch cho Minipas

4.3.1. Xây dựng trình phân tích từ vựng cho Minipas

Bảng sau liệt kê danh sách các token của ngôn ngữ Minipas và biểu thức chính quy xác định các token đó:

Token Biểu thức chính quy

CHAR \'.\'

STRING \".*\"

DIGIT [0-9]+|\-[0-9]+

DOUBLE [0-9]+\.[0-9]*|\-[0-9]+\.[0-9]*

BOOLEAN true | false

IDENTIFIER [a-zA-Z_][a-zA-Z0-9_]* BEGIN begin END end LET var CONST const READ read WRITE write WRITELINE writeline IF if THEN then ELSE else FOR for TO to DO do ENDFOR endfor WHILE while ENDWHILE endwhile NUMBER_VAL {DIGIT} NUMBERD_VAL {DOUBLE}

CHR_VAL {CHAR} STR_VAL {STRING} FUNCTION function RETURN return ENDFUNCTION endfunction WAIT wait

ARRAY_I "array integer"

ARRAY_B "array bol"

ARRAY_C "array char"

ARRAY_D "array double"

ARRAY_S "array string"

ASSGNOP ":="

comment "//"

comment "/*"

whitespace [ \t\n]+

Bảng 4.1. Biểu thức chính quy đặc tả token 4.3.2. Xây dựng trình phân tích cú pháp cho Minipas

Văn phạm phi ngữ cảnh sinh ngơn ngữ Minipas:

// Luật sinh sinh chƣơng trình

program  var declarations

constant const_declarations func_decls BEGIN commands END ‘.’

// Các luật sinh khai báo identifier

var   | LET

declarations   | declarations declaration

declaration  INTEGER id_seq_int IDENTIFIER ‘;’

| CHAR id_seq_chr IDENTIFIER ‘;’

| DOUBLE id_seq_dou IDENTIFIER ‘;’

| STRING id_seq_str IDENTIFIER ‘;’

| BOOLEAN id_seq_bol IDENTIFIER ‘;’

| ARRAY_I IDENTIFIER '[' NUMBER_VAL ']'‘;’ | ARRAY_D IDENTIFIER '[' NUMBER_VAL ']'‘;’ | ARRAY_C IDENTIFIER '[' NUMBER_VAL ']'‘;’ | ARRAY_B IDENTIFIER '[' NUMBER_VAL ']'‘;’

id_seq_int   | id_seq_int IDENTIFIER‘,’

id_seq_chr   | id_seq_chr IDENTIFIER‘,’

id_seq_dou   | id_seq_dou IDENTIFIER’,’

id_seq_bol   | id_seq_bol IDENTIFIER‘,’

id_seq_str   | id_seq_str IDENTIFIER‘,’

// Các luật sinh khai báo hằng

constant   | CONST

const_declarations   | const_declarations const_declaration

const_declaration  INTEGER IDENTIFIER ASSGNOP NUMBER_VAL‘;’

| CHAR IDENTIFIER ASSGNOP CHR_VAL ‘;’

| DOUBLE IDENTIFIER ASSGNOP NUMBERD_VAL ‘;’ | STRING IDENTIFIER ASSGNOP STR_VAL ‘;’ | BOOLEAN IDENTIFIER ASSGNOP NUMBERB_VAL‘;’ // Các luật sinh khai báo hàm

func_decls   | func_decl

func_decl  FUNCTION IDENTIFIER '(' func_var_decls ')'

func_declarations IN

commands ENDFUNCTION ‘;’

func_var_decls   | func_var_decls func_var_decl

func_var_decl  var_seq INTEGER IDENTIFIER

| var_seq CHAR IDENTIFIER

| var_seq DOUBLE IDENTIFIER

| var_seq TRING IDENTIFIER

| var_seq BOOLEAN IDENTIFIER

var_seq   | var_seq INTEGER IDENTIFIER ‘,’

| var_seq CHAR IDENTIFIER ‘,’

| var_seq DOUBLE IDENTIFIER ‘,’

| var_seq STRING IDENTIFIER ‘,’

| var_seq BOOLEAN IDENTIFIER ‘,’

func_declarations   | func_declarations func_declaration

func_declaration  INTEGER fun_id_seq_int IDENTIFIER ‘;’

| CHAR fun_id_seq_chr IDENTIFIER ‘;’

| DOUBLE fun_id_seq_dou IDENTIFIER ‘;’

| STRING fun_id_seq_str IDENTIFIER ‘;’

| BOOLEAN fun_id_seq_bol IDENTIFIER ‘;’

| ARRAY_I IDENTIFIER '[' NUMBER_VAL ']' ‘;’

| ARRAY_D IDENTIFIER '[' NUMBER_VAL ']' ‘;’

| ARRAY_C IDENTIFIER '[' NUMBER_VAL ']' ‘;’

| ARRAY_B IDENTIFIER '[' NUMBER_VAL ']' ‘;’

fun_id_seq_int   | fun_id_seq_int IDENTIFIER ‘,’

fun_id_seq_chr   | fun_id_seq_chr IDENTIFIER ‘,’

fun_id_seq_dou   | fun_id_seq_dou IDENTIFIER ‘,’

// Các luật sinh câu lệnh

commands  | commands command ‘;’

command  READ IDENTIFIER read_exp

| READ IDENTIFIER '[' index ']' read_array_exp

| WRITE IDENTIFIER

| WRITE STR_VAL | WRITE exp

| WRITELINE | RETURN exp

| IDENTIFIER ASSGNOP exp

| IDENTIFIER '[' index ']' ASSGNOP exp | WAIT | IF exp THEN commands else_exp ENDIF | WHILE exp DO commands ENDWHILE

|FOR IDENTIFIER ASSGNOP exp TO exp DO commands ENDFOR

read_exp | read_exp ‘,’ IDENTIFIER

read_array_exp | read_array_exp ‘,’ IDENTIFIER

else_exp  | ELSE commands

// Luật sinh biểu thức

exp  NUMBER_VAL | NUMBERD_VAL | STR_VAL | CHR_VAL

| NUMBERB_VAL | IDENTIFIER | IDENTIFIER '(' values ')' | exp '<' exp | exp '=' exp

| exp '!''=' exp | exp '>''=' exp | exp '<''=' exp | exp '>' exp | exp '+' exp | exp '-' exp | exp '*' exp | exp '/' exp | exp '&' exp | exp '|' exp | exp '%' exp | '!' exp

| arr_exp | '(' exp ')'

arr_exp  IDENTIFIER '[' index ']'

values  | exp value_seq

index  NUMBER_VAL | IDENTIFIER

4.3.3. Xử lý ngữ nghĩa cho ngôn ngữ Minipas

Khi bộ phân tích cú pháp nhận dạng ra một luật sinh (khai báo, biểu thức, câu lệnh...) thì nó sẽ gọi đến một chƣơng trình con xử lý ngữ nghĩa tƣơng ứng nằm ở phía bên phải luật sinh đó.

 Xử lý ngữ nghĩa cho khai báo identifier

install( char *symname , char *scope, enum type_code type, int length);

{

symrec *s;

s = getsym (sym_name, scope);

if (s == 0) {s = putsym(sym_name, scope, type, length);} else

{ errors++;

printf( "%s ", sym_name );

yyerror("is already defined");

} }

Hàm install(char *symname, char *scope, enum type_code

type, int length) dùng để kiểm tra cài đặt một ký hiệu có tên là symname,

phạm vi là scope, độ dài là length đã có trong bảng ký hiệu hay chƣa. Nếu chƣa có thì sẽ lƣu ký hiệu này vào trong bảng ký hiệu, ngƣợc lại thì sẽ thơng báo là ký hiệu đã tồn tại trong bảng ký hiệu.

Hàm getsym(sym_name, scope) dùng để truy xuất một ký hiệu có tên là sym_name và phạm vi là scope trong bảng ký hiệu.

Hàm putsym(sym_name, scope, type, length) dùng để lƣu một ký

hiệu có tên là sym_name, phạm vi là scope, kiểu dữ liệu là type và độ dài là length vào trong bảng ký hiệu.

 Xử lý ngữ nghĩa cho việc sinh biểu thức

exp : NUMBER_VAL { gen_code( LD_INT, $1 ); }

| NUMBERD_VAL { gen_code_double( LD_DOU, $1 );}

| STR_VAL { gen_code_string( LD_STR, $1);}

| CHR_VAL { gen_code_char ( LD_CHR, $1);}

| NUMBERB_VAL { gen_code_boolean ( LD_BOL, $1);}

| IDENTIFIER { context_check( LD_VAR, $1,

function_name);}

| exp '<' exp { gen_code( LT, 0 ); }

| exp '=' exp { gen_code( EQ, 0 ); }

| exp '!''=' exp { gen_code( NEQ, 0 );}

| exp '>''=' exp { gen_code( GTEQ, 0 );}

| exp '<''=' exp { gen_code( LTEQ, 0 ); }

| exp '>' exp { gen_code( GT, 0 ); }

| exp '+' exp { gen_code( ADD, 0 ); }

| exp '-' exp { gen_code( SUB, 0 ); }

| exp '*' exp { gen_code( MULT, 0 ); }

| exp '/' exp { gen_code( DIV, 0 ); }

| exp '&' exp { gen_code( AND, 0 ); }

| exp '|' exp { gen_code( OR, 0 ); }

| exp '%' exp { gen_code( MOD, 0 );}

| '!' exp { gen_code( NOT, 0 );}

| '(' exp ')'

Thủ tục gen_code(enum code_ops operation, int arg) dùng để sinh mã trung gian cho mỗi ký hiệu phép toán với tham số thứ nhất là tên của

mã trung gian và tham số thứ hai là giá trị thuộc tính của ký hiệu bên phải của luật sinh.

 Xử lý ngữ nghĩa sinh câu lệnh nhập, xuất dữ liệu

context_check ( enum code_ops operation, char *sym_name, char *scope )

{

symrec *identifier;

identifier = getsym(sym_name, scope); if ( identifier == 0 )

{

identifier = getsym(sym_name, "global");

if ( identifier == 0 )

{

errors++;

printf( "%s", sym_name );

yyerror(" is an undeclared identifier");

}

}

if (identifier != 0) {

if (identifier->type==ARR_D && operation==INT_ARR_STORE)

gen_code_variable( DOU_ARR_STORE, identifier);

else

if (identifier->type==ARR_B && operation==INT_ARR_STORE)

gen_code_variable( BOL_ARR_STORE, identifier);

else

if (identifier->type==ARR_C && operation==INT_ARR_STORE)

gen_code_variable( CHR_ARR_STORE, identifier);

if (identifier->type==ARR_D && operation == READ_INT_ARR)

gen_code_variable( READ_DOU_ARR, identifier);

else

if (identifier->type==ARR_B && operation == READ_INT_ARR)

gen_code_variable( READ_BOL_ARR, identifier);

else

if (identifier->type==ARR_C && operation == READ_INT_ARR)

gen_code_variable( READ_CHR_ARR, identifier);

else

gen_code_variable( operation, identifier);

} }

Hàm context_check(enum code_ops operation, char *sym_name,

char *scope) sẽ thực hiện kiểm tra một ký hiệu có tên là sym_name và phạm vi

là scope đã đƣợc khởi tạo hay chƣa sau đó sinh ra mã trung gian cho ký hiệu đó có tên là operation cho vào trong ngăn xếp chứa mã trung gian thông qua hàm

gen_code_variable(operation, identifier).  Xử lý ngữ nghĩa sinh câu lệnh if ...then....else

command: IF exp

{

if_var = (struct lbs *) newlblrec();

if_var->for_jmp_false = reserve_loc(); } THEN commands { if_var->for_goto = reserve_loc(); } else_exp

ENDIF

{

back_patch( if_var->for_goto, GOTO, gen_label());

}

Trƣớc tiên do sau token IF là một exp vì vậy chƣơng trình sẽ tìm kiếm cấu trúc của exp trƣớc rồi thực hiện hành vi ngữ nghĩa của exp đó (xem phần hành

vi ngữ nghĩa của biểu thức).

Sau khi nhận dạng exp thì sẽ thực hiện hành vi ngữ nghĩa { if_var =

(struct lbs *) newlblrec(); if_var->for_jmp_false =

reserve_loc()}. lbs là một cấu trúc dành riêng cho câu lệnh if, while, for.

Riêng đối với câu lệnh if nó có nhiệm vụ lƣu lại vị trí ngăn xếp mã trung gian sau exp của IF (lưu vào biến for_jmp_false) và vị trí ngăn xếp mã trung gian trƣớc khối lệnh ELSE (lưu vào biến for_goto). Cách dùng lbs trong câu lệnh if nhƣ sau. Nếu giá trị của điều kiện exp là sai thì sẽ nhảy đến vị trí sau token ELSE cịn nếu là đúng thì sau khi thực hiện command trong if thì sẽ nhảy đến vị trí kết thúc câu lệnh if. Muốn thực hiện hành động này ta dùng hàm back_patch ( int addr, enum code_ops operation, int arg ). Hàm này có nhiệm

vụ lƣu lại tại một địa chỉ addr sẽ thực hiện một mã trung gian là operation với tham số để nhảy của mã trung gian là arg. Nhƣ vậy nếu sử dụng hàm

Một phần của tài liệu (LUẬN văn THẠC sĩ) nghiên cứu tổng quan về chương trình dịch luận văn ths toán học 60 46 35 (Trang 35)

Tải bản đầy đủ (PDF)

(60 trang)