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  }