lexers เทียบกับ parsers


308

lexers และ parsers แตกต่างกันจริงๆในทางทฤษฎีหรือไม่

ดูเหมือนว่าแฟชั่นจะเกลียดการแสดงออกปกติ: การเข้ารหัสสยองขวัญ , บล็อกโพสต์อีก

อย่างไรก็ตามเครื่องมือที่ได้รับความนิยมจาก lexing: pygments , geshiหรือprettifyทั้งหมดใช้การแสดงออกปกติ พวกเขาดูเหมือนจะเป็นอะไรที่ ...

เมื่อ lexing เพียงพอคุณต้องการ EBNF เมื่อใด

มีใครบ้างที่ใช้โทเค็นที่ผลิตโดย lexers เหล่านี้กับเครื่องกำเนิดไฟฟ้าตัวแยกวิเคราะห์กระทิงหรือ antlr


2
ใช่. ฉันกำลังพยายามแยกออโต้คีย์ ฉันสามารถสร้างปากกาเน้นข้อความที่ใช้ pygments ได้อย่างรวดเร็ว แต่ antlr ใช้เวลานานกว่านี้ ... ฉันไม่ได้เห็นการผสมเกสรข้ามกันมากระหว่างเครื่องมือทั้งสอง
Naveen

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

2
ฉันเริ่มที่จะรับความเร็วด้วย antlr ขอบคุณ lexing มากมายไม่มีบริบทและบางครั้งก็ขึ้นอยู่กับบริบทด้วย
Naveen

1
แง่มุมพื้นฐานหนึ่งของปัญหา lexer กับ parser คือ lexers อิงตามขอบเขตของออโตมาตา (FSA) หรือตัวแปลงสัญญาณ จำกัด ที่แม่นยำกว่า (FST) การแยกวิเคราะห์แบบแผนส่วนใหญ่ (ไม่ใช่แค่บริบทฟรี) จะถูกปิดภายใต้จุดตัดด้วย FSA หรือการใช้ FST ดังนั้นการใช้ formnalism แบบนิพจน์ปกติที่ง่ายขึ้นสำหรับ lexer จะไม่เพิ่มความซับซ้อนของโครงสร้างวากยสัมพันธ์ของพิธีการ parser ที่ซับซ้อนมากขึ้น นี่เป็นปัญหาแบบแยกส่วนที่สำคัญอย่างยิ่งเมื่อกำหนดโครงสร้างและความหมายของภาษาโดยไม่สนใจคำตอบที่ได้รับการโหวตอย่างมีความสุข
Babou

ควรสังเกตว่า lexers และ parsers ไม่จำเป็นต้องแตกต่างกันเช่นLLLPGและ ANTLR รุ่นก่อนหน้าใช้ระบบแยกวิเคราะห์ LL (k) เดียวกันสำหรับ lexers และ parsers ความแตกต่างที่สำคัญคือ regexes มักจะเพียงพอสำหรับ lexers แต่ไม่ parsers
Qwertie

คำตอบ:


475

