Soy relativamente nuevo en Haskell con los principales conocimientos de programación procedentes de los lenguajes OO. Estoy tratando de escribir un intérprete con un analizador para un lenguaje de programación simple. Hasta ahora tengo el intérprete en un estado con el que estoy razonablemente feliz, pero estoy luchando un poco con el analizador.Análisis en Haskell para un intérprete simple
Aquí es el trozo de código que estoy teniendo problemas con
data IntExp
= IVar Var
| ICon Int
| Add IntExp IntExp
deriving (Read, Show)
whitespace = many1 (char ' ')
parseICon :: Parser IntExp
parseICon =
do x <- many (digit)
return (ICon (read x :: Int))
parseIVar :: Parser IntExp
parseIVar =
do x <- many (letter)
prime <- string "'" <|> string ""
return (IVar (x ++ prime))
parseIntExp :: Parser IntExp
parseIntExp =
do x <- try(parseICon)<|>try(parseIVar)<|>parseAdd
return x
parseAdd :: Parser IntExp
parseAdd =
do x <- parseIntExp
whitespace
string "+"
whitespace
y <- parseIntExp
return (Add x y)
runP :: Show a => Parser a -> String -> IO()
runP p input
= case parse p "" input of
Left err ->
do putStr "parse error at "
print err
Right x -> print x
El lenguaje es un poco más complejo, pero esto es suficiente para mostrar mi problema.
Por lo tanto, en el tipo IntExp ICon es una constante y IVar es una variable, pero ahora sobre el problema. Esto por ejemplo se ejecuta correctamente
RUNP parseAdd "5 + 5"
que da (Añadir (ICON 5) (ICON 5)), que es el resultado esperado. El problema surge cuando se utiliza Ivars en lugar de iconos, por ejemplo,
RUNP parseAdd "n + m"
Esto hace que el programa a error a cabo diciendo que no había un inesperado "n" donde se esperaba un dígito. Esto me lleva a creer que parseIntExp no está funcionando como esperaba. Mi intención era intentar analizar un ICon, si eso falla, intentar analizar un IVar, etc.
Así que o creo que el problema existe en parseIntExp, o que me falta algo en parseIVar y parseICon.
Espero haber dado suficiente información sobre mi problema y estaba lo suficientemente claro.
Gracias por cualquier ayuda que pueda darme!
La respuesta de camccann es muy buena. Algunos consejos adicionales ... El manejo de "Lexing" y espacios en blanco usualmente se realiza con los módulos Parsec.Token y Parsec.Language. El estilo de estos lexers es bastante idiomático: si obtiene las fuentes de Parsec de http://legacy.cs.uu.nl/daan/parsec.html, hay ejemplos simples, como uno para Henk, donde puede copiar principalmente el código de . El módulo Token también te brinda mejores analizadores de números para que puedas evitar usar muchos dígitos y luego leer. Además, la instancia Aplicable para que Parsec obtenga la notación (<$>) y (<*>) solo está disponible con las versiones 3.0 y posteriores. –
Muchas gracias por la respuesta y el consejo. Parece que solucionará mi problema, y debería poder mejorar mi estilo de codificación. ¡Aclamaciones! – Josh
En el ejemplo 'parseICon', preferiría el' ICon. read = << many digit' choice, ya que es más claro de leer. – fuz