packages feed

hugs2yc-0.1: Yhc/Core/FrontEnd/Hugs/ParseUtil.hs

-- Parser utility functions for Hugs Core.

module Yhc.Core.FrontEnd.Hugs.ParseUtil where

import Yhc.Core.Extra
import Data.Char
import Data.List
import Data.Maybe
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Token
import Text.ParserCombinators.Parsec.Language (haskellDef)

-- Tokenizer for Hugs Core, based on Haskell language definitions.

tkp = makeTokenParser haskellDef {
  identStart = letter <|> oneOf "_'"
 ,identLetter = alphaNum <|> oneOf "_'"
 ,reservedNames = ["module", "data", "primitive", "case", "in", "of", "let",
                   "let_", "_fatbar", "[]", "()", "::"]
}

-- State of the Hugs core parser

data PState = PState {
  modName :: String                -- current Core module name
 ,funName :: String                -- current function name
 ,fbStack :: [Int]                 -- stack of FATBARs: number identifies
                                   -- a currently introduced FATBAR binding:
                                   -- the number will be appended to "v_fail_"
                                   -- If the stack contains only a zero, default
                                   -- binding is in effect, that is a code to throw
                                   -- a pattern match exception.
                                   -- to get a variable bound to the current FATBAR
 ,counter :: Int                   -- unique numbers producer
 ,autoFuncs :: [CoreFunc]          -- any functions not included in the source Hugs Core
                                   -- and generated during the conversion.
}

-- Qualified identifier: data structure to distinguish between
-- a function and a data constructor.

data QUALID = QCTOR String
            | QFUNC String
            deriving (Show, Eq)

-- Two-letter internal variable name generator.

nameGen = [a : b : [] | a <- ['a' .. 'z'], b <- ['0' .. '9']]

-- Parser. Reads in Hugs Core syntax, returns Yhc Core structure.

-- ident = id [. id...]

ident = (identifier tkp) `sepBy1` (dot tkp) >>= return . concat . intersperse "."

-- Special names for data constructors: [], (), #n for tuples of n.
-- Tuples will be recoded in Yhc way (,...,)

nil = try $ squares tkp (return "[]")

unit = try $ parens tkp (return "()")

tuple = try $ do
  char '#'
  n <- natural tkp
  return $ "(" ++ replicate (fromIntegral n - 1) ',' ++ ")"

-- Parser for a qualified (dot-separated) name, distinguishes
-- between tuples (which are not qualified) functions and data constructors.
-- Tuples will be artificially qualified with Prelude.

pQUALID :: GenParser Char a QUALID

pQUALID = 
      (tuple >>= return . QCTOR . ("Hugs.Prelude;" ++)) 
  <|> (try $ do
         let anyid = nil <|> infctor <|> unit <|> tuple <|> identifier tkp <|> operator tkp
             fstupr [] = False
             fstupr (h : t) = isUpper h
             funcid [] = False
             funcid (h : t) = isLower h || h `elem` "'_"
             infctid [] = False
             infctid (':' : _) = True
             infctid _ = False
             isoper s = either (const False) (const True) (parse (operator tkp) "" s)
         parts <- anyid `sepBy` dot tkp
         let strap = reverse parts
             x [] = (QCTOR, 0)
             x [_] = (QCTOR, 0)
             x (c : "Make" : md) | all fstupr (c : md) = (QCTOR, 2)
             x (f : md) | all fstupr md && funcid f = (QFUNC, 1)
             x (c : md) | all fstupr md && c `elem` ["[]", "()"] = (QCTOR, 1)
             x (c : md) | all fstupr md && infctid c = (QCTOR, 1)
             x (o : md) | all fstupr md && isoper o = (QFUNC, 1)
             x ps | all fstupr ps = (QCTOR, 1)
                  | otherwise = (QCTOR, 0)
         case x strap of
           (_, 0) -> pzero
           (c, n) -> do 
             let (m, f) = splitAt n strap
             (return . c . concat) $ reverse 
               (intersperse "." m ++ [";"] ++ intersperse "." f))

-- Infix constructor: colon followed by an operator

infctor = try $ do
  c <- char ':'
  o <- (operator tkp) <|> (reserved tkp "=" >> return "=") <|> (whiteSpace tkp >> return "")
  return (c : o)

