[F#] Monadic parser
От: Димчанский Литва http://dimchansky.github.io/
Дата: 12.03.09 13:34
Оценка:
Играюсь с монадическими парсерами, в смысле пытаюсь реализовать на F# (в теме функциональногог порграммирования недавно, сильно прошу не пинать). Сделал такой набросок:
#light

type Result<'s,'r> = seq<list<'s> * 'r>
type Parser<'s,'r> = Parser of (list<'s> -> Result<'s, 'r>)

let parserFun (Parser(f)) = f

let succeed v : Parser<'s,'r> = Parser(fun i -> seq{yield (i,v)})
let fail : Parser<'s,'r> = Parser(fun _ -> Seq.empty)

let satisfy p = Parser(fun i -> match i with
                                | h::t when p h -> seq{yield (t, h)}
                                | _             -> Seq.empty)
let symbol c = satisfy (fun s -> c = s)
                                                   
let (<|>) (p1:Parser<'s,'r>) (p2:Parser<'s,'r>) : Parser<'s,'r> = 
    Parser(fun i -> Seq.append (parserFun p1 i) (parserFun p2 i))
    
let (<*>) (p1:Parser<'s,'r>) (p2:Parser<'s,'t>) : Parser<'s,'r*'t> = 
    Parser(fun i -> 
              i |> parserFun p1 
                |> Seq.map (fun (rest1, result1) -> 
                    rest1 |> parserFun p2
                          |> Seq.map(fun (rest2,result2) -> (rest2,(result1,result2))))
                |> Seq.concat)
                
let (=>>) (p:Parser<'s,'r>) (f:'r->'t) : Parser<'s,'t> =
    Parser(fun i -> i |> parserFun p                                     
                      |> Seq.map (fun (rest, result) -> (rest, f result)))

let bindP (p:Parser<'s,'r>) (f:'r->Parser<'s,'t>) : Parser<'s,'t>= 
    Parser(fun i -> i |> parserFun p 
                      |> Seq.map (fun (rest, result) -> rest |> parserFun(f result))
                      |> Seq.concat)

let rec many (p:Parser<'s,'r>) : Parser<'s,'r list> =
    Parser(fun i -> i |> parserFun p 
                      |> Seq.map (fun (rest1, result1) -> 
                            rest1 |> parserFun (many p)
                                  |> Seq.map (fun (rest2, result2) -> (rest2, result1::result2)))
                      |> Seq.concat)
    <|> succeed []
                                                             
let many1 p : Parser<'s,'r list> = (p <*> many p) =>> (fun (a,b) -> a::b)

type ParserBuilder() =
    member b.Return(v) = succeed v
    member b.Bind(p,f) = bindP p f
    member b.Zero() = fail
                                    
let parser = ParserBuilder()

Можно ли как-то переписать функцию many, чтобы рекурсия стала хвостовой, а то она отваливается на совершенно смешных размерах входных данных?

P.S.:
Параллельно пытаюсь смотреть на реализацию таких парсеров на Haskell, так смотрю там как-то все проще получается и при этом орудуют только понятием списка. Стоило мне seq результатов заменить на list, как отжирания памяти стали просто громадными. Ну я подозреваю почему это происходит — вычисления списка-результата происходят энергично на каждом этапе, потому заменил его на seq. В Haskell видимо с этим проще из-за ленивости.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.