การหยุดไวยากรณ์ Raku ที่ EOS (End of String)


9

ในกระบวนการเขียนนักแปลของภาษาดนตรีหนึ่งไปยังอีกภาษาหนึ่ง (ABC ถึง Alda) เป็นข้ออ้างในการเรียนรู้ความสามารถของ Raku DSL ฉันสังเกตว่าดูเหมือนจะไม่มีทางที่จะยุติ a .parse! นี่คือตัวอย่างรหัสย่อของฉัน:

#!/home/hsmyers/rakudo741/bin/perl6
use v6d;

# use Grammar::Debugger;
use Grammar::Tracer;

my $test-n01 = q:to/EOS/;
a b c d e f g
A B C D E F G
EOS

grammar test {
  token TOP { <score>+ }
  token score {
      <.ws>?
      [
          | <uc>
          | <lc>
      ]+
      <.ws>?
  }
  token uc { <[A..G]> }
  token lc { <[a..g]> }
}

test.parse($test-n01).say;

และนี่เป็นส่วนสุดท้ายของการแสดง Grammer :: Tracer ที่แสดงถึงปัญหาของฉัน

|  score
|  |  uc
|  |  * MATCH "G"
|  * MATCH "G\n"
|  score
|  * FAIL
* MATCH "a b c d e f g\nA B C D E F G\n"
「a b c d e f g
A B C D E F G
」

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


ฉันไม่ต้องการที่จะแทรกแซงในกระบวนการเรียนรู้ของคุณ แต่เพียงในกรณีที่คุณไม่ทราบมีเป็นโมดูลเอบีซี
raiph

1
อย่างน้อยเราก็ไม่ได้เลือกเพลงเดียวกันเพื่อทดสอบ!
hsmyers

คำตอบ:


10

เมื่อคุณใช้ดีบักเกอร์ไวยากรณ์จะช่วยให้คุณเห็นได้อย่างชัดเจนว่าเอ็นจิ้นวิเคราะห์สตริงอย่างไร - ล้มเหลวเป็นปกติและคาดว่า การพิจารณาเช่นการจับคู่กับสตริงa+b* aabคุณควรได้รับการจับคู่สองตัวสำหรับ 'a' แล้วตามด้วยการล้มเหลว (เพราะbไม่ใช่a) แต่จากนั้นจะลองอีกครั้งbและจับคู่สำเร็จ

สิ่งนี้อาจเห็นได้ง่ายขึ้นหากคุณทำการสลับกับ||(ซึ่งบังคับให้ลำดับ) ถ้าคุณมี

token TOP   { I have a <fruit> }
token fruit { apple || orange || kiwi }

และคุณแยกประโยค "ฉันมีกีวี" คุณจะเห็นมันตรงกับครั้งแรก "ฉันมี" ตามด้วยสองล้มเหลวด้วย "แอปเปิ้ล" และ "ส้ม" และในที่สุดก็ตรงกับ "กีวี"

ตอนนี้เรามาดูกรณีของคุณ:

TOP                  # Trying to match top (need >1 match of score)
|  score             #   Trying to match score (need >1 match of lc/uc)
|  |  lc             #     Trying to match lc
|  |  * MATCH "a"    #     lc had a successful match! ("a")
|  * MATCH "a "      #   and as a result so did score! ("a ")
|  score             #   Trying to match score again (because <score>+)
|  |  lc             #     Trying to match lc 
|  |  * MATCH "b"    #     lc had a successful match! ("b")
|  * MATCH "b "      #   and as a result so did score! ("b ")
……………                #     …so forth and so on until…
|  score             #   Trying to match score again (because <score>+)
|  |  uc             #     Trying to match uc
|  |  * MATCH "G"    #     uc had a successful match! ("G")
|  * MATCH "G\n"     #   and as a result, so did score! ("G\n")
|  score             #   Trying to match *score* again (because <score>+)
|  * FAIL            #   failed to match score, because no lc/uc.
|
|  # <--------------   At this point, the question is, did TOP match?
|  #                     Remember, TOP is <score>+, so we match TOP if there 
|  #                     was at least one <score> token that matched, there was so...
|
* MATCH "a b c d e f g\nA B C D E F G\n" # this is the TOP match

ความล้มเหลวที่นี่เป็นเรื่องปกติ: ในบางครั้งเราจะหมด<score>โทเค็นดังนั้นความล้มเหลวจึงหลีกเลี่ยงไม่ได้ เมื่อสิ่งนั้นเกิดขึ้นเอ็นจินไวยากรณ์สามารถย้ายไปยังสิ่งใดก็ตามที่<score>+อยู่ในไวยากรณ์ของคุณ เนื่องจากไม่มีสิ่งใดเลยความล้มเหลวจึงส่งผลให้เกิดการจับคู่ของสตริงทั้งหมด (เนื่องจากTOPตรงกันกับโดยนัย/^…$/)

นอกจากนี้คุณอาจลองเขียนไวยากรณ์ของคุณใหม่ด้วยกฎที่แทรก <.ws> * โดยอัตโนมัติ (เว้นแต่ว่ามันสำคัญที่จะต้องมีพื้นที่เดียวเท่านั้น):

grammar test {
  rule TOP { <score>+ }
  token score {
      [
          | <uc>
          | <lc>
      ]+
  }
  token uc { <[A..G]> }
  token lc { <[a..g]> }
}

นอกจากนี้ IME คุณอาจต้องการเพิ่มโทเค็นโปรโตสำหรับ uc / lc เพราะเมื่อคุณมี[ <foo> | <bar> ]คุณจะมีหนึ่งในนั้นไม่ได้กำหนดซึ่งสามารถทำให้การประมวลผลในระดับการกระทำที่น่ารำคาญเล็กน้อย คุณสามารถลอง:

grammar test {
  rule  TOP   { <score>  + }
  token score { <letter> + }

  proto token letter    {     *    }
        token letter:uc { <[A..G]> }
        token letter:lc { <[a..g]> }
}

$<letter> จะถูกกำหนดด้วยวิธีนี้เสมอ


สิ่งนี้อธิบายความจริงที่ว่าวัตถุการจับคู่คืนค่า 'ออกมาเป็นจริงแม้จะมี' FAIL ' ฉันคิดว่าอาจเป็นกรณี; ฉันจะย้ายกลับไปยังการเพิ่มโทเค็นที่จำเป็นสำหรับโครงการจริง)
hsmyers

ดูเหมือนว่าไวยากรณ์ที่แท้จริงจะไม่ชอบการแทรก <.ws> * โดยอัตโนมัติ อาจเนื่องมาจากเลเยอร์เพิ่มเติมที่เกี่ยวข้องเกิน <score> ข้อเสนอแนะของคุณเพื่อใช้ proto ดูดีทันทีที่ฉันสามารถห่อหัวของฉันรอบเทคนิค ...
hsmyers


ฉันเกลียดการมีโค้ดที่ฉันไม่ต้องการ - จะดีบั๊กและมีความสวยงามของมันทั้งหมด! ปัญหาที่แท้จริงคือ ABC ไม่ได้ให้เรื่องช่องว่าง มีข้อยกเว้นบางประการ แต่โดยมากแล้วสามารถเกิดขึ้นได้เกือบทุกที่ กรณี 'ใช้' เป็นเรื่องของความชัดเจนเหมือนเครื่องหมายจุลภาคในสตริงจำนวนมาก ฉันจะกลับมาทบทวนปัญหาตามที่จำเป็นจนกว่าฉันจะเข้าใจปัญหาและลดปัญหาให้เหลือน้อยที่สุด
hsmyers

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