2011-07-20 13 views
5

Próbuję parsować standardowe typy proste (w znaczeniu rachunku lambda) przy użyciu FParsec, ale mam trudności z przechodzeniem od stylu Lex/Yacc do tego używanego w FParsec, szczególnie z szacunkiem do definicji rekursywnych.Parsowanie prostych typów w FParsec

Przykłady typów Staram się analizować to:

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

A oto moja próba:


    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"` 

Kiedy ładuję to do interakcji ve ostatnia linia powoduje przepełnienie stosu (ironicznie dość trudne do wyszukania w tych dniach). Mogę sobie wyobrazić, dlaczego, biorąc pod uwagę, że istnieją referencje rekursywne, ale przypuszczam, że przewinięcie z jednym znacznikiem uniemożliwiłoby dokonanie pierwszego (w nawiasie) wyboru w stype. Zakładam więc, że musi to być wybór arr, który wybiera stype i tak dalej. Ale jak zapobiec tej pętli?

Interesują mnie komentarze na temat idiomatycznego wykorzystania biblioteki, a także poprawki do mojego rozwiązania.

+0

To może być chcesz chcesz: http://stackoverflow.com/questions/6186230/recursive-grammars-in-fparsec –

+0

Dzięki, mam czytać to pytanie/odpowiedź, ale nie bardzo rozumiem, jak przenieść odpowiedź na mój problem. Ale będę miał inne spojrzenie. – rneatherway

Odpowiedz

4

Podczas pracy z FParsec trzeba analizować sekwencje z pomocą sequence combinators zamiast lewej rekursji. W twoim przypadku można na przykład użyć sepBy1 COMBINATOR:

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" 
+0

Ciągle zapominam sepBy. Niezła odpowiedź! –

+0

To wielkie dzięki, podobnie jak użycie >>%. Nie uwzględnia to jednak prawostronności "->". Zmieniłem definicję stypeRef na 'chainr1 elem ((str_ws" -> ") >>% (fun t1 t2 -> Arrow (t1, t2)))), chociaż prawdopodobnie mógłbyś użyć prawostowarzyszącej wersji' List .reduce' również. – rneatherway

+0

Wymieniłem 'reduce' na' reduceBack', aby przeanalizować strzałkę jako operator z prawym skojarzeniem. Znajduję proste rozwiązanie, używając 'sepBy' i' reduceBack' 'czystszego niż implementacja 'chainr', zwłaszcza, że ​​funkcja redukcji jest stała. Ponieważ strzałka jest prawostronnym operatorem, zawsze musisz zbudować jakiś pośredni stos lub listę z elementami sekwencji, więc użycie 'chainr1' tutaj również nie ma przewagi wydajności. Wręcz przeciwnie, powinno być nieco wolniej, ponieważ musi również rejestrować przeanalizowane funkcje redukcji. –

0

To działa, ale prawdopodobnie jest zhakowane razem zbyt wiele. Kompilacja type Parser... pochodzi z dokumentów FParsec, aby uniknąć błędów kompilatora.

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 
     }