packages feed

SableCC2Hs-0.0.0.1: src/SableCC/LexerDefinition.hs

----------------------------------------------------------------------------
-- |
-- Module      :  SableCC.LexerDefinition
-- Copyright   :  (c) Fontaine 2011
-- License     :  BSD3
--
-- Maintainer  :  fontaine@cs.uni-duesseldorf.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
-- Highlevel definition of SableCC lexer tables.
--
-----------------------------------------------------------------------------

{-# LANGUAGE GeneralizedNewtypeDeriving,RecordWildCards #-}
module SableCC.LexerDefinition
(
  LexerDefinition (..)
  ,makeLexerDefinition
)
where

import SableCC.ParserTypes
import SableCC.XML.ParserDefinitionRaw as Raw
import SableCC.XML.XML2Parser
import Data.Array as Array

import Data.List as List

data LexerDefinition = LexerDefinition {
   lexerModes :: Modes
  ,lexerTokens :: Array LexTokenID Token
  ,modeTransitions :: Array (LexTokenID,ModeID) ModeID
  }

-- | Translate the low-lever parser definition to a higher-level lexer definition.
makeLexerDefinition :: Raw.Parser -> LexerDefinition
makeLexerDefinition Parser {..} = LexerDefinition {..}
  where
    Lexer_Data {..} = parser_lexer_data
    numberOfTokens = length parser_tokens
    numberOfModes  = length $ lexer_modeNames
    mkArray :: Ix i => (Int -> i) -> [e] -> Array i e
    mkArray iType elems = listArray (iType 0, iType $ length elems -1 ) elems

    lexerModes = mkArray ModeID $ makeModes parser_lexer_data

    makeModes Lexer_Data {..} = zipWith3 makeMode lexer_modeNames lexer_accept_table lexer_goto_table
    makeMode :: (LexerModeName,Int) -> [Int] -> [[Lexer_Goto]] -> (ModeName, Mode)
    makeMode (name,_) acceptedStates gotoList
      = (name, Mode $ mkArray LexStateID $ zipWith makeState acceptedStates gotoList)

    makeState :: Int -> [Lexer_Goto] -> State
    makeState token transitions
       = State {
          stateAcceptedToken = if token == -1 then Nothing else Just $ LexTokenID token
         ,stateTransitions = mkIntervalTree $ map mkLexerGoto transitions
         }
       where
         mkLexerGoto :: Lexer_Goto -> (Char, TransitionTarget, Char)
         mkLexerGoto Lexer_Goto{..} = (toEnum lexer_goto_low, mkTransitionTarget lexer_goto_state, toEnum lexer_goto_high)


    mkTransitionTarget :: Int -> TransitionTarget
    mkTransitionTarget x | x >=0  = NotEpsilon $ LexStateID x
    mkTransitionTarget (-1)       = error "illegal TransitionTarget in mkTransitionTarget"
    mkTransitionTarget x | x < -1 = Epsilon $ LexStateID $ -2 -x

    lexerTokens = mkArray LexTokenID parser_tokens

    modeTransitions = transitionsIdentety // (concat $ zipWith collectModeTransitions [LexTokenID 0 ..] parser_tokens)
      where
        transitionsIdentety = array ((LexTokenID 0,ModeID 0),(LexTokenID (numberOfTokens -1), ModeID (numberOfModes-1)))
            [((LexTokenID t,ModeID m),ModeID m) | t <- [0 .. numberOfTokens-1], m <- [0 .. numberOfModes-1]]
        collectModeTransitions :: LexTokenID -> Token -> [((LexTokenID,ModeID),ModeID)]
        collectModeTransitions tokenID token
          = map (\(fromName,toName) -> ((tokenID, resolveModeName fromName),resolveModeName toName))
                (token_transitions token)
        resolveModeName :: String -> ModeID
        resolveModeName name = case List.lookup name lexer_modeNames of
          Nothing -> error $ "unknown lexermode : " ++ name
          Just i -> ModeID i

mkIntervalTree :: [(Char, TransitionTarget, Char)] -> IntervalTree
mkIntervalTree [] = IntervalLeaf
mkIntervalTree iList = mkNode 1 len
  where
    len = length iList
    table = listArray (1,len) iList
    mkNode low high | high < low = IntervalLeaf
    mkNode low high | low == high
        = IntervalNode IntervalLeaf lowChar state highChar IntervalLeaf
      where (lowChar,state,highChar) = table ! low
    mkNode low high
        = IntervalNode
            (mkNode low $ middle -1)
            lowChar state highChar
            (mkNode (middle +1) high)
      where
        (lowChar,state,highChar) = table ! middle
        middle = (low + high) `div` 2