5

我正在尝试使用 FParsec 解析标准的简单类型(在 lambda 演算的意义上),但是我很难从 Lex/Yacc 样式转换为 FParsec 中使用的样式,特别是在递归定义方面。

我试图解析的类型示例是:

  • o->o
  • (o -> o -> o) -> o

这是我的尝试:


    type SType =
      | Atom
      | Arrow of SType * SType

    let ws = spaces
    let stype, styperef = createParserForwardedToRef()

    let atom = pchar 'o' .>> ws |>> (fun _ -> Atom)

    let arrow = pipe2 (stype .>> (pstring "->" .>> ws)) 
                      stype
                      (fun t1 t2 -> Arrow (t1,t2))

    let arr = parse {
                let! t1 = stype
                do!  ws
                let! _  = pstring "->"
                let! t2 = stype
                do!  ws
                return Arrow (t1,t2)
              }

    styperef := choice [ pchar '(' >>. stype .>> pchar ')';
                         arr;
                         atom ]

    let _ = run stype "o -> o"`

当我将其加载到交互式时,最后一行会导致堆栈溢出(具有讽刺意味的是,这些天很难搜索)。鉴于存在递归引用,我可以想象为什么,但我会认为一个标记前瞻会阻止遵循stype. 因此,我假设它必须是选择arr,选择stype,等等。但是如何防止这种循环呢?

我对有关该库的惯用用法的评论以及对我尝试的解决方案的更正感兴趣。

4

2 回答 2

4

当您使用 FParsec 时,您需要借助序列组合器而不是左递归来解析序列。在您的情况下,您可以例如使用sepBy1组合器:

open FParsec

type SType =
     | Atom
     | Arrow of SType * SType

let ws = spaces : Parser<unit, unit>
let str_ws s = pstring s >>. ws

let stype, stypeRef = createParserForwardedToRef()

let atom = str_ws "o" >>% Atom

let elem = atom <|> between (str_ws "(") (str_ws ")") stype

do stypeRef:= sepBy1 elem (str_ws "->") 
              |>> List.reduceBack (fun t1 t2 -> Arrow(t1, t2))

let _ = run stype "o -> o"
于 2011-07-20T21:04:51.813 回答
0

这运行,但可能被黑客攻击太多。这些type Parser...东西来自 FParsec 文档,以避免编译器错误。

type SType = 
    | Atom 
    | Arrow of SType * SType

type UserState = unit
type Parser<'t> = Parser<'t, UserState>


let ws = spaces

let atom : Parser<_> = pchar 'o' .>> ws |>> (fun _ -> Atom)

let rec term =
    parse {
        // Force something to come before another term.  Either
        // an atom, or a term in parens.
        let! first = choice [atom;
                             (pstring "(" .>> ws) >>. term .>> 
                              (pstring ")" .>> ws)]

        // Check if this is an arrow.  If not, just return first.
        let! res = choice [((pstring "->") .>> ws) >>. term |>> (fun x ->
                               Arrow (first, x));
                           preturn first]
        return res
        }
于 2011-07-20T17:13:00.490 回答