360 likes | 545 Views
Lex & Yacc. โดย...นายชัชวาลย์ ฮาสุวรรณกิจ. UNIX ได้จัดเครื่องมือสำหรับการพัฒนาตัวภาษา (Language Development Tools) เครื่องมือนี้คือ lex ( LEXical analyzer) yacc (Yet Another Compiler Compiler). Lex เป็นเครื่องมือที่ใช้เพื่อตรวจจับรูปแบบเหมือน (Pattern matching)
E N D
Lex & Yacc โดย...นายชัชวาลย์ ฮาสุวรรณกิจ
UNIXได้จัดเครื่องมือสำหรับการพัฒนาตัวภาษา • (Language Development Tools) • เครื่องมือนี้คือ • lex ( LEXical analyzer) • yacc (Yet Another Compiler Compiler)
Lex • เป็นเครื่องมือที่ใช้เพื่อตรวจจับรูปแบบเหมือน (Pattern matching) • จาก Input ที่อ่านเข้ามาทีละอักขระ ได้เป็น Tokens ต่างๆ • ผลลัพธ์ที่ได้จาก lex เป็นชุดคำสั่งย่อยภาษาซี ชื่อ yylex() • มักเรียกอีกอย่างว่า Scanner • Yacc • เป็นเครื่องมือที่ใช้วิเคราะห์วากยสัมพันธ์ (Syntax Analysis) • ผลลัพธ์ที่ได้จาก yacc เป็นชุดคำสั่งย่อยภาษาซี ชื่อ yyparse() • มักเรียกอีกอย่างว่าParser
Lex มี Input เป็น Lex specifications file และมี Output ออกเป็นภาษา C Lex Specifications fileมีรูปแบบทั่วไปดังนี้ definitions %% lex regular expressions and actions %% User functions
Lex specifications file ตัวอย่าง 1 %% [0-9]+ { printf(“INTEGER\n”); } [-*/+] { printf(“OPERATOR\n”); } ตัวอย่าง 2 %% [0-9]+ { return (INT); } [-*/+] { return (OPR); }
Lex Definitions • INTEGER [0-9]+ • %% • { INTEGER } printf(" Integer encountered\n"); • { INTEGER } \. { INTEGER } { • printf(" Real number encountered\n"); • }
Yacc specifications file มีโครงสร้างที่คล้ายคลึงกับ Lex specifications file ดังนี้ declarations %% yacc rules and actions %% User functions
ตัวอย่าง Yacc specifications program สำหรับ Parser อย่างง่ายข้างต้นเขียนได้ดังนี้
%token INT OPR %start expr %% expt : INT OPR INT { printf("The input expression was syntactically correct\n"); } | error { printf("The input expression was syntactically incorrect\n"); } %%
Main program #include <stdio.h> main( ) { yyparse( ); } #include "y.tab.c" #include "lex.yy.c"
การ Run Parser lex file-name File-name เป็นชื่อของ File ที่มี Lex specifications ในการ Execute คำสั่งจะสร้าง File ที่เรียกว่า lex.yy.c ซึ่งเป็น C program ที่มี function yylex อยู่ yacc file-name File-name เป็นชื่อของ File ที่มี yacc specifications ในการ Execute คำสั่งจะสร้าง File ที่เรียกว่า y.tab.c ซึ่งเป็น C program ที่มี function yyparse
การ Run Parser • สร้าง a.out parser ด้วยการใช้คำสั่งดังข้างล่างนี้ • cc main.c -ly • การ Compile จะใช้ Standard CC command และ • เราสามารถเรียกใช้ Yacc library ได้โดยการใช้ -ly option • เราสามารถเรียกใช้Lex library โดยการใช้ -ll option • ในคำสั่ง CC command line • แต่ไม่จำเป็นเสมอไปทุกครั้งที่จะต้องใช้ Yacc และ Lex libraries • -ly ใช้ Yacc library เพราะว่ามีการใช้ Error option
Context Sensitivity • Lex ได้มีกลไกที่เป็นเงื่อนไขในการทำงาน (Start condition) สำหรับ กรณี Left context sensitivity ไว้ให้ • Left context sensitivity บอกว่า ความหมายของ Token หนึ่งจะขึ้นอยู่กับ Token ที่อยู่ก่อนหน้ามัน • หลักการของการใช้ start condition คือเพื่อ Set และ Unset ตัว Token นั้น เมื่อมีเหตุการณ์เกิดขึ้น • Start conditionsใน Lex กำหนดได้ตามรูปแบบนี้ • %start start1 start2 start3 …
โดยที่ start1, start2 และ start3 เป็นชื่อของ Start conditions • การ Set start condition ใช้ Statement ดังนี้ • BIGIN start1 • โดยที่ start1 เป็นชื่อของ Start condition • การ Unset start conditionต่างๆ ใช้ Statement ดังนี้ • BEGIN 0; • การ Match สตริงหนึ่งกับ Regular expression ที่มี Start condition • เขียนได้ดังนี้ • < start1 > reg_expr { actions }
ตัวอย่างการใช้ Start Condition แปลงตัวเลขจำนวนนับสองหลักไปเป็นการแสดงด้วยตัวหนังสือ โดยให้ input ขึ้นต้นด้วย $ (Dollar sign) แล้วตามหลังด้วยตัวเลขสองตัว ตัวอย่างเช่นถ้า Input เป็น $23 จะได้ output เป็น Twenty three dollars เราสามารถเขียน Lex specifications file สำหรับโปรแกรมนี้ดังดังนี้
%start TT ZZ DD TN %% \ $ { BEGIN TT; } < TT > [ 0-9 ] { tenth = yytext[0]; BEGIN 0; Switch (yytext[0]) { case '0' : BEGIN ZZ; break; case '1' : BEGIN TN; break; default : BEGIN DD; break; } }
< ZZ > [ 0-9 ] { BEGIN 0; Switch (yytext[0]) { case '0' : printf("Zero dollars"); break; case '1' : printf("One dollars"); break; case '2' : printf("Two dollars"); break; case '3' : printf("Three dollars"); break; case '4' : printf("Four dollars"); break; case '5' : printf("Five dollars"); break; case '6' : printf("Six dollars"); break; case '7' : printf("Seven dollars"); break; case '8' : printf("Eight dollars"); break; case '9' : printf("Nine dollars"); break; } }
< TN > [ 0-9] { BEGIN 0; Switch (yytext[0]) { case '0' : printf("Ten dollars"); break; case '1' : printf("Eleven dollars"); break; case '2' : printf("Twelve dollars"); break; case '3' : printf("Thirteen dollars"); break; case '4' : printf("Fourteen dollars"); break; case '5' : printf("Fifteen dollars"); break; case '6' : printf("Sixteen dollars"); break; case '7' : printf("Seventeen dollars"); break; case '8' : printf("Eighteen dollars"); break; case '9' : printf("Nineteen dollars"); break; } }
< DD > [ 0-9 ] { BEGIN 0; Switch (tenth) { case '2' : printf("Twenty "); break; case '3' : printf("Thirty "); break; case '4' : printf("Forty "); break; case '5' : printf("Fifty "); break; case '6' : printf("Sixty "); break; case '7' : printf("Seventy "); break; case '8' : printf("Eighty "); break; case '9' : printf("Ninety "); break; }
Switch (yytext[0]) { case '0' : printf("dollars"); break; case '1' : printf("One dollars"); break; case '2' : printf("Two dollars"); break; case '3' : printf("Three dollars"); break; case '4' : printf("Four dollars"); break; case '5' : printf("Five dollars"); break; case '6' : printf("Six dollars"); break; case '7' : printf("Seven dollars"); break; case '8' : printf("Eight dollars"); break; case '9' : printf("Nine dollars"); break; } }
main function โปรแกรมภาษา C เขียนได้ดังข้างล่างนี้ #include < stdio.h> char tenth; main( ) { while (1) yylex( ); } yywrap( ) { exit (0); } #include " lex.yy.c "
Predefined Pseudovariables • ค่าที่ส่งกลับมาสำหรับแต่ละ Token โดย Lexical analyzer • สามารถจะเข้าถึงและนำไปใช้งานได้ภายในกฎของ Yacc • Precefined pseudovariables ถูกใช้ในการเข้าถึงค่าต่างๆของ Token • ใช้สัญลักษณ์เริ่มต้นด้วย $ (Dollar Sign) แล้วตามด้วยตัวเลข (Integer) • โดยที่เลข Integer นี้จะอ้างถึงตำแหน่งของ Terminal symbol • ภายใน Rule และ $$ จะอ้างอิงถึง Left-hand nontermal symbol • ในกฎดังนี้ • result : VAR1 '+' VAR2 '+' VAR3 • { $$ = $1 + $3+ $5; };
$1ถูกกำหนดค่าให้โดย VAR1, $2จะถูกกำหนดค่าให้โดย Terminal symbol +, และต่อไปตามลำดับ...จนถึง $5จะถูกกำหนดค่าโดย VAR3 โดยผลของการคำนวณจะส่งไปให้กับ Nonterminal result Predefined pseudovariablesจะมีประโยชน์โดยเฉพาะ ใช้ในการวิเคราะห์การกระจาย (Parsing) และ การหาค่าจากรูปประโยคทางคณิตศาสตร์ (Arithmetic expressions)
มาทำโปรแกรม Calculator กันเถอะ
Lex specifications file > cat cal.l %{ #include "y.tab.h" extern char yytext[]; extern int yylval; %} %% [0-9]+ { yylval = atoi(yytext); return INTEGER; } (ต่อ)
(ต่อ) [-+\n] return *yytext; [\t] ; . { exit(0); } %% yywrap() { return 1; }
Yacc specifications file • > cat cal.y | more • %token INTEGER • %% • program:program expr '\n' {printf("%d\n",$2);} • | • ; • expr: • INTEGER {$$ = $1;} • |expr '+' expr {$$ = $1 + $3 ;} • |expr '-' expr {$$ = $1 - $3 ;} • ; (ต่อ)
(ต่อ) %% yyerror() { exit(0); } main(){ yyparse(); return 0; }
Compile และRun โปรแกรม • >yacc –d cal.y • > lex cal.l • > cc lex.yy.c y.tab.c -o cal • > ./cal • 3-1/* input */ • 2/* ผลลัพธ์ */ • 4-2+1 • 1/* ผลลัพธ์ */ • 5+3+2-1 • 9/* ผลลัพธ์ */ • ./* exit */ • >
REFERENCE UNIX UTILITIES, International Edition, R.S. Tare, McGraw-Hill, 1988 “A Compact Guide to LEX and YACC” http://epaperpress.com/y_man.html, Access date Feb,4 2001