// a Lisp-like form static class Lisp implements Iterable { S head; new L args; // O more; // additional info, user-defined *() {} *(S *head) {} *(S *head, Lisp... args) { this.args.addAll(asList(args)); } *(S *head, Collection args) { for (O arg : args) add(arg); } // INEFFICIENT public S toString() { if (args.isEmpty()) 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) { args.add(l); ret this; } Lisp add(S s) { args.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 args.size(); } bool empty() { ret args.isEmpty(); } bool isEmpty() { ret args.isEmpty(); } bool isLeaf() { ret args.isEmpty(); } 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 args.iterator(); } Lisp subList(int fromIndex, int toIndex) { Lisp l = new Lisp(head); l.args.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) && eq(args, l.args); } public int hashCode() { ret head.hashCode() + args.hashCode(); } 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"); } }