parsing - guia - Gestionando la información de posición con Alex y Happy.
qgis español (1)
Estoy aprendiendo a usar Alex y Happy para escribir un pequeño compilador. Quiero mantener la información de línea y columna para mis nodos AST para poder proporcionar mensajes de error significativos al usuario. Para ilustrar cómo planeo hacerlo, escribí un pequeño ejemplo (vea el código a continuación), y me gustaría saber si abordé el problema (con AlexPosn conectado a los tokens, adjuntando un campo de atributo polimórfico a los nodos AST , usar tkPos y astAttr) es un buen estilo o si hay mejores maneras de manejar la información de posición.
Lexer.x:
{
module Lexer where
}
%wrapper "posn"
$white = [/ /t/n]
tokens :-
$white+ ;
[xX] { /pos s -> MkToken pos X }
"+" { /pos s -> MkToken pos Plus }
"*" { /pos s -> MkToken pos Times }
"(" { /pos s -> MkToken pos LParen }
")" { /pos s -> MkToken pos RParen }
{
data Token = MkToken AlexPosn TokenClass
deriving (Show, Eq)
data TokenClass = X
| Plus
| Times
| LParen
| RParen
deriving (Show, Eq)
tkPos :: Token -> (Int, Int)
tkPos (MkToken (AlexPn _ line col) _) = (line, col)
}
Parser.y:
{
module Parser where
import Lexer
}
%name simple
%tokentype { Token }
%token
''('' { MkToken _ LParen }
'')'' { MkToken _ RParen }
''+'' { MkToken _ Plus }
''*'' { MkToken _ Times }
x { MkToken _ X }
%%
Expr : Term ''+'' Expr { NAdd $1 $3 (astAttr $1) }
| Term { $1 }
Term : Factor ''*'' Term { NMul $1 $3 (astAttr $1) }
| Factor { $1 }
Factor : x { NX (tkPos $1) }
| ''('' Expr '')'' { $2 }
{
data AST a = NX a
| NMul (AST a) (AST a) a
| NAdd (AST a) (AST a) a
deriving (Show, Eq)
astAttr :: AST a -> a
astAttr (NX a) = a
astAttr (NMul _ _ a) = a
astAttr (NAdd _ _ a) = a
happyError :: [Token] -> a
happyError _ = error "parse error"
}
Main.hs:
module Main where
import Lexer
import Parser
main :: IO ()
main = do
s <- getContents
let toks = alexScanTokens s
print $ simple toks
Personalmente estaría bastante bien con el estilo que has descrito. Sin embargo, es muy manual y esperaba al menos proporcionar una alternativa que pudiera ser más fácil de administrar.
Si mira un poco más abajo en la documentación de envoltorios de alex , notará que tanto los envoltorios de mónada como de monoestado contienen información de posición. El inconveniente es que ahora tienes todo envuelto en una mónada y eso complica ligeramente el analizador. Sin embargo, al envolverlo en una mónada, el resultado del análisis es un Alex a
que significa que tiene acceso completo a la información de línea y columna cuando crea sus nodos de ast. Ahora, esto simplemente elimina parte de la placa de la caldera del lexer y no hace mucho más.
Al hacer esto, también podría llevar el AlexState con su token, pero eso podría ser innecesario.
Si necesita ayuda para arreglar el analizador para manejar un envoltorio monadstate / monad, escribí una respuesta sobre cómo logré que funcionara aquí: ¿Cómo usar un lexer monádico Alex con Happy?