อะไรทำให้ Java แยกวิเคราะห์ได้ง่ายกว่า C


90

ฉันคุ้นเคยกับข้อเท็จจริงที่ว่าไวยากรณ์ของ C และ C ++ มีความละเอียดอ่อนตามบริบทและโดยเฉพาะอย่างยิ่งคุณต้องมี "lexer hack" ใน C ในทางกลับกันฉันรู้สึกว่าคุณสามารถแยกวิเคราะห์ Java ได้เพียง 2 โทเค็นแห่งการมองไปข้างหน้าแม้จะมีความคล้ายคลึงกันมากระหว่างสองภาษา

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

ฉันถามเพราะตัวอย่างทั้งหมดที่ฉันเคยเห็นเกี่ยวกับความไวต่อบริบทของ C นั้นสามารถทำได้ในทางเทคนิค แต่ก็แปลกมาก ตัวอย่างเช่น,

foo (a);

อาจจะมีการเรียกฟังก์ชันโมฆะกับข้อโต้แย้งfoo aหรืออาจเป็นการประกาศว่าaเป็นวัตถุประเภทหนึ่งfooแต่คุณสามารถกำจัด parantheses ได้อย่างง่ายดาย ส่วนหนึ่งความแปลกนี้เกิดขึ้นเนื่องจากกฎการผลิต "ผู้ประกาศโดยตรง" สำหรับไวยากรณ์ Cตอบสนองวัตถุประสงค์สองประการในการประกาศทั้งฟังก์ชันและตัวแปร

ในทางกลับกันไวยากรณ์ Javaมีกฎการผลิตแยกต่างหากสำหรับการประกาศตัวแปรและการประกาศฟังก์ชัน ถ้าคุณเขียน

foo a;

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

ฉันเคยเห็นมันบอกว่า C นั้นยากที่จะแยกวิเคราะห์เนื่องจาก typedef แต่คุณสามารถประกาศประเภทของคุณเองใน Java ได้เช่นกัน กฎไวยากรณ์ภาษา C ใดที่direct_declaratorเป็นข้อผิดพลาด


7
คำถามเด็ด. อาจเป็นวิธีที่กว้างเกินไปหรือมีความเห็นเป็นหลัก
asteri

37
นี่เป็นคำถามที่ถูกต้องเกี่ยวกับตัวแยกวิเคราะห์และสิ่งเดียวที่กว้าง ๆ หรือความคิดเห็นจากประโยคนี้คือประโยคคู่สุดท้าย (ซึ่งน่าจะถูกทิ้งหรือเปลี่ยนแปลง) เลิกตามคะแนนโหวต
R .. GitHub STOP HELPING ICE

1
ฉันแก้ไขคำถามตามนั้นขอบคุณสำหรับ @R .. สำหรับคำติชม
korrok

3
แทบทุกคน (มาตรฐาน) ภาษาคอมพิวเตอร์เป็นบริบทที่สำคัญ ; คุณไม่สามารถประกาศตัวแปรชนิดหนึ่งและในทางที่ผิดมันมากที่สุดlangauges ที่แตกต่างจาก " ไวยากรณ์ทั้งหมดสำหรับภาษา" มีความอ่อนไหวต่อบริบท คนส่วนใหญ่ที่สร้างโปรแกรมแยกวิเคราะห์จะสร้างตัวแยกวิเคราะห์ที่ไม่มีบริบท (หรือเข้มงวดกว่านั้น) จากนั้นใช้แฮ็กภายนอกโปรแกรมแยกวิเคราะห์เพื่อตรวจสอบคุณสมบัติที่ไม่มีบริบท
Ira Baxter

1
@IraBaxter ฉันจะไม่เรียกสิ่งนั้นว่า "แฮ็ก" การแยกปัญหาออกเป็นสองส่วนดูเหมือนจะเป็นสิ่งที่สมเหตุสมผลเนื่องจากการแยกวิเคราะห์ภาษาที่คำนึงถึงบริบทไม่สามารถทำได้อย่างมีประสิทธิภาพ (และในความเป็นจริงแล้วแม้แต่การแยกวิเคราะห์ภาษาที่ไม่มีบริบทก็ไม่ได้มีประสิทธิภาพและนั่นคือเหตุผลที่เรา จำกัด เฉพาะส่วนย่อยที่ไม่มีบริบท) . การวิเคราะห์แบบแยกวิเคราะห์แบบไม่มีบริบท + แบบคงที่เพื่อตรวจสอบเฉพาะคุณสมบัติที่คำนึงถึงบริบทใน AST จึงเป็นสิ่งที่สมเหตุสมผล
Bakuriu

คำตอบ:


76

การแยกวิเคราะห์ C ++ เริ่มยากขึ้น การแยกวิเคราะห์ Java นั้นยากพอ ๆ กัน

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

IIRC, ไวยากรณ์ LALR (1) ที่ "ชัดเจน" ของ Java 1.4 นั้นไม่คลุมเครือดังนั้นการแยกวิเคราะห์จึง "ง่าย" ฉันไม่แน่ใจว่า Java สมัยใหม่ไม่มีความคลุมเครือในท้องถิ่นทางไกลเป็นอย่างน้อย มักจะมีปัญหาในการตัดสินใจว่า "... >>" ปิดสองเทมเพลตหรือเป็น "ตัวดำเนินการกะที่ถูกต้อง" ฉันสงสัยว่าJava สมัยใหม่ไม่แยกวิเคราะห์ด้วย LALR (1) อีกต่อไปอีกต่อไป

