// a Lisp-like form static class Lisp implements Iterable { S head; L args; // O more; // additional info, user-defined *() {} *(S head) { this.head = _compactString(head); } *(S *head, Lisp... args) { argsForEdit().addAll(asList(args)); } *(S head, Collection args) { this.head = _compactString(head); for (O arg : args) add(arg); } L argsForEdit() { ret args == null ? (args = new L) : args; } // INEFFICIENT public S toString() { if (empty()) return quoteIfNotIdentifierOrInteger(head); new L bla; for (Lisp a : args) bla.add(a.toString()); S inner = join(", ", bla); if (head.equals("")) return "{" + inner + "}"; // list else return quoteIfNotIdentifier(head) + "(" + inner + ")"; } S raw() { if (!isEmpty ()) fail("not raw: " + this); ret head; } Lisp add(Lisp l) { argsForEdit().add(l); ret this; } Lisp add(S s) { argsForEdit().add(new Lisp(s)); ret this; } Lisp add(O o) { if (o instanceof Lisp) add((Lisp) o); else if (o instanceof S) add((S) o); else fail("Bad argument type: " + structure(o)); ret this; } int size() { return l(args); } bool empty() { ret main.empty(args); } bool isEmpty() { ret main.empty(args); } bool isLeaf() { ret main.empty(args); } Lisp get(int i) { return main.get(args, i); } S getString(int i) { Lisp a = get(i); ret a == null ? null : a.head; } S s(int i) { ret getString(i); } S rawOrNull(int i) { Lisp a = get(i); ret a != null && a.isLeaf() ? a.head : null; } S raw(int i) { ret assertNotNull(rawOrNull(i)); } bool isLeaf(int i) { ret rawOrNull(i) != null; } boolean isA(S head) { return eq(head, this.head); } boolean is(S head, int size) { ret isA(head) && size() == size; } bool is(S head) { ret isA(head); } bool headIs(S head) { ret isA(head); } boolean is(S... heads) { return asList(heads).contains(head); } // check head for one of these (ignore case) boolean isic(S... heads) { return containsIgnoreCase(heads, head); } public Iterator iterator() { return main.iterator(args); } Lisp subList(int fromIndex, int toIndex) { Lisp l = new Lisp(head); l.argsForEdit().addAll(args.subList(fromIndex, toIndex)); // better to copy here I guess - safe return l; } public bool equals(O o) { if (o == null || o.getClass() != Lisp.class) ret false; Lisp l = cast o; ret eq(head, l.head) && (isLeaf() ? l.isLeaf() : l.args != null && eq(args, l.args)); } public int hashCode() { ret head.hashCode() + main.hashCode(args); } Lisp addAll(L args) { for (O arg : args) add(arg); ret this; } S unquoted() { ret unquote(raw()); } S unq() { ret unquoted(); } S unq(int i) { ret get(i).unq(); } // heads of arguments L heads() { ret collect(args, "head"); } }