Come devo elaborare in modo ricorsivo un file in Haskell?

4

Fondamentalmente sto espandendo un programma di alfabetizzazione in Haskell. Ci sono due cose che devo cercare di espandere. Ecco un esempio di file:

program.c.lit:
==============
Some comments in the source file that I can ignore
    <main>=
    code.. code ...
    code..
    <a> 

Some comments in the source file that I can ignore
    <a>=
    code... code...
    code..

In un file, ho bisogno di esaminare ogni <some var> e sostituirlo con la sua definizione di corrispondenza definita al di sotto di <some var>= . Tutte le linee che sono rientrate con 4 spazi al di sotto di un <some defn>= sono incluse in quel blocco. Alla fine dovrei avere un file composto solo da code ... lines.

Come dovrei affrontare questo problema in termini di elaborazione dei file, qual è un buon approccio? Mi sento a mio agio con la ricorsione, meno a mio agio con le monadi.

    
posta cdosborn 31.07.2014 - 19:57
fonte

1 risposta

2

Farò alcune supposizioni nella mia risposta. Sentiti libero di correggermi se uno di essi non è corretto:

  • lo stai facendo come esercizio
  • desideri solo incorporare le definizioni di livello superiore
  • esiste al massimo una sola definizione per un dato nome di variabile
  • vuoi semplificare una determinata definizione inserendo tutte le variabili conosciute

Se stai facendo questo per un progetto "reale", o vuoi fare trasformazioni più complesse, ti consiglio di guardare i pacchetti esistenti come Haskell-src-exts .

Ora, se vuoi una soluzione robusta, avrai bisogno di quelle funzioni:

parse 
    :: String        -- the input text
    -> [Declaration] -- a datastructure representing the expression
inline
    :: Declaration   -- the definition to inline
    -> Declaration   -- the definition to modify
    -> Declaration   -- the result definition
showDecl :: Declaration -> String
simplify 
    :: String        -- the input text
    -> String        -- the variable name to simplify
    -> String        -- the result

La dichiarazione può essere semplice come (String, String) (il nome della variabile e la sua definizione). Si noti che sto usando definizioni tipicamente vaghe. Sto anche usando String, che potrebbe non essere la soluzione più efficiente. Puoi prendere in considerazione l'utilizzo di Text per velocizzare le cose.

La funzione di analisi può essere complicata. Raccomando di usare la libreria parsec. Usa una monade, ma non sentirsi intimidito da essa. Usare le monadi non richiede di comprenderle a un livello fondamentale. In realtà, è vero il contrario. Vedi qui per un'introduzione. È possibile visualizzare il tipo di parsec come un'istruzione che consuma una parte di testo e può restituire un risultato.

parse input = /case analysis of / runParser (many parseBlock) () "source name" input

parseBlock = do
    skipComment
    parseDef

parseDef = do
    varName <- parseVar
    string " = "
    definition <- parseExpr
    return $ Declaration varName defintion

parseExpr = ...

Qui, i test unitari sono i tuoi migliori amici. Assicurati che tutti i parser più piccoli funzionino bene prima di combinarli. Presta attenzione alla semantica del backtracking (cioè il combinatore try ) perché con questo puoi facilmente spararti nel piede.

La funzione inline è più semplice. Sarà solo una ricerca ben congegnata e sostituita. La funzione di semplificazione è solo una questione di chiamare parse , estrarre la dichiarazione per semplificare e chiamare inline con tutti gli altri su di esso. Nessuna ricorsione necessaria.

In alternativa, la tua funzione inline può essere di tipo Declaration -> String -> String e modificare direttamente la stringa di input. In questo modo non dovrai scrivere la funzione showDecl .

    
risposta data 01.08.2014 - 10:26
fonte