ทำไม C ++ ไม่สามารถแยกวิเคราะห์ด้วยตัวแยกวิเคราะห์ LR (1)


153

ฉันอ่านเกี่ยวกับตัวแยกวิเคราะห์และตัวแยกวิเคราะห์และพบคำสั่งนี้ในการแยกวิเคราะห์ LR ของวิกิพีเดีย - หน้า:

ภาษาการเขียนโปรแกรมจำนวนมากสามารถแยกวิเคราะห์ได้โดยใช้รูปแบบของตัวแยกวิเคราะห์ LR ข้อยกเว้นหนึ่งที่น่าสังเกตคือ C ++

ทำไมถึงเป็นเช่นนั้น? มีคุณสมบัติพิเศษใดของ C ++ ที่ทำให้ไม่สามารถวิเคราะห์ด้วยตัวแยกวิเคราะห์ LR ได้

เมื่อใช้ google ฉันพบว่า C สามารถแยกวิเคราะห์ได้อย่างสมบูรณ์แบบด้วย LR (1) แต่ C ++ ต้องใช้ LR (∞)


7
เช่นเดียวกับ: คุณต้องเข้าใจการเรียกซ้ำเพื่อเรียนรู้การเรียกซ้ำ ;-)
Toon Krijthe

5
"คุณจะเข้าใจตัวแยกวิเคราะห์เมื่อคุณแยกวิเคราะห์วลีนี้"
ilya n

คำตอบ:


92

มีหัวข้อที่น่าสนใจในเป็นแลมบ์ดาสุดที่กล่าวถึงไวยากรณ์ LALR สำหรับ C ++

มันมีลิงค์ไปยังวิทยานิพนธ์ระดับปริญญาเอกที่มีการอภิปรายของการแยก C ++ ซึ่งระบุว่า:

"ไวยากรณ์ C ++ นั้นคลุมเครือขึ้นอยู่กับบริบทและอาจต้องใช้การมองไม่สิ้นสุดเพื่อแก้ไขความคลุมเครือ"

มันจะให้ตัวอย่างจำนวนมาก (ดูหน้า 147 ของ pdf)

ตัวอย่างคือ:

int(x), y, *const z;

ความหมาย

int x;
int y;
int *const z;

เปรียบเทียบกับ:

int(x), y, new int;

ความหมาย

(int(x)), (y), (new int));

(นิพจน์คั่นด้วยเครื่องหมายจุลภาค)

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


29
คงจะดีถ้ามีบทสรุปเกี่ยวกับหน้า 147 ในหน้านี้ ฉันจะอ่านหน้านั้น (+1)
ชื่นใจ

11
ตัวอย่างคือ: int (x), y, * const z; // ความหมาย: int x; int y; int * const z; (ลำดับของการประกาศ) int (x), y, int ใหม่; // ความหมาย: (int (x)), (y), (new int)); (นิพจน์คั่นด้วยเครื่องหมายจุลภาค) ลำดับโทเค็นทั้งสองมีการเริ่มต้นที่เหมือนกัน แต่แยกวิเคราะห์ต้นไม้ซึ่งขึ้นอยู่กับองค์ประกอบสุดท้าย อาจมีโทเค็นจำนวนมากโดยพลการก่อนที่จะปิดบังโทเค็น
Blaisorblade

6
ในบริบทนั้น∞หมายถึง "หลายคนโดยพลการ" เพราะ lookahead จะถูก จำกัด ด้วยความยาวของอินพุต
MauganRa

1
ฉันค่อนข้างงุนงงกับการอ้างอิงที่สกัดจากวิทยานิพนธ์ระดับปริญญาเอก หากมีความกำกวมจากนั้นตามคำนิยามไม่มี lookahead อาจ "แก้ไข" ความกำกวม (เช่นตัดสินใจว่าการแยกวิเคราะห์ที่ถูกต้องคืออะไร oen อย่างน้อย 2 parses ถือว่าถูกต้องโดยไวยากรณ์) ยิ่งกว่านั้นการกล่าวถึงความกำกวมของ C แต่คำอธิบายไม่ได้แสดงถึงความกำกวม แต่เป็นเพียงตัวอย่างที่ไม่ชัดเจนที่การตัดสินใจในการแยกวิเคราะห์สามารถทำได้หลังจากการมองไปข้างหน้าโดยพลการ
dodecaplex

231

