โปรแกรมแยกวิเคราะห์ GCC และ Clang เขียนด้วยลายมือจริงหรือ


90

ดูเหมือนว่า GCC และ LLVM-Clang กำลังใช้ตัวแยกวิเคราะห์การสืบเชื้อสายแบบเรียกซ้ำที่เขียนด้วยลายมือและไม่ได้สร้างด้วยเครื่องโดยใช้ Bison-Flex การแยกวิเคราะห์จากล่างขึ้นบน

ขอคนที่นี่ช่วยยืนยันว่าเป็นกรณีนี้ได้ไหม และถ้าเป็นเช่นนั้นเหตุใดเฟรมเวิร์กคอมไพเลอร์หลักจึงใช้ตัวแยกวิเคราะห์ที่เขียนด้วยลายมือ

อัปเดต : บล็อกที่น่าสนใจในหัวข้อนี้ที่นี่


27
คอมไพเลอร์กระแสหลักเกือบทั้งหมดใช้ตัวแยกวิเคราะห์ที่เขียนด้วยลายมือ มีปัญหาอะไร
SK-logic

2
คุณต้องทำ (กึ่ง) ด้วยตนเองหากคุณต้องการประสิทธิภาพ
Gene Bushuyev

15
และไม่เพียง แต่ประสิทธิภาพเท่านั้น - ข้อความแสดงข้อผิดพลาดที่ดีขึ้นความสามารถในการกู้คืน ฯลฯ
SK-logic

แล้ว MS VisualStudio ล่ะ? แม้ว่าจะไม่ใช่โอเพ่นซอร์สใครบางคนจาก MS สามารถตรวจสอบได้ว่าพวกเขาใช้ตัวแยกวิเคราะห์การสืบเชื้อสายแบบเขียนซ้ำด้วยมือหรือไม่?
OrenIshShalom

3
@GeneBushuyev จาก GCC wiki: "... แม้ว่าการกำหนดเวลาจะแสดงความเร็ว 1.5%แต่ประโยชน์หลัก ๆ ก็คือการอำนวยความสะดวกในการปรับปรุงในอนาคต ... " การเร่งความเร็วนี้ดูเหมือนจะค่อนข้างเล็กน้อย ...
OrenIshShalom

คำตอบ:


78

ใช่:

  • GCC ใช้ตัวแยกวิเคราะห์ yacc (วัวกระทิง) นาน ๆ ครั้ง แต่มันถูกแทนที่ด้วยตัวแยกวิเคราะห์โคตรซ้ำที่เขียนด้วยมือในบางช่วงของซีรีส์ 3.x: ดูhttp://gcc.gnu.org/wiki/New_C_Parserสำหรับ ลิงก์ไปยังการส่งโปรแกรมแก้ไขที่เกี่ยวข้อง

  • เสียงดังกราวยังใช้ parser โคตร recursive ที่เขียนด้วยมือ: ดูส่วน "A parser ปึกแผ่นเดียวสำหรับ C, วัตถุประสงค์ C, C ++ และวัตถุประสงค์ C ++" ใกล้ถึงจุดสิ้นสุดของhttp://clang.llvm.org/features.html


3
นั่นหมายความว่า ObjC, C และ C ++ มี LL (k) Grammars หรือไม่?
Lindemann

47
ไม่: แม้แต่ C ซึ่งง่ายที่สุดในสามก็มีไวยากรณ์ที่ไม่ชัดเจน ยกตัวอย่างเช่นfoo * bar;สามารถแยกวิเคราะห์เป็นทั้งการแสดงออกคูณ (กับผลที่ไม่ได้ใช้) หรือประกาศของตัวแปรที่มีประเภทตัวชี้-to-bar fooข้อใดถูกต้องขึ้นอยู่กับว่า a typedeffor fooอยู่ในขอบเขตในขณะนั้นซึ่งไม่ใช่สิ่งที่สามารถกำหนดได้ด้วยจำนวนผู้มอง แต่นั่นหมายความว่าตัวแยกวิเคราะห์การสืบเชื้อสายแบบเรียกซ้ำจำเป็นต้องมีการเพิ่มเครื่องจักรพิเศษที่น่าเกลียดเพื่อจัดการกับสิ่งนั้น
Matthew Slattery