parsers และ lexers มีอะไรที่เหมือนกัน:

  1. พวกเขาอ่านสัญลักษณ์ของตัวอักษรจากอินพุต

    • คำแนะนำ: ตัวอักษรไม่จำเป็นต้องเป็นตัวอักษร แต่จะต้องเป็นสัญลักษณ์ที่เป็นอะตอมมิกสำหรับภาษาที่ parser / lexer เข้าใจ
    • สัญลักษณ์สำหรับตัวเล็ก: ตัวอักษร ASCII
    • สัญลักษณ์สำหรับ parser: โทเค็นเฉพาะซึ่งเป็นสัญลักษณ์เทอร์มินัลของไวยากรณ์
  2. พวกเขาวิเคราะห์สัญลักษณ์เหล่านี้และพยายามจับคู่กับไวยากรณ์ของภาษาที่พวกเขาเข้าใจ

    • ความแตกต่างที่แท้จริงมักจะอยู่ที่นี่ ดูด้านล่างสำหรับเพิ่มเติม
    • ไวยากรณ์เข้าใจโดย lexers: ไวยากรณ์ปกติ (Chomsky ระดับ 3)
    • ไวยากรณ์เข้าใจโดย parsers: ไวยากรณ์ที่ไม่มีบริบท (Chomsky ระดับ 2)
  3. พวกเขาแนบความหมาย (ความหมาย) กับชิ้นส่วนของภาษาที่พบ

    • lexers แนบความหมายโดยการจำแนกlexemes (สตริงของสัญลักษณ์จาก input) เช่นโดยเฉพาะอย่างยิ่งราชสกุล เช่นทุก lexemes เหล่านี้: *, ==, <=, ^จะถูกจัดเป็น "ผู้ประกอบการ" token โดย C / C ++ lexer
    • Parsers แนบความหมายโดยการจำแนกสายของสัญญาณจากอินพุท (ประโยค) โดยเฉพาะอย่างยิ่งเป็นnonterminalsและสร้างต้นไม้แยก ทั้งหมดเหล่านี้เช่นสายโทเค็น: [number][operator][number], [id][operator][id], [id][operator][number][operator][number]จะถูกจัดเป็น "แสดงออก" nonterminal โดย C / C ++ parser
  4. พวกเขาสามารถแนบความหมายเพิ่มเติม (ข้อมูล) กับองค์ประกอบที่รู้จัก

    • เมื่อ lexer รู้จักลำดับอักขระที่ประกอบด้วยตัวเลขที่เหมาะสมมันสามารถแปลงเป็นค่าไบนารีและจัดเก็บด้วยโทเค็น "number"
    • ในทำนองเดียวกันเมื่อ parser รู้จักการแสดงออกก็สามารถคำนวณค่าและจัดเก็บด้วยโหนด "การแสดงออก" ของต้นไม้ไวยากรณ์
  5. พวกเขาสร้างประโยคที่เหมาะสมสำหรับภาษาที่พวกเขาจำได้

    • Lexers สร้างโทเค็นซึ่งเป็นประโยคของภาษาปกติที่รู้จัก แต่ละโทเค็นสามารถมีไวยากรณ์ภายใน (แม้ว่าระดับ 3 ไม่ใช่ระดับ 2) แต่นั่นไม่สำคัญสำหรับข้อมูลเอาต์พุตและสำหรับหนึ่งที่อ่านพวกเขา
    • ตัวแยกวิเคราะห์สร้างแผนภูมิไวยากรณ์ซึ่งเป็นตัวแทนของประโยคของภาษาที่ไม่ใช้บริบทที่พวกเขารู้จัก โดยปกติจะเป็นต้นไม้ใหญ่เพียงต้นเดียวสำหรับทั้งเอกสาร / ไฟล์ต้นฉบับเนื่องจากทั้งเอกสาร / ไฟล์ต้นฉบับเป็นประโยคที่เหมาะสมสำหรับพวกเขา แต่ไม่มีเหตุผลใดที่ parser ไม่สามารถสร้างชุดของต้นไม้ไวยากรณ์ในผลลัพธ์ของมัน เช่นมันอาจเป็น parser ซึ่งรู้จักแท็ก SGML ที่ติดอยู่ในข้อความธรรมดา ดังนั้นมันจะtokenizeเอกสาร SGML [TXT][TAG][TAG][TXT][TAG][TXT]...เข้าไปในชุดของราชสกุลนี้:

