static ParsedWithTokens pwt_combine(A value, ParsedWithTokens p1, ParsedWithTokens p2) {
assertSame(p1.tok, p2.tok);
ret new ParsedWithTokens(value, p1.tok, min(p1.iStart, p2.iStart), max(p1.iRemaining, p2.iRemaining));
}
// shorter version taking value of first pwt
static ParsedWithTokens pwt_combine(ParsedWithTokens p1, ParsedWithTokens p2) {
ret pwt_combine(p1!, p1, p2);
}
// entirely different meaning
static L> pwt_combine(Iterable> l1, Iterable> l2, IF2 f) {
new L> out;
for (ParsedWithTokens a : l1)
for (ParsedWithTokens b : pwt_toTheRightOf(l2, l1))
out.add(pwt_combine(f.get(a!, b!), a, b));
ret out;
}