content: import System.IO (stdout, hFlush)
  
  import System.Environment (getArgs)
  
  import qualified Data.Map as M (Map, lookup, insert, empty)
  
  getLines :: IO [String]
  getLines = reverse <$> getLines_ []
    where
      getLines_ xs = do
        line <- getLine
        case line of
          [] -> return xs
          _ -> getLines_ $ line : xs
  
  prompt :: String -> IO String
  prompt p = putStr p >> hFlush stdout >> getLine
  
  getKeyword :: String -> Maybe String
  getKeyword ('<':xs) = getKeyword_ xs []
    where
      getKeyword_ [] _ = Nothing
      getKeyword_ (x:'>':_) acc = Just $ '<' : reverse ('>' : x : acc)
      getKeyword_ (x:xs) acc = getKeyword_ xs $ x : acc
  getKeyword _ = Nothing
  
  parseText :: String -> M.Map String String -> IO String
  parseText [] _ = return []
  parseText line@(l:lx) keywords =
    case getKeyword line of
      Nothing -> (l :) <$> parseText lx keywords
      Just keyword -> do
        let rest = drop (length keyword) line
        case M.lookup keyword keywords of
          Nothing -> do
            newword <- prompt $ "Enter a word for " ++ keyword ++ ": "
            rest_ <- parseText rest $ M.insert keyword newword keywords
            return $ newword ++ rest_
          Just knownword -> do
            rest_ <- parseText rest keywords
            return $ knownword ++ rest_
  
  main :: IO ()
  main = do
    args <- getArgs
    nlines <-
      case args of
        [] -> unlines <$> getLines
        arg:_ -> readFile arg
    nlines_ <- parseText nlines M.empty
    putStrLn ""
    putStrLn nlines_
  