2013-12-15 10 views
20

Sto imparando ad usare Alex e Happy per scrivere un piccolo compilatore. Voglio mantenere le informazioni di riga e colonna per i miei nodi AST in modo che possa fornire all'utente significativi messaggi di errore. Per illustrare come ho intenzione di farlo, ho scritto un piccolo esempio (vedi codice sotto), e vorrei sapere se il modo in cui ho affrontato il problema (avendo AlexPosn collegato ai token, associando un campo di attributo polimorfo ai nodi AST , usando tkPos e astAttr) è buono stile o se ci sono modi migliori per gestire le informazioni sulla posizione.Gestione delle informazioni sulla posizione con Alex e Happy

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 
+1

trovato una soluzione che si desidera condividere? Chiedendo esattamente lo stesso – mfaerevaag

risposta

1

io personalmente sarebbe piuttosto male con lo stile che hai descritto . Tuttavia, è molto manuale e speravo di fornire almeno un'alternativa che fosse più facile da gestire.

Se si osserva un po 'più in basso il documentation for alex wrappers, si noterà che i wrapper monad e monadstate contengono entrambi informazioni sulla posizione. Il rovescio della medaglia è che ora hai l'intera cosa avvolta in una monade e questo complica leggermente il parser. Tuttavia, avvolgendolo in una monade, il risultato del parse è un Alex a che significa che hai pieno accesso alle informazioni di riga e colonna quando crei i tuoi nodi. Ora questo semplicemente rimuove alcuni dei piatti della caldaia dal lexer e non fa molto di più.

In questo modo, è possibile portare con sé anche l'AlexState con il token, ma ciò potrebbe non essere necessario.

Se hai bisogno di aiuto in realtà che fissa il parser per gestire un involucro monade/monadstate, ho scritto una risposta su come sono riuscito a farlo funzionare qui: How to use an Alex monadic lexer with Happy?

Problemi correlati