การออกแบบคอมไพเลอร์คืออะไร? ประเภท เครื่องมือก่อสร้าง ตัวอย่าง
⚡ สรุปอย่างชาญฉลาด
ขั้นตอนการทำงานของคอมไพเลอร์อธิบายถึงหกขั้นตอนที่แปลงโค้ดต้นฉบับให้เป็นโค้ดเครื่อง ได้แก่ การวิเคราะห์คำศัพท์ การวิเคราะห์ไวยากรณ์ การวิเคราะห์ความหมาย การสร้างโค้ดระดับกลาง การปรับปรุงโค้ดให้เหมาะสม และการสร้างโค้ด โดยได้รับการสนับสนุนจากการจัดการตารางสัญลักษณ์และการจัดการข้อผิดพลาด
การออกแบบคอมไพเลอร์มีขั้นตอนอะไรบ้าง?
A ผู้รวบรวม คอมไพเลอร์ทำงานในหลายขั้นตอน และแต่ละขั้นตอนจะแปลงโปรแกรมต้นฉบับจากรูปแบบหนึ่งไปเป็นอีกรูปแบบหนึ่ง แต่ละขั้นตอนจะรับอินพุตจากขั้นตอนก่อนหน้าและส่งเอาต์พุตไปยังขั้นตอนถัดไปของคอมไพเลอร์ คอมไพเลอร์มีทั้งหมด 6 ขั้นตอน แต่ละขั้นตอนช่วยในการแปลงภาษาโปรแกรมระดับสูงให้เป็นรหัสเครื่อง ขั้นตอนของคอมไพเลอร์มีดังนี้:
- การวิเคราะห์คำศัพท์
- การวิเคราะห์ไวยากรณ์
- การวิเคราะห์เชิงความหมาย
- เครื่องสร้างโค้ดระดับกลาง
- Code เพิ่มประสิทธิภาพ
- Code เครื่องกำเนิดไฟฟ้า
เฟสของคอมไพเลอร์
ขั้นตอนทั้งหมดนี้จะแปลงโค้ดต้นฉบับโดยการแบ่งออกเป็นโทเค็น สร้างแผนผังการวิเคราะห์ และปรับปรุงประสิทธิภาพของโค้ดต้นฉบับผ่านขั้นตอนต่างๆ
ระยะที่ 1: การวิเคราะห์คำศัพท์
การวิเคราะห์คำศัพท์เป็นขั้นตอนแรก เมื่อคอมไพเลอร์สแกนซอร์สโค้ด กระบวนการนี้สามารถดำเนินการจากซ้ายไปขวา ทีละตัวอักษร และจัดกลุ่มตัวอักษรเหล่านั้นเป็นโทเค็น
ในขั้นตอนนี้ กระแสอักขระจากโปรแกรมต้นฉบับจะถูกจัดกลุ่มเป็นลำดับที่มีความหมายโดยการระบุโทเค็น จากนั้นจะทำการบันทึกโทเค็นที่ตรงกันลงในตารางสัญลักษณ์และส่งโทเค็นนั้นไปยังขั้นตอนถัดไป
หน้าที่หลักของเฟสนี้คือ:
- ระบุหน่วยคำศัพท์ในซอร์สโค้ด
- จำแนกหน่วยคำศัพท์ออกเป็นคลาสต่างๆ เช่น ค่าคงที่ คำสงวน และบันทึกไว้ในตารางต่างๆ โดยจะไม่สนใจความคิดเห็นในโปรแกรมต้นฉบับ
- ระบุโทเค็นที่ไม่ใช่ส่วนหนึ่งของภาษา
ตัวอย่าง: x = y + 10
| เหรียญ | ประเภท |
|---|---|
| X | ระบุ |
| = | ตัวดำเนินการมอบหมาย |
| Y | ระบุ |
| + | ตัวดำเนินการเพิ่มเติม |
| 10 | จำนวน |
ขั้นตอนที่ 2: การวิเคราะห์ไวยากรณ์
การวิเคราะห์ไวยากรณ์คือการค้นหาโครงสร้างในโค้ด โดยจะตรวจสอบว่าข้อความนั้นเป็นไปตามรูปแบบที่คาดหวังหรือไม่ เป้าหมายหลักของขั้นตอนนี้คือการตรวจสอบให้แน่ใจว่าโค้ดต้นฉบับที่โปรแกรมเมอร์เขียนนั้นถูกต้องหรือไม่
การวิเคราะห์ไวยากรณ์นั้นอิงตามกฎของภาษาโปรแกรมเฉพาะ โดยการสร้างแผนผังการวิเคราะห์ด้วยความช่วยเหลือของโทเค็น นอกจากนี้ยังกำหนดโครงสร้างของภาษาต้นฉบับและไวยากรณ์หรือโครงสร้างประโยกของภาษานั้นด้วย
ต่อไปนี้คือรายการของงานที่ดำเนินการในขั้นตอนนี้:
- รับโทเค็นจากตัววิเคราะห์คำศัพท์
- ตรวจสอบว่านิพจน์นั้นถูกต้องตามหลักไวยากรณ์หรือไม่
- รายงานข้อผิดพลาดทางไวยากรณ์ทั้งหมด
- สร้างโครงสร้างแบบลำดับชั้นที่เรียกว่า แผนผังการวิเคราะห์ (parse tree)
ตัวอย่าง
ตัวระบุ/ตัวเลขใดๆ ก็ตามถือเป็นนิพจน์ ถ้า x เป็นตัวระบุ และ y+10 เป็นนิพจน์ ดังนั้น x = y+10 จึงเป็นคำสั่ง ลองพิจารณาแผนผังการวิเคราะห์ไวยากรณ์สำหรับตัวอย่างต่อไปนี้:
(a+b)*c
ในแผนผังโครงสร้างข้อมูล (Parse Tree):
- โหนดภายใน: เรคอร์ดที่มีฟิลด์ตัวดำเนินการและฟิลด์ย่อยสองฟิลด์
- Leaf: ระเบียนที่มี 2 ฟิลด์ขึ้นไป โดยฟิลด์หนึ่งสำหรับโทเค็น และอีกฟิลด์หนึ่งสำหรับข้อมูลอื่นๆ เกี่ยวกับโทเค็น
- ตรวจสอบให้แน่ใจว่าส่วนประกอบต่างๆ ของโปรแกรมนั้นสอดคล้องกันอย่างมีความหมาย
- รวบรวมข้อมูลประเภทและตรวจสอบความเข้ากันได้ของประเภท
- ตรวจสอบว่าตัวดำเนินการนั้นได้รับอนุญาตจากภาษาต้นฉบับหรือไม่
ระยะที่ 3: การวิเคราะห์เชิงความหมาย
การวิเคราะห์เชิงความหมายจะตรวจสอบความสอดคล้องเชิงความหมายของโค้ด ใช้แผนผังไวยากรณ์ของเฟสก่อนหน้าพร้อมกับตารางสัญลักษณ์เพื่อตรวจสอบว่าซอร์สโค้ดที่กำหนดมีความสอดคล้องทางความหมาย นอกจากนี้ยังตรวจสอบว่าโค้ดสื่อความหมายที่เหมาะสมหรือไม่
ตัววิเคราะห์ความหมายจะตรวจสอบความไม่ตรงกันของชนิดข้อมูล ตัวดำเนินการที่ไม่เข้ากัน ฟังก์ชันที่เรียกใช้ด้วยอาร์กิวเมนต์ที่ไม่ถูกต้อง ตัวแปรที่ไม่ได้ประกาศ ฯลฯ
หน้าที่ของขั้นตอนการวิเคราะห์ความหมายมีดังนี้:
- ช่วยให้คุณจัดเก็บข้อมูลประเภทที่รวบรวมได้ และบันทึกไว้ในตารางสัญลักษณ์หรือโครงสร้างต้นไม้ทางไวยากรณ์
- อนุญาตให้คุณทำการตรวจสอบประเภทข้อมูล
- ในกรณีที่ประเภทข้อมูลไม่ตรงกัน และไม่มีกฎการแก้ไขประเภทข้อมูลที่แน่ชัดซึ่งสอดคล้องกับการดำเนินการที่ต้องการ จะแสดงข้อผิดพลาดทางความหมาย
- รวบรวมข้อมูลประเภทและตรวจสอบความเข้ากันได้ของประเภท
- ตรวจสอบว่าภาษาต้นทางอนุญาตให้ใช้ตัวดำเนินการเหล่านั้นหรือไม่
ตัวอย่าง
float x = 20.2; float y = x*30;
ในโค้ดข้างต้น ตัววิเคราะห์ความหมายจะแปลงค่าจำนวนเต็ม 30 เป็นค่าทศนิยม 30.0 ก่อนทำการคูณ
ระยะที่ 4: ระดับกลาง Code รุ่น
เมื่อขั้นตอนการวิเคราะห์ความหมายเสร็จสิ้น คอมไพเลอร์จะสร้างโค้ดระดับกลางสำหรับเครื่องเป้าหมาย ซึ่งแสดงถึงโปรแกรมสำหรับเงื่อนไขสัมบูรณ์บางประการtracเครื่องจักร t
รหัสระดับกลางอยู่ระหว่างภาษาโปรแกรมระดับสูงและภาษาโปรแกรมระดับเครื่อง รหัสระดับกลางนี้จำเป็นต้องถูกสร้างขึ้นในลักษณะที่ทำให้สามารถแปลงเป็นรหัสเครื่องเป้าหมายได้อย่างง่ายดาย
หน้าที่ของตัวกลาง Code รุ่น:
- ควรสร้างมันขึ้นมาจากตัวแทนเชิงความหมายของโปรแกรมต้นฉบับ
- เก็บค่าที่คำนวณได้ระหว่างกระบวนการแปล
- ช่วยให้คุณแปลงโค้ดขั้นกลางเป็นภาษาเป้าหมายได้
- ช่วยให้คุณรักษาลำดับความสำคัญของภาษาต้นฉบับไว้ได้
- มันเก็บจำนวนตัวถูกดำเนินการที่ถูกต้องตามคำสั่งนั้น
ตัวอย่าง
ตัวอย่างเช่น:
total = count + rate * 5
รหัสกลางโดยใช้วิธีรหัสที่อยู่คือ:
t1 := int_to_float(5)
t2 := rate * t1
t3 := count + t2
total := t3
เฟส 5: Code การเพิ่มประสิทธิภาพ
ขั้นตอนต่อไปคือการปรับปรุงประสิทธิภาพของโค้ดระดับกลาง ขั้นตอนนี้จะลบโค้ดที่ไม่จำเป็นออกและจัดเรียงลำดับคำสั่งใหม่เพื่อให้โปรแกรมทำงานได้เร็วขึ้นโดยไม่สิ้นเปลืองทรัพยากร เป้าหมายหลักของขั้นตอนนี้คือการปรับปรุงโค้ดระดับกลางเพื่อให้ได้โค้ดที่ทำงานได้เร็วขึ้นและใช้พื้นที่น้อยลง
หน้าที่หลักของเฟสนี้คือ:
- มันช่วยให้คุณกำหนดจุดสมดุลระหว่างความเร็วในการประมวลผลและความเร็วในการคอมไพล์ได้
- ช่วยปรับปรุงระยะเวลาการทำงานของโปรแกรมเป้าหมาย
- สร้างโค้ดที่กระชับขึ้น แต่ยังคงอยู่ในรูปแบบการแสดงผลระดับกลาง
- ลบโค้ดที่ไม่สามารถเข้าถึงได้และกำจัดตัวแปรที่ไม่ใช้งานแล้ว
- ลบคำสั่งที่ไม่เปลี่ยนแปลงออกจากลูป
ตัวอย่าง: พิจารณารหัสต่อไปนี้:
a = intofloat(10)
b = c * a
d = e + b
f = d
สามารถกลายเป็น:
b = c * 10.0
f = e + b
เฟส 6: Code รุ่น
Code การสร้างโค้ด (Generation) เป็นขั้นตอนสุดท้ายของกระบวนการคอมไพเลอร์ โดยจะรับข้อมูลจากขั้นตอนการปรับแต่งโค้ด (Code Optimization) และสร้างโค้ดเพจ (Page Code) หรือโค้ดออบเจ็กต์ (Object Code) ออกมาเป็นผลลัพธ์ วัตถุประสงค์ของขั้นตอนนี้คือการจัดสรรพื้นที่จัดเก็บและสร้างโค้ดเครื่องที่สามารถย้ายตำแหน่งได้ (Relocatable Machine Code)
นอกจากนี้ยังจัดสรรตำแหน่งหน่วยความจำสำหรับตัวแปรต่างๆ ด้วย คำสั่งในโค้ดระดับกลางจะถูกแปลงเป็นคำสั่งเครื่อง ขั้นตอนนี้จะแปลงโค้ดที่ปรับให้เหมาะสมหรือโค้ดระดับกลางให้เป็นภาษาเป้าหมาย
ภาษาเป้าหมายคือรหัสเครื่อง ดังนั้น ตำแหน่งหน่วยความจำและรีจิสเตอร์ทั้งหมดจึงถูกเลือกและจัดสรรในขั้นตอนนี้ รหัสที่สร้างขึ้นในขั้นตอนนี้จะถูกประมวลผลเพื่อรับอินพุตและสร้างเอาต์พุตที่คาดหวัง
ตัวอย่าง
a = b + 60.0 อาจแปลงเป็นค่าในรีจิสเตอร์ได้ดังนี้:
MOVF a, R1 MULF #60.0, R2 ADDF R1, R2
การจัดการตารางสัญลักษณ์
ตารางสัญลักษณ์ประกอบด้วยระเบียนสำหรับตัวระบุแต่ละตัว โดยมีฟิลด์สำหรับคุณลักษณะของตัวระบุ ส่วนประกอบนี้ช่วยให้คอมไพเลอร์ค้นหาระเบียนตัวระบุและเรียกใช้ได้อย่างรวดเร็ว นอกจากนี้ ตารางสัญลักษณ์ยังช่วยในการจัดการขอบเขตอีกด้วย ตารางสัญลักษณ์และตัวจัดการข้อผิดพลาดจะโต้ตอบกับทุกขั้นตอน และตารางสัญลักษณ์จะได้รับการอัปเดตตามไปด้วย
เกิดข้อผิดพลาดในการจัดการกิจวัตร
ในกระบวนการออกแบบคอมไพเลอร์ ข้อผิดพลาดอาจเกิดขึ้นได้ในทุกขั้นตอนดังต่อไปนี้:
- ตัววิเคราะห์คำศัพท์: โทเค็นที่สะกดผิด
- ตัววิเคราะห์ไวยากรณ์: วงเล็บหายไป
- ตัวสร้างรหัสระดับกลาง: ตัวถูกดำเนินการไม่ตรงกันกับตัวดำเนินการ
- Code เครื่องมือเพิ่มประสิทธิภาพ: เมื่อไม่สามารถเข้าถึงข้อความดังกล่าวได้
- Code Generator: เมื่อหน่วยความจำเต็มหรือไม่ได้จัดสรรรีจิสเตอร์ที่เหมาะสม
- ตารางสัญลักษณ์: เกิดข้อผิดพลาดเนื่องจากมีการประกาศตัวระบุหลายรายการ
ข้อผิดพลาดที่พบบ่อยที่สุด ได้แก่ ลำดับอักขระที่ไม่ถูกต้องในการสแกน ลำดับโทเค็นที่ไม่ถูกต้องในประเภท ข้อผิดพลาดเกี่ยวกับขอบเขต และการวิเคราะห์ความหมายที่ไม่ถูกต้อง
อาจพบข้อผิดพลาดได้ในขั้นตอนใดขั้นตอนหนึ่งข้างต้น หลังจากพบข้อผิดพลาดแล้ว ขั้นตอนดังกล่าวจำเป็นต้องจัดการกับข้อผิดพลาดเหล่านั้นเพื่อดำเนินการคอมไพล์ต่อไป ข้อผิดพลาดเหล่านี้จะต้องถูกรายงานไปยังตัวจัดการข้อผิดพลาด ซึ่งจะจัดการกับข้อผิดพลาดเพื่อดำเนินการคอมไพล์ต่อไป โดยทั่วไป ข้อผิดพลาดจะถูกรายงานในรูปแบบของข้อความ