อย่างที่คุณเห็น parsers และ tokenizers มีเหมือนกันมาก ตัวแยกวิเคราะห์หนึ่งตัวสามารถเป็น tokenizer สำหรับตัวแยกวิเคราะห์อื่นซึ่งอ่านโทเค็นอินพุตเป็นสัญลักษณ์จากตัวอักษรของมันเอง (โทเค็นเป็นเพียงสัญลักษณ์ของตัวอักษรบางตัว) ในลักษณะเดียวกับที่ประโยคจากภาษาหนึ่งสามารถเป็นสัญลักษณ์ตัวอักษร ภาษา. ตัวอย่างเช่นถ้า*และ-เป็นสัญลักษณ์ของตัวอักษรM(ในฐานะ "สัญลักษณ์รหัสมอร์ส") คุณสามารถสร้างโปรแกรมแยกวิเคราะห์ซึ่งรับรู้สตริงของจุดและบรรทัดเหล่านี้เป็นตัวอักษรที่เข้ารหัสในรหัสมอร์ส ประโยคในภาษา "รหัสมอร์ส" อาจเป็นโทเค็นสำหรับตัวแยกวิเคราะห์อื่นซึ่งโทเค็นเหล่านี้เป็นสัญลักษณ์อะตอมมิกของภาษา (เช่น "คำภาษาอังกฤษ") และ "คำศัพท์ภาษาอังกฤษ" เหล่านี้อาจเป็นโทเค็น (สัญลักษณ์ของตัวอักษร) สำหรับตัวแยกวิเคราะห์ระดับสูงกว่าซึ่งเข้าใจภาษา "ประโยคภาษาอังกฤษ" และทุกภาษาเหล่านี้แตกต่างกันเพียง แต่ในความซับซ้อนของไวยากรณ์ ไม่มีอะไรเพิ่มเติม

แล้วทั้งหมดเกี่ยวกับ "ระดับไวยากรณ์ของ Chomsky" เหล่านี้คืออะไร? อืมชัมสกีจัดกลุ่มไวยากรณ์เป็นสี่ระดับขึ้นอยู่กับความซับซ้อนของพวกเขา:

  • ระดับ 3: ไวยากรณ์ปกติ

    พวกเขาใช้การแสดงออกปกตินั่นคือพวกเขาสามารถมีเพียงสัญลักษณ์ของตัวอักษร ( a, b) concatenations ของพวกเขา ( ab, aba, bbbETD.) หรือทางเลือก (เช่นa|b)
    พวกเขาสามารถนำมาใช้ในฐานะ จำกัด ออโตมาตะ (FSA) เช่น NFA (Nondeterministic จำกัด เครื่องจักรอัตโนมัติ) หรือดีกว่า DFA (กำหนดแน่นอน จำกัด เครื่องจักร)
    ไวยากรณ์ปกติไม่สามารถจัดการกับไวยากรณ์ที่ซ้อนกันได้เช่นวงเล็บซ้อน / จับคู่อย่างเหมาะสม(()()(()()))แท็ก HTML / BBcode ที่ซ้อนกันบล็อกที่ซ้อนกันเป็นต้นเนื่องจากรัฐออโตมาตาที่จะจัดการกับมันควรจะต้องมีหลายรัฐในการจัดการระดับการรัง
  • ระดับ 2: ไวยากรณ์ที่ไม่มีบริบท

    พวกเขาสามารถมีกิ่งซ้อนซ้ำ recursive สาขาคล้ายตนเองในต้นไม้ไวยากรณ์ของพวกเขาเพื่อให้พวกเขาสามารถจัดการกับโครงสร้างซ้อนกันได้ดี
    สามารถใช้งานเป็นสถานะออโตเมติกพร้อมสแต็ก สแต็กนี้ใช้เพื่อแสดงระดับการซ้อนของไวยากรณ์ ในทางปฏิบัติพวกมันมักจะถูกนำไปใช้เป็นตัวแยกวิเคราะห์แบบบนลงล่างแบบเรียกซ้ำซึ่งใช้การเรียกโพรซีเดอร์ของเครื่องเพื่อติดตามระดับการซ้อนและใช้แบบเรียกซ้ำขั้นตอน / ฟังก์ชันสำหรับสัญลักษณ์ที่ไม่ใช่เทอร์มินัลทุกตัวในไวยากรณ์
    แต่พวกเขาไม่สามารถจัดการกับไวยากรณ์ตามบริบท เช่นเมื่อคุณมีนิพจน์x+3และในบริบทเดียวนี่xอาจเป็นชื่อของตัวแปรและในบริบทอื่นอาจเป็นชื่อของฟังก์ชันเป็นต้น
  • ระดับ 1: ไวยากรณ์ไวต่อบริบท

  • ระดับ 0: ไวยากรณ์ที่ไม่ จำกัด
    เรียกอีกอย่างว่าไวยากรณ์ที่นับซ้ำได้


