Add some Pretty instances

and golden tests
This commit is contained in:
Francesco Gazzetta
2022-07-01 20:14:43 +02:00
parent 594fcdc173
commit 91fa85e816
14 changed files with 178 additions and 3 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ dist-newstyle
*.o *.o
*.hi *.hi
.ghc.environment.* .ghc.environment.*
*.tix

1
golden/const.qbe Normal file
View File

@@ -0,0 +1 @@
[-1, 2, s_0.1, d_-0.2, $global]

1
golden/ident.qbe Normal file
View File

@@ -0,0 +1 @@
[:aggregateTy, $global, %temporary, @label]

1
golden/jmp.qbe Normal file
View File

@@ -0,0 +1 @@
jmp @target

1
golden/jnz.qbe Normal file
View File

@@ -0,0 +1 @@
jnz 0, @target1, @target2

1
golden/linkage.qbe Normal file
View File

@@ -0,0 +1 @@
(export, section secName, section secName flag1 flag2)

1
golden/opaque.qbe Normal file
View File

@@ -0,0 +1 @@
type :t = align 8 {16}

1
golden/ret.qbe Normal file
View File

@@ -0,0 +1 @@
ret %x

1
golden/type.qbe Normal file
View File

@@ -0,0 +1 @@
([w, l, s, d], [w, b, h])

1
golden/typedef.qbe Normal file
View File

@@ -0,0 +1 @@
type :t = 8 {h 16, :t1}

1
golden/val.qbe Normal file
View File

@@ -0,0 +1 @@
[0, %temporary, $global]

View File

@@ -14,12 +14,24 @@ build-type: Simple
extra-doc-files: CHANGELOG.md extra-doc-files: CHANGELOG.md
-- extra-source-files: -- extra-source-files:
common common
ghc-options: -Wall
-Wunused-packages
-Wmissing-home-modules
-Wredundant-constraints
-Wincomplete-uni-patterns
-Wincomplete-record-updates
default-language: Haskell2010
library library
import: common
exposed-modules: Language.QBE exposed-modules: Language.QBE
-- other-modules: -- other-modules:
other-extensions: DataKinds other-extensions: DataKinds
KindSignatures KindSignatures
GeneralizedNewtypeDeriving GeneralizedNewtypeDeriving
FlexibleInstances
OverloadedStrings
build-depends: base ^>=4.16.1.0 build-depends: base ^>=4.16.1.0
, text , text
, text-short , text-short
@@ -27,6 +39,21 @@ library
, hashable , hashable
, deepseq , deepseq
, binary , binary
, prettyprinter
hs-source-dirs: src hs-source-dirs: src
ghc-options: -Wall
default-language: Haskell2010 test-suite golden
import: common
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
build-depends: base
, qbe
, filepath
, tasty
, tasty-silver
, prettyprinter
other-extensions: TypeApplications
OverloadedStrings
DataKinds
ExistentialQuantification

View File

