persistable sclass G22NetworkElement > MetaWithChangeListeners { // network element belongs to settable G22Network network; // position in visual representation settable Rect bounds; // identifier in network (optional) settableWithVar S identifier; // script code to adapt element and to create instance settableWithVar S code; { onChange(-> { network?.change(); }); } class Port { // position on element's border (0 to 1, 0 to 1) settable DoublePt position; // name of port settable S name; // cable attached to this port settable G22NetworkCable cable; G22NetworkElement element() { ret G22NetworkElement.this; } } L ports = syncL(); LASCompileResult newCompileResult() { ret new LASCompileResult; } transient LASCompileResult compileResult; LASCompileResult compile(G22Utils g22utils) { S code = this.code; if (code == null) null; var cr = compileResult; if (cr != null && eq(cr.script, code)) ret cr; cr = newCompileResult(); var parser = g22utils.leftArrowParser(); configureParser(parser); cr.script(code).parser(parser).compile(); ret compileResult = cr; } void configureParser(GazelleV_LeftArrowScriptParser parser) { parser.addVar("blueprint"); } O makeInstance(G22Utils g22utils) null on exception { var compiled = compile(g22utils); if (compiled == null) null; var compiled2 = compiled!; if (!compiled.runnable()) null; new FlexibleVarContext ctx; ctx.put("blueprint", this); ret compiled2.get(ctx); } toString { ret or2(identifier(), "Unnamed element"); } Port getPort(S name) { ret firstThat(ports, p -> eq(p.name, name)); } void addPort aka addSlot(S name, DoublePt etc position) { Port p = getPort(name); if (p == null) ports.add(p = new Port().name(name)); p.position(position); change(); } void removePorts { ports.clear(); change(); } }