github.phpd.cn/thought-machine/please@v12.2.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 const testMultilineString = `x = """ 100 hello\n 101 world 102 """` 103 104 // expected output after lexing; note quotes are broken to a single one and \n does not become a newline. 105 const expectedMultilineString = `" 106 hello 107 108 world 109 "` 110 111 func TestLexMultilineString(t *testing.T) { 112 l := newLexer(strings.NewReader(testMultilineString)) 113 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 114 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 115 assertToken(t, l.Next(), String, expectedMultilineString, 1, 5, 5) 116 assertToken(t, l.Next(), EOL, "", 4, 4, 26) 117 assertToken(t, l.Next(), EOF, "", 5, 1, 27) 118 } 119 120 func TestLexAttributeAccess(t *testing.T) { 121 l := newLexer(strings.NewReader(`x.call(y)`)) 122 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 123 assertToken(t, l.Next(), '.', ".", 1, 2, 2) 124 assertToken(t, l.Next(), Ident, "call", 1, 3, 3) 125 assertToken(t, l.Next(), '(', "(", 1, 7, 7) 126 assertToken(t, l.Next(), Ident, "y", 1, 8, 8) 127 assertToken(t, l.Next(), ')', ")", 1, 9, 9) 128 assertToken(t, l.Next(), EOL, "", 1, 10, 10) 129 assertToken(t, l.Next(), EOF, "", 2, 1, 11) 130 } 131 132 func TestLexFunctionArgs(t *testing.T) { 133 l := newLexer(strings.NewReader(`def test(name='name', timeout=10, args=CONFIG.ARGS):`)) 134 assertToken(t, l.Next(), Ident, "def", 1, 1, 1) 135 assertToken(t, l.Next(), Ident, "test", 1, 5, 5) 136 assertToken(t, l.Next(), '(', "(", 1, 9, 9) 137 assertToken(t, l.Next(), Ident, "name", 1, 10, 10) 138 assertToken(t, l.Next(), '=', "=", 1, 14, 14) 139 assertToken(t, l.Next(), String, "\"name\"", 1, 15, 15) 140 assertToken(t, l.Next(), ',', ",", 1, 21, 21) 141 assertToken(t, l.Next(), Ident, "timeout", 1, 23, 23) 142 assertToken(t, l.Next(), '=', "=", 1, 30, 30) 143 assertToken(t, l.Next(), Int, "10", 1, 31, 31) 144 assertToken(t, l.Next(), ',', ",", 1, 33, 33) 145 assertToken(t, l.Next(), Ident, "args", 1, 35, 35) 146 assertToken(t, l.Next(), '=', "=", 1, 39, 39) 147 assertToken(t, l.Next(), Ident, "CONFIG", 1, 40, 40) 148 assertToken(t, l.Next(), '.', ".", 1, 46, 46) 149 assertToken(t, l.Next(), Ident, "ARGS", 1, 47, 47) 150 assertToken(t, l.Next(), ')', ")", 1, 51, 51) 151 assertToken(t, l.Next(), ':', ":", 1, 52, 52) 152 } 153 154 const inputFunction = ` 155 python_library( 156 name = 'lib', 157 srcs = [ 158 'lib1.py', 159 'lib2.py', 160 ], 161 ) 162 ` 163 164 func TestMoreComplexFunction(t *testing.T) { 165 l := newLexer(strings.NewReader(inputFunction)) 166 assertToken(t, l.Next(), Ident, "python_library", 2, 1, 2) 167 assertToken(t, l.Next(), '(', "(", 2, 15, 16) 168 assertToken(t, l.Next(), Ident, "name", 3, 5, 22) 169 assertToken(t, l.Next(), '=', "=", 3, 10, 27) 170 assertToken(t, l.Next(), String, "\"lib\"", 3, 12, 29) 171 assertToken(t, l.Next(), ',', ",", 3, 17, 34) 172 assertToken(t, l.Next(), Ident, "srcs", 4, 5, 40) 173 assertToken(t, l.Next(), '=', "=", 4, 10, 45) 174 assertToken(t, l.Next(), '[', "[", 4, 12, 47) 175 assertToken(t, l.Next(), String, "\"lib1.py\"", 5, 9, 57) 176 assertToken(t, l.Next(), ',', ",", 5, 18, 66) 177 assertToken(t, l.Next(), String, "\"lib2.py\"", 6, 9, 76) 178 assertToken(t, l.Next(), ',', ",", 6, 18, 85) 179 assertToken(t, l.Next(), ']', "]", 7, 5, 91) 180 assertToken(t, l.Next(), ',', ",", 7, 6, 92) 181 assertToken(t, l.Next(), ')', ")", 8, 1, 94) 182 } 183 184 const multiUnindent = ` 185 for y in x: 186 for z in y: 187 for a in z: 188 pass 189 ` 190 191 func TestMultiUnindent(t *testing.T) { 192 l := newLexer(strings.NewReader(multiUnindent)) 193 assertToken(t, l.Next(), Ident, "for", 2, 1, 2) 194 assertToken(t, l.Next(), Ident, "y", 2, 5, 6) 195 assertToken(t, l.Next(), Ident, "in", 2, 7, 8) 196 assertToken(t, l.Next(), Ident, "x", 2, 10, 11) 197 assertToken(t, l.Next(), ':', ":", 2, 11, 12) 198 assertToken(t, l.Next(), EOL, "", 2, 12, 13) 199 assertToken(t, l.Next(), Ident, "for", 3, 5, 18) 200 assertToken(t, l.Next(), Ident, "z", 3, 9, 22) 201 assertToken(t, l.Next(), Ident, "in", 3, 11, 24) 202 assertToken(t, l.Next(), Ident, "y", 3, 14, 27) 203 assertToken(t, l.Next(), ':', ":", 3, 15, 28) 204 assertToken(t, l.Next(), EOL, "", 3, 16, 29) 205 assertToken(t, l.Next(), Ident, "for", 4, 9, 38) 206 assertToken(t, l.Next(), Ident, "a", 4, 13, 42) 207 assertToken(t, l.Next(), Ident, "in", 4, 15, 44) 208 assertToken(t, l.Next(), Ident, "z", 4, 18, 47) 209 assertToken(t, l.Next(), ':', ":", 4, 19, 48) 210 assertToken(t, l.Next(), EOL, "", 4, 20, 49) 211 assertToken(t, l.Next(), Ident, "pass", 5, 13, 62) 212 assertToken(t, l.Next(), EOL, "", 6, 1, 66) 213 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 214 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 215 assertToken(t, l.Next(), Unindent, "", 6, 1, 67) 216 } 217 218 const multiLineFunctionArgs = ` 219 def test(name='name', timeout=10, 220 args=CONFIG.ARGS): 221 pass 222 ` 223 224 func TestMultiLineFunctionArgs(t *testing.T) { 225 l := newLexer(strings.NewReader(multiLineFunctionArgs)) 226 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 227 assertToken(t, l.Next(), Ident, "test", 2, 5, 6) 228 assertToken(t, l.Next(), '(', "(", 2, 9, 10) 229 assertToken(t, l.Next(), Ident, "name", 2, 10, 11) 230 assertToken(t, l.Next(), '=', "=", 2, 14, 15) 231 assertToken(t, l.Next(), String, "\"name\"", 2, 15, 16) 232 assertToken(t, l.Next(), ',', ",", 2, 21, 22) 233 assertToken(t, l.Next(), Ident, "timeout", 2, 23, 24) 234 assertToken(t, l.Next(), '=', "=", 2, 30, 31) 235 assertToken(t, l.Next(), Int, "10", 2, 31, 32) 236 assertToken(t, l.Next(), ',', ",", 2, 33, 34) 237 assertToken(t, l.Next(), Ident, "args", 3, 10, 45) 238 assertToken(t, l.Next(), '=', "=", 3, 14, 49) 239 assertToken(t, l.Next(), Ident, "CONFIG", 3, 15, 50) 240 assertToken(t, l.Next(), '.', ".", 3, 21, 56) 241 assertToken(t, l.Next(), Ident, "ARGS", 3, 22, 57) 242 assertToken(t, l.Next(), ')', ")", 3, 26, 61) 243 assertToken(t, l.Next(), ':', ":", 3, 27, 62) 244 assertToken(t, l.Next(), EOL, "", 3, 28, 63) 245 assertToken(t, l.Next(), Ident, "pass", 4, 5, 68) 246 assertToken(t, l.Next(), EOL, "", 5, 1, 72) 247 assertToken(t, l.Next(), Unindent, "", 5, 1, 73) 248 } 249 250 func TestComparisonOperator(t *testing.T) { 251 l := newLexer(strings.NewReader("x = y == z")) 252 assertToken(t, l.Next(), Ident, "x", 1, 1, 1) 253 assertToken(t, l.Next(), '=', "=", 1, 3, 3) 254 assertToken(t, l.Next(), Ident, "y", 1, 5, 5) 255 assertToken(t, l.Next(), LexOperator, "==", 1, 7, 7) 256 } 257 258 const blankLinesInFunction = ` 259 def x(): 260 """test""" 261 262 return 42 263 ` 264 265 func TestBlankLinesInFunction(t *testing.T) { 266 l := newLexer(strings.NewReader(blankLinesInFunction)) 267 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 268 assertToken(t, l.Next(), Ident, "x", 2, 5, 6) 269 assertToken(t, l.Next(), '(', "(", 2, 6, 7) 270 assertToken(t, l.Next(), ')', ")", 2, 7, 8) 271 assertToken(t, l.Next(), ':', ":", 2, 8, 9) 272 assertToken(t, l.Next(), EOL, "", 2, 9, 10) 273 assertToken(t, l.Next(), String, "\"test\"", 3, 5, 15) 274 assertToken(t, l.Next(), EOL, "", 4, 1, 26) 275 assertToken(t, l.Next(), Ident, "return", 5, 5, 31) 276 assertToken(t, l.Next(), Int, "42", 5, 12, 38) 277 assertToken(t, l.Next(), EOL, "", 6, 1, 40) 278 assertToken(t, l.Next(), Unindent, "", 6, 1, 41) 279 } 280 281 const commentsAndEOLs = ` 282 pass 283 284 # something 285 286 ` 287 288 func TestCommentsAndEOLs(t *testing.T) { 289 l := newLexer(strings.NewReader(commentsAndEOLs)) 290 assertToken(t, l.Next(), Ident, "pass", 2, 1, 2) 291 assertToken(t, l.Next(), EOL, "", 3, 1, 7) 292 assertToken(t, l.Next(), EOF, "", 6, 1, 21) 293 } 294 295 // This is a much-simplified version of the true motivating case. 296 const unevenIndent = ` 297 def x(): 298 if True: 299 pass 300 return 301 ` 302 303 func TestUnevenUnindent(t *testing.T) { 304 l := newLexer(strings.NewReader(unevenIndent)) 305 assertToken(t, l.Next(), Ident, "def", 2, 1, 2) 306 assertToken(t, l.Next(), Ident, "x", 2, 5, 6) 307 assertToken(t, l.Next(), '(', "(", 2, 6, 7) 308 assertToken(t, l.Next(), ')', ")", 2, 7, 8) 309 assertToken(t, l.Next(), ':', ":", 2, 8, 9) 310 assertToken(t, l.Next(), EOL, "", 2, 9, 10) 311 assertToken(t, l.Next(), Ident, "if", 3, 5, 15) 312 assertToken(t, l.Next(), Ident, "True", 3, 8, 18) 313 assertToken(t, l.Next(), ':', ":", 3, 12, 22) 314 assertToken(t, l.Next(), EOL, "", 3, 13, 23) 315 assertToken(t, l.Next(), Ident, "pass", 4, 13, 36) 316 assertToken(t, l.Next(), EOL, "", 5, 5, 40) 317 assertToken(t, l.Next(), Unindent, "", 5, 5, 45) 318 assertToken(t, l.Next(), Ident, "return", 5, 5, 45) 319 assertToken(t, l.Next(), EOL, "", 6, 1, 51) 320 assertToken(t, l.Next(), Unindent, "", 6, 1, 52) 321 assertToken(t, l.Next(), EOF, "", 6, 1, 52) 322 } 323 324 const implicitStringConcatenation = ` 325 str('testing that we can carry these ' 326 'over multiple lines') 327 ` 328 329 func TestImplicitStringConcatenation(t *testing.T) { 330 l := newLexer(strings.NewReader(implicitStringConcatenation)) 331 assertToken(t, l.Next(), Ident, "str", 2, 1, 2) 332 assertToken(t, l.Next(), '(', "(", 2, 4, 5) 333 assertToken(t, l.Next(), String, `"testing that we can carry these over multiple lines"`, 2, 5, 6) 334 } 335 336 func TestImplicitStringConcatenationOnlyHappensInsideBraces(t *testing.T) { 337 l := newLexer(strings.NewReader("'hello' 'world'")) 338 assertToken(t, l.Next(), String, `"hello"`, 1, 1, 1) 339 assertToken(t, l.Next(), String, `"world"`, 1, 9, 9) 340 }