packages feed

yapb-0.2.2: src/parserlib/LoadAutomaton.hs

module LoadAutomaton where

import AutomatonType
import SaveProdRules(tokenizeLhs)
import System.IO
import Text.Read (readMaybe)
import Data.Char (isSpace)

loadAutomaton :: String -> String -> String
              -> IO (ActionTable, GotoTable, ProdRules)
loadAutomaton grammarFileName actionTblFileName gotoTblFileName = do
  grammarStr   <- readFile grammarFileName
  actionTblStr <- readFile actionTblFileName
  gotoTblStr   <- readFile gotoTblFileName

  actionTbl <- loadActionTbl actionTblStr
  gotoTbl   <- loadGotoTbl gotoTblStr
  prodRules <- loadProdRules grammarStr

  return (actionTbl, gotoTbl, prodRules)

-- Load action table
loadActionTbl :: String -> IO ActionTable
loadActionTbl str = tokenizeStateNumInAction str

tokenizeStateNumInAction :: String -> IO ActionTable
tokenizeStateNumInAction str =   
  case lex str of
    [] -> return []
    [("", therest)] -> return []
    [(stateNum, therest)] -> do
      (terminal, action, actTbl) <- tokenizeTerminalInAction therest
      case readMaybe stateNum :: Maybe Int of
        Just stateNum_i -> return $ ((stateNum_i, terminal), action) : actTbl
        Nothing -> error $ "[tokenizeStateNumInAction] Unexpected state number\n" ++ stateNum

tokenizeTerminalInAction :: String -> IO (String, Action, ActionTable)
tokenizeTerminalInAction str =
  let str' = dropWhile Data.Char.isSpace str
      terminal = takeWhile (not . Data.Char.isSpace) str'
      str'' = drop (length terminal) str'
      lexRes = if length terminal > 0 then [(terminal,str'')] else [("",str'')]
  in
  case lexRes of
    [] -> fail "No terminal found (1)"
    [("", therest)] -> fail "No terminal found (2)"
    [(terminal, therest)] -> do
      (action, actTbl) <- tokenizeActioninAction therest
      return (terminal, action, actTbl)

tokenizeActioninAction :: String -> IO (Action, ActionTable)
tokenizeActioninAction str =
  case lex str of
    [] -> fail "No action found (1)"
    [("", therest)] -> fail "No action found (2)"
    [(action, therest)] -> do
      case action of
        "Shift" -> do
          tokenizeShiftReduceStateNumInAction therest Shift
        "Reduce" -> do
          tokenizeShiftReduceStateNumInAction therest Reduce
        "Accept" -> do
          actTbl <- tokenizeStateNumInAction therest
          return (Accept, actTbl)
        _ -> fail ("Unexpected action: " ++ action)

tokenizeShiftReduceStateNumInAction :: String -> (Int -> Action)
  -> IO (Action, ActionTable)
tokenizeShiftReduceStateNumInAction str fn =
  case lex str of
    [] -> fail "No shift/reduce state number found (1)"
    [("", therest)] -> fail "No shift/reduce state number found (2)"
    [(stateNum, therest)] -> do
      actTbl <- tokenizeStateNumInAction therest
      case readMaybe stateNum :: Maybe Int of
        Just stateNum_i -> return (fn stateNum_i, actTbl)
        Nothing ->
          error $ "[tokenizeShiftReduceStateNumInAction] unexpected state number:"
                    ++ stateNum
      

-- Load goto table
loadGotoTbl :: String -> IO GotoTable
loadGotoTbl str = tokenizeStateNumInGoto str

tokenizeStateNumInGoto :: String -> IO GotoTable
tokenizeStateNumInGoto str =
  case lex str of
    [] -> return []
    [("", therest)] -> return []
    [(stateNum, therest)] -> do
      (nonterminal, toStateNum, actTbl) <- tokenizeNonterminalInGoto therest
      case (readMaybe stateNum :: Maybe Int, readMaybe toStateNum :: Maybe Int) of
        (Just stateNum_i, Just toStateNum_i) -> return $ ((stateNum_i, nonterminal), toStateNum_i) : actTbl
        (_,_) -> error $ "[tokenizeStateNumInGoto] Unexpected state numbers: "
                              ++ stateNum ++ " or " ++ toStateNum

tokenizeNonterminalInGoto :: String -> IO (String, String, GotoTable)
tokenizeNonterminalInGoto str =
  case lex str of
    [] -> fail "No nonterminal found (1)"
    [("", therest)] -> fail "No nonterminal found (2)"
    [(nonterminal,therest)] -> do
      (toStateNum, actTbl) <- tokenizeToStateNumInGoto therest
      return (nonterminal, toStateNum, actTbl)

tokenizeToStateNumInGoto :: String -> IO (String, GotoTable)
tokenizeToStateNumInGoto str =
  case lex str of
    [] -> fail "No to-state found (1)"
    [("", therest)] -> fail "No to-state found (2)"
    [(toStateNum,therest)] -> do
      actTbl <- tokenizeStateNumInGoto therest
      return (toStateNum, actTbl)

-- Load production rules
loadProdRules :: String -> IO ProdRules
loadProdRules str = do
  numLhsRhsList <- mapM tokenizeNumInProdRules (splitWithCR str)
  return [ (lhs, rhs) | (i, lhs, rhs) <- numLhsRhsList ]

tokenizeNumInProdRules :: String -> IO (Int, String, [String])
tokenizeNumInProdRules str =
  case lex str of 
    [] -> fail "No rule number found (1)"
    [("", therest)] -> fail "No rule number found (2)"
    [(ruleNumStr, therest)] -> do
      (lhs, rhs) <- tokenizeColonInProdRules therest
      case readMaybe ruleNumStr :: Maybe Int of
        Just ruleNumStr_i -> return (ruleNumStr_i, lhs, rhs)
        Nothing -> error $ "[tokenizeNumInProdRules] Unexpected rule number: "
                              ++ ruleNumStr

tokenizeColonInProdRules :: String -> IO (String, [String])
tokenizeColonInProdRules str =
  case lex str of
    [] -> fail "No colon found (1)"
    [("", therest)] -> fail "No colon found (2)"
    [(colon, therest)] -> do
      let lhsRhs = tokenizeLhs therest
      return (head lhsRhs, tail lhsRhs)
    

splitWithCR :: String -> [String]
splitWithCR str =
  [ line | line <- splitWithCR' "" str, line /= "" ]

splitWithCR' :: String -> String -> [String]
splitWithCR' app [] = (reverse app) : []
splitWithCR' app ('\n':therest) = (reverse app) : splitWithCR' "" therest
splitWithCR' app (ch:therest) = splitWithCR' (ch : app) therest