รู้จัก lex & yacc ไหมครับ ?
หนึ่งในเรื่องที่คนเรียนคอมพ์หลายคนขยาด
แต่เป็นหนึ่งในเรื่องที่ผมชอบมากที่สุดตอนเรียน
เป็นเรื่องที่ต้องคิดวิเคราะห์ ผสมจินตนาการ
ว่าจะสร้างสรรค์ให้คอมพิวเตอร์ตีความข้อความคำสั่งต่าง ๆ ให้ถูกต้องได้อย่างไร
เสียดายที่เวลาจำกัดทำให้อาจารย์สามารถสอนได้แค่ lex
ส่วน yacc ต้องไปศึกษาต่อเอง (ซึ่งแน่นอนว่าไม่ได้ทำ)
สรุปว่า เรียนจบมาโดยที่ยังไม่รู้ว่าจะนำ lex & yacc ไปใช้ประโยชน์อย่างไร
จนกระทั่งเมื่อสัปดาห์ที่แล้ว ผมตัดสินใจหยิบเอา lex & yacc มาใช้
ทั้ง ๆ ที่ไม่มีความจำเป็นเท่าไหร่ เขียนเองแบบง่าย ๆ ก็ได้
แต่อยากใช้เครื่องมือทรงพลังตัวนี้ให้เป็นมากกว่า
และตัดปัญหาเรื่องบั๊ก(ในกรณีเขียนเอง)ไปในตัว
แน่นอนว่าต้องเริ่มรื้อฟื้นศึกษากันใหม่พอสมควร
lex
ส่วน lex ตอนแรกก็ไม่มีปัญหาอะไรมาก
ตัด ๆ ข้อความออกเป็น token แล้วส่งให้ yacc ไปตรวจไวยากรณ์และตีความต่อ
ปัญหาเกิดขึ้นเมื่อต้องการให้ใส่ "ข้อความ" ได้
กำหนดไวยากรณ์ไว้แบบนี้
ดูเหมือนจะง่าย ๆ แต่ถ้าต้องการให้ข้อความสามารถใช้เครื่องหมายวงเล็บได้ล่ะ ?
เช่น ERRORMSG( ผิดพลาด(255) ) เป็นต้น
งานนี้ก็เลยต้องเพิ่มตัวนับวงเล็บ ว่าตอนนี้มีวงเล็บกี่ชั้นแล้ว (บังคับว่ามีวงเปิด-ปิดคู่กันเสมอ)
อันนี้ต้องลองผิดลองถูกอยู่เป็นชั่วโมง กว่าจะทำได้ถูกต้องตามต้องการ
โดยตรงส่วนนี้ต้องประสานกับโค้ดใน yacc ด้วย
(ถ้าใครอยากรู้จะนำมาลงให้ศึกษาในภายหลัง)
yacc
ตัวยากคือเจ้านี่แหละครับ
yacc มีหน้าที่ตรวจสอบว่า token ต่าง ๆ ที่รับมาจาก lex นั้นเรียงลำดับถูกต้องตามที่ควรจะเป็น
โดยเราจะต้องเขียน "กฎไวยากรณ์" ให้ครบในทุก ๆ กรณี
ถึงแม้จะยาก แต่ก็เป็นส่วนที่สนุกที่สุดแล้วล่ะ
กฎที่เขียนออกมาตอนแรกเจอ conflict มากมาย
ที่ทำให้ข้อความเดียวกันสามารถตีความได้หลายแบบ (ambiguous)
อันนี้ต้องลองผิดลองถูกล้วน ๆ เพราะเพิ่งจับ yacc เป็นครั้งแรก
ผลลัพธ์สุดท้ายเมื่อแก้ conflict ได้หมด ก็เป็นไปตามที่หนังสือว่าไว้
จริง ๆ หลังจากปรับแก้ conflict ได้หมดแล้ว ตัวภาษายังเหมือนเดิมไม่เปลี่ยนแปลงครับ
แต่สิ่งที่เปลี่ยนไปคือ กฎไวยากรณ์ที่อ่านเข้าใจง่ายขึ้นอีกจมเลยทีเดียว
สำหรับใครที่คิดจะใช้ yacc แนะนำว่า ต้องศึกษากลไกการทำงานของ yacc เสียก่อน
ว่ามีวิธีการ shift & reduce อย่างไร คุณจะใช้ประโยชน์จาก yacc ได้หรือไม่ก็อยู่ตรงนี้แหละ
flex = lex และ bison = yacc
lex กับ yacc ต้นฉบับมันเก่าแล้ว ก็มีคนพัฒนาตัวใหม่ ๆ ออกมาหลายตัวด้วยกัน
ตัวที่ผมใช้คือเจ้าสองตัวนี้
เป็นโอเพนซอร์ส นำมาคอมไพล์และติดตั้งบนลินุกซ์ได้โดยไม่มีปัญหา
bison กับภาษา C++
อันนี้เป็นคำเตือนสำหรับผู้ที่จะใช้ flex & bison กับโปรแกรมที่พัฒนาด้วยภาษา C++
ตอนแรกผมใช้ภาษา C ในการศึกษาวิธีการใช้งาน flex & bison
และพัฒนากฎขึ้นมาให้ทำงานตามต้องการ
แต่โปรแกรมที่จะนำไปใช้จริง ๆ มันเป็นภาษา C++ ครับ
โดยปกติโปรแกรมภาษา C สามารถนำไปใช้งาน
กับคอมไพเลอร์ภาษา C++ ได้อย่างไม่มีปัญหา
แต่ไม่ใช่สำหรับเจ้า flex & bison
พอเปลี่ยนคอมไพเลอร์จาก gcc ไปเป็น g++
ก็พบปัญหาว่า ลิงก์ไม่ผ่าน เนื่องจากฟังก์ชัน yylex() หายไป
ค้นข้อมูลและอ่านซอร์สโค้ดที่ flex & bison สร้างขึ้นมาให้โดยละเอียด
ก็พบว่า ตัว bison เองมีโหมดสำหรับภาษา C++ โดยเฉพาะ
ซึ่งในโหมดภาษา C++ นี้ ตัว bison จะสร้างโค้ดของ yacc ขึ้นมาให้เป็นคลาส (class) ครับ
ประเด็นก็คือ เมื่อตัว yacc เปลี่ยนเป็นคลาส ตัว yylex() จึงถูกย้ายไปอยู่ในคลาสด้วย
และมันถูกลบออกไปจากไลบรารีภาษา C++ ของ bison
แล้วเพิ่มโค้ด yylex() ไว้ในโค้ดที่ bison สร้างขึ้นมาให้แทน
นั่นหมายความว่า ถ้าต้องการใช้ bison กับ g++
คุณจะต้องใช้ yacc ผ่านคลาสที่ bison สร้างขึ้นมาให้เท่านั้นครับ
ไม่มีทางเลือกอื่น นอกจากจะขยันเขียน yylex() ขึ้นมาเอง
ตอนนี้งานก็เลยยังไม่เสร็จซะที
ต้องมาศึกษาเพิ่มว่า คลาส yacc ตัวนี้ใช้งานยังไง
หวังว่าจะเสร็จได้ทันพรุ่งนี้นะ....
เอาน่า ศึกษาไว้ทีเดียวคุ้ม
เริ่มใช้เป็นแล้วก็สามารถเอาไปประยุกต์ใช้ได้อีกหลายอย่างเลย ^^
อัพเดต
กว่าจะทำให้ใช้กับ C++ ได้ ก็หกโมงเย็นของวันที่ 13 พอดี
พรุ่งนี้ต้องนำเข้าไปรวมกับโปรแกรมจริง เสียเวลาเพิ่มอีก 1 วัน T_T
หนึ่งในเรื่องที่คนเรียนคอมพ์หลายคนขยาด
แต่เป็นหนึ่งในเรื่องที่ผมชอบมากที่สุดตอนเรียน
เป็นเรื่องที่ต้องคิดวิเคราะห์ ผสมจินตนาการ
ว่าจะสร้างสรรค์ให้คอมพิวเตอร์ตีความข้อความคำสั่งต่าง ๆ ให้ถูกต้องได้อย่างไร
เสียดายที่เวลาจำกัดทำให้อาจารย์สามารถสอนได้แค่ lex
ส่วน yacc ต้องไปศึกษาต่อเอง (ซึ่งแน่นอนว่าไม่ได้ทำ)
สรุปว่า เรียนจบมาโดยที่ยังไม่รู้ว่าจะนำ lex & yacc ไปใช้ประโยชน์อย่างไร
จนกระทั่งเมื่อสัปดาห์ที่แล้ว ผมตัดสินใจหยิบเอา lex & yacc มาใช้
ทั้ง ๆ ที่ไม่มีความจำเป็นเท่าไหร่ เขียนเองแบบง่าย ๆ ก็ได้
แต่อยากใช้เครื่องมือทรงพลังตัวนี้ให้เป็นมากกว่า
และตัดปัญหาเรื่องบั๊ก(ในกรณีเขียนเอง)ไปในตัว
แน่นอนว่าต้องเริ่มรื้อฟื้นศึกษากันใหม่พอสมควร
lex
ส่วน lex ตอนแรกก็ไม่มีปัญหาอะไรมาก
ตัด ๆ ข้อความออกเป็น token แล้วส่งให้ yacc ไปตรวจไวยากรณ์และตีความต่อ
ปัญหาเกิดขึ้นเมื่อต้องการให้ใส่ "ข้อความ" ได้
กำหนดไวยากรณ์ไว้แบบนี้
ERRORMSG( ข้อความ )
ดูเหมือนจะง่าย ๆ แต่ถ้าต้องการให้ข้อความสามารถใช้เครื่องหมายวงเล็บได้ล่ะ ?
เช่น ERRORMSG( ผิดพลาด(255) ) เป็นต้น
งานนี้ก็เลยต้องเพิ่มตัวนับวงเล็บ ว่าตอนนี้มีวงเล็บกี่ชั้นแล้ว (บังคับว่ามีวงเปิด-ปิดคู่กันเสมอ)
อันนี้ต้องลองผิดลองถูกอยู่เป็นชั่วโมง กว่าจะทำได้ถูกต้องตามต้องการ
โดยตรงส่วนนี้ต้องประสานกับโค้ดใน yacc ด้วย
(ถ้าใครอยากรู้จะนำมาลงให้ศึกษาในภายหลัง)
yacc
ตัวยากคือเจ้านี่แหละครับ
yacc มีหน้าที่ตรวจสอบว่า token ต่าง ๆ ที่รับมาจาก lex นั้นเรียงลำดับถูกต้องตามที่ควรจะเป็น
โดยเราจะต้องเขียน "กฎไวยากรณ์" ให้ครบในทุก ๆ กรณี
ถึงแม้จะยาก แต่ก็เป็นส่วนที่สนุกที่สุดแล้วล่ะ
กฎที่เขียนออกมาตอนแรกเจอ conflict มากมาย
ที่ทำให้ข้อความเดียวกันสามารถตีความได้หลายแบบ (ambiguous)
อันนี้ต้องลองผิดลองถูกล้วน ๆ เพราะเพิ่งจับ yacc เป็นครั้งแรก
ผลลัพธ์สุดท้ายเมื่อแก้ conflict ได้หมด ก็เป็นไปตามที่หนังสือว่าไว้
" Languages that yacc has trouble parsing are often hard for people to parse in their heads; you'll end up with a better language design once you change your language to remove the conflicts. "
- จากหนังสือ lex & yacc
- จากหนังสือ lex & yacc
จริง ๆ หลังจากปรับแก้ conflict ได้หมดแล้ว ตัวภาษายังเหมือนเดิมไม่เปลี่ยนแปลงครับ
แต่สิ่งที่เปลี่ยนไปคือ กฎไวยากรณ์ที่อ่านเข้าใจง่ายขึ้นอีกจมเลยทีเดียว
สำหรับใครที่คิดจะใช้ yacc แนะนำว่า ต้องศึกษากลไกการทำงานของ yacc เสียก่อน
ว่ามีวิธีการ shift & reduce อย่างไร คุณจะใช้ประโยชน์จาก yacc ได้หรือไม่ก็อยู่ตรงนี้แหละ
flex = lex และ bison = yacc
lex กับ yacc ต้นฉบับมันเก่าแล้ว ก็มีคนพัฒนาตัวใหม่ ๆ ออกมาหลายตัวด้วยกัน
ตัวที่ผมใช้คือเจ้าสองตัวนี้
เป็นโอเพนซอร์ส นำมาคอมไพล์และติดตั้งบนลินุกซ์ได้โดยไม่มีปัญหา
bison กับภาษา C++
อันนี้เป็นคำเตือนสำหรับผู้ที่จะใช้ flex & bison กับโปรแกรมที่พัฒนาด้วยภาษา C++
ตอนแรกผมใช้ภาษา C ในการศึกษาวิธีการใช้งาน flex & bison
และพัฒนากฎขึ้นมาให้ทำงานตามต้องการ
แต่โปรแกรมที่จะนำไปใช้จริง ๆ มันเป็นภาษา C++ ครับ
โดยปกติโปรแกรมภาษา C สามารถนำไปใช้งาน
กับคอมไพเลอร์ภาษา C++ ได้อย่างไม่มีปัญหา
แต่ไม่ใช่สำหรับเจ้า flex & bison
พอเปลี่ยนคอมไพเลอร์จาก gcc ไปเป็น g++
ก็พบปัญหาว่า ลิงก์ไม่ผ่าน เนื่องจากฟังก์ชัน yylex() หายไป
ค้นข้อมูลและอ่านซอร์สโค้ดที่ flex & bison สร้างขึ้นมาให้โดยละเอียด
ก็พบว่า ตัว bison เองมีโหมดสำหรับภาษา C++ โดยเฉพาะ
ซึ่งในโหมดภาษา C++ นี้ ตัว bison จะสร้างโค้ดของ yacc ขึ้นมาให้เป็นคลาส (class) ครับ
ประเด็นก็คือ เมื่อตัว yacc เปลี่ยนเป็นคลาส ตัว yylex() จึงถูกย้ายไปอยู่ในคลาสด้วย
และมันถูกลบออกไปจากไลบรารีภาษา C++ ของ bison
แล้วเพิ่มโค้ด yylex() ไว้ในโค้ดที่ bison สร้างขึ้นมาให้แทน
นั่นหมายความว่า ถ้าต้องการใช้ bison กับ g++
คุณจะต้องใช้ yacc ผ่านคลาสที่ bison สร้างขึ้นมาให้เท่านั้นครับ
ไม่มีทางเลือกอื่น นอกจากจะขยันเขียน yylex() ขึ้นมาเอง
ตอนนี้งานก็เลยยังไม่เสร็จซะที
ต้องมาศึกษาเพิ่มว่า คลาส yacc ตัวนี้ใช้งานยังไง
หวังว่าจะเสร็จได้ทันพรุ่งนี้นะ....
เอาน่า ศึกษาไว้ทีเดียวคุ้ม
เริ่มใช้เป็นแล้วก็สามารถเอาไปประยุกต์ใช้ได้อีกหลายอย่างเลย ^^
อัพเดต
กว่าจะทำให้ใช้กับ C++ ได้ ก็หกโมงเย็นของวันที่ 13 พอดี
พรุ่งนี้ต้องนำเข้าไปรวมกับโปรแกรมจริง เสียเวลาเพิ่มอีก 1 วัน T_T

