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  }