Move about internal and external modules
[lsp-test.git] / src / Language / Haskell / LSP / Test / Script.hs
index 9577ee335b2036ad6ee4a74bc3f672e88503e8b1..ce4466429c853aafe7e292d5b3a13d53d0b873e0 100644 (file)
-module Main where
+module Language.Haskell.LSP.Test.Script where
 
 import Control.Applicative ( (<|>), some )
+import Control.Monad
 import Data.Char
+import qualified Data.Text as T
+import qualified Data.HashMap.Lazy as HM
+import Data.Scientific
 import Text.ParserCombinators.ReadP
-import System.Environment
-
-{-
- - "asdf"
- -    wait for
- -      asdsdf == "asdf"
- -      adsf   == "adsf"
- -    send
- -      foo
- -      bar
- -
- -  str   ::= " char "
- -  wait  ::= wait for (pred+ | any)
- -  pred  ::= x == y
- -  send  ::= send msg+
- -  msg   ::= str
- -  block ::= str wait send?
- -}
-
-data Block = Block String Wait (Maybe Send)
+
+data Block = Block String Wait [Action]
   deriving Show
+
 data Wait = WaitPred [Predicate]
           | WaitAny
   deriving Show
-data Predicate = Predicate String String
+
+data Predicate = Predicate Accessor Comparison
+  deriving Show
+
+data Accessor = AccessorTerm String
+              | Accessor String Accessor
   deriving Show
-data Send = Send [Message]
+
+data Comparison = EqualsNumber Scientific
+                | EqualsString String
+                | ContainsString String
+  deriving Show
+
+data Action = OpenDoc FilePath String
+            | Request String Method MessageParam
+            | Reply Method MessageParam
+            | Notify Method MessageParam
+  deriving Show
+
+type Method = String
+
+data MessageParam = ParamObject (HM.HashMap T.Text MessageParam)
+                  | ParamString T.Text
+                  | ParamUri FilePath
   deriving Show
-type Message = String
 
-skip = skipMany $ satisfy isSpace <|> char '\n' <|> char '\r'
+-- | Parse a string literal like "foo".
+strLiteral :: ReadP String
+strLiteral = between (char '"') (char '"') (many (satisfy (/= '"')))
 
-strLit :: ReadP String
-strLit = between (char '"') (char '"') (many (satisfy (/= '"')))
+-- | Parse mandatory whitespace, including newlines
+space :: ReadP ()
+space = void $ some (satisfy isSpace)
 
 block :: ReadP Block
 block = do
-  skip
-  name <- strLit
-  skip
+  skipSpaces
+  name <- strLiteral
+  skipSpaces
+  between (char '{') (char '}') $ do
+    skipSpaces
     w <- wait
-  skip
-  s <- option Nothing (Just <$> send)
-  return $ Block name w s
+    actions <- option [] $ do
+      space
+      string "then"
+      space
+      action `sepBy1` space
+    skipSpaces
+    return $ Block name w actions
 
 wait :: ReadP Wait
 wait = do
   string "wait for"
-  skip
+  space
   f <|> g
   where f = string "any" >> return WaitAny
         g = WaitPred <$> some predicate
 
 predicate :: ReadP Predicate
 predicate = do
-  skip
-  x <- strLit
-  skip
-  string "=="
-  skip
-  y <- strLit
-  return $ Predicate x y
-
-send :: ReadP Send
-send = do
-  -- skip
-  string "send"
-  Send <$> some (skip >> strLit)
+  x <- accessor
+  Predicate x <$> comparison
 
-parseScript :: String -> [Block]
-parseScript = fst . last . readP_to_S (some block)
+accessor :: ReadP Accessor
+accessor = do
+  x:xs <- reverse <$> sepBy1 property (char '.')
+  return $ foldl (flip Accessor) (AccessorTerm x) xs
+  where property = many (satisfy isAlphaNum)
+
+comparison :: ReadP Comparison
+comparison = do
+  space
+  operator <- string "==" <|> string "is in"
+  space
+  choice [eqString, eqNumber]
+  -- todo: contains string
+  where eqString = EqualsString <$> strLiteral
+        eqNumber = EqualsNumber . read <$> some (satisfy isNumber)
+
+action :: ReadP Action
+action = choice
+    [ openAction
+    , requestAction
+    , sendAction "reply"   Reply
+    , sendAction "notify"  Notify
+    ]
+  where
+    requestAction = do
+      skipSpaces
+      identifier <- manyTill (satisfy isAlphaNum) (skipSpaces >> char ':')
+      skipSpaces
+      sendAction "request" (Request identifier)
+
+openAction :: ReadP Action
+openAction = do
+  skipSpaces
+  string "open"
+  space
+  fp <- strLiteral
+  space
+  OpenDoc fp <$> strLiteral
+
+sendAction :: String -> (String -> MessageParam -> Action) -> ReadP Action
+sendAction keyword con = do
+  skipSpaces
+  string keyword
+  skipSpaces
+  method <- strLiteral
+  skipSpaces
+  con method <$> messageParam
 
-main = do
-  fileName <- head <$> getArgs
-  print . parseScript =<< readFile fileName
+messageParam :: ReadP MessageParam
+messageParam = choice [uriParam, stringParam, objParam]
+  where
+    uriParam = do
+      skipSpaces
+      string "uri"
+      skipSpaces
+      fp <- strLiteral
+      skipSpaces
+      return (ParamUri fp)
+
+    stringParam = ParamString . T.pack <$> strLiteral
+
+    objParam = do
+      props <- between (char '{') (char '}') (some parseProp)
+      return (ParamObject (HM.fromList props))
+
+    parseProp = do
+      skipSpaces
+      name <- many (satisfy (\x -> (x /= ':') && isAlphaNum x))
+      char ':'
+      skipSpaces
+      param <- messageParam
+      skipSpaces
+      return (T.pack name, param)
+
+parseScript :: String -> [Block]
+parseScript str =
+  case readP_to_S parser str of
+    [] -> error "Couldn't parse"
+    xs -> fst $ last xs
+  where
+    parser = do
+      blocks <- some block
+      skipSpaces
+      eof
+      return blocks
\ No newline at end of file