เพียงเพื่อระบุปัญหาปัญหา Dangling Else นั้นเป็นความกำกวมในการกำหนดไวยากรณ์ของรหัสซึ่งอาจไม่ชัดเจนในกรณีของ ifs และ elses ถัดไปซึ่งเป็นของอื่น
ตัวอย่างที่ง่ายและคลาสสิค:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
มันไม่ชัดเจนสำหรับผู้ที่ไม่ทราบรายละเอียดของข้อกำหนดภาษาด้วยใจซึ่งif
ได้รับelse
(และข้อมูลโค้ดเฉพาะนี้ใช้ได้ในครึ่งโหลภาษา แต่อาจทำงานแตกต่างกันในแต่ละภาษา)
โครงสร้าง Dangling Else ก่อให้เกิดปัญหาที่อาจเกิดขึ้นสำหรับการใช้งานตัวแยกวิเคราะห์แบบไม่ใช้สแกนเนอร์เนื่องจากกลยุทธ์คือการสตรีมไฟล์ทีละตัวอักษรทีละตัวจนกว่าตัวแยกวิเคราะห์จะเห็นว่ามันเพียงพอที่จะทำเครื่องหมาย (แยกย่อยเป็นภาษาแอสเซมบลี . สิ่งนี้อนุญาตให้ parser รักษาสถานะน้อยที่สุด ทันทีที่มันคิดว่ามีข้อมูลเพียงพอที่จะเขียนโทเค็นที่มีการแยกวิเคราะห์ไฟล์มันจะทำเช่นนั้น นั่นคือเป้าหมายสุดท้ายของเครื่องมือแยกวิเคราะห์แบบสแกนเนอร์ การรวบรวมที่รวดเร็วง่ายและมีน้ำหนักเบา
สมมติว่าบรรทัดใหม่และช่องว่างก่อนหรือหลังเครื่องหมายวรรคตอนไม่มีความหมาย (เนื่องจากเป็นภาษา C-style ส่วนใหญ่) ข้อความนี้จะปรากฏต่อคอมไพเลอร์เป็น:
if(conditionA)if(conditionB)doFoo();else doBar;
แยกวิเคราะห์คอมพิวเตอร์ได้อย่างสมบูรณ์แบบดังนั้นมาดูกัน ฉันจะได้รับตัวละครทีละตัวจนกว่าฉันจะได้:
if(conditionA)
โอ้ฉันรู้ว่าสิ่งที่หมายถึง (ใน C #) มันหมายถึง " push
conditionA บน eval stack และจากนั้นโทรbrfalse
เพื่อข้ามไปยังคำสั่งหลังจากเซมิโคลอนถัดไปถ้ามันไม่จริง" ตอนนี้ฉันไม่เห็นเครื่องหมายอัฒภาคดังนั้นตอนนี้ฉันจะตั้งค่าการข้ามของฉันไปที่ช่องว่างถัดไปหลังจากคำแนะนำนี้และฉันจะเพิ่มออฟเซ็ตที่ฉันใส่คำแนะนำเพิ่มเติมจนกว่าฉันจะเห็นเครื่องหมายอัฒภาค กำลังดำเนินการแยกวิเคราะห์ ...
if(conditionB)
ตกลงนี่แยกวิเคราะห์การดำเนินการของ IL ที่คล้ายกันและมันจะไปทันทีหลังจากคำสั่งที่ฉันแยกวิเคราะห์ ฉันไม่เห็นเครื่องหมายอัฒภาคดังนั้นฉันจะเพิ่มการชดเชยข้ามของคำแถลงก่อนหน้านี้ตามความยาวของสองคำสั่งของฉัน
doFoo();
ตกลงนั่นเป็นเรื่องง่าย นั่นคือ " call
doFoo" และนั่นคืออัฒภาคที่ฉันเห็นหรือไม่ นั่นเยี่ยมมากนั่นคือจุดสิ้นสุดของเส้น ฉันจะเพิ่มการกระโดดข้ามบล็อกของฉันทั้งสองตามความยาวของคำสั่งทั้งสองนี้และลืมว่าฉันเคยใส่ใจ ตกลงย้ายที่ ...
else
... เอ่อโอ้. มันไม่ง่ายอย่างที่คิด ตกลงฉันลืมสิ่งที่ฉันเพิ่งทำ แต่else
วิธีการที่มีคำสั่งหยุดพักตามเงื่อนไขบางแห่งที่ฉันได้เห็นแล้วดังนั้นให้ฉันดูกลับ ... อ๋อมีนั่นคือbrfalse
ทันทีหลังจากที่ฉันกด "เงื่อนไข B" บน กองอะไรก็ตามที่เป็น ตกลงตอนนี้ฉันต้องการเงื่อนไขที่ไม่มีเงื่อนไขbreak
เป็นคำสั่งต่อไป คำแถลงที่จะตามมาในตอนนี้คือเป้าหมายการหยุดพักตามเงื่อนไขของฉันอย่างแน่นอนดังนั้นฉันจะตรวจสอบให้แน่ใจว่าฉันมีสิทธิ์แล้วและฉันจะเพิ่มการหยุดพักแบบไม่มีเงื่อนไขที่ฉันวางไว้
doBar();
ง่ายมาก " call
doBar" และมีเครื่องหมายอัฒภาคและฉันไม่เคยเห็นวงเล็บปีกกาใด ๆ ดังนั้นเงื่อนไขที่ไม่มีเงื่อนไขbreak
ควรข้ามไปยังข้อความถัดไปไม่ว่าจะเป็นอะไรและฉันสามารถลืมได้ว่าฉันเคยใส่ใจ
ดังนั้นเรามีอะไร ... (หมายเหตุ: มันคือ 10:00 PM และฉันไม่รู้สึกอยากแปลง bit offsets เป็นเลขฐานสิบหกหรือเติมเปลือก IL แบบเต็มของฟังก์ชั่นด้วยคำสั่งเหล่านี้ดังนั้นนี่คือหลอกหลอก-IL ใช้หมายเลขบรรทัดโดยปกติจะมีออฟเซ็ตไบต์):
ldarg.1 //conditionA
brfalse <line 6> //jumps to "break"
ldarg.2 //conditionB
brfalse <line 7> //jumps to "call doBar"
call doFoo
break <line 8> //jumps beyond statement in scope
call doBar
<line 8 is here>
ดีที่ดำเนินการอย่างถูกต้องจริงถ้ากฎ (ในขณะที่ส่วนใหญ่ภาษา C-style) คือการที่จะไปกับที่อยู่ใกล้ที่สุดelse
if
เยื้องเพื่อติดตามการซ้อนการดำเนินการมันจะดำเนินการเช่นนี้โดยที่ถ้าเงื่อนไข A เป็นเท็จส่วนที่เหลือทั้งหมดของตัวอย่างจะถูกข้าม:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
... แต่มันทำโดยบังเอิญเพราะตัวแบ่งที่เกี่ยวข้องกับif
คำสั่งด้านนอกกระโดดไปที่break
คำสั่งในตอนท้ายของด้านใน if
ซึ่งจะใช้ตัวชี้การดำเนินการเกินคำสั่งทั้งหมด มันเป็นการกระโดดที่ไม่จำเป็นเป็นพิเศษและถ้าตัวอย่างนี้มีความซับซ้อนมากขึ้นมันอาจไม่ทำงานอีกต่อไปถ้าแยกวิเคราะห์และโทเค็นด้วยวิธีนี้
นอกจากนี้ถ้าข้อมูลจำเพาะเกี่ยวกับภาษาบอกว่าห้อยelse
เป็นของแรกif
และถ้า conditionA เป็นเท็จ doBar จะถูกดำเนินการในขณะที่ถ้าเงื่อนไข A เป็นจริง แต่ไม่ใช่เงื่อนไข B ก็ไม่มีอะไรเกิดขึ้นเช่นนั้น?
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
ตัวแยกวิเคราะห์ได้ลืมสิ่งแรกที่if
เคยมีอยู่ดังนั้นอัลกอริธึมตัวแยกวิเคราะห์แบบง่ายนี้จะไม่สร้างรหัสที่ถูกต้อง
ตอนนี้ parser อาจฉลาดพอที่จะจดจำif
s และelse
s ได้เป็นเวลานาน แต่ถ้า spec ภาษาบอกว่าelse
หลังจากif
จับคู่สองครั้งเดียวกับครั้งแรกif
ที่ทำให้เกิดปัญหากับสองif
s กับการจับคู่else
s:
if(conditionA)
if(conditionB)
doFoo();
else
doBar();
else
doBaz();
ตัวแยกวิเคราะห์จะเห็นอันแรกelse
จับคู่กับตัวแรกif
จากนั้นดูอันที่สองและเข้าสู่โหมดตื่นตระหนก "สิ่งที่ฉันทำอีกครั้ง" เมื่อมาถึงจุดนี้ parser มีรหัสค่อนข้างมากในสถานะที่ไม่แน่นอนว่ามันจะค่อนข้างผลักออกไปยัง filestream เอาท์พุทแล้ว
มีวิธีแก้ไขปัญหาเหล่านี้และสิ่งที่ควรทำ แต่รหัสที่จำเป็นต้องเป็นสมาร์ทจะเพิ่มความซับซ้อนของอัลกอริธึม parser หรือสเป็คภาษาที่อนุญาตให้ parser เป็น dumb นี้เพิ่มความฟุ่มเฟื่อยของซอร์สโค้ดภาษาเช่นโดยต้องการคำสั่งสิ้นสุดเช่นend if
หรือวงเล็บแสดง nested บล็อกถ้าif
ข้อความมีelse
(ทั้งสองอย่างซึ่งมักจะเห็นในรูปแบบภาษาอื่น ๆ )
นี่เป็นเพียงตัวอย่างง่าย ๆ ของข้อความสองสามif
ประโยคและดูการตัดสินใจทั้งหมดที่คอมไพเลอร์ต้องทำ นี่คือรายละเอียดที่อยู่เบื้องหลังคำกล่าวที่ไม่น่ากลัวจาก Wikipedia ในคำถามของคุณ