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)
}