Add some Pretty instances
and golden tests
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ dist-newstyle
|
|||||||
*.o
|
*.o
|
||||||
*.hi
|
*.hi
|
||||||
.ghc.environment.*
|
.ghc.environment.*
|
||||||
|
*.tix
|
||||||
|
|||||||
1
golden/const.qbe
Normal file
1
golden/const.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[-1, 2, s_0.1, d_-0.2, $global]
|
||||||
1
golden/ident.qbe
Normal file
1
golden/ident.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[:aggregateTy, $global, %temporary, @label]
|
||||||
1
golden/jmp.qbe
Normal file
1
golden/jmp.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
jmp @target
|
||||||
1
golden/jnz.qbe
Normal file
1
golden/jnz.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
jnz 0, @target1, @target2
|
||||||
1
golden/linkage.qbe
Normal file
1
golden/linkage.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(export, section secName, section secName flag1 flag2)
|
||||||
1
golden/opaque.qbe
Normal file
1
golden/opaque.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
type :t = align 8 {16}
|
||||||
1
golden/ret.qbe
Normal file
1
golden/ret.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ret %x
|
||||||
1
golden/type.qbe
Normal file
1
golden/type.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
([w, l, s, d], [w, b, h])
|
||||||
1
golden/typedef.qbe
Normal file
1
golden/typedef.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
type :t = 8 {h 16, :t1}
|
||||||
1
golden/val.qbe
Normal file
1
golden/val.qbe
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[0, %temporary, $global]
|
||||||
31
qbe.cabal
31
qbe.cabal
@@ -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
|
||||||
|
|||||||
@@ -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
55
test/Main.hs
Normal 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)
|
||||||
Reference in New Issue
Block a user