persistable sclass NLABlock { S text; // original text LS lines; // same, split into lines Set entities; // entities L parsedLines; class Line { S text; Entity introducedEntity; L proposition; // fragments *() {} *(S *text) {} *(S *text, L *proposition) {} toString { ret joinWithSpace(proposition) + (introducedEntity == null ? "" : " => " + introducedEntity); } int index() { ret parsedLines.indexOf(this); } } sclass Entity extends Cluster { Line definedInLine; O userObject; *(S name, Line *definedInLine) { this(name); if (definedInLine != null) definedInLine.introducedEntity = this; } *(S name) { addSynonym(name); } } *(S *text) {} bool isParsed() { ret parsedLines != null; } toString { ret !isParsed() ? text : lines_rtrim(parsedLines); } L introductions() { ret filter(parsedLines, line -> line.introducedEntity != null); } L propositionLines() { ret filter(parsedLines, line -> line.introducedEntity == null); } Set propositionSet() { ret collectAsSet proposition(propositionLines()); } Map propositionIndex() { ret indexByField proposition(propositionLines()); } }