aboutsummaryrefslogtreecommitdiff
path: root/src/Language/SimpleShell/Parser/Function.hs
blob: 5bc2007eca6a06a66597a6e8f432943995d70c47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}

module Language.SimpleShell.Parser.Function
  ( functionP
  , functionP0
  )
where


import Language.SimpleShell.AST.Expr (Expr)
import Language.SimpleShell.AST.Function (Function(..))
import Language.SimpleShell.AST.Name (FunName, VarName)
import Language.SimpleShell.AST.SimpleType (SimpleType, FunSig, Typed)
import Language.SimpleShell.Parser.Common.Lexeme (symbol)
import Language.SimpleShell.Parser.Common.Root (RootParser)
import Language.SimpleShell.Parser.Expr
  ( ExprContext(ExprContext)
  , strongTermP
  , strongTermP0_
  )
import Language.SimpleShell.Parser.Name (nameP, keyword)
import Language.SimpleShell.Parser.SimpleType (simpleTypeP, forceType)

import Control.Applicative ((<|>))
import Control.Monad (guard)
import Control.Monad.Reader (ReaderT, withReaderT)
import Data.List (nub)
import Data.Map (Map)
import qualified Data.Map as Map (fromList)
import Data.Text (Text)
import Data.Tuple (swap)
import Text.Megaparsec (MonadParsec, sepBy)


type FunctionParser0 = RootParser
type FunctionParser = ReaderT (Map FunName FunSig) RootParser


functionPrefixP
  :: (MonadParsec e Text m) => m (SimpleType, FunName, [Typed VarName])
functionPrefixP = do
  keyword "function"
  t' <- simpleTypeP
  fname <- nameP
  params <- symbol "(" *> paramP `sepBy` symbol "," <* symbol ")"
  symbol "="
  return (t', fname, params)

functionP :: FunctionParser Function
functionP = do
  (t', fname, params) <- functionPrefixP
  let vars = map snd params
  checkNoDupVarNames vars
  body <- forceType t' (bodyP params) <|> fail "Function return type mismatch."
  return $ Function fname vars body

-- | First-pass function parser.
functionP0 :: FunctionParser0 (FunName, FunSig)
functionP0 = do
  (t', fname, params) <- functionPrefixP
  let ts = map fst params
  bodyP_
  return (fname, (t', ts))


checkNoDupVarNames :: [VarName] -> FunctionParser ()
checkNoDupVarNames vars =
  guard (length (nub vars) == length vars)  -- TODO: inefficient
    <|> fail "Duplicate variable name."


paramP :: (MonadParsec e Text m) => m (Typed VarName)
paramP = (,) <$> simpleTypeP <*> nameP

bodyP :: [Typed VarName] -> FunctionParser (Typed Expr)
bodyP params = withReaderT f strongTermP
  where
    f funs = ExprContext funs (Map.fromList $ map swap params)

bodyP_ :: FunctionParser0 ()
bodyP_ = strongTermP0_