1module Parse.Helpers where
3import Prelude hiding (until)
4import Control.Monad (join)
5import Control.Monad.State (State)
6import Data.Char (isUpper)
7import qualified Data.Map as Map
8import qualified Language.GLSL.Parser as GLP
9import qualified Language.GLSL.Syntax as GLS
10import Text.Parsec hiding (newline, spaces, State)
11import Text.Parsec.Indent (indented, runIndent)
12import qualified Text.Parsec.Token as T
14import qualified AST.Declaration as Decl
15import qualified AST.Expression.General as E
16import qualified AST.Expression.Source as Source
17import qualified AST.Literal as L
18import qualified AST.Variable as Variable
19import qualified Reporting.Annotation as A
20import qualified Reporting.Error.Syntax as Syntax
21import qualified Reporting.Region as R
27 [ "if", "then", "else"
32 , "import", "exposing"
49type OpTable = Map.Map String (Int, Decl.Assoc)
50type SourceM = State SourcePos
51type IParser a = ParsecT String OpTable SourceM a
54iParse :: IParser a -> String -> Either ParseError a
56 iParseWithTable "" Map.empty parser source
59iParseWithTable :: SourceName -> OpTable -> IParser a -> String -> Either ParseError a
60iParseWithTable sourceName table aParser input =
61 runIndent sourceName $ runParserT aParser table sourceName input
68 makeVar (letter <|> char '_') <?> "a name"
71lowVar :: IParser String
73 makeVar lower <?> "a lower case name"
76capVar :: IParser String
78 makeVar upper <?> "an upper case name"
81qualifiedVar :: IParser String
83 do vars <- many ((++) <$> capVar <*> string ".")
84 (++) (concat vars) <$> lowVar
87rLabel :: IParser String
91innerVarChar :: IParser Char
93 alphaNum <|> char '_' <|> char '\'' <?> "more letters in this name"
96makeVar :: IParser Char -> IParser String
98 do variable <- (:) <$> firstChar <*> many innerVarChar
99 if variable `elem` reserveds
100 then fail (Syntax.keyword variable)
104reserved :: String -> IParser String
106 expecting ("reserved word `" ++ word ++ "`") $
108 notFollowedBy innerVarChar
116equals :: IParser String
118 string "=" <?> "an equals sign '='"
121rightArrow :: IParser String
123 string "->" <?> "an arrow '->'"
126hasType :: IParser String
128 string ":" <?> "the \"has type\" symbol ':'"
135 try (lookAhead check) >> p
142spaceySepBy1 :: IParser b -> IParser a -> IParser [a]
143spaceySepBy1 sep parser =
145 (value:) <$> spaceyPrefixBy sep parser
148spaceyPrefixBy :: IParser sep -> IParser a -> IParser [a]
149spaceyPrefixBy sep parser =
150 many (commitIf (whitespace >> sep) (padded sep >> parser))
155 char ',' <?> "a comma ','"
158commaSep1 :: IParser a -> IParser [a]
163commaSep :: IParser a -> IParser [a]
165 option [] . commaSep1
168semiSep1 :: IParser a -> IParser [a]
170 spaceySepBy1 (char ';' <?> "a semicolon ';'")
173pipeSep1 :: IParser a -> IParser [a]
175 spaceySepBy1 (char '|' <?> "a vertical bar '|'")
178consSep1 :: IParser a -> IParser [a]
180 spaceySepBy1 (string "::" <?> "a cons operator '::'")
183dotSep1 :: IParser a -> IParser [a]
185 (:) <$> p <*> many (try (char '.') >> p)
188spaceSep1 :: IParser a -> IParser [a]
190 (:) <$> p <*> spacePrefix p
194 constrainedSpacePrefix p (\_ -> return ())
197constrainedSpacePrefix parser constraint =
199 [ try (spacing >> lookAhead (oneOf "[({")) >> parser
200 , try (spacing >> parser)
205 constraint n <?> Syntax.whitespace
219betwixt :: Char -> Char -> IParser a -> IParser a
223 char b <?> "a closing '" ++ [b] ++ "'"
227surround :: Char -> Char -> String -> IParser a -> IParser a
228surround a z name p = do
231 char z <?> unwords ["a closing", name, show z]
235braces :: IParser a -> IParser a
237 surround '[' ']' "brace"
240parens :: IParser a -> IParser a
242 surround '(' ')' "paren"
245brackets :: IParser a -> IParser a
247 surround '{' '}' "bracket"
251-- HELPERS FOR EXPRESSIONS
254getMyPosition :: IParser R.Position
256 R.fromSourcePos <$> getPosition
259addLocation :: IParser a -> IParser (A.Located a)
261 do (start, e, end) <- located expr
262 return (A.at start end e)
265located :: IParser a -> IParser (R.Position, a, R.Position)
267 do start <- getMyPosition
270 return (start, value, end)
273accessible :: IParser Source.Expr -> IParser Source.Expr
274accessible exprParser =
275 do start <- getMyPosition
277 annotatedRootExpr@(A.A _ rootExpr) <- exprParser
279 access <- optionMaybe (try dot <?> "a field access like .name")
283 return annotatedRootExpr
289 return . A.at start end $
291 E.Var (Variable.Raw name@(c:_))
293 E.rawVar (name ++ '.' : v)
295 E.Access annotatedRootExpr v
301 notFollowedBy (char '.')
308padded :: IParser a -> IParser a
316spaces :: IParser String
318 let space = string " " <|> multiComment <?> Syntax.whitespace
320 concat <$> many1 space
323forcedWS :: IParser String
326 ws <- many1 (spaces <|> newline)
328 column <- sourceColumn <$> getPosition
330 then fail badWhiteSpaceMessage
331 else return (concat ws)
334badWhiteSpaceMessage :: String
335badWhiteSpaceMessage =
336 "I need whitespace, but got stuck on what looks like a new declaration.\n"
338 ++ "You are either missing some stuff in the declaration above or just need to add\n"
339 ++ "some spaces here:"
342failIfTabFound :: IParser ()
344 do foundTab <- option False (lookAhead (char '\t') >> return True) <?> Syntax.tab
350-- Just eats whitespace until the next meaningful character.
351dumbWhitespace :: IParser String
353 concat <$> many (spaces <|> newline)
356whitespace :: IParser String
361freshLine :: IParser String
364 do ws <- many1 (spaces <|> newline)
365 column <- sourceColumn <$> getPosition
367 then return (concat ws)
368 else fail badFreshLineMessage
369 ) <?> Syntax.freshLine
372badFreshLineMessage :: String
374 "I need a fresh line to start a new declaration. This means a new line that\n"
375 ++ "starts with stuff, not with spaces or comments."
378newline :: IParser String
380 simpleNewline <|> lineComment <?> Syntax.newline
383simpleNewline :: IParser String
385 try (string "\r\n") <|> string "\n"
388lineComment :: IParser String
391 comment <- anyUntil $ simpleNewline <|> (eof >> return "\n")
392 return ("--" ++ comment)
395docComment :: IParser String
397 do try (string "{-|")
398 contents <- closeComment
399 return (init (init contents))
402multiComment :: IParser String
404 (++) <$> try (string "{-" <* notFollowedBy (string "|")) <*> closeComment
407closeComment :: IParser String
411 [ try (string "-}") <?> "the end of a comment -}"
412 , concat <$> sequence [ try (string "{-"), closeComment, closeComment ]
427until :: IParser a -> IParser b -> IParser b
431 go = end <|> (p >> go)
434anyUntil :: IParser String -> IParser String
439 end <|> (:) <$> anyChar <*> go
442ignoreUntil :: IParser a -> IParser (Maybe a)
451 [ try (ignore chr) <|> ignore str
452 , ignore multiComment
460 , filler `until` choice [ const Nothing <$> eof, newline >> go ]
464onFreshLines :: (a -> b -> b) -> b -> IParser a -> IParser b
465onFreshLines insert init thing =
469 do optionValue <- ignoreUntil thing
471 Nothing -> return values
472 Just v -> go (insert v values)
475withSource :: IParser a -> IParser (String, a)
477 do start <- getParserState
479 endPos <- getPosition
481 raw <- anyUntilPos endPos
485anyUntilPos :: SourcePos -> IParser String
487 do currentPos <- getPosition
490 else (:) <$> anyChar <*> anyUntilPos pos
497shader :: IParser (String, L.GLShaderTipe)
499 do try (string "[glsl|")
500 rawSrc <- closeShader id
501 case glSource rawSrc of
502 Left err -> parserFail . show $ err
503 Right tipe -> return (rawSrc, tipe)
506closeShader :: (String -> a) -> IParser a
509 [ do try (string "|]")
512 closeShader (builder . (c:))
516glSource :: String -> Either ParseError L.GLShaderTipe
518 case GLP.parse src of
520 Right (GLS.TranslationUnit decls) ->
521 map extractGLinputs decls
523 |> foldr addGLinput emptyDecls
528 emptyDecls = L.GLShaderTipe Map.empty Map.empty Map.empty
530 addGLinput (qual,tipe,name) glDecls =
533 glDecls { L.attribute = Map.insert name tipe $ L.attribute glDecls }
536 glDecls { L.uniform = Map.insert name tipe $ L.uniform glDecls }
539 glDecls { L.varying = Map.insert name tipe $ L.varying glDecls }
541 _ -> error "Should never happen due to below filter"
543 extractGLinputs decl =
549 (Just (GLS.TypeQualSto qual))
550 (GLS.TypeSpec _prec (GLS.TypeSpecNoPrecision tipe _mexpr1))))
551 [GLS.InitDecl name _mexpr2 _mexpr3]
553 case elem qual [GLS.Attribute, GLS.Varying, GLS.Uniform] of
557 GLS.Int -> return (qual, L.Int,name)
558 GLS.Float -> return (qual, L.Float,name)
559 GLS.Vec2 -> return (qual, L.V2,name)
560 GLS.Vec3 -> return (qual, L.V3,name)
561 GLS.Vec4 -> return (qual, L.V4,name)
562 GLS.Mat4 -> return (qual, L.M4,name)
563 GLS.Sampler2D -> return (qual, L.Texture,name)
569-- STRINGS AND CHARACTERS
574 expecting "a string like \"hello\"" $
575 do charSeq <- choice [ multiStr, singleStr ]
576 either (fail . show) return $
577 processAs T.stringLiteral ('"' : charSeq ++ "\"")
580delimitedSequence :: IParser a -> IParser String -> IParser String
581delimitedSequence quote insides =
583 concat <$> manyTill insides quote
586multiStr :: IParser String
588 delimitedSequence (try (string "\"\"\"")) multilineStringChar
591singleStr :: IParser String
593 delimitedSequence (char '"') (charWithin '"')
598 expecting "a character like 'x'" $
600 charSeq <- concat <$> many1 (charWithin '\'') <?> "a character"
601 case processAs T.charLiteral ("'" ++ charSeq ++ "'") of
608 "Elm uses double quotes for strings. Switch\
609 \ the ' to \" on both ends of the string and\
610 \ you should be all set!"
617charWithin :: Char -> IParser String
621 , (:[]) <$> satisfy (/= delim)
625multilineStringChar :: IParser String
627 do notFollowedBy (string "\"\"\"")
631 , escapeQuotes <$> anyChar
635escapeQuotes :: Char -> String
637 if c == '\"' then "\\\"" else [c]
640newlineChar :: IParser String
643 [ char '\n' >> return "\\n"
644 , char '\r' >> return "\\r"
648escaped :: Char -> IParser String
652 c <- char '\\' <|> char delim
657 :: (T.GenTokenParser String u SourceM -> IParser a)
659 -> Either ParseError a
660processAs processor string =
661 iParse (processor lexer) string
663 lexer :: T.GenTokenParser String u SourceM
665 T.makeTokenParser elmDef
667 -- I don't know how many of these are necessary for charLiteral/stringLiteral
668 elmDef :: T.GenLanguageDef String u SourceM
671 { T.commentStart = "{-"
672 , T.commentEnd = "-}"
673 , T.commentLine = "--"
674 , T.nestedComments = True
675 , T.identStart = undefined
676 , T.identLetter = undefined
677 , T.opStart = undefined
678 , T.opLetter = undefined
679 , T.reservedNames = reserveds
680 , T.reservedOpNames = [":", "->", "|"]
681 , T.caseSensitive = True
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list