github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/scanner/scanner_test.go (about)

     1  package scanner
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/arnodel/golua/token"
     9  )
    10  
    11  type tok struct {
    12  	tp        token.Type
    13  	lit       string
    14  	pos, l, c int
    15  }
    16  
    17  func (t tok) Token() *token.Token {
    18  	line := t.l
    19  	if line == 0 {
    20  		line = 1
    21  	}
    22  	col := t.c
    23  	if col == 0 {
    24  		col = 1
    25  	}
    26  	return &token.Token{
    27  		Type: t.tp,
    28  		Lit:  []byte(t.lit),
    29  		Pos:  token.Pos{Offset: t.pos, Line: line, Column: col},
    30  	}
    31  }
    32  
    33  func tokenString(t *token.Token) string {
    34  	return fmt.Sprintf("Type:%d, Lit:%q, Pos:%s", t.Type, t.Lit, t.Pos)
    35  }
    36  
    37  func TestScanner(t *testing.T) {
    38  	tests := []struct {
    39  		text string
    40  		toks []tok
    41  		err  string
    42  	}{
    43  		//
    44  		// Tokens
    45  		//
    46  		{
    47  			`hello there`,
    48  			[]tok{
    49  				{token.IDENT, "hello", 0, 1, 1},
    50  				{token.IDENT, "there", 6, 1, 7},
    51  				{token.EOF, "", 11, 1, 12},
    52  			},
    53  			"",
    54  		},
    55  		{
    56  			`123.45 "abc" -0xff .5`,
    57  			[]tok{
    58  				{token.NUMDEC, "123.45", 0, 1, 1},
    59  				{token.STRING, `"abc"`, 7, 1, 8},
    60  				{token.SgMinus, "-", 13, 1, 14},
    61  				{token.NUMHEX, "0xff", 14, 1, 15},
    62  				{token.NUMDEC, ".5", 19, 1, 20},
    63  				{token.EOF, "", 21, 1, 22},
    64  			},
    65  			"",
    66  		},
    67  		{
    68  			"...,:<=}///[]>=~~====  --hi\n--[bye",
    69  			[]tok{
    70  				{token.SgEtc, "...", 0, 1, 1},
    71  				{token.SgComma, ",", 3, 1, 4},
    72  				{token.SgColon, ":", 4, 1, 5},
    73  				{token.SgLessEqual, "<=", 5, 1, 6},
    74  				{token.SgCloseBrace, "}", 7, 1, 8},
    75  				{token.SgSlashSlash, "//", 8, 1, 9},
    76  				{token.SgSlash, "/", 10, 1, 11},
    77  				{token.SgOpenSquareBkt, "[", 11, 1, 12},
    78  				{token.SgCloseSquareBkt, "]", 12, 1, 13},
    79  				{token.SgGreaterEqual, ">=", 13, 1, 14},
    80  				{token.SgTilde, "~", 15, 1, 16},
    81  				{token.SgNotEqual, "~=", 16, 1, 17},
    82  				{token.SgEqual, "==", 18, 1, 19},
    83  				{token.SgAssign, "=", 20, 1, 21},
    84  				{token.EOF, "", 34, 2, 7},
    85  			},
    86  			"",
    87  		},
    88  		{
    89  			"a\r\nb\n\rc",
    90  			[]tok{
    91  				{token.IDENT, "a", 0, 1, 1},
    92  				{token.IDENT, "b", 3, 2, 1},
    93  				{token.IDENT, "c", 6, 3, 1},
    94  			},
    95  			"",
    96  		},
    97  		// Token errors
    98  		{
    99  			`abc?xyz`,
   100  			[]tok{
   101  				{token.IDENT, "abc", 0, 1, 1},
   102  				{token.INVALID, "?", 3, 1, 4},
   103  			},
   104  			"illegal character",
   105  		},
   106  		// Number errors
   107  		{
   108  			"123zabc",
   109  			[]tok{
   110  				{token.NUMDEC, "123", 0, 1, 1},
   111  				{token.INVALID, "z", 3, 1, 4},
   112  			},
   113  			"illegal character following number",
   114  		},
   115  		{
   116  			"0x10abP(",
   117  			[]tok{{token.INVALID, "0x10abP", 0, 1, 1}},
   118  			"digit required after exponent",
   119  		},
   120  		//
   121  		// Long brackets
   122  		//
   123  		{
   124  			`if[[hello]"there]]`,
   125  			[]tok{
   126  				{token.KwIf, "if", 0, 1, 1},
   127  				{token.LONGSTRING, `[[hello]"there]]`, 2, 1, 3},
   128  			},
   129  			"",
   130  		},
   131  		{
   132  			"[==[xy\n\n]]===]==]123.34",
   133  			[]tok{
   134  				{token.LONGSTRING, "[==[xy\n\n]]===]==]", 0, 1, 1},
   135  				{token.NUMDEC, "123.34", 17, 3, 10},
   136  			},
   137  			"",
   138  		},
   139  		{
   140  			`1e3--[=[stuff--[[inner]] ]=]'"o"'`,
   141  			[]tok{
   142  				{token.NUMDEC, "1e3", 0, 1, 1},
   143  				{token.STRING, `'"o"'`, 28, 1, 29},
   144  			},
   145  			"",
   146  		},
   147  		// Long bracket errors
   148  		{
   149  			`[==!!!`,
   150  			[]tok{{token.INVALID, `[==!`, 0, 1, 1}},
   151  			"expected opening long bracket",
   152  		},
   153  		{
   154  			`   [===[foo]==]`,
   155  			[]tok{{token.UNFINISHED, `[===[foo]==]`, 3, 1, 4}},
   156  			"illegal <eof> in long bracket of level 3",
   157  		},
   158  		//
   159  		// Short strings
   160  		//
   161  		{
   162  			"'\\u{123a}\\xff\\\n\\''",
   163  			[]tok{
   164  				{token.STRING, "'\\u{123a}\\xff\\\n\\''", 0, 1, 1},
   165  				{token.EOF, "", 18, 2, 4},
   166  			},
   167  			"",
   168  		},
   169  		{
   170  			"'abc\\z\n  \r\\65x'",
   171  			[]tok{
   172  				{token.STRING, "'abc\\z\n  \r\\65x'", 0, 1, 1},
   173  				{token.EOF, "", 15, 3, 6},
   174  			},
   175  			"",
   176  		},
   177  		// Short string errors
   178  		{
   179  			`"\x2w"`,
   180  			[]tok{{token.INVALID, `"\x2`, 0, 1, 1}},
   181  			`\x must be followed by 2 hex digits`,
   182  		},
   183  		{
   184  			`'\uz`,
   185  			[]tok{{token.INVALID, `'\uz`, 0, 1, 1}},
   186  			`\u must be followed by '{'`,
   187  		},
   188  		{
   189  			`'  \u{l}''`,
   190  			[]tok{{token.INVALID, `'  \u{`, 0, 1, 1}},
   191  			"at least 1 hex digit required",
   192  		},
   193  		{
   194  			`"\u{1ef.}"`,
   195  			[]tok{{token.INVALID, `"\u{1ef.`, 0, 1, 1}},
   196  			`missing '}'`,
   197  		},
   198  		{
   199  			"'hello\nthere'",
   200  			[]tok{{token.INVALID, "'hello\n", 0, 1, 1}},
   201  			"illegal new line in string literal",
   202  		},
   203  		{
   204  			`"foo\"`,
   205  			[]tok{{token.INVALID, `"foo\"`, 0, 1, 1}},
   206  			"illegal <eof> in string literal",
   207  		},
   208  		{
   209  			`"\o"`,
   210  			[]tok{{token.INVALID, `"\o`, 0, 1, 1}},
   211  			"illegal escaped character",
   212  		},
   213  	}
   214  	for i, test := range tests {
   215  		name := fmt.Sprintf("Test %d", i+1)
   216  		t.Run(name, func(t *testing.T) {
   217  			scanner := New("test", []byte(test.text))
   218  			for j, ts := range test.toks {
   219  				next := scanner.Scan()
   220  				if next == nil {
   221  					t.Fatalf("Token %d: scan returns nil", j+1)
   222  				}
   223  				if !reflect.DeepEqual(next, ts.Token()) {
   224  					t.Fatalf("Token %d: expected <%s>, got <%s>", j+1, tokenString(ts.Token()), tokenString(next))
   225  				}
   226  			}
   227  			if scanner.ErrorMsg() != test.err {
   228  				t.Fatalf("Wrong error message: expected %q, got %q", test.err, scanner.ErrorMsg())
   229  			}
   230  		})
   231  	}
   232  }