9
ฉันสามารถยืนยันได้จากหลักฐานเชิงประจักษ์ว่า C ++ 11, C และ Objective C มีไวยากรณ์ที่ไม่มีบริบทซึ่งตัวแยกวิเคราะห์ GLR สามารถจัดการได้
Ira Baxter

2
เกี่ยวกับความอ่อนไหวต่อบริบทคำตอบนี้ไม่อ้างว่า: การแยกวิเคราะห์ภาษาเหล่านี้น่าจะเป็นภาษาทัวริงที่สมบูรณ์
Ioannis Filippidis

107

มีทฤษฎีบทชาวบ้านที่บอกว่า C นั้นยากที่จะแยกวิเคราะห์และโดยพื้นฐานแล้ว C ++ เป็นไปไม่ได้

มันไม่เป็นความจริง

สิ่งที่เป็นความจริงก็คือ C และ C ++ นั้นค่อนข้างยากที่จะแยกวิเคราะห์โดยใช้ตัวแยกวิเคราะห์ LALR (1) โดยไม่ต้องแฮ็คเครื่องจักรในการแยกวิเคราะห์และทำให้ข้อมูลตารางสัญลักษณ์ยุ่งเหยิง ในความเป็นจริงแล้ว GCC ใช้ในการแยกวิเคราะห์โดยใช้ YACC และแฮ็กเกอร์เพิ่มเติมเช่นนี้และใช่มันน่าเกลียด ตอนนี้ GCC ใช้ตัวแยกวิเคราะห์ที่เขียนด้วยลายมือ แต่ยังคงใช้แฮ็กเกอร์ตารางสัญลักษณ์ ชาวเสียงดังไม่เคยพยายามใช้เครื่องแยกวิเคราะห์อัตโนมัติ AFAIK ตัวแยกวิเคราะห์เสียงดังได้รับการเข้ารหัสแบบเรียกซ้ำเสมอ

ความจริงก็คือ C และ C ++ นั้นค่อนข้างง่ายในการแยกวิเคราะห์ด้วยตัวแยกวิเคราะห์ที่สร้างขึ้นโดยอัตโนมัติที่แข็งแกร่งกว่าเช่นตัวแยกวิเคราะห์ GLRและคุณไม่จำเป็นต้องแฮ็กใด ๆ Elsa c ++ แยกวิเคราะห์เป็นตัวอย่างหนึ่งของเรื่องนี้ Front End C ++ของเราเป็นอีกส่วนหนึ่ง (เช่นเดียวกับส่วนหน้า "คอมไพเลอร์" ของเรา GLR เป็นเทคโนโลยีการแยกวิเคราะห์ที่ยอดเยี่ยมมาก)

ส่วนหน้า C ++ ของเราไม่เร็วเท่า GCC และช้ากว่า Elsa อย่างแน่นอน เราใช้พลังงานเพียงเล็กน้อยในการปรับแต่งมันอย่างระมัดระวังเพราะเรามีปัญหาเร่งด่วนอื่น ๆ อีก (อย่างไรก็ตามมันถูกใช้กับโค้ด C ++ หลายล้านบรรทัด) Elsa น่าจะช้ากว่า GCC เพียงเพราะมันเป็นเรื่องทั่วไป ด้วยความเร็วโปรเซสเซอร์ในปัจจุบันความแตกต่างเหล่านี้อาจไม่สำคัญมากนักในทางปฏิบัติ

แต่ "คอมไพเลอร์ตัวจริง" ที่เผยแพร่กันอย่างแพร่หลายในปัจจุบันมีรากฐานมาจากคอมไพเลอร์เมื่อ 10 หรือ 20 ปีก่อนหรือมากกว่านั้น จากนั้นความไร้ประสิทธิภาพก็มีความสำคัญมากขึ้นและไม่มีใครเคยได้ยินเกี่ยวกับตัวแยกวิเคราะห์ GLR ดังนั้นผู้คนจึงทำในสิ่งที่พวกเขารู้ว่าต้องทำอย่างไร เสียงดังขึ้นเป็นเรื่องล่าสุด แต่แล้วทฤษฎีบทชาวบ้านก็ยังคงรักษา "การโน้มน้าวใจ" ไว้เป็นเวลานาน

คุณไม่จำเป็นต้องทำแบบนั้นอีกต่อไป คุณสามารถใช้ GLR และตัวแยกวิเคราะห์อื่น ๆ เป็นส่วนหน้าได้อย่างสมเหตุสมผลโดยมีการปรับปรุงการบำรุงรักษาคอมไพเลอร์

