forked from purescript/purescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMonad.hs
More file actions
187 lines (159 loc) · 5.54 KB
/
Monad.hs
File metadata and controls
187 lines (159 loc) · 5.54 KB
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
module Language.PureScript.CST.Monad where
import Prelude
import Data.List (sortOn)
import Data.List.NonEmpty qualified as NE
import Data.Ord (comparing)
import Data.Text (Text)
import Language.PureScript.CST.Errors (ParserError, ParserErrorInfo(..), ParserErrorType(..), ParserWarning, ParserWarningType)
import Language.PureScript.CST.Layout (LayoutStack)
import Language.PureScript.CST.Positions (widen)
import Language.PureScript.CST.Types (Comment, LineFeed, SourcePos(..), SourceRange(..), SourceToken(..), Token, TokenAnn(..))
type LexResult = Either (LexState, ParserError) SourceToken
data LexState = LexState
{ lexPos :: SourcePos
, lexLeading :: [Comment LineFeed]
, lexSource :: Text
, lexStack :: LayoutStack
} deriving (Show)
data ParserState = ParserState
{ parserBuff :: [LexResult]
, parserErrors :: [ParserError]
, parserWarnings :: [ParserWarning]
} deriving (Show)
-- | A bare bones, CPS'ed `StateT s (Except e) a`.
newtype ParserM e s a =
Parser (forall r. s -> (s -> e -> r) -> (s -> a -> r) -> r)
type Parser = ParserM ParserError ParserState
instance Functor (ParserM e s) where
{-# INLINE fmap #-}
fmap f (Parser k) =
Parser $ \st kerr ksucc ->
k st kerr (\st' a -> ksucc st' (f a))
instance Applicative (ParserM e s) where
{-# INLINE pure #-}
pure a = Parser $ \st _ k -> k st a
{-# INLINE (<*>) #-}
Parser k1 <*> Parser k2 =
Parser $ \st kerr ksucc ->
k1 st kerr $ \st' f ->
k2 st' kerr $ \st'' a ->
ksucc st'' (f a)
instance Monad (ParserM e s) where
{-# INLINE return #-}
return = pure
{-# INLINE (>>=) #-}
Parser k1 >>= k2 =
Parser $ \st kerr ksucc ->
k1 st kerr $ \st' a -> do
let Parser k3 = k2 a
k3 st' kerr ksucc
runParser :: ParserState -> Parser a -> (ParserState, Either (NE.NonEmpty ParserError) a)
runParser st (Parser k) = k st left right
where
left st'@ParserState {..} err =
(st', Left $ NE.sortBy (comparing errRange) $ err NE.:| parserErrors)
right st'@ParserState {..} res
| null parserErrors = (st', Right res)
| otherwise = (st', Left $ NE.fromList $ sortOn errRange parserErrors)
runTokenParser :: Parser a -> [LexResult] -> Either (NE.NonEmpty ParserError) ([ParserWarning], a)
runTokenParser p buff = fmap (warnings,) res
where
(ParserState _ _ warnings, res) =
runParser initialState p
initialState = ParserState
{ parserBuff = buff
, parserErrors = []
, parserWarnings = []
}
{-# INLINE throw #-}
throw :: e -> ParserM e s a
throw e = Parser $ \st kerr _ -> kerr st e
parseError :: SourceToken -> Parser a
parseError tok = Parser $ \st kerr _ ->
kerr st $ ParserErrorInfo
{ errRange = tokRange . tokAnn $ tok
, errToks = [tok]
, errStack = [] -- TODO parserStack st
, errType = ErrToken
}
mkParserError :: LayoutStack -> [SourceToken] -> a -> ParserErrorInfo a
mkParserError stack toks ty =
ParserErrorInfo
{ errRange = range
, errToks = toks
, errStack = stack
, errType = ty
}
where
range = case NE.nonEmpty toks of
Nothing -> SourceRange (SourcePos 0 0) (SourcePos 0 0)
Just neToks -> widen
(tokRange . tokAnn $ NE.head neToks)
(tokRange . tokAnn $ NE.last neToks)
addFailure :: [SourceToken] -> ParserErrorType -> Parser ()
addFailure toks ty = Parser $ \st _ ksucc ->
ksucc (st { parserErrors = mkParserError [] toks ty : parserErrors st }) ()
parseFail' :: [SourceToken] -> ParserErrorType -> Parser a
parseFail' toks msg = Parser $ \st kerr _ -> kerr st (mkParserError [] toks msg)
parseFail :: SourceToken -> ParserErrorType -> Parser a
parseFail = parseFail' . pure
addWarning :: [SourceToken] -> ParserWarningType -> Parser ()
addWarning toks ty = Parser $ \st _ ksucc ->
ksucc (st { parserWarnings = mkParserError [] toks ty : parserWarnings st }) ()
pushBack :: SourceToken -> Parser ()
pushBack tok = Parser $ \st _ ksucc ->
ksucc (st { parserBuff = Right tok : parserBuff st }) ()
{-# INLINE tryPrefix #-}
tryPrefix :: Parser a -> Parser b -> Parser (Maybe a, b)
tryPrefix (Parser lhs) rhs = Parser $ \st kerr ksucc ->
lhs st
(\_ _ -> do
let Parser k = (Nothing,) <$> rhs
k st kerr ksucc)
(\st' res -> do
let Parser k = (Just res,) <$> rhs
k st' kerr ksucc)
oneOf :: NE.NonEmpty (Parser a) -> Parser a
oneOf parsers = Parser $ \st kerr ksucc -> do
let
prevErrs = parserErrors st
go (st', Right a) _ = (st', Right a)
go _ (st', Right a) = (st', Right a)
go (st1, Left errs1) (st2, Left errs2)
| errRange (NE.last errs2) > errRange (NE.last errs1) = (st2, Left errs2)
| otherwise = (st1, Left errs1)
case foldr1 go $ runParser (st { parserErrors = [] }) <$> parsers of
(st', Left errs) -> kerr (st' { parserErrors = prevErrs <> NE.tail errs}) $ NE.head errs
(st', Right res) -> ksucc (st' { parserErrors = prevErrs }) res
manyDelimited :: Token -> Token -> Token -> Parser a -> Parser [a]
manyDelimited open close sep p = do
_ <- token open
res <- go1
_ <- token close
pure res
where
go1 =
oneOf $ NE.fromList
[ go2 . pure =<< p
, pure []
]
go2 acc =
oneOf $ NE.fromList
[ token sep *> (go2 . (: acc) =<< p)
, pure (reverse acc)
]
token :: Token -> Parser SourceToken
token t = do
t' <- munch
if t == tokValue t'
then pure t'
else parseError t'
munch :: Parser SourceToken
munch = Parser $ \state@ParserState {..} kerr ksucc ->
case parserBuff of
Right tok : parserBuff' ->
ksucc (state { parserBuff = parserBuff' }) tok
Left (_, err) : _ ->
kerr state err
[] ->
error "Empty input"