簡體   English   中英

parsec.py遞歸定義

[英]parsec.py recursive definitions

我喜歡這個圖書館的簡潔。 可悲的是,我還沒弄清楚如何進行遞歸定義:

考慮這個最小的人為例子:

import parsec as psc
digit = psc.regex("[0-9]")
number = lambda: digit ^ (digit + number())
_ = number().parse("42")

正如所料, number()的評估是無限遞歸的:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 1, in <lambda>
  [Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded

我知道在這個具體的例子中,它可以通過改變第3行來解決

number = lambda: psc.many1(digit)

但我不確定一般情況總是可行的。

是否有可能使數字的解析遞歸,如

<number> ::= <digit><number> | <digit>

(更新)免責聲明:正如我們在評論中發現的那樣,這僅適用於3.3 以上的parsec.py版本(截至2018年8月)是PyPI上可用的最新版本,所以現在你必須手動安裝開發來自GitHub的版本,以便此解決方案工作。

更新2: parsec.py v3.4終於發布並解決了問題。


在這樣的情況下,我發現通過“手動擴展” parsec提供的原語並編寫我們自己的“低級”解析器(即采用textindex參數,而不是從parsec構建的解析器)通常很有幫助。原始人),只是為了看看發生了什么。 通過從parsec源代碼中try_choice^ )的定義並手動插入表達式1的組成部分,我們可以編寫一個解析器,我認為它可以做你想要的:

import parsec as psc
digit = psc.regex("[0-9]")

def number():
  @psc.Parser
  def number_parser(text, index):
    res = (digit + number())(text, index)
    return res if res.status else digit(text, index)
  return number_parser

_ = number().parse("42423")
print(_)

輸出:

('4', ('2', ('4', ('2', '3'))))

這樣做的原因當然是因為“解析器返回函數” number()僅在實際解析器中有條件地被調用,而不是(無條件地)在其自身內。

寫一個更簡單的方法是根本不使用“解析器返回函數”,而只是直接編寫解析器本身:

@psc.Parser
def number(text, index):
  return ((digit + number) ^ digit)(text, index)

用法從number().parse("42423")更改為number.parse("42423")但它執行相同的操作。

最后,parsec.py中是否有一些功能允許您在沒有顯式textindex參數的情況下執行此操作,以便解析器完全由其他parsec.py原語構建? 事實證明, parsec.generate適合這個法案! 從其源代碼文檔

構建解析器的最有效方法是使用generate裝飾器。 generate會從generate創建一個解析器,該解析器應該生成解析器。 這些解析器連續應用,並使用.send()協議將其結果發送回生成器。 生成器應返回結果或另一個解析器,這相當於應用它並返回其結果。

你可以在這里找到示例用法,從中我們可以寫出:

@psc.generate
def number():
  r = yield (digit + number) ^ digit
  return r

這是有效的,因為除了它所做的所有奇特的生成器魔法之外, generate裝飾器返回一個本身用@parsec.Parser (參見上面鏈接的源代碼)裝飾的函數,因此生成的完全裝飾的number函數將與第二個解決方案中的一個。 因此,我們可以以與digit等相同的方式使用它,而不必調用它或任何東西,這就是我們在yield表達式中所做的。

用法再次只是number.parse("42423") ,它返回與其他兩個解決方案相同的東西。

也許有更好的解決方案,但這是我能想到的。


1我必須將digit ^ (digit + number())(digit + number()) ^ digit ,因為第一個數字只返回第一個數字,然后認為自己完成了。 我希望這沒關系?

如果已經有很多解析器返回函數或只是想在樣式上保持一致,另一種快速解決此問題的方法是使函數延遲求值:

import parsec as psc

def lazy(fn):
  @psc.Parser
  def _lazy(text, index):
    return fn()(text, index)
  return _lazy

digit = lambda: psc.regex("[0-9]")
number = lambda: (digit() + lazy(number)) ^ digit()
print(number().parse("423242"))

打印:

('4', ('2', ('3', ('2', ('4', '2')))))

暫無
暫無

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

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