title: Managing position information with Alex and Happy
body: <p>I'm learning to use Alex and Happy to write a small compiler.  I want to maintain line and column information for my AST nodes so that I can provide meaningful error messages to the user.  To illustrate how I plan to do it, I wrote a small example (see code below), and I'd like to know if the way I approached the problem (having AlexPosn attached to the tokens, attaching a polymorphic attribute field to AST nodes, using tkPos and astAttr) is good style or if there are better ways to handle position information.</p>
  
  <p>Lexer.x:</p>
  
  <pre><code>{
  module Lexer where
  }
  
  %wrapper "posn"
  
  $white = [\ \t\n]
  
  tokens :-
  
  $white+ ;
  [xX] { \pos s -&gt; MkToken pos X }
  "+"  { \pos s -&gt; MkToken pos Plus }
  "*"  { \pos s -&gt; MkToken pos Times }
  "("  { \pos s -&gt; MkToken pos LParen }
  ")"  { \pos s -&gt; MkToken pos RParen }
  
  {
  data Token = MkToken AlexPosn TokenClass
             deriving (Show, Eq)
  
  data TokenClass = X
                  | Plus
                  | Times
                  | LParen
                  | RParen
                    deriving (Show, Eq)
  
  tkPos :: Token -&gt; (Int, Int)
  tkPos (MkToken (AlexPn _ line col) _) = (line, col)
  }
  </code></pre>
  
  <p>Parser.y:</p>
  
  <pre><code>{
  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 -&gt; a
  astAttr (NX a)       = a
  astAttr (NMul _ _ a) = a
  astAttr (NAdd _ _ a) = a
  
  happyError :: [Token] -&gt; a
  happyError _ = error "parse error"
  }
  </code></pre>
  
  <p>Main.hs:</p>
  
  <pre><code>module Main where
  
  import Lexer
  import Parser
  
  main :: IO ()
  main = do
    s &lt;- getContents
    let toks = alexScanTokens s
    print $ simple toks
  </code></pre>