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 }