github.com/cheshirekow/buildtools@v0.0.0-20200224190056-5d637702fe81/build/parse.y (about)

     1  // BUILD file parser.
     2  
     3  // This is a yacc grammar. Its lexer is in lex.go.
     4  //
     5  // For a good introduction to writing yacc grammars, see
     6  // Kernighan and Pike's book The Unix Programming Environment.
     7  //
     8  // The definitive yacc manual is
     9  // Stephen C. Johnson and Ravi Sethi, "Yacc: A Parser Generator",
    10  // online at http://plan9.bell-labs.com/sys/doc/yacc.pdf.
    11  
    12  %{
    13  package build
    14  %}
    15  
    16  // The generated parser puts these fields in a struct named yySymType.
    17  // (The name %union is historical, but it is inaccurate for Go.)
    18  %union {
    19  	// input tokens
    20  	tok       string     // raw input syntax
    21  	str       string     // decoding of quoted string
    22  	pos       Position   // position of token
    23  	triple    bool       // was string triple quoted?
    24  
    25  	// partial syntax trees
    26  	expr      Expr
    27  	exprs     []Expr
    28  	string    *StringExpr
    29  	ifstmt    *IfStmt
    30  	loadarg   *struct{from Ident; to Ident}
    31  	loadargs  []*struct{from Ident; to Ident}
    32  
    33  	// supporting information
    34  	comma     Position   // position of trailing comma in list, if present
    35  	lastStmt  Expr  // most recent rule, to attach line comments to
    36  }
    37  
    38  // These declarations set the type for a $ reference ($$, $1, $2, ...)
    39  // based on the kind of symbol it refers to. Other fields can be referred
    40  // to explicitly, as in $<tok>1.
    41  //
    42  // %token is for input tokens generated by the lexer.
    43  // %type is for higher-level grammar rules defined here.
    44  //
    45  // It is possible to put multiple tokens per line, but it is easier to
    46  // keep ordered using a sparser one-per-line list.
    47  
    48  %token	<pos>	'%'
    49  %token	<pos>	'('
    50  %token	<pos>	')'
    51  %token	<pos>	'*'
    52  %token	<pos>	'+'
    53  %token	<pos>	','
    54  %token	<pos>	'-'
    55  %token	<pos>	'.'
    56  %token	<pos>	'/'
    57  %token	<pos>	':'
    58  %token	<pos>	'<'
    59  %token	<pos>	'='
    60  %token	<pos>	'>'
    61  %token	<pos>	'['
    62  %token	<pos>	']'
    63  %token	<pos>	'{'
    64  %token	<pos>	'}'
    65  %token	<pos>	'|'
    66  %token	<pos>	'&'
    67  %token	<pos>	'^'
    68  %token	<pos>	'~'
    69  
    70  // By convention, yacc token names are all caps.
    71  // However, we do not want to export them from the Go package
    72  // we are creating, so prefix them all with underscores.
    73  
    74  %token	<pos>	_AUGM    // augmented assignment
    75  %token	<pos>	_AND     // keyword and
    76  %token	<pos>	_COMMENT // top-level # comment
    77  %token	<pos>	_EOF     // end of file
    78  %token	<pos>	_EQ      // operator ==
    79  %token	<pos>	_FOR     // keyword for
    80  %token	<pos>	_GE      // operator >=
    81  %token	<pos>	_IDENT   // non-keyword identifier
    82  %token	<pos>	_NUMBER  // number
    83  %token	<pos>	_IF      // keyword if
    84  %token	<pos>	_ELSE    // keyword else
    85  %token	<pos>	_ELIF    // keyword elif
    86  %token	<pos>	_IN      // keyword in
    87  %token	<pos>	_IS      // keyword is
    88  %token	<pos>	_LAMBDA  // keyword lambda
    89  %token	<pos>	_LOAD    // keyword load
    90  %token	<pos>	_LE      // operator <=
    91  %token	<pos>	_NE      // operator !=
    92  %token	<pos>	_STAR_STAR // operator **
    93  %token	<pos>	_INT_DIV // operator //
    94  %token	<pos>	_BIT_LSH // bitwise operator <<
    95  %token	<pos>	_BIT_RSH // bitwise operator >>
    96  %token	<pos>	_NOT     // keyword not
    97  %token	<pos>	_OR      // keyword or
    98  %token	<pos>	_STRING  // quoted string
    99  %token	<pos>	_DEF     // keyword def
   100  %token	<pos>	_RETURN  // keyword return
   101  %token	<pos>	_PASS    // keyword pass
   102  %token	<pos>	_BREAK   // keyword break
   103  %token	<pos>	_CONTINUE // keyword continue
   104  %token	<pos>	_INDENT  // indentation
   105  %token	<pos>	_UNINDENT // unindentation
   106  
   107  %type	<pos>		comma_opt
   108  %type	<expr>		argument
   109  %type	<exprs>		arguments
   110  %type	<exprs>		arguments_opt
   111  %type	<expr>		parameter
   112  %type	<exprs>		parameters
   113  %type	<exprs>		parameters_opt
   114  %type	<expr>		test
   115  %type	<expr>		test_opt
   116  %type	<exprs>		tests_opt
   117  %type	<expr>		primary_expr
   118  %type	<expr>		expr
   119  %type	<expr>		expr_opt
   120  %type <exprs>		tests
   121  %type	<exprs>		exprs
   122  %type	<exprs>		exprs_opt
   123  %type	<expr>		loop_vars
   124  %type	<expr>		for_clause
   125  %type	<exprs>		for_clause_with_if_clauses_opt
   126  %type	<exprs>		for_clauses_with_if_clauses_opt
   127  %type	<expr>		ident
   128  %type	<expr>		number
   129  %type	<exprs>		stmts
   130  %type	<exprs>		stmt          // a simple_stmt or a for/if/def block
   131  %type	<expr>		block_stmt    // a single for/if/def statement
   132  %type	<ifstmt>	if_else_block // a complete if-elif-else block
   133  %type	<ifstmt>	if_chain      // an elif-elif-else chain
   134  %type <pos>		elif          // `elif` or `else if` token(s)
   135  %type	<exprs>		simple_stmt   // One or many small_stmts on one line, e.g. 'a = f(x); return str(a)'
   136  %type	<expr>		small_stmt    // A single statement, e.g. 'a = f(x)'
   137  %type <exprs>		small_stmts_continuation  // A sequence of `';' small_stmt`
   138  %type	<expr>		keyvalue
   139  %type	<exprs>		keyvalues
   140  %type	<exprs>		keyvalues_no_comma
   141  %type	<string>	string
   142  %type	<exprs>		suite
   143  %type	<exprs>		comments
   144  %type	<loadarg>	load_argument
   145  %type	<loadargs>	load_arguments
   146  
   147  // Operator precedence.
   148  // Operators listed lower in the table bind tighter.
   149  
   150  // We tag rules with this fake, low precedence to indicate
   151  // that when the rule is involved in a shift/reduce
   152  // conflict, we prefer that the parser shift (try for a longer parse).
   153  // Shifting is the default resolution anyway, but stating it explicitly
   154  // silences yacc's warning for that specific case.
   155  %left	ShiftInstead
   156  
   157  %left	'\n'
   158  %left	_ASSERT
   159  // '=' and augmented assignments have the lowest precedence
   160  // e.g. "x = a if c > 0 else 'bar'"
   161  // followed by
   162  // 'if' and 'else' which have lower precedence than all other operators.
   163  // e.g. "a, b if c > 0 else 'foo'" is either a tuple of (a,b) or 'foo'
   164  // and not a tuple of "(a, (b if ... ))"
   165  %left  '=' _AUGM
   166  %left  _IF _ELSE _ELIF
   167  %left  ','
   168  %left  ':'
   169  %left  _IS
   170  %left  _OR
   171  %left  _AND
   172  %left  '<' '>' _EQ _NE _LE _GE _NOT _IN
   173  %left  '|'
   174  %left  '^'
   175  %left  '&'
   176  %left  _BIT_LSH _BIT_RSH
   177  %left  '+' '-'
   178  %left  '*' '/' '%' _INT_DIV
   179  %left  '.' '[' '('
   180  %right _UNARY
   181  %left  _STRING
   182  
   183  %%
   184  
   185  // Grammar rules.
   186  //
   187  // A note on names: if foo is a rule, then foos is a sequence of foos
   188  // (with interleaved commas or other syntax as appropriate)
   189  // and foo_opt is an optional foo.
   190  
   191  file:
   192  	stmts _EOF
   193  	{
   194  		yylex.(*input).file = &File{Stmt: $1}
   195  		return 0
   196  	}
   197  
   198  suite:
   199  	'\n' comments _INDENT stmts _UNINDENT
   200  	{
   201  		statements := $4
   202  		if $2 != nil {
   203  			// $2 can only contain *CommentBlock objects, each of them contains a non-empty After slice
   204  			cb := $2[len($2)-1].(*CommentBlock)
   205  			// $4 can't be empty and can't start with a comment
   206  			stmt := $4[0]
   207  			start, _ := stmt.Span()
   208  			if start.Line - cb.After[len(cb.After)-1].Start.Line == 1 {
   209  				// The first statement of $4 starts on the next line after the last comment of $2.
   210  				// Attach the last comment to the first statement
   211  				stmt.Comment().Before = cb.After
   212  				$2 = $2[:len($2)-1]
   213  			}
   214  			statements = append($2, $4...)
   215  		}
   216  		$$ = statements
   217  		$<lastStmt>$ = $<lastStmt>4
   218  	}
   219  |	simple_stmt linebreaks_opt
   220  	{
   221  		$$ = $1
   222  	}
   223  
   224  linebreaks_opt:
   225  | linebreaks_opt '\n'
   226  
   227  comments:
   228  	{
   229  		$$ = nil
   230  		$<lastStmt>$ = nil
   231  	}
   232  |	comments _COMMENT '\n'
   233  	{
   234  		$$ = $1
   235  		$<lastStmt>$ = $<lastStmt>1
   236  		if $<lastStmt>$ == nil {
   237  			cb := &CommentBlock{Start: $2}
   238  			$$ = append($$, cb)
   239  			$<lastStmt>$ = cb
   240  		}
   241  		com := $<lastStmt>$.Comment()
   242  		com.After = append(com.After, Comment{Start: $2, Token: $<tok>2})
   243  	}
   244  |	comments '\n'
   245  	{
   246  		$$ = $1
   247  		$<lastStmt>$ = nil
   248  	}
   249  
   250  stmts:
   251  	{
   252  		$$ = nil
   253  		$<lastStmt>$ = nil
   254  	}
   255  |	stmts stmt
   256  	{
   257  		// If this statement follows a comment block,
   258  		// attach the comments to the statement.
   259  		if cb, ok := $<lastStmt>1.(*CommentBlock); ok {
   260  			$$ = append($1[:len($1)-1], $2...)
   261  			$2[0].Comment().Before = cb.After
   262  			$<lastStmt>$ = $<lastStmt>2
   263  			break
   264  		}
   265  
   266  		// Otherwise add to list.
   267  		$$ = append($1, $2...)
   268  		$<lastStmt>$ = $<lastStmt>2
   269  
   270  		// Consider this input:
   271  		//
   272  		//	foo()
   273  		//	# bar
   274  		//	baz()
   275  		//
   276  		// If we've just parsed baz(), the # bar is attached to
   277  		// foo() as an After comment. Make it a Before comment
   278  		// for baz() instead.
   279  		if x := $<lastStmt>1; x != nil {
   280  			com := x.Comment()
   281  			// stmt is never empty
   282  			$2[0].Comment().Before = com.After
   283  			com.After = nil
   284  		}
   285  	}
   286  |	stmts '\n'
   287  	{
   288  		// Blank line; sever last rule from future comments.
   289  		$$ = $1
   290  		$<lastStmt>$ = nil
   291  	}
   292  |	stmts _COMMENT '\n'
   293  	{
   294  		$$ = $1
   295  		$<lastStmt>$ = $<lastStmt>1
   296  		if $<lastStmt>$ == nil {
   297  			cb := &CommentBlock{Start: $2}
   298  			$$ = append($$, cb)
   299  			$<lastStmt>$ = cb
   300  		}
   301  		com := $<lastStmt>$.Comment()
   302  		com.After = append(com.After, Comment{Start: $2, Token: $<tok>2})
   303  	}
   304  
   305  stmt:
   306  	simple_stmt
   307  	{
   308  		$$ = $1
   309  		$<lastStmt>$ = $1[len($1)-1]
   310  	}
   311  |	block_stmt
   312  	{
   313  		$$ = []Expr{$1}
   314  		$<lastStmt>$ = $1
   315  		if cbs := extractTrailingComments($1); len(cbs) > 0 {
   316  			$$ = append($$, cbs...)
   317  			$<lastStmt>$ = cbs[len(cbs)-1]
   318  			if $<lastStmt>1 == nil {
   319  				$<lastStmt>$ = nil
   320  			}
   321  		}
   322  	}
   323  
   324  block_stmt:
   325  	_DEF _IDENT '(' parameters_opt ')' ':' suite
   326  	{
   327  		$$ = &DefStmt{
   328  			Function: Function{
   329  				StartPos: $1,
   330  				Params: $4,
   331  				Body: $7,
   332  			},
   333  			Name: $<tok>2,
   334  			ColonPos: $6,
   335  			ForceCompact: forceCompact($3, $4, $5),
   336  			ForceMultiLine: forceMultiLine($3, $4, $5),
   337  		}
   338  		$<lastStmt>$ = $<lastStmt>7
   339  	}
   340  |	_FOR loop_vars _IN expr ':' suite
   341  	{
   342  		$$ = &ForStmt{
   343  			For: $1,
   344  			Vars: $2,
   345  			X: $4,
   346  			Body: $6,
   347  		}
   348  		$<lastStmt>$ = $<lastStmt>6
   349  	}
   350  |	if_else_block
   351  	{
   352  		$$ = $1
   353  		$<lastStmt>$ = $<lastStmt>1
   354  	}
   355  
   356  // One or several if-elif-elif statements
   357  if_chain:
   358  	_IF expr ':' suite
   359  	{
   360  		$$ = &IfStmt{
   361  			If: $1,
   362  			Cond: $2,
   363  			True: $4,
   364  		}
   365  		$<lastStmt>$ = $<lastStmt>4
   366  	}
   367  |	if_chain elif expr ':' suite
   368  	{
   369  		$$ = $1
   370  		inner := $1
   371  		for len(inner.False) == 1 {
   372  			inner = inner.False[0].(*IfStmt)
   373  		}
   374  		inner.ElsePos = End{Pos: $2}
   375  		inner.False = []Expr{
   376  			&IfStmt{
   377  				If: $2,
   378  				Cond: $3,
   379  				True: $5,
   380  			},
   381  		}
   382  		$<lastStmt>$ = $<lastStmt>5
   383  	}
   384  
   385  // A complete if-elif-elif-else chain
   386  if_else_block:
   387  	if_chain
   388  |	if_chain _ELSE ':' suite
   389  	{
   390  		$$ = $1
   391  		inner := $1
   392  		for len(inner.False) == 1 {
   393  			inner = inner.False[0].(*IfStmt)
   394  		}
   395  		inner.ElsePos = End{Pos: $2}
   396  		inner.False = $4
   397  		$<lastStmt>$ = $<lastStmt>4
   398  	}
   399  
   400  elif:
   401  	_ELSE _IF
   402  |	_ELIF
   403  
   404  simple_stmt:
   405  	small_stmt small_stmts_continuation semi_opt '\n'
   406  	{
   407  		$$ = append([]Expr{$1}, $2...)
   408  		$<lastStmt>$ = $$[len($$)-1]
   409  	}
   410  
   411  small_stmts_continuation:
   412  	{
   413  		$$ = []Expr{}
   414  	}
   415  |	small_stmts_continuation ';' small_stmt
   416  	{
   417  		$$ = append($1, $3)
   418  	}
   419  
   420  small_stmt:
   421  	expr %prec ShiftInstead
   422  |	_RETURN expr
   423  	{
   424  		$$ = &ReturnStmt{
   425  			Return: $1,
   426  			Result: $2,
   427  		}
   428  	}
   429  |	_RETURN
   430  	{
   431  		$$ = &ReturnStmt{
   432  			Return: $1,
   433  		}
   434  	}
   435  |	expr '=' expr      { $$ = binary($1, $2, $<tok>2, $3) }
   436  |	expr _AUGM expr    { $$ = binary($1, $2, $<tok>2, $3) }
   437  |	_PASS
   438  	{
   439  		$$ = &BranchStmt{
   440  			Token: $<tok>1,
   441  			TokenPos: $1,
   442  		}
   443  	}
   444  |	_BREAK
   445  	{
   446  		$$ = &BranchStmt{
   447  			Token: $<tok>1,
   448  			TokenPos: $1,
   449  		}
   450  	}
   451  |	_CONTINUE
   452  	{
   453  		$$ = &BranchStmt{
   454  			Token: $<tok>1,
   455  			TokenPos: $1,
   456  		}
   457  	}
   458  
   459  semi_opt:
   460  |	';'
   461  
   462  primary_expr:
   463  	ident
   464  |	number
   465  |	string
   466  	{
   467  		$$ = $1
   468  	}
   469  |	primary_expr '.' _IDENT
   470  	{
   471  		$$ = &DotExpr{
   472  			X: $1,
   473  			Dot: $2,
   474  			NamePos: $3,
   475  			Name: $<tok>3,
   476  		}
   477  	}
   478  |	_LOAD '(' string ',' load_arguments comma_opt ')'
   479  	{
   480  		load := &LoadStmt{
   481  			Load: $1,
   482  			Module: $3,
   483  			Rparen: End{Pos: $7},
   484  			ForceCompact: $1.Line == $7.Line,
   485  		}
   486  		for _, arg := range $5 {
   487  			load.From = append(load.From, &arg.from)
   488  			load.To = append(load.To, &arg.to)
   489  		}
   490  		$$ = load
   491  	}
   492  |	primary_expr '(' arguments_opt ')'
   493  	{
   494  		$$ = &CallExpr{
   495  			X: $1,
   496  			ListStart: $2,
   497  			List: $3,
   498  			End: End{Pos: $4},
   499  			ForceCompact: forceCompact($2, $3, $4),
   500  			ForceMultiLine: forceMultiLine($2, $3, $4),
   501  		}
   502  	}
   503  |	primary_expr '[' expr ']'
   504  	{
   505  		$$ = &IndexExpr{
   506  			X: $1,
   507  			IndexStart: $2,
   508  			Y: $3,
   509  			End: $4,
   510  		}
   511  	}
   512  |	primary_expr '[' expr_opt ':' test_opt ']'
   513  	{
   514  		$$ = &SliceExpr{
   515  			X: $1,
   516  			SliceStart: $2,
   517  			From: $3,
   518  			FirstColon: $4,
   519  			To: $5,
   520  			End: $6,
   521  		}
   522  	}
   523  |	primary_expr '[' expr_opt ':' test_opt ':' test_opt ']'
   524  	{
   525  		$$ = &SliceExpr{
   526  			X: $1,
   527  			SliceStart: $2,
   528  			From: $3,
   529  			FirstColon: $4,
   530  			To: $5,
   531  			SecondColon: $6,
   532  			Step: $7,
   533  			End: $8,
   534  		}
   535  	}
   536  |	'[' tests_opt ']'
   537  	{
   538  		$$ = &ListExpr{
   539  			Start: $1,
   540  			List: $2,
   541  			End: End{Pos: $3},
   542  			ForceMultiLine: forceMultiLine($1, $2, $3),
   543  		}
   544  	}
   545  |	'[' test for_clauses_with_if_clauses_opt ']'
   546  	{
   547  		$$ = &Comprehension{
   548  			Curly: false,
   549  			Lbrack: $1,
   550  			Body: $2,
   551  			Clauses: $3,
   552  			End: End{Pos: $4},
   553  			ForceMultiLine: forceMultiLineComprehension($1, $2, $3, $4),
   554  		}
   555  	}
   556  |	'{' keyvalue for_clauses_with_if_clauses_opt '}'
   557  	{
   558  		$$ = &Comprehension{
   559  			Curly: true,
   560  			Lbrack: $1,
   561  			Body: $2,
   562  			Clauses: $3,
   563  			End: End{Pos: $4},
   564  			ForceMultiLine: forceMultiLineComprehension($1, $2, $3, $4),
   565  		}
   566  	}
   567  |	'{' keyvalues '}'
   568  	{
   569  		$$ = &DictExpr{
   570  			Start: $1,
   571  			List: $2,
   572  			End: End{Pos: $3},
   573  			ForceMultiLine: forceMultiLine($1, $2, $3),
   574  		}
   575  	}
   576  |	'{' tests comma_opt '}'  // TODO: remove, not supported
   577  	{
   578  		$$ = &SetExpr{
   579  			Start: $1,
   580  			List: $2,
   581  			End: End{Pos: $4},
   582  			ForceMultiLine: forceMultiLine($1, $2, $4),
   583  		}
   584  	}
   585  |	'(' tests_opt ')'
   586  	{
   587  		if len($2) == 1 && $<comma>2.Line == 0 {
   588  			// Just a parenthesized expression, not a tuple.
   589  			$$ = &ParenExpr{
   590  				Start: $1,
   591  				X: $2[0],
   592  				End: End{Pos: $3},
   593  				ForceMultiLine: forceMultiLine($1, $2, $3),
   594  			}
   595  		} else {
   596  			$$ = &TupleExpr{
   597  				Start: $1,
   598  				List: $2,
   599  				End: End{Pos: $3},
   600  				ForceCompact: forceCompact($1, $2, $3),
   601  				ForceMultiLine: forceMultiLine($1, $2, $3),
   602  			}
   603  		}
   604  	}
   605  
   606  arguments_opt:
   607  	{
   608  		$$ = nil
   609  	}
   610  |	arguments comma_opt
   611  	{
   612  		$$ = $1
   613  	}
   614  
   615  arguments:
   616  	argument
   617  	{
   618  		$$ = []Expr{$1}
   619  	}
   620  |	arguments ',' argument
   621  	{
   622  		$$ = append($1, $3)
   623  	}
   624  
   625  argument:
   626  	test
   627  |	ident '=' test
   628  	{
   629  		$$ = binary($1, $2, $<tok>2, $3)
   630  	}
   631  |	'*' test
   632  	{
   633  		$$ = unary($1, $<tok>1, $2)
   634  	}
   635  |	_STAR_STAR test
   636  	{
   637  		$$ = unary($1, $<tok>1, $2)
   638  	}
   639  
   640  load_arguments:
   641  	load_argument {
   642  		$$ = []*struct{from Ident; to Ident}{$1}
   643  	}
   644  | load_arguments ',' load_argument
   645  	{
   646  		$1 = append($1, $3)
   647  		$$ = $1
   648  	}
   649  
   650  load_argument:
   651  	string {
   652  		start := $1.Start.add("'")
   653  		if $1.TripleQuote {
   654  			start = start.add("''")
   655  		}
   656  		$$ = &struct{from Ident; to Ident}{
   657  			from: Ident{
   658  				Name: $1.Value,
   659  				NamePos: start,
   660  			},
   661  			to: Ident{
   662  				Name: $1.Value,
   663  				NamePos: start,
   664  			},
   665  		}
   666  	}
   667  | ident '=' string
   668  	{
   669  		start := $3.Start.add("'")
   670  		if $3.TripleQuote {
   671  			start = start.add("''")
   672  		}
   673  		$$ = &struct{from Ident; to Ident}{
   674  			from: Ident{
   675  				Name: $3.Value,
   676  				NamePos: start,
   677  			},
   678  			to: *$1.(*Ident),
   679  		}
   680  	}
   681  
   682  parameters_opt:
   683  	{
   684  		$$ = nil
   685  	}
   686  |	parameters comma_opt
   687  	{
   688  		$$ = $1
   689  	}
   690  
   691  parameters:
   692  	parameter
   693  	{
   694  		$$ = []Expr{$1}
   695  	}
   696  |	parameters ',' parameter
   697  	{
   698  		$$ = append($1, $3)
   699  	}
   700  
   701  parameter:
   702  	ident
   703  |	ident '=' test
   704  	{
   705  		$$ = binary($1, $2, $<tok>2, $3)
   706  	}
   707  |	'*' ident
   708  	{
   709  		$$ = unary($1, $<tok>1, $2)
   710  	}
   711  |	'*'
   712  	{
   713  		$$ = unary($1, $<tok>1, nil)
   714  	}
   715  |	_STAR_STAR ident
   716  	{
   717  		$$ = unary($1, $<tok>1, $2)
   718  	}
   719  
   720  expr:
   721  	test
   722  |	expr ',' test
   723  	{
   724  		tuple, ok := $1.(*TupleExpr)
   725  		if !ok || !tuple.NoBrackets {
   726  			tuple = &TupleExpr{
   727  				List: []Expr{$1},
   728  				NoBrackets: true,
   729  				ForceCompact: true,
   730  				ForceMultiLine: false,
   731  			}
   732  		}
   733  		tuple.List = append(tuple.List, $3)
   734  		$$ = tuple
   735  	}
   736  
   737  expr_opt:
   738  	{
   739  		$$ = nil
   740  	}
   741  |	expr
   742  
   743  exprs:
   744  	expr
   745  	{
   746  		$$ = []Expr{$1}
   747  	}
   748  |	exprs ',' expr
   749  	{
   750  		$$ = append($1, $3)
   751  	}
   752  
   753  exprs_opt:
   754  	{
   755  		$$ = nil
   756  	}
   757  |	exprs comma_opt
   758  	{
   759  		$$ = $1
   760  	}
   761  
   762  test:
   763  	primary_expr
   764  |	_LAMBDA exprs_opt ':' expr  // TODO: remove, not supported
   765  	{
   766  		$$ = &LambdaExpr{
   767  			Function: Function{
   768  				StartPos: $1,
   769  				Params: $2,
   770  				Body: []Expr{$4},
   771  			},
   772  		}
   773  	}
   774  |	_NOT test %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
   775  |	'-' test  %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
   776  |	'+' test  %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
   777  |	'~' test  %prec _UNARY { $$ = unary($1, $<tok>1, $2) }
   778  |	test '*' test      { $$ = binary($1, $2, $<tok>2, $3) }
   779  |	test '%' test      { $$ = binary($1, $2, $<tok>2, $3) }
   780  |	test '/' test      { $$ = binary($1, $2, $<tok>2, $3) }
   781  |	test _INT_DIV test { $$ = binary($1, $2, $<tok>2, $3) }
   782  |	test '+' test      { $$ = binary($1, $2, $<tok>2, $3) }
   783  |	test '-' test      { $$ = binary($1, $2, $<tok>2, $3) }
   784  |	test '<' test      { $$ = binary($1, $2, $<tok>2, $3) }
   785  |	test '>' test      { $$ = binary($1, $2, $<tok>2, $3) }
   786  |	test _EQ test      { $$ = binary($1, $2, $<tok>2, $3) }
   787  |	test _LE test      { $$ = binary($1, $2, $<tok>2, $3) }
   788  |	test _NE test      { $$ = binary($1, $2, $<tok>2, $3) }
   789  |	test _GE test      { $$ = binary($1, $2, $<tok>2, $3) }
   790  |	test _IN test      { $$ = binary($1, $2, $<tok>2, $3) }
   791  |	test _NOT _IN test { $$ = binary($1, $2, "not in", $4) }
   792  |	test _OR test      { $$ = binary($1, $2, $<tok>2, $3) }
   793  |	test _AND test     { $$ = binary($1, $2, $<tok>2, $3) }
   794  |	test '|' test      { $$ = binary($1, $2, $<tok>2, $3) }
   795  |	test '&' test      { $$ = binary($1, $2, $<tok>2, $3) }
   796  |	test '^' test      { $$ = binary($1, $2, $<tok>2, $3) }
   797  |	test _BIT_LSH test { $$ = binary($1, $2, $<tok>2, $3) }
   798  |	test _BIT_RSH test { $$ = binary($1, $2, $<tok>2, $3) }
   799  |	test _IS test
   800  	{
   801  		if b, ok := $3.(*UnaryExpr); ok && b.Op == "not" {
   802  			$$ = binary($1, $2, "is not", b.X)
   803  		} else {
   804  			$$ = binary($1, $2, $<tok>2, $3)
   805  		}
   806  	}
   807  |	test _IF test _ELSE test
   808  	{
   809  		$$ = &ConditionalExpr{
   810  			Then: $1,
   811  			IfStart: $2,
   812  			Test: $3,
   813  			ElseStart: $4,
   814  			Else: $5,
   815  		}
   816  	}
   817  
   818  tests:
   819  	test
   820  	{
   821  		$$ = []Expr{$1}
   822  	}
   823  |	tests ',' test
   824  	{
   825  		$$ = append($1, $3)
   826  	}
   827  
   828  test_opt:
   829  	{
   830  		$$ = nil
   831  	}
   832  |	test
   833  
   834  tests_opt:
   835  	{
   836  		$$, $<comma>$ = nil, Position{}
   837  	}
   838  |	tests comma_opt
   839  	{
   840  		$$, $<comma>$ = $1, $2
   841  	}
   842  
   843  // comma_opt is an optional comma. If the comma is present,
   844  // the rule's value is the position of the comma. Otherwise
   845  // the rule's value is the zero position. Tracking this
   846  // lets us distinguish (x) and (x,).
   847  comma_opt:
   848  	{
   849  		$$ = Position{}
   850  	}
   851  |	','
   852  
   853  keyvalue:
   854  	test ':' test  {
   855  		$$ = &KeyValueExpr{
   856  			Key: $1,
   857  			Colon: $2,
   858  			Value: $3,
   859  		}
   860  	}
   861  
   862  keyvalues_no_comma:
   863  	keyvalue
   864  	{
   865  		$$ = []Expr{$1}
   866  	}
   867  |	keyvalues_no_comma ',' keyvalue
   868  	{
   869  		$$ = append($1, $3)
   870  	}
   871  
   872  keyvalues:
   873  	{
   874  		$$ = nil
   875  	}
   876  |	keyvalues_no_comma
   877  	{
   878  		$$ = $1
   879  	}
   880  |	keyvalues_no_comma ','
   881  	{
   882  		$$ = $1
   883  	}
   884  
   885  loop_vars:
   886  	primary_expr
   887  |	loop_vars ',' primary_expr
   888  	{
   889  		tuple, ok := $1.(*TupleExpr)
   890  		if !ok || !tuple.NoBrackets {
   891  			tuple = &TupleExpr{
   892  				List: []Expr{$1},
   893  				NoBrackets: true,
   894  				ForceCompact: true,
   895  				ForceMultiLine: false,
   896  			}
   897  		}
   898  		tuple.List = append(tuple.List, $3)
   899  		$$ = tuple
   900  	}
   901  
   902  string:
   903  	_STRING
   904  	{
   905  		$$ = &StringExpr{
   906  			Start: $1,
   907  			Value: $<str>1,
   908  			TripleQuote: $<triple>1,
   909  			End: $1.add($<tok>1),
   910  			Token: $<tok>1,
   911  		}
   912  	}
   913  
   914  ident:
   915  	_IDENT
   916  	{
   917  		$$ = &Ident{NamePos: $1, Name: $<tok>1}
   918  	}
   919  
   920  number:
   921  	_NUMBER
   922  	{
   923  		$$ = &LiteralExpr{Start: $1, Token: $<tok>1}
   924  	}
   925  
   926  for_clause:
   927  	_FOR loop_vars _IN test
   928  	{
   929  		$$ = &ForClause{
   930  			For: $1,
   931  			Vars: $2,
   932  			In: $3,
   933  			X: $4,
   934  		}
   935  	}
   936  
   937  for_clause_with_if_clauses_opt:
   938  	for_clause {
   939  		$$ = []Expr{$1}
   940  	}
   941  |	for_clause_with_if_clauses_opt _IF test {
   942  		$$ = append($1, &IfClause{
   943  			If: $2,
   944  			Cond: $3,
   945  		})
   946  	}
   947  
   948  for_clauses_with_if_clauses_opt:
   949  	for_clause_with_if_clauses_opt
   950  	{
   951  		$$ = $1
   952  	}
   953  |	for_clauses_with_if_clauses_opt for_clause_with_if_clauses_opt {
   954  		$$ = append($1, $2...)
   955  	}
   956  
   957  %%
   958  
   959  // Go helper code.
   960  
   961  // unary returns a unary expression with the given
   962  // position, operator, and subexpression.
   963  func unary(pos Position, op string, x Expr) Expr {
   964  	return &UnaryExpr{
   965  		OpStart: pos,
   966  		Op:      op,
   967  		X:       x,
   968  	}
   969  }
   970  
   971  // binary returns a binary expression with the given
   972  // operands, position, and operator.
   973  func binary(x Expr, pos Position, op string, y Expr) Expr {
   974  	_, xend := x.Span()
   975  	ystart, _ := y.Span()
   976  
   977  	switch op {
   978  	case "=", "+=", "-=", "*=", "/=", "//=", "%=", "|=":
   979  		return &AssignExpr{
   980  			LHS:       x,
   981  			OpPos:     pos,
   982  			Op:        op,
   983  			LineBreak: xend.Line < ystart.Line,
   984  			RHS:       y,
   985  		}
   986  	}
   987  
   988  	return &BinaryExpr{
   989  		X:         x,
   990  		OpStart:   pos,
   991  		Op:        op,
   992  		LineBreak: xend.Line < ystart.Line,
   993  		Y:         y,
   994  	}
   995  }
   996  
   997  // isSimpleExpression returns whether an expression is simple and allowed to exist in
   998  // compact forms of sequences.
   999  // The formal criteria are the following: an expression is considered simple if it's
  1000  // a literal (variable, string or a number), a literal with a unary operator or an empty sequence.
  1001  func isSimpleExpression(expr *Expr) bool {
  1002  	switch x := (*expr).(type) {
  1003  	case *LiteralExpr, *StringExpr, *Ident:
  1004  		return true
  1005  	case *UnaryExpr:
  1006  		_, literal := x.X.(*LiteralExpr)
  1007  		_, ident := x.X.(*Ident)
  1008  		return literal || ident
  1009  	case *ListExpr:
  1010  		return len(x.List) == 0
  1011  	case *TupleExpr:
  1012  		return len(x.List) == 0
  1013  	case *DictExpr:
  1014  		return len(x.List) == 0
  1015  	case *SetExpr:
  1016  		return len(x.List) == 0
  1017  	default:
  1018  		return false
  1019  	}
  1020  }
  1021  
  1022  // forceCompact returns the setting for the ForceCompact field for a call or tuple.
  1023  //
  1024  // NOTE 1: The field is called ForceCompact, not ForceSingleLine,
  1025  // because it only affects the formatting associated with the call or tuple syntax,
  1026  // not the formatting of the arguments. For example:
  1027  //
  1028  //	call([
  1029  //		1,
  1030  //		2,
  1031  //		3,
  1032  //	])
  1033  //
  1034  // is still a compact call even though it runs on multiple lines.
  1035  //
  1036  // In contrast the multiline form puts a linebreak after the (.
  1037  //
  1038  //	call(
  1039  //		[
  1040  //			1,
  1041  //			2,
  1042  //			3,
  1043  //		],
  1044  //	)
  1045  //
  1046  // NOTE 2: Because of NOTE 1, we cannot use start and end on the
  1047  // same line as a signal for compact mode: the formatting of an
  1048  // embedded list might move the end to a different line, which would
  1049  // then look different on rereading and cause buildifier not to be
  1050  // idempotent. Instead, we have to look at properties guaranteed
  1051  // to be preserved by the reformatting, namely that the opening
  1052  // paren and the first expression are on the same line and that
  1053  // each subsequent expression begins on the same line as the last
  1054  // one ended (no line breaks after comma).
  1055  func forceCompact(start Position, list []Expr, end Position) bool {
  1056  	if len(list) <= 1 {
  1057  		// The call or tuple will probably be compact anyway; don't force it.
  1058  		return false
  1059  	}
  1060  
  1061  	// If there are any named arguments or non-string, non-literal
  1062  	// arguments, cannot force compact mode.
  1063  	line := start.Line
  1064  	for _, x := range list {
  1065  		start, end := x.Span()
  1066  		if start.Line != line {
  1067  			return false
  1068  		}
  1069  		line = end.Line
  1070  		if !isSimpleExpression(&x) {
  1071  			return false
  1072  		}
  1073  	}
  1074  	return end.Line == line
  1075  }
  1076  
  1077  // forceMultiLine returns the setting for the ForceMultiLine field.
  1078  func forceMultiLine(start Position, list []Expr, end Position) bool {
  1079  	if len(list) > 1 {
  1080  		// The call will be multiline anyway, because it has multiple elements. Don't force it.
  1081  		return false
  1082  	}
  1083  
  1084  	if len(list) == 0 {
  1085  		// Empty list: use position of brackets.
  1086  		return start.Line != end.Line
  1087  	}
  1088  
  1089  	// Single-element list.
  1090  	// Check whether opening bracket is on different line than beginning of
  1091  	// element, or closing bracket is on different line than end of element.
  1092  	elemStart, elemEnd := list[0].Span()
  1093  	return start.Line != elemStart.Line || end.Line != elemEnd.Line
  1094  }
  1095  
  1096  // forceMultiLineComprehension returns the setting for the ForceMultiLine field for a comprehension.
  1097  func forceMultiLineComprehension(start Position, expr Expr, clauses []Expr, end Position) bool {
  1098  	// Return true if there's at least one line break between start, expr, each clause, and end
  1099  	exprStart, exprEnd := expr.Span()
  1100  	if start.Line != exprStart.Line {
  1101  		return true
  1102  	}
  1103  	previousEnd := exprEnd
  1104  	for _, clause := range clauses {
  1105  		clauseStart, clauseEnd := clause.Span()
  1106  		if previousEnd.Line != clauseStart.Line {
  1107  			return true
  1108  		}
  1109  		previousEnd = clauseEnd
  1110  	}
  1111  	return previousEnd.Line != end.Line
  1112  }
  1113  
  1114  // extractTrailingComments extracts trailing comments of an indented block starting with the first
  1115  // comment line with indentation less than the block indentation.
  1116  // The comments can either belong to CommentBlock statements or to the last non-comment statement
  1117  // as After-comments.
  1118  func extractTrailingComments(stmt Expr) []Expr {
  1119  	body := getLastBody(stmt)
  1120  	var comments []Expr
  1121  	if body != nil && len(*body) > 0 {
  1122  		// Get the current indentation level
  1123  		start, _ := (*body)[0].Span()
  1124  		indentation := start.LineRune
  1125  
  1126  		// Find the last non-comment statement
  1127  		lastNonCommentIndex := -1
  1128  		for i, stmt := range *body {
  1129  			if _, ok := stmt.(*CommentBlock); !ok {
  1130  				lastNonCommentIndex = i
  1131  			}
  1132  		}
  1133  		if lastNonCommentIndex == -1 {
  1134  			return comments
  1135  		}
  1136  
  1137  		// Iterate over the trailing comments, find the first comment line that's not indented enough,
  1138  		// dedent it and all the following comments.
  1139  		for i := lastNonCommentIndex; i < len(*body); i++ {
  1140  			stmt := (*body)[i]
  1141  			if comment := extractDedentedComment(stmt, indentation); comment != nil {
  1142  				// This comment and all the following CommentBlock statements are to be extracted.
  1143  				comments = append(comments, comment)
  1144  				comments = append(comments, (*body)[i+1:]...)
  1145  				*body = (*body)[:i+1]
  1146  				// If the current statement is a CommentBlock statement without any comment lines
  1147  				// it should be removed too.
  1148  				if i > lastNonCommentIndex && len(stmt.Comment().After) == 0 {
  1149  					*body = (*body)[:i]
  1150  				}
  1151  			}
  1152  		}
  1153    }
  1154    return comments
  1155  }
  1156  
  1157  // extractDedentedComment extract the first comment line from `stmt` which indentation is smaller
  1158  // than `indentation`, and all following comment lines, and returns them in a newly created
  1159  // CommentBlock statement.
  1160  func extractDedentedComment(stmt Expr, indentation int) Expr {
  1161  	for i, line := range stmt.Comment().After {
  1162  		// line.Start.LineRune == 0 can't exist in parsed files, it indicates that the comment line
  1163  		// has been added by an AST modification. Don't take such lines into account.
  1164  		if line.Start.LineRune > 0 && line.Start.LineRune < indentation {
  1165  			// This and all the following lines should be dedented
  1166  			cb := &CommentBlock{
  1167  				Start: line.Start,
  1168  				Comments: Comments{After: stmt.Comment().After[i:]},
  1169  			}
  1170  			stmt.Comment().After = stmt.Comment().After[:i]
  1171  			return cb
  1172  		}
  1173  	}
  1174  	return nil
  1175  }
  1176  
  1177  // getLastBody returns the last body of a block statement (the only body for For- and DefStmt
  1178  // objects, the last in a if-elif-else chain
  1179  func getLastBody(stmt Expr) *[]Expr {
  1180  	switch block := stmt.(type) {
  1181  	case *DefStmt:
  1182  		return &block.Body
  1183  	case *ForStmt:
  1184  		return &block.Body
  1185  	case *IfStmt:
  1186  		if len(block.False) == 0 {
  1187  			return &block.True
  1188  		} else if len(block.False) == 1 {
  1189  			if next, ok := block.False[0].(*IfStmt); ok {
  1190  				// Recursively find the last block of the chain
  1191  				return getLastBody(next)
  1192  			}
  1193  		}
  1194  		return &block.False
  1195  	}
  1196  	return nil
  1197  }