{-
Lexical analysis of a file.
-}

module Parse.Lexical(lexical,lexicalCont,Lex
                ,LexState,PosToken,PosTokenPre,Pos) where

import Util.Extra(Pos,toPos,strPos,insertPos)
import Parse.Lex
import Parse.LexPre
import SysDeps(PackedString,packString,unpackPS)
import TokenId

type PosToken = (Pos,Lex, LexState, [PosTokenPre])
type LexState = [Int]  -- stack of indentations of {} blocks

-- 0 : no active indentation (explicit layout)

lexical :: Bool -> [Char] -> [Char] -> [PosToken]
 -- with H'98 underscore -> filename -> file content -> tokens
-- lexPre basically does the lexing, but afterwards iLex handles
-- indentation for the layout rule
lexical u file l = iLex [0] 0 (beginning (lexPre u file' l))
  where
    file' = packString file
    -- handle pragmas and start and missing "module" header
    beginning :: [PosTokenPre] -> [PosTokenPre]
    beginning toks =
       case toks of
           lp@((f,r,c,L_module):_)    ->  lp
           lp@((f,r,c,L_AVARID t):_) | t==tinterface ->  lp
           (lp@(f,r,c,L_LANNOT):rest) ->  lp: discard_pragma rest
           lp                         ->  ((file',toPos 1 0 0 0,0,L_module)
                                          :(file',toPos 1 0 0 0,0,L_ACONID tMain)
                                          :(file',toPos 1 0 0 0,0,L_where)
                                          :lp)
    discard_pragma (lp@(f,r,c,L_RANNOT):rest) = lp: beginning rest
    discard_pragma (lp@(f,r,c,_):rest)        = lp: discard_pragma rest

lexicalCont :: PosToken -> Either String [PosToken]
lexicalCont (p,t,(i:s@(i':_)),r) =
                if i > 0
                then -- Right ((p,t,s,r) : iLex s i' r) -- not correct?
                     case r of
                       ((f,_,_,_):_) -> Right (piLex f s i' p t r)
                else Left "Layout }"
lexicalCont (p,t, []  ,r) = 
                Left "Layout }"

---  local

iLex :: LexState -> Int -> [PosTokenPre] -> [PosToken]
iLex s i [] = []
iLex s i ((f,p,c,t):pt) = 
  seq p $
  if c > i then
    piLex f s i p t pt
  else if c == i && i /= 0 && t /= L_in then
    seq p' $ (p',L_SEMI',s,pt) : piLex f s i p t pt
  else if c == 0 && i == 0 then
    piLex f s i p t pt
  else
    seq p' $ (p',L_RCURL',s,pt) : iLex s' i' ((f,p,c,t):pt)
  where
  (_:s'@(i':_)) = s
  p' = insertPos p

piLex :: PackedString -> LexState -> Int -> Pos -> Lex -> [PosTokenPre] 
      -> [PosToken]
piLex file s i p tok tr@((f,p',c,t'):pt)
      | tok `elem` [L_let, L_where, L_of, L_do] =
          (p,tok,s,tr)
          : if t' == L_LCURL 
              then seq p' $ (p',L_LCURL, s,pt) : iLex (0:s) 0 pt 
            else
                let p'' = insertPos p' in seq p'' $ (p'', L_LCURL',s,tr)
                : if c > i then
                    seq p' $ piLex f (c:s) c p' t' pt
                  else
                    (p, L_RCURL',s,tr) : iLex s i tr
piLex file s i p L_LCURL  pt =
          (p,L_LCURL,s,pt)
          : iLex (0:s) 0 pt
piLex file s i p L_RCURL  pt = 
      if i == 0
      then case s of 
             (_:s'@(i':_)) -> (p,L_RCURL,s,pt) : iLex s' i' pt
             _             -> failPos file p "Unbalanced '}' (Stack empty)."
      else failPos file p "Unbalanced '}' (No explicit '{' in scope)"
piLex file s i p t pt  =
          (p,t,s,pt)
          : iLex s i pt


failPos :: PackedString -> Pos -> [Char] -> a
failPos file p msg = error ("Internal in " ++ unpackPS file ++ " at " ++ strPos p ++ ": " ++ msg ++ "\n")
