Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/compiler98/Remove1_3.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


{- ---------------------------------------------------------------------------
Three functions for removing some syntactic sugar:
removeDecls: create selectors for record fields
  mkSel: create a single selector for a named field
removeDo: remove do notation
translateExpRecord: remove record expressions
  (patternmatching, construction and updating)
-}
module Remove1_3(removeDecls,mkSel,removeDo,translateExpRecord) where

import Syntax
import State
import IntState
import TokenId(t_gtgt,t_gtgteq,tfail,t_recConError)
import TokenInt
import Type.Lib(getState,newIdent,getIdent)
import SyntaxPos
import Type.Data(TypeMonad)
import IdKind
import Util.Extra(strPos,mixCommaAnd,noPos,dropRight,isRight)
import List
import Id(Id)
import Maybe


{- ---------------------------------------------------------------------------
Create selectors for record fields.
Done before strongly connected components analysis.
-}

type SelectorMonad a = State () ([Id],IntState,TidFun) a ([Id],IntState,TidFun)
-- () -> ([Id],IntState) -> (a,([Id],IntState))

type TidFun = (TokenId,IdKind) -> Id

{-
Replace DeclConstrs in the declarations by definitions for selectors.
Also collect identifiers of all field names.
-}
removeDecls :: Decls Id -> ((TokenId,IdKind) -> Id) -> IntState
            -> (Decls Id   -- modified declarations
               ,[Id]       -- identifiers of all data constructors
               ,IntState)

removeDecls (DeclsParse decls) tidFun state =
  case mapS removeDecl decls () ([],state,tidFun) of
    (decls,(zcons,state,_)) -> (DeclsParse (concat decls),zcons,state)


{-
Replace a single DeclConstrs by definitions for selectors.
-}
removeDecl :: Decl Id -> SelectorMonad [Decl Id]

removeDecl (DeclConstrs pos zcon cs) =
  remember zcon >>>= \_ ->
  mapS mkSel cs
removeDecl d = unitS [d]


{-
Create the definition for a given selector identfier.
-}
mkSel :: (Pos -- point of definition of selector, i.e in type definition
         ,Id  -- field name id
         ,Id) -- selector name id
         -> SelectorMonad (Decl Id)

mkSel (pos,field,selector) =
  r13Info field >>>= \  (InfoField unique tid ie icon_offs iData iSel) ->
  mapS (mkFun pos) icon_offs >>>= \ alts ->
  unitS (DeclFun pos selector alts)

{-
Make one equation of a selector for given data constructor and offset
-}
mkFun :: Pos
      -> (Id,Int) -- (data constructor, offset)
      -> SelectorMonad (Fun Id)

mkFun pos (c,i) =
  r13Info c >>>= \ conInfo ->
  r13Unique >>>= \ v ->
  r13ModId >>>= \ mi ->
  r13TidFun >>>= \ tidFun ->
  let wildcard = PatWildcard pos {- ExpApplication pos [ExpVar noPos $ tidFun (t_recConError,Var),
                                     ExpLit pos $
                                        LitString Boxed $
                                            mi ++ ": record field was never given a value " ++ strPos pos ++ "." ] -}
      var = ExpVar pos v
      vars = take (arityI conInfo) (repeat wildcard)
               -- arityI safe for constructors :-)
  in
    unitS (Fun [ExpApplication pos (ExpCon pos c : onePos var i vars)]
             (Unguarded var) (DeclsParse []))


{-
Replace list element at given index by given new element.
-}
onePos :: a -> Int -> [a] -> [a]

onePos v 1 (x:xs) = v:xs
onePos v n (x:xs) = x: onePos v (n-1 ::Int) xs


r13Info :: Id -> SelectorMonad Info
r13Info    i down thread@(zcon,state,_) = (fromJust (lookupIS state i),thread)


{- get a new unique id -}
r13Unique :: SelectorMonad Id

r13Unique  down  thread@(zcon,state,tf) =
  case uniqueIS state of
   (u,state) -> (u,(zcon,state,tf))

r13ModId :: SelectorMonad String
r13ModId down thread@(zcon,state,_) = (getModuleId state,thread)

