github.com/aretext/aretext@v1.3.0/syntax/languages/golang_test.go (about)

     1  package languages
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  
     8  	"github.com/aretext/aretext/syntax/parser"
     9  )
    10  
    11  func TestGolangParseFunc(t *testing.T) {
    12  	testCases := []struct {
    13  		name     string
    14  		text     string
    15  		expected []TokenWithText
    16  	}{
    17  		{
    18  			name: "line comment",
    19  			text: "// comment",
    20  			expected: []TokenWithText{
    21  				{Text: `// comment`, Role: parser.TokenRoleComment},
    22  			},
    23  		},
    24  		{
    25  			name: "empty comment at end of file",
    26  			text: "//",
    27  			expected: []TokenWithText{
    28  				{Text: "//", Role: parser.TokenRoleComment},
    29  			},
    30  		},
    31  		{
    32  			name: "general comment",
    33  			text: "/* abcd\n123 */",
    34  			expected: []TokenWithText{
    35  				{Text: "/* abcd\n123 */", Role: parser.TokenRoleComment},
    36  			},
    37  		},
    38  		{
    39  			name: "variable declaration",
    40  			text: `var foo []int`,
    41  			expected: []TokenWithText{
    42  				{Text: "var", Role: parser.TokenRoleKeyword},
    43  				{Text: "int", Role: parser.TokenRoleKeyword},
    44  			},
    45  		},
    46  		{
    47  			name: "operators",
    48  			text: "a + b / c",
    49  			expected: []TokenWithText{
    50  				{Text: "+", Role: parser.TokenRoleOperator},
    51  				{Text: "/", Role: parser.TokenRoleOperator},
    52  			},
    53  		},
    54  		{
    55  			name: "raw string",
    56  			text: "`abcd\n123`",
    57  			expected: []TokenWithText{
    58  				{Text: "`abcd\n123`", Role: parser.TokenRoleString},
    59  			},
    60  		},
    61  		{
    62  			name: "interpreted string",
    63  			text: `"abcd"`,
    64  			expected: []TokenWithText{
    65  				{Text: `"abcd"`, Role: parser.TokenRoleString},
    66  			},
    67  		},
    68  		{
    69  			name: "interpreted string empty",
    70  			text: `""`,
    71  			expected: []TokenWithText{
    72  				{Text: `""`, Role: parser.TokenRoleString},
    73  			},
    74  		},
    75  		{
    76  			name: "interpreted string with escaped quote",
    77  			text: `"ab\"cd"`,
    78  			expected: []TokenWithText{
    79  				{Text: `"ab\"cd"`, Role: parser.TokenRoleString},
    80  			},
    81  		},
    82  		{
    83  			name: "interpreted string ending with escaped backslash",
    84  			text: `"abc\\"`,
    85  			expected: []TokenWithText{
    86  				{Text: `"abc\\"`, Role: parser.TokenRoleString},
    87  			},
    88  		},
    89  		{
    90  			name: "incomplete interpreted string ending with escaped quote",
    91  			text: `"abc\" 123`,
    92  			expected: []TokenWithText{
    93  				{Text: `123`, Role: parser.TokenRoleNumber},
    94  			},
    95  		},
    96  		{
    97  			name:     "incomplete interpreted string with newline before quote",
    98  			text:     "\"abc\n\"",
    99  			expected: []TokenWithText{},
   100  		},
   101  		{
   102  			name: "rune",
   103  			text: `'a'`,
   104  			expected: []TokenWithText{
   105  				{Text: "'a'", Role: parser.TokenRoleString},
   106  			},
   107  		},
   108  		{
   109  			name: "rune with escaped newline",
   110  			text: `'\n'`,
   111  			expected: []TokenWithText{
   112  				{Text: `'\n'`, Role: parser.TokenRoleString},
   113  			},
   114  		},
   115  		{
   116  			name: "rune with escaped quote",
   117  			text: `'\''`,
   118  			expected: []TokenWithText{
   119  				{Text: `'\''`, Role: parser.TokenRoleString},
   120  			},
   121  		},
   122  		{
   123  			name: "rune with escaped backslash",
   124  			text: `'\\'`,
   125  			expected: []TokenWithText{
   126  				{Text: `'\\'`, Role: parser.TokenRoleString},
   127  			},
   128  		},
   129  		{
   130  			name:     "incomplete rune with newline before quote",
   131  			text:     "'\n'",
   132  			expected: []TokenWithText{},
   133  		},
   134  		{
   135  			name:     "identifier with underscore prefix",
   136  			text:     "_x9",
   137  			expected: []TokenWithText{},
   138  		},
   139  		{
   140  			name:     "identifier with mixed case",
   141  			text:     "ThisVariableIsExported",
   142  			expected: []TokenWithText{},
   143  		},
   144  		{
   145  			name:     "identifier with non-ascii unicode",
   146  			text:     "αβ",
   147  			expected: []TokenWithText{},
   148  		},
   149  		{
   150  			name: "integer with underscore",
   151  			text: "4_2",
   152  			expected: []TokenWithText{
   153  				{Text: `4_2`, Role: parser.TokenRoleNumber},
   154  			},
   155  		},
   156  		{
   157  			name: "octal without o separator",
   158  			text: "0600",
   159  			expected: []TokenWithText{
   160  				{Text: `0600`, Role: parser.TokenRoleNumber},
   161  			},
   162  		},
   163  		{
   164  			name: "integer with leading zero and underscore",
   165  			text: "0_600",
   166  			expected: []TokenWithText{
   167  				{Text: `0_600`, Role: parser.TokenRoleNumber},
   168  			},
   169  		},
   170  		{
   171  			name: "octal with lowercase o",
   172  			text: "0o600",
   173  			expected: []TokenWithText{
   174  				{Text: `0o600`, Role: parser.TokenRoleNumber},
   175  			},
   176  		},
   177  		{
   178  			name: "octal with uppercase O",
   179  			text: "0O600",
   180  			expected: []TokenWithText{
   181  				{Text: `0O600`, Role: parser.TokenRoleNumber},
   182  			},
   183  		},
   184  		{
   185  			name: "octal denoted by leading zero",
   186  			text: "0123",
   187  			expected: []TokenWithText{
   188  				{Text: "0123", Role: parser.TokenRoleNumber},
   189  			},
   190  		},
   191  		{
   192  			name: "hex with lowercase x",
   193  			text: "0xBadFace",
   194  			expected: []TokenWithText{
   195  				{Text: `0xBadFace`, Role: parser.TokenRoleNumber},
   196  			},
   197  		},
   198  		{
   199  			name: "hex with lowercase x and underscore",
   200  			text: "0xBad_Face",
   201  			expected: []TokenWithText{
   202  				{Text: `0xBad_Face`, Role: parser.TokenRoleNumber},
   203  			},
   204  		},
   205  		{
   206  			name: "hex with leading underscore",
   207  			text: "0x_67_7a_2f_cc_40_c6",
   208  			expected: []TokenWithText{
   209  				{Text: `0x_67_7a_2f_cc_40_c6`, Role: parser.TokenRoleNumber},
   210  			},
   211  		},
   212  		{
   213  			name: "long number no underscores",
   214  			text: "170141183460469231731687303715884105727",
   215  			expected: []TokenWithText{
   216  				{Text: `170141183460469231731687303715884105727`, Role: parser.TokenRoleNumber},
   217  			},
   218  		},
   219  		{
   220  			name: "long number with underscores",
   221  			text: "170_141183_460469_231731_687303_715884_105727",
   222  			expected: []TokenWithText{
   223  				{Text: `170_141183_460469_231731_687303_715884_105727`, Role: parser.TokenRoleNumber},
   224  			},
   225  		},
   226  		{
   227  			name:     "identifier with leading underscore and digits",
   228  			text:     "_42",
   229  			expected: []TokenWithText{},
   230  		},
   231  		{
   232  			name: "invalid number with digits and trailing underscore",
   233  			text: "42_",
   234  			expected: []TokenWithText{
   235  				{Text: `42`, Role: parser.TokenRoleNumber},
   236  			},
   237  		},
   238  		{
   239  			name: "invalid number with multiple underscores",
   240  			text: "4__2",
   241  			expected: []TokenWithText{
   242  				{Text: `4`, Role: parser.TokenRoleNumber},
   243  			},
   244  		},
   245  		{
   246  			name: "invalid hex with leading underscore",
   247  			text: "0_xBadFace",
   248  			expected: []TokenWithText{
   249  				{Text: `0`, Role: parser.TokenRoleNumber},
   250  			},
   251  		},
   252  		{
   253  			name: "floating point zero with decimal",
   254  			text: "0.",
   255  			expected: []TokenWithText{
   256  				{Text: `0.`, Role: parser.TokenRoleNumber},
   257  			},
   258  		},
   259  		{
   260  			name: "floating point with decimal",
   261  			text: "72.40",
   262  			expected: []TokenWithText{
   263  				{Text: `72.40`, Role: parser.TokenRoleNumber},
   264  			},
   265  		},
   266  		{
   267  			name: "floating point with decimal and leading zero",
   268  			text: "072.40",
   269  			expected: []TokenWithText{
   270  				{Text: `072.40`, Role: parser.TokenRoleNumber},
   271  			},
   272  		},
   273  		{
   274  			name: "floating point with single leading decimal",
   275  			text: "2.71828",
   276  			expected: []TokenWithText{
   277  				{Text: `2.71828`, Role: parser.TokenRoleNumber},
   278  			},
   279  		},
   280  		{
   281  			name: "floating point with exponent zero",
   282  			text: "1.e+0",
   283  			expected: []TokenWithText{
   284  				{Text: `1.e+0`, Role: parser.TokenRoleNumber},
   285  			},
   286  		},
   287  		{
   288  			name: "floating point with negative exponent",
   289  			text: "6.67428e-11",
   290  			expected: []TokenWithText{
   291  				{Text: `6.67428e-11`, Role: parser.TokenRoleNumber},
   292  			},
   293  		},
   294  		{
   295  			name: "exponent with uppercase E",
   296  			text: "1E6",
   297  			expected: []TokenWithText{
   298  				{Text: `1E6`, Role: parser.TokenRoleNumber},
   299  			},
   300  		},
   301  		{
   302  			name: "floating point decimal no leading zero",
   303  			text: ".25",
   304  			expected: []TokenWithText{
   305  				{Text: `.25`, Role: parser.TokenRoleNumber},
   306  			},
   307  		},
   308  		{
   309  			name: "floating point decimal no leading zero with exponent",
   310  			text: ".12345E+5",
   311  			expected: []TokenWithText{
   312  				{Text: `.12345E+5`, Role: parser.TokenRoleNumber},
   313  			},
   314  		},
   315  		{
   316  			name: "floating point decimal with underscore",
   317  			text: "1_5.",
   318  			expected: []TokenWithText{
   319  				{Text: `1_5.`, Role: parser.TokenRoleNumber},
   320  			},
   321  		},
   322  		{
   323  			name: "floating point exponent with underscore",
   324  			text: "0.15e+0_2",
   325  			expected: []TokenWithText{
   326  				{Text: `0.15e+0_2`, Role: parser.TokenRoleNumber},
   327  			},
   328  		},
   329  		{
   330  			name: "floating point hex with negative exponent",
   331  			text: "0x1p-2",
   332  			expected: []TokenWithText{
   333  				{Text: `0x1p-2`, Role: parser.TokenRoleNumber},
   334  			},
   335  		},
   336  		{
   337  			name: "floating point hex with positive exponent",
   338  			text: "0x2.p10",
   339  			expected: []TokenWithText{
   340  				{Text: `0x2.p10`, Role: parser.TokenRoleNumber},
   341  			},
   342  		},
   343  		{
   344  			name: "floating point hex with plus zero exponent",
   345  			text: "0x1.Fp+0",
   346  			expected: []TokenWithText{
   347  				{Text: `0x1.Fp+0`, Role: parser.TokenRoleNumber},
   348  			},
   349  		},
   350  		{
   351  			name: "floating point hex with minus zero exponent",
   352  			text: "0X.8p-0",
   353  			expected: []TokenWithText{
   354  				{Text: `0X.8p-0`, Role: parser.TokenRoleNumber},
   355  			},
   356  		},
   357  		{
   358  			name: "floating point hex with underscores",
   359  			text: "0X_1FFFP-16",
   360  			expected: []TokenWithText{
   361  				{Text: `0X_1FFFP-16`, Role: parser.TokenRoleNumber},
   362  			},
   363  		},
   364  		{
   365  			name: "floating point hex minus integer",
   366  			text: "0x15e-2",
   367  			expected: []TokenWithText{
   368  				{Text: `0x15e`, Role: parser.TokenRoleNumber},
   369  				{Text: `-`, Role: parser.TokenRoleOperator},
   370  				{Text: `2`, Role: parser.TokenRoleNumber},
   371  			},
   372  		},
   373  		{
   374  			name: "floating point hex invalid, mantissa has no digits",
   375  			text: "0x.p1",
   376  			expected: []TokenWithText{
   377  				{Text: `0`, Role: parser.TokenRoleNumber},
   378  			},
   379  		},
   380  		{
   381  			name: "floating point hex invalid, p exponent requires hexadecimal mantissa",
   382  			text: "1p-2",
   383  			expected: []TokenWithText{
   384  				{Text: `1`, Role: parser.TokenRoleNumber},
   385  				{Text: `-`, Role: parser.TokenRoleOperator},
   386  				{Text: `2`, Role: parser.TokenRoleNumber},
   387  			},
   388  		},
   389  		{
   390  			name: "floating point hex invalid, hexadecimal mantissa requires p exponent",
   391  			text: "0x1.5e-2",
   392  			expected: []TokenWithText{
   393  				{Text: `0x1`, Role: parser.TokenRoleNumber},
   394  				{Text: `.5e-2`, Role: parser.TokenRoleNumber},
   395  			},
   396  		},
   397  		{
   398  			name: "floating point hex invalid, _ must separate successive digits before decimal point",
   399  			text: "1_.5",
   400  			expected: []TokenWithText{
   401  				{Text: `1`, Role: parser.TokenRoleNumber},
   402  				{Text: `.5`, Role: parser.TokenRoleNumber},
   403  			},
   404  		},
   405  		{
   406  			name: "floating point hex invalid, _ must separate successive digits after decimal point",
   407  			text: "1._5",
   408  			expected: []TokenWithText{
   409  				{Text: `1.`, Role: parser.TokenRoleNumber},
   410  			},
   411  		},
   412  		{
   413  			name: "floating point hex invalid, _ must separate successive digits before exponent",
   414  			text: "1.5_e1",
   415  			expected: []TokenWithText{
   416  				{Text: `1.5`, Role: parser.TokenRoleNumber},
   417  			},
   418  		},
   419  		{
   420  			name: "floating point hex invalid, _ must separate successive digits after exponent",
   421  			text: "1.5e_1",
   422  			expected: []TokenWithText{
   423  				{Text: `1.5`, Role: parser.TokenRoleNumber},
   424  			},
   425  		},
   426  		{
   427  			name: "floating point hex invalid, _ must separate successive digits at end",
   428  			text: "1.5e1_",
   429  			expected: []TokenWithText{
   430  				{Text: `1.5e1`, Role: parser.TokenRoleNumber},
   431  			},
   432  		},
   433  		{
   434  			name: "imaginary zero",
   435  			text: "0i",
   436  			expected: []TokenWithText{
   437  				{Text: `0i`, Role: parser.TokenRoleNumber},
   438  			},
   439  		},
   440  		{
   441  			name: "imaginary decimal with leading zero",
   442  			text: "0123i",
   443  			expected: []TokenWithText{
   444  				{Text: `0123i`, Role: parser.TokenRoleNumber},
   445  			},
   446  		},
   447  		{
   448  			name: "imaginary octal",
   449  			text: "0o123i",
   450  			expected: []TokenWithText{
   451  				{Text: `0o123i`, Role: parser.TokenRoleNumber},
   452  			},
   453  		},
   454  		{
   455  			name: "imaginary hex",
   456  			text: "0xabci",
   457  			expected: []TokenWithText{
   458  				{Text: `0xabci`, Role: parser.TokenRoleNumber},
   459  			},
   460  		},
   461  		{
   462  			name: "imaginary floating point zero",
   463  			text: "0.i",
   464  			expected: []TokenWithText{
   465  				{Text: `0.i`, Role: parser.TokenRoleNumber},
   466  			},
   467  		},
   468  		{
   469  			name: "imaginary floating point with decimal",
   470  			text: "2.71828i",
   471  			expected: []TokenWithText{
   472  				{Text: `2.71828i`, Role: parser.TokenRoleNumber},
   473  			},
   474  		},
   475  		{
   476  			name: "imaginary floating point zero exponent",
   477  			text: "1.e+0i",
   478  			expected: []TokenWithText{
   479  				{Text: `1.e+0i`, Role: parser.TokenRoleNumber},
   480  			},
   481  		},
   482  		{
   483  			name: "imaginary floating point negative exponent",
   484  			text: "6.67428e-11i",
   485  			expected: []TokenWithText{
   486  				{Text: `6.67428e-11i`, Role: parser.TokenRoleNumber},
   487  			},
   488  		},
   489  		{
   490  			name: "imaginary floating point uppercase exponent",
   491  			text: "1E6i",
   492  			expected: []TokenWithText{
   493  				{Text: `1E6i`, Role: parser.TokenRoleNumber},
   494  			},
   495  		},
   496  		{
   497  			name: "imaginary floating point no leading zero",
   498  			text: ".25i",
   499  			expected: []TokenWithText{
   500  				{Text: `.25i`, Role: parser.TokenRoleNumber},
   501  			},
   502  		},
   503  		{
   504  			name: "imaginary floating point no leading zero with exponent",
   505  			text: ".12345E+5i",
   506  			expected: []TokenWithText{
   507  				{Text: `.12345E+5i`, Role: parser.TokenRoleNumber},
   508  			},
   509  		},
   510  		{
   511  			name: "imaginary floating point hex with negative exponent",
   512  			text: "0x1p-2i",
   513  			expected: []TokenWithText{
   514  				{Text: `0x1p-2i`, Role: parser.TokenRoleNumber},
   515  			},
   516  		},
   517  		{
   518  			name: "const declaration",
   519  			text: `const foo = "test"`,
   520  			expected: []TokenWithText{
   521  				{Text: "const", Role: parser.TokenRoleKeyword},
   522  				{Text: "=", Role: parser.TokenRoleOperator},
   523  				{Text: `"test"`, Role: parser.TokenRoleString},
   524  			},
   525  		},
   526  		{
   527  			name: "interface with underlying type",
   528  			text: `
   529  interface {
   530  	~int
   531  	String() string
   532  }`,
   533  			expected: []TokenWithText{
   534  				{Text: "interface", Role: parser.TokenRoleKeyword},
   535  				{Text: "~", Role: parser.TokenRoleOperator},
   536  				{Text: "int", Role: parser.TokenRoleKeyword},
   537  				{Text: "string", Role: parser.TokenRoleKeyword},
   538  			},
   539  		},
   540  	}
   541  
   542  	for _, tc := range testCases {
   543  		t.Run(tc.name, func(t *testing.T) {
   544  			tokens := ParseTokensWithText(GolangParseFunc(), tc.text)
   545  			assert.Equal(t, tc.expected, tokens)
   546  		})
   547  	}
   548  }
   549  
   550  func BenchmarkGolangParser(b *testing.B) {
   551  	BenchmarkParser(b, GolangParseFunc(), "testdata/golang/fib.go")
   552  }