github.com/tiagovtristao/plz@v13.4.0+incompatible/src/parse/asp/lexer_test.go (about) 1 package asp 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 ) 9 10 func assertToken(t *testing.T, tok Token, tokenType rune, value string, line, column, offset int) { 11 assert.EqualValues(t, tokenType, tok.Type, "incorrect type") 12 assert.Equal(t, value, tok.Value, "incorrect value") 13 assert.Equal(t, line, tok.Pos.Line, "incorrect line") 14 assert.Equal(t, column, tok.Pos.Column, "incorrect column") 15 assert.Equal(t, offset, tok.Pos.Offset, "incorrect offset") 16 } 17 18 func TestLexBasic(t *testing.T) { 19 l := newLexer(strings.NewReader("hello world")) 20 assertToken(t, l.Next(), Ident, "hello", 1, 1, 1) 21 assertToken(t, l.Peek(), Ident, "world", 1, 7, 7) 22 assertToken(t, l.Next(), Ident, "world", 1, 7, 7) 23 assertToken(t, l.Next(), EOL, "", 1, 12, 12) 24 assertToken(t, l.Next(), EOF, "", 2, 1, 13) 25 } 26 27 func TestLexMultiline(t *testing.T) { 28 l := newLexer(strings.NewReader("hello\nworld\n")) 29 assertToken(t, l.Next(), Ident, "hello", 1, 1, 1) 30 assertToken(t, l.Next(), EOL, "", 1, 6, 6) 31 assertToken(t, l.Next(), Ident, "world", 2, 1, 7) 32 assertToken(t, l.Next(), EOL, "", 2, 6, 12) 33 assertToken(t, l.Next(), EOF, "", 3, 1, 13) 34 } 35 36 const testFunction = ` 37 def func(x): 38 pass 39 ` 40 41 func TestLexFunction(t *testing.T) { 42 l := newLexer(strings.NewReader(testFunction)) 43 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 44 assertToken(t, l.Next(), Ident, "func", 2, 5, 6) 45 assertToken(t, l.Next(), '(', "(", 2, 9, 10) 46 assertToken(t, l.Next(), Ident, "x", 2, 10, 11) 47 assertToken(t, l.Next(), ')', ")", 2, 11, 12) 48 assertToken(t, l.Next(), ':', ":", 2, 12, 13) 49 assertToken(t, l.Next(), EOL, "", 2, 13, 14) 50 assertToken(t, l.Next(), Ident, "pass", 3, 5, 19) 51 assertToken(t, l.Next(), EOL, "", 4, 1, 23) 52 assertToken(t, l.Next(), Unindent, "", 4, 1, 24) 53 assertToken(t, l.Next(), EOF, "", 4, 1, 24) 54 } 55 56 func TestLexUnicode(t *testing.T) { 57 l := newLexer(strings.NewReader("懂了吗 你愁脸 有没有")) 58 assertToken(t, l.Next(), Ident, "懂了吗", 1, 1, 1) 59 assertToken(t, l.Next(), Ident, "你愁脸", 1, 11, 11) 60 assertToken(t, l.Next(), Ident, "有没有", 1, 21, 21) 61 assertToken(t, l.Next(), EOL, "", 1, 30, 30) 62 assertToken(t, l.Next(), EOF, "", 2, 1, 31) 63 } 64 65 func TestLexString(t *testing.T) { 66 l := newLexer(strings.NewReader(`x = "hello world"`)) 67 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 68 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 69 assertToken(t, l.Next(), String, "\"hello world\"", 1, 5, 5) 70 assertToken(t, l.Next(), EOL, "", 1, 18, 18) 71 assertToken(t, l.Next(), EOF, "", 2, 1, 19) 72 } 73 74 func TestLexStringEscape(t *testing.T) { 75 l := newLexer(strings.NewReader(`x = '\n\\'`)) 76 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 77 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 78 assertToken(t, l.Next(), String, "\"\n\\\"", 1, 5, 5) 79 assertToken(t, l.Next(), EOL, "", 1, 11, 11) 80 assertToken(t, l.Next(), EOF, "", 2, 1, 12) 81 } 82 83 func TestLexStringEscape2(t *testing.T) { 84 l := newLexer(strings.NewReader(`'echo -n "import \( \";'`)) 85 assertToken(t, l.Next(), String, `"echo -n "import \( ";"`, 1, 1, 1) 86 assertToken(t, l.Next(), EOL, "", 1, 25, 25) 87 assertToken(t, l.Next(), EOF, "", 2, 1, 26) 88 } 89 90 func TestLexRawString(t *testing.T) { 91 l := newLexer(strings.NewReader(`x = r'\n\\'`)) 92 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 93 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 94 assertToken(t, l.Next(), String, `"\n\\"`, 1, 5, 5) 95 assertToken(t, l.Next(), EOL, "", 1, 12, 12) 96 assertToken(t, l.Next(), EOF, "", 2, 1, 13) 97 } 98 99 func TestLexFString(t *testing.T) { 100 l := newLexer(strings.NewReader(`x = f'{x}'`)) 101 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 102 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 103 assertToken(t, l.Next(), String, `f"{x}"`, 1, 5, 5) 104 assertToken(t, l.Next(), EOL, "", 1, 11, 11) 105 assertToken(t, l.Next(), EOF, "", 2, 1, 12) 106 } 107 108 const testMultilineString = `x = """ 109 hello\n 110 world 111 """` 112 113 // expected output after lexing; note quotes are broken to a single one and \n does not become a newline. 114 const expectedMultilineString = `" 115 hello 116 117 world 118 "` 119 120 func TestLexMultilineString(t *testing.T) { 121 l := newLexer(strings.NewReader(testMultilineString)) 122 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 123 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 124 assertToken(t, l.Next(), String, expectedMultilineString, 1, 5, 5) 125 assertToken(t, l.Next(), EOL, "", 4, 4, 26) 126 assertToken(t, l.Next(), EOF, "", 5, 1, 27) 127 } 128 129 func TestLexAttributeAccess(t *testing.T) { 130 l := newLexer(strings.NewReader(`x.call(y)`)) 131 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 132 assertToken(t, l.Next(), '.', ".", 1, 2, 2) 133 assertToken(t, l.Next(), Ident, "call", 1, 3, 3) 134 assertToken(t, l.Next(), '(', "(", 1, 7, 7) 135 assertToken(t, l.Next(), Ident, "y", 1, 8, 8) 136 assertToken(t, l.Next(), ')', ")", 1, 9, 9) 137 assertToken(t, l.Next(), EOL, "", 1, 10, 10) 138 assertToken(t, l.Next(), EOF, "", 2, 1, 11) 139 } 140 141 func TestLexFunctionArgs(t *testing.T) { 142 l := newLexer(strings.NewReader(`def test(name='name', timeout=10, args=CONFIG.ARGS):`)) 143 assertToken(t, l.Next(), Ident, "def", 1, 1, 1) 144 assertToken(t, l.Next(), Ident, "test", 1, 5, 5) 145 assertToken(t, l.Next(), '(', "(", 1, 9, 9) 146 assertToken(t, l.Next(), Ident, "name", 1, 10, 10) 147 assertToken(t, l.Next(), '=', "=", 1, 14, 14) 148 assertToken(t, l.Next(), String, "\"name\"", 1, 15, 15) 149 assertToken(t, l.Next(), ',', ",", 1, 21, 21) 150 assertToken(t, l.Next(), Ident, "timeout", 1, 23, 23) 151 assertToken(t, l.Next(), '=', "=", 1, 30, 30) 152 assertToken(t, l.Next(), Int, "10", 1, 31, 31) 153 assertToken(t, l.Next(), ',', ",", 1, 33, 33) 154 assertToken(t, l.Next(), Ident, "args", 1, 35, 35) 155 assertToken(t, l.Next(), '=', "=", 1, 39, 39) 156 assertToken(t, l.Next(), Ident, "CONFIG", 1, 40, 40) 157 assertToken(t, l.Next(), '.', ".", 1, 46, 46) 158 assertToken(t, l.Next(), Ident, "ARGS", 1, 47, 47) 159 assertToken(t, l.Next(), ')', ")", 1, 51, 51) 160 assertToken(t, l.Next(), ':', ":", 1, 52, 52) 161 } 162 163 const inputFunction = ` 164 python_library( 165 name = 'lib', 166 srcs = [ 167 'lib1.py', 168 'lib2.py', 169 ], 170 ) 171 ` 172 173 func TestMoreComplexFunction(t *testing.T) { 174 l := newLexer(strings.NewReader(inputFunction)) 175 assertToken(t, l.Next(), Ident, "python_library", 2, 1, 2) 176 assertToken(t, l.Next(), '(', "(", 2, 15, 16) 177 assertToken(t, l.Next(), Ident, "name", 3, 5, 22) 178 assertToken(t, l.Next(), '=', "=", 3, 10, 27) 179 assertToken(t, l.Next(), String, "\"lib\"", 3, 12, 29) 180 assertToken(t, l.Next(), ',', ",", 3, 17, 34) 181 assertToken(t, l.Next(), Ident, "srcs", 4, 5, 40) 182 assertToken(t, l.Next(), '=', "=", 4, 10, 45) 183 assertToken(t, l.Next(), '[', "[", 4, 12, 47) 184 assertToken(t, l.Next(), String, "\"lib1.py\"", 5, 9, 57) 185 assertToken(t, l.Next(), ',', ",", 5, 18, 66) 186 assertToken(t, l.Next(), String, "\"lib2.py\"", 6, 9, 76) 187 assertToken(t, l.Next(), ',', ",", 6, 18, 85) 188 assertToken(t, l.Next(), ']', "]", 7, 5, 91) 189 assertToken(t, l.Next(), ',', ",", 7, 6, 92) 190 assertToken(t, l.Next(), ')', ")", 8, 1, 94) 191 } 192 193 const multiUnindent = ` 194 for y in x: 195 for z in y: 196 for a in z: 197 pass 198 ` 199 200 func TestMultiUnindent(t *testing.T) { 201 l := newLexer(strings.NewReader(multiUnindent)) 202 assertToken(t, l.Next(), Ident, "for", 2, 1, 2) 203 assertToken(t, l.Next(), Ident, "y", 2, 5, 6) 204 assertToken(t, l.Next(), Ident, "in", 2, 7, 8) 205 assertToken(t, l.Next(), Ident, "x", 2, 10, 11) 206 assertToken(t, l.Next(), ':', ":", 2, 11, 12) 207 assertToken(t, l.Next(), EOL, "", 2, 12, 13) 208 assertToken(t, l.Next(), Ident, "for", 3, 5, 18) 209 assertToken(t, l.Next(), Ident, "z", 3, 9, 22) 210 assertToken(t, l.Next(), Ident, "in", 3, 11, 24) 211 assertToken(t, l.Next(), Ident, "y", 3, 14, 27) 212 assertToken(t, l.Next(), ':', ":", 3, 15, 28) 213 assertToken(t, l.Next(), EOL, "", 3, 16, 29) 214 assertToken(t, l.Next(), Ident, "for", 4, 9, 38) 215 assertToken(t, l.Next(), Ident, "a", 4, 13, 42) 216 assertToken(t, l.Next(), Ident, "in", 4, 15, 44) 217 assertToken(t, l.Next(), Ident, "z", 4, 18, 47) 218 assertToken(t, l.Next(), ':', ":", 4, 19, 48) 219 assertToken(t, l.Next(), EOL, "", 4, 20, 49) 220 assertToken(t, l.Next(), Ident, "pass", 5, 13, 62) 221 assertToken(t, l.Next(), EOL, "", 6, 1, 66) 222 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 223 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 224 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 225 } 226 227 const multiLineFunctionArgs = ` 228 def test(name='name', timeout=10, 229 args=CONFIG.ARGS): 230 pass 231 ` 232 233 func TestMultiLineFunctionArgs(t *testing.T) { 234 l := newLexer(strings.NewReader(multiLineFunctionArgs)) 235 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 236 assertToken(t, l.Next(), Ident, "test", 2, 5, 6) 237 assertToken(t, l.Next(), '(', "(", 2, 9, 10) 238 assertToken(t, l.Next(), Ident, "name", 2, 10, 11) 239 assertToken(t, l.Next(), '=', "=", 2, 14, 15) 240 assertToken(t, l.Next(), String, "\"name\"", 2, 15, 16) 241 assertToken(t, l.Next(), ',', ",", 2, 21, 22) 242 assertToken(t, l.Next(), Ident, "timeout", 2, 23, 24) 243 assertToken(t, l.Next(), '=', "=", 2, 30, 31) 244 assertToken(t, l.Next(), Int, "10", 2, 31, 32) 245 assertToken(t, l.Next(), ',', ",", 2, 33, 34) 246 assertToken(t, l.Next(), Ident, "args", 3, 10, 45) 247 assertToken(t, l.Next(), '=', "=", 3, 14, 49) 248 assertToken(t, l.Next(), Ident, "CONFIG", 3, 15, 50) 249 assertToken(t, l.Next(), '.', ".", 3, 21, 56) 250 assertToken(t, l.Next(), Ident, "ARGS", 3, 22, 57) 251 assertToken(t, l.Next(), ')', ")", 3, 26, 61) 252 assertToken(t, l.Next(), ':', ":", 3, 27, 62) 253 assertToken(t, l.Next(), EOL, "", 3, 28, 63) 254 assertToken(t, l.Next(), Ident, "pass", 4, 5, 68) 255 assertToken(t, l.Next(), EOL, "", 5, 1, 72) 256 assertToken(t, l.Next(), Unindent, "", 5, 1, 73) 257 } 258 259 func TestComparisonOperator(t *testing.T) { 260 l := newLexer(strings.NewReader("x = y == z")) 261 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 262 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 263 assertToken(t, l.Next(), Ident, "y", 1, 5, 5) 264 assertToken(t, l.Next(), LexOperator, "==", 1, 7, 7) 265 } 266 267 const blankLinesInFunction = ` 268 def x(): 269 """test""" 270 271 return 42 272 ` 273 274 func TestBlankLinesInFunction(t *testing.T) { 275 l := newLexer(strings.NewReader(blankLinesInFunction)) 276 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 277 assertToken(t, l.Next(), Ident, "x", 2, 5, 6) 278 assertToken(t, l.Next(), '(', "(", 2, 6, 7) 279 assertToken(t, l.Next(), ')', ")", 2, 7, 8) 280 assertToken(t, l.Next(), ':', ":", 2, 8, 9) 281 assertToken(t, l.Next(), EOL, "", 2, 9, 10) 282 assertToken(t, l.Next(), String, "\"test\"", 3, 5, 15) 283 assertToken(t, l.Next(), EOL, "", 4, 1, 26) 284 assertToken(t, l.Next(), Ident, "return", 5, 5, 31) 285 assertToken(t, l.Next(), Int, "42", 5, 12, 38) 286 assertToken(t, l.Next(), EOL, "", 6, 1, 40) 287 assertToken(t, l.Next(), Unindent, "", 6, 1, 41) 288 } 289 290 const commentsAndEOLs = ` 291 pass 292 293 # something 294 295 ` 296 297 func TestCommentsAndEOLs(t *testing.T) { 298 l := newLexer(strings.NewReader(commentsAndEOLs)) 299 assertToken(t, l.Next(), Ident, "pass", 2, 1, 2) 300 assertToken(t, l.Next(), EOL, "", 3, 1, 7) 301 assertToken(t, l.Next(), EOF, "", 6, 1, 21) 302 } 303 304 // This is a much-simplified version of the true motivating case. 305 const unevenIndent = ` 306 def x(): 307 if True: 308 pass 309 return 310 ` 311 312 func TestUnevenUnindent(t *testing.T) { 313 l := newLexer(strings.NewReader(unevenIndent)) 314 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 315 assertToken(t, l.Next(), Ident, "x", 2, 5, 6) 316 assertToken(t, l.Next(), '(', "(", 2, 6, 7) 317 assertToken(t, l.Next(), ')', ")", 2, 7, 8) 318 assertToken(t, l.Next(), ':', ":", 2, 8, 9) 319 assertToken(t, l.Next(), EOL, "", 2, 9, 10) 320 assertToken(t, l.Next(), Ident, "if", 3, 5, 15) 321 assertToken(t, l.Next(), Ident, "True", 3, 8, 18) 322 assertToken(t, l.Next(), ':', ":", 3, 12, 22) 323 assertToken(t, l.Next(), EOL, "", 3, 13, 23) 324 assertToken(t, l.Next(), Ident, "pass", 4, 13, 36) 325 assertToken(t, l.Next(), EOL, "", 5, 5, 40) 326 assertToken(t, l.Next(), Unindent, "", 5, 5, 45) 327 assertToken(t, l.Next(), Ident, "return", 5, 5, 45) 328 assertToken(t, l.Next(), EOL, "", 6, 1, 51) 329 assertToken(t, l.Next(), Unindent, "", 6, 1, 52) 330 assertToken(t, l.Next(), EOF, "", 6, 1, 52) 331 } 332 333 const implicitStringConcatenation = ` 334 str('testing that we can carry these ' 335 'over multiple lines') 336 ` 337 338 func TestImplicitStringConcatenation(t *testing.T) { 339 l := newLexer(strings.NewReader(implicitStringConcatenation)) 340 assertToken(t, l.Next(), Ident, "str", 2, 1, 2) 341 assertToken(t, l.Next(), '(', "(", 2, 4, 5) 342 assertToken(t, l.Next(), String, `"testing that we can carry these over multiple lines"`, 2, 5, 6) 343 } 344 345 func TestImplicitStringConcatenationOnlyHappensInsideBraces(t *testing.T) { 346 l := newLexer(strings.NewReader("'hello' 'world'")) 347 assertToken(t, l.Next(), String, `"hello"`, 1, 1, 1) 348 assertToken(t, l.Next(), String, `"world"`, 1, 9, 9) 349 }