สิ่งที่เป็นความจริงก็คือการได้รับไวยากรณ์ที่ตรงกับพฤติกรรมของคอมไพเลอร์เพื่อนบ้านที่เป็นมิตรของคุณนั้นยาก ในขณะที่คอมไพเลอร์ C ++ เกือบทั้งหมดใช้มาตรฐานดั้งเดิม (ส่วนใหญ่) แต่ก็มักจะมีส่วนขยายมุมมืดมากมายเช่นข้อกำหนด DLL ในคอมไพเลอร์ MS เป็นต้นหากคุณมีเครื่องมือแยกวิเคราะห์ที่แข็งแกร่งคุณสามารถใช้เวลาของคุณในการพยายามหา ไวยากรณ์ขั้นสุดท้ายเพื่อให้ตรงกับความเป็นจริงแทนที่จะพยายามดัดไวยากรณ์ของคุณเพื่อให้ตรงกับข้อ จำกัด ของตัวสร้างโปรแกรมแยกวิเคราะห์ของคุณ

แก้ไขพฤศจิกายน 2555: ตั้งแต่เขียนคำตอบนี้เราได้ปรับปรุงส่วนหน้า C ++ ของเราเพื่อรองรับ C ++ 11 แบบเต็มรวมถึงภาษาถิ่น ANSI, GNU และ MS ในขณะที่มีสิ่งพิเศษมากมายเราไม่จำเป็นต้องเปลี่ยนเครื่องมือแยกวิเคราะห์ของเรา เราเพิ่งแก้ไขกฎไวยากรณ์ เราไม่ต้องเปลี่ยนการวิเคราะห์ความหมาย; C ++ 11 มีความซับซ้อนมากในเชิงความหมายและงานนี้ทำให้ความพยายามในการเรียกใช้โปรแกรมแยกวิเคราะห์

แก้ไขกุมภาพันธ์ 2015: ... ตอนนี้จัดการ C ++ 14 แบบเต็ม (ดูรับ AST ที่มนุษย์สามารถอ่านได้จากรหัส c ++สำหรับการแยกวิเคราะห์ GLR ของรหัสบิตธรรมดาและ "การแยกวิเคราะห์ที่น่าอับอายที่สุดของ C ++")

แก้ไขเมษายน 2017: ตอนนี้จัดการ (ร่าง) C ++ 17


6
PostScript: เช่นเดียวกับการทำให้ไวยากรณ์ตรงกับสิ่งที่ผู้ขายทำนั้นยากขึ้นขอชื่อและความละเอียดประเภทเพื่อให้ตรงกับการตีความคู่มือ C ++ 11 ของผู้ขายรายอื่นนั้นยากกว่าเนื่องจากหลักฐานเดียวที่คุณมีคือโปรแกรมที่รวบรวมเล็กน้อย แตกต่างกันออกไปหากคุณสามารถค้นหาได้ ส่วนใหญ่เราผ่านมาแล้วในเดือนสิงหาคม 2013 สำหรับ C ++ 11 ที่เหมาะสม แต่ฉันสิ้นหวังเล็กน้อยที่คณะกรรมการ C ++ ซึ่งดูเหมือนจะงุนงงในการสร้างมาตรฐานที่ใหญ่กว่า (และจากประสบการณ์ทำให้สับสนมากขึ้น) ในรูปแบบของ C ++ 1 ปี
Ira Baxter

5
ฉันอยากรู้จริงๆ: คุณจัดการกับfoo * bar;ความคลุมเครือนั้นได้อย่างไร?
Martin

14
@ มาร์ติน: โปรแกรมแยกวิเคราะห์ของเราจะแยกวิเคราะห์ทั้งสองวิธีโดยสร้างต้นไม้ที่มี "โหนดที่ไม่ชัดเจน" แบบพิเศษซึ่งมีลูก ๆ เป็นตัวแยกวิเคราะห์ทางเลือก เด็ก ๆ แบ่งปันบุตรหลานของตนอย่างเต็มที่ดังนั้นเราจึงลงเอยด้วย DAG แทนต้นไม้ หลังจากการแยกวิเคราะห์เสร็จสิ้นเราจะเรียกใช้ตัวประเมินไวยากรณ์แอตทริบิวต์ (AGE) บน DAG (ชื่อแฟนซีสำหรับ "เดินตามต้นไม้และทำสิ่งต่างๆ" หากคุณไม่ทราบ) ซึ่งจะคำนวณประเภทของตัวระบุที่ประกาศทั้งหมด ...
Ira Baxter