r13TidFun :: SelectorMonad TidFun
r13TidFun down thread@(_,_,tf) = (tf,thread)

remember :: Id -> SelectorMonad ()
remember zcon down thread@(zcons,state,tf) = ((),(zcon:zcons,state,tf))



{- ---------------------------------------------------------------------------
Remove syntactic sugar of do notation.
Done after strongly connected components analysis,
more precisely: called by type checker
-}

{-
Remove syntactic sugar of do notation.
-}
removeDo :: [Stmt Id] -> TypeMonad (Exp Id)

removeDo [StmtExp exp] = unitS exp
removeDo (StmtExp exp:r) =
  let pos = getPos exp
  in
    getIdent (t_gtgt,Var) >>>= \ gtgt ->
    removeDo r >>>= \ exp2 ->
    unitS (ExpApplication pos [ExpVar pos gtgt, exp, exp2])
removeDo (StmtLet decls :r) =
  let pos = getPos decls
  in
    removeDo r >>>= \ exp2 ->
    unitS (ExpLet pos decls exp2)
removeDo (StmtBind pat exp:r) =
  getIdent (t_gtgteq,Var) >>>= \ gtgteq ->
  getState >>>= \ state ->
  removeDo r >>>= \ exp2 ->
  let pos = getPos exp
  in
    if nofail state pat
    then
      -- this is only an optimisation; the else-case is never wrong
      unitS (ExpApplication pos [ExpVar pos gtgteq, exp, ExpLambda pos [pat] exp2])
    else
      getIdent (tfail,Var) >>>= \ fail ->
      newIdent >>>= \ x ->
      let eX = ExpVar pos x
          eFail = ExpApplication pos [ExpVar pos fail
                                     ,ExpLit pos (LitString Boxed "pattern-match failure in do expression")]
      in unitS
           (ExpApplication pos
             [ExpVar pos gtgteq
             ,exp
             ,ExpLambda pos [eX]
               (ExpCase pos eX
                 [Alt pat (Unguarded exp2) (DeclsScc [])
                 ,Alt (PatWildcard pos) (Unguarded eFail) (DeclsScc [])
                 ])])


{-
Test if matching the given pattern cannot fail.
-}
nofail :: IntState -> Pat Id -> Bool

nofail state (ExpCon pos con) =
  case lookupIS state con of
    Just (InfoConstr unique tid ie fix nt fields iType) ->
      case lookupIS state iType of
        Just (InfoData   unique tid exp nt dk) ->
          case dk of
            (DataNewType unboxed constructors) -> True
            (Data unboxed  constrs) -> length constrs == 1
nofail state (ExpVar _ _) = True
nofail state (ExpApplication pos es) = all (nofail state) es
nofail state (PatWildcard _) = True
nofail state (PatAs _ _ pat) = nofail state pat
nofail state (PatIrrefutable pos pat) = True
nofail state _ = False


{- ---------------------------------------------------------------------------
Remove record expressions.
Done after strongly connected components analysis,
more precisely: called by type checker
-}

fieldInfo :: IntState
          -> Field Id
          -> (Id  -- type constructor
             ,([(Id,Int)] -- data constructors with offsets for field
             ,Exp Id))   -- expressions from "field=exp"

fieldInfo state (FieldExp pos field exp) =
  case lookupIS state field of
    Just (InfoField unique tid ie icon_offs idata iSel) -> (idata,(icon_offs,exp))


{- lookup value in association list; if not there, then return default value -}
fixArg :: Eq a => [(a,b)] -> (a,b) -> b

fixArg given (i,def) =
  case lookup i given of
    Just e -> e
    Nothing -> def


{- construct alternative for record updating for one data constructor -}
fixAlt :: Pos
       -> [Exp Id]   -- arguments for offsets
       -> (Id,[Int]) -- (data constructor, offsets)
       -> IntState
       -> (Alt Id,IntState)

