2013-12-15 23 views
20

Uczę się używać Alexa i Happy do napisania małego kompilatora. Chcę zachować informacje o liniach i kolumnach dla moich węzłów AST, aby móc dostarczać użytkownikowi znaczące komunikaty o błędach. Aby zilustrować, jak mam to zrobić, napisałem mały przykład (zobacz poniższy kod) i chciałbym się dowiedzieć, czy sposób, w jaki podchodziłem do problemu (po podłączeniu AlexPosn do tokenów, dołączanie pola atrybutów polimorficznych do węzłów AST) , za pomocą tkPos i astAttr) jest dobry styl lub jeśli istnieją lepsze sposoby na obsługę informacji o pozycji.Zarządzanie informacjami o pozycji z Alexem i 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

znalazł rozwiązanie, które chcesz udostępnić? Zastanawiając się dokładnie tak samo – mfaerevaag

Odpowiedz

1

Osobiście byłoby całkiem ok ze stylem opisałeś . Jest jednak bardzo ręczny i miałem nadzieję, że przynajmniej podam jedną alternatywę, którą łatwiej będzie zarządzać.

Jeśli spojrzysz nieco dalej w stronę documentation for alex wrappers, zauważysz, że opakowania monad i monadstate zawierają informacje o pozycji. Minusem jest to, że teraz masz wszystko opakowane w monadę i nieco komplikuje parser. Jednakże, poprzez zawinięcie go w monadę, wynikiem analizy jest Alex a, co oznacza, że ​​masz pełny dostęp do informacji o linii i kolumnie podczas tworzenia węzłów ast. Teraz po prostu usuwa część płyty kotła z lexera i nie robi nic więcej.

W ten sposób można również nosić przy sobie token AlexState, ale może to być niepotrzebne.

Jeśli potrzebujesz pomocy rzeczywiście ustalające parsera obsłużyć owijkę monada/monadstate napisałem odpowiedź, w jaki sposób udało mi się dostać to działa tutaj: How to use an Alex monadic lexer with Happy?