ตัวแยกวิเคราะห์ LR ไม่สามารถจัดการกฎไวยากรณ์ที่คลุมเครือได้โดยการออกแบบ (ทำให้ทฤษฎีย้อนกลับไปง่ายขึ้นในทศวรรษ 1970 เมื่อความคิดต่าง ๆ ถูกนำมาใช้)

ทั้ง C และ C ++ อนุญาตให้ใช้คำสั่งต่อไปนี้:

x * y ;

มีการแยกวิเคราะห์สองแบบ:

  1. มันสามารถประกาศ y เป็นตัวชี้ไปยังพิมพ์ x
  2. มันสามารถคูณ x และ y ได้โดยละทิ้งคำตอบ

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

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

ดังนั้นการแยก LR บริสุทธิ์จึงไม่สามารถจัดการสิ่งนี้ได้ หรือตัวแยกวิเคราะห์อื่น ๆ ที่มีอยู่อย่างกว้างขวางเช่น Antlr, JavaCC, YACC หรือ Bison ดั้งเดิมหรือแม้แต่ตัวแยกวิเคราะห์สไตล์ PEG ที่ใช้ในวิธี "บริสุทธิ์"

มีกรณีที่ซับซ้อนมากขึ้น (ไวยากรณ์ของเทมเพลตการแยกวิเคราะห์ต้องใช้ lookahead โดยพลการในขณะที่ LALR (k) สามารถมองไปที่โทเค็นส่วนใหญ่ได้) แต่มีเพียงตัวอย่างเดียวเท่านั้นที่จะยิงLR บริสุทธิ์ (หรืออื่น ๆ )

ตัวแยกวิเคราะห์ C / C ++ ที่แท้จริงส่วนใหญ่จัดการตัวอย่างนี้โดยใช้ตัวแยกวิเคราะห์ที่กำหนดค่าบางชนิดที่มีการแฮ็กเพิ่มเติม: พวกมันรวมการแยกวิเคราะห์กับการรวบรวมตารางสัญลักษณ์ ... ดังนั้นเมื่อถึงเวลา "x" ตัวแยกวิเคราะห์รู้ว่า x เป็นประเภท หรือไม่และสามารถเลือกระหว่าง parses ที่มีศักยภาพสองแบบ แต่ตัวแยกวิเคราะห์ที่ทำสิ่งนี้ไม่ได้เป็นบริบทฟรีและตัวแยกวิเคราะห์ LR (อันที่จริง ฯลฯ ) เป็นบริบทที่ดีที่สุด

หนึ่งสามารถโกงและเพิ่มการตรวจสอบความหมายลดเวลาต่อกฎใน parsers LR เพื่อทำ disambiguation นี้ (รหัสนี้มักจะไม่ง่าย) ตัวแยกวิเคราะห์ชนิดอื่นส่วนใหญ่มีวิธีการเพิ่มการตรวจสอบความหมายที่จุดต่าง ๆ ในการแยกวิเคราะห์ซึ่งสามารถใช้ในการทำเช่นนี้ได้

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

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

เราใช้เทคนิคนี้ในส่วนหน้า C และ C ++ สำหรับ DMS Software Reengineering Tookit (ณ เดือนมิถุนายน 2560 สิ่งเหล่านี้จัดการ C ++ 17 เต็มในภาษา MS และ GNU) พวกมันถูกใช้เพื่อประมวลผลบรรทัดของระบบ C และ C ++ ขนาดใหญ่หลายล้านบรรทัดด้วยการแยกวิเคราะห์ที่สมบูรณ์และแม่นยำซึ่งสร้าง AST พร้อมรายละเอียดของซอร์สโค้ดอย่างสมบูรณ์ (ดูAST สำหรับการแยกวิเคราะห์ที่รำคาญที่สุดของ C ++ )


11
ในขณะที่ตัวอย่าง 'x * y' นั้นน่าสนใจสิ่งเดียวกันสามารถเกิดขึ้นได้ใน C ('y' อาจเป็น typedef หรือตัวแปร) แต่ C สามารถแยกวิเคราะห์โดย parser LR (1) ดังนั้นความแตกต่างกับ C ++ คืออะไร
35790 Martin Cote

12
ผู้ตอบของฉันสังเกตเห็นแล้วว่า C มีปัญหาเดียวกันฉันคิดว่าคุณพลาดไป ไม่มันไม่สามารถแยกวิเคราะห์โดย LR (1) ด้วยเหตุผลเดียวกัน เอ่อคุณหมายถึงอะไร 'y' สามารถพิมพ์ได้? บางทีคุณอาจหมายถึง 'x' นั่นไม่เปลี่ยนแปลงอะไรเลย
Ira Baxter

