Bảng các token của ngôn ngữ Minipas

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 39)

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

back_patch trong câu lệnh if ta sẽ sử dụng nhƣ sau:

back_patch( if_var->for_jmp_false,JMP_FALSE,gen_label());

Tại địa chỉ ngăn xếp mã trung gian for_jmp_false sẽ thực hiện mã trung gian

thực hiện khi exp trả về giá trị 0. Vì vậy khi giá trị của điều kiện exp là sai (có

giá trị 0) thì sẽ thực hiện nhảy về vị trí ngăn xếp mã trung gian sau token ELSE.

back_patch( if_var->for_goto, GOTO, gen_label() ); Tại địa

chỉ ngăn xếp mã trung gian for_goto sẽ thực hiện mã trung gian là GOTO và

nhảy đến vị trí ngăn xếp là gen_label(), ở đây vị trí ngăn xếp gen_label() là vị trí sau token ENDIF. Vì vậy trƣờng hợp giá trị của điều kiện exp là đúng thì sẽ khơng thực hiện mã trung gian JMP_FALSE và tiếp tục thực hiện commands sau

token THEN. Sau khi thực hiện commands sau token THEN thì tại vị trí đấy là địa

chỉ ngăn xếp mã trung gian for_goto, nên sẽ thực hiện mã trung gian GOTO, thực hiện nhảy đến vị trí sau token ENDIF.

 Xử lý ngữ nghĩa sinh câu lệnh while...do

command: WHILE { $1 = (struct lbs *) newlblrec(); $1->for_goto = gen_label(); } exp { $1->for_jmp_false = reserve_loc(); } DO commands ENDWHILE { gen_code(GOTO, $1->for_goto ); back_patch($1->for_jmp_false,JMP_FALSE, gen_label()); }

Khi nhận ra cấu trúc WHILE thì chƣơng trình sẽ thực hiện hành vi ngữ

nghĩa sau: { $1 = (struct lbs *) newlblrec(); $1->for_goto =

gen_label(); }. Tức là tạo ra một cấu trúc có tên là $1. Lƣu lại vị trí trong

ngăn xếp mã trung gian vào biến $1->for_goto để thực hiện nhảy cho vòng lặp

WHILE trong trƣờng hợp điều kiện là đúng.

Tiếp đến là thực hiện hành vi ngữ nghĩa của biểu thức exp. Sau khi thực hiện hành vi ngữ nghĩa của biểu thức exp thì chƣơng trình sẽ thực hiện tiếp

hành vi ngữ nghĩa: { $1->for_jmp_false = reserve_loc(); }. để lƣu địa chỉ ngăn xếp mã trung gian vào biến $1->for_jmp_false.

Tiếp đến chƣơng trình sẽ thực hiện hành vi ngữ nghĩa cho các câu lệnh trong commands nằm sau token DO.

Kết thúc của câu lệnh WHILE thì chƣơng trình sẽ thực hiện hành vi ngữ

nghĩa sinh mã trung gian GOTO với tham số $1->for_goto và mã trung gian

JMP_FALSE cho địa chỉ ngăn xếp mã trung gian $1->for_jmp_false. Với ý

nghĩa: khi gặp mã trung gian GOTO thì sẽ nhảy trở về địa chỉ là $1->for_goto.

Khi gặp mã trung gian JMP_FALSE tại địa chỉ ngăn xếp mã trung gian $1-

>for_jmp_false sẽ kiểm tra giá trị của exp có bằng 0 hay khơng. Nếu bằng 0

thì nhảy về địa chỉ ngăn xếp mã trung gian là nơi kết thúc lệnh WHILE.

 Xử lý ngữ nghĩa sinh câu lệnh for...to...do

command: FOR IDENTIFIER ASSGNOP exp

{

context_check( STORE, $2, function_name );

$1 = (struct lbs *) newlblrec(); $1->for_goto = gen_label();

context_check( LD_VAR, $2, function_name ); } TO exp { gen_code( LTEQ, 0 ); $1->for_jmp_false = reserve_loc(); } DO commands { context_check(LD_VAR, $2, function_name); gen_code( LD_INT, 1 ); gen_code( ADD, 0 );

context_check( STORE, $2, function_name);

}

ENDFOR

{

gen_code( GOTO, $1->for_goto );

back_patch($1->for_jmp_false,JMP_FALSE,gen_label());

}

Về cơ bản nguyên lý của câu lệnh FOR giống với câu lệnh WHILE. Tuy

nhiên điểm khác quan trọng khi chƣơng trình thực hiện sinh mã trung gian là câu lệnh WHILE bản thân trong nó đã chứa đựng điều kiện của biểu thức exp để thực hiện lặp cho câu lệnh này, còn câu lệnh FOR thì phải sinh điều kiện thực hiện lặp cho nó và phải thực hiện việc tăng biến chạy thêm một giá trị sau mỗi vòng lặp.

 Xử lý ngữ nghĩa sinh hàm

func_decls :  | func_decls func_decl ; func_decl: FUNCTION IDENTIFIER

if (function_name != NULL) free(function_name); function_name = (char *) malloc (strlen($2)+1); function_name = $2;

install( function_name , function_name, FUN, gen_label()); install( function_name , "global", FUN, gen_label()); } '(' func_var_decls ')'

var

func_declarations BEGIN

{

gen_code( DATA, data_location() - 1 ); }

commands ENDFUNCTION ';' {

if (function_name != NULL) free(function_name); function_name = "main";

gen_code(END_CAL, 0); }

Khi xử lý với hàm trong Minipas, sẽ chia thành các bƣớc cơ bản sau: + Nhận biết khai báo hàm

+ Nhận biết gọi hàm

Ta sẽ tập trung vào việc gọi hàm. Cách nhận biết gọi hàm trong chƣơng trình nhƣ sau:

Chƣơng trình sẽ duyệt trong tệp parser.y để tìm ra cấu trúc gọi hàm thì sẽ thực hiện hành vi ngữ nghĩa của cấu trúc này nhƣ sau:

'('values')' {context_check(CAL, $1, "global");}

Trong trƣờng hợp nhận biết đƣợc một câu lệnh gọi hàm thì sẽ sinh ra một mã trung gian là bắt đầu gọi hàm để đánh dấu từ điểm bắt đầu gọi hàm đến mã trung gian gọi hàm là danh sách các giá trị truyền vào cho hàm đó. Ở hành vi ngữ nghĩa trên thì thể hiện của nó nhƣ sau:

{gen_code(BEGIN_CAL,0);}: sinh mã trung gian bắt đầu gọi hàm

'(' values ')': xử lý sinh mã trung gian cho các giá trị truyền vào hàm

context_check(CAL, $1, "global");: Kiểm tra hàm đã đƣợc khai báo

KẾT LUẬN

Sau quá trình nghiên cứu, luận văn đã đạt đƣợc những kết quả nhƣ sau: Tìm hiểu đƣợc tổng quan về chƣơng trình dịch gồm các bƣớc để thiết kế một chƣơng trình dịch. Đi sâu vào tìm hiểu ba bƣớc chính là phân tích từ vựng, phân tích cú pháp và phân tích ngữ nghĩa.

Tìm hiểu về hai thuật tốn để phân tích cú pháp là thuật toán Earley và

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 39)

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

(60 trang)