Preprocessor คืออะไร? ทำความรู้จักกับโปรแกรมประมวลผลล่วงหน้า
สรุปใจความสำคัญ
- Preprocessor คือโปรแกรมที่เตรียมข้อมูลนำเข้าให้อยู่ในรูปแบบที่เหมาะสมก่อนจะส่งต่อไปยังโปรแกรมอื่น เช่น คอมไพเลอร์
- Lexical Preprocessor ทำงานในระดับข้อความ (Text) โดยเน้นการแทนที่ Token และการรวมไฟล์
- Syntactic Preprocessor ทำงานในระดับโครงสร้างไวยากรณ์ (Syntax Tree) ช่วยให้สามารถสร้างภาษาเฉพาะทาง (DSL) ภายในภาษาหลักได้
ในทางวิทยาการคอมพิวเตอร์ Preprocessor (หรือ Precompiler) คือโปรแกรมที่ทำหน้าที่ประมวลผลข้อมูลนำเข้า (Input) เพื่อสร้างผลลัพธ์ (Output) ซึ่งจะถูกนำไปใช้เป็นข้อมูลนำเข้าสำหรับโปรแกรมอื่นต่อไป โดยทั่วไปแล้ว ผลลัพธ์ที่ได้จะถูกเรียกว่า "รูปแบบที่ผ่านการประมวลผลล่วงหน้า" (Preprocessed form) ซึ่งมักจะถูกส่งต่อไปยังโปรแกรมถัดไป เช่น คอมไพเลอร์ (Compiler)
ระดับและความซับซ้อนของการประมวลผลจะขึ้นอยู่กับประเภทของ Preprocessor บางตัวอาจทำหน้าที่เพียงแค่การแทนที่ข้อความง่ายๆ (Textual substitutions) และการขยายแมโคร (Macro expansions) ในขณะที่บางตัวมีความสามารถสูงเทียบเท่ากับภาษาโปรแกรมเต็มรูปแบบ
ประเภทของ Preprocessor
Preprocessor สามารถแบ่งออกเป็นประเภทหลักๆ ตามระดับของการทำงานได้ดังนี้:
1. Lexical Preprocessors (ตัวประมวลผลเชิงคำศัพท์)
เป็น Preprocessor ระดับต่ำที่สุด เนื่องจากต้องการเพียงการวิเคราะห์เชิงคำศัพท์ (Lexical analysis) โดยจะทำงานกับข้อความต้นฉบับก่อนที่จะมีการ Parse โดยการแทนที่ลำดับตัวอักษรที่ถูก Tokenize ตามกฎที่ผู้ใช้กำหนด
- หน้าที่หลัก: การแทนที่แมโคร (Macro substitution), การรวมไฟล์ (Textual inclusion), และการคอมไพล์แบบมีเงื่อนไข (Conditional compilation)
- ตัวอย่างที่ชัดเจน: C Preprocessor ซึ่งจะประมวลผลบรรทัดที่เริ่มต้นด้วยเครื่องหมาย
#(Directives) โดยที่ C Preprocessor ไม่จำเป็นต้องเข้าใจไวยากรณ์ของภาษา C ในการทำงาน
ในบางภาษาโปรแกรมอาจใช้ฟีเจอร์ภายในแทนการใช้ Lexical Preprocessor เช่น การใช้ Inlining และ Templates แทนแมโคร หรือการใช้ Compile-time imports แทนการรวมไฟล์
2. Syntactic Preprocessors (ตัวประมวลผลเชิงไวยากรณ์)
ถูกนำมาใช้ครั้งแรกในตระกูลภาษา Lisp โดยมีหน้าที่ในการแปลง Syntax Trees ตามกฎที่ผู้ใช้กำหนด ซึ่งอาจเขียนด้วยภาษาเดียวกันกับโปรแกรม (Compile-time reflection) เช่นใน Lisp และ OCaml หรือใช้ภาษาภายนอก เช่น XSLT สำหรับ XML
Syntactic Preprocessors มักถูกนำมาใช้เพื่อ:
- ปรับแต่งไวยากรณ์ (Customizing Syntax): เช่น ในภาษา Objective Caml ที่สามารถเขียนได้ทั้งแบบ "Normal syntax" และ "Revised syntax"
- ขยายความสามารถของภาษา (Extending a Language): เช่น ในตระกูล Lisp ที่ใช้แมโครเพื่อเพิ่มความสามารถในการเขียนโปรแกรมแบบ Imperative หรือ Object-oriented ลงในแกนกลางที่เป็น Functional
- สร้างภาษาเฉพาะทาง (Specializing a Language): การสร้าง Internal DSL (Domain-Specific Language) เช่น แมโคร
LOOPใน Common Lisp ที่สร้างภาษาขนาดเล็กสำหรับจัดการการวนซ้ำที่ซับซ้อน
General Purpose Preprocessor (ตัวประมวลผลล่วงหน้าอเนกประสงค์)
ในขณะที่ Preprocessor ส่วนใหญ่ถูกออกแบบมาเพื่องานเฉพาะทาง (เช่น การคอมไพล์ภาษา C) แต่ก็มี Preprocessor บางประเภทที่เป็นแบบอเนกประสงค์ ซึ่งไม่ได้จำกัดอยู่กับภาษาใดภาษาหนึ่งและสามารถใช้กับงานประมวลผลข้อความที่หลากหลาย
ตัวอย่างที่โดดเด่นที่สุดคือ M4 ซึ่งเป็นที่รู้จักอย่างกว้างขวางในระบบ Build system ข้ามแพลตฟอร์ม เช่น autoconf นอกจากนี้ C Preprocessor บางครั้งก็ถูกนำมาใช้ในงานที่ไม่เกี่ยวข้องกับภาษา C เช่น การประมวลผล JavaScript หรือการจัดการ Device Tree ภายใน Linux Kernel
คำถามที่พบบ่อย
C Preprocessor ทำงานอย่างไร?
C Preprocessor จะมองหาบรรทัดที่เริ่มต้นด้วยเครื่องหมาย # (Directives) และทำการแทนที่ข้อความหรือรวมไฟล์ตามคำสั่งนั้นๆ ก่อนที่โค้ดจะถูกส่งไปยังขั้นตอนการคอมไพล์จริง
ความแตกต่างระหว่าง Lexical และ Syntactic Preprocessor คืออะไร?
Lexical Preprocessor ทำงานกับข้อความดิบๆ (Raw text) โดยการแทนที่คำหรือข้อความ ส่วน Syntactic Preprocessor ทำงานกับโครงสร้างทางไวยากรณ์ (Syntax Tree) ซึ่งมีความซับซ้อนและทรงพลังมากกว่า
M4 คืออะไร?
M4 คือ General Purpose Preprocessor หรือตัวประมวลผลล่วงหน้าอเนกประสงค์ที่สามารถใช้จัดการข้อความได้หลากหลายรูปแบบ ไม่จำกัดเฉพาะภาษาโปรแกรมใดภาษาหนึ่ง