fixAlt pos exps (con,offsets) state =
  (Alt (ExpApplication pos (econ:vars))
       (Unguarded
         (ExpApplication pos
           (econ : map (fixArg (zip offsets exps)) (zip nargs vars))))
       (DeclsScc [])
  ,state')
  where
  nargs = [1 .. arityIS state con]
  (newNIds,state') = uniqueISs state nargs
  vars = map (ExpVar noPos . snd) newNIds
  econ = ExpCon pos con


getOffsets :: [[(Id,Int)]] -> Id -> Either (Id,[Maybe Int]) (Id,[Int])

getOffsets icon_offs con =
  let offsets   = map (\ icon_off -> lookup con icon_off) icon_offs
  in if all isJust offsets
     then Right (con,map fromJust offsets)
     else Left (con,offsets)


{-
Replace record expression exp{field1=exp1,...} by a non-record expression.
Used for record patterns as well.
(in fact, undefined constructor arguments are filled with wildcard patterns)
-}
translateExpRecord :: Exp Id -> [Field Id] -> IntState
                -> (Either String (Exp Id),IntState)

translateExpRecord e@(ExpRecord exp' fields') fields state =
  translateExpRecord exp' (fields ++ fields') state
translateExpRecord e@(ExpCon pos con) fields state =
  let coes = map (fieldInfo state) fields
  in  if firstIsEqual coes
      then
        let (icon_offs,exps) = unzip (map snd coes)
        in case getOffsets icon_offs con of
            Right (con,offsets) ->
              (Right (ExpApplication pos (e:map (fixArg (zip offsets exps))
                                             (zip [1 .. arityIS state con]
                                                (repeat (PatWildcard  pos))
                                             ) ))
              ,state)
            Left (con,offsets) ->
              (Left (errField1 state pos con offsets fields)
              ,state)
      else (Left (errField2 state fields),state)
translateExpRecord exp [] state =
  (Left (errField4 (getPos exp)),state)
translateExpRecord exp fields state =
  let coes@((t,_):_) = map (fieldInfo state) fields
  in  if firstIsEqual coes -- all fields belong to same data type
      then
        let (icon_offs,exps) = unzip (map snd coes)
            pos = getPos exp
        in case (partition isRight . map (getOffsets icon_offs) .
                 constrsI  . fromJust . lookupIS state) t of
          ([],_)  -> (Left (errField3 state fields),state)
          (rps,_) -> let consFixAlt rps (alts,state) =
                           case fixAlt pos exps rps state of
                             (alt,state') -> (alt:alts,state')
                         (alts,state') = foldr consFixAlt ([],state)
                                           (map dropRight rps)
                     in (Right (ExpCase (getPos exp) exp alts), state')
      else (Left (errField2 state fields),state)


{- Test if all first components are equal. -}
firstIsEqual :: Eq a => [(a,b)] -> Bool

firstIsEqual [] = True
firstIsEqual ((k,_):kvs) = all (k==) (map fst kvs)


errField1 :: IntState -> Pos -> Id -> [Maybe a] -> [Field Id] -> String

errField1 state pos con offsets fields =
  "The field(s)" ++
  mixCommaAnd (map (\(_,FieldExp pos field exp) -> ' ':show (tidIS state field)
                    ++ " at " ++ strPos pos)
                   (filter (isNothing.fst) (zip offsets fields)))
  ++ " do(es) not belong to constructor " ++ show (tidIS state con) ++
  " used at " ++ strPos pos ++ "."


errField2 :: IntState -> [Field Id] -> String

errField2 state fields =
  "The fields" ++
  mixCommaAnd (map (\(FieldExp pos field exp) -> ' ': show (tidIS state field)
                      ++ " at " ++ strPos pos)
                   fields)
  ++ " do not belong to the same type."


errField3 :: IntState -> [Field Id] -> String

errField3 state fields =
  "The fields " ++
  mixCommaAnd (map (\(FieldExp pos field exp) -> ' ':show (tidIS state field)
                    ++ " at " ++ strPos pos)
               fields)
  ++ " do not belong to the same constructor."


errField4 :: Pos -> [Char]

errField4 pos =
  "The update of the expression at " ++ strPos pos ++
  " uses an empty list of fields."

{- End Remove1_3 ------------------------------------------------------------}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].