คอมไพเลอร์กู้คืนจากข้อผิดพลาดประเภทได้อย่างไร


10

ฉันได้อ่านบทความบทความและส่วน 4.1.4 ตอนที่ 4 ของคอมไพเลอร์: หลักการเทคนิคและเครื่องมือ (รุ่นที่ 2) (หรือที่รู้จักกันในชื่อ "The Dragon Book") ซึ่งทุกคนกล่าวถึงหัวข้อการกู้คืนข้อผิดพลาดทางไวยากรณ์ อย่างไรก็ตามหลังจากทดลองกับคอมไพเลอร์สมัยใหม่หลาย ๆ ตัวฉันได้เห็นแล้วว่าพวกเขากู้คืนจากข้อผิดพลาดทางความหมายเช่นเดียวกับข้อผิดพลาดทางไวยากรณ์

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

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

1 / (2 * (3 + "4"))

คอมไพเลอร์จะสร้างแผนผังไวยากรณ์นามธรรมต่อไปนี้:

      op(/)
        |
     -------
    /       \ 
 int(1)    op(*)
             |
          -------
         /       \
       int(2)   op(+)
                  |
               -------
              /       \
           int(3)   str(4)

ขั้นตอนการสร้างรหัสจะใช้รูปแบบผู้เยี่ยมชมเพื่อสำรวจทรีไวยากรณ์แบบนามธรรมซ้ำ ๆ และดำเนินการตรวจสอบชนิด ต้นไม้ไวยากรณ์นามธรรมจะถูก traversed จนกว่าคอมไพเลอร์มาถึงส่วนด้านในสุดของการแสดงออก; (3 + "4"). คอมไพเลอร์ตรวจสอบแต่ละด้านของนิพจน์และเห็นว่าพวกเขาไม่เท่าเทียมกันทางความหมาย คอมไพเลอร์ทำให้เกิดข้อผิดพลาดประเภท ที่นี่ปัญหาอยู่ที่ไหน คอมไพเลอร์ควรทำอย่างไร?

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

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

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


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

คำตอบ:


8

แนวคิดที่เสนอของคุณนั้นถูกต้องแล้ว

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


3

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

  • การดำเนินการใด ๆ ที่ดำเนินการสำเร็จ (เพื่อป้องกันการเรียงซ้อนของข้อความแสดงข้อผิดพลาดทั้งหมดที่เกิดจากความผิดพลาดเดิม)
  • ผลลัพธ์ของการดำเนินการใด ๆ ที่ดำเนินการบนวัตถุที่มีประเภทข้อผิดพลาดก็มีประเภทข้อผิดพลาด
  • หากประเภทข้อผิดพลาดเกิดขึ้นจนถึงการสร้างรหัสเครื่องกำเนิดรหัสจะตรวจสอบการใช้งานและสร้างรหัสที่ล้มเหลว (เช่นการส่งข้อยกเว้นการยกเลิกหรือสิ่งอื่นใดที่เหมาะสมกับภาษาของคุณ)

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


ขอบคุณสำหรับคำตอบจูลส์ ตลกพอนี่เป็นวิธีที่แน่นอนที่ฉันใช้ จิตใจที่ดีคิดเหมือนกันใช่มั้ย ;-)
Christian Dean

2

หากมีข้อผิดพลาดทางความหมายข้อความแสดงข้อผิดพลาดในการคอมไพล์บ่งชี้ว่ามีการออกให้แก่ผู้ใช้

เมื่อดำเนินการเสร็จแล้วคุณสามารถยกเลิกการคอมไพล์ได้เนื่องจากโปรแกรมอินพุตมีข้อผิดพลาด - ไม่ใช่โปรแกรมกฎหมายในภาษาดังนั้นจึงสามารถถูกปฏิเสธได้

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

ตัวอย่างเช่นมันสามารถยกเลิกการวิเคราะห์ประเภทใด ๆ เพิ่มเติมสำหรับแผนภูมินิพจน์ปัจจุบันและดำเนินการประมวลผลนิพจน์จากคำสั่งที่ตามมา


2

สมมติว่าภาษาของคุณอนุญาตให้เพิ่มจำนวนเต็มและอนุญาตให้มีการต่อสตริงกับตัว+ดำเนินการ

เนื่องจากint + stringไม่ได้รับอนุญาตการประเมิน+จะส่งผลให้เกิดข้อผิดพลาดในการรายงาน คอมไพเลอร์สามารถกลับมาerrorเป็นประเภท หรือมันอาจจะฉลาดมากขึ้นเนื่องจากint + int -> intและstring + string -> stringได้รับอนุญาตก็อาจจะกลับข้อผิดพลาด "อาจจะเป็น int หรือสตริง"

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


ฉันคิดว่าฉันทำตามที่คุณ, @gnasher แต่สิ่งที่คุณหมายถึงโดย"" ผู้ประกอบ ? เป็นคำที่พิมพ์ผิดใช่ไหม
Christian Dean

@ChristianDean มีเครื่องหมายดอกจันในเครื่องหมายคำพูดซึ่งถูกตีความว่าเป็นมาร์กอัปมาร์กอัปแทนที่จะเป็นเรนเดอร์
JakeRobb

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