เจาะลึกกลไกเบื้องหลังคอมไพเลอร์และอินเทอร์พรีเตอร์ แปลงซอร์สโค้ดสู่การทำงานจริง
ทำความเข้าใจกระบวนการทำงานของภาษาโปรแกรมตั้งแต่ซอร์สโค้ด ตัวอักษร โทเค็น ไปจนถึงโครงสร้างต้นไม้ AST โดยไม่ต้องพึ่งพาทฤษฎีซับซ้อน

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- คอมไพเลอร์และอินเทอร์พรีเตอร์ไม่ได้ทำงานเป็นโปรแกรมขนาดยักษ์ชิ้นเดียว แต่แบ่งเป็นขั้นตอนย่อย
- เลกเซอร์ (Lexer) ทำหน้าที่อ่านซอร์สโค้ดและตัดแบ่งเป็นชิ้นส่วนความหมายเรียกว่าโทเค็น
- พาร์เซอร์ (Parser) สร้างโครงสร้าง AST เพื่อกำหนดลำดับความสำคัญของเครื่องหมายและนิพจน์
- อินเทอร์พรีเตอร์ประเมินค่า AST โดยตรง ขณะที่คอมไพเลอร์แปลง AST เป็นชุดคำสั่ง
เวลาที่เราเขียนโค้ดสั้นๆ อย่างคำสั่งบวกเลขแล้วสั่งพิมพ์ผลลัพธ์ผ่านหน้าจอ ชวนให้รู้สึกว่าคอมพิวเตอร์น่าจะแค่อ่าน ทำความเข้าใจ แล้วก็รันโค้ดนั้นได้ทันที แต่ในความเป็นจริงแล้วคอมพิวเตอร์ไม่ได้เข้าใจซอร์สโค้ดในแบบที่เรามองเห็นตามธรรมชาติ
การทำงานของภาษาโปรแกรมจำเป็นต้องนำซอร์สโค้ดนั้นมาค่อยๆ แปลงสภาพทีละขั้นจนกลายเป็นรูปแบบที่ระบบสามารถนำไปใช้งานต่อได้ กระบวนการนี้ถือเป็นหนึ่งในส่วนที่น่าสนใจที่สุดเบื้องหลังการทำงานของภาษาโปรแกรม โดยที่เราไม่จำเป็นต้องมีความรู้ระดับสูงด้านทฤษฎีคอมไพเลอร์ก็สามารถทำความเข้าใจได้
บริบทเพิ่มเติม: การมองระบบประมวลผลภาษาโปรแกรมในลักษณะท่อส่งข้อมูล (Pipeline) ช่วยให้การพัฒนาซอฟต์แวร์มีความยืดหยุ่นสูง หากแต่ละขั้นตอนทำงานแยกจากกันอย่างเด็ดขาด นักพัฒนาสามารถปรับปรุงตัวแปลภาษาหรือเพิ่มฟีเจอร์ใหม่ในขั้นตอนหนึ่งได้โดยไม่ต้องรื้อระบบทั้งหมด
จุดเริ่มต้นที่มีประโยชน์คือการเลิกมองว่าคอมไพเลอร์หรืออินเทอร์พรีเตอร์เป็นโปรแกรมมหึมาโปรแกรมเดียว แต่ให้มองภาพขั้นตอนการทำงานตามลำดับดังนี้:
- ซอร์สโค้ด (Source Code)
- เลกเซอร์ (Lexer)
- โทเค็น (Tokens)
- พาร์เซอร์ (Parser)
- โครงสร้าง AST
- อินเทอร์พรีเตอร์ หรือ คอมไพเลอร์
แต่ละขั้นตอนจะรับข้อมูลเข้ามาทำการแปลงและส่งผลลัพธ์ต่อไปยังขั้นตอนถัดไป เมื่อเราสร้างโมเดลความคิดนี้ขึ้นมาได้ คอมไพเลอร์และอินเทอร์พรีเตอร์ก็จะไม่ใช่เรื่องลึกลับอีกต่อไป ลองมาติดตามโปรแกรมตัวอย่างขนาดจิ๋วตลอดบทความนี้:
let x = 5 + 3
print(x)
ในมุมมองของมนุษย์เราสามารถมองเห็นและเข้าใจความหมายได้ทันที แต่ระบบของภาษาโปรแกรมไม่สามารถอาศัยสัญชาตญาณแบบนั้นได้ จำเป็นต้องมีกระบวนการประมวลผลซอร์สโค้ดอย่างเป็นระบบระเบียบ

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
ตัวเลกเซอร์ (Lexer) หรือที่บางครั้งเรียกว่าโทเค็นไนเซอร์ (Tokenizer) มีหน้าที่อ่านซอร์สโค้ดแล้วแตกย่อยออกเป็นชิ้นส่วนที่มีความหมายเรียกว่าโทเค็น เช่นตัวอักษรเฉพาะอย่างตัวเลขและเครื่องหมายคำนวณ เลกเซอร์จะแปลงข้อมูลเหล่านี้ให้มีประโยชน์มากขึ้น เช่น แปลงตัวเลขและเครื่องหมายบวกให้ระบุประเภทได้อย่างชัดเจน
สิ่งสำคัญคือเลกเซอร์สนใจเรื่องการจดจำแพทเทิร์นเป็นหลัก เช่น คำสั่งคือคีย์เวิร์ด ตัวอักษรคือตัวระบุตัวตน ตัวเลขคือจำนวนเต็ม และเครื่องหมายคือโอเปอเรเตอร์ โดยมันไม่ต้องทำความเข้าใจความหมายทั้งหมดของโปรแกรม และมักจะข้ามช่องว่าง (Whitespace) ไป ทำให้ขั้นตอนถัดไปได้รับข้อมูลที่สะอาดกว่าเดิม
แม้จะได้โทเค็นมาแล้วแต่ก็ยังเป็นเพียงลำดับข้อมูลแบบแบนๆ เท่านั้น คำถามสำคัญคือโครงสร้างของนิพจน์นั้นคืออะไรกันแน่ระหว่างการบวกก่อนคูณหรือคูณก่อนบวก ซึ่งพาร์เซอร์ (Parser) จะทำหน้าที่เข้ามาตอบคำถามนี้โดยการสร้างโครงสร้างที่เรียกว่า Abstract Syntax Tree หรือ AST ขึ้นมา
เมื่อได้ AST มาแล้ว เราสามารถเดินหน้าไปได้หลายทิศทาง โดยอินเทอร์พรีเตอร์สามารถประเมินค่า AST ได้โดยตรงและให้ผลลัพธ์ออกมาทันที ในขณะที่คอมไพเลอร์สามารถแปลง AST ให้กลายเป็นอีกรูปแบบหนึ่งอย่างชุดคำสั่ง (Instructions) ได้เช่นกัน
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น