แต่เราสามารถผ่านพ้นปัญหาการแยกวิเคราะห์ได้โดยใช้ตัวแยกวิเคราะห์ที่แข็งแกร่ง (หรือตัวแยกวิเคราะห์ที่อ่อนแอและแฮ็กคอลเลกชันบริบทเนื่องจากส่วนหน้าของ C และ C ++ ส่วนใหญ่ทำในขณะนี้) สำหรับทั้งสองภาษา C และ C ++ มีความซับซ้อนเพิ่มเติมในการมีตัวประมวลผลล่วงหน้า สิ่งเหล่านี้มีความซับซ้อนในทางปฏิบัติมากกว่าที่คิด ข้อเรียกร้องอย่างหนึ่งคือตัวแยกวิเคราะห์ C และ C ++ นั้นยากมากที่ต้องเขียนด้วยมือ ไม่เป็นความจริง คุณสามารถสร้างตัวแยกวิเคราะห์ Java และ C ++ ได้ดีด้วยตัวสร้างตัวแยกวิเคราะห์ GLR

แต่การแยกวิเคราะห์ไม่ใช่ปัญหาจริงๆ

เมื่อคุณแยกวิเคราะห์แล้วคุณจะต้องทำอะไรบางอย่างกับต้นไม้ AST / parse ในทางปฏิบัติคุณจำเป็นต้องรู้สำหรับตัวระบุทุกตัวความหมายของมันคืออะไรและใช้ที่ไหน ("ชื่อและความละเอียดประเภท" อย่างเลอะเทอะการสร้างตารางสัญลักษณ์) สิ่งนี้กลายเป็นงานที่มากกว่าการทำให้ตัวแยกวิเคราะห์ถูกต้องประกอบไปด้วยการสืบทอดอินเทอร์เฟซการโอเวอร์โหลดและเทมเพลตและความสับสนจากความจริงที่ว่าความหมายของทั้งหมดนี้เขียนด้วยภาษาธรรมชาติที่ไม่เป็นทางการซึ่งแพร่กระจายไปทั่วหลายสิบถึงหลายร้อยหน้า ของมาตรฐานภาษา C ++ แย่มากที่นี่ Java 7 และ 8 กำลังแย่มากจากมุมมองนี้ (และตารางสัญลักษณ์ก็ไม่ใช่ทั้งหมดที่คุณต้องการดูประวัติของฉันสำหรับบทความที่ยาวขึ้นเรื่อง "Life After Parsing")

คนส่วนใหญ่ต่อสู้กับส่วนการแยกวิเคราะห์ที่บริสุทธิ์ (มักจะไม่จบ; ตรวจสอบ SO ด้วยตัวเองสำหรับคำถามมากมายเกี่ยวกับวิธีสร้างตัวแยกวิเคราะห์ที่ใช้งานได้สำหรับภาษาที่แท้จริง) ดังนั้นพวกเขาจึงไม่เคยเห็นชีวิตหลังจากการแยกวิเคราะห์ จากนั้นเราก็ได้ทฤษฎีบทชาวบ้านเกี่ยวกับสิ่งที่ยากต่อการแยกวิเคราะห์และไม่มีสัญญาณว่าจะเกิดอะไรขึ้นหลังจากขั้นตอนนั้น

การแก้ไขไวยากรณ์ C ++ จะไม่ช่วยให้คุณไปได้ทุกที่

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

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


คำตอบที่ดี. ดู D และ C + ด้วยซึ่งพยายามแก้ไขปัญหาเหล่านี้บางส่วน s / content / contend /
david.pfx

3
ฉันเคยอ่าน Life After Parsing มาก่อนและพบว่ามันเป็นการเปิดหูเปิดตาจริงๆ มันทำให้ฉันชัดเจนว่ามีงานในการวิเคราะห์เชิงความหมาย (ความละเอียดของชื่อ / ประเภท, ... ) มากกว่าที่มีในการแยกวิเคราะห์ ฉันไม่ได้พยายามเปลี่ยนไวยากรณ์ของภาษาใด ๆ ฉันไม่ต้องการที่จะเข้าใจสิ่งที่คุณสมบัติที่มีภาษาในที่ที่คุณสามารถทำวิเคราะห์ประโยคแรกและจากนั้นการวิเคราะห์ความหมาย C ไม่ใช่ภาษาดังกล่าว (ต้องใช้ lexer hack); ฉันคิดเสมอว่า Java คืออะไรและฉันอยากรู้ว่าทำไม
korrok

1
@ Korrok: อ่านคำตอบของฉันเกี่ยวกับการสร้าง Java / C ++ ด้วยตัวแยกวิเคราะห์ GLR คุณไม่จำเป็นต้องสับ lexer ใด ๆ ดังนั้นความแตกต่างอยู่ในใจของคนที่ใช้เทคโนโลยีการแยกวิเคราะห์ที่ไม่ถูกต้อง ... จริงอยู่ที่การสร้างส่วนหน้า C ++ แบบเต็ม (โดยเฉพาะ C ++ 14 ซึ่งเราได้ทำไปแล้ว) นั้นยากกว่าการทำ Java8 แต่ก็ยากทั้งคู่ (ในแง่ของความพยายามและการใส่ใจในรายละเอียด) และการแยกวิเคราะห์ เป็นชิ้นที่ง่ายที่สุด
Ira Baxter

1
ฉันเห็นด้วยเกี่ยวกับ "Life after Parsing" ของคุณ: เช่นความละเอียดเกินพิกัดใน C # สามารถเข้ารหัสปัญหา 3-SAT ได้ดังนั้นจึงเป็น NP-hard
Jörg W Mittag

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