|
@@ -1,231 +0,0 @@
|
|
|
-{-# LANGUAGE OverloadedStrings #-}
|
|
|
-{-# LANGUAGE LambdaCase #-}
|
|
|
-
|
|
|
-module Main where
|
|
|
-
|
|
|
-import Data.Either
|
|
|
-import Data.SCargot
|
|
|
-import Data.SCargot.Comments
|
|
|
-import Data.SCargot.Repr
|
|
|
-import Data.Semigroup
|
|
|
-import qualified Data.Text as T
|
|
|
-import qualified Data.Text.IO as TIO
|
|
|
-import System.Exit
|
|
|
-import Test.HUnit
|
|
|
-import Text.Parsec as P
|
|
|
-import Text.Parsec.Text (Parser)
|
|
|
-import Text.Printf ( printf )
|
|
|
-
|
|
|
-
|
|
|
-main = do
|
|
|
- putStrLn "Parsing a large S-expression"
|
|
|
- srcs <- mapM (\n -> (,) n <$> TIO.readFile n) [ "test/small-sample.sexp"
|
|
|
- , "test/med-sample.sexp"
|
|
|
- , "test/med2-sample.sexp"
|
|
|
- , "test/big-sample.sexp"
|
|
|
- ]
|
|
|
- counts <- runTestTT $ TestList
|
|
|
- [ TestLabel "basic checks" $ TestList
|
|
|
- [ TestLabel "flat print" $ TestList
|
|
|
- [ TestLabel "flatprint SNil" $ "()" ~=? printSExpr SNil
|
|
|
- , TestLabel "flatprint SAtom" $ "hi" ~=? printSExpr (SAtom (AIdent "hi"))
|
|
|
- , TestLabel "flatprint pair" $ "(hi . world)" ~=?
|
|
|
- printSExpr (SCons (SAtom (AIdent "hi")) (SAtom (AIdent "world")))
|
|
|
- , TestLabel "flatprint list of 1" $ "(hi)" ~=?
|
|
|
- printSExpr (SCons (SAtom (AIdent "hi")) SNil)
|
|
|
- , TestLabel "flatprint list of 2" $ "(hi world)" ~=?
|
|
|
- printSExpr (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- SNil))
|
|
|
- , TestLabel "flatprint list of 2 pairs" $ "((hi . hallo) (world . welt))" ~=?
|
|
|
- printSExpr (SCons (SCons (SAtom (AIdent "hi"))
|
|
|
- (SAtom (AIdent "hallo")))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SAtom (AIdent "welt"))))
|
|
|
- , TestLabel "flatprint list of 3 ending in a pair" $ "(hi world (hallo . welt))" ~=?
|
|
|
- printSExpr (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SCons (SAtom (AIdent "hallo"))
|
|
|
- (SAtom (AIdent "welt")))))
|
|
|
- , TestLabel "flatprint list of 3" $ "(hi world hallo)" ~=?
|
|
|
- printSExpr (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SCons (SAtom (AIdent "hallo"))
|
|
|
- SNil)))
|
|
|
- ]
|
|
|
- , TestLabel "pretty print" $
|
|
|
- let pprintIt = pprintSExpr 40 Swing in TestList
|
|
|
- [ TestLabel "pretty print SNil" $ "()\n" ~=? pprintIt SNil
|
|
|
- , TestLabel "pretty print SAtom" $ "hi\n" ~=? pprintIt (SAtom (AIdent "hi"))
|
|
|
- , TestLabel "pretty print pair" $ "(hi . world)\n" ~=?
|
|
|
- pprintIt (SCons (SAtom (AIdent "hi")) (SAtom (AIdent "world")))
|
|
|
- , TestLabel "pretty print list of 1" $ "(hi)\n" ~=?
|
|
|
- pprintIt (SCons (SAtom (AIdent "hi")) SNil)
|
|
|
- , TestLabel "pretty print list of 2" $ "(hi world)\n" ~=?
|
|
|
- pprintIt (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- SNil))
|
|
|
- , TestLabel "pretty print list of 2 pairs" $
|
|
|
- "((hi . hallo) (world . welt))\n" ~=?
|
|
|
- pprintIt (SCons (SCons (SAtom (AIdent "hi"))
|
|
|
- (SAtom (AIdent "hallo")))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SAtom (AIdent "welt"))))
|
|
|
- , TestLabel "pretty print list of 3 ending in a pair" $
|
|
|
- "(hi world (hallo . welt))\n" ~=?
|
|
|
- pprintIt (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SCons (SAtom (AIdent "hallo"))
|
|
|
- (SAtom (AIdent "welt")))))
|
|
|
- , TestLabel "pretty print list of 3" $ "(hi world hallo)\n" ~=?
|
|
|
- pprintIt (SCons (SAtom (AIdent "hi"))
|
|
|
- (SCons (SAtom (AIdent "world"))
|
|
|
- (SCons (SAtom (AIdent "hallo"))
|
|
|
- SNil)))
|
|
|
- ]
|
|
|
- ]
|
|
|
- , TestLabel "round-trip" $ TestList $
|
|
|
- concatMap (\t -> map t srcs) $
|
|
|
- [ testParsePrint
|
|
|
- ]
|
|
|
- ]
|
|
|
- if errors counts + failures counts > 0
|
|
|
- then exitFailure
|
|
|
- else exitSuccess
|
|
|
-
|
|
|
-
|
|
|
-testParsePrint :: (String, T.Text) -> Test
|
|
|
-testParsePrint (n,s) = TestList
|
|
|
- [ testParseFlatPrint n s
|
|
|
-
|
|
|
- , testParsePPrint 80 Swing n s
|
|
|
- , testParsePPrint 60 Swing n s
|
|
|
- , testParsePPrint 40 Swing n s
|
|
|
- , testParsePPrint 20 Swing n s
|
|
|
- , testParsePPrint 15 Swing n s
|
|
|
- , testParsePPrint 10 Swing n s
|
|
|
-
|
|
|
- , testParsePPrint 80 Align n s
|
|
|
- , testParsePPrint 40 Align n s
|
|
|
- , testParsePPrint 10 Align n s
|
|
|
- ]
|
|
|
-
|
|
|
-
|
|
|
-testParseFlatPrint testName src =
|
|
|
- testRoundTrip (testName <> " flat print")
|
|
|
- (fromRight (error "Failed parse") . parseSExpr)
|
|
|
- printSExpr
|
|
|
- stripAllText
|
|
|
- src
|
|
|
-
|
|
|
-testParsePPrint width indentStyle testName src =
|
|
|
- testRoundTrip (testName <> " pretty print")
|
|
|
- (fromRight (error "Failed parse") . parseSExpr)
|
|
|
- (pprintSExpr width indentStyle)
|
|
|
- stripAllText
|
|
|
- src
|
|
|
-
|
|
|
-stripAllText = T.unwords . concatMap T.words . T.lines
|
|
|
-
|
|
|
-testRoundTrip nm there back prep src = TestList
|
|
|
- [ TestLabel (nm <> " round trip") $
|
|
|
- TestCase $ (prep src) @=? (prep $ back $ there src)
|
|
|
-
|
|
|
- , TestLabel (nm <> " round trip twice") $
|
|
|
- TestCase $ (prep src) @=? (prep $ back $ there $ back $ there src)
|
|
|
- ]
|
|
|
-
|
|
|
-
|
|
|
|
|
|
-
|
|
|
-data FAtom = AIdent String
|
|
|
- | AQuoted String
|
|
|
- | AString String
|
|
|
- | AInt Integer
|
|
|
- | ABV Int Integer
|
|
|
- deriving (Eq, Show)
|
|
|
-
|
|
|
-
|
|
|
-string :: String -> SExpr FAtom
|
|
|
-string = SAtom . AString
|
|
|
-
|
|
|
-ident :: String -> SExpr FAtom
|
|
|
-ident = SAtom . AIdent
|
|
|
-
|
|
|
-quoted :: String -> SExpr FAtom
|
|
|
-quoted = SAtom . AQuoted
|
|
|
-
|
|
|
-int :: Integer -> SExpr FAtom
|
|
|
-int = SAtom . AInt
|
|
|
-
|
|
|
-
|
|
|
-printAtom :: FAtom -> T.Text
|
|
|
-printAtom a =
|
|
|
- case a of
|
|
|
- AIdent s -> T.pack s
|
|
|
- AQuoted s -> T.pack ('\'' : s)
|
|
|
- AString s -> T.pack (show s)
|
|
|
- AInt i -> T.pack (show i)
|
|
|
- ABV w val -> formatBV w val
|
|
|
-
|
|
|
-
|
|
|
-printSExpr :: SExpr FAtom -> T.Text
|
|
|
-printSExpr = encodeOne (flatPrint printAtom)
|
|
|
-
|
|
|
-pprintSExpr :: Int -> Indent -> SExpr FAtom -> T.Text
|
|
|
-pprintSExpr w i = encodeOne (setIndentStrategy (const i) $
|
|
|
- setMaxWidth w $
|
|
|
- setIndentAmount 1 $
|
|
|
- basicPrint printAtom)
|
|
|
-
|
|
|
-getIdent :: FAtom -> Maybe String
|
|
|
-getIdent (AIdent s) = Just s
|
|
|
-getIdent _ = Nothing
|
|
|
-
|
|
|
-formatBV :: Int -> Integer -> T.Text
|
|
|
-formatBV w val = T.pack (prefix ++ printf fmt val)
|
|
|
- where
|
|
|
- (prefix, fmt)
|
|
|
- | w `rem` 4 == 0 = ("#x", "%0" ++ show (w `div` 4) ++ "x")
|
|
|
- | otherwise = ("#b", "%0" ++ show w ++ "b")
|
|
|
-
|
|
|
-parseIdent :: Parser String
|
|
|
-parseIdent = (:) <$> first <*> P.many rest
|
|
|
- where first = P.letter P.<|> P.oneOf "+-=<>_"
|
|
|
- rest = P.letter P.<|> P.digit P.<|> P.oneOf "+-=<>_"
|
|
|
-
|
|
|
-parseString :: Parser String
|
|
|
-parseString = do
|
|
|
- _ <- P.char '"'
|
|
|
- s <- P.many (P.noneOf ['"'])
|
|
|
- _ <- P.char '"'
|
|
|
- return s
|
|
|
-
|
|
|
-parseBV :: Parser (Int, Integer)
|
|
|
-parseBV = P.char '#' >> ((P.char 'b' >> parseBin) P.<|> (P.char 'x' >> parseHex))
|
|
|
- where parseBin = P.oneOf "10" >>= \d -> parseBin' (1, if d == '1' then 1 else 0)
|
|
|
-
|
|
|
- parseBin' :: (Int, Integer) -> Parser (Int, Integer)
|
|
|
- parseBin' (bits, x) = do
|
|
|
- P.optionMaybe (P.oneOf "10") >>= \case
|
|
|
- Just d -> parseBin' (bits + 1, x * 2 + (if d == '1' then 1 else 0))
|
|
|
- Nothing -> return (bits, x)
|
|
|
-
|
|
|
- parseHex = (\s -> (length s * 4, read ("0x" ++ s))) <$> P.many1 P.hexDigit
|
|
|
-
|
|
|
-parseAtom :: Parser FAtom
|
|
|
-parseAtom
|
|
|
- = AIdent <$> parseIdent
|
|
|
- P.<|> AQuoted <$> (P.char '\'' >> parseIdent)
|
|
|
- P.<|> AString <$> parseString
|
|
|
- P.<|> AInt . read <$> P.many1 P.digit
|
|
|
- P.<|> uncurry ABV <$> parseBV
|
|
|
-
|
|
|
-parserLL :: SExprParser FAtom (SExpr FAtom)
|
|
|
-parserLL = withLispComments (mkParser parseAtom)
|
|
|
-
|
|
|
-parseSExpr :: T.Text -> Either String (SExpr FAtom)
|
|
|
-parseSExpr = decodeOne parserLL
|