git.iko.soy/rowtype-yoga/purescript-native
on 38d43e4 help sign in
283 lines 13 kB history raw
1-----------------------------------------------------------------------------
2--
3-- Module : PureScript.Parser.Values
4-- Copyright : (c) Phil Freeman 2013
5-- License : MIT
6--
7-- Maintainer : Phil Freeman <paf31@cantab.net>
8-- Stability : experimental
9-- Portability :
10--
11-- |
12--
13-----------------------------------------------------------------------------
15module PureScript.Parser.Values (
16 parseValue,
17 parseBinder
18) where
20import PureScript.Values
21import PureScript.Names
22import PureScript.Declarations
23import PureScript.Parser.State
24import Data.Function (on)
25import Data.List
26import Data.Functor.Identity
27import qualified Data.Map as M
28import qualified PureScript.Parser.Common as C
29import Control.Applicative
30import qualified Text.Parsec as P
31import Text.Parsec.Expr
32import Control.Monad
33import Control.Arrow (Arrow(..))
34import PureScript.Parser.Types
35import PureScript.Types
37booleanLiteral :: P.Parsec String ParseState Bool
38booleanLiteral = (C.reserved "true" >> return True) P.<|> (C.reserved "false" >> return False)
40parseNumericLiteral :: P.Parsec String ParseState Value
41parseNumericLiteral = NumericLiteral <$> C.integerOrFloat
43parseStringLiteral :: P.Parsec String ParseState Value
44parseStringLiteral = StringLiteral <$> C.stringLiteral
46parseBooleanLiteral :: P.Parsec String ParseState Value
47parseBooleanLiteral = BooleanLiteral <$> booleanLiteral
49parseArrayLiteral :: P.Parsec String ParseState Value
50parseArrayLiteral = ArrayLiteral <$> (C.squares $ parseValue `P.sepBy` (C.indented *> C.comma))
52parseObjectLiteral :: P.Parsec String ParseState Value
53parseObjectLiteral = ObjectLiteral <$> (C.braces $ parseIdentifierAndValue `P.sepBy` (C.indented *> C.comma))
55parseIdentifierAndValue :: P.Parsec String ParseState (String, Value)
56parseIdentifierAndValue = (,) <$> (C.indented *> C.identifier <* C.indented <* C.colon)
57 <*> (C.indented *> parseValue)
59parseAbs :: P.Parsec String ParseState Value
60parseAbs = do
61 C.lexeme $ P.char '\\'
62 args <- (C.indented *> C.parseIdent) `P.sepBy` (C.indented *> C.comma)
63 C.lexeme $ C.indented *> P.string "->"
64 value <- parseValue
65 return $ Abs args value
67parseApp :: P.Parsec String ParseState Value
68parseApp = App <$> parseValue
69 <*> (C.indented *> C.parens (parseValue `P.sepBy` (C.indented *> C.comma)))
71parseVar :: P.Parsec String ParseState Value
72parseVar = Var <$> C.parseIdent
74parseConstructor :: P.Parsec String ParseState Value
75parseConstructor = Constructor <$> C.properName
77parseCase :: P.Parsec String ParseState Value
78parseCase = Case <$> (P.between (C.reserved "case") (C.indented *> C.reserved "of") parseValue)
79 <*> (C.indented *> C.mark (P.many (C.same *> C.mark parseCaseAlternative)))
81parseCaseAlternative :: P.Parsec String ParseState (Binder, Value)
82parseCaseAlternative = (,) <$> (parseGuardedBinder <* C.lexeme (P.string "->")) <*> parseValue
84parseIfThenElse :: P.Parsec String ParseState Value
85parseIfThenElse = IfThenElse <$> (C.reserved "if" *> C.indented *> parseValue)
86 <*> (C.indented *> C.reserved "then" *> C.indented *> parseValue)
87 <*> (C.indented *> C.reserved "else" *> C.indented *> parseValue)
89parseBlock :: P.Parsec String ParseState Value
90parseBlock = Block <$> (C.reserved "do" *> parseManyStatements)
92parseManyStatements :: P.Parsec String ParseState [Statement]
93parseManyStatements = C.indented *> (C.mark $ P.many $ C.same *> C.mark parseStatement)
95parseValueAtom :: P.Parsec String ParseState Value
96parseValueAtom = C.indented *> P.choice (map P.try
97 [ parseNumericLiteral
98 , parseStringLiteral
99 , parseBooleanLiteral
100 , parseArrayLiteral
101 , parseObjectLiteral
102 , parseAbs
103 , parseVar
104 , parseConstructor
105 , parseBlock
106 , parseCase
107 , parseIfThenElse
108 , C.parens parseValue ])
110parseValue :: P.Parsec String ParseState Value
111parseValue = do
112 customOps <- fixities <$> P.getState
113 buildExpressionParser (operators customOps) . C.buildPostfixParser postfixTable2 $ indexersAndAccessors
114 where
115 indexersAndAccessors = C.buildPostfixParser postfixTable1 $ parseValueAtom
116 postfixTable1 = [ Accessor <$> (C.indented *> C.dot *> C.indented *> C.identifier)
117 , Indexer <$> (C.indented *> C.squares parseValue) ]
118 postfixTable2 = [ C.indented *> indexersAndAccessors >>= \t2 -> return (\t1 -> App t1 [t2])
119 , flip App <$> (C.indented *> C.parens (parseValue `P.sepBy` (C.indented *> C.comma)))
120 , flip TypedValue <$> (P.try $ C.lexeme (C.indented *> P.string "::") *> parsePolyType) ]
121 operators user =
122 [ [ Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "!") >> return (Unary Not)
123 , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "~") >> return (Unary BitwiseNot)
124 , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "-") >> return (Unary Negate)
125 , Prefix $ C.lexeme (P.try $ C.indented *> C.reservedOp "+") >> return id ]
126 ] ++ customOperatorTable user ++
127 [ [ Infix (C.lexeme (P.try (C.indented *> C.parseIdentInfix) >>= \ident -> return $ \t1 t2 -> App (App (Var ident) [t1]) [t2])) AssocRight ]
128 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "<=") >> return (Binary LessThanOrEqualTo)) AssocRight
129 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp ">=") >> return (Binary GreaterThanOrEqualTo)) AssocRight ]
130 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "<") >> return (Binary LessThan)) AssocRight
131 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp ">") >> return (Binary GreaterThan)) AssocRight ]
132 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "*") >> return (Binary Multiply)) AssocRight
133 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "/") >> return (Binary Divide)) AssocRight
134 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "%") >> return (Binary Modulus)) AssocRight ]
135 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "++") >> return (Binary Concat)) AssocRight
136 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "+") >> return (Binary Add)) AssocRight
137 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "-") >> return (Binary Subtract)) AssocRight ]
138 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "<<") >> return (Binary ShiftLeft)) AssocRight
139 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp ">>>") >> return (Binary ZeroFillShiftRight)) AssocRight
140 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp ">>") >> return (Binary ShiftRight)) AssocRight ]
141 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "==") >> return (Binary EqualTo)) AssocRight
142 , Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "!=") >> return (Binary NotEqualTo)) AssocRight ]
143 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "&") >> return (Binary BitwiseAnd)) AssocRight ]
144 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "^") >> return (Binary BitwiseXor)) AssocRight ]
145 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "|") >> return (Binary BitwiseOr)) AssocRight ]
146 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "&&") >> return (Binary And)) AssocRight ]
147 , [ Infix (C.lexeme (P.try $ C.indented *> C.reservedOp "||") >> return (Binary Or)) AssocRight ]
148 ]
150customOperatorTable :: M.Map String Fixity -> OperatorTable String ParseState Identity Value
151customOperatorTable fixities =
152 let
153 ops = map (\(name, Fixity a p) -> (name, (a, p))) . M.toList $ fixities
154 sorted = sortBy (compare `on` (snd . snd)) ops
155 levels = groupBy ((==) `on` (snd . snd)) sorted
156 in
157 flip map levels (map (\(name, (a, _)) -> flip Infix (toAssoc a) $ do
158 C.lexeme $ P.try $ do
159 C.indented
160 C.reservedOp name
161 return $ \t1 t2 -> App (App (Var (Op name)) [t1]) [t2]))
163toAssoc :: Associativity -> Assoc
164toAssoc Infixl = AssocLeft
165toAssoc Infixr = AssocRight
167parseVariableIntroduction :: P.Parsec String ParseState Statement
168parseVariableIntroduction = do
169 C.reserved "var"
170 name <- C.indented *> C.parseIdent
171 C.lexeme $ C.indented *> P.char '='
172 value <- parseValue
173 return $ VariableIntroduction name value
175parseAssignment :: P.Parsec String ParseState Statement
176parseAssignment = do
177 tgt <- parseAssignmentTarget
178 C.lexeme $ C.indented *> P.char '='
179 value <- parseValue
180 return $ Assignment tgt value
182parseWhile :: P.Parsec String ParseState Statement
183parseWhile = While <$> (C.reserved "while" *> C.indented *> parseValue <* C.indented <* C.colon)
184 <*> parseManyStatements
186parseFor :: P.Parsec String ParseState Statement
187parseFor = For <$> (C.reserved "for" *> C.indented *> C.parseIdent)
188 <*> (C.indented *> C.lexeme (P.string "<-") *> parseValue)
189 <*> (C.indented *> C.reserved "until" *> parseValue <* C.colon)
190 <*> parseManyStatements
192parseForEach :: P.Parsec String ParseState Statement
193parseForEach = ForEach <$> (C.reserved "foreach" *> C.indented *> C.parseIdent)
194 <*> (C.indented *> C.reserved "in" *> parseValue <* C.colon)
195 <*> parseManyStatements
197parseIf :: P.Parsec String ParseState Statement
198parseIf = If <$> parseIfStatement
200parseIfStatement :: P.Parsec String ParseState IfStatement
201parseIfStatement =
202 IfStatement <$> (C.reserved "if" *> C.indented *> parseValue <* C.indented <* C.colon)
203 <*> parseManyStatements
204 <*> P.optionMaybe (C.same *> parseElseStatement)
206parseElseStatement :: P.Parsec String ParseState ElseStatement
207parseElseStatement = C.reserved "else" >> (ElseIf <$> (C.indented *> parseIfStatement)
208 <|> Else <$> (C.indented *> C.colon *> parseManyStatements))
210parseReturn :: P.Parsec String ParseState Statement
211parseReturn = Return <$> (C.reserved "return" *> parseValue)
213parseStatement :: P.Parsec String ParseState Statement
214parseStatement = P.choice (map P.try
215 [ parseVariableIntroduction
216 , parseAssignment
217 , parseWhile
218 , parseFor
219 , parseForEach
220 , parseIf
221 , parseReturn ])
223parseStringBinder :: P.Parsec String ParseState Binder
224parseStringBinder = StringBinder <$> C.stringLiteral
226parseBooleanBinder :: P.Parsec String ParseState Binder
227parseBooleanBinder = BooleanBinder <$> booleanLiteral
229parseNumberBinder :: P.Parsec String ParseState Binder
230parseNumberBinder = NumberBinder <$> C.integerOrFloat
232parseVarBinder :: P.Parsec String ParseState Binder
233parseVarBinder = VarBinder <$> C.parseIdent
235parseNullaryBinder :: P.Parsec String ParseState Binder
236parseNullaryBinder = NullaryBinder <$> C.lexeme C.properName
238parseUnaryBinder :: P.Parsec String ParseState Binder
239parseUnaryBinder = UnaryBinder <$> C.lexeme C.properName <*> (C.indented *> parseBinder)
241parseObjectBinder :: P.Parsec String ParseState Binder
242parseObjectBinder = ObjectBinder <$> C.braces ((C.indented *> parseIdentifierAndBinder) `P.sepBy` (C.indented *> C.comma))
244parseArrayBinder :: P.Parsec String ParseState Binder
245parseArrayBinder = C.squares $ ArrayBinder <$> ((C.indented *> parseBinder) `P.sepBy` (C.indented *> C.comma))
246 <*> P.optionMaybe (C.indented *> C.colon *> C.indented *> parseBinder)
248parseNamedBinder :: P.Parsec String ParseState Binder
249parseNamedBinder = NamedBinder <$> (C.parseIdent <* C.indented <* C.lexeme (P.char '@'))
250 <*> (C.indented *> parseBinder)
252parseNullBinder :: P.Parsec String ParseState Binder
253parseNullBinder = C.lexeme (P.char '_') *> P.notFollowedBy C.identLetter *> return NullBinder
255parseIdentifierAndBinder :: P.Parsec String ParseState (String, Binder)
256parseIdentifierAndBinder = do
257 name <- C.lexeme C.identifier
258 C.lexeme $ C.indented *> P.char '='
259 binder <- C.indented *> parseBinder
260 return (name, binder)
262parseBinder :: P.Parsec String ParseState Binder
263parseBinder = P.choice (map P.try
264 [ parseNullBinder
265 , parseStringBinder
266 , parseBooleanBinder
267 , parseNumberBinder
268 , parseNamedBinder
269 , parseVarBinder
270 , parseUnaryBinder
271 , parseNullaryBinder
272 , parseObjectBinder
273 , parseArrayBinder
274 , C.parens parseBinder ])
276parseGuardedBinder :: P.Parsec String ParseState Binder
277parseGuardedBinder = flip ($) <$> parseBinder <*> P.option id (GuardedBinder <$> (C.indented *> C.lexeme (P.char '|') *> C.indented *> parseValue))
279parseAssignmentTarget :: P.Parsec String ParseState AssignmentTarget
280parseAssignmentTarget = buildExpressionParser operators (AssignVariable <$> C.parseIdent)
281 where
282 operators = [ [ Postfix $ AssignArrayIndex <$> (C.indented *> C.squares parseValue)
283 , Postfix $ AssignObjectProperty <$> (C.indented *> C.dot *> C.indented *> C.identifier) ] ]