-- module = "module" id ";" [datas] [funcs]

pModule :: GenParser Char PState Core

pModule = do
  reserved tkp "module"
  mid <- ident
  semi tkp
  updateState (\st -> st {modName = mid})
  datas <- many pData
  prims <- many pPrim
  funcs <- many pFunc
  cmts <- whiteSpace tkp
  autof <- getState >>= return . autoFuncs
  return $ Core {
    coreName = mid, 
    coreImports = [], 
    coreDatas = datas, 
    coreFuncs = prims ++ funcs ++ autof}

-- primitive = "primitive" QUALID args ";"

pPrim = do
  reserved tkp "primitive"
  pid <- pQUALID
  pargs <- many ident
  semi tkp
  case pid of
    QCTOR _ -> pzero
    QFUNC pf -> return $ CorePrim {
      coreFuncName = pf,
      corePrimArity = length pargs,
      corePrimExternal = dropModule pf,
      corePrimConv = "hugsprim",
      corePrimImport = True,
      corePrimTypes = []}

-- data = "data" id "=" [ctors] ";"

pData = do
  reserved tkp "data"
  did <- (nil <|> unit <|> ident <|> tuple)
  symbol tkp "="
  ctors <- pCtor `sepBy` (symbol tkp "|")
  semi tkp
  mid <- getState >>= return . modName
  return $ CoreData {
    coreDataName = mid ++ ";" ++ did,
    coreDataTypes = [],
    coreDataCtors = ctors}

-- ctor = id ["*"]
-- Constructor parser.

pCtor = do
  cid <- (nil <|> infctor <|> unit <|> ident <|> tuple) 
  stars <- many (symbol tkp "*")
  let cargs = take (length stars) nameGen
      cflds = zip cargs (repeat Nothing)
  mid <- getState >>= return . modName
  let qcid = mid ++ ";" ++ cid
  return $ CoreCtor {
    coreCtorName = qcid,
    coreCtorFields = cflds}
 
-- function = id [id] "=" expr

pFunc = do
  fname <- identifier tkp <|> parens tkp (operator tkp)
  fargs <- many ident
  symbol tkp "="
  mid <- getState >>= return . modName
  let xfname = mid ++ ";" ++ fname
  updateState (\st -> st {funName = xfname})
  fexpr <- pExpr
  semi tkp
  return $ CoreFunc {
    coreFuncName = xfname,
    coreFuncArgs = fargs,
    coreFuncBody = fexpr}

-- Typed constants. They have format (value :: TYPE) where TYPE may be
-- Int | Integer | Float | Double

pTypConst = try $ parens tkp (tyFloat <|> tyInt) >>= return

tyInt = try $ do
  n <- integer tkp
  reserved tkp "::"
  t <- many1 letter
  case t of
    "Int" -> return $ CoreLit (CoreInt $ fromIntegral n)
    "Integer" -> return $ CoreLit (CoreInteger n)
    _ -> fail "tyInt"
  
tyFloat = try $ do
  n <- float tkp
  reserved tkp "::"
  t <- many1 letter
  case t of
    "Float" -> return $ CoreLit (CoreFloat $ realToFrac n)
    "Double" -> return $ CoreLit (CoreDouble n)
    _ -> fail "tyFloat"
  

-- General expression parsers.

pString = try $ do s <- stringLiteral tkp
                   return $ CoreLit (CoreStr s)

pChar = try $ do c <- charLiteral tkp
                 return $ CoreLit (CoreChr c)

pInt = try $ do n <- integer tkp 
                return $ CoreLit (CoreInteger n)

pFloat = try $ do f <- float tkp
                  return $ CoreLit (CoreDouble f)

pVar = do i <- identifier tkp
          return $ CoreVar i

pQVar = try $ do q <- pQUALID
                 let cf = case q of
                       QCTOR c -> CoreCon c
                       QFUNC f -> CoreFun f
                 return cf

pParExpr = try $ parens tkp pExpr >>= return

pLetExpr = try $ do
  reserved tkp "let"
  bnds <- many1 pBind
  reserved tkp "in"
  e <- pExpr
  return $ coreLet bnds e

pBind = try $ do
  vn <- ident
  symbol tkp "="
  be <- pExpr
  semi tkp
  return (vn, be)

