github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/asm/lex/lex_test.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package lex 6 7 import ( 8 "strings" 9 "testing" 10 "text/scanner" 11 ) 12 13 type lexTest struct { 14 name string 15 input string 16 output string 17 } 18 19 var lexTests = []lexTest{ 20 { 21 "empty", 22 "", 23 "", 24 }, 25 { 26 "simple", 27 "1 (a)", 28 "1.(.a.)", 29 }, 30 { 31 "simple define", 32 lines( 33 "#define A 1234", 34 "A", 35 ), 36 "1234.\n", 37 }, 38 { 39 "define without value", 40 "#define A", 41 "", 42 }, 43 { 44 "macro without arguments", 45 "#define A() 1234\n" + "A()\n", 46 "1234.\n", 47 }, 48 { 49 "macro with just parens as body", 50 "#define A () \n" + "A\n", 51 "(.).\n", 52 }, 53 { 54 "macro with parens but no arguments", 55 "#define A (x) \n" + "A\n", 56 "(.x.).\n", 57 }, 58 { 59 "macro with arguments", 60 "#define A(x, y, z) x+z+y\n" + "A(1, 2, 3)\n", 61 "1.+.3.+.2.\n", 62 }, 63 { 64 "argumented macro invoked without arguments", 65 lines( 66 "#define X() foo ", 67 "X()", 68 "X", 69 ), 70 "foo.\n.X.\n", 71 }, 72 { 73 "multiline macro without arguments", 74 lines( 75 "#define A 1\\", 76 "\t2\\", 77 "\t3", 78 "before", 79 "A", 80 "after", 81 ), 82 "before.\n.1.\n.2.\n.3.\n.after.\n", 83 }, 84 { 85 "multiline macro with arguments", 86 lines( 87 "#define A(a, b, c) a\\", 88 "\tb\\", 89 "\tc", 90 "before", 91 "A(1, 2, 3)", 92 "after", 93 ), 94 "before.\n.1.\n.2.\n.3.\n.after.\n", 95 }, 96 { 97 "LOAD macro", 98 lines( 99 "#define LOAD(off, reg) \\", 100 "\tMOVBLZX (off*4)(R12), reg \\", 101 "\tADDB reg, DX", 102 "", 103 "LOAD(8, AX)", 104 ), 105 "\n.\n.MOVBLZX.(.8.*.4.).(.R12.).,.AX.\n.ADDB.AX.,.DX.\n", 106 }, 107 { 108 "nested multiline macro", 109 lines( 110 "#define KEYROUND(xmm, load, off, r1, r2, index) \\", 111 "\tMOVBLZX (BP)(DX*4), R8 \\", 112 "\tload((off+1), r2) \\", 113 "\tMOVB R8, (off*4)(R12) \\", 114 "\tPINSRW $index, (BP)(R8*4), xmm", 115 "#define LOAD(off, reg) \\", 116 "\tMOVBLZX (off*4)(R12), reg \\", 117 "\tADDB reg, DX", 118 "KEYROUND(X0, LOAD, 8, AX, BX, 0)", 119 ), 120 "\n.MOVBLZX.(.BP.).(.DX.*.4.).,.R8.\n.\n.MOVBLZX.(.(.8.+.1.).*.4.).(.R12.).,.BX.\n.ADDB.BX.,.DX.\n.MOVB.R8.,.(.8.*.4.).(.R12.).\n.PINSRW.$.0.,.(.BP.).(.R8.*.4.).,.X0.\n", 121 }, 122 { 123 "taken #ifdef", 124 lines( 125 "#define A", 126 "#ifdef A", 127 "#define B 1234", 128 "#endif", 129 "B", 130 ), 131 "1234.\n", 132 }, 133 { 134 "not taken #ifdef", 135 lines( 136 "#ifdef A", 137 "#define B 1234", 138 "#endif", 139 "B", 140 ), 141 "B.\n", 142 }, 143 { 144 "taken #ifdef with else", 145 lines( 146 "#define A", 147 "#ifdef A", 148 "#define B 1234", 149 "#else", 150 "#define B 5678", 151 "#endif", 152 "B", 153 ), 154 "1234.\n", 155 }, 156 { 157 "not taken #ifdef with else", 158 lines( 159 "#ifdef A", 160 "#define B 1234", 161 "#else", 162 "#define B 5678", 163 "#endif", 164 "B", 165 ), 166 "5678.\n", 167 }, 168 { 169 "nested taken/taken #ifdef", 170 lines( 171 "#define A", 172 "#define B", 173 "#ifdef A", 174 "#ifdef B", 175 "#define C 1234", 176 "#else", 177 "#define C 5678", 178 "#endif", 179 "#endif", 180 "C", 181 ), 182 "1234.\n", 183 }, 184 { 185 "nested taken/not-taken #ifdef", 186 lines( 187 "#define A", 188 "#ifdef A", 189 "#ifdef B", 190 "#define C 1234", 191 "#else", 192 "#define C 5678", 193 "#endif", 194 "#endif", 195 "C", 196 ), 197 "5678.\n", 198 }, 199 { 200 "nested not-taken/would-be-taken #ifdef", 201 lines( 202 "#define B", 203 "#ifdef A", 204 "#ifdef B", 205 "#define C 1234", 206 "#else", 207 "#define C 5678", 208 "#endif", 209 "#endif", 210 "C", 211 ), 212 "C.\n", 213 }, 214 { 215 "nested not-taken/not-taken #ifdef", 216 lines( 217 "#ifdef A", 218 "#ifdef B", 219 "#define C 1234", 220 "#else", 221 "#define C 5678", 222 "#endif", 223 "#endif", 224 "C", 225 ), 226 "C.\n", 227 }, 228 { 229 "nested #define", 230 lines( 231 "#define A #define B THIS", 232 "A", 233 "B", 234 ), 235 "THIS.\n", 236 }, 237 { 238 "nested #define with args", 239 lines( 240 "#define A #define B(x) x", 241 "A", 242 "B(THIS)", 243 ), 244 "THIS.\n", 245 }, 246 /* This one fails. See comment in Slice.Col. 247 { 248 "nested #define with args", 249 lines( 250 "#define A #define B (x) x", 251 "A", 252 "B(THIS)", 253 ), 254 "x.\n", 255 }, 256 */ 257 } 258 259 func TestLex(t *testing.T) { 260 for _, test := range lexTests { 261 input := NewInput(test.name) 262 input.Push(NewTokenizer(test.name, strings.NewReader(test.input), nil)) 263 result := drain(input) 264 if result != test.output { 265 t.Errorf("%s: got %q expected %q", test.name, result, test.output) 266 } 267 } 268 } 269 270 // lines joins the arguments together as complete lines. 271 func lines(a ...string) string { 272 return strings.Join(a, "\n") + "\n" 273 } 274 275 // drain returns a single string representing the processed input tokens. 276 func drain(input *Input) string { 277 var buf strings.Builder 278 for { 279 tok := input.Next() 280 if tok == scanner.EOF { 281 return buf.String() 282 } 283 if tok == '#' { 284 continue 285 } 286 if buf.Len() > 0 { 287 buf.WriteByte('.') 288 } 289 buf.WriteString(input.Text()) 290 } 291 } 292 293 type badLexTest struct { 294 input string 295 error string 296 } 297 298 var badLexTests = []badLexTest{ 299 { 300 "3 #define foo bar\n", 301 "'#' must be first item on line", 302 }, 303 { 304 "#ifdef foo\nhello", 305 "unclosed #ifdef or #ifndef", 306 }, 307 { 308 "#ifndef foo\nhello", 309 "unclosed #ifdef or #ifndef", 310 }, 311 { 312 "#ifdef foo\nhello\n#else\nbye", 313 "unclosed #ifdef or #ifndef", 314 }, 315 { 316 "#define A() A()\nA()", 317 "recursive macro invocation", 318 }, 319 { 320 "#define A a\n#define A a\n", 321 "redefinition of macro", 322 }, 323 { 324 "#define A a", 325 "no newline after macro definition", 326 }, 327 } 328 329 func TestBadLex(t *testing.T) { 330 for _, test := range badLexTests { 331 input := NewInput(test.error) 332 input.Push(NewTokenizer(test.error, strings.NewReader(test.input), nil)) 333 err := firstError(input) 334 if err == nil { 335 t.Errorf("%s: got no error", test.error) 336 continue 337 } 338 if !strings.Contains(err.Error(), test.error) { 339 t.Errorf("got error %q expected %q", err.Error(), test.error) 340 } 341 } 342 } 343 344 // firstError returns the first error value triggered by the input. 345 func firstError(input *Input) (err error) { 346 panicOnError = true 347 defer func() { 348 panicOnError = false 349 switch e := recover(); e := e.(type) { 350 case nil: 351 case error: 352 err = e 353 default: 354 panic(e) 355 } 356 }() 357 358 for { 359 tok := input.Next() 360 if tok == scanner.EOF { 361 return 362 } 363 } 364 }