func()

in internal/compile/compile.go [1466:1540]


func (fcomp *fcomp) plus(e *syntax.BinaryExpr) {
	// Gather all the right operands of the left tree of plusses.
	// A tree (((a+b)+c)+d) becomes args=[a +b +c +d].
	args := make([]summand, 0, 2) // common case: 2 operands
	for plus := e; ; {
		args = append(args, summand{unparen(plus.Y), plus.OpPos})
		left := unparen(plus.X)
		x, ok := left.(*syntax.BinaryExpr)
		if !ok || x.Op != syntax.PLUS {
			args = append(args, summand{x: left})
			break
		}
		plus = x
	}
	// Reverse args to syntactic order.
	for i, n := 0, len(args)/2; i < n; i++ {
		j := len(args) - 1 - i
		args[i], args[j] = args[j], args[i]
	}

	// Fold sums of adjacent literals of the same type: ""+"", []+[], ()+().
	out := args[:0] // compact in situ
	for i := 0; i < len(args); {
		j := i + 1
		if code := addable(args[i].x); code != 0 {
			for j < len(args) && addable(args[j].x) == code {
				j++
			}
			if j > i+1 {
				args[i].x = add(code, args[i:j])
			}
		}
		out = append(out, args[i])
		i = j
	}
	args = out

	// Emit code for an n-ary sum (n > 0).
	fcomp.expr(args[0].x)
	for _, summand := range args[1:] {
		fcomp.expr(summand.x)
		fcomp.setPos(summand.plusPos)
		fcomp.emit(PLUS)
	}

	// If len(args) > 2, use of an accumulator instead of a chain of
	// PLUS operations may be more efficient.
	// However, no gain was measured on a workload analogous to Bazel loading;
	// TODO(adonovan): opt: re-evaluate on a Bazel analysis-like workload.
	//
	// We cannot use a single n-ary SUM operation
	//    a b c SUM<3>
	// because we need to report a distinct error for each
	// individual '+' operation, so three additional operations are
	// needed:
	//
	//   ACCSTART => create buffer and append to it
	//   ACCUM    => append to buffer
	//   ACCEND   => get contents of buffer
	//
	// For string, list, and tuple values, the interpreter can
	// optimize these operations by using a mutable buffer.
	// For all other types, ACCSTART and ACCEND would behave like
	// the identity function and ACCUM behaves like PLUS.
	// ACCUM must correctly support user-defined operations
	// such as list+foo.
	//
	// fcomp.emit(ACCSTART)
	// for _, summand := range args[1:] {
	// 	fcomp.expr(summand.x)
	// 	fcomp.setPos(summand.plusPos)
	// 	fcomp.emit(ACCUM)
	// }
	// fcomp.emit(ACCEND)
}