【问题标题】:FParsec and postfix modifiers to parsed items已解析项目的 FParsec 和后缀修饰符
【发布时间】:2020-10-08 06:59:01
【问题描述】:

作为我自己的练习,我正在使用 FParsec 编写一个函数,该函数可以从(有限的)正则表达式形式的规范生成随机字符串。

例如

Input: ([Hh]ello ){1,3} world!?
Output: Hello hello world!

Input: (\d{1,2}\.){3}(\d{1,2})
Output: 38.12.29.05

我有很多工作,但我对后缀术语的想法有疑问(即解析器可能需要返回并修改输出,而不仅仅是 Char 流中的位置)。例如。 “a”与“a+”

这是我的域类型的缩减版:

type Count =
    | ExactCount of int
    | MinCount of int
    | MaxCount of int
    | RangeCount of int * int

type Term =
    | CharLiteral of char
    | Count of Term * Count

type RandExpSpec = Term list

所以输入ab 应该生成[CharLiteral 'a'; CharLiteral 'b']ab+ 应该生成[CharLiteral 'a'; Count (CharLiteral 'b', MinCount 1)]。因此,这意味着,在流中遇到 Count 术语时,解析器需要回溯输出,以便将最后一个术语包装在另一个对象中。

现在,我不知道该怎么做。这是我当前的解析定义,它(大部分)确实有效,但效率很低:

let parseCharLiteral = choice [ letter; digit ] |>> CharLiteral

let rec parseTerm =
    parse.Delay(fun () -> choice [ parseCharLiteral ])

and parseCount =
    parseTerm
    .>>. choice [ skipChar '*' >>% (MinCount 0)
                  skipChar '+' >>% (MinCount 1)
                  skipChar '?' >>% (RangeCount(0, 1)) ]
    |>> Count

let parseTerms =
    many ((attempt parseCount) <|> parseTerm) .>> eof

可以看到在parseCount我先调用parseTerm,然后解析实际计数信息。然后,在parseTerms 中,我每次都尝试parseCount 解析器,如果它不起作用,则通过 input 回溯。这是非常低效的,因为我实际上是对输入流中的几乎每个字符进行两次传递,以防万一它后面跟着一个计数修饰符。

有没有更有效的方法来做到这一点?我觉得我应该写一些更像:

let parseCharLiteral = choice [ letter; digit ] |>> CharLiteral

let rec parseTerm =
    parse.Delay(fun () -> choice [ parseCharLiteral ] .>>. (attempt parseCount))

and parseCount =
    choice [ skipChar '*' >>% (MinCount 0)
             skipChar '+' >>% (MinCount 1)
             skipChar '?' >>% (RangeCount(0, 1)) ]
    |>> Count

let parseTerms =
    many parseTerm .>> eof

但我无法完成这项工作,因为parseCount 需要包装parseTerm 返回的上一个术语。

【问题讨论】:

    标签: parsing f# fparsec


    【解决方案1】:

    我认为您可以使用optparseCount 在没有计数的情况下找不到计数:

    let parseCount =
        parseTerm
        .>>. opt (choice [ skipChar '*' >>% (MinCount 0)
                           skipChar '+' >>% (MinCount 1)
                           skipChar '?' >>% (RangeCount(0, 1)) ])
        |>> function
        | term, None -> term
        | term, Some count -> Count (term, count)
    
    let parseTerms =
        many parseCount .>> eof
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-23
      • 1970-01-01
      相关资源
      最近更新 更多