{-# LANGUAGE QuasiQuotes, OverloadedStrings #-} {-# OPTIONS_GHC -Wno-orphans #-} import Data.Char import Test.Hspec import Text.RawString.QQ import Text.Trifecta.Parser import Text.Trifecta.Result import Language.CMake.AST import Language.CMake.Parser instance Eq a => Eq (Result a) where Success a == Success b = a == b Failure _ == Failure _ = True _ == _ = False (~~>) :: HasCallStack => String -> File -> Expectation (~~>) str res = parseString fileParser mempty (str <> "\n") `shouldBe` Success res dropLeading :: String -> String dropLeading str = init $ unlines $ l : (drop numSpaces <$> init ls) where (l:ls) = lines str numSpaces = length $ takeWhile isSpace $ last ls main :: IO () main = hspec $ do describe "Parsing simple commands" $ do it "without args" $ [r|add_executable()|] ~~> File [ CommandElement $ CommandInvocation "add_executable" [] ] it "with leading spaces" $ [r| add_executable()|] ~~> File [ CommandElement $ CommandInvocation "add_executable" [] ] it "empty line" $ [r||] ~~> File [ NonCommandElement ] it "empty line with spaces" $ " \t " ~~> File [ NonCommandElement ] describe "Parsing a sequence of commands" $ do it "without args" $ [r|add_executable() add_library()|] ~~> File [ CommandElement $ CommandInvocation "add_executable" [] , CommandElement $ CommandInvocation "add_library" [] ] it "with empty lines" $ [r|add_executable() add_library()|] ~~> File [ CommandElement $ CommandInvocation "add_executable" [] , NonCommandElement , CommandElement $ CommandInvocation "add_library" [] ] describe "Parsing comments" $ do it "parses single whole-line comment" $ [r|# this is a comment|] ~~> File [NonCommandElement] it "parses multiple whole-line comments" $ [r|# this is a comment # and this is a continuation|] ~~> File [NonCommandElement, NonCommandElement] it "parses end-of-line comments" $ [r|add_library() # this is a comment|] ~~> File [CommandElement $ CommandInvocation "add_library" []] describe "Bracket comments" $ do it "parses simplest empty bracket comments" $ [r|#[[]]|] ~~> File [NonCommandElement] it "parses zero-eq bracket comments with text" $ [r|#[[ foobar ]]|] ~~> File [NonCommandElement] it "parses non-zero-eq bracket comments with text" $ [r|#[==[ foobar ]==]|] ~~> File [NonCommandElement] it "parses non-zero-eq bracket comments with nested eqs (less)" $ [r|#[==[ ]=] ]==]|] ~~> File [NonCommandElement] it "parses non-zero-eq bracket comments with nested eqs (more)" $ [r|#[==[ ]===] ]==]|] ~~> File [NonCommandElement] it "parses multilinenon-zero-eq bracket comments" $ [r|#[==[ foobar ]=] fdsafsdahljkrew hjkfdshafkjlashjk ]===] ]==]|] ~~> File [NonCommandElement] describe "Parsing simple command arguments" $ do it "parses command with one arg" $ [r|add_executable(foo)|] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo"] ] it "parses command with several simple args" $ [r|add_executable(foo bar baz)|] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo", "bar", "baz"] ] describe "Parsing bracket command arguments" $ do it "parses command with bracket arguments" $ dropLeading [r|add_executable([==[ This is a bracket argument ]==]) |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a bracket argument\n"] ] it "parses command with multiline bracket arguments" $ dropLeading [r|add_executable([==[ This is a bracket argument ]==]) |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a\nbracket\nargument\n"] ] it "does not escape in bracket arguments" $ dropLeading [r|add_executable([==[ This is \t \; \n a \- bracket argument \meh ]==]) |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is \\t \\; \\n a \\- bracket argument \\meh\n"] ] it "does not expand variables in bracket arguments" $ dropLeading [r|add_executable([==[ This is a ${bracket} argument ]==]) |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a ${bracket} argument\n"] ] describe "Parsing quoted command arguments" $ do it "parses command with one arg" $ [r|add_executable("foo")|] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo"] ] it "parses command with multple args" $ [r|add_executable("foo" "bar")|] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo", "bar"] ] it "does escaping" $ [r|add_executable("foo \n \; bar")|] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo \n ; bar"] ] it "parses command with multiline arg" $ dropLeading [r|add_executable("foo bar baz") |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo\nbar\nbaz"] ] it "parses command with multiline arg and final line feed" $ dropLeading [r|add_executable("foo bar baz ") |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo\nbar\nbaz\n"] ] it "parses command with single-line arg spanning multiple lines" $ dropLeading [r|add_executable("foo \ bar \ baz") |] ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo bar baz"] ]