parsing - Haskell/Parsec: cómo uso Text.Parsec.Token con Text.Parsec.Indent(del paquete de sangría)
types indentation (1)
¿Que estás tratando de hacer?
Parece que desea que sus analizadores estén definidos en todas partes como de tipo
Parser Something
(donde Something es el tipo de retorno) y para hacer que esto funcione ocultando y redefiniendo el tipo de Parser
que normalmente se importa desde Text.Parsec.String
o similar. Todavía necesitas importar algo de Text.Parsec.String
, para hacer de Stream una instancia de una mónada; haz esto con la línea:
import Text.Parsec.String ()
Su definición de Parser
es correcta. Alternativamente y de manera equivalente (para aquellos que siguen el chat en los comentarios) puede usar
import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
type Parser = ParsecT String () (State SourcePos)
y posiblemente import Text.Parsec.Indent (IndentParser)
la import Text.Parsec.Indent (IndentParser)
en el archivo en el que aparece esta definición.
Error, error en la pared.
Su problema es que está viendo la parte incorrecta del mensaje de error del compilador. Te estás enfocando en
Couldn''t match expected type `State SourcePos'' with actual type `Identity''
cuando deberías centrarte en
Expected type: P.GenTokenParser ...
Actual type: P.TokenParser ...
¡Se compila!
Donde "importa" los analizadores de Text.Parsec.Token
, lo que realmente hace, por supuesto (como mencionó brevemente) es primero definir un registro de sus parámetros de idioma y luego pasarlo a la función makeTokenParser
, que devuelve un registro que contiene los analizadores de fichas.
Por lo tanto, debes tener algunas líneas que se parezcan a esto:
import qualified Text.Parsec.Token as P
beetleDef :: P.LanguageDef st
beetleDef =
haskellStyle {
parameters, parameters etc.
}
lexer :: P.TokenParser ()
lexer = P.makeTokenParser beetleDef
... pero un P.LanguageDef st
es solo una GenLanguageDef String st Identity
, y un P.TokenParser ()
es realmente una GenTokenParser String () Identity
.
Debe cambiar sus declaraciones de tipo a lo siguiente:
import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
import qualified Text.Parsec.Token as P
beetleDef :: P.GenLanguageDef String st (State SourcePos)
beetleDef =
haskellStyle {
parameters, parameters etc.
}
lexer :: P.GenTokenParser String () (State SourcePos)
lexer = P.makeTokenParser beetleDef
... ¡y eso es! Esto permitirá que sus analizadores de token "importados" tengan el tipo ParsecT String () (State SourcePos) Something
, en lugar de Parsec String () Something
(que es un alias para ParsecT String () Identity Something
) y su código debería compilarse.
(Para mayor generalidad, supongo que podría estar definiendo el tipo de Parser
en un archivo separado e importado por el archivo en el que define sus funciones de analizador reales. Por lo tanto, las dos declaraciones de import
repetidas).
Gracias
Muchas gracias a Daniel Fischer por ayudarme con esto.
El paquete de sangría para el Parsec de Haskell proporciona una manera de analizar lenguajes de estilo de sangría (como Haskell y Python). Redefine el tipo de Parser
, así que, ¿cómo usa las funciones del analizador de tokens exportadas por el módulo Text.Parsec.Token
de Parsec, que son del tipo de Parser
normal?
Fondo
- Parsec es una librería combinadora de analizadores, lo que sea que eso signifique
- IndentParser 0.2.1 es un paquete antiguo que proporciona los dos módulos
Text.ParserCombinators.Parsec.IndentParser
yText.ParserCombinators.Parsec.IndentParser.Token
- indents 0.3.3 es un nuevo paquete que proporciona el módulo único
Text.Parsec.Indent
Parsec viene con Parsec . la mayoría de ellos exportan un montón de analizadores útiles (por ejemplo, newline
de Text.Parsec.Char
, que analiza una nueva línea) o combinadores de analizador (por ejemplo, count np
de Text.Parsec.Combinator
, que ejecuta el analizador p , n veces)
Sin embargo, el módulo Text.Parsec.Token
desea exportar funciones parametrizadas por el usuario con características del lenguaje que se está analizando, de modo que, por ejemplo, la función de braces p
ejecutará el analizador p después de analizar un ''{'' y antes de analizar un ''}'', ignorando cosas como comentarios, cuya sintaxis depende de su idioma.
La forma en que Text.Parsec.Token
logra esto es que exporta una sola función makeTokenParser
, a la que llama, dándole los parámetros de su idioma específico (como el aspecto de un comentario) y devuelve un registro que contiene todas las funciones en Text.Parsec.Token
, adaptado a su idioma según lo especificado.
Por supuesto, en un lenguaje de estilo de sangría, estos deberían ser adaptados aún más (quizás? Aquí no estoy seguro, lo explicaré en un momento), así que observo que el paquete IndentParser (presumiblemente obsoleto) proporciona un módulo Text.ParserCombinators.Parsec.IndentParser.Token
que parece ser un reemplazo Text.Parsec.Token
para Text.Parsec.Token
.
Debo mencionar en algún momento que todos los analizadores Parsec son funciones monádicas, por lo que hacen cosas mágicas con estado para que los mensajes de error puedan indicar en qué línea y columna del archivo de origen apareció el error.
Mi problema
Por un par de pequeñas razones, me parece que el paquete de sangrados es más o menos la versión actual de IndentParser, sin embargo, no proporciona un módulo que se parece a Text.ParserCombinators.Parsec.IndentParser.Token
, solo proporciona Text.Parsec.Indent
, así que me pregunto cómo se hace para obtener todos los analizadores de token de Text.Parsec.Token
(como reserved "something"
que analiza la palabra clave reservada "algo", o braces
que mencioné anteriormente).
Me parece que (el nuevo) Text.Parsec.Indent
funciona mediante algún tipo de magia de estado monádica para determinar en qué columna son los bits del código fuente, de modo que no es necesario modificar los analizadores de token como whiteSpace
from Text.Parsec.Token
, que es probablemente la razón por la que no proporciona un módulo de reemplazo. Pero estoy teniendo un problema con los tipos.
Verá, sin Text.Parsec.Indent
, todos mis analizadores son de tipo Parser Something
donde Something es el tipo devuelto y Parser
es un alias de tipo definido en Text.Parsec.String como
type Parser = Parsec String ()
pero con Text.Parsec.Indent
, en lugar de importar Text.Parsec.String
, utilizo mi propia definición
type Parser a = IndentParser String () a
lo que hace que todos mis analizadores de tipo IndentParser String () Something
, donde IndentParser se define en Text.Parsec.Indent. pero los analizadores de tokens que recibo de makeTokenParser
en Text.Parsec.Token
son del tipo incorrecto.
Si esto no tiene mucho sentido ahora, es porque estoy un poco perdido. El problema de tipo se discute un poco aquí .
El error que recibo es que he intentado reemplazar la definición de Parser
anterior con la otra, pero cuando intento usar uno de los analizadores de token de Text.Parsec.Token
, Text.Parsec.Token
el error de compilación
Couldn''t match expected type `Control.Monad.Trans.State.Lazy.State
Text.Parsec.Pos.SourcePos''
with actual type `Data.Functor.Identity.Identity''
Expected type: P.GenTokenParser
String
()
(Control.Monad.Trans.State.Lazy.State Text.Parsec.Pos.SourcePos)
Actual type: P.TokenParser ()
Campo de golf
- Parsec
- IndentParser (paquete antiguo)
- sangría, proporcionando Text.Parsec.Indent (nuevo paquete)
- alguna discusión de los tipos de analizador con código de ejemplo
- otro ejemplo de uso de Text.Parsec.Indent
Lamentablemente, ninguno de los ejemplos anteriores usa analizadores de token como los de Text.Parsec.Token.