70
โอ้ใช่? ดังนั้น "คำหรือโทเค็น" คืออะไร? พวกมันเป็นเพียงประโยคในภาษาปกติซึ่งประกอบด้วยตัวอักษรของตัวอักษร และสิ่งที่ "สร้าง" หรือ "ต้นไม้" ใน parser คืออะไร? พวกเขายังประโยคแต่ในภาษาที่แตกต่างกันในระดับที่สูงขึ้นซึ่งโทเค็นเฉพาะเป็นสัญลักษณ์ตัวอักษร ความแตกต่างไม่ใช่สิ่งที่คุณได้กล่าวว่า แต่ในความซับซ้อนของภาษาที่ใช้ เผชิญหน้ากับ -1 ด้วยคู่มือใด ๆ เกี่ยวกับทฤษฎีการแยกวิเคราะห์
SasQ

3
@SasQ มันจะยุติธรรมหรือไม่ที่จะบอกว่าทั้ง Lexers และ Parsers ใช้ไวยากรณ์และโทเค็นเป็นชุดข้อมูลหรือไม่
Parag

4
ค่อนข้างมาก พวกเขาทั้งคู่ใช้ชุดสัญลักษณ์จากตัวอักษรที่พวกเขารู้จัก สำหรับ lexer ตัวอักษรนี้ประกอบด้วยเพียงตัวอักษรธรรมดา สำหรับ parser ตัวอักษรประกอบด้วยสัญลักษณ์เทอร์มินัลอะไรก็ตามที่กำหนดไว้ พวกเขาอาจเป็นตัวละครได้เช่นกันหากคุณไม่ใช้ lexer และใช้ตัวระบุหนึ่งตัวและตัวเลขหนึ่งหลักเป็นต้น (ค่อนข้างมีประโยชน์ในช่วงแรกของการพัฒนา) แต่พวกมันมักจะเป็นโทเค็น (คลาสของคำศัพท์) เพราะโทเค็นเป็นนามธรรมที่ดี: คุณสามารถเปลี่ยน lexemes (สตริง) ที่แท้จริงที่พวกมันใช้แทนและ parser ไม่เห็นการเปลี่ยนแปลง
SasQ

6
ตัวอย่างเช่นคุณสามารถใช้สัญลักษณ์เทอร์มินัลSTMT_ENDในไวยากรณ์ของคุณ (สำหรับ parser) เพื่อแสดงถึงจุดสิ้นสุดของคำแนะนำ ตอนนี้คุณสามารถมีโทเค็นที่มีชื่อเดียวกันกับมันสร้างโดย lexer แต่คุณสามารถเปลี่ยนคำศัพท์จริงที่มันหมายถึง เช่น. คุณสามารถกำหนดSTMT_ENDเป็น;ที่จะมี C / C ++ - เช่นรหัสที่มา หรือคุณสามารถกำหนดendให้คล้ายกับสไตล์ Pascal หรือคุณสามารถกำหนดมันเป็นเพียงแค่'\n'จบการเรียนการสอนด้วยจุดสิ้นสุดของบรรทัดเช่นใน Python แต่ไวยากรณ์ของคำสั่ง (และ parser) ยังคงไม่เปลี่ยนแปลง :-) เฉพาะ lexer เท่านั้นที่ต้องเปลี่ยน
SasQ

24
ชั่วโมงบนวิกิพีเดียและ google ไม่ได้ช่วยอะไร แต่คุณอธิบายไวยากรณ์ของชัมสกีใน 3 นาที ขอบคุณ.
enrey

107

ใช่พวกเขามีความแตกต่างทางทฤษฎีและในการนำไปใช้

Lexers ถูกใช้เพื่อรับรู้ "คำศัพท์" ที่ประกอบขึ้นเป็นองค์ประกอบทางภาษาเพราะโครงสร้างของคำดังกล่าวนั้นโดยทั่วไปนั้นง่าย นิพจน์ทั่วไปนั้นดีมากในการจัดการโครงสร้างที่ง่ายกว่านี้และมีเอ็นจิ้นการจับคู่นิพจน์ทั่วไปที่มีประสิทธิภาพสูงที่ใช้ในการปรับใช้ lexers

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

