on fd05d50 help sign in
167 lines 5.4 kB history raw
1{-# LANGUAGE DeriveGeneric, CPP #-}
2module TutorialD.Interpreter.Base where
3import ProjectM36.Base
4import ProjectM36.AtomType
5import ProjectM36.Relation
7import Text.Megaparsec
8import Text.Megaparsec.Text
9import qualified Text.Megaparsec.Lexer as Lex
10import Data.Text hiding (count)
11import System.Random
12import qualified Data.Text as T
13import qualified Data.List as L
14import qualified Data.Vector as V
15import qualified Data.Text.IO as TIO
16import System.IO
17import ProjectM36.Relation.Show.Term
18import GHC.Generics
19import Data.Monoid
20import qualified Data.UUID as U
21import Control.Monad.Random
22import Data.List.NonEmpty as NE
23import Data.Time.Clock
24import Data.Time.Format
26displayOpResult :: TutorialDOperatorResult -> IO ()
27displayOpResult QuitResult = return ()
28displayOpResult (DisplayResult out) = TIO.putStrLn out
29displayOpResult (DisplayIOResult ioout) = ioout
30displayOpResult (DisplayErrorResult err) = let outputf = if T.length err > 0 && T.last err /= '\n' then TIO.hPutStrLn else TIO.hPutStr in
31 outputf stderr ("ERR: " <> err)
32displayOpResult QuietSuccessResult = return ()
33displayOpResult (DisplayRelationResult rel) = do
34 gen <- newStdGen
35 let randomlySortedRel = evalRand (randomizeTupleOrder rel) gen
36 TIO.putStrLn (showRelation randomlySortedRel)
37displayOpResult (DisplayParseErrorResult promptLength err) = TIO.putStrLn pointyString >> TIO.putStr ("ERR:" <> errString)
38 where
39 errString = T.pack (parseErrorPretty err)
40 errorIndent = unPos (sourceColumn (NE.head (errorPos err)))
41 pointyString = T.justifyRight (promptLength + fromIntegral errorIndent) '_' "^"
43spaceConsumer :: Parser ()
44spaceConsumer = Lex.space (void spaceChar) (Lex.skipLineComment "--") (Lex.skipBlockComment "{-" "-}")
46opChar :: Parser Char
47opChar = oneOf (":!#$%&*+./<=>?\\^|-~" :: String)-- remove "@" so it can be used as attribute marker without spaces
49reserved :: String -> Parser ()
50reserved word = try (string word *> notFollowedBy opChar *> spaceConsumer)
52reservedOp :: String -> Parser ()
53reservedOp op = try (string op *> notFollowedBy opChar *> spaceConsumer)
55parens :: Parser a -> Parser a
56parens = between (symbol "(") (symbol ")")
58braces :: Parser a -> Parser a
59braces = between (symbol "{") (symbol "}")
61identifier :: Parser Text
62identifier = do
63 istart <- letterChar <|> char '_'
64 irest <- many (alphaNumChar <|> char '_' <|> char '#')
65 spaceConsumer
66 pure (pack (istart:irest))
68symbol :: String -> Parser Text
69symbol sym = pack <$> Lex.symbol spaceConsumer sym
71comma :: Parser Text
72comma = symbol ","
74pipe :: Parser Text
75pipe = symbol "|"
77quote :: Parser Text
78quote = symbol "\""
80tripleQuote :: Parser Text
81tripleQuote = symbol "\"\"\""
83arrow :: Parser Text
84arrow = symbol "->"
86semi :: Parser Text
87semi = symbol ";"
89{-
90whiteSpace :: Parser ()
91whiteSpace = Token.whiteSpace lexer
92-}
94integer :: Parser Integer
95integer = Lex.integer
97float :: Parser Double
98float = Lex.float
100capitalizedIdentifier :: Parser Text
101capitalizedIdentifier = do
102 fletter <- upperChar
103 restOfIdentifier_ fletter
105restOfIdentifier_ :: Char -> Parser Text
106restOfIdentifier_ fletter = do
107 rest <- option "" identifier
108 spaceConsumer
109 pure (T.cons fletter rest)
111uncapitalizedIdentifier :: Parser Text
112uncapitalizedIdentifier = do
113 fletter <- lowerChar
114 restOfIdentifier_ fletter
116showRelationAttributes :: Attributes -> Text
117showRelationAttributes attrs = "{" <> T.concat (L.intersperse ", " $ L.map showAttribute attrsL) <> "}"
118 where
119 showAttribute (Attribute name atomType) = name <> " " <> prettyAtomType atomType
120 attrsL = V.toList attrs
122data TutorialDOperatorResult = QuitResult |
123 DisplayResult StringType |
124 DisplayIOResult (IO ()) |
125 DisplayRelationResult Relation |
126 DisplayErrorResult StringType |
127 DisplayParseErrorResult Int (ParseError Char Dec) | -- Int refers to length of prompt text
128 QuietSuccessResult
129 deriving (Generic)
131type TransactionGraphWasUpdated = Bool
133--allow for python-style triple quoting because guessing the correct amount of escapes in different contexts is annoying
134tripleQuotedString :: Parser Text
135tripleQuotedString = do
136 _ <- tripleQuote
137 pack <$> manyTill anyChar (try (tripleQuote >> notFollowedBy quote))
139normalQuotedString :: Parser Text
140normalQuotedString = quote *> (T.pack <$> manyTill Lex.charLiteral quote)
142quotedString :: Parser Text
143quotedString = try tripleQuotedString <|> normalQuotedString
145uuidP :: Parser U.UUID
146uuidP = do
147 uuidStart <- count 8 hexDigitChar
148 _ <- char '-' -- min 28 with no dashes, maximum 4 dashes
149 uuidMid1 <- count 4 hexDigitChar
150 _ <- char '-'
151 uuidMid2 <- count 4 hexDigitChar
152 _ <- char '-'
153 uuidMid3 <- count 4 hexDigitChar
154 _ <- char '-'
155 uuidEnd <- count 12 hexDigitChar
156 let uuidStr = L.intercalate "-" [uuidStart, uuidMid1, uuidMid2, uuidMid3, uuidEnd]
157 case U.fromString uuidStr of
158 Nothing -> fail "Invalid uuid string"
159 Just uuid -> return uuid
161utcTimeP :: Parser UTCTime
162utcTimeP = do
163 timeStr <- quotedString
164 case parseTimeM True defaultTimeLocale "%Y-%m-%d %H:%M:%S" (T.unpack timeStr) of
165 Nothing -> fail "invalid datetime input, use \"YYYY-MM-DD HH:MM:SS\""
166 Just stamp -> pure stamp