-
Notifications
You must be signed in to change notification settings - Fork 53
/
Copy pathTerm.hs
233 lines (208 loc) · 7.93 KB
/
Term.hs
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
{-# LANGUAGE OverloadedStrings #-}
-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Parser for terms of the Swarm language.
module Swarm.Language.Parser.Term where
import Control.Lens (view, (^.))
import Control.Monad (guard, join)
import Control.Monad.Combinators.Expr
import Data.Foldable (Foldable (..), asum)
import Data.Functor (($>))
import Data.Map (Map)
import Data.Map qualified as M
import Data.Maybe (mapMaybe)
import Data.Set qualified as S
import Data.Set.Lens (setOf)
import Swarm.Language.Parser.Core
import Swarm.Language.Parser.Lex
import Swarm.Language.Parser.Record (parseRecord)
import Swarm.Language.Parser.Type
import Swarm.Language.Syntax
import Swarm.Language.Syntax.Direction
import Swarm.Language.Types
import Swarm.Util (failT, findDup)
import Text.Megaparsec hiding (runParser)
import Text.Megaparsec.Char
import Prelude hiding (Foldable (..))
-- Imports for doctests (cabal-docspec needs this)
-- $setup
-- >>> import qualified Data.Map.Strict as M
--------------------------------------------------
-- Parser
parseDirection :: Parser Direction
parseDirection = asum (map alternative allDirs) <?> "direction constant"
where
alternative d = d <$ (reserved . directionSyntax) d
-- | Parse Const as reserved words (e.g. @Fail <$ reserved "fail"@)
parseConst :: Parser Const
parseConst = do
ver <- view languageVersion
asum (map (alternative ver) consts) <?> "built-in user function"
where
consts = filter isUserFunc allConst
alternative ver c = c <$ reserved (syntax $ constInfo' ver c)
-- Version 0.6 of the language had a constant named @return@, which
-- is now renamed to @pure@
constInfo' SwarmLang0_6 Pure = (constInfo Pure) {syntax = "return"}
constInfo' _ c = constInfo c
-- | Parse an atomic term, optionally trailed by record projections like @t.x.y.z@.
-- Record projection binds more tightly than function application.
parseTermAtom :: Parser Syntax
parseTermAtom = do
s1 <- parseTermAtom2
ps <- many (symbol "." *> parseLocG tmVar)
return $ foldl' (\(Syntax l1 t) (l2, x) -> Syntax (l1 <> l2) (TProj t x)) s1 ps
-- | Parse an atomic term.
parseTermAtom2 :: Parser Syntax
parseTermAtom2 =
parseLoc
( TUnit <$ symbol "()"
<|> TConst <$> parseConst
<|> TVar <$> tmVar
<|> TDir <$> parseDirection
<|> TInt <$> integer
<|> TText <$> textLiteral
<|> TBool <$> ((True <$ reserved "true") <|> (False <$ reserved "false"))
<|> reserved "require"
*> ( ( TRequireDevice
<$> (textLiteral <?> "device name in double quotes")
)
<|> ( (TRequire . fromIntegral <$> integer)
<*> (textLiteral <?> "entity name in double quotes")
)
)
<|> uncurry SRequirements <$> (reserved "requirements" *> match parseTerm)
<|> SLam
<$> (symbol "\\" *> locTmVar)
<*> optional (symbol ":" *> parseType)
<*> (symbol "." *> parseTerm)
<|> sLet LSLet
<$> (reserved "let" *> locTmVar)
<*> optional (symbol ":" *> parsePolytype)
<*> (symbol "=" *> parseTerm)
<*> (reserved "in" *> parseTerm)
<|> sLet LSDef
<$> (reserved "def" *> locTmVar)
<*> optional (symbol ":" *> parsePolytype)
<*> (symbol "=" *> parseTerm <* reserved "end")
<*> (optional (symbol ";") *> (parseTerm <|> (eof $> sNoop)))
<|> STydef
<$> (reserved "tydef" *> locTyName)
<*> join (bindTydef <$> many tyVar <*> (symbol "=" *> parseType <* reserved "end"))
<*> pure Nothing
<*> (optional (symbol ";") *> (parseTerm <|> (eof $> sNoop)))
<|> SRcd <$> brackets (parseRecord (optional (symbol "=" *> parseTerm)))
<|> parens (view sTerm . mkTuple <$> (parseTerm `sepBy` symbol ","))
)
<|> parseLoc (TDelay (TConst Noop) <$ try (symbol "{" *> symbol "}"))
<|> parseLoc (SDelay <$> braces parseTerm)
<|> parseLoc (view antiquoting >>= (guard . (== AllowAntiquoting)) >> parseAntiquotation)
-- | Construct an 'SLet', automatically filling in the Boolean field
-- indicating whether it is recursive.
sLet :: LetSyntax -> LocVar -> Maybe RawPolytype -> Syntax -> Syntax -> Term
sLet ls x ty t1 = SLet ls (lvVar x `S.member` setOf freeVarsV t1) x ty Nothing mempty t1
sNoop :: Syntax
sNoop = STerm (TConst Noop)
-- | Create a polytype from a list of variable binders and a type.
-- Ensure that no binder is repeated, and all type variables in the
-- type are present in the list of binders (/i.e./ the type contains
-- no free type variables).
bindTydef :: [Var] -> Type -> Parser Polytype
bindTydef xs ty
| Just repeated <- findDup xs =
failT ["Duplicate variable on left-hand side of tydef:", repeated]
| not (S.null free) =
failT $
"Undefined type variable(s) on right-hand side of tydef:" : S.toList free
| otherwise = return . absQuantify $ mkPoly xs ty
where
free = tyVars ty `S.difference` S.fromList xs
parseAntiquotation :: Parser Term
parseAntiquotation =
TAntiText <$> (lexeme . try) (symbol "$str:" *> tmVar)
<|> TAntiInt <$> (lexeme . try) (symbol "$int:" *> tmVar)
-- | Parse a Swarm language term.
parseTerm :: Parser Syntax
parseTerm = sepEndBy1 parseStmt (symbol ";") >>= mkBindChain
mkBindChain :: [Stmt] -> Parser Syntax
mkBindChain stmts = case last stmts of
Binder x _ -> return $ foldr mkBind (STerm (TApp (TConst Pure) (TVar (lvVar x)))) stmts
BareTerm t -> return $ foldr mkBind t (init stmts)
where
mkBind (BareTerm t1) t2 = loc Nothing t1 t2 $ SBind Nothing Nothing Nothing Nothing t1 t2
mkBind (Binder x t1) t2 = loc (Just x) t1 t2 $ SBind (Just x) Nothing Nothing Nothing t1 t2
loc mx a b = Syntax $ maybe NoLoc lvSrcLoc mx <> (a ^. sLoc) <> (b ^. sLoc)
data Stmt
= BareTerm Syntax
| Binder LocVar Syntax
deriving (Show)
parseStmt :: Parser Stmt
parseStmt =
mkStmt <$> optional (try (locTmVar <* symbol "<-")) <*> parseExpr
mkStmt :: Maybe LocVar -> Syntax -> Stmt
mkStmt Nothing = BareTerm
mkStmt (Just x) = Binder x
parseExpr :: Parser Syntax
parseExpr =
parseLoc $ ascribe <$> parseExpr' <*> optional (symbol ":" *> parsePolytype)
where
ascribe :: Syntax -> Maybe RawPolytype -> Term
ascribe s Nothing = s ^. sTerm
ascribe s (Just ty) = SAnnotate s ty
parseExpr' :: Parser Syntax
parseExpr' = makeExprParser parseTermAtom table
where
table = snd <$> M.toDescList tableMap
tableMap =
M.unionsWith
(++)
[ M.singleton 9 [InfixL (exprLoc2 $ SApp <$ string "")]
, binOps
, unOps
]
-- add location for ExprParser by combining all
exprLoc2 :: Parser (Syntax -> Syntax -> Term) -> Parser (Syntax -> Syntax -> Syntax)
exprLoc2 p = do
(l, f) <- parseLocG p
pure $ \s1 s2 -> Syntax (l <> (s1 ^. sLoc) <> (s2 ^. sLoc)) $ f s1 s2
-- | Precedences and parsers of binary operators.
--
-- >>> M.map length binOps
-- fromList [(0,1),(2,1),(3,1),(4,6),(6,3),(7,2),(8,1)]
binOps :: Map Int [Operator Parser Syntax]
binOps = M.unionsWith (++) $ mapMaybe binOpToTuple allConst
where
binOpToTuple c = do
let ci = constInfo c
ConstMBinOp assoc <- pure (constMeta ci)
let assI = case assoc of
L -> InfixL
N -> InfixN
R -> InfixR
pure $
M.singleton
(fixity ci)
[assI (mkOp c <$ operator (syntax ci))]
-- | Precedences and parsers of unary operators (currently only 'Neg').
--
-- >>> M.map length unOps
-- fromList [(7,1)]
unOps :: Map Int [Operator Parser Syntax]
unOps = M.unionsWith (++) $ mapMaybe unOpToTuple allConst
where
unOpToTuple c = do
let ci = constInfo c
ConstMUnOp assoc <- pure (constMeta ci)
let assI = case assoc of
P -> Prefix
S -> Postfix
pure $
M.singleton
(fixity ci)
[assI (exprLoc1 $ SApp (noLoc $ TConst c) <$ operator (syntax ci))]
-- combine location for ExprParser
exprLoc1 :: Parser (Syntax -> Term) -> Parser (Syntax -> Syntax)
exprLoc1 p = do
(l, f) <- parseLocG p
pure $ \s -> Syntax (l <> s ^. sLoc) $ f s