module Transform.Declaration (combineAnnotations, toExpr) where
import Control.Applicative ((<$>))
import qualified AST.Annotation as A
import qualified AST.Declaration as D
import qualified AST.Expression.General as E
import qualified AST.Expression.Source as Source
import qualified AST.Expression.Valid as Valid
import qualified AST.Expression.Canonical as Canonical
import qualified AST.Module as Module
import qualified AST.Pattern as P
import qualified AST.Type as T
import qualified AST.Variable as Var
import qualified Transform.Expression as Expr
import qualified Transform.Definition as Def
combineAnnotations :: [D.SourceDecl] -> Either String [D.ValidDecl]
combineAnnotations =
go
where
errorMessage kind name =
"Syntax Error: The type annotation for " ++ kind ++ " '" ++
name ++ "' must be directly above its definition."
exprCombineAnnotations =
Expr.crawlLet Def.combineAnnotations
go decls =
case decls of
[] ->
return []
D.Datatype name tvars ctors : rest ->
(:) (D.Datatype name tvars ctors) <$> go rest
D.TypeAlias name tvars alias : rest ->
(:) (D.TypeAlias name tvars alias) <$> go rest
D.Fixity assoc prec op : rest ->
(:) (D.Fixity assoc prec op) <$> go rest
D.Definition def : defRest ->
case def of
Source.Definition pat expr ->
do expr' <- exprCombineAnnotations expr
let def' = Valid.Definition pat expr' Nothing
(:) (D.Definition def') <$> go defRest
Source.TypeAnnotation name tipe ->
case defRest of
D.Definition (Source.Definition pat@(P.Var name') expr) : rest
| name == name' ->
do expr' <- exprCombineAnnotations expr
let def' = Valid.Definition pat expr' (Just tipe)
(:) (D.Definition def') <$> go rest
_ -> Left (errorMessage "value" name)
D.Port port : rest ->
case port of
D.PortAnnotation name tipe ->
case rest of
D.Port (D.PortDefinition name' expr) : restRest
| name == name' ->
do expr' <- exprCombineAnnotations expr
let port' = D.Out name expr' tipe
(:) (D.Port port') <$> go restRest
_ ->
(:) (D.Port (D.In name tipe)) <$> go rest
D.PortDefinition name _ ->
Left (errorMessage "port" name)
toExpr :: Module.Name -> [D.CanonicalDecl] -> [Canonical.Def]
toExpr moduleName =
concatMap (toDefs moduleName)
toDefs :: Module.Name -> D.CanonicalDecl -> [Canonical.Def]
toDefs moduleName decl =
let typeVar = Var.Canonical (Var.Module moduleName) in
case decl of
D.Definition def ->
[def]
D.Datatype name tvars constructors ->
concatMap toDefs' constructors
where
toDefs' (ctor, tipes) =
let vars = take (length tipes) arguments
tbody = T.App (T.Type (typeVar name)) (map T.Var tvars)
body = A.none . E.Data ctor $ map (A.none . E.localVar) vars
in
[ definition ctor (buildFunction body vars) (foldr T.Lambda tbody tipes) ]
D.TypeAlias name tvars tipe@(T.Record fields ext) ->
[ definition name (buildFunction record vars) (foldr T.Lambda result args) ]
where
result =
T.Aliased (typeVar name) (zip tvars (map T.Var tvars)) (T.Holey tipe)
args =
map snd fields ++ maybe [] (:[]) ext
var = A.none . E.localVar
vars = take (length args) arguments
efields = zip (map fst fields) (map var vars)
record =
case ext of
Nothing -> A.none $ E.Record efields
Just _ ->
foldl (\r (f,v) -> A.none $ E.Insert r f v) (var $ last vars) efields
D.TypeAlias _ _ _ ->
[]
D.Port (D.CanonicalPort impl) ->
let body = A.none (E.Port impl)
in
case impl of
E.In name tipe ->
[ definition name body (T.portType tipe) ]
E.Out name _expr tipe ->
[ definition name body (T.portType tipe) ]
E.Task name _expr tipe ->
[ definition name body (T.portType tipe) ]
D.Fixity _ _ _ ->
[]
arguments :: [String]
arguments =
map (:[]) ['a'..'z'] ++ map (\n -> "_" ++ show (n :: Int)) [1..]
buildFunction :: Canonical.Expr -> [String] -> Canonical.Expr
buildFunction body@(A.A s _) vars =
foldr (\p e -> A.A s (E.Lambda p e)) body (map P.Var vars)
definition :: String -> Canonical.Expr -> T.CanonicalType -> Canonical.Def
definition name expr tipe =
Canonical.Definition (P.Var name) expr (Just tipe)