1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
2--
3-- SPDX-License-Identifier: GPL-3.0-only
4
5module Parser where
6
7import Language.QBE.Parser (dataDef, funcDef, typeDef)
8import Language.QBE.Types
9import Test.Tasty
10import Test.Tasty.HUnit
11import qualified Text.ParserCombinators.Parsec as P
12
13typeTests :: TestTree
14typeTests =
15 testGroup
16 "Aggregate Type Definition"
17 [ testCase "Opaque type with alignment" $
18 let v = TypeDef (UserIdent "opaque") (Just AlignLongLong) (AOpaque 32)
19 in parse "type :opaque = align 16 { 32 }" @?= Right v,
20 testCase "Regular empty type" $
21 let v = TypeDef (UserIdent "empty") Nothing (ARegular [])
22 in parse "type :empty = {}" @?= Right v,
23 testCase "Regular type with multiple fields" $
24 let f = [(SExtType (Base Single), Nothing), (SExtType (Base Single), Nothing)]
25 v = TypeDef (UserIdent "twofloats") Nothing (ARegular f)
26 in parse "type :twofloats = { s, s }" @?= Right v,
27 testCase "Regular type with trailing whitespaces" $
28 let f = [(SExtType Byte, Nothing), (SExtType (Base Word), Just 100)]
29 v = TypeDef (UserIdent "abyteandmanywords") Nothing (ARegular f)
30 in parse "type :abyteandmanywords = { b, w 100 }" @?= Right v,
31 testCase "Union type with multiple fields" $
32 let f = [[(SExtType Byte, Nothing)], [(SExtType (Base Single), Nothing)]]
33 v = TypeDef (UserIdent "un9") Nothing (AUnion f)
34 in parse "type :un9 = { { b } { s } }" @?= Right v,
35 testCase "Union type with multiple nested fields" $
36 let f =
37 [ [(SExtType (Base Long), Nothing), (SExtType (Base Single), Nothing)],
38 [(SExtType (Base Word), Nothing), (SExtType (Base Long), Nothing)]
39 ]
40 v = TypeDef (UserIdent "un9") Nothing (AUnion f)
41 in parse "type :un9 = { { l, s } { w, l } }" @?= Right v
42 ]
43 where
44 parse :: String -> Either P.ParseError TypeDef
45 parse = P.parse typeDef ""
46
47dataTests :: TestTree
48dataTests =
49 testGroup
50 "Data Definition"
51 [ testCase "Data definition with zero fill" $
52 let v = DataDef [] (GlobalIdent "foo") Nothing [OZeroFill 42]
53 in parse "data $foo = { z 42 }" @?= Right v,
54 testCase "Data definition with empty value" $
55 let v = DataDef [] (GlobalIdent "foo") Nothing []
56 in parse "data $foo = {}" @?= Right v,
57 testCase "Data definition without optional spaces" $
58 let v = DataDef [] (GlobalIdent "foo") Nothing [OZeroFill 42]
59 in parse "data $foo={z 42}" @?= Right v,
60 testCase "Data definition with newlines as spaces" $
61 let v = DataDef [] (GlobalIdent "foo") Nothing [OZeroFill 42]
62 in parse "data\n$foo={z\n42}" @?= Right v,
63 testCase "Data definition with comments" $
64 let v = DataDef [] (GlobalIdent "foo") Nothing [OZeroFill 42]
65 in parse "data\n#test\n$foo={z\n#foo\n42}" @?= Right v,
66 testCase "Data definition with comments and whitespaces" $
67 let v = DataDef [] (GlobalIdent "foo") Nothing [OZeroFill 42]
68 in parse "data\n#test1 \n #test2\n$foo={z\n#foo\n42}" @?= Right v,
69 testCase "Data definition with linkage" $
70 let v = DataDef [LExport] (GlobalIdent "foo") Nothing [OZeroFill 42]
71 in parse "export data $foo = { z 42 }" @?= Right v,
72 testCase "Data definition with linkage, newlines, and comments" $
73 let v = DataDef [LExport, LThread] (GlobalIdent "foo") Nothing [OZeroFill 42]
74 in parse "export\nthread\n#foo\ndata $foo = { z 42 }" @?= Right v,
75 testCase "Data definition with types" $
76 let w = [DConst (Number 23), DConst (Number 42)]
77 v = DataDef [] (GlobalIdent "bar") Nothing [OItem (Base Word) w]
78 in parse "data $bar = { w 23 42 }" @?= Right v,
79 testCase "An object containing two 64-bit fields" $
80 let o =
81 [ OItem (Base Long) [DConst (Number 0xffffffffffffffff)],
82 OItem (Base Long) [DConst (Number 23)]
83 ]
84 v = DataDef [] (GlobalIdent "c") Nothing o
85 in parse "data $c = { l -1, l 23 }" @?= Right v,
86 testCase "Data definition with specified alignment and linkage" $
87 let v = DataDef [LExport] (GlobalIdent "b") (Just AlignLong) [OZeroFill 1000]
88 in parse "export data $b = align 8 { z 1000 }" @?= Right v,
89 testCase "Data definition with linkage section and string escape sequences" $
90 let v = DataDef [LSection "f\\oo\\\"bar" Nothing] (GlobalIdent "b") (Just AlignLong) [OZeroFill 1]
91 in parse "section \"f\\oo\\\"bar\" data $b =align 8 {z 1}" @?= Right v
92 ]
93 where
94 parse :: String -> Either P.ParseError DataDef
95 parse = P.parse dataDef ""
96
97funcTests :: TestTree
98funcTests =
99 testGroup
100 "Function Definition"
101 [ testCase "Minimal function definition" $
102 let p = [Regular (ABase Word) (LocalIdent "argc")]
103 b = [Block {label = BlockIdent "start", stmt = [], term = Return Nothing}]
104 f = FuncDef [] (GlobalIdent "main") Nothing p b
105 in parse "function $main(w %argc) {\n@start\nret\n}" @?= Right f,
106 testCase "Function definition with load instruction" $
107 let s = [Assign (LocalIdent "v") Word (Load (LBase Word) (VLocal $ LocalIdent "addr"))]
108 b = [Block {label = BlockIdent "begin", stmt = s, term = Return Nothing}]
109 f = FuncDef [] (GlobalIdent "main") Nothing [] b
110 in parse "function $main() {\n@begin\n%v =w loadw %addr\nret\n}" @?= Right f,
111 testCase "Function definition with linkage and return type" $
112 let p = [Regular (ABase Long) (LocalIdent "v")]
113 b = [Block {label = BlockIdent "start", stmt = [], term = Return Nothing}]
114 f = FuncDef [LExport, LThread] (GlobalIdent "example") (Just (ABase Word)) p b
115 in parse "export\nthread function w $example(l %v) {\n@start\nret\n}" @?= Right f,
116 testCase "Function definition with section linkage" $
117 let p = [Regular (ABase Long) (LocalIdent "v")]
118 b = [Block {label = BlockIdent "start", stmt = [], term = Return Nothing}]
119 f = FuncDef [LSection "foo" Nothing] (GlobalIdent "bla") (Just (ABase Word)) p b
120 in parse "section \"foo\"\nfunction w $bla(l %v) {\n@start\nret\n}" @?= Right f,
121 testCase "Function definition with subword return type" $
122 let b = [Block {label = BlockIdent "here", stmt = [], term = Halt}]
123 f = FuncDef [] (GlobalIdent "f") (Just (ASubWordType SignedHalf)) [] b
124 in parse "function sh $f() {\n@here\nhlt\n}" @?= Right f,
125 testCase "Function definition with comments" $
126 let p = [Regular (ABase Long) (LocalIdent "v")]
127 b = [Block {label = BlockIdent "start", stmt = [], term = Return Nothing}]
128 f = FuncDef [LSection "foo" (Just "bar")] (GlobalIdent "bla") (Just (ABase Word)) p b
129 in parse "section \"foo\" \"bar\"\n#test\nfunction w $bla(l %v) {\n#foo\n@start\n# bar \nret\n#bllubbb\n#bllaaa\n}" @?= Right f
130 ]
131 where
132 parse :: String -> Either P.ParseError FuncDef
133 parse = P.parse funcDef ""
134
135mkParser :: TestTree
136mkParser =
137 testGroup
138 "Tests for the QBE parser"
139 [typeTests, dataTests, funcTests]