-- FATBAR means that some expression will be evaluated as a default of a CASE.

pFatBar = try $ do
  reserved tkp "let_" 
  reserved tkp "_fatbar"
  symbol tkp "="
  fbexpr <- pExpr
  semi tkp
  reserved tkp "in"
  doCase (Just fbexpr)

-- This means that CASE reuses the currently bound FATBAR variable.
-- If no FATBAR has been specified, this is a pattern match failure.

pCase = doCase Nothing

-- Common part for all CASE constructions. If new FATBAR was introduced.
-- it creates a new binding for a "v_fail_N" variable. Otherwise
-- it just generates a CASE construction. If a stack is empty (only
-- 0 on top), failure expression will be bound to default failure
-- handler (which throws a pattern match exception).

doCase mbfb = try $ do
  reserved tkp "case"
  scrut <- pExpr
  reserved tkp "of"
  doCase' mbfb scrut

-- A new FATBAR binding was introduced: push it on the stack and
-- bind default pattern to it.

doCase' (Just fb) scrut = do
  cnt <- getCount
  updateState (\st -> st {fbStack = cnt : fbStack st})
  let failvar = "v_fail_" ++ show cnt
  cc <- doCase' Nothing scrut
  updateState (\st -> st {fbStack = tail (fbStack st)})
  return $ coreLet [(failvar, fb)] cc

-- No FATBAR was introduced. Use whatever is on the stack. If 0
-- is on the stack, bind default pattern to the default failure
-- handler, otherwise to the failure variable whose name is
-- determined by the stack contents.

doCase' Nothing scrut = do
  fbnum <- getState >>= return . head . fbStack
  fun <- getState >>= return . funName
  let failvar = "v_fail_" ++ show fbnum
  cpats <- braces tkp ((pConPat <|> pCharPat <|> pDefPat failvar) `sepBy1` semi tkp)
  let dflet = if fbnum == 0 then coreLet [(failvar, defFailExpr fun)] else id
      cc = CoreCase scrut cpats
  return $ dflet cc

-- Character literal pattern.

pCharPat = try $ do
  (CoreLit c) <- pChar
  symbol tkp "->"
  patexp <- pExpr
  return (PatLit {patLit = c}, patexp)

-- Default pattern is given a variable bound to a current FATBAR. If
-- no actual expression is specified to handle the default case,
-- FATBAR will be invoked.

pDefPat fv = try $ do
  symbol tkp "_"
  symbol tkp "->"
  defexp <- (try (symbol tkp "_fatbar" >> return Nothing)) <|> (pExpr >>= return . Just)
  let patexp = fromMaybe (CoreVar fv) defexp
  return (PatDefault, patexp)

-- Conctructor pattern takes care that the name given corresponds to
-- a constructor.

pConPat = try $ do
  q <- pQUALID
  cargs <- many ident
  symbol tkp "->"
  patexp <- pExpr
  case q of
    QCTOR c -> return (PatCon {patCon = c, patVars = cargs}, patexp)
    _ -> pzero

-- A simple expression: one that does not include function application,
-- or a parenthesized expression (i. e. with one term at tolevel)

pSmpExpr = pString <|> pChar <|> pTypConst <|> pFloat <|> 
           pInt <|> pQVar <|> pVar <|> pLetExpr <|>
           pCase <|> pFatBar <|> pParExpr

-- Toplevel expression that may be an application

pExpr :: GenParser Char PState CoreExpr

pExpr = try $ do (f:es) <- many1 pSmpExpr
                 return $ coreApp f es

-- Default failure expression
-- Prelude.throw (Hugs.Prelude.PatternMatchFail fun)

defFailExpr fun = 
  let failmsg = "pattern match failure in function " ++ fun
      primthrow = CoreFun "Hugs.Prelude;throw"
      exctor = CoreCon "Hugs.Prelude;PatternMatchFail"
      litstr = CoreLit (CoreStr failmsg)
  in  coreApp primthrow [coreApp exctor [litstr]]

-- Calling this function returns the current unique value counter
-- and increments the counter.

getCount = do cnt <- getState >>= return . counter
              updateState (\st -> st {counter = cnt + 1})
              return $ cnt + 1