packages feed

hls-eval-plugin-0.1.0.0: src/Ide/Plugin/Eval/Parse/Section.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# OPTIONS_GHC -Wwarn #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}

-- |Parse a Section, a group of zero or more tests defined in a multiline comment or a sequence of one line comments.
module Ide.Plugin.Eval.Parse.Section (
    allSections,
    validSections,
    Section (..),
) where

import qualified Control.Applicative.Combinators.NonEmpty as NE
import Control.Monad.Combinators (
    many,
    optional,
    some,
    (<|>),
 )
import qualified Data.List.NonEmpty as NE
import Data.Maybe (catMaybes, fromMaybe)
import Ide.Plugin.Eval.Parse.Parser (
    Parser,
    runParser,
    satisfy,
 )
import Ide.Plugin.Eval.Parse.Token (
    Token (BlockOpen, blockFormat, blockLanguage, blockName),
    TokenS,
    isBlockClose,
    isBlockOpen,
    isCodeLine,
    isPropLine,
    isStatement,
    isTextLine,
    unsafeContent,
 )
import Ide.Plugin.Eval.Types (
    Format (SingleLine),
    Loc,
    Located (Located, located, location),
    Section (..),
    Test (Example, Property),
    hasTests,
    unLoc,
 )

type Tk = Loc TokenS

validSections :: [Tk] -> Either String [Section]
validSections = (filter hasTests <$>) . allSections

allSections :: [Tk] -> Either String [Section]
allSections = runParser sections

{-
>>> import Ide.Plugin.Eval.Parse.Token
>>> import  System.IO.Extra(readFileUTF8')
>>> testSource_ = runParser sections . tokensFrom
>>> testSource fp = testSource_ <$> readFileUTF8' fp

>>> testSource "plugins/default/src/Ide/Plugin/Eval/Test/TestGHC.hs"
Right [Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 36, located = Property {testline = " \\(l::[Bool]) -> reverse (reverse l) == l", testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 40, located = Example {testLines = " :set -XScopedTypeVariables -XExplicitForAll" :| [" import qualified Test.QuickCheck as Q11"," runProp11 p = Q11.quickCheckWithResult Q11.stdArgs p >>= return . Q11.output"," prop11 = \\(l::[Int]) -> reverse (reverse l) == l"," runProp11 prop11"], testOutput = []}},Located {location = 46, located = Property {testline = " \\(l::[Int]) -> reverse (reverse l) == l", testOutput = []}}], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [Located {location = 50, located = Example {testLines = " t" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 55, located = Example {testLines = " run $ runEval \"3+2\"" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [Located {location = 125, located = Example {testLines = " isStmt \"\"" :| [], testOutput = ["stmt = let x =33;print x"]}}], sectionLanguage = Haddock, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = MultiLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine},Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = SingleLine}]

>>> testSource "test/testdata/eval/T11.hs"
Right [Section {sectionName = "", sectionTests = [Located {location = 2, located = Example {testLines = " :kind! a" :| [], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine}]

>>> testSource "test/testdata/eval/T12.hs"
Right [Section {sectionName = "", sectionTests = [Located {location = 6, located = Example {testLines = " type N = 1" :| [" type M = 40"," :kind N + M + 1"], testOutput = []}}], sectionLanguage = Plain, sectionFormat = SingleLine}]

>>> testSource_ $ "{"++"-\n       -" ++ "}"
Right [Section {sectionName = "", sectionTests = [], sectionLanguage = Plain, sectionFormat = MultiLine}]
-}
sections :: Parser Tk [Section]
sections =
    catMaybes <$> many (const Nothing <$> some code <|> Just <$> section)

section :: Parser Tk Section
section = sectionBody >>= sectionEnd

sectionBody :: Parser Tk Section
sectionBody =
    ( \(unLoc -> BlockOpen{..}) ts ->
        Section (fromMaybe "" blockName) (catMaybes ts) blockLanguage blockFormat
    )
        <$> open <*> many (Just <$> example <|> Just <$> property <|> const Nothing <$> doc)

sectionEnd :: Section -> Parser Tk Section
sectionEnd s
    | sectionFormat s == SingleLine = optional code *> return s
    | otherwise = close *> return s

-- section = do
--   s <-
--     maybe
--       (Section "" [] Plain SingleLine)
--       ( \(Located _ BlockOpen {..}) ->
--           Section (fromMaybe "" blockName) [] blockLanguage blockFormat
--       )
--       <$> optional open
--   ts <- many (Just <$> example <|> Just <$> property <|> const Nothing <$> doc)
--   optional close
--   return $ s {sectionTests = catMaybes ts}

-- singleSection :: Parser Tk Section
-- singleSection = (\ts -> Section "" (catMaybes ts) Plain SingleLine) <$> tests

-- tests :: Parser Tk [Maybe (Loc Test)]
-- tests = some (Just <$> example <|> Just <$> property <|> const Nothing <$> doc)

doc :: Parser Tk [Tk]
doc = some text

example, property :: Parser Tk (Loc Test)
property =
    ( \(Located l p) rs ->
        Located l (Property (unsafeContent p) (unsafeContent . located <$> rs))
    )
        <$> prop
        <*> many nonEmptyText
example =
    ( \es rs ->
        Located
            (location (NE.head es))
            (Example (unsafeContent . located <$> es) (unsafeContent . located <$> rs))
    )
        <$> NE.some statement
        <*> many nonEmptyText

open, close, statement, nonEmptyText, text, prop, code :: Parser Tk Tk
statement = is isStatement
text = is isTextLine
prop = is isPropLine
open = is isBlockOpen
close = is isBlockClose
code = is isCodeLine
nonEmptyText = is (\l -> isTextLine l && not (null (unsafeContent l)))

is :: (b -> Bool) -> Parser (Loc b) (Loc b)
is p = satisfy (p . unLoc)