12
... เด็กที่คลุมเครือไม่สามารถมีความสอดคล้องกันได้ทั้งคู่ AGE ในการค้นพบเด็กที่มีความคลุมเครือซึ่งไม่สามารถพิมพ์ผิดได้เพียงแค่ลบมันออกไป สิ่งที่เหลืออยู่คือเด็กพิมพ์ดีด ดังนั้นเราจึงได้พิจารณาว่าการแยกวิเคราะห์ของ "foo bar; ถูกต้อง. เคล็ดลับนี้ใช้ได้กับความคลุมเครือบ้าๆทุกประเภทที่พบในไวยากรณ์จริงที่เราสร้างขึ้นสำหรับภาษาถิ่นที่แท้จริงของ C ++ 11 และ *แยกการแยกวิเคราะห์ออกจากการวิเคราะห์ความหมายของชื่อโดยสิ้นเชิง การแยกที่สะอาดนี้หมายถึงงานวิศวกรรมที่ต้องทำน้อยกว่ามาก (ไม่มีปัญหาในการแก้ไขข้อบกพร่อง) ดูstackoverflow.com/a/1004737/120163สำหรับการสนทนาเพิ่มเติม
Ira Baxter

3
@ TimCas: อันที่จริงฉันอยู่กับคุณในความโง่เขลาของการออกแบบไวยากรณ์ภาษา (และความหมาย) ที่ซับซ้อนมากจนยากที่จะทำให้ถูกต้อง (ใช่ภาษา C ++ ทนทุกข์ทรมานที่นี่ไม่ดี) ฉันหวังว่าคณะกรรมการออกแบบภาษาจะออกแบบไวยากรณ์เพื่อให้เทคโนโลยีการแยกวิเคราะห์ที่ง่ายขึ้นจะใช้งานได้และกำหนดความหมายของภาษาอย่างชัดเจนและตรวจสอบด้วยเครื่องมือวิเคราะห์ความหมาย อนิจจาโลกดูเหมือนจะไม่เป็นเช่นนั้น ดังนั้นฉันจึงใช้มุมมองที่คุณสร้างสิ่งที่คุณต้องสร้างให้ดีที่สุดเท่าที่จะทำได้และใช้ชีวิตต่อไปแม้จะมีความอึดอัดก็ตาม
Ira Baxter

31

ตัวแยกวิเคราะห์ของ Clang เป็นตัวแยกวิเคราะห์แบบเรียกซ้ำที่เขียนด้วยมือเช่นเดียวกับส่วนหน้า C และ C ++ แบบโอเพนซอร์สและเชิงพาณิชย์อื่น ๆ