6
การแยกวิเคราะห์ 2 ไม่จำเป็นต้องโง่ใน C ++ เนื่องจาก * อาจถูกเขียนทับเพื่อให้มีผลข้างเคียง
Dour High Arch

8
ฉันดูx * yและหัวเราะเบา ๆ - มันน่าอัศจรรย์ที่เด็กน้อยคิดว่ามีความคลุมเครือเล็กน้อยเช่นนี้
new123456

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

16

ปัญหาไม่ได้ถูกนิยามเช่นนี้ในขณะที่มันน่าสนใจ:

ชุดเล็กที่สุดของการปรับเปลี่ยนไวยากรณ์ C ++ ที่จะจำเป็นเพื่อให้ไวยากรณ์ใหม่นี้สามารถแยกวิเคราะห์ได้อย่างสมบูรณ์แบบโดย parser yacc "ไม่ใช่บริบทที่ไม่มีบริบท" คืออะไร? (การใช้เพียงหนึ่ง 'แฮ็ค': ความผิดเพี้ยนของตัวพิมพ์ / ตัวระบุตัวแยกวิเคราะห์แจ้ง lexer ของ typedef / class / struct ทุกตัว)

ฉันเห็นไม่กี่คน:

  1. Type Type;เป็นสิ่งต้องห้าม ตัวระบุที่ประกาศเป็นชื่อพิมพ์ต้องไม่เป็นตัวระบุที่ไม่ใช่ชื่อ (หมายเหตุที่struct Type Typeไม่ชัดเจนและอาจได้รับอนุญาต)

    มี 3 ประเภท names tokens :

    • types : builtin-type หรือเพราะ typedef / class / struct
    • แม่แบบฟังก์ชั่น
    • ตัวระบุ: ฟังก์ชั่น / วิธีการและตัวแปร / วัตถุ

    พิจารณาเทมเพลตฟังก์ชั่นเป็นโทเค็นที่แตกต่างกันแก้ปัญหาfunc<ความกำกวม ถ้าfuncเป็นชื่อฟังก์ชั่นเทมเพลต<จะต้องเป็นจุดเริ่มต้นของรายการพารามิเตอร์เทมเพลตมิฉะนั้นfuncจะเป็นตัวชี้ฟังก์ชั่นและ<เป็นตัวดำเนินการเปรียบเทียบ

  2. Type a(2);คือการสร้างอินสแตนซ์ของวัตถุ Type a();และType a(int)เป็นฟังก์ชั่นต้นแบบ

  3. int (k); เป็นสิ่งต้องห้ามอย่างสมบูรณ์ควรจะเขียน int k;

  4. typedef int func_type(); และ typedef int (func_type)();เป็นสิ่งต้องห้าม

    ฟังก์ชั่น typedef จะต้องเป็นตัวชี้ฟังก์ชั่น typedef: typedef int (*func_ptr_type)();

  5. การเรียกซ้ำแม่แบบถูก จำกัด ไว้ที่ 1024 มิฉะนั้นจำนวนสูงสุดที่เพิ่มขึ้นอาจถูกส่งผ่านเป็นตัวเลือกสำหรับคอมไพเลอร์

  6. int a,b,c[9],*d,(*f)(), (*g)()[9], h(char); อาจถูกห้ามเช่นกันแทนที่ด้วย int a,b,c[9],*d; int (*f)();

    int (*g)()[9];

    int h(char);

    หนึ่งบรรทัดต่อฟังก์ชันต้นแบบหรือการประกาศฟังก์ชันพอยน์เตอร์

    ทางเลือกที่ต้องการอย่างมากคือการเปลี่ยนไวยากรณ์ตัวชี้ฟังก์ชันอันยิ่งใหญ่

    int (MyClass::*MethodPtr)(char*);

    ถูก resyntaxed เป็น:

    int (MyClass::*)(char*) MethodPtr;

    สิ่งนี้สอดคล้องกับตัวดำเนินการร่าย (int (MyClass::*)(char*))

  7. typedef int type, *type_ptr; อาจถูกห้ามด้วยเช่นกัน: หนึ่งบรรทัดต่อ typedef ดังนั้นมันจะกลายเป็น

    typedef int type;

    typedef int *type_ptr;

  8. sizeof int, sizeof char, sizeof long longและร่วม สามารถประกาศในแต่ละไฟล์ต้นฉบับ ดังนั้นไฟล์ต้นฉบับแต่ละไฟล์ที่ใช้งานintควรเริ่มต้นด้วย

    #type int : signed_integer(4)

    และunsigned_integer(4)จะเป็นสิ่งต้องห้ามนอก#type คำสั่งนี้จะเป็นขั้นตอนใหญ่ในsizeof intความคลุมเครือโง่อยู่ในส่วนหัว C ++ มากมาย