เทคโนโลยี lexing หรือการแยกวิเคราะห์ไม่น่าจะหายไปในไม่ช้า

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


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

ฉันเดาว่า parser คือ lexer เนื่องจาก tree walker เป็น parser ฉันไม่เชื่อว่าทฤษฎีนั้นแตกต่างกัน: antlr.org/wiki/display/~admin/ANTLR+v4+lexersแต่ฉันเริ่มเข้าใจถึงความแตกต่างในการประชุมระหว่างพวกเขา ...
Naveen

4
ทฤษฎีแตกต่างกันมาก เทคโนโลยี parser ส่วนใหญ่พยายามจัดการภาษาที่ไม่มีบริบทในระดับหนึ่ง (บางส่วนมีเพียงบางส่วนเท่านั้นเช่น LALR บางส่วนทำได้ทั้งหมดเช่น GLR) เทคโนโลยี lexer ส่วนใหญ่พยายามทำนิพจน์ปกติเท่านั้น
Ira Baxter

3
ทฤษฎีนี้แตกต่างกันเพราะมันถูกเสนอโดยคนต่าง ๆ มากมายและใช้คำศัพท์และอัลกอริทึมต่างกัน แต่ถ้าคุณมองพวกมันอย่างใกล้ชิดคุณสามารถมองเห็นความคล้ายคลึงกันได้ ตัวอย่างเช่นปัญหาการเรียกซ้ำซากด้านซ้ายคล้ายกับปัญหาการไม่กำหนดระดับใน NFA และการลบการเรียกซ้ำที่เหลืออยู่นั้นคล้ายกับการลบการไม่กำหนดระดับและแปลง NFA เป็น DFA โทเค็นเป็นประโยคสำหรับ tokenizer (เอาต์พุต) แต่สัญลักษณ์ตัวอักษรสำหรับ parser (อินพุต) ฉันไม่ปฏิเสธความแตกต่าง (ระดับ Chomsky) แต่ความคล้ายคลึงกันช่วยออกแบบได้มากมาย
SasQ

1
หน้าที่ของฉันคือทฤษฎีหมวดหมู่ เขาแสดงให้เห็นว่าแนวคิดเชิงทฤษฎีของ Sheaves ครอบคลุมการจับคู่รูปแบบทุกประเภทและสามารถแยก LR ได้จากการจัดหมวดหมู่เชิงนามธรรม ในความเป็นจริงถ้าคุณมีความเป็นนามธรรมมากพอคุณสามารถค้นหาสิ่งเหล่านี้ได้ ประเด็นของทฤษฎีหมวดหมู่คือคุณมักจะเป็นนามธรรม "ตลอดไป"; ฉันแน่ใจว่าคุณสามารถสร้างตัวแยกวิเคราะห์ทฤษฎีหมวดหมู่ที่ลบความแตกต่างได้ แต่การใช้งานในทางปฏิบัติใด ๆ ของมันจะต้องยกตัวอย่างลงไปที่โดเมนปัญหาที่เฉพาะเจาะจงจากนั้นความแตกต่างก็ปรากฏขึ้นจริง
Ira Baxter

32

เมื่อ lexing เพียงพอคุณต้องการ EBNF เมื่อใด

EBNF ไม่ได้เพิ่มพลังของไวยากรณ์มากนัก มันเป็นเพียงสัญลักษณ์ความสะดวกสบาย / ทางลัด / "น้ำตาลประโยค"เหนือกฎไวยากรณ์มาตรฐานฟอร์มฟอร์ม (CNF) ของ Chomsky ตัวอย่างเช่นทางเลือก EBNF:

S --> A | B

คุณสามารถประสบความสำเร็จใน CNF เพียงแค่แสดงรายการการผลิตทางเลือกแต่ละรายการแยกกัน:

S --> A      // `S` can be `A`,
S --> B      // or it can be `B`.

