簡體   English   中英

[Nearley]:如何解析匹配的開始和結束標簽

[英][Nearley]: how to parse matching opening and closing tag

我正在嘗試使用 nearley 解析一種非常簡單的語言:您可以在匹配的開始標簽和結束標簽之間放置一個字符串,並且可以鏈接一些標簽。 它看起來像一種 XML,但用[而不是< ,標簽總是 2 個字符長,並且沒有嵌套。

[aa]My text[/aa][ab]Another Text[/ab]

但我似乎無法正確解析這個,因為我得到the grammar should be unambiguous的,只要我有多個標簽。

我現在擁有的語法:

@builtin "string.ne"
@builtin "whitespace.ne"

openAndCloseTag[X] -> "[" $X "]" string  "[/" $X "]"

languages -> openAndCloseTag[[a-zA-Z] [a-zA-Z]] (_ openAndCloseTag[[a-zA-Z] [a-zA-Z]]):*

string -> sstrchar:* {% (d) => d[0].join("") %}

並且相關,理想情況下,我希望標簽不區分大小寫(例如, [bc]TESt[/BC]將是有效的)

有誰知道我們該怎么做? 我找不到一個近乎 XML 解析器示例。

您的語言幾乎太簡單以至於不需要解析器生成器。 同時,它不是上下文無關的,這使得使用解析器生成器變得很困難。 因此,Nearly 解析器很可能不是最適合您的工具,盡管它可能會通過一些黑客技術使其工作。

第一件事。 您實際上並沒有提供您的語言的明確定義,這就是您的解析器報告歧義的原因。 要查看歧義,請考慮輸入

[aa]My text[/ab][ab]Another Text[/aa]

這與您的測試輸入非常相似; 我所做的只是交換一對字母。 現在,問題來了:這是由單個aa標簽組成的有效輸入嗎? 還是語法錯誤? (這是一個嚴肅的問題。像這樣的標簽系統的一些定義認為標簽只能由匹配的關閉標簽關閉,因此看起來像不同標簽的東西被認為是純文本。這樣的系統將接受輸入作為單個標記值。)

問題是您將string定義為sstrchar:* ,如果我們查看sstrcharstring.ne的定義,我們會看到(忽略不相關的后處理操作):

sstrchar -> [^\\'\n]
    | "\\" strescape
    | "\\'"

現在,第一種可能性是“除反斜杠、單引號或換行符之外的任何字符”,很容易看出[/ab]中的所有字符都在sstrchar中。 (我不清楚您為什么選擇sstrchar ;單引號在您的語言中似乎並不特殊。或者您可能只是沒有提及它們的重要性。)因此, string可以延伸到輸入的末尾。 當然,語法需要一個結束標記,如果有匹配項,Nearley 解析器就會確定要找到匹配項。 但事實上,有兩個。 因此解析器聲明了一個歧義,因為它沒有任何標准可以在兩個關閉標簽之間進行選擇。

在這里,我們遇到了您的語言不是上下文無關的問題。 (實際上,在某種技術意義上,它是上下文無關的,因為“只有”676 個不區分大小寫的兩個字母標簽,理論上可以列出所有 676 個可能性。但我猜你不想要要做到這一點。)

上下文無關文法不能表達堅持兩個非終結符擴展為同一個字符串的語言。 這就是上下文無關的定義:如果一個非終結符只能匹配與前一個非終結符相同的輸入,那么第二個非終結符匹配取決於上下文,特別是由第一個非終結符產生的匹配。終端。 在上下文無關文法中,非終結符擴展為相同的事物,而與文本的 rest 無關。 不允許非終結符出現的上下文影響擴展。

現在,您很可能期望您的宏定義:

openAndCloseTag[X] -> "[" $X "]" string  "[/" $X "]"

通過重復$X宏參數來表示上下文相關的匹配。 但 Nearley 文檔將此構造描述為宏並非偶然。 這里的X指的是宏調用中使用的字符串。 所以當你說:

openAndCloseTag[[a-zA-Z] [a-zA-Z]]

Nearly macro 將其擴展到

 "[" [a-zA-Z] [a-zA-Z] "]" string  "[/" [a-zA-Z] [a-zA-Z] "]"

這就是它將用作語法產生的內容。 觀察到兩個$X宏參數被擴展為相同的參數,但這並不意味着它將匹配相同的輸入文本。 這些子模式中的每一個都將獨立匹配任何兩個字母字符。 上下文無關。

正如我之前提到的,你可以使用這個宏來寫出 676 種可能的標簽模式:

tag -> openAndCloseTag["aa"i]
     | openAndCloseTag["ab"i]
     | openAndCloseTag["ac"i]
     | ...
     | openAndCloseTag["zz"i]

如果你這樣做了(並且你設法正確地列出了所有的可能性),那么只要你從不在同一個輸入中兩次使用同一個標簽,解析器就不會抱怨歧義。 因此,您的原始輸入和我更改的輸入都可以(只要您接受我的輸入是單個標記對象的解釋)。 但它仍會將以下內容報告為模棱兩可:

[aa]My text[/aa][aa]Another Text[/aa]

這是模棱兩可的,因為語法允許它是單個aa標記字符串(其文本包括看起來像關閉和打開標記的字符)或兩個連續的aa標記字符串。

為了消除歧義,您必須以不允許內部標記的方式編寫string模式,就像sstrchar不允許內部單引號一樣。 當然,除了匹配不包含模式的字符串比匹配不包含單個字符的字符串要簡單得多。 可以使用 Nearley 來完成,但我真的不認為這是你想要的。

最好的選擇可能是使用本機 Javascript 正則表達式來匹配標記的字符串。 這將證明更簡單,因為 Javascript 正則表達式比數學正則表達式更強大,甚至允許匹配(某些)上下文相關結構的可能性。 例如,您可以將 Javascript 正則表達式與 Moo 詞法分析器一起使用,該詞法分析器很好地集成到 Nearley 中。 或者你可以直接使用正則表達式,因為一旦你匹配了標記的文本,你就不需要做很多其他的事情了。

為了幫助您入門,這里有一個簡單的 Javascript 正則表達式,它匹配帶有不區分大小寫標簽的標記字符串(末尾的i標志):

/\[([a-zA-Z]{2})\].*?\[\/\1\]/gmi

您可以使用Regex 101在線玩它

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM