220 likes | 600 Views
Abstract Syntax Tree และการทำ Type Checking. เฟสของคอมไพเลอร์ที่ผ่านมา. AST. Abstract Syntax Tree เป็นโครงสร้างข้อมูลที่ใช้แทนภาษาโปรแกรมที่จะแปลสู่ภาษาระดับล่าง เฟสของคอมไพเลอร์หลังจากการทำ parsing จะใช้ AST ในการปฏิบัติการหลายๆอย่าง Type checking Optimization ต่างๆ
E N D
เฟสของคอมไพเลอร์ที่ผ่านมาเฟสของคอมไพเลอร์ที่ผ่านมา
AST • Abstract Syntax Tree เป็นโครงสร้างข้อมูลที่ใช้แทนภาษาโปรแกรมที่จะแปลสู่ภาษาระดับล่าง • เฟสของคอมไพเลอร์หลังจากการทำ parsing จะใช้ AST ในการปฏิบัติการหลายๆอย่าง • Type checking • Optimization ต่างๆ • การผลิตโค๊ดระหว่างกลาง (Intermediate Code Generation)
การสร้าง AST • ค่อยๆสร้างแบบ bottom-up • ใส่โค๊ดในการสร้าง node และ link ไว้ที่ semantic action • Semantic action คือสิ่งที่จะถูกดำเนินการขณะที่ non-terminal หรือ terminal ด้านขวาของ CFG ได้รับการประมวลเรียบร้อยแล้ว • นั่นคือเวลาที่ production rule ของ CFG ได้ปฏิบัติการ rule นั้นเสร็จเรียบร้อยแล้ว
หัวใจในการสร้าง AST • ต้อง abstract • ใส่เฉพาะ node ที่จำเป็นใน AST • เราต้องการ tree ที่ abstract ในลักษณะนี้ • Tree ที่ได้จากการ parse expression (1 + 2) + 3 แบบนี้ไม่ abstract เท่าไหร่ • ทั้งนี้เพราะเราใส่ node เข้าไปที่ AST ทุกๆครั้งที่ production rule ที่จะทำการสร้าง expression นี้ ดำเนินการเสร็จ
คอมไพเลอร์ยุคเก่า • รันบนเครื่องคอมพิวเตอร์ที่มีหน่วยความจำหลักน้อย • หน่วยความจำหลักไม่เพียงพอที่จะบรรจุ AST ของทั้งโปรแกรมได้ • จะผลิตโค๊ดและทำการ type checking ที่ semantic action โดยตรงเลย • คอมไพเลอร์ขนาดเล็กและเข้าใจได้ง่าย แต่ • ไม่ถูกต้องตามหลักการวิศวกรรมซอฟแวร์ที่ดี • ปฏิบัติการหลายๆอย่างทำได้ลำบาก • คอมไพเลอร์ CSubset ไม่มีการสร้าง AST
โค๊ดการสร้าง AST: node ของ tree typedefstructA_exp_ *A_exp; structA_exp_ {enum {A_varExp, A_intExp, A_callExp, A_opExp, A_assignExp, A_ifExp, A_whileExp, A_arrayExp} kind; A_pos pos; union {A_varvar; /* nil; - needs only the pos */ intintt; struct {S_symbolfunc; A_expListargs;} call; struct {A_operoper; A_exp left; A_exp right;} op; struct {A_varvar; A_exp exp;} assign; struct {A_exp test, then, elsee;} iff; /* elsee is optional */ struct {A_exp test, body;} whilee; } u; };
โค๊ดการสร้าง AST: ตัวอย่าง prototype • A_expA_OpExp(A_pos pos, A_operoper, A_exp left, A_exp right); • A_expA_AssignExp(A_pos pos, A_varvar, A_exp exp); • A_expA_IfExp(A_pos pos, A_exp test, A_exp then, A_expelsee); • A_expA_WhileExp(A_pos pos, A_exp test, A_exp body);
โค๊ดการสร้าง AST:OpExp • A_expA_OpExp(A_pos pos, A_operoper, A_exp left, A_exp right) { • A_exp p = malloc(sizeof(*p)); • p->kind=A_opExp; • p->pos=pos; • p->u.op.oper=oper; • p->u.op.left=left; • p->u.op.right=right; • return p; • }
โค๊ดการสร้าง AST:AssignExp • A_expA_AssignExp(A_pos pos, A_varvar, A_exp exp) { • A_exp p = malloc(sizeof(*p)); • p->kind=A_assignExp; • p->pos=pos; • p->u.assign.var=var; • p->u.assign.exp=exp; • return p; • }
โค๊ดการสร้าง AST:IfExp • A_expA_IfExp(A_pos pos, A_exp test, A_exp then, A_expelsee) { • A_exp p = malloc(sizeof(*p)); • p->kind=A_ifExp; • p->pos=pos; • p->u.iff.test=test; • p->u.iff.then=then; • p->u.iff.elsee=elsee; • return p; • }
โค๊ดการสร้าง AST:WhileExp • A_expA_WhileExp(A_pos pos, A_exp test, A_exp body) { • A_exp p = malloc(sizeof(*p)); • p->kind=A_whileExp; • p->pos=pos; • p->u.whilee.test=test; • p->u.whilee.body=body; • return p; • }
สร้าง AST ที่ semantic action • จาก production: expr = expr + exprใส่ semantic action (ให้ exprด้านขวาคือ e1 และ e2 ตามลำดับ): {return A_OpExp(A_pos pos, A_operA_plusOp, A_exp e1, A_exp e2);} • จาก production: stmt = if (expr) stmt else stmt ใส่ semantic action (ให้ exprและ stmt ทั้งสองที่อยู่ด้านขวาเป็น e1 s1 และ s2 ตามลำดับ): {return A_IfExp(A_pos pos, A_exp e1, A_exp s1, A_exp s2);}
โค๊ดการสร้าง AST ใน parser • A_expparseStmt() { • A_exp result; • switch (next_token) { • case IF: consume(IF); consume(LPAREN); • A_exp e1 = parseExpr(); • consume(RPAREN); • A_exp s1, s2; • s1 = parseStmt(); • if (next_token == ELSE) { • consume(ELSE); • s2 = parseStmt(); • } • else s2 = EmptyStmt(); • result = A_IfExp(A_pos pos, A_exp e1, A_exp s1, A_exp s2); • break; • case ID: ... • case WHILE: ...
Type Checking • Traverse AST โดยใช้ recursive function • Function ที่ traverse AST จะตรวจสอบ expression ที่จะ type check • หนึ่งใน argument ของ function นี้คือ context หรือ environment ที่ให้ข้อมูลเพิ่มเติมเกี่ยวกับ expression ที่จะ type check • Symbol table เก็บข้อมูล context • Function นี้จะ return ข้อมูลเกี่ยวกับ type ของ expression ที่กำลังพิจารณา
โค๊ดสำหรับการทำ type check ข้อมูลเกี่ยวกับ type และตัวอย่างของ prototype typedefstructTy_ty_ *Ty_ty; structTy_ty_ {enum {Ty_record, Ty_nil, Ty_int, Ty_string, Ty_array, Ty_name, Ty_void} kind; union { Ty_ty array; struct {S_symbol sym; Ty_tyty;} name; } u; }; Ty_tyTy_Int(void); Ty_tyTy_String(void); Ty_tyTy_Void(void); Ty_tyTy_Array(Ty_tyty);
โค๊ดสำหรับการทำ type check ฟังก์ชั่นที่ return ข้อมูลเกี่ยวกับ type ชนิดต่างๆ • static structTy_ty_ tyint = {Ty_int}; • Ty_tyTy_Int(void) {return &tyint;} • static structTy_ty_ tystring = {Ty_string}; • Ty_tyTy_String(void) {return &tystring;} • static structTy_ty_ tyvoid = {Ty_void}; • Ty_tyTy_Void(void) {return &tyvoid;} • Ty_tyTy_Array(Ty_tyty) { • Ty_ty p = malloc(sizeof(*p)); • p->kind=Ty_array; • p->u.array=ty; • return p; • }
Function สำหรับ type check Ty_tytransExp(S_tableenv, A_exp a) { switch(a->kind) { case A_varExp : { return transVar(env, a->u.var); } case A_intExp: { return Ty_Int(); } case A_opExp: { A_operoper = a->u.op.oper; Ty_ty left = transExp(env, a->u.op.left); Ty_ty right = transExp(env, a->u.op.right); if (oper == A_plusOp) { if (left->kind == Ty_int && right->kind == Ty_int) return Ty_Int(); else if … else Print_Error(“Type error”) } else if … break; } …
Type check สำหรับ assignment statement Ty_tytransExp(S_tableenv, A_exp a) { switch(a->kind) { … case A_assignExp: { // Retrieve types for the expression and variable parts of the assignment. Ty_tyassignExp = transExp(env, a->u.assign.exp); Ty_tyassignVar = transVar(env, a->u.assign.var); // Insert code to check if assignExp is type-compatible with assignVar return Ty_Void(); break; } …
Type check สำหรับ if statement Ty_tytransExp(S_tableenv, A_exp a) { switch(a->kind) { … case A_ifExp: { Ty_ty test; Ty_ty then; Ty_tyelsee; // Grab expressions for test, then, and else. test = transExp(env, a->u.iff.test); then = transExp(env, a->u.iff.then); if (a->u.iff.elsee != NULL) elsee = transExp(env, a->u.iff.elsee); else elsee = Ty_Nil(); // Insert code to check if test is type-compatible with boolean // if then and elsee are well-typed, return Void type return Ty_Void(); break; } …
Type check สำหรับ call statement case A_callExp: { E_enventryfunctionID = NULL; A_expListargsList = NULL; Ty_tyList formals; Ty_tyreturnVal; // Verify that function has been defined previously. functionID = S_look(venv, a->u.call.func); if (functionID == NULL) { Print_Error(“No function by that name”); } returnVal = functionID->u.fun.result; if (no argument function) { return ActualTypeOf(returnVal)); } // Check parameters and function call types. argsList = a->u.call.args; formals = functionID->u.fun.formals; while (argsList != NULL && formals != NULL) { Ty_tyfType = formals->head; A_expargExp = argsList->head; Ty_tyargType = transExp(env, argExp); formals = formals->tail; argsList = argsList->tail; // Insert code to check if fType and artType are compatible } return ActualTypeOf(returnVal)); break; } Additional typedef typedefstructTy_tyList_ *Ty_tyList; structTy_tyList_ {Ty_ty head; Ty_tyList tail;}; typedefstructA_expList_ *A_expList; structA_expList_ {A_exp head; A_expList tail;};
ฟังก์ชั่น main ของคอมไพเลอร์ extern A_expabsyn_root; void SEM_transProg(A_exp exp) { Ty_tyexprType; // Create environments. S_tableenv= NULL; env= symTabConstruct(exp); // construct the symbol table exprType = transExp(env, exp); // type-checking } int main(intargc, char **argv) { if (argc!=2) { fprintf(stderr,"usage: parsetest filename\n"); exit(1); } /* Step 1, get tokens from lexical analysis; Step 2, build an AST while parsing */ parseInput(argv[1]); /* Step 3, traverse AST for semantic analysis from the entry point of absyn_root. */ SEM_transProg(absyn_root); return 0; }