ฉันจะเอาแทงใส่ลงไปในข้อตกลงของคนธรรมดา
หากคุณคิดในแง่ของการแยกวิเคราะห์ต้นไม้ (ไม่ใช่ AST แต่เป็นตัวแยกวิเคราะห์การขยายและการป้อนข้อมูล) การเรียกซ้ำที่เหลือจะส่งผลให้เกิดการเรียกซ้ำในต้นไม้ที่เติบโตขึ้นซ้ายและลง การเรียกซ้ำที่ถูกต้องเป็นสิ่งที่ตรงกันข้าม
เป็นตัวอย่างไวยากรณ์ทั่วไปในคอมไพเลอร์คือรายการของไอเท็ม ให้จดรายการสตริง ("สีแดง", "สีเขียว", "สีฟ้า") แล้วแยกวิเคราะห์ ฉันสามารถเขียนไวยากรณ์ได้สองสามวิธี ตัวอย่างต่อไปนี้ซ้ำซากโดยตรงหรือซ้ายซ้ำตามลำดับ:
arg_list: arg_list:
STRING STRING
| arg_list ',' STRING | STRING ',' arg_list
ต้นไม้สำหรับการแยกวิเคราะห์เหล่านี้:
(arg_list) (arg_list)
/ \ / \
(arg_list) BLUE RED (arg_list)
/ \ / \
(arg_list) GREEN GREEN (arg_list)
/ /
RED BLUE
สังเกตว่ามันเติบโตไปในทิศทางของการเรียกซ้ำ
นี่ไม่ใช่ปัญหาจริงๆมันก็โอเคที่จะเขียนไวยากรณ์แบบเรียกซ้ำซ้ำซากทางด้านซ้าย ... หากเครื่องมือแยกวิเคราะห์ของคุณสามารถจัดการได้ ตัวแยกวิเคราะห์จากล่างขึ้นบนจัดการได้ดี ดังนั้นตัวแยกวิเคราะห์ LL ที่ทันสมัยมากขึ้นสามารถ ปัญหาเกี่ยวกับไวยากรณ์แบบเรียกซ้ำไม่ใช่การเรียกซ้ำ แต่เป็นการเรียกซ้ำโดยไม่ใช้ตัวแยกวิเคราะห์หรือเรียกซ้ำโดยไม่ใช้โทเค็น หากเราบริโภคโทเค็นอย่างน้อย 1 ครั้งเสมอเมื่อเราเรียกเก็บเงินเราก็จะไปถึงจุดสิ้นสุดของการแยกวิเคราะห์ การเรียกซ้ำทางซ้ายถูกกำหนดให้เป็นการเรียกซ้ำโดยไม่บริโภคซึ่งเป็นลูปที่ไม่สิ้นสุด
ข้อ จำกัด นี้เป็นรายละเอียดการใช้งานอย่างแท้จริงของการใช้ไวยากรณ์ที่มีตัวแยกวิเคราะห์ LL บนลงล่างไร้เดียงสา (parser สืบเชื้อสายซ้ำ) หากคุณต้องการติดกับไวยากรณ์แบบเรียกซ้ำซ้ำซากซ้ายคุณสามารถจัดการกับมันได้โดยเขียนการผลิตใหม่เพื่อใช้โทเค็นอย่างน้อย 1 โทเค็นก่อนที่จะเรียกซ้ำดังนั้นจึงมั่นใจได้ว่าเราจะไม่ติดขัด สำหรับกฎไวยากรณ์ใด ๆ ที่เหลือซ้ำเราสามารถเขียนใหม่ได้โดยการเพิ่มกฎกลางที่จะทำให้ไวยากรณ์เรียบลงในระดับหนึ่งของ lookahead ซึ่งใช้โทเค็นระหว่างการผลิตแบบเรียกซ้ำ (หมายเหตุ: ฉันไม่ได้บอกว่านี่เป็นวิธีเดียวหรือวิธีที่ต้องการเขียนไวยากรณ์เพียงชี้กฎทั่วไปในตัวอย่างง่ายๆนี้ตัวเลือกที่ดีที่สุดคือการใช้แบบฟอร์มเรียกซ้ำขวา) เนื่องจากวิธีการนี้เป็นวิธีทั่วไป ตัวแยกวิเคราะห์สามารถใช้งานได้โดยไม่ต้องเกี่ยวข้องกับโปรแกรมเมอร์ (ในทางทฤษฎี) ในทางปฏิบัติฉันเชื่อว่า ANTLR 4 จะทำเช่นนั้น
สำหรับไวยากรณ์ข้างต้นการใช้ LL ที่แสดงการเรียกซ้ำซ้ายจะมีลักษณะเช่นนี้ โปรแกรมแยกวิเคราะห์จะเริ่มต้นด้วยการทำนายรายการ ...
bool match_list()
{
if(lookahead-predicts-something-besides-comma) {
match_STRING();
} else if(lookahead-is-comma) {
match_list(); // left-recursion, infinite loop/stack overflow
match(',');
match_STRING();
} else {
throw new ParseException();
}
}
ในความเป็นจริงสิ่งที่เรากำลังเผชิญอยู่ก็คือ เราเริ่มต้นประโยคที่กำหนดล่วงหน้าแล้วจึงเรียกใช้ฟังก์ชันซ้ำสำหรับการคาดคะเนนั้นและฟังก์ชันนั้นเรียกการคาดการณ์เดิมอีกครั้งอย่างไร้เดียงสา
ตัวแยกวิเคราะห์จากล่างขึ้นบนไม่ได้มีปัญหาของกฎแบบวนซ้ำในทิศทางใดทิศทางหนึ่งเนื่องจากพวกเขาไม่ได้แยกจุดเริ่มต้นของประโยคซ้ำพวกเขาทำงานโดยนำประโยคกลับมารวมกัน
การเรียกซ้ำในไวยากรณ์เป็นปัญหาเฉพาะเมื่อเราสร้างจากบนลงล่างคือ โปรแกรมแยกวิเคราะห์ของเราทำงานโดย "ขยาย" การคาดการณ์ของเราเมื่อเราบริโภคโทเค็น หากแทนที่จะขยายเรายุบ (การผลิตเป็น "ลดลง") เช่นเดียวกับในตัวแยกวิเคราะห์จากล่างขึ้นบน LALR (Yacc / Bison) จากนั้นการเรียกซ้ำของทั้งสองข้างจะไม่เกิดปัญหา
::=
จากExpression
เป็นTerm
และถ้าคุณทำแบบเดียวกันหลังจากครั้งแรก||
มันจะไม่เหลือซ้ำอีกไหม? แต่ถ้าคุณทำหลังจาก::=
นั้นเท่านั้น แต่ไม่ใช่||
มันจะยังคงวนซ้ำอยู่ใช่ไหม?