องค์ประกอบเสริมจาก EBNF:

S --> X?

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

S --> B       // `S` can be `B`,
B --> X       // and `B` can be just `X`,
B -->         // or it can be empty.

การผลิตในรูปแบบเดียวกับที่กล่าวBข้างต้นเรียกว่า "ลบ" เพราะสามารถลบสิ่งที่มันหมายถึงในการผลิตอื่น ๆ (ผลิตภัณฑ์สตริงที่ว่างเปล่าแทนสิ่งอื่น)

การทำซ้ำไม่เกินศูนย์จาก EBNF:

S --> A*

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

S --> S A    // `S` is just itself ended with `A` (which can be done many times),
S -->        // or it can begin with empty-string, which stops the recursion.

เมื่อรู้ว่ามันสร้างเพียงสตริงว่าง (ท้ายที่สุด) ตามด้วยศูนย์หรือมากกว่าAนั้นสตริงเดียวกัน ( แต่ไม่ใช่ภาษาเดียวกัน! ) สามารถแสดงโดยใช้การเรียกซ้ำแบบขวา :

S --> A S    // `S` can be `A` followed by itself (which can be done many times),
S -->        // or it can be just empty-string end, which stops the recursion.

และเมื่อพูดถึง+การทำซ้ำหนึ่งครั้งหรือมากกว่าจาก EBNF:

S --> A+

มันสามารถทำได้โดยแยกออกเป็นหนึ่งAและใช้*เป็นมาก่อน:

S --> A A*

ซึ่งคุณสามารถแสดงเป็น CNF ได้เช่นนี้ (ฉันใช้การเรียกซ้ำที่ถูกต้องที่นี่ลองคิดดูอีกแบบว่าเป็นแบบฝึกหัด):

S --> A S   // `S` can be one `A` followed by `S` (which stands for more `A`s),
S --> A     // or it could be just one single `A`.

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

A -->        // Empty (nullable) production (AKA erasure).
B --> x      // Single terminal symbol.
C --> y D    // Simple state change from `C` to `D` when seeing input `y`.
E --> F z    // Simple state change from `E` to `F` when seeing input `z`.
G --> G u    // Left recursion.
H --> v H    // Right recursion.

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

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

S --> a S b    // `S` can be itself "parenthesized" by `a` and `b` on both sides.
S -->          // or it could be (ultimately) empty, which ends recursion.

จากนั้นคุณสามารถเห็นได้อย่างง่ายดายว่าสิ่งนี้ไม่สามารถทำได้ด้วยการแสดงออกปกติเพราะคุณไม่สามารถแก้ไขมันเป็นการผลิต EBNF เดียวในทางใดทางหนึ่ง; คุณจะลงเอยด้วยการทดแทนSอย่างไม่มีกำหนดซึ่งจะเพิ่มอีกas และbs ทั้งสองด้านเสมอ lexers (มากขึ้นโดยเฉพาะ: Finite รัฐออโตใช้โดย lexers) ไม่สามารถนับจำนวนข้อ (? พวกเขาจะ จำกัด จำ) ดังนั้นพวกเขาจึงไม่ทราบว่าหลายaท่านอยู่ที่นั่นเพื่อให้ตรงกับพวกเขาอย่างเท่าเทียมกันด้วยดังนั้นหลายbs Grammars เช่นนี้เรียกว่าgrammars ที่ไม่มีบริบท (อย่างน้อยที่สุด) และพวกเขาต้องการเครื่องมือแยกวิเคราะห์

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

A R B --> A S B

คุณสามารถนึกถึงสัญลักษณ์เพิ่มเติมเหล่านี้ทางซ้ายเป็น "บริบท" สำหรับการใช้กฎ อาจมีปัจจัยพื้นฐานบาง postconditions ฯลฯ ตัวอย่างเช่นกฎดังกล่าวข้างต้นจะแทนRเข้าไปSแต่เมื่อมันอยู่ในระหว่างAและBทิ้งเหล่านั้นAและBตัวเองไม่เปลี่ยนแปลง ไวยากรณ์ชนิดนี้ยากที่จะแยกวิเคราะห์จริงๆเพราะต้องใช้เครื่องทัวริงแบบเต็มรูปแบบ มันเป็นอีกเรื่องหนึ่งดังนั้นฉันจะจบที่นี่