Clang ใช้ตัวแยกวิเคราะห์การสืบเชื้อสายซ้ำด้วยเหตุผลหลายประการ:

  • ประสิทธิภาพ : โปรแกรมแยกวิเคราะห์ที่เขียนด้วยมือช่วยให้เราสามารถเขียนตัวแยกวิเคราะห์ที่รวดเร็วเพิ่มประสิทธิภาพเส้นทางฮอตได้ตามต้องการและเราจะควบคุมประสิทธิภาพนั้นเสมอ การมีตัวแยกวิเคราะห์ที่รวดเร็วทำให้สามารถใช้ Clang ในเครื่องมือการพัฒนาอื่น ๆ ซึ่งโดยทั่วไปจะไม่ใช้ตัวแยกวิเคราะห์ "จริง" เช่นการเน้นไวยากรณ์และการเติมโค้ดใน IDE
  • การวินิจฉัยและการกู้คืนข้อผิดพลาด : เนื่องจากคุณสามารถควบคุมได้อย่างเต็มที่ด้วยตัวแยกวิเคราะห์แบบเรียกซ้ำที่เขียนด้วยมือจึงง่ายต่อการเพิ่มกรณีพิเศษที่ตรวจพบปัญหาทั่วไปและให้การวินิจฉัยที่ยอดเยี่ยมและการกู้คืนข้อผิดพลาด (เช่นดูที่http: //clang.llvm .org / features.html # expressivediags ) ด้วยตัวแยกวิเคราะห์ที่สร้างขึ้นโดยอัตโนมัติคุณจะถูก จำกัด ไว้ที่ความสามารถของตัวสร้าง
  • ความเรียบง่าย : ตัวแยกวิเคราะห์ที่สืบเชื้อสายซ้ำนั้นง่ายต่อการเขียนเข้าใจและแก้ไขข้อบกพร่อง คุณไม่จำเป็นต้องเป็นผู้เชี่ยวชาญด้านการแยกวิเคราะห์หรือเรียนรู้เครื่องมือใหม่ในการขยาย / ปรับปรุงโปรแกรมแยกวิเคราะห์ (ซึ่งสำคัญอย่างยิ่งสำหรับโครงการโอเพนซอร์ส) แต่คุณยังคงได้รับผลลัพธ์ที่ยอดเยี่ยม

โดยรวมแล้วสำหรับคอมไพเลอร์ C ++ มันก็ไม่สำคัญมากนัก: ส่วนการแยกวิเคราะห์ของ C ++ นั้นไม่สำคัญ แต่ก็ยังเป็นส่วนที่ง่ายกว่าดังนั้นจึงต้องจ่ายเพื่อให้ง่าย การวิเคราะห์เชิงความหมาย - โดยเฉพาะการค้นหาชื่อการเริ่มต้นความละเอียดเกินและการสร้างอินสแตนซ์เทมเพลต --- เป็นคำสั่งขนาดที่ซับซ้อนกว่าการแยกวิเคราะห์ หากคุณต้องการการพิสูจน์ให้ตรวจสอบการกระจายของโค้ดและคอมโพเนนต์ "Sema" ของ Clang (สำหรับการวิเคราะห์เชิงความหมาย) เทียบกับองค์ประกอบ "Parse" (สำหรับการแยกวิเคราะห์)


4
ใช่การวิเคราะห์เชิงความหมายนั้นยากกว่ามาก เรามีกฎไวยากรณ์ 4000 บรรทัดซึ่งประกอบด้วยไวยากรณ์ C ++ 11 ของเราและโค้ดไวยากรณ์แอตทริบิวต์ 180,000 บรรทัดสำหรับรายการข้อสงสัย "การวิเคราะห์เชิงความหมาย" ด้านบนพร้อมโค้ดสนับสนุนอีก 100,000 บรรทัด การแยกวิเคราะห์ไม่ใช่ปัญหาแม้ว่าจะยากพอสมควรหากคุณเริ่มเดินผิด
Ira Baxter

1
ฉันไม่แน่ใจว่าตัวแยกวิเคราะห์ที่เขียนด้วยมือจำเป็นต้องดีกว่าสำหรับการรายงาน / การกู้คืนข้อผิดพลาด ดูเหมือนว่าผู้คนได้ใส่พลังงานให้กับตัววิเคราะห์ดังกล่าวมากกว่าการเพิ่มตัววิเคราะห์ที่ผลิตโดยเครื่องแยกวิเคราะห์อัตโนมัติในทางปฏิบัติ ดูเหมือนจะมีงานวิจัยที่ดีในหัวข้อนี้ บทความนี้ดึงดูดสายตาของฉันมาก: MG Burke, 1983, วิธีปฏิบัติสำหรับการวินิจฉัยและการกู้คืนข้อผิดพลาดทางไวยากรณ์ LR และ LL, วิทยานิพนธ์ระดับปริญญาเอก, ภาควิชาวิทยาการคอมพิวเตอร์, มหาวิทยาลัยนิวยอร์ก, ดูarchive.org/details/practicalmethodf00burk
Ira Baxter

1
... ดำเนินการฝึกความคิดนี้ต่อไป: หากคุณยินดีที่จะแก้ไข / ขยาย / ปรับแต่งโปรแกรมแยกวิเคราะห์ที่สร้างขึ้นด้วยมือของคุณเพื่อตรวจสอบกรณีพิเศษเพื่อการวินิจฉัยที่ดีขึ้นคุณควรเต็มใจที่จะลงทุนอย่างเท่าเทียมกันในการวิเคราะห์ตัววิเคราะห์ที่สร้างขึ้นโดยกลไกได้ดีขึ้น สำหรับการแยกวิเคราะห์พิเศษใด ๆ ที่คุณสามารถเข้ารหัสสำหรับคู่มือการใช้งานคุณสามารถเขียนโค้ดการตรวจสอบกลไกหนึ่งได้เช่นกัน (และสำหรับตัวแยกวิเคราะห์ (G) LR คุณสามารถทำได้โดยการตรวจสอบความหมายเกี่ยวกับการลดลง) ถึงแม้จะดูไม่น่ารับประทานก็แค่ขี้เกียจ แต่นั่นไม่ใช่คำฟ้องของโปรแกรมวิเคราะห์ IMHO ที่สร้างขึ้นโดยกลไก
Ira Baxter

8

ตัวแยกวิเคราะห์ของ gcc เขียนด้วยลายมือ . ฉันสงสัยเหมือนกันสำหรับเสียงดัง อาจเป็นเพราะสาเหตุบางประการ:

  • ประสิทธิภาพ : สิ่งที่คุณปรับให้เหมาะกับงานเฉพาะของคุณมักจะทำงานได้ดีกว่าโซลูชันทั่วไป Abstraction มักจะมีผลต่อประสิทธิภาพ
  • ระยะเวลา : อย่างน้อยในกรณีของ GCC GCC มีเครื่องมือสำหรับนักพัฒนาฟรีจำนวนมาก (ออกมาในปี 2530) ในเวลานั้นไม่มี yacc เวอร์ชันฟรี ฯลฯ ซึ่งฉันคิดว่าจะมีความสำคัญต่อผู้คนใน FSF

นี่อาจไม่ใช่กรณีของกลุ่มอาการ "ไม่ได้คิดค้นที่นี่" แต่เป็นมากกว่านั้นตามแนวของ "ไม่มีอะไรที่ปรับให้เหมาะกับสิ่งที่เราต้องการโดยเฉพาะดังนั้นเราจึงเขียนของเราเอง"


15
ไม่มี yacc เวอร์ชันฟรีในปี 1987? ฉันคิดว่ามีเวอร์ชันฟรีเมื่อ yacc ถูกส่งครั้งแรกภายใต้ Unix ย้อนกลับไปในยุค 70 และ IIRC (โปสเตอร์อื่น ๆ ดูเหมือนกัน) GCC เคยมีตัวแยกวิเคราะห์ที่ใช้ YACC ฉันได้ยินข้ออ้างในการเปลี่ยนแปลงคือการรายงานข้อผิดพลาดที่ดีขึ้น
Ira Baxter

7
ฉันต้องการเพิ่มบ่อยครั้งที่ง่ายกว่าในการสร้างข้อความแสดงข้อผิดพลาดที่ดีจากโปรแกรมแยกวิเคราะห์ที่เขียนด้วยลายมือ
Dietrich Epp

1
ประเด็นของคุณเกี่ยวกับเวลาไม่ถูกต้อง GCC เคยมีตัวแยกวิเคราะห์ที่ใช้ YACC แต่สิ่งนี้ถูกแทนที่ด้วยตัวแยกวิเคราะห์โคตรซ้ำที่เขียนด้วยลายมือในภายหลัง
Tommy Andersen

7

มีคำตอบแปลก ๆ !

ไวยากรณ์ C / C ++ ไม่ใช่บริบทฟรี พวกเขามีความอ่อนไหวต่อบริบทเนื่องจากแถบ Foo * ความคลุมเครือ เราต้องสร้างรายการของ typedef เพื่อให้ทราบว่า Foo เป็นประเภทหรือไม่

Ira Baxter: ฉันไม่เห็นประเด็นกับสิ่ง GLR ของคุณ ทำไมต้องสร้างต้นไม้แยกวิเคราะห์ซึ่งประกอบด้วยความคลุมเครือ การแยกวิเคราะห์หมายถึงการแก้ความคลุมเครือการสร้างโครงสร้างไวยากรณ์ คุณแก้ไขความคลุมเครือเหล่านี้ในรอบที่สองดังนั้นสิ่งนี้จึงไม่น่าเกลียดน้อยลง สำหรับฉันมันน่าเกลียดกว่ามาก ...

Yacc เป็นตัวสร้างตัวแยกวิเคราะห์ LR (1) (หรือ LALR (1)) แต่สามารถแก้ไขได้อย่างง่ายดายเพื่อให้มีความอ่อนไหวตามบริบท และไม่มีอะไรน่าเกลียดอยู่ในนั้น Yacc / Bison ถูกสร้างขึ้นเพื่อช่วยในการแยกวิเคราะห์ภาษา C ดังนั้นอาจไม่ใช่เครื่องมือที่น่าเกลียดที่สุดในการสร้างตัวแยกวิเคราะห์ C ...

จนกว่า GCC 3.x ตัวแยกวิเคราะห์ C จะถูกสร้างขึ้นโดย yacc / bison โดยมีตาราง typedefs ที่สร้างขึ้นระหว่างการแยกวิเคราะห์ ด้วยการสร้างตาราง typedefs "ในการแยกวิเคราะห์" ไวยากรณ์ C จะกลายเป็นบริบทที่ไม่มีในท้องถิ่นและยิ่งไปกว่านั้น "เฉพาะ LR (1)"

ตอนนี้ใน Gcc 4.x เป็นตัวแยกวิเคราะห์การสืบเชื้อสายแบบเรียกซ้ำ มันเป็นตัวแยกวิเคราะห์เดียวกันกับใน Gcc 3.x โดยยังคงเป็น LR (1) และมีกฎไวยากรณ์เหมือนกัน ความแตกต่างคือตัวแยกวิเคราะห์ yacc ถูกเขียนด้วยมือตอนนี้ shift / ลดจะซ่อนอยู่ใน call stack และไม่มี "state454: if (nextsym == '(') goto state398" เหมือนใน gcc 3.x yacc's โปรแกรมแยกวิเคราะห์จึงง่ายต่อการแก้ไขจัดการข้อผิดพลาดและพิมพ์ข้อความที่ดีกว่าและดำเนินการขั้นตอนการคอมไพล์ถัดไประหว่างการแยกวิเคราะห์ในราคาที่ "อ่านง่าย" น้อยกว่ามากสำหรับรหัส gcc

เหตุใดพวกเขาจึงเปลี่ยนจาก yacc เป็นการสืบเชื้อสายซ้ำ เนื่องจากมีความจำเป็นมากที่จะต้องหลีกเลี่ยง yacc ในการแยกวิเคราะห์ C ++ และเนื่องจาก GCC ใฝ่ฝันที่จะเป็นคอมไพเลอร์หลายภาษานั่นคือการแชร์โค้ดสูงสุดระหว่างภาษาต่างๆที่สามารถคอมไพล์ได้ นี่คือสาเหตุที่ตัวแยกวิเคราะห์ C ++ และ C เขียนในลักษณะเดียวกัน

C ++ แยกวิเคราะห์ได้ยากกว่า C เนื่องจากไม่ใช่ LR (1) แบบ "เฉพาะที่" เป็น C ไม่ใช่แม้แต่ LR (k) ดูที่func<4 > 2>ซึ่งเป็นฟังก์ชั่นแม่แบบ instantiated 4> 2 คือจะต้องมีการอ่านเป็นfunc<4 > 2> func<1>นี่ไม่ใช่ LR (1) อย่างแน่นอน ตอนนี้พิจารณาfunc<4 > 2 > 1 > 3 > 3 > 8 > 9 > 8 > 7 > 8>. นี่คือที่ที่การสืบเชื้อสายซ้ำสามารถแก้ความคลุมเครือได้อย่างง่ายดายในราคาของการเรียกใช้ฟังก์ชันเพิ่มเติมอีกสองสามครั้ง (parse_template_parameter คือฟังก์ชันตัวแยกวิเคราะห์ที่ไม่ชัดเจนหาก parse_template_parameter (17tokens) ล้มเหลวให้ลองอีกครั้ง parse_template_parameter (15tokens), parse_template_paramens) ... จนถึง 13tokens มันได้ผล).