คอมไพเลอร์การใช้ C ++ จะ resyntaxed ถ้าต้องเผชิญหน้ากับ c ++ แหล่งที่มาการใช้ไวยากรณ์คลุมเครือย้ายsource.cppเกินไปambiguous_syntaxโฟลเดอร์และจะสร้างโดยอัตโนมัติโปร่งใสแปลsource.cppก่อนที่จะรวบรวม

โปรดเพิ่มไวยากรณ์ C ++ ที่ไม่ชัดเจนของคุณหากคุณรู้!


3
c ++ ยึดที่ดีเกินไป ไม่มีใครทำเช่นนี้ในทางปฏิบัติ คนเหล่านั้น (เช่นเรา) ที่สร้างส่วนหน้าเพียงแค่กัดกระสุนและทำวิศวกรรมเพื่อให้ตัวแยกวิเคราะห์ทำงาน และตราบใดที่เทมเพลตมีอยู่ในภาษาคุณจะไม่ได้รับการแยกวิเคราะห์บริบทอย่างแท้จริง
Ira Baxter

9

ในขณะที่คุณสามารถมองเห็นในของฉันคำตอบที่นี่ , C ++ มีไวยากรณ์ที่ไม่สามารถแยกวิเคราะห์ deterministically โดย LL หรือ LR parser เนื่องจากขั้นตอนความละเอียดประเภท (โดยปกติการโพสต์แยก) การเปลี่ยนแปลงคำสั่งของการดำเนินงานและดังนั้นจึงรูปร่างพื้นฐานของ AST (ที่ โดยทั่วไปคาดว่าจะได้รับการแยกวิเคราะห์ขั้นตอนแรก)


3
การแยกวิเคราะห์เทคโนโลยีที่จัดการกับความคลุมเครือเพียงแค่สร้างตัวแปร AST ทั้งสองขณะแยกวิเคราะห์และกำจัดสิ่งที่ไม่ถูกต้องโดยขึ้นอยู่กับข้อมูลประเภท
Ira Baxter

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

"GLL"? แน่นอนว่าคุณจะต้องเข้าใจทฤษฎีและเขียนบทความเพื่อใช้ส่วนที่เหลือ มีโอกาสมากขึ้นที่คุณสามารถใช้ตัวแยกวิเคราะห์ที่เข้ารหัสจากบนลงล่างหรือตัวแยกวิเคราะห์ LALR () ตัวแยกสัญญาณย้อนกลับ (แต่เก็บตัวแยกวิเคราะห์ "ปฏิเสธ") หรือเรียกใช้ตัวแยกวิเคราะห์ Earley GLR มีข้อได้เปรียบในการเป็นทางออกที่ดีงามมีเอกสารและตอนนี้ได้รับการพิสูจน์แล้ว เทคโนโลยี GLL จะต้องมีข้อดีที่สำคัญพอสมควรในการแสดง GLR
Ira Baxter

โครงการ Rascal (เนเธอร์แลนด์) อ้างว่าพวกเขากำลังสร้างตัวแยกวิเคราะห์ GLL ที่ใช้สแกนเนอร์ กำลังดำเนินการอยู่อาจหาข้อมูลออนไลน์ได้ยาก en.wikipedia.org/wiki/RascalMPL
Ira Baxter

@IraBaxter ดูเหมือนว่าจะมีการพัฒนาใหม่เกี่ยวกับ GLL: ดูบทความ 2010 เกี่ยวกับ GLL dotat.at/tmp/gll.pdf
Sjoerd

6

ฉันคิดว่าคุณค่อนข้างใกล้กับคำตอบ

LR (1) หมายความว่าการแยกวิเคราะห์จากซ้ายไปขวาต้องการเพียงโทเค็นเดียวที่จะมองไปข้างหน้าสำหรับบริบทในขณะที่ LR (∞) หมายถึงการมองที่ไม่สิ้นสุด นั่นคือ parser จะต้องรู้ทุกอย่างที่มาเพื่อคิดออกตอนนี้


4
ฉันจำได้จากคลาสคอมไพเลอร์ของฉันที่ LR (n) สำหรับ n> 0 สามารถลดได้ทางคณิตศาสตร์เป็น LR (1) นั่นไม่เป็นความจริงสำหรับ n = อนันต์ใช่ไหม
rmeador