@@ -1,13 +1,19 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-} {-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Language.QBE where module Language.QBE where
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Short (ShortText) import Data.Text.Short (ShortText)
import qualified Data.Text.Short as TS
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.Word (Word64) import Data.Word (Word64)
import Data.List.NonEmpty (NonEmpty) import Data.List.NonEmpty (NonEmpty)
import Prettyprinter
( Pretty(pretty), (<+>)
, space, encloseSep, lbrace, rbrace, comma, equals, braces )
-- Instances -- Instances
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Control.DeepSeq (NFData) import Control.DeepSeq (NFData)
@@ -28,18 +34,52 @@ data Sigil
-- | QBE identifiers. The sigil is represented at the type level, so that -- | QBE identifiers. The sigil is represented at the type level, so that
-- mixing incompatible identifiers is impossible. -- mixing incompatible identifiers is impossible.
--
-- >>> :set -XOverloadedStrings
-- >>> :set -XDataKinds
-- >>> :set -XTypeApplications
-- >>> pretty $ Jmp $ Ident @'Label "a"
-- jmp @a
-- >>> pretty $ Jmp $ Ident @'Global "a"
-- <interactive>:5:16: error:
-- • Couldn't match type 'Global with 'Label
-- Expected: Ident 'Label
-- Actual: Ident 'Global
-- • In the second argument of ($), namely Ident @'Global "a"
-- In the second argument of ($), namely Jmp $ Ident @'Global "a"
-- In the expression: pretty $ Jmp $ Ident @'Global "a"
newtype Ident (t :: Sigil) = Ident RawIdent newtype Ident (t :: Sigil) = Ident RawIdent
deriving (Show, Eq, Ord, IsString, Binary, NFData, Hashable) deriving (Show, Eq, Ord, IsString, Binary, NFData, Hashable)
instance Pretty (Ident 'AggregateTy) where
pretty (Ident raw) = pretty ':' <> pretty (TS.toText raw)
instance Pretty (Ident 'Global) where
pretty (Ident raw) = pretty '$' <> pretty (TS.toText raw)
instance Pretty (Ident 'Temporary) where
pretty (Ident raw) = pretty '%' <> pretty (TS.toText raw)
instance Pretty (Ident 'Label) where
pretty (Ident raw) = pretty '@' <> pretty (TS.toText raw)
-- * Types -- * Types
---------- ----------
data BaseTy = Word | Long | Single | Double data BaseTy = Word | Long | Single | Double
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty BaseTy where
pretty Word = pretty 'w'
pretty Long = pretty 'l'
pretty Single = pretty 's'
pretty Double = pretty 'd'
data ExtTy = BaseTy BaseTy | Byte | HalfWord data ExtTy = BaseTy BaseTy | Byte | HalfWord
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty ExtTy where
pretty (BaseTy baseTy) = pretty baseTy
pretty Byte = pretty 'b'
pretty HalfWord = pretty 'h'
-- * Constants -- * Constants
-------------- --------------
@@ -50,6 +90,13 @@ data Const
| CGlobal (Ident 'Global) | CGlobal (Ident 'Global)
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty Const where
pretty (CInt negative int) | negative = pretty '-' <> pretty int
| otherwise = pretty int
pretty (CSingle float) = "s_" <> pretty float
pretty (CDouble double) = "d_" <> pretty double
pretty (CGlobal ident) = pretty ident
-- * Linkage -- * Linkage
------------ ------------
@@ -58,6 +105,12 @@ data Linkage
| Section ShortText (Maybe Text) | Section ShortText (Maybe Text)
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty Linkage where
pretty Export = "export"
pretty (Section secName Nothing) = "section" <+> pretty (TS.toText secName)
pretty (Section secName (Just secFlags)) =
"section" <+> pretty (TS.toText secName) <+> pretty secFlags
-- * Definitions -- * Definitions
---------------- ----------------
@@ -73,11 +126,27 @@ data Typedef
| Opaque (Ident 'AggregateTy) Alignment Size | Opaque (Ident 'AggregateTy) Alignment Size
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty Typedef where
pretty (Typedef ident alignment def) =
"type" <+> pretty ident <+> equals
<> maybe mempty (\x -> space <> pretty x) alignment
<+> encloseSep lbrace rbrace (comma <> space) (prettyItem <$> def)
where
prettyItem (subTy, Nothing ) = pretty subTy
prettyItem (subTy, Just amount) = pretty subTy <+> pretty amount
pretty (Opaque ident alignment size) =
"type" <+> pretty ident <+> equals
<+> "align" <+> pretty alignment <+> braces (pretty size)
data SubTy data SubTy
= SubExtTy = SubExtTy ExtTy
| SubAggregateTy (Ident 'AggregateTy) | SubAggregateTy (Ident 'AggregateTy)
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty SubTy where
pretty (SubExtTy extTy) = pretty extTy
pretty (SubAggregateTy ident) = pretty ident
-- ** Data -- ** Data
---------- ----------
@@ -120,6 +189,11 @@ data Val
| ValGlobal (Ident 'Global) | ValGlobal (Ident 'Global)
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty Val where
pretty (ValConst c) = pretty c
pretty (ValTemporary ident) = pretty ident
pretty (ValGlobal ident) = pretty ident
data Block = Block (Ident 'Label) [Phi] [Inst] Jump data Block = Block (Ident 'Label) [Phi] [Inst] Jump
deriving (Show, Eq) deriving (Show, Eq)
@@ -129,6 +203,14 @@ data Jump
| Ret (Maybe Val) | Ret (Maybe Val)
deriving (Show, Eq) deriving (Show, Eq)
instance Pretty Jump where
pretty (Jmp ident) = "jmp" <+> pretty ident
pretty (Jnz val label1 label2) =
"jnz" <+> pretty val <> comma
<+> pretty label1 <> comma
<+> pretty label2
pretty (Ret val) = "ret" <+> pretty val
-- * Instructions -- * Instructions
----------------- -----------------

55
test/Main.hs Normal file
View File

@@ -0,0 +1,55 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}
module Main (main) where
import Language.QBE
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.Silver (goldenVsAction)
import System.FilePath ((</>), (<.>))
import Prettyprinter (Pretty(pretty), layoutPretty, defaultLayoutOptions)
import Prettyprinter.Render.Text (renderStrict)
data P = forall a. Pretty a => P a
instance Pretty P where
pretty (P x) = pretty x
main :: IO ()
main = defaultMain goldenTests
goldenTests :: TestTree
goldenTests = testGroup "golden tests"
[ t "ident"
[ P $ Ident @'AggregateTy "aggregateTy"
, P $ Ident @'Global "global"
, P $ Ident @'Temporary "temporary"
, P $ Ident @'Label "label"
]
, t "type" ([Word, Long, Single, Double], [BaseTy Word, Byte, HalfWord])
, t "const"
[ CInt True 1
, CInt False 2
, CSingle 0.1
, CDouble (-0.2)
, CGlobal $ Ident "global"
]
, t "linkage" (Export, Section "secName" Nothing, Section "secName" $ Just "flag1 flag2")
, t "typedef" $ Typedef (Ident "t") (Just 8)
[ (SubExtTy HalfWord, Just 16)
, (SubAggregateTy (Ident "t1"), Nothing)
]
, t "opaque" $ Opaque (Ident "t") 8 16
, t "val" [ValConst (CInt False 0), ValTemporary $ Ident "temporary", ValGlobal $ Ident "global"]
, t "jmp" $ Jmp $ Ident "target"
, t "jnz" $ Jnz (ValConst $ CInt False 0) (Ident "target1") (Ident "target2")
, t "ret" $ Ret $ Just $ ValTemporary $ Ident "x"
]
where
t name value = goldenVsAction
name
("golden" </> name <.> "qbe")
(pure $ pretty value)
(renderStrict . layoutPretty defaultLayoutOptions)