1
คุณระบุว่า EBNF เป็น "เพียงแค่สัญลักษณ์ความสะดวกสบาย / ทางลัด /" น้ำตาลประโยค "เหนือกฎไวยากรณ์ไวยากรณ์ฟอร์มปกติ (CNF) ของ Chomsky แต่ CNF แทบจะไม่เกี่ยวข้องกับหัวข้อ EBNF สามารถเปลี่ยนเป็น BNF มาตรฐานได้อย่างง่ายดาย ระยะเวลา มันเป็นน้ำตาล syntactic สำหรับ BNF มาตรฐาน
Babou

11

เพื่อตอบคำถามตามที่ถาม (โดยไม่ทำซ้ำจนเกินไปสิ่งที่ปรากฏในคำตอบอื่น ๆ )

Lexers และ parsers ไม่แตกต่างกันมากตามคำแนะนำที่ได้รับการยอมรับ ทั้งสองมีพื้นฐานมาจากภาษาแบบง่าย ๆ : ภาษาปกติสำหรับ lexers และเกือบทุกภาษา (CF) - ภาษาบริบทสำหรับ parsers พวกเขาทั้งสองเกี่ยวข้องกับรูปแบบการคำนวณที่ค่อนข้างง่ายหุ่นยนต์สถานะ จำกัด และหุ่นยนต์กดลง ภาษาปกติเป็นกรณีพิเศษของภาษาที่ไม่มีบริบทเพื่อให้lexers สามารถผลิตได้ด้วยเทคโนโลยี CF ที่ค่อนข้างซับซ้อนกว่า แต่ไม่ใช่ความคิดที่ดีอย่างน้อยสองเหตุผล

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

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

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

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

อย่างไรก็ตาม CF ประกอบด้วย CF (ผ่านตัวแปลงสัญญาณ CF ... ขอโทษสำหรับคณิตศาสตร์) ไม่จำเป็นต้องให้ CF และอาจทำให้สิ่งต่าง ๆ ทั่วไป แต่ในทางปฏิบัติน้อยลง ดังนั้น CF ไม่ใช่เครื่องมือที่เหมาะสมสำหรับ lexers แม้ว่าจะสามารถใช้งานได้ก็ตาม

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

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

BNF เป็นเพียงไวยากรณ์เฉพาะสำหรับการนำเสนอ CF grammars

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

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

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

ฉันขอแนะนำให้ดูคำตอบของ AHRด้วย

แต่นี่เป็นการเปิดคำถาม: ทำไมต้องเป็นต้นไม้

ต้นไม้เป็นพื้นฐานที่ดีสำหรับการระบุไวยากรณ์เนื่องจาก

  • พวกเขาให้โครงสร้างที่เรียบง่ายกับข้อความ

  • มีความสะดวกมากสำหรับการเชื่อมโยงความหมายกับข้อความบนพื้นฐานของโครงสร้างนั้นด้วยเทคโนโลยีที่เข้าใจทางคณิตศาสตร์ได้ดี (การประกอบผ่านโฮโมมอร์ฟิซึม) ตามที่ระบุไว้ข้างต้น มันเป็นเครื่องมือพีชคณิตขั้นพื้นฐานในการกำหนดความหมายของพิธีการทางคณิตศาสตร์

ดังนั้นจึงเป็นตัวแทนระดับกลางที่ดีดังที่แสดงโดยความสำเร็จของ Abstract Syntax Trees (AST) โปรดทราบว่า AST มักจะแตกต่างจากการแยกวิเคราะห์ต้นไม้เนื่องจากเทคโนโลยีการแยกวิเคราะห์ที่ใช้โดยผู้เชี่ยวชาญหลายคน (เช่น LL หรือ LR) ใช้กับชุดย่อยของ CF grammars เท่านั้นดังนั้นบังคับให้มีการบิดเบือนทางไวยากรณ์ซึ่งต่อมาถูกแก้ไขใน AST สิ่งนี้สามารถหลีกเลี่ยงได้ด้วยเทคโนโลยีการแยกวิเคราะห์ทั่วไป (ขึ้นอยู่กับการเขียนโปรแกรมแบบไดนามิก) ที่ยอมรับไวยากรณ์ CF ใด ๆ