14
ไม่มีภูเขาที่ไม่สามารถใช้ได้ซึ่งมีความแตกต่างระหว่าง n และอินฟินิตี้
ephemient

4
ไม่ใช่คำตอบ: ใช่ให้เวลาไม่ จำกัด ? :)
Steve Fallows

7
ที่จริงแล้วโดยความทรงจำที่คลุมเครือของฉันเกี่ยวกับวิธีการที่ LR (n) -> LR (1) เกิดขึ้นมันเกี่ยวข้องกับการสร้างสถานะตัวกลางใหม่ดังนั้น runtime จึงเป็นฟังก์ชันที่ไม่คงที่ของ 'n' การแปล LR (inf) -> LR (1) จะใช้เวลาไม่ จำกัด
แอรอน

5
"ไม่ใช่คำตอบ: ใช่ให้เวลาไม่ จำกัด " - ไม่: วลี 'ที่ให้เวลาไม่ จำกัด ' เป็นเพียงวิธีการพูดสั้น ๆ ที่ไม่ไวต่อความรู้สึกในการพูดว่า "ไม่สามารถทำได้ในเวลาที่ จำกัด " เมื่อคุณเห็น "ไม่มีที่สิ้นสุด" ให้คิดว่า: "ไม่ จำกัด "
ChrisW

4

ปัญหา "typedef" ใน C ++ สามารถแยกวิเคราะห์ด้วยตัวแยกวิเคราะห์ LALR (1) ที่สร้างตารางสัญลักษณ์ขณะแยกวิเคราะห์ (ไม่ใช่ตัวแยกวิเคราะห์ LALR บริสุทธิ์) ปัญหา "เทมเพลต" อาจไม่สามารถแก้ไขได้ด้วยวิธีนี้ ข้อดีของ LALR (1) parser ประเภทนี้คือไวยากรณ์ (แสดงด้านล่าง) คือ LALR (1) ไวยากรณ์ (ไม่มีความกำกวม)

/* C Typedef Solution. */

/* Terminal Declarations. */

   <identifier> => lookup();  /* Symbol table lookup. */

/* Rules. */

   Goal        -> [Declaration]... <eof>               +> goal_

   Declaration -> Type... VarList ';'                  +> decl_
               -> typedef Type... TypeVarList ';'      +> typedecl_

   VarList     -> Var /','...     
   TypeVarList -> TypeVar /','...

   Var         -> [Ptr]... Identifier 
   TypeVar     -> [Ptr]... TypeIdentifier                               

   Identifier     -> <identifier>       +> identifier_(1)      
   TypeIdentifier -> <identifier>      =+> typedefidentifier_(1,{typedef})

// The above line will assign {typedef} to the <identifier>,  
// because {typedef} is the second argument of the action typeidentifier_(). 
// This handles the context-sensitive feature of the C++ language.

   Ptr          -> '*'                  +> ptr_

   Type         -> char                 +> type_(1)
                -> int                  +> type_(1)
                -> short                +> type_(1)
                -> unsigned             +> type_(1)
                -> {typedef}            +> type_(1)

/* End Of Grammar. */

อินพุตต่อไปนี้สามารถแยกวิเคราะห์ได้โดยไม่มีปัญหา:

 typedef int x;
 x * y;

 typedef unsigned int uint, *uintptr;
 uint    a, b, c;
 uintptr p, q, r;

เครื่องมือสร้างตัวแยกวิเคราะห์ LRSTARอ่านสัญลักษณ์ไวยากรณ์ข้างต้นและสร้างตัวแยกวิเคราะห์ที่จัดการปัญหา "typedef" โดยไม่ต้องมีความกำกวมในการแยกวิเคราะห์ต้นไม้หรือ AST (การเปิดเผยข้อมูล: ฉันเป็นคนที่สร้าง LRSTAR)


นั่นคือการแฮ็กมาตรฐานที่ GCC ใช้กับ parser LR เดิมเพื่อจัดการกับความคลุมเครือของสิ่งต่าง ๆ เช่น "x * y;" อนิจจายังคงมีความต้องการ lookahead ขนาดใหญ่โดยพลการในการแยกวิเคราะห์การก่อสร้างอื่น ๆ ดังนั้น LR (k) จึงไม่สามารถแก้ปัญหาใด ๆ ที่คงที่ k (GCC เปลี่ยนเป็นการสืบเชื้อสายแบบเรียกซ้ำพร้อมกับการใส่โฆษณามากขึ้น)
Ira Baxter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.