ฉันไม่รู้ว่าทำไมจึงไม่สามารถเพิ่มไวยากรณ์ย่อยซ้ำ yacc / bison ได้บางทีนี่อาจเป็นขั้นตอนต่อไปในการพัฒนาตัวแยกวิเคราะห์ gcc / GNU


9
"สำหรับฉันมันน่าเกลียดกว่านี้มาก". สิ่งที่ฉันบอกคุณได้ก็คือวิศวกรรมของตัวแยกวิเคราะห์คุณภาพการผลิตโดยใช้ GLR และการแก้ปัญหาความไม่ชัดเจนของการหน่วงเวลานั้นใช้ได้จริงกับทีมงานขนาดเล็กจริงๆ วิธีแก้ปัญหาอื่น ๆ ทั้งหมดที่ฉันเคยเห็นมีส่วนเกี่ยวข้องกับการกัดฟันในที่สาธารณะเป็นเวลาหลายปีในการตีกลับและการแฮ็กที่จำเป็นเพื่อให้สามารถใช้งานได้กับ LR การสืบเชื้อสายซ้ำคุณตั้งชื่อมัน คุณสามารถคาดเดาเทคโนโลยีการแยกวิเคราะห์ใหม่ ๆ ที่ยอดเยี่ยมมากมาย แต่เท่าที่ฉันสามารถบอกได้นั่นเป็นเพียงการกัดฟันมากขึ้นในตอนนี้ ไอเดียถูก; การประหารชีวิตเป็นที่รัก
Ira Baxter

