!7 sclass Token { S text; CNC group; int index; toString { ret text; } } sclass CNC { S text; // if known new L tokens; } p-exp { S s = "There are 6 chess pieces. How many chess pieces do you know?"; CNC in1 = makeCNC("There are 6 chess pieces. How many chess pieces do you know?"); CNC out1 = makeCNC("6 I guess"); CNC in2 = makeCNC("There are 16 chess pieces. How many chess pieces do you know?"); LPair l = zipCNC(in1, in2); if (l == null) ret; l = antiFilter(l, func(Pair p) -> Bool { eqic(p.a.text, p.b.text) }); print("Remaining differences:"); pnl(l); SS daMap = pairsToMap(pairListMap(func(Token t) -> S { t.text }, l)); printStruct(daMap); S result = join(mapTokens_getOrKeep(collect(out1.tokens, 'text), daMap)); assertEqualsVerbose("16 I guess", result); } static CNC makeCNC(S s) { CNC cnc = nu(CNC, text := s); L tok = javaTokWithAngleBrackets(s); for i over tok: cnc.tokens.add(nu(Token, text := tok.get(i), group := cnc, index := i); ret cnc; } // may return null (no match) static LPair zipCNC(CNC cnc1, CNC cnc2) { ret zipTwoListsToPairs_ifSameLength(cnc1.tokens, cnc2.tokens); } static LPair dropSame(LPair l) { ret antiFilter(l, func(Pair p) -> Bool { eqic(p.a.text, p.b.text) }); }