คำแถลงเกี่ยวกับความจริงที่ว่าภาษาการเขียนโปรแกรมมีความไวต่อบริบท (CS) แทนที่จะเป็น CF โดยพลการและเป็นข้อโต้แย้ง

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

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

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


ใช่การแยกวิเคราะห์ต้นไม้และ AST นั้นแตกต่างกันไป แต่ก็ไม่ค่อยมีประโยชน์เท่าไหร่ ดูการสนทนาของฉันนี้: stackoverflow.com/a/1916687/120163
Ira Baxter

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

มุมมอง "ในทางปฏิบัติ" คือเหตุผลที่ฉันอ้างว่าพวกเขาไม่ได้แตกต่างกันมากในทางที่เป็นประโยชน์ และฉันก็ไม่เชื่อว่าการใช้ (ad hoc AST) ช่วยให้คุณ "แปลงถูกต้องพิสูจน์"; ad hoc AST ของคุณไม่มีความสัมพันธ์ที่ชัดเจนกับไวยากรณ์ที่แท้จริงของการจองที่กำลังดำเนินการอยู่ (และที่นี่ใช่ระบบของฉันสามารถป้องกันได้ว่า "AST" ของเรานั้นเทียบเท่ากับ isomorphic เทียบเท่ากับ BNF) Ad Hoc AST ไม่ได้ให้ความสามารถเพิ่มเติมใด ๆ แก่คุณในการแยก "การแสดงที่เป็นรูปธรรมหลายรูปแบบ) คุณคัดค้าน GLR (ไม่ได้มีประสิทธิภาพมากที่สุด) ดูเหมือนไม่มีจุดหมายสวยมากและพวกเขาก็ไม่ใช่คนไร้สาระ
Ira Baxter

ดังนั้นในความเป็นจริงฉันไม่เข้าใจส่วนหนึ่งส่วนใดของคุณคัดค้านความคิดเห็นของฉัน คุณจะต้องเขียนว่า "คำตอบที่สะอาด"
Ira Baxter

@IraBaxter ความคิดเห็นมีข้อ จำกัด มากเกินไปสำหรับคำตอบที่เหมาะสม (คำแนะนำ?) "Ad hoc" ไม่ใช่ qualifier ที่เหมาะสมสำหรับ AST I advocate ซึ่งควรจะเป็นไวยากรณ์อ้างอิง (บางครั้ง) นี่คือความจริงทางประวัติศาสตร์มองทั้งประวัติศาสตร์ของแนวคิดของ AST ในวิทยาการคอมพิวเตอร์และที่ประวัติศาสตร์ของระบบที่เป็นทางการเป็นเงื่อนไข (ต้นไม้) ในพีชคณิตเรียงพร้อมกับการตีความ AST เป็นแบบอ้างอิงไม่ใช่แบบฟอร์มที่ได้รับ ดูระบบพิสูจน์ที่ทันสมัยและการสร้างโปรแกรมอัตโนมัติ คุณอาจจะลำเอียงจากความจริงที่ว่าคุณต้องทำงานจากไวยากรณ์ที่เป็นรูปธรรมซึ่งออกแบบโดยผู้อื่น
30901

7

มีสาเหตุหลายประการที่ทำให้ส่วนการวิเคราะห์ของคอมไพเลอร์โดยปกติแยกออกเป็นการวิเคราะห์คำและขั้นตอนการแยกวิเคราะห์ (การวิเคราะห์ไวยากรณ์)

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

resource___ Compilers (รุ่นที่ 2) เขียนโดย - Alfred V. Abo Columbia University โมนิกาเอส. Lam Stanford University Ravi Sethi Avaya เจฟฟรีย์ดี. Ullman Stanford University

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