@IraBaxter: หนู! citeseerx.ist.psu.edu/viewdoc/…
Fizz

@ Fizz: บทความที่น่าสนใจเกี่ยวกับการแยกวิเคราะห์ป้อมซึ่งเป็นภาษาการเขียนโปรแกรมทางวิทยาศาสตร์ที่ซับซ้อน พวกเขากล่าวถึงสิ่งที่ควรทราบหลายประการ: a) เครื่องแยกวิเคราะห์แบบคลาสสิก (LL (k), LALR (1)) ไม่สามารถจัดการกับไวยากรณ์ที่ยากได้ b) พวกเขาลองใช้ GLR มีปัญหากับขนาด แต่ผู้พัฒนาไม่มีประสบการณ์จึงไม่ เสร็จสมบูรณ์ [นั่นไม่ใช่ความผิดของ GLR] และ c) พวกเขาใช้ตัวแยกวิเคราะห์ Packrat แบบย้อนกลับ (ธุรกรรม) และใช้ความพยายามอย่างมากรวมถึงการทำงานเพื่อสร้างข้อความแสดงข้อผิดพลาดที่ดีขึ้น เกี่ยวกับตัวอย่างการแยกวิเคราะห์ "{| x || x ← mySet, 3 | x}" ฉันเชื่อว่า GLR จะทำได้ดีและไม่ต้องเว้นวรรค
Ira Baxter

