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
.