2014-05-13 5 views
7

Ecco il mio problema: sto cercando di scrivere un parser sfruttando la potenza dei pattern attivi in ​​F #. La firma di base di una funzione di parsing è la seguenteF #: uso avanzato dei pattern attivi

LazyList<Token> -> 'a * LazyList<Token> 

Significato ci vuole un elenco pigro di token, e restituisce il risultato del parsing e la nuova lista di token dopo l'analisi, in modo da seguire design funzionale.

Ora, come passo successivo, posso definire modelli attivi che aiuteranno Mi incontri alcuni costrutti direttamente nelle espressioni partita, questa convenzione è

let inline (|QualName|_|) token_stream = 
    match parse_qualified_name token_stream with 
     | Some id_list, new_stream -> Some (id_list, new_stream) 
     | None, new_stream -> None 

let inline (|Tok|_|) token_stream = 
    match token_stream with 
     | Cons (token, tail) -> Some(token.variant, tail) 
     | _ -> None 

e poi abbino analizzare i risultati in modo alto livello in questo modo

Il problema che ho con questo codice è che ogni nuovo costrutto corrispondente è annidato, che non è leggibile, specialmente se si ha una lunga catena di risultati da abbinare. Mi piacerebbe avere la possibilità di definire un operatore di corrispondenza come ad esempio l'operatore :: per la lista, che mi avrebbe permesso di effettuare le seguenti operazioni:

let parse_subprogram_profile = function 
    | Tok (Kw (KwProcedure | KwFunction)) :: 
     QualName(qual_name) :: 
     Tok (Punc (OpeningPar)) :: stream_tail as token_stream -> 
     // some code 
    | token_stream -> None, token_stream 

Ma non credo che una cosa del genere è possibile in F #. Accetterei persino un disegno in cui devo chiamare uno specifico pattern attivo "ChainN" dove N è il numero di elementi che voglio analizzare, ma non so come progettare una funzione del genere se è possibile.

Qualche consiglio o indicazione su questo? C'è un disegno evidente che non ho visto?

risposta

3

Anch'io avevo in mente qualcosa del genere, ma in realtà ho smesso di andare per questo esatto progetto. Qualcosa che puoi fare è usare liste reali.

In tal caso, si avrà una CombinedList composta da (in primo luogo) una lista normale che funge da buffer e (secondariamente) una lista pigra.

Quando si desidera abbinare contro un modello, si può fare:

match tokens.EnsureBuffer(4) with 
| el1 :: el2 :: remaining    -> (el1.v+el2.v, tokens.SetBuffer(remaining)) 
| el3 :: el4 :: el5 :: el6 :: remaining -> (el1.v-el2.v+el3.v-el4.v, tokens.SetBuffer(remaining)) 

dove EnsureBuffer e SetBuffer possono sia mutare "gettoni" e restituirlo o restituirlo se non hanno bisogno cambiare o restituire un nuove istanze altrimenti.

Questo risolverebbe il tuo problema? François