0

ดูเหมือนว่า GCC และ LLVM-Clang กำลังใช้ตัวแยกวิเคราะห์การสืบเชื้อสายแบบเรียกซ้ำที่เขียนด้วยลายมือและไม่ได้สร้างด้วยเครื่องโดยใช้ Bison-Flex การแยกวิเคราะห์จากล่างขึ้นบน

โดยเฉพาะอย่างยิ่ง Bison ฉันไม่คิดว่าจะสามารถจัดการกับไวยากรณ์ได้โดยไม่ต้องแยกวิเคราะห์บางสิ่งอย่างคลุมเครือและทำครั้งที่สองในภายหลัง

ฉันรู้ว่า Happy ของ Haskell อนุญาตให้มีตัวแยกวิเคราะห์ monadic (เช่นขึ้นอยู่กับรัฐ) ที่สามารถแก้ไขปัญหาเฉพาะกับไวยากรณ์ C ได้ แต่ฉันรู้ว่าไม่มีตัวสร้างตัวแยกวิเคราะห์ C ที่อนุญาตให้ monad ของรัฐที่ผู้ใช้จัดหาให้

ตามทฤษฎีแล้วการกู้คืนข้อผิดพลาดจะเป็นจุดที่ช่วยให้โปรแกรมแยกวิเคราะห์ที่เขียนด้วยลายมือ แต่ประสบการณ์ของฉันกับ GCC / Clang พบว่าข้อความแสดงข้อผิดพลาดไม่ดีเป็นพิเศษ

สำหรับประสิทธิภาพ - การอ้างสิทธิ์บางส่วนดูเหมือนจะไม่มีเหตุผล การสร้างเครื่องขนาดใหญ่โดยใช้ตัวสร้างตัวแยกวิเคราะห์ควรส่งผลให้เกิดบางอย่างO(n)และฉันสงสัยว่าการแยกวิเคราะห์เป็นปัญหาคอขวดในการใช้เครื่องมือมาก


3
คำถามนี้มีคำตอบคุณภาพสูงอยู่แล้วคุณกำลังพยายามเพิ